diff options
Diffstat (limited to 'libjava/classpath/javax/swing')
312 files changed, 32365 insertions, 12342 deletions
diff --git a/libjava/classpath/javax/swing/AbstractButton.java b/libjava/classpath/javax/swing/AbstractButton.java index 21c4fc0a26c..376b3a056ae 100644 --- a/libjava/classpath/javax/swing/AbstractButton.java +++ b/libjava/classpath/javax/swing/AbstractButton.java @@ -165,6 +165,8 @@ public abstract class AbstractButton extends JComponent */ public void stateChanged(ChangeEvent ev) { + AbstractButton.this.fireStateChanged(); + repaint(); } } @@ -375,6 +377,7 @@ public abstract class AbstractButton extends JComponent protected AccessibleAbstractButton() { + // Nothing to do here yet. } public AccessibleStateSet getAccessibleStateSet() @@ -509,11 +512,37 @@ public abstract class AbstractButton extends JComponent } /** - * Creates a new AbstractButton object. + * Creates a new AbstractButton object. Subclasses should call the following + * sequence in their constructor in order to initialize the button correctly: + * <pre> + * super(); + * init(text, icon); + * </pre> + * + * The {@link #init(String, Icon)} method is not called automatically by this + * constructor. + * + * @see #init(String, Icon) */ public AbstractButton() { - init("", null); + actionListener = createActionListener(); + changeListener = createChangeListener(); + itemListener = createItemListener(); + + 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 = ""; updateUI(); } @@ -524,7 +553,7 @@ public abstract class AbstractButton extends JComponent */ public ButtonModel getModel() { - return model; + return model; } /** @@ -569,25 +598,6 @@ public abstract class AbstractButton extends JComponent if (icon != null) default_icon = icon; - - actionListener = createActionListener(); - changeListener = createChangeListener(); - itemListener = createItemListener(); - - horizontalAlignment = CENTER; - horizontalTextPosition = TRAILING; - verticalAlignment = CENTER; - verticalTextPosition = CENTER; - borderPainted = true; - contentAreaFilled = true; - - focusPainted = true; - setFocusable(true); - - setAlignmentX(LEFT_ALIGNMENT); - setAlignmentY(CENTER_ALIGNMENT); - - setDisplayedMnemonicIndex(-1); } /** @@ -615,7 +625,8 @@ public abstract class AbstractButton extends JComponent */ public void setActionCommand(String actionCommand) { - model.setActionCommand(actionCommand); + if (model != null) + model.setActionCommand(actionCommand); } /** @@ -782,7 +793,10 @@ public abstract class AbstractButton extends JComponent */ public int getMnemonic() { - return getModel().getMnemonic(); + ButtonModel mod = getModel(); + if (mod != null) + return mod.getMnemonic(); + return -1; } /** @@ -810,11 +824,15 @@ public abstract class AbstractButton extends JComponent */ public void setMnemonic(int mne) { - int old = getModel().getMnemonic(); + ButtonModel mod = getModel(); + int old = -1; + if (mod != null) + old = mod.getMnemonic(); if (old != mne) { - getModel().setMnemonic(mne); + if (mod != null) + mod.setMnemonic(mne); if (text != null && !text.equals("")) { @@ -907,7 +925,9 @@ public abstract class AbstractButton extends JComponent */ public void setSelected(boolean s) { - getModel().setSelected(s); + ButtonModel mod = getModel(); + if (mod != null) + mod.setSelected(s); } /** @@ -918,7 +938,10 @@ public abstract class AbstractButton extends JComponent */ public boolean isSelected() { - return getModel().isSelected(); + ButtonModel mod = getModel(); + if (mod != null) + return mod.isSelected(); + return false; } /** @@ -929,8 +952,14 @@ public abstract class AbstractButton extends JComponent */ public void setEnabled(boolean b) { + // Do nothing if state does not change. + if (b == isEnabled()) + return; super.setEnabled(b); - getModel().setEnabled(b); + setFocusable(b); + ButtonModel mod = getModel(); + if (mod != null) + mod.setEnabled(b); } /** @@ -1608,16 +1637,9 @@ public abstract class AbstractButton extends JComponent * * @return The new ChangeListener */ - protected ChangeListener createChangeListener() + protected ChangeListener createChangeListener() { - return new ChangeListener() - { - public void stateChanged(ChangeEvent e) - { - AbstractButton.this.fireStateChanged(); - AbstractButton.this.repaint(); - } - }; + return new ButtonChangeListener(); } /** @@ -1669,18 +1691,22 @@ public abstract class AbstractButton extends JComponent */ public void doClick(int pressTime) { - getModel().setArmed(true); - getModel().setPressed(true); - try + ButtonModel mod = getModel(); + if (mod != null) { - java.lang.Thread.sleep(pressTime); - } - catch (java.lang.InterruptedException e) - { - // probably harmless + 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); } - getModel().setPressed(false); - getModel().setArmed(false); } /** @@ -1979,6 +2005,7 @@ public abstract class AbstractButton extends JComponent */ public void updateUI() { + // TODO: What to do here? } /** diff --git a/libjava/classpath/javax/swing/AbstractSpinnerModel.java b/libjava/classpath/javax/swing/AbstractSpinnerModel.java index d61113b0827..1a82f0a359d 100644 --- a/libjava/classpath/javax/swing/AbstractSpinnerModel.java +++ b/libjava/classpath/javax/swing/AbstractSpinnerModel.java @@ -61,6 +61,7 @@ public abstract class AbstractSpinnerModel implements SpinnerModel */ public AbstractSpinnerModel() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/ActionMap.java b/libjava/classpath/javax/swing/ActionMap.java index c14bafdb4be..65e193d2e79 100644 --- a/libjava/classpath/javax/swing/ActionMap.java +++ b/libjava/classpath/javax/swing/ActionMap.java @@ -80,6 +80,7 @@ public class ActionMap */ public ActionMap() { + // Nothing to do here. } /** @@ -170,7 +171,9 @@ public class ActionMap */ public Object[] keys() { - return actionMap.keySet().toArray(); + if (size() != 0) + return actionMap.keySet().toArray(); + return null; } /** @@ -187,7 +190,9 @@ public class ActionMap set.addAll(Arrays.asList(parent.allKeys())); set.addAll(actionMap.keySet()); - return set.toArray(); + 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 index 45cf3bbe074..ca78deb1290 100644 --- a/libjava/classpath/javax/swing/BorderFactory.java +++ b/libjava/classpath/javax/swing/BorderFactory.java @@ -71,7 +71,7 @@ public class BorderFactory */ public static Border createLineBorder(Color color) { - return null; + return createLineBorder(color, 1); } /** diff --git a/libjava/classpath/javax/swing/BoundedRangeModel.java b/libjava/classpath/javax/swing/BoundedRangeModel.java index 5ca5a7e043e..54446acd5ea 100644 --- a/libjava/classpath/javax/swing/BoundedRangeModel.java +++ b/libjava/classpath/javax/swing/BoundedRangeModel.java @@ -165,13 +165,13 @@ public interface BoundedRangeModel * * @param value the value * @param extent the extent - * @param minnimum the minimum value + * @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); + boolean adjusting); /** * Adds a <code>ChangeListener</code> to this object. diff --git a/libjava/classpath/javax/swing/Box.java b/libjava/classpath/javax/swing/Box.java index b2cb44aceb5..57519f6fcbd 100644 --- a/libjava/classpath/javax/swing/Box.java +++ b/libjava/classpath/javax/swing/Box.java @@ -40,6 +40,7 @@ package javax.swing; import java.awt.AWTError; import java.awt.Component; +import java.awt.Container; import java.awt.Dimension; import java.awt.LayoutManager; @@ -63,13 +64,13 @@ public class Box extends JComponent implements Accessible /** * Provides accessibility support for <code>Box</code>es. */ - // FIXME: disable to make libjava compile; visibility rules are broken - protected class AccessibleBox // extends Container.AccessibleAWTContainer + protected class AccessibleBox extends Container.AccessibleAWTContainer { private static final long serialVersionUID = -7775079816389931944L; protected AccessibleBox() { + // Nothing to do here. } public AccessibleRole getAccessibleRole() @@ -88,13 +89,14 @@ public class Box extends JComponent implements Accessible /** * Provides accessibility support for <code>Box.Filler</code>. */ - // FIXME: disable to make libjava compile; visibility rules are broken - protected class AccessibleBoxFiller // extends Component.AccessibleAWTComponent + protected class AccessibleBoxFiller + extends Component.AccessibleAWTComponent { private static final long serialVersionUID = 164963348357479321L; protected AccessibleBoxFiller() { + // Nothing to do here. } public AccessibleRole getAccessibleRole() @@ -103,8 +105,6 @@ public class Box extends JComponent implements Accessible } } - protected AccessibleContext accessibleContext; - private transient Dimension min, pref, max; /** @@ -135,9 +135,8 @@ public class Box extends JComponent implements Accessible public AccessibleContext getAccessibleContext() { - // FIXME: disable to make libjava compile; visibility rules are broken - // if (accessibleContext == null) - // accessibleContext = new AccessibleBoxFiller(); + if (accessibleContext == null) + accessibleContext = new AccessibleBoxFiller(); return accessibleContext; } @@ -284,8 +283,8 @@ public class Box extends JComponent implements Accessible public AccessibleContext getAccessibleContext() { - // if (accessibleContext == null) - // accessibleContext = new AccessibleBox(); + if (accessibleContext == null) + accessibleContext = new AccessibleBox(); return accessibleContext; } diff --git a/libjava/classpath/javax/swing/BoxLayout.java b/libjava/classpath/javax/swing/BoxLayout.java index 28bb53928ef..ebc0b4c211c 100644 --- a/libjava/classpath/javax/swing/BoxLayout.java +++ b/libjava/classpath/javax/swing/BoxLayout.java @@ -45,12 +45,6 @@ import java.awt.Dimension; import java.awt.Insets; import java.awt.LayoutManager2; import java.io.Serializable; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Vector; - -import gnu.java.awt.AWTUtilities; /** * A layout that stacks the children of a container in a Box, either @@ -63,248 +57,6 @@ public class BoxLayout implements LayoutManager2, Serializable { /** - * This is an abstraction that allows the BoxLayout algorithm to - * be applied to both direction (X and Y) without duplicating the - * algorithm. It defines several methods that access properties of - * a component for a specific direction. - */ - static interface Direction - { - /** - * Returns the correct part of <code>d</code> for this direction. This will - * be <code>d.width</code> for horizontal and <code>d.height</code> for - * vertical direction. - * - * @param d the size as Dimension object - * - * @return the correct part of <code>d</code> for this direction - */ - int size(Dimension d); - - /** - * Returns the lower bounds of the {@link Insets} object according to this - * direction. This will be <code>insets.top</code> for vertical direction - * and <code>insets.left</code> for horizontal direction. - * - * @param the {@link Insets} object from which to return the lower bounds - * - * @return the lower bounds of the {@link Insets} object according to this - * direction - */ - int lower(Insets insets); - - /** - * Returns the alignment property according to this direction. - * - * @param comp the Component for which to return the alignment property - * - * @return the alignment property according to this direction - */ - float alignment(Component comp); - - /** - * Sets the location for Component <code>c</code>. <code>coord1</code> - * specifies the coordinate of the location in this direction, - * <code>coord2</code> the coordinate of the location in the opposite - * direction. - * - * @param c the Component for which to set the location - * @param coord1 the coordinate in this direction - * @param coord2 the coordinate in the opposite direction - */ - void setLocation(Component c, int coord1, int coord2); - - /** - * Sets the size for Component <code>c</code>. <code>coord1</code> - * specifies the size in this direction, - * <code>coord2</code> the size in the opposite - * direction. - * - * @param c the Component for which to set the size - * @param size1 the size in this direction - * @param size2 the size in the opposite direction - */ - void setSize(Component c, int size1, int size2); - } - - /** - * The horizontal direction. - */ - static class Horizontal implements Direction - { - /** - * Returns the correct part of <code>d</code> for this direction. This will - * be <code>d.width</code> for horizontal and <code>d.height</code> for - * vertical direction. - * - * @param d the size as Dimension object - * - * @return the correct part of <code>d</code> for this direction - */ - public int size(Dimension d) - { - return d.width; - } - - /** - * Returns the lower bounds of the {@link Insets} object according to this - * direction. This will be <code>insets.top</code> for vertical direction - * and <code>insets.left</code> for horizontal direction. - * - * @param insets the {@link Insets} object from which to return the lower - * bounds - * - * @return the lower bounds of the {@link Insets} object according to this - * direction - */ - public int lower(Insets insets) - { - return insets.left; - } - - /** - * Returns the alignment property according to this direction. - * - * @param comp the Component for which to return the alignment property - * - * @return the alignment property according to this direction - */ - public float alignment(Component comp) - { - return comp.getAlignmentX(); - } - - /** - * Sets the location for Component <code>c</code>. <code>coord1</code> - * specifies the coordinate of the location in this direction, - * <code>coord2</code> the coordinate of the location in the opposite - * direction. - * - * @param c the Component for which to set the location - * @param coord1 the coordinate in this direction - * @param coord2 the coordinate in the opposite direction - */ - public void setLocation(Component c, int coord1, int coord2) - { - c.setLocation(coord1, coord2); - } - - /** - * Sets the size for Component <code>c</code>. <code>coord1</code> - * specifies the size in this direction, - * <code>coord2</code> the size in the opposite - * direction. - * - * @param c the Component for which to set the size - * @param size1 the size in this direction - * @param size2 the size in the opposite direction - */ - public void setSize(Component c, int size1, int size2) - { - c.setSize(size1, size2); - } - } - /** - * The vertical direction. - */ - static class Vertical implements Direction - { - /** - * Returns the correct part of <code>d</code> for this direction. This will - * be <code>d.width</code> for horizontal and <code>d.height</code> for - * vertical direction. - * - * @param d the size as Dimension object - * - * @return the correct part of <code>d</code> for this direction - */ - public int size(Dimension d) - { - return d.height; - } - - /** - * Returns the lower bounds of the {@link Insets} object according to this - * direction. This will be <code>insets.top</code> for vertical direction - * and <code>insets.left</code> for horizontal direction. - * - * @param insets the {@link Insets} object from which to return the lower - * bounds - * - * @return the lower bounds of the {@link Insets} object according to this - * direction - */ - public int lower(Insets insets) - { - return insets.top; - } - - /** - * Returns the alignment property according to this direction. - * - * @param comp the Component for which to return the alignment property - * - * @return the alignment property according to this direction - */ - public float alignment(Component comp) - { - return comp.getAlignmentY(); - } - - /** - * Sets the location for Component <code>c</code>. <code>coord1</code> - * specifies the coordinate of the location in this direction, - * <code>coord2</code> the coordinate of the location in the opposite - * direction. - * - * @param c the Component for which to set the location - * @param coord1 the coordinate in this direction - * @param coord2 the coordinate in the opposite direction - */ - public void setLocation(Component c, int coord1, int coord2) - { - c.setLocation(coord2, coord1); - } - - /** - * Sets the size for Component <code>c</code>. <code>coord1</code> - * specifies the size in this direction, - * <code>coord2</code> the size in the opposite - * direction. - * - * @param c the Component for which to set the size - * @param size1 the size in this direction - * @param size2 the size in the opposite direction - */ - public void setSize(Component c, int size1, int size2) - { - c.setSize(size2, size1); - } - } - - /** - * A helper class that temporarily stores the size specs of a component. - */ - static class SizeReq - { - int size; - int min; - int pref; - int max; - float align; - Component comp; - SizeReq(Component comp, Direction dir) - { - this.min = dir.size(comp.getMinimumSize()); - this.pref = dir.size(comp.getPreferredSize()); - this.max = dir.size(comp.getMaximumSize()); - this.size = dir.size(comp.getSize()); - this.align = dir.alignment(comp); - this.comp = comp; - } - } - - /** * Specifies that components are laid out left to right. */ public static final int X_AXIS = 0; @@ -334,16 +86,50 @@ public class BoxLayout implements LayoutManager2, Serializable */ private Container container; - /* + /** * Current type of component layouting. Defaults to X_AXIS. */ private int way = X_AXIS; - /** Constant for the horizontal direction. */ - private static final Direction HORIZONTAL = new Horizontal(); + /** + * 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; - /** Constant for the vertical direction. */ - private static final Direction VERTICAL = new Vertical(); + /** + * The spans of the child components in the Y direction. + */ + private int[] spansY; /** * Constructs a <code>BoxLayout</code> object. @@ -355,6 +141,9 @@ public class BoxLayout implements LayoutManager2, Serializable */ 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; @@ -369,6 +158,7 @@ public class BoxLayout implements LayoutManager2, Serializable */ public void addLayoutComponent(String name, Component component) { + // Nothing to do here. } /** @@ -378,6 +168,7 @@ public class BoxLayout implements LayoutManager2, Serializable */ public void removeLayoutComponent(Component component) { + // Nothing to do here. } private boolean isHorizontalIn(Container parent) @@ -401,45 +192,16 @@ public class BoxLayout implements LayoutManager2, Serializable */ public Dimension preferredLayoutSize(Container parent) { - if (parent != container) - throw new AWTError("invalid parent"); - - Insets insets = parent.getInsets(); - int x = 0; - int y = 0; - - List children = AWTUtilities.getVisibleChildren(parent); + synchronized (container.getTreeLock()) + { + if (container != parent) + throw new AWTError("BoxLayout can't be shared"); - if (isHorizontalIn(parent)) - { - x = insets.left + insets.right; - // sum up preferred widths of components, find maximum of preferred - // heights - for (Iterator i = children.iterator(); i.hasNext();) - { - Component comp = (Component) i.next(); - Dimension sz = comp.getPreferredSize(); - x += sz.width; - y = Math.max(y, sz.height); - } - y += insets.bottom + insets.top; - } - else - { - y = insets.top + insets.bottom; - // sum up preferred heights of components, find maximum of - // preferred widths - for (Iterator i = children.iterator(); i.hasNext();) - { - Component comp = (Component) i.next(); - Dimension sz = comp.getPreferredSize(); - y += sz.height; - x = Math.max(x, sz.width); - } - x += insets.left + insets.right; + checkTotalRequirements(); + Insets i = container.getInsets(); + return new Dimension(xTotal.preferred + i.left + i.right, + yTotal.preferred + i.top + i.bottom); } - - return new Dimension(x, y); } /** @@ -451,41 +213,16 @@ public class BoxLayout implements LayoutManager2, Serializable */ public Dimension minimumLayoutSize(Container parent) { - if (parent != container) - throw new AWTError("invalid parent"); - - Insets insets = parent.getInsets(); - int x = insets.left + insets.right; - int y = insets.bottom + insets.top; - - List children = AWTUtilities.getVisibleChildren(parent); - - if (isHorizontalIn(parent)) + synchronized (container.getTreeLock()) { - // sum up preferred widths of components, find maximum of preferred - // heights - for (Iterator i = children.iterator(); i.hasNext();) - { - Component comp = (Component) i.next(); - Dimension sz = comp.getMinimumSize(); - x += sz.width; - y = Math.max(y, sz.height); - } - } - else - { - // sum up preferred heights of components, find maximum of - // preferred widths - for (Iterator i = children.iterator(); i.hasNext();) - { - Component comp = (Component) i.next(); - Dimension sz = comp.getMinimumSize(); - y += sz.height; - x = Math.max(x, sz.width); - } + 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); } - - return new Dimension(x, y); } /** @@ -495,12 +232,20 @@ public class BoxLayout implements LayoutManager2, Serializable */ public void layoutContainer(Container parent) { - if (isHorizontalIn(parent)) - layoutAlgorithm(parent, HORIZONTAL, VERTICAL); - else - layoutAlgorithm(parent, VERTICAL, HORIZONTAL); + 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 * @@ -509,6 +254,7 @@ public class BoxLayout implements LayoutManager2, Serializable */ public void addLayoutComponent(Component child, Object constraints) { + // Nothing to do here. } /** @@ -520,10 +266,14 @@ public class BoxLayout implements LayoutManager2, Serializable */ public float getLayoutAlignmentX(Container parent) { - if (parent != container) - throw new AWTError("invalid parent"); - - return 0; + synchronized (container.getTreeLock()) + { + if (container != parent) + throw new AWTError("BoxLayout can't be shared"); + + checkTotalRequirements(); + return xTotal.alignment; + } } /** @@ -535,10 +285,14 @@ public class BoxLayout implements LayoutManager2, Serializable */ public float getLayoutAlignmentY(Container parent) { - if (parent != container) - throw new AWTError("invalid parent"); - - return 0; + synchronized (container.getTreeLock()) + { + if (container != parent) + throw new AWTError("BoxLayout can't be shared"); + + checkTotalRequirements(); + return yTotal.alignment; + } } /** @@ -548,8 +302,20 @@ public class BoxLayout implements LayoutManager2, Serializable */ public void invalidateLayout(Container parent) { - if (parent != container) - throw new AWTError("invalid 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; + } } /** @@ -562,188 +328,117 @@ public class BoxLayout implements LayoutManager2, Serializable */ public Dimension maximumLayoutSize(Container parent) { - if (parent != container) - throw new AWTError("invalid parent"); - - Insets insets = parent.getInsets(); - int x = insets.left + insets.right; - int y = insets.top + insets.bottom; + synchronized (container.getTreeLock()) + { + if (container != parent) + throw new AWTError("BoxLayout can't be shared"); - List children = AWTUtilities.getVisibleChildren(parent); + checkTotalRequirements(); + Insets i = container.getInsets(); + return new Dimension(xTotal.maximum + i.left + i.right, + yTotal.maximum + i.top + i.bottom); + } + } - if (isHorizontalIn(parent)) + /** + * 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) { - - // sum up preferred widths of components, find maximum of preferred - // heights - for (Iterator i = children.iterator(); i.hasNext();) + checkRequirements(); + if (isHorizontalIn(container)) { - Component comp = (Component) i.next(); - Dimension sz = comp.getMaximumSize(); - x += sz.width; - // Check for overflow. - if (x < 0) - x = Integer.MAX_VALUE; - y = Math.max(y, sz.height); + xTotal = SizeRequirements.getTiledSizeRequirements(xChildren); + yTotal = SizeRequirements.getAlignedSizeRequirements(yChildren); } - } - else - { - // sum up preferred heights of components, find maximum of - // preferred widths - for (Iterator i = children.iterator(); i.hasNext();) + else { - Component comp = (Component) i.next(); - Dimension sz = comp.getMaximumSize(); - y += sz.height; - // Check for overflow - if (y < 0) - y = Integer.MAX_VALUE; - x = Math.max(x, sz.width); + xTotal = SizeRequirements.getAlignedSizeRequirements(xChildren); + yTotal = SizeRequirements.getTiledSizeRequirements(yChildren); } - } - return new Dimension(x, y); + } } /** - * Lays out the Container <code>c</code> in the layout direction - * <code>layoutDir</code>. The direction that is crossing the layout - * direction is specified in <code>crossDir</code>. - * - * @param parent - * @param layoutDir - * @param crossDir + * 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. */ - void layoutAlgorithm(Container parent, Direction layoutDir, Direction crossDir) + private void checkRequirements() { - if (parent != container) - throw new AWTError("invalid parent"); - - Dimension parentSize = parent.getSize(); - Insets insets = parent.getInsets(); - Dimension innerSize = new Dimension(parentSize.width - insets.left - - insets.right, parentSize.height - - insets.bottom - insets.top); - - // Set all components to their preferredSizes and sum up the allocated - // space. Create SizeReqs for each component and store them in - // sizeReqs. Find the maximum size in the crossing direction. - List children = AWTUtilities.getVisibleChildren(parent); - Vector sizeReqs = new Vector(); - int allocated = 0; - for (Iterator i = children.iterator(); i.hasNext();) - { - Component c = (Component) i.next(); - SizeReq sizeReq = new SizeReq(c, layoutDir); - int preferred = layoutDir.size(c.getPreferredSize()); - sizeReq.size = preferred; - allocated += preferred; - sizeReqs.add(sizeReq); - } - - // Distribute remaining space (may be positive or negative) over components - int remainder = layoutDir.size(innerSize) - allocated; - distributeSpace(sizeReqs, remainder, layoutDir); - - // Resize and relocate components. If the component can be sized to - // take the full space in the crossing direction, then do so, otherwise - // align according to its alingnmentX or alignmentY property. - int loc = 0; - int offset1 = layoutDir.lower(insets); - int offset2 = crossDir.lower(insets); - for (Iterator i = sizeReqs.iterator(); i.hasNext();) + if (xChildren == null || yChildren == null) { - SizeReq sizeReq = (SizeReq) i.next(); - Component c = sizeReq.comp; - int availCrossSize = crossDir.size(innerSize); - int maxCross = crossDir.size(c.getMaximumSize()); - int crossSize = Math.min(availCrossSize, maxCross); - int crossRemainder = availCrossSize - crossSize; - int crossLoc = (int) (crossDir.alignment(c) * crossRemainder); - layoutDir.setSize(c, sizeReq.size, crossSize); - layoutDir.setLocation(c, offset1 + loc, offset2 + crossLoc); - loc += sizeReq.size; + 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()); + } + } } } /** - * Distributes some space over a set of components. This implementation - * tries to set the components as close as possible to their - * <code>preferredSize</code>s, and respects the components - * <code>minimumSize</code> and <code>maximumSize</code>. - * - * The algorithm is implemented as follows: - * - * <ul> - * <li>The <code>remainder</code> is divided by the number of components - * in <code>freeComponents</code>.</li> - * <li>The result is added to (or substracted from) the size of each - * component.</li> - * <li>If the <code>minimumSize</code> or <code>maximumSize</code> of a - * component is exceeded, then this component is set to its - * <code>minimumSize</code> or <code>maximumSize</code>, it is removed from - * <code>freeComponents</code> and the difference is added to a new - * remainder.</li> - * <li>Finally, if there is a new remainer != 0 and the - * <code>freeComponents.size() != 0</code>, then this method is called - * recursivly to distribute the newly allocated remaining space.</li> - * </ul> - * - * @param freeComponents a SizeReq collection for components that have space - * left so that they can be moved freely - * @param remainder the space that should be distributed between the - * components - * @param dir the direction in which we operate + * 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. */ - void distributeSpace(Collection freeComponents, int remainder, Direction dir) + private void checkLayout() { - // Sum up total available space in components. If the remainder is negative - // then we sum up the difference between minSize and size. If remainder - // is positive we sum up the difference between maxSize and size. - double totalAvailable = 0; - for (Iterator i = freeComponents.iterator(); i.hasNext();) - { - SizeReq sizeReq = (SizeReq) i.next(); - if (remainder >= 0) - totalAvailable += sizeReq.max - sizeReq.size; - else - totalAvailable += sizeReq.min - sizeReq.size; - } - if (totalAvailable == 0) - if (remainder >= 0) - totalAvailable = 1; - else - totalAvailable = -1; - - int newRemainder = 0; - Vector stillFree = new Vector(); - for (Iterator i = freeComponents.iterator(); i.hasNext();) + if (offsetsX == null || offsetsY == null || spansX == null + || spansY == null) { - // Add/substract share to component. - SizeReq sizeReq = (SizeReq) i.next(); - double available = 0; - if (remainder >= 0) - available = sizeReq.max - sizeReq.size; + 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 - available = sizeReq.min - sizeReq.size; - int share = (int) ((available / totalAvailable) * remainder); - sizeReq.size += share; - // check for min/maximumSize - if (sizeReq.size < sizeReq.min) - { - newRemainder += sizeReq.size - sizeReq.min; - sizeReq.size = sizeReq.min; - } - else if (sizeReq.size > sizeReq.max) - { - newRemainder += sizeReq.size - sizeReq.max; - sizeReq.size = sizeReq.max; - } - else - stillFree.add(sizeReq); + { + SizeRequirements.calculateAlignedPositions(width, + xTotal, xChildren, + offsetsX, spansX); + SizeRequirements.calculateTiledPositions(height, + yTotal, yChildren, + offsetsY, spansY); + } } - // recursivly call this method if necessary - if (newRemainder != 0 && stillFree.size() > 0) - distributeSpace(stillFree, newRemainder, dir); } } diff --git a/libjava/classpath/javax/swing/ButtonGroup.java b/libjava/classpath/javax/swing/ButtonGroup.java index 3de1d4b9f16..94f0109e634 100644 --- a/libjava/classpath/javax/swing/ButtonGroup.java +++ b/libjava/classpath/javax/swing/ButtonGroup.java @@ -79,6 +79,7 @@ public class ButtonGroup implements Serializable */ public ButtonGroup() { + // Nothing to do here. } /** @@ -89,6 +90,8 @@ public class ButtonGroup implements Serializable public void add(AbstractButton b) { b.getModel().setGroup(this); + if (b.isSelected()) + sel = b.getModel(); buttons.addElement(b); } @@ -158,7 +161,7 @@ public class ButtonGroup implements Serializable { ButtonModel old = sel; sel = m; - + if (old != null) old.setSelected(false); AbstractButton button = FindButton(old); diff --git a/libjava/classpath/javax/swing/ButtonModel.java b/libjava/classpath/javax/swing/ButtonModel.java index 1bdc5d1850d..03fac13d2fa 100644 --- a/libjava/classpath/javax/swing/ButtonModel.java +++ b/libjava/classpath/javax/swing/ButtonModel.java @@ -49,37 +49,253 @@ import javax.swing.event.ChangeListener; */ public interface ButtonModel extends ItemSelectable { - boolean isArmed(); - void setArmed(boolean b); + /** + * Returns <code>true</code> if the button is armed, <code>false</code> + * otherwise. + * + * A button is armed, when the user has pressed the mouse over it, but has + * not yet released the mouse. + * + * @return <code>true</code> if the button is armed, <code>false</code> + * otherwise + * + * @see #setArmed(boolean) + */ + boolean isArmed(); - boolean isEnabled(); - void setEnabled(boolean b); + /** + * 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 <code>true</code> if the button is armed, <code>false</code> + * otherwise + * + * @see #isArmed() + */ + void setArmed(boolean b); - void setPressed(boolean b); - boolean isPressed(); + /** + * Returns <code>true</code> if the button is enabled, <code>false</code> + * otherwise. + * + * When a button is disabled, it is usually grayed out and the user cannot + * change its state. + * + * @return <code>true</code> if the button is enabled, <code>false</code> + * 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 <code>true</code> if the button is enabled, <code>false</code> + * otherwise + * + * @see #isEnabled() + */ + void setEnabled(boolean b); - void removeActionListener(ActionListener l); - void addActionListener(ActionListener l); + /** + * 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 <code>true</code> if the button is pressed, <code>false</code> + * otherwise + * + * @see #isPressed() + */ + void setPressed(boolean b); - void addItemListener(ItemListener l); - void removeItemListener(ItemListener l); - - void addChangeListener(ChangeListener l); - void removeChangeListener(ChangeListener l); + /** + * Returns <code>true</code> if the button is pressed, <code>false</code> + * 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 <code>true</code> if the button is pressed, <code>false</code> + * otherwise + * + * @see #setPressed(boolean) + */ + boolean isPressed(); - void setRollover(boolean b); - boolean isRollover(); + /** + * Removes an {@link ActionListener} from the list of registered listeners. + * + * @param l the action listener to remove + * + * @see #addActionListener(ActionListener) + */ + void removeActionListener(ActionListener l); - int getMnemonic(); - void setMnemonic(int key); + /** + * Adds an {@link ActionListener} to the list of registered listeners. + * + * An <code>ActionEvent</code> is usually fired when the user clicks on a + * button. + * + * @param l the action listener to add + * + * @see #removeActionListener(ActionListener) + */ + void addActionListener(ActionListener l); - void setActionCommand(String s); - String getActionCommand(); + /** + * Adds an {@link ItemListener} to the list of registered listeners. + * + * An <code>ItemEvent</code> 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); - void setGroup(ButtonGroup group); + /** + * Adds an {@link ItemListener} to the list of registered listeners. + * + * @param l the item listener to add + * + * @see #removeItemListener(ItemListener) + */ + void removeItemListener(ItemListener l); - void setSelected(boolean b); - boolean isSelected(); + /** + * Adds an {@link ChangeListener} to the list of registered listeners. + * + * A <code>ChangeEvent</code> 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 <code>true</code> if the button is rollover, <code>false</code> + * otherwise + * + * @see #isRollover() + */ + void setRollover(boolean b); + + /** + * Returns <code>true</code> if the button is rollover-ed, <code>false</code> + * otherwise. + * + * A button is rollover-ed, when the user has moved the mouse over it, but has + * not yet pressed the mouse. + * + * @return <code>true</code> if the button is rollover, <code>false</code> + * 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 + * <code>ActionEvents</code> 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 <code>true</code> if the button is selected, <code>false</code> + * otherwise + * + * @see #isSelected() + */ + void setSelected(boolean b); + + /** + * Returns <code>true</code> if the button is selected, <code>false</code> + * 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 <code>true</code> if the button is selected, <code>false</code> + * otherwise + * + * @see #setSelected(boolean) + */ + boolean isSelected(); } diff --git a/libjava/classpath/javax/swing/CellEditor.java b/libjava/classpath/javax/swing/CellEditor.java index bdb1665750d..3d229b26675 100644 --- a/libjava/classpath/javax/swing/CellEditor.java +++ b/libjava/classpath/javax/swing/CellEditor.java @@ -83,7 +83,7 @@ public interface CellEditor /** * addCellEditorListener - * @param value0 TODO + * @param listener TODO */ void addCellEditorListener(CellEditorListener listener); diff --git a/libjava/classpath/javax/swing/CellRendererPane.java b/libjava/classpath/javax/swing/CellRendererPane.java index 886d5c5f2a8..c59afd3188a 100644 --- a/libjava/classpath/javax/swing/CellRendererPane.java +++ b/libjava/classpath/javax/swing/CellRendererPane.java @@ -54,9 +54,7 @@ import javax.accessibility.AccessibleRole; * * @author Andrew Selkirk */ -public class CellRendererPane - extends Container - implements Accessible +public class CellRendererPane extends Container implements Accessible { private static final long serialVersionUID = -7642183829532984273L; @@ -72,6 +70,7 @@ public class CellRendererPane */ protected AccessibleCellRendererPane() { + // Nothing to do here. } /** @@ -89,22 +88,13 @@ public class CellRendererPane */ protected AccessibleContext accessibleContext = null; - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - /** * Constructs a new CellRendererPane. */ public CellRendererPane() { - } // CellRendererPane() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- + // Nothing to do here. + } /** * Should not be called. @@ -113,7 +103,8 @@ public class CellRendererPane */ public void update(Graphics graphics) { - } // update() + //Nothing to do here. + } /** * Despite normal behaviour this does <em>not</em> cause the container @@ -121,7 +112,8 @@ public class CellRendererPane */ public void invalidate() { - } // invalidate() + // Overridden to do nothing. + } /** * Should not be called. @@ -130,6 +122,7 @@ public class CellRendererPane */ public void paint(Graphics graphics) { + // Overridden to do nothing. } /** @@ -147,7 +140,7 @@ public class CellRendererPane { super.addImpl(c, constraints, index); } - } // addImpl() + } /** * Paints the specified component <code>c</code> on the {@link Graphics} @@ -175,9 +168,10 @@ public class CellRendererPane // reparent c addImpl(c, null, 0); + Rectangle oldClip = graphics.getClipBounds(); // translate to (x,y) graphics.translate(x, y); - + graphics.clipRect(0, 0, w, h); // set bounds of c c.setBounds(0, 0, w, h); @@ -192,8 +186,8 @@ public class CellRendererPane // untranslate g graphics.translate(-x, -y); - - } // paintComponent() + graphics.setClip(oldClip); + } /** * Paints the specified component <code>c</code> on the {@link Graphics} @@ -215,7 +209,7 @@ public class CellRendererPane Container p, int x, int y, int w, int h) { paintComponent(graphics, c, p, x, y, w, h, false); - } // paintComponent() + } /** * Paints the specified component <code>c</code> on the {@link Graphics} @@ -233,7 +227,7 @@ public class CellRendererPane Container p, Rectangle r) { paintComponent(graphics, c, p, r.x, r.y, r.width, r.height); - } // paintComponent() + } /** * getAccessibleContext <em>TODO</em> diff --git a/libjava/classpath/javax/swing/ComboBoxEditor.java b/libjava/classpath/javax/swing/ComboBoxEditor.java index 4eb5fc56206..8e914e4b9fe 100644 --- a/libjava/classpath/javax/swing/ComboBoxEditor.java +++ b/libjava/classpath/javax/swing/ComboBoxEditor.java @@ -64,7 +64,7 @@ public interface ComboBoxEditor * combo box list then this method should be called to change editting item * to the new selected item. * - * @param selectedItem item that is currently selected in the combo box + * @param item item that is currently selected in the combo box */ void setItem(Object item); diff --git a/libjava/classpath/javax/swing/ComponentInputMap.java b/libjava/classpath/javax/swing/ComponentInputMap.java index f95c3104535..28aa8e22cf6 100644 --- a/libjava/classpath/javax/swing/ComponentInputMap.java +++ b/libjava/classpath/javax/swing/ComponentInputMap.java @@ -78,7 +78,8 @@ public class ComponentInputMap extends InputMap public void put(KeyStroke keystroke, Object value) { super.put(keystroke, value); - // FIXME: Notify component. + if (component != null) + component.updateComponentInputMap(this); } /** @@ -87,7 +88,8 @@ public class ComponentInputMap extends InputMap public void clear() { super.clear(); - // FIXME: Notify component. + if (component != null) + component.updateComponentInputMap(this); } /** @@ -98,7 +100,8 @@ public class ComponentInputMap extends InputMap public void remove(KeyStroke keystroke) { super.remove(keystroke); - // FIXME: Notify component. + if (component != null) + component.updateComponentInputMap(this); } /** @@ -111,14 +114,19 @@ public class ComponentInputMap extends InputMap */ public void setParent(InputMap parentMap) { - if (! (parentMap instanceof ComponentInputMap)) - throw new IllegalArgumentException(); - - if (((ComponentInputMap) parentMap).getComponent() != component) - throw new IllegalArgumentException(); + 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); - // FIXME: Notify component. + if (component != null) + component.updateComponentInputMap(this); } /** diff --git a/libjava/classpath/javax/swing/DebugGraphics.java b/libjava/classpath/javax/swing/DebugGraphics.java index 137b82337af..126309a5844 100644 --- a/libjava/classpath/javax/swing/DebugGraphics.java +++ b/libjava/classpath/javax/swing/DebugGraphics.java @@ -42,6 +42,7 @@ 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; @@ -84,15 +85,16 @@ public class DebugGraphics extends Graphics static PrintStream debugLogStream = System.out; /** - * graphics + * Counts the created DebugGraphics objects. This is used by the + * logging facility. */ - Graphics graphics; + static int counter = 0; /** - * color + * graphics */ - Color color = Color.BLACK; - + Graphics graphics; + /** * buffer */ @@ -123,7 +125,7 @@ public class DebugGraphics extends Graphics */ public DebugGraphics() { - // TODO + counter++; } /** @@ -134,7 +136,7 @@ public class DebugGraphics extends Graphics */ public DebugGraphics(Graphics graphics, JComponent component) { - this.graphics = graphics; + this(graphics); // FIXME: What shall we do with component ? } @@ -145,6 +147,7 @@ public class DebugGraphics extends Graphics */ public DebugGraphics(Graphics graphics) { + this(); this.graphics = graphics; } @@ -155,7 +158,10 @@ public class DebugGraphics extends Graphics */ public void setColor(Color color) { - this.color = color; + if ((debugOptions & LOG_OPTION) != 0) + logStream().println(prefix() + " Setting color: " + color); + + graphics.setColor(color); } /** @@ -166,7 +172,9 @@ public class DebugGraphics extends Graphics */ public Graphics create() { - return new DebugGraphics(graphics.create()); + DebugGraphics copy = new DebugGraphics(graphics.create()); + copy.debugOptions = debugOptions; + return copy; } /** @@ -182,7 +190,10 @@ public class DebugGraphics extends Graphics */ public Graphics create(int x, int y, int width, int height) { - return new DebugGraphics(graphics.create(x, y, width, height)); + DebugGraphics copy = new DebugGraphics(graphics.create(x, y, width, + height)); + copy.debugOptions = debugOptions; + return copy; } /** @@ -282,6 +293,9 @@ public class DebugGraphics extends Graphics */ public void setFont(Font font) { + if ((debugOptions & LOG_OPTION) != 0) + logStream().println(prefix() + " Setting font: " + font); + graphics.setFont(font); } @@ -292,7 +306,7 @@ public class DebugGraphics extends Graphics */ public Color getColor() { - return color; + return graphics.getColor(); } /** @@ -325,6 +339,9 @@ public class DebugGraphics extends Graphics */ 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); } @@ -333,6 +350,9 @@ public class DebugGraphics extends Graphics */ public void setPaintMode() { + if ((debugOptions & LOG_OPTION) != 0) + logStream().println(prefix() + " Setting paint mode"); + graphics.setPaintMode(); } @@ -343,6 +363,9 @@ public class DebugGraphics extends Graphics */ public void setXORMode(Color color) { + if ((debugOptions & LOG_OPTION) != 0) + logStream().println(prefix() + " Setting XOR mode: " + color); + graphics.setXORMode(color); } @@ -366,7 +389,16 @@ public class DebugGraphics extends Graphics */ 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()); } /** @@ -379,6 +411,12 @@ public class DebugGraphics extends Graphics */ 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); } @@ -399,6 +437,9 @@ public class DebugGraphics extends Graphics */ public void setClip(Shape shape) { + if ((debugOptions & LOG_OPTION) != 0) + logStream().println(prefix() + " Setting new clipRect: " + shape); + graphics.setClip(shape); } @@ -424,18 +465,27 @@ public class DebugGraphics extends Graphics */ public void drawRect(int x, int y, int width, int height) { - for (int index = 0; index < (debugFlashCount - 1); ++index) + if ((debugOptions & LOG_OPTION) != 0) { - graphics.setColor(color); - graphics.drawRect(x, y, width, height); - sleep(debugFlashTime); + logStream().println(prefix() + " Drawing rect: " + + new Rectangle(x, y, width, height)); + } - graphics.setColor(debugFlashColor); - graphics.drawRect(x, y, width, height); - sleep(debugFlashTime); + 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.setColor(color); graphics.drawRect(x, y, width, height); } @@ -449,18 +499,27 @@ public class DebugGraphics extends Graphics */ public void fillRect(int x, int y, int width, int height) { - for (int index = 0; index < (debugFlashCount - 1); ++index) + if ((debugOptions & LOG_OPTION) != 0) { - graphics.setColor(color); - graphics.fillRect(x, y, width, height); - sleep(debugFlashTime); + logStream().println(prefix() + " Filling rect: " + + new Rectangle(x, y, width, height)); + } - graphics.setColor(debugFlashColor); - graphics.fillRect(x, y, width, height); - sleep(debugFlashTime); + 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.setColor(color); graphics.fillRect(x, y, width, height); } @@ -474,6 +533,12 @@ public class DebugGraphics extends Graphics */ 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); } @@ -490,6 +555,14 @@ public class DebugGraphics extends Graphics 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); } @@ -506,6 +579,14 @@ public class DebugGraphics extends Graphics 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); } @@ -519,6 +600,12 @@ public class DebugGraphics extends Graphics */ 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); } @@ -533,6 +620,13 @@ public class DebugGraphics extends Graphics */ 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); } @@ -547,6 +641,13 @@ public class DebugGraphics extends Graphics */ 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); } @@ -560,6 +661,12 @@ public class DebugGraphics extends Graphics */ 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); } @@ -573,6 +680,12 @@ public class DebugGraphics extends Graphics */ 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); } @@ -589,6 +702,14 @@ public class DebugGraphics extends Graphics 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); } @@ -605,6 +726,14 @@ public class DebugGraphics extends Graphics 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); } @@ -617,6 +746,12 @@ public class DebugGraphics extends Graphics */ 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); } @@ -629,6 +764,12 @@ public class DebugGraphics extends Graphics */ 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); } @@ -641,6 +782,12 @@ public class DebugGraphics extends Graphics */ 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); } @@ -653,6 +800,12 @@ public class DebugGraphics extends Graphics */ 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); } @@ -666,6 +819,12 @@ public class DebugGraphics extends Graphics 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); } @@ -681,6 +840,9 @@ public class DebugGraphics extends Graphics 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); } @@ -696,18 +858,24 @@ public class DebugGraphics extends Graphics public void drawChars(char[] data, int offset, int length, int x, int y) { - for (int index = 0; index < (debugFlashCount - 1); ++index) + 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); - sleep(debugFlashTime); - - graphics.setColor(debugFlashColor); - graphics.drawChars(data, offset, length, x, y); - sleep(debugFlashTime); } - graphics.setColor(color); graphics.drawChars(data, offset, length, x, y); } @@ -723,6 +891,12 @@ public class DebugGraphics extends Graphics 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); } @@ -741,6 +915,12 @@ public class DebugGraphics extends Graphics 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); } @@ -759,6 +939,13 @@ public class DebugGraphics extends Graphics 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); } @@ -779,6 +966,13 @@ public class DebugGraphics extends Graphics 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); } @@ -802,6 +996,13 @@ public class DebugGraphics extends Graphics 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); } @@ -827,6 +1028,14 @@ public class DebugGraphics extends Graphics 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); } @@ -843,6 +1052,13 @@ public class DebugGraphics extends Graphics 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); } @@ -873,6 +1089,11 @@ public class DebugGraphics extends Graphics 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"); } /** @@ -884,4 +1105,21 @@ public class DebugGraphics extends Graphics { return debugOptions; } + + /** + * Creates and returns the prefix that should be prepended to all logging + * messages. The prefix is made up like this: + * + * <code>Graphics(<counter>-1)</code> 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/DefaultButtonModel.java b/libjava/classpath/javax/swing/DefaultButtonModel.java index f7d09d5780d..7ecf3b85fc6 100644 --- a/libjava/classpath/javax/swing/DefaultButtonModel.java +++ b/libjava/classpath/javax/swing/DefaultButtonModel.java @@ -145,6 +145,7 @@ public class DefaultButtonModel implements ButtonModel, Serializable */ public DefaultButtonModel() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/DefaultCellEditor.java b/libjava/classpath/javax/swing/DefaultCellEditor.java index 00e00864432..39e48551efb 100644 --- a/libjava/classpath/javax/swing/DefaultCellEditor.java +++ b/libjava/classpath/javax/swing/DefaultCellEditor.java @@ -69,7 +69,7 @@ public class DefaultCellEditor private static final long serialVersionUID = 3564035141373880027L; /** - * Delegates a couple of method calls (such as {@link #isCellEditable) + * Delegates a couple of method calls (such as {@link #isCellEditable} * to the component it contains and listens for events that indicate * that editing has stopped. */ @@ -88,12 +88,13 @@ public class DefaultCellEditor */ protected EditorDelegate() { + // Nothing to do here. } /** * setValue * - * @param event TODO + * @param value TODO */ public void setValue(Object value) { @@ -387,7 +388,7 @@ public class DefaultCellEditor /** * getTableCellEditorComponent * - * @param tree TODO + * @param table TODO * @param value TODO * @param isSelected TODO * @param row TODO diff --git a/libjava/classpath/javax/swing/DefaultComboBoxModel.java b/libjava/classpath/javax/swing/DefaultComboBoxModel.java index b48b968d697..ea261a33bbf 100644 --- a/libjava/classpath/javax/swing/DefaultComboBoxModel.java +++ b/libjava/classpath/javax/swing/DefaultComboBoxModel.java @@ -1,5 +1,5 @@ /* DefaultComboBoxModel.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -41,13 +41,14 @@ import java.io.Serializable; import java.util.Arrays; import java.util.Vector; +import javax.swing.event.ListDataEvent; + /** - * The default implementation of {@link MutableComboBoxModel}. - * This model keeps track - * of elements contained in the JComboBox as well as the current combo box - * selection. Whenever selection in the JComboBox changes, the ComboBoxModel - * will fire ListDataEvents to ComboBox's ListDataListeners. + * A model that stores a list of elements and a selected item (which may be + * <code>null</code>). 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 @@ -59,17 +60,17 @@ public class DefaultComboBoxModel extends AbstractListModel private static final long serialVersionUID = 6698657703676921904L; /** - * List containing items in the combo box + * Storage for the elements in the model's list. */ private Vector list; /** - * Currently selected item in the combo box list + * The selected item (<code>null</code> indicates no selection). */ private Object selectedItem = null; /** - * Constructor DefaultComboBoxModel. Create empty JComboBox. + * Creates a new model, initially empty. */ public DefaultComboBoxModel() { @@ -77,64 +78,92 @@ public class DefaultComboBoxModel extends AbstractListModel } /** - * Constructs new DefaultComboBoxModel object and initializes its item list - * to values in the given array. + * 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 + * <code>null</code> if the array length is zero. * - * @param items array containing items of the combo box. + * @param items an array containing items for the model (<code>null</code> + * not permitted). + * + * @throws NullPointerException if <code>items</code> is <code>null</code>. */ public DefaultComboBoxModel(Object[] items) { list = new Vector(Arrays.asList(items)); + if (list.size() > 0) + selectedItem = list.get(0); } /** - * Consturcts new DefaultComboBoxModel object and initializes its item list - * to values in the given vector. + * 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 <code>null</code> if the vector length is zero. * - * @param vector Vector containing items for this combo box. + * @param vector a vector containing items for the model (<code>null</code> + * not permitted). + * + * @throws NullPointerException if <code>vector</code> is <code>null</code>. */ public DefaultComboBoxModel(Vector vector) { this.list = vector; + if (vector.size() > 0) + selectedItem = vector.get(0); } /** - * This method adds element to the combo box list. It fires ListDataEvent - * indicating that component was added to the combo box to all of the - * JComboBox's registered ListDataListeners. + * 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, it is set as the selected item. * - * @param object item to add to the combo box list + * @param object item to add to the model's item list. */ public void addElement(Object object) { list.add(object); - fireIntervalAdded(this, list.size() - 1, list.size()); + fireIntervalAdded(this, list.size() - 1, list.size() - 1); + if (list.size() == 1) + setSelectedItem(object); } /** - * This method removes element at the specified index from the combo box - * list. It fires ListDataEvent indicating that component was removed from - * the combo box list to all of the JComboBox's registered - * ListDataListeners. + * 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 index specifying location of the element to remove in the - * combo box list. + * @param index the index of the item to remove. + * + * @throws ArrayIndexOutOfBoundsException if <code>index</code> is out of + * bounds. */ public void removeElementAt(int index) { + int selected = getIndexOf(selectedItem); list.remove(index); + if (selected == index) // choose a new selected item + { + if (selected > 0) + selectedItem = getElementAt(selected - 1); + else + selectedItem = getElementAt(selected); + } fireIntervalRemoved(this, index, index); } /** - * This method inserts given object to the combo box list at the specified - * index. It fires ListDataEvent indicating that component was inserted to - * the combo box list to all of the JComboBox's registered - * ListDataListeners. + * 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 <code>index</code> is out of + * bounds. + * + * @see #addElement(Object) */ public void insertElementAt(Object object, int index) { @@ -143,11 +172,13 @@ public class DefaultComboBoxModel extends AbstractListModel } /** - * Removes given object from the combo box list. It fires ListDataEvent - * indicating that component was removed from the combo box list to all of - * the JComboBox's registered ListDataListeners. + * 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 Element that will be removed from the combo box list + * @param object the element to remove. */ public void removeElement(Object object) { @@ -157,21 +188,25 @@ public class DefaultComboBoxModel extends AbstractListModel } /** - * Removes all the items from the JComboBox's item list. It fires - * ListDataEvent indicating that all the elements were removed from the - * combo box list to all of the JComboBox's registered ListDataListeners. + * Removes all the items from the model's item list, resets and selected item + * to <code>null</code>, and sends a {@link ListDataEvent} to all registered + * listeners. */ public void removeAllElements() { - list.clear(); - int listSize = getSize(); - fireIntervalAdded(this, 0, listSize); + selectedItem = null; + int size = getSize(); + if (size > 0) + { + list.clear(); + fireIntervalRemoved(this, 0, size - 1); + } } /** - * Returns number of items in the combo box list + * Returns the number of items in the model's item list. * - * @return number of items in the combo box list + * @return The number of items in the model's item list. */ public int getSize() { @@ -179,32 +214,32 @@ public class DefaultComboBoxModel extends AbstractListModel } /** - * Selects given object in the combo box list. This method fires - * ListDataEvent to all registered ListDataListeners of the JComboBox. The - * start and end index of the event is set to -1 to indicate combo box's - * selection has changed, and not its contents. - * - * <p>If the given object is not contained in the combo box list then nothing - * happens.</p> + * 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 item to select in the JComboBox + * @param object the new selected item (<code>null</code> permitted). */ public void setSelectedItem(Object object) { - - // Updates the selected item only if the given object - // is null or in the list (this is how the JDK behaves). - if(object == null || list.contains(object)) { - selectedItem = object; - fireContentsChanged(this, -1, -1); - } - + if (selectedItem == null) + { + if (object == null) + return; + } + else + { + if (selectedItem.equals(object)) + return; + } + selectedItem = object; + fireContentsChanged(this, -1, -1); } /** - * Returns currently selected item in the combo box list + * Returns the selected item. * - * @return currently selected item in the combo box list + * @return The selected item (possibly <code>null</code>). */ public Object getSelectedItem() { @@ -212,24 +247,27 @@ public class DefaultComboBoxModel extends AbstractListModel } /** - * Returns element in the combo box list located at the given index + * Returns the element at the specified index in the model's item list. * - * @param index specifying location of the element in the list + * @param index the element index. * - * @return return element in the combo box list located at the given index + * @return The element at the specified index in the model's item list, or + * <code>null</code> if the <code>index</code> 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 index of the specified object in the combo box list. + * Returns the index of the specified element in the model's item list. * - * @param object element to look for in the combo box list . + * @param object the element. * - * @return Index specifying position of the specified element in combo box - * list. + * @return The index of the specified element in the model's item list. */ public int getIndexOf(Object object) { diff --git a/libjava/classpath/javax/swing/DefaultDesktopManager.java b/libjava/classpath/javax/swing/DefaultDesktopManager.java index 2b8977e9d6d..7f62c948625 100644 --- a/libjava/classpath/javax/swing/DefaultDesktopManager.java +++ b/libjava/classpath/javax/swing/DefaultDesktopManager.java @@ -91,6 +91,7 @@ public class DefaultDesktopManager implements DesktopManager, Serializable */ public DefaultDesktopManager() { + // Nothing to do here. } /** @@ -223,6 +224,7 @@ public class DefaultDesktopManager implements DesktopManager, Serializable } catch (PropertyVetoException e) { + // Do nothing if attempt is vetoed. } } @@ -302,6 +304,7 @@ public class DefaultDesktopManager implements DesktopManager, Serializable } catch (PropertyVetoException e) { + // Do nothing if attempt is vetoed. } } @@ -329,6 +332,7 @@ public class DefaultDesktopManager implements DesktopManager, Serializable } catch (PropertyVetoException e) { + // Do nothing if attempt is vetoed. } } } diff --git a/libjava/classpath/javax/swing/DefaultListCellRenderer.java b/libjava/classpath/javax/swing/DefaultListCellRenderer.java index 5a34ba7aa18..9a8e07071b5 100644 --- a/libjava/classpath/javax/swing/DefaultListCellRenderer.java +++ b/libjava/classpath/javax/swing/DefaultListCellRenderer.java @@ -68,6 +68,7 @@ public class DefaultListCellRenderer extends JLabel { public UIResource() { + super(); } } @@ -124,62 +125,75 @@ public class DefaultListCellRenderer extends JLabel 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/DefaultListSelectionModel.java b/libjava/classpath/javax/swing/DefaultListSelectionModel.java index f8d544d9b20..ce1dfdd79c5 100644 --- a/libjava/classpath/javax/swing/DefaultListSelectionModel.java +++ b/libjava/classpath/javax/swing/DefaultListSelectionModel.java @@ -238,14 +238,32 @@ public class DefaultListSelectionModel implements Cloneable, */ 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; - + return; + int R1 = Math.min(anchorSelectionIndex, oldLeadIndex); int R2 = Math.max(anchorSelectionIndex, oldLeadIndex); int S1 = Math.min(anchorSelectionIndex, leadIndex); @@ -254,8 +272,6 @@ public class DefaultListSelectionModel implements Cloneable, int lo = Math.min(R1, S1); int hi = Math.max(R2, S2); - BitSet oldRange = sel.get(lo, hi+1); - if (isSelectedIndex(anchorSelectionIndex)) { sel.clear(R1, R2+1); @@ -265,10 +281,7 @@ public class DefaultListSelectionModel implements Cloneable, { sel.set(R1, R2+1); sel.clear(S1, S2+1); - } - - BitSet newRange = sel.get(lo, hi+1); - newRange.xor(oldRange); + } int beg = sel.nextSetBit(0), end = -1; for(int i=beg; i >= 0; i=sel.nextSetBit(i+1)) @@ -278,6 +291,27 @@ public class DefaultListSelectionModel implements Cloneable, } /** + * Moves the lead selection index to <code>leadIndex</code> 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 @@ -388,6 +422,9 @@ public class DefaultListSelectionModel implements Cloneable, */ public boolean isSelectedIndex(int a) { + // TODO: Probably throw an exception here? + if (a >= sel.length() || a < 0) + return false; return sel.get(a); } @@ -415,7 +452,7 @@ public class DefaultListSelectionModel implements Cloneable, oldSel = sel.clone(); if (selectionMode == SINGLE_SELECTION) - sel.clear(); + setSelectionInterval(index0, index1); // COMPAT: Like Sun (but not like IBM), we allow calls to // addSelectionInterval when selectionMode is @@ -426,10 +463,7 @@ public class DefaultListSelectionModel implements Cloneable, isSelectedIndex(index1) || isSelectedIndex(Math.max(lo-1,0)) || isSelectedIndex(Math.min(hi+1,sel.size())))) - sel.clear(); - - if (selectionMode == SINGLE_SELECTION) - index0 = index1; + sel.clear(); // We have to update the anchorSelectionIndex and leadSelectionIndex // variables diff --git a/libjava/classpath/javax/swing/DesktopManager.java b/libjava/classpath/javax/swing/DesktopManager.java index 300d66517ba..620c7ffb857 100644 --- a/libjava/classpath/javax/swing/DesktopManager.java +++ b/libjava/classpath/javax/swing/DesktopManager.java @@ -95,7 +95,7 @@ public interface DesktopManager * This method should give focus to the JInternalFrame and its default focus * owner. * - * @param frame The JInternalFrame to activate. + * @param vframe The JInternalFrame to activate. */ void activateFrame(JInternalFrame vframe); diff --git a/libjava/classpath/javax/swing/FocusManager.java b/libjava/classpath/javax/swing/FocusManager.java index 179fa6f82d2..a2109ee06c6 100644 --- a/libjava/classpath/javax/swing/FocusManager.java +++ b/libjava/classpath/javax/swing/FocusManager.java @@ -38,10 +38,19 @@ 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 @@ -54,46 +63,409 @@ public abstract class FocusManager extends DefaultKeyboardFocusManager { /** - * DisabledFocusManager + * 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) */ - static class DisabledFocusManager + 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 <code>true</code> if the event has been dispatched, + * <code>false</code> otherwise + */ + public boolean dispatchEvent(AWTEvent ev) + { + return wrapped.dispatchEvent(ev); + } + + /** + * Wraps {@link DefaultKeyboardFocusManager#dispatchKeyEvent(KeyEvent)}. + * + * @param ev the event to dispatch + * + * @return <code>true</code> if the event has been dispatched, + * <code>false</code> 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); + } /** - * Constructor DisabledFocusManager + * Wraps + * {@link KeyboardFocusManager#removeVetoableChangeListener(String, VetoableChangeListener)}. + * + * @param n the property name + * @param l the listener */ - DisabledFocusManager() + public void removeVetoableChangeListener(String n, VetoableChangeListener l) { - // TODO + wrapped.removeVetoableChangeListener(n, l); } /** - * processKeyEvent - * @param component TODO - * @param event TODO + * Wraps + * {@link KeyboardFocusManager#setDefaultFocusTraversalKeys(int, Set)}. + * + * @param id the ID + * @param k the keystrokes */ - public void processKeyEvent(Component component, KeyEvent event) + public void setDefaultFocusTraversalKeys(int id, Set k) { - // TODO + wrapped.setDefaultFocusTraversalKeys(id, k); } /** - * focusNextComponent - * @param component TODO + * Wraps {@link KeyboardFocusManager#setDefaultFocusTraversalPolicy(FocusTraversalPolicy)}. + * + * @param p the focus traversal policy */ - public void focusNextComponent(Component component) + public void setDefaultFocusTraversalPolicy(FocusTraversalPolicy p) { - // TODO + wrapped.setDefaultFocusTraversalPolicy(p); } /** - * focusPreviousComponent - * @param value0 TODO + * Wraps + * {@link KeyboardFocusManager#setGlobalCurrentFocusCycleRoot(Container)}. + * + * @param r the focus cycle root */ - public void focusPreviousComponent(Component value0) + public void setGlobalCurrentFocusCycleRoot(Container r) { - // TODO + wrapped.setGlobalCurrentFocusCycleRoot(r); } } @@ -117,20 +489,9 @@ public abstract class FocusManager */ public static FocusManager getCurrentManager() { - KeyboardFocusManager fm = - KeyboardFocusManager.getCurrentKeyboardFocusManager(); - if (fm instanceof FocusManager) - return (FocusManager) fm; - else - { - System.err.println("The Swing FocusManager API has been obsoleted by"); - System.err.println("the new KeyboardFocusManager system."); - System.err.println("You should either not use the Swing FocusManager"); - System.err.println("API or set the system property"); - System.err.println - ("gnu.java.awt.FocusManager=javax.swing.FocusManager"); - } - return null; + KeyboardFocusManager m = + KeyboardFocusManager.getCurrentKeyboardFocusManager(); + return new WrappingFocusManager(m); } /** diff --git a/libjava/classpath/javax/swing/ImageIcon.java b/libjava/classpath/javax/swing/ImageIcon.java index b650cd81f23..b6ed949d8dc 100644 --- a/libjava/classpath/javax/swing/ImageIcon.java +++ b/libjava/classpath/javax/swing/ImageIcon.java @@ -73,6 +73,7 @@ public class ImageIcon */ protected AccessibleImageIcon() { + // Nothing to do here. } /** @@ -204,7 +205,10 @@ public class ImageIcon private static final long serialVersionUID = 532615968316031794L; /** A dummy Component that is used in the MediaTracker. */ - protected static Component component = new Component(){}; + protected static Component component = new Component() + { + // No need to implement this. + }; /** The MediaTracker used to monitor the loading of images. */ protected static MediaTracker tracker = new MediaTracker(component); @@ -227,6 +231,7 @@ public class ImageIcon */ public ImageIcon() { + // Nothing to do here. } /** @@ -417,7 +422,7 @@ public class ImageIcon } catch (InterruptedException ex) { - ; // ignore this for now + // Ignore this for now. } finally { diff --git a/libjava/classpath/javax/swing/InputMap.java b/libjava/classpath/javax/swing/InputMap.java index a7ec38c4117..cc65dfeed3e 100644 --- a/libjava/classpath/javax/swing/InputMap.java +++ b/libjava/classpath/javax/swing/InputMap.java @@ -171,8 +171,12 @@ public class InputMap */ public KeyStroke[] keys() { - KeyStroke[] array = new KeyStroke[size()]; - return (KeyStroke[]) inputMap.keySet().toArray(array); + if (size() != 0) + { + KeyStroke[] array = new KeyStroke[size()]; + return (KeyStroke[]) inputMap.keySet().toArray(array); + } + return null; } /** @@ -189,7 +193,9 @@ public class InputMap set.addAll(Arrays.asList(parent.allKeys())); set.addAll(inputMap.keySet()); - KeyStroke[] array = new KeyStroke[size()]; + 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 index 8e02ab813a3..eeb81b5d503 100644 --- a/libjava/classpath/javax/swing/InputVerifier.java +++ b/libjava/classpath/javax/swing/InputVerifier.java @@ -53,6 +53,7 @@ public abstract class InputVerifier */ public InputVerifier() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/JApplet.java b/libjava/classpath/javax/swing/JApplet.java index cafb2dabbb8..3ee1046c802 100644 --- a/libjava/classpath/javax/swing/JApplet.java +++ b/libjava/classpath/javax/swing/JApplet.java @@ -47,6 +47,7 @@ import java.awt.Graphics; import java.awt.LayoutManager; import java.awt.event.KeyEvent; +import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; /** @@ -55,8 +56,28 @@ import javax.accessibility.AccessibleContext; * @author original author unknown */ public class JApplet extends Applet - implements RootPaneContainer + implements RootPaneContainer, Accessible { + /** + * Provides accessibility support for <code>JApplet</code>. + */ + protected class AccessibleJApplet extends Applet.AccessibleApplet + { + /** + * Creates a new instance of <code>AccessibleJApplet</code>. + */ + public AccessibleJApplet() + { + super(); + // Nothing to do here. + } + } + + /** + * The accessible context for this <code>JApplet</code>. + */ + protected AccessibleContext accessibleContext; + private static final long serialVersionUID = 7269359214497372587L; protected JRootPane rootPane; @@ -64,20 +85,13 @@ public class JApplet extends Applet /** * @specnote rootPaneCheckingEnabled is false to comply with J2SE 5.0 */ - protected boolean rootPaneCheckingEnabled=false; - - /** - * Tells us if we're in the initialization stage. - * If so, adds go to top-level Container, otherwise they go - * to the content pane for this container - */ - private boolean initStageDone = false; + protected boolean rootPaneCheckingEnabled = false; public JApplet() { super.setLayout(new BorderLayout(1, 1)); getRootPane(); // Will do set/create. - initStageDone = true; // Init stage is now over. + setRootPaneCheckingEnabled(true); // Init stage is now over. } public Dimension getPreferredSize() @@ -89,13 +103,8 @@ public class JApplet extends Applet { // Check if we're in initialization stage. If so, call super.setLayout // otherwise, valid calls go to the content pane - if (initStageDone) - { - if (isRootPaneCheckingEnabled()) - throw new Error("Cannot set layout. Use getContentPane().setLayout()" - + "instead."); - getContentPane().setLayout(manager); - } + if (isRootPaneCheckingEnabled()) + getContentPane().setLayout(manager); else super.setLayout(manager); } @@ -155,20 +164,17 @@ public class JApplet extends Applet { // If we're adding in the initialization stage use super.add. // Otherwise pass the add onto the content pane. - if (!initStageDone) - super.addImpl(comp, constraints, index); + if (isRootPaneCheckingEnabled()) + getContentPane().add(comp, constraints, index); else - { - if (isRootPaneCheckingEnabled()) - throw new Error("Do not use add() on JApplet directly. Use " - + "getContentPane().add() instead"); - getContentPane().add(comp, constraints, index); - } + super.addImpl(comp, constraints, index); } public AccessibleContext getAccessibleContext() { - return null; + if (accessibleContext == null) + accessibleContext = new AccessibleJApplet(); + return accessibleContext; } public JMenuBar getJMenuBar() diff --git a/libjava/classpath/javax/swing/JButton.java b/libjava/classpath/javax/swing/JButton.java index 5653fbf42f1..ff0ecfccfd4 100644 --- a/libjava/classpath/javax/swing/JButton.java +++ b/libjava/classpath/javax/swing/JButton.java @@ -75,9 +75,6 @@ public class JButton extends AbstractButton boolean def; boolean is_def; - /** The AccessibleContext for this JButton. */ - AccessibleJButton accessibleContext; - public JButton() { this(null, null); @@ -166,6 +163,10 @@ public class JButton extends AbstractButton */ public void removeNotify() { + JRootPane root = SwingUtilities.getRootPane(this); + if (root != null && root.getDefaultButton() == this) + root.setDefaultButton(null); + super.removeNotify(); } public void setDefaultCapable(boolean defaultCapable) diff --git a/libjava/classpath/javax/swing/JCheckBox.java b/libjava/classpath/javax/swing/JCheckBox.java index a743308dcca..74fda8f6dbe 100644 --- a/libjava/classpath/javax/swing/JCheckBox.java +++ b/libjava/classpath/javax/swing/JCheckBox.java @@ -38,7 +38,9 @@ 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 @@ -54,8 +56,32 @@ import javax.accessibility.AccessibleContext; * * @author Ronald Veldema (rveldema@cs.vu.nl) */ -public class JCheckBox extends JToggleButton +public class JCheckBox extends JToggleButton implements Accessible { + + /** + * Provides accessibility support for <code>JCheckBox</code>. + */ + protected class AccessibleJCheckBox extends AccessibleJToggleButton + { + /** + * Creates a new instance of <code>AccessibleJCheckBox</code>. + */ + public AccessibleJCheckBox() + { + // Nothing to do here. + } + + /** + * Returns the accessble role of <code>JCheckBox</code>, + * {@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 = @@ -71,61 +97,47 @@ public class JCheckBox extends JToggleButton public JCheckBox() { - super(); - init(); + this(null, null, false); } public JCheckBox(Action action) { super(action); - init(); } public JCheckBox(Icon icon) { - super(icon); - init(); + this(null, icon, false); } public JCheckBox(Icon icon, boolean selected) { - super(icon, selected); - init(); + this(null, icon, selected); } public JCheckBox(String text) { - super(text); - init(); + this(text, null, false); } public JCheckBox(String text, boolean selected) { - super(text, selected); - init(); + this(text, null, selected); } public JCheckBox(String text, Icon icon) { - super(text, icon); - init(); + this(text, icon, false); } public JCheckBox(String text, Icon icon, boolean selected) { super(text, icon, selected); - init(); + setHorizontalAlignment(LEADING); + setBorderPainted(false); } /** - * Gets the AccessibleContext associated with this JCheckBox. - */ - public AccessibleContext getAccessibleContext() - { - return null; - } - - /** * Returns a string that specifies the name of the Look and Feel class * that renders this component. */ @@ -149,4 +161,16 @@ public class JCheckBox extends JToggleButton firePropertyChange("borderPaintedFlat", borderPaintedFlat, newValue); borderPaintedFlat = newValue; } + + /** + * Returns the accessible context for this <code>JCheckBox</code>. + * + * @return the accessible context for this <code>JCheckBox</code> + */ + 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 index f9dd56500fe..815244259be 100644 --- a/libjava/classpath/javax/swing/JCheckBoxMenuItem.java +++ b/libjava/classpath/javax/swing/JCheckBoxMenuItem.java @@ -38,9 +38,6 @@ exception statement from your version. */ package javax.swing; -import java.io.IOException; -import java.io.ObjectOutputStream; - import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleRole; @@ -48,8 +45,9 @@ import javax.accessibility.AccessibleRole; /** * A menu item that displays a checkbox. Its behaviour is very similar * to {@link JCheckBox}. Just like the <code>JCheckBox</code>, user can check - * and uncheck this menu item by clicking on it. Also {@link #setSelected()} - * and {@link #setState()} can be use used for the same purpose. + * and uncheck this menu item by clicking on it. Also + * {@link AbstractButton#setSelected} and {@link #setState} can be use used + * for the same purpose. * <code>JCheckBoxMenuItem</code> uses * <code>ToggleButtonModel</code> to keep track of its selection. * @@ -152,10 +150,6 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, this.setVisible(true); } - private void writeObject(ObjectOutputStream stream) throws IOException - { - } - /** * This method returns a name to identify which look and feel class will be * the UI delegate for the menuItem. @@ -248,6 +242,7 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, */ protected AccessibleJCheckBoxMenuItem() { + // Nothing to do here. } public AccessibleRole getAccessibleRole() diff --git a/libjava/classpath/javax/swing/JColorChooser.java b/libjava/classpath/javax/swing/JColorChooser.java index 4016b82f3fd..a9650ffb7e0 100644 --- a/libjava/classpath/javax/swing/JColorChooser.java +++ b/libjava/classpath/javax/swing/JColorChooser.java @@ -87,6 +87,7 @@ public class JColorChooser extends JComponent implements Accessible */ protected AccessibleJColorChooser() { + // Nothing to do here. } /** @@ -247,6 +248,7 @@ public class JColorChooser extends JComponent implements Accessible } catch (InterruptedException e) { + // TODO: Should this be handled? } } diff --git a/libjava/classpath/javax/swing/JComboBox.java b/libjava/classpath/javax/swing/JComboBox.java index 47d18323a25..cd30840a6aa 100644 --- a/libjava/classpath/javax/swing/JComboBox.java +++ b/libjava/classpath/javax/swing/JComboBox.java @@ -46,8 +46,6 @@ import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.io.IOException; -import java.io.ObjectOutputStream; import java.util.Vector; import javax.accessibility.Accessible; @@ -58,6 +56,7 @@ import javax.accessibility.AccessibleSelection; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; import javax.swing.event.PopupMenuListener; +import javax.swing.event.PopupMenuEvent; import javax.swing.plaf.ComboBoxUI; /** @@ -212,10 +211,6 @@ public class JComboBox extends JComponent implements ItemSelectable, this(new DefaultComboBoxModel()); } - private void writeObject(ObjectOutputStream stream) throws IOException - { - } - /** * This method returns true JComboBox is editable and false otherwise * @@ -310,7 +305,8 @@ public class JComboBox extends JComponent implements ItemSelectable, // 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); } @@ -551,14 +547,37 @@ public class JComboBox extends JComponent implements ItemSelectable, 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 <code>null</code>). + * + * @since 1.4 + * @see #setPrototypeDisplayValue(Object) + */ public Object getPrototypeDisplayValue() { return prototypeDisplayValue; } - public void setPrototypeDisplayValue(Object newPrototypeDisplayValue) + /** + * 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 <code>prototypeDisplayValue</code>) is sent to all registered + * listeners. + * + * @param value the new value (<code>null</code> permitted). + * + * @since 1.4 + * @see #getPrototypeDisplayValue() + */ + public void setPrototypeDisplayValue(Object value) { - prototypeDisplayValue = newPrototypeDisplayValue; + Object oldValue = prototypeDisplayValue; + prototypeDisplayValue = value; + firePropertyChange("prototypeDisplayValue", oldValue, value); } /** @@ -820,6 +839,47 @@ public class JComboBox extends JComponent implements ItemSelectable, } /** + * Fires a popupMenuCanceled() event to all <code>PopupMenuListeners</code>. + * + * 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 + * <code>PopupMenuListeners</code>. + * + * 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 + * <code>PopupMenuListeners</code>. + * + * 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 @@ -836,8 +896,9 @@ public class JComboBox extends JComponent implements ItemSelectable, // Fire ItemEvent to indicate that new item is selected Object newSelection = getSelectedItem(); - fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, - newSelection, ItemEvent.SELECTED)); + if (newSelection != null) + fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, + newSelection, ItemEvent.SELECTED)); // Fire Action Event to JComboBox's registered listeners fireActionEvent(); @@ -961,19 +1022,19 @@ public class JComboBox extends JComponent implements ItemSelectable, */ public void processKeyEvent(KeyEvent e) { - } - - /** - * This method always returns false to indicate that JComboBox itself is - * not focus traversable. - * - * @return false to indicate that JComboBox itself is not focus traversable. - * - * @deprecated - */ - public boolean isFocusTraversable() - { - return false; + if (e.getKeyCode() == KeyEvent.VK_TAB) + setPopupVisible(false); + else if (keySelectionManager != null) + { + int i = keySelectionManager.selectionForKey(e.getKeyChar(), + getModel()); + if (i >= 0) + setSelectedIndex(i); + else + super.processKeyEvent(e); + } + else + super.processKeyEvent(e); } /** @@ -983,6 +1044,7 @@ public class JComboBox extends JComponent implements ItemSelectable, */ public void setKeySelectionManager(KeySelectionManager aManager) { + keySelectionManager = aManager; } /** @@ -1147,6 +1209,7 @@ public class JComboBox extends JComponent implements ItemSelectable, protected AccessibleJComboBox() { + // Nothing to do here. } public int getAccessibleChildrenCount() @@ -1206,18 +1269,22 @@ public class JComboBox extends JComponent implements ItemSelectable, public void addAccessibleSelection(int value0) { + // TODO: Implement this properly. } public void removeAccessibleSelection(int value0) { + // TODO: Implement this properly. } public void clearAccessibleSelection() { + // TODO: Implement this properly. } public void selectAllAccessibleSelection() { + // TODO: Implement this properly. } } } diff --git a/libjava/classpath/javax/swing/JComponent.java b/libjava/classpath/javax/swing/JComponent.java index dc7689b0930..021a2a34080 100644 --- a/libjava/classpath/javax/swing/JComponent.java +++ b/libjava/classpath/javax/swing/JComponent.java @@ -44,6 +44,7 @@ import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; +import java.awt.EventQueue; import java.awt.FlowLayout; import java.awt.FocusTraversalPolicy; import java.awt.Font; @@ -53,6 +54,7 @@ import java.awt.Image; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; +import java.awt.Shape; import java.awt.Window; import java.awt.dnd.DropTarget; import java.awt.event.ActionEvent; @@ -64,7 +66,6 @@ import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.geom.Rectangle2D; -import java.awt.image.ImageObserver; import java.awt.peer.LightweightPeer; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -83,6 +84,8 @@ import javax.accessibility.AccessibleKeyBinding; import javax.accessibility.AccessibleRole; 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; @@ -121,9 +124,18 @@ public abstract class JComponent extends Container implements Serializable protected class AccessibleFocusHandler implements FocusListener { - protected AccessibleFocusHandler(){} - public void focusGained(FocusEvent event){} - public void focusLost(FocusEvent valevent){} + protected AccessibleFocusHandler() + { + // TODO: Implement this properly. + } + public void focusGained(FocusEvent event) + { + // TODO: Implement this properly. + } + public void focusLost(FocusEvent valevent) + { + // TODO: Implement this properly. + } } /** @@ -132,9 +144,18 @@ public abstract class JComponent extends Container implements Serializable protected class AccessibleContainerHandler implements ContainerListener { - protected AccessibleContainerHandler() {} - public void componentAdded(ContainerEvent event) {} - public void componentRemoved(ContainerEvent valevent) {} + protected AccessibleContainerHandler() + { + // TODO: Implement this properly. + } + public void componentAdded(ContainerEvent event) + { + // TODO: Implement this properly. + } + public void componentRemoved(ContainerEvent valevent) + { + // TODO: Implement this properly. + } } private static final long serialVersionUID = -7047089700479897799L; @@ -142,39 +163,217 @@ public abstract class JComponent extends Container implements Serializable protected ContainerListener accessibleContainerHandler; protected FocusListener accessibleFocusHandler; - protected AccessibleJComponent() {} - public void addPropertyChangeListener(PropertyChangeListener listener) {} - public void removePropertyChangeListener(PropertyChangeListener listener) {} - public int getAccessibleChildrenCount() { return 0; } - public Accessible getAccessibleChild(int value0) { return null; } - public AccessibleStateSet getAccessibleStateSet() { return null; } - public String getAccessibleName() { return null; } - public String getAccessibleDescription() { return null; } - public AccessibleRole getAccessibleRole() { return null; } - protected String getBorderTitle(Border value0) { return null; } - public String getToolTipText() { return null; } - public String getTitledBorderText() { return null; } - public AccessibleKeyBinding getAccessibleKeyBinding() { return null; } + /** + * Manages the property change listeners; + */ + private SwingPropertyChangeSupport changeSupport; + + protected AccessibleJComponent() + { + changeSupport = new SwingPropertyChangeSupport(this); + } + + /** + * Adds a property change listener to the list of registered listeners. + * + * @param listener the listener to add + */ + public void addPropertyChangeListener(PropertyChangeListener listener) + { + changeSupport.addPropertyChangeListener(listener); + } + + /** + * Removes a propery change listener from the list of registered listeners. + * + * @param listener the listener to remove + */ + public void removePropertyChangeListener(PropertyChangeListener listener) + { + changeSupport.removePropertyChangeListener(listener); + } + + /** + * Returns the number of accessible children of this object. + * + * @return the number of accessible children of this object + */ + public int getAccessibleChildrenCount() + { + int count = 0; + Component[] children = getComponents(); + for (int i = 0; i < children.length; ++i) + { + if (children[i] instanceof Accessible) + count++; + } + return count; + } + + /** + * Returns the accessible child component at index <code>i</code>. + * + * @param i the index of the accessible child to return + * + * @return the accessible child component at index <code>i</code> + */ + public Accessible getAccessibleChild(int i) + { + int index = 0; + Component[] children = getComponents(); + Accessible found = null; + for (int j = 0; index != i; j++) + { + if (children[j] instanceof Accessible) + index++; + if (index == i) + found = (Accessible) children[index]; + } + // TODO: Figure out what to do when i is not a valid index. + return found; + } + + /** + * Returns the accessible state set of this component. + * + * @return the accessible state set of this component + */ + public AccessibleStateSet getAccessibleStateSet() + { + // FIXME: Figure out which states should be set here, and which are + // inherited from the super class. + return super.getAccessibleStateSet(); + } + + /** + * 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 <code>null</code> if this + * object has no name + */ + public String getAccessibleName() + { + // TODO: Figure out what exactly to return here. It's possible that this + // method simply should return null. + return null; + } + + /** + * Returns the localized description of this object. + * + * @return the localized description of this object or <code>null</code> + * if this object has no description + */ + public String getAccessibleDescription() + { + // TODO: Figure out what exactly to return here. It's possible that this + // method simply should return null. + return null; + } + + /** + * Returns the accessible role of this component. + * + * @return the accessible role of this component + * + * @see AccessibleRole + */ + public AccessibleRole getAccessibleRole() + { + // TODO: Check if this is correct. + return AccessibleRole.SWING_COMPONENT; + } + + /** + * Recursivly searches a border hierarchy (starting at <code>border) for + * a titled border and returns the title if one is found, <code>null</code> + * 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 <code>null</code>. + * + * @return the title of the border of this accessible component if + * this component has a titled border, otherwise returns + * <code>null</code> + */ + public String getTitledBorderText() + { + return getBorderTitle(getBorder()); + } + + /** + * Returns the keybindings associated with this accessible component or + * <code>null</code> if the component does not support key bindings. + * + * @return the keybindings associated with this accessible component + */ + public AccessibleKeyBinding getAccessibleKeyBinding() + { + // TODO: Implement this properly. + return null; + } } /** * An explicit value for the component's preferred size; if not set by a * user, this is calculated on the fly by delegating to the {@link - * ComponentUI.getPreferredSize} method on the {@link #ui} property. + * ComponentUI#getPreferredSize} method on the {@link #ui} property. */ Dimension preferredSize; /** * An explicit value for the component's minimum size; if not set by a * user, this is calculated on the fly by delegating to the {@link - * ComponentUI.getMinimumSize} method on the {@link #ui} property. + * ComponentUI#getMinimumSize} method on the {@link #ui} property. */ Dimension minimumSize; /** * An explicit value for the component's maximum size; if not set by a * user, this is calculated on the fly by delegating to the {@link - * ComponentUI.getMaximumSize} method on the {@link #ui} property. + * ComponentUI#getMaximumSize} method on the {@link #ui} property. */ Dimension maximumSize; @@ -191,7 +390,7 @@ public abstract class JComponent extends Container implements Serializable * @see javax.swing.OverlayLayout * @see javax.swing.BoxLayout */ - float alignmentX = 0.5f; + float alignmentX = -1.0F; /** * A value between 0.0 and 1.0 indicating the preferred vertical @@ -206,7 +405,7 @@ public abstract class JComponent extends Container implements Serializable * @see javax.swing.OverlayLayout * @see javax.swing.BoxLayout */ - float alignmentY = 0.5f; + float alignmentY = -1.0F; /** * The border painted around this component. @@ -219,14 +418,14 @@ public abstract class JComponent extends Container implements Serializable * The text to show in the tooltip associated with this component. * * @see #setToolTipText - * @see #getToolTipText + * @see #getToolTipText() */ String toolTipText; /** * <p>Whether to double buffer this component when painting. This flag - * should generally be <code>false</code>, except for top level - * components such as {@link JFrame} or {@link JApplet}.</p> + * should generally be <code>true</code>, to ensure good painting + * performance.</p> * * <p>All children of a double buffered component are painted into the * double buffer automatically, so only the top widget in a window needs @@ -234,22 +433,21 @@ public abstract class JComponent extends Container implements Serializable * * @see #setDoubleBuffered * @see #isDoubleBuffered - * @see #paintLock * @see #paint */ - boolean doubleBuffered = false; + 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}. + * {@link DebugGraphics#NONE_OPTION}, {@link DebugGraphics#LOG_OPTION}, + * {@link DebugGraphics#FLASH_OPTION}, or {@link + * DebugGraphics#BUFFERED_OPTION}. * - * @see setDebugGraphicsOptions - * @see getDebugGraphicsOptions + * @see #setDebugGraphicsOptions + * @see #getDebugGraphicsOptions * @see DebugGraphics - * @see getComponentGraphics + * @see #getComponentGraphics */ int debugGraphicsOptions; @@ -299,7 +497,7 @@ public abstract class JComponent extends Container implements Serializable * try to request focus, but the request might fail. Thus it is only * a hint guiding swing's behavior. * - * @see #requestFocus + * @see #requestFocus() * @see #isRequestFocusEnabled * @see #setRequestFocusEnabled */ @@ -312,12 +510,18 @@ public abstract class JComponent extends Container implements Serializable * 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 + * @see #setAutoscrolls + * @see #getAutoscrolls */ boolean autoscrolls = false; /** + * Indicates whether the current paint call is already double buffered or + * not. + */ + static boolean isPaintingDoubleBuffered = false; + + /** * Listeners for events other than {@link PropertyChangeEvent} are * handled by this listener list. PropertyChangeEvents are handled in * {@link #changeSupport}. @@ -342,7 +546,7 @@ public abstract class JComponent extends Container implements Serializable private InputMap inputMap_whenFocused; private InputMap inputMap_whenAncestorOfFocused; - private InputMap inputMap_whenInFocusedWindow; + private ComponentInputMap inputMap_whenInFocusedWindow; private ActionMap actionMap; /** @since 1.3 */ private boolean verifyInputWhenFocusTarget; @@ -350,16 +554,17 @@ public abstract class JComponent extends Container implements Serializable private TransferHandler transferHandler; - /** - * A lock held during recursive painting; this is used to serialize - * access to the double buffer, and also to select the "top level" - * object which should acquire the double buffer in a given widget - * tree (which may have multiple double buffered children). - * - * @see #doubleBuffered - * @see #paint + /** + * Indicates if this component is currently painting a tile or not. + */ + private boolean paintingTile; + + /** + * 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 final Object paintLock = new Object(); + private static transient Rectangle rectCache; /** * The default locale of the component. @@ -404,6 +609,13 @@ public abstract class JComponent extends Container implements Serializable public static final int WHEN_IN_FOCUSED_WINDOW = 2; /** + * Indicates if this component is completely dirty or not. This is used + * by the RepaintManager's + * {@link RepaintManager#isCompletelyDirty(JComponent)} method. + */ + boolean isCompletelyDirty = false; + + /** * Creates a new <code>JComponent</code> instance. */ public JComponent() @@ -452,7 +664,9 @@ public abstract class JComponent extends Container implements Serializable /** * Add a client property <code>value</code> to this component, associated * with <code>key</code>. If there is an existing client property - * associated with <code>key</code>, it will be replaced. + * associated with <code>key</code>, it will be replaced. A + * {@link PropertyChangeEvent} is sent to registered listeners (with the + * name of the property being <code>key.toString()</code>). * * @param key The key of the client property association to add * @param value The value of the client property association to add @@ -463,10 +677,13 @@ public abstract class JComponent extends Container implements Serializable */ public final void putClientProperty(Object key, Object value) { + Hashtable t = getClientProperties(); + Object old = t.get(key); if (value != null) - getClientProperties().put(key, value); + t.put(key, value); else - getClientProperties().remove(key); + t.remove(key); + firePropertyChange(key.toString(), old, value); } /** @@ -771,7 +988,8 @@ public abstract class JComponent extends Container implements Serializable { VetoableChangeListener[] listeners = getVetoableChangeListeners(); - PropertyChangeEvent evt = new PropertyChangeEvent(this, propertyName, oldValue, newValue); + PropertyChangeEvent evt = + new PropertyChangeEvent(this, propertyName, oldValue, newValue); for (int i = 0; i < listeners.length; i++) listeners[i].vetoableChange(evt); @@ -797,7 +1015,12 @@ public abstract class JComponent extends Container implements Serializable */ public float getAlignmentX() { - return alignmentX; + float ret = alignmentX; + if (alignmentX < 0) + // alignment has not been set explicitly. + ret = super.getAlignmentX(); + + return ret; } /** @@ -810,7 +1033,12 @@ public abstract class JComponent extends Container implements Serializable */ public float getAlignmentY() { - return alignmentY; + float ret = alignmentY; + if (alignmentY < 0) + // alignment has not been set explicitly. + ret = super.getAlignmentY(); + + return ret; } /** @@ -832,9 +1060,13 @@ public abstract class JComponent extends Container implements Serializable */ public void setBorder(Border newBorder) { - Border oldBorder = border; + Border oldBorder = getBorder(); + if (oldBorder == newBorder) + return; + border = newBorder; firePropertyChange("border", oldBorder, newBorder); + repaint(); } /** @@ -885,10 +1117,19 @@ public abstract class JComponent extends Container implements Serializable * @see #paint */ protected Graphics getComponentGraphics(Graphics g) - { - g.setFont (this.getFont()); - g.setColor (this.getForeground()); - return 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; } /** @@ -901,7 +1142,19 @@ public abstract class JComponent extends Container implements Serializable */ public int getDebugGraphicsOptions() { - return 0; + 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; } /** @@ -1298,6 +1551,7 @@ public abstract class JComponent extends Container implements Serializable */ public void grabFocus() { + // TODO: Implement this properly. } /** @@ -1366,13 +1620,16 @@ public abstract class JComponent extends Container implements Serializable } /** - * Return <code>true</code> if this component is currently painting a tile. + * Return <code>true</code> if this component is currently painting a tile, + * this means that paint() is called again on another child component. This + * method returns <code>false</code> if this component does not paint a tile + * or if the last tile is currently painted. * - * @return Whether the component is painting a tile + * @return whether the component is painting a tile */ public boolean isPaintingTile() { - return false; + return paintingTile; } /** @@ -1406,16 +1663,6 @@ public abstract class JComponent extends Container implements Serializable * RepaintManager}. Client code should usually call {@link #repaint()} to * trigger painting.</p> * - * <p>This method will acquire a double buffer from the {@link - * RepaintManager} if the component's {@link #doubleBuffered} property is - * <code>true</code> and the <code>paint</code> call is the - * <em>first</em> recursive <code>paint</code> call inside swing.</p> - * - * <p>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 <code>paint</code>.</p> - * * <p>The body of the <code>paint</code> call involves calling {@link * #paintComponent}, {@link #paintBorder}, and {@link #paintChildren} in * order. If you want to customize painting behavior, you should override @@ -1431,32 +1678,30 @@ public abstract class JComponent extends Container implements Serializable */ public void paint(Graphics g) { - Graphics g2 = g; - Image doubleBuffer = null; RepaintManager rm = RepaintManager.currentManager(this); - - if (isDoubleBuffered() - && (rm.isDoubleBufferingEnabled()) - && (! Thread.holdsLock(paintLock))) - { - doubleBuffer = rm.getOffscreenBuffer(this, getWidth(), getHeight()); - } - - synchronized (paintLock) + // 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 (!isPaintingDoubleBuffered && isDoubleBuffered() + && rm.isDoubleBufferingEnabled()) + paintDoubleBuffered(g); + else { - if (doubleBuffer != null) - { - g2 = doubleBuffer.getGraphics(); - g2.setClip(g.getClipBounds()); - } - - g2 = getComponentGraphics(g2); + if (g.getClip() == null) + g.setClip(0, 0, getWidth(), getHeight()); + Graphics g2 = getComponentGraphics(g); paintComponent(g2); paintBorder(g2); paintChildren(g2); - - if (doubleBuffer != null) - g.drawImage(doubleBuffer, 0, 0, (ImageObserver) null); + Rectangle clip = g2.getClipBounds(); + if (clip.x == 0 && clip.y == 0 && clip.width == getWidth() + && clip.height == getHeight()) + RepaintManager.currentManager(this).markCompletelyClean(this); } } @@ -1495,7 +1740,69 @@ public abstract class JComponent extends Container implements Serializable */ protected void paintChildren(Graphics g) { - super.paint(g); + Shape originalClip = g.getClip(); + Rectangle inner = SwingUtilities.calculateInnerArea(this, rectCache); + g.clipRect(inner.x, inner.y, inner.width, inner.height); + Component[] children = getComponents(); + + // Find the bottommost component that needs to be painted. This is a + // component that completely covers the current clip and is opaque. In + // this case we don't need to paint the components below it. + int startIndex = children.length - 1; + // No need to check for overlapping components when this component is + // optimizedDrawingEnabled (== it tiles its children). + if (! isOptimizedDrawingEnabled()) + { + Rectangle clip = g.getClipBounds(); + for (int i = 0; i < children.length; i++) + { + Rectangle childBounds = children[i].getBounds(); + if (children[i].isOpaque() + && SwingUtilities.isRectangleContainingRectangle(childBounds, + g.getClipBounds())) + { + startIndex = i; + break; + } + } + } + // paintingTile becomes true just before we start painting the component's + // children. + paintingTile = true; + for (int i = startIndex; i >= 0; --i) + { + // paintingTile must be set to false before we begin to start painting + // the last tile. + if (i == 0) + paintingTile = false; + + if (!children[i].isVisible()) + continue; + + Rectangle bounds = children[i].getBounds(rectCache); + Rectangle oldClip = g.getClipBounds(); + if (oldClip == null) + oldClip = bounds; + + if (!g.hitClip(bounds.x, bounds.y, bounds.width, bounds.height)) + continue; + + boolean translated = false; + try + { + g.clipRect(bounds.x, bounds.y, bounds.width, bounds.height); + g.translate(bounds.x, bounds.y); + translated = true; + children[i].paint(g); + } + finally + { + if (translated) + g.translate(-bounds.x, -bounds.y); + g.setClip(oldClip); + } + } + g.setClip(originalClip); } /** @@ -1518,7 +1825,7 @@ public abstract class JComponent extends Container implements Serializable Graphics g2 = g; if (!(g instanceof Graphics2D)) g2 = g.create(); - ui.update(getComponentGraphics(g2), this); + ui.update(g2, this); if (!(g instanceof Graphics2D)) g2.dispose(); } @@ -1544,24 +1851,93 @@ public abstract class JComponent extends Container implements Serializable * that root pane. This method is called from the {@link RepaintManager} * and should always be called within the painting thread. * + * <p>This method will acquire a double buffer from the {@link + * RepaintManager} if the component's {@link #doubleBuffered} property is + * <code>true</code> and the <code>paint</code> call is the + * <em>first</em> recursive <code>paint</code> call inside swing.</p> + * + * <p>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 <code>paint</code>.</p> + * * @param r The dirty rectangle to paint */ public void paintImmediately(Rectangle r) { - Component root = SwingUtilities.getRoot(this); - if (root == null || ! root.isShowing()) + // Try to find a root pane for this component. + //Component root = findPaintRoot(r); + Component root = findPaintRoot(r); + // If no paint root is found, then this component is completely overlapped + // by another component and we don't need repainting. + if (root == null) return; - Graphics g = root.getGraphics(); - if (g == null) + if (root == null || !root.isShowing()) return; - Rectangle clip = SwingUtilities.convertRectangle(this, r, root); - g.setClip(clip); - root.paint(g); + Rectangle rootClip = SwingUtilities.convertRectangle(this, r, root); + if (root instanceof JComponent) + ((JComponent) root).paintImmediately2(rootClip); + else + root.repaint(rootClip.x, rootClip.y, rootClip.width, rootClip.height); + } + + /** + * Performs the actual work of paintImmediatly on the repaint root. + * + * @param r the area to be repainted + */ + void paintImmediately2(Rectangle r) + { + RepaintManager rm = RepaintManager.currentManager(this); + Graphics g = getGraphics(); + g.setClip(r.x, r.y, r.width, r.height); + if (rm.isDoubleBufferingEnabled() && isDoubleBuffered()) + paintDoubleBuffered(g); + else + paintSimple(g); g.dispose(); } /** + * Performs double buffered repainting. + * + * @param g the graphics context to paint to + */ + void paintDoubleBuffered(Graphics g) + { + + Rectangle r = g.getClipBounds(); + if (r == null) + r = new Rectangle(0, 0, getWidth(), getHeight()); + RepaintManager rm = RepaintManager.currentManager(this); + + // Paint on the offscreen buffer. + Image buffer = rm.getOffscreenBuffer(this, getWidth(), getHeight()); + Graphics g2 = buffer.getGraphics(); + g2 = getComponentGraphics(g2); + g2.setClip(r.x, r.y, r.width, r.height); + isPaintingDoubleBuffered = true; + paint(g2); + isPaintingDoubleBuffered = false; + g2.dispose(); + + // Paint the buffer contents on screen. + g.drawImage(buffer, 0, 0, this); + } + + /** + * Performs normal painting without double buffering. + * + * @param g the graphics context to use + */ + void paintSimple(Graphics g) + { + Graphics g2 = getComponentGraphics(g); + paint(g2); + } + + /** * Return a string representation for this component, for use in * debugging. * @@ -1685,7 +2061,11 @@ public abstract class JComponent extends Container implements Serializable break; case WHEN_IN_FOCUSED_WINDOW: - inputMap_whenInFocusedWindow = map; + 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: @@ -1711,7 +2091,7 @@ public abstract class JComponent extends Container implements Serializable case WHEN_IN_FOCUSED_WINDOW: if (inputMap_whenInFocusedWindow == null) - inputMap_whenInFocusedWindow = new InputMap(); + inputMap_whenInFocusedWindow = new ComponentInputMap(this); return inputMap_whenInFocusedWindow; case UNDEFINED_CONDITION: @@ -1797,6 +2177,7 @@ public abstract class JComponent extends Container implements Serializable */ protected void processComponentKeyEvent(KeyEvent e) { + // This method does nothing, it is meant to be overridden by subclasses. } /** @@ -1825,40 +2206,56 @@ public abstract class JComponent extends Container implements Serializable // 4. The WHEN_IN_FOCUSED_WINDOW maps of all the enabled components in // the focused window are searched. - if (processKeyBinding(KeyStroke.getKeyStrokeForEvent(e), - e, WHEN_FOCUSED, e.getID() == KeyEvent.KEY_PRESSED)) - // This is step 1 from above comment. - e.consume(); - else if (processKeyBinding(KeyStroke.getKeyStrokeForEvent(e), - e, WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, - e.getID() == KeyEvent.KEY_PRESSED)) - // This is step 2 from above comment. - e.consume(); - else + KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(e); + boolean pressed = e.getID() == KeyEvent.KEY_PRESSED; + + if (processKeyBinding(keyStroke, e, WHEN_FOCUSED, pressed)) { - // This is step 3 from above comment. - Container current = this; - while ((current = current.getParent()) instanceof JComponent) + // 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)) { - if (((JComponent)current).processKeyBinding - (KeyStroke.getKeyStrokeForEvent(e), e, - WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, - e.getID() == KeyEvent.KEY_PRESSED)) - { - e.consume(); - break; - } - if (current instanceof Window || current instanceof Applet - || current instanceof JInternalFrame) - break; - } - if (e.isConsumed()) - return; + 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; - // This is step 4 from above comment. - // FIXME: Implement. Note, should use ComponentInputMaps rather - // than walking the entire containment hierarchy. + // 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, @@ -1975,8 +2372,19 @@ public abstract class JComponent extends Container implements Serializable */ public void revalidate() { - invalidate(); - RepaintManager.currentManager(this).addInvalidComponent(this); + if (! EventQueue.isDispatchThread()) + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + revalidate(); + } + }); + else + { + invalidate(); + RepaintManager.currentManager(this).addInvalidComponent(this); + } } /** @@ -1999,7 +2407,12 @@ public abstract class JComponent extends Container implements Serializable */ public void setAlignmentX(float a) { - alignmentX = a; + if (a < 0.0F) + alignmentX = 0.0F; + else if (a > 1.0) + alignmentX = 1.0F; + else + alignmentX = a; } /** @@ -2009,7 +2422,12 @@ public abstract class JComponent extends Container implements Serializable */ public void setAlignmentY(float a) { - alignmentY = a; + if (a < 0.0F) + alignmentY = 0.0F; + else if (a > 1.0) + alignmentY = 1.0F; + else + alignmentY = a; } /** @@ -2049,9 +2467,11 @@ public abstract class JComponent extends Container implements Serializable */ public void setEnabled(boolean enable) { - boolean oldEnabled = isEnabled(); + if (enable == isEnabled()) + return; super.setEnabled(enable); - firePropertyChange("enabled", oldEnabled, enable); + firePropertyChange("enabled", !enable, enable); + repaint(); } /** @@ -2061,7 +2481,11 @@ public abstract class JComponent extends Container implements Serializable */ public void setFont(Font f) { + if (f == getFont()) + return; super.setFont(f); + revalidate(); + repaint(); } /** @@ -2071,7 +2495,10 @@ public abstract class JComponent extends Container implements Serializable */ public void setBackground(Color bg) { + if (bg == getBackground()) + return; super.setBackground(bg); + repaint(); } /** @@ -2081,42 +2508,51 @@ public abstract class JComponent extends Container implements Serializable */ public void setForeground(Color fg) { + if (fg == getForeground()) + return; super.setForeground(fg); + repaint(); } /** - * Set the value of the {@link #maximumSize} property. + * Set the value of the {@link #maximumSize} property. The passed value is + * copied, the later direct changes on the argument have no effect on the + * property value. * * @param max The new value of the property */ public void setMaximumSize(Dimension max) { Dimension oldMaximumSize = maximumSize; - maximumSize = max; + maximumSize = new Dimension(max); firePropertyChange("maximumSize", oldMaximumSize, maximumSize); } /** - * Set the value of the {@link #minimumSize} property. + * Set the value of the {@link #minimumSize} property. The passed value is + * copied, the later direct changes on the argument have no effect on the + * property value. * * @param min The new value of the property */ public void setMinimumSize(Dimension min) { Dimension oldMinimumSize = minimumSize; - minimumSize = min; + minimumSize = new Dimension(min); firePropertyChange("minimumSize", oldMinimumSize, minimumSize); } /** - * Set the value of the {@link #preferredSize} property. + * Set the value of the {@link #preferredSize} property. The passed value is + * copied, the later direct changes on the argument have no effect on the + * property value. * * @param pref The new value of the property */ public void setPreferredSize(Dimension pref) { Dimension oldPreferredSize = preferredSize; - preferredSize = pref; + preferredSize = new Dimension(pref); firePropertyChange("preferredSize", oldPreferredSize, preferredSize); } @@ -2131,6 +2567,7 @@ public abstract class JComponent extends Container implements Serializable */ public void setNextFocusableComponent(Component aComponent) { + // TODO: Implement this properly. } /** @@ -2191,11 +2628,29 @@ public abstract class JComponent extends Container implements Serializable /** * 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(); } /** @@ -2248,7 +2703,8 @@ public abstract class JComponent extends Container implements Serializable ui.installUI(this); firePropertyChange("UI", oldUI, newUI); - + revalidate(); + repaint(); } /** @@ -2427,47 +2883,22 @@ public abstract class JComponent extends Container implements Serializable */ 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(); - // let parents inherit the keybord mapping - InputMap input = getInputMap(); - ActionMap actions = getActionMap(); - - Container parent = getParent(); - while ((parent != null) && (parent instanceof JComponent)) - { - JComponent jParent = (JComponent) parent; - InputMap parentInput = jParent.getInputMap(); - ActionMap parentAction = jParent.getActionMap(); - - KeyStroke[] ikeys = input.keys(); - for (int i = 0; i < ikeys.length; i++) - { - Object o = input.get(ikeys[i]); - parentInput.put(ikeys[i], o); - } - - Object[] akeys = actions.keys(); - for (int i = 0; i < akeys.length; i++) - { - Action a = actions.get(akeys[i]); - parentAction.put(akeys[i], a); - } - - parent = jParent.getParent(); - } - - // notify ancestor listeners - AncestorListener[] ls = getAncestorListeners(); - AncestorEvent ev = new AncestorEvent(this, AncestorEvent.ANCESTOR_ADDED, - this, parent); - for (int i = 0; i < ls.length; i++) - { - ls[i].ancestorAdded(ev); - } + // Notify AncestorListeners. + fireAncestorEvent(this, AncestorEvent.ANCESTOR_ADDED); // fire property change event for 'ancestor' - firePropertyChange("ancestor", null, parent); + firePropertyChange("ancestor", null, getParent()); } /** @@ -2490,43 +2921,14 @@ public abstract class JComponent extends Container implements Serializable { super.removeNotify(); - // let parents inherit the keybord mapping - InputMap input = getInputMap(); - ActionMap actions = getActionMap(); - - Container parent = getParent(); - while ((parent != null) && (parent instanceof JComponent)) - { - JComponent jParent = (JComponent) parent; - InputMap parentInput = jParent.getInputMap(); - ActionMap parentAction = jParent.getActionMap(); - - KeyStroke[] ikeys = input.allKeys(); - for (int i = 0; i < ikeys.length; i++) - { - parentInput.remove(ikeys[i]); - } - - Object[] akeys = actions.allKeys(); - for (int i = 0; i < akeys.length; i++) - { - parentAction.remove(akeys[i]); - } - - parent = jParent.getParent(); - } - - // notify ancestor listeners - AncestorListener[] ls = getAncestorListeners(); - AncestorEvent ev = new AncestorEvent(this, AncestorEvent.ANCESTOR_ADDED, - this, parent); - for (int i = 0; i < ls.length; i++) - { - ls[i].ancestorAdded(ev); - } + // FIXME: remove the WHEN_IN_FOCUSED_WINDOW bindings from the + // KeyboardManager + + // Notify ancestor listeners. + fireAncestorEvent(this, AncestorEvent.ANCESTOR_REMOVED); // fire property change event for 'ancestor' - firePropertyChange("ancestor", parent, null); + firePropertyChange("ancestor", getParent(), null); } /** @@ -2733,6 +3135,215 @@ public abstract class JComponent extends Container implements Serializable */ 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. + Component[] children = getComponents(); + for (int i = 0; i < children.length; i++) + { + if (!(children[i] instanceof JComponent)) + continue; + JComponent jc = (JComponent) children[i]; + jc.fireAncestorEvent(ancestor, id); + } + } + + /** + * Finds a suitable paint root for painting this component. This method first + * checks if this component is overlapped using + * {@link #findOverlapFreeParent(Rectangle)}. The returned paint root is then + * feeded to {@link #findOpaqueParent(Component)} to find the nearest opaque + * component for this paint root. If no paint is necessary, then we return + * <code>null</code>. + * + * @param c the clip of this component + * + * @return the paint root or <code>null</code> if no painting is necessary + */ + private Component findPaintRoot(Rectangle c) + { + Component p = findOverlapFreeParent(c); + if (p == null) + return null; + Component root = findOpaqueParent(p); + return root; + } + + /** + * Scans the containment hierarchy upwards for components that overlap the + * this component in the specified clip. This method returns + * <code>this</code>, if no component overlaps this component. It returns + * <code>null</code> if another component completely covers this component + * in the specified clip (no repaint necessary). If another component partly + * overlaps this component in the specified clip, then the parent of this + * component is returned (this is the component that must be used as repaint + * root). For efficient lookup, the method + * {@link #isOptimizedDrawingEnabled()} is used. + * + * @param clip the clip of this component + * + * @return the paint root, or <code>null</code> if no paint is necessary + */ + private Component findOverlapFreeParent(Rectangle clip) + { + Rectangle currentClip = clip; + Component found = this; + Container parent = this; + while (parent != null && !(parent instanceof Window)) + { + Container newParent = parent.getParent(); + if (newParent == null) + break; + // If the parent is optimizedDrawingEnabled, then its children are + // tiled and cannot have an overlapping child. Go directly to next + // parent. + if (newParent instanceof JComponent + && ((JComponent) newParent).isOptimizedDrawingEnabled()) + { + parent = newParent; + continue; + } + + // First we must check if the new parent itself somehow clips the + // target rectangle. This can happen in JViewports. + Rectangle parRect = new Rectangle(0, 0, newParent.getWidth(), + newParent.getHeight()); + Rectangle target = SwingUtilities.convertRectangle(found, + currentClip, + newParent); + if (target.contains(parRect) || target.intersects(parRect)) + { + found = newParent; + currentClip = target; + parent = newParent; + continue; + } + + // Otherwise we must check if one of the children of this parent + // overlaps with the current component. + Component[] children = newParent.getComponents(); + // This flag is used to skip components that are 'below' the component + // in question. + boolean skip = true; + for (int i = children.length - 1; i >= 0; i--) + { + if (children[i] == parent) + skip = false; + if (skip) + continue; + Component c = children[i]; + Rectangle compBounds = c.getBounds(); + // If the component completely overlaps the clip in question, we + // don't need to repaint. Return null. + if (compBounds.contains(target)) + return null; + if (compBounds.intersects(target)) + { + // We found a parent whose children overlap with our current + // component. Make this the current component. + found = newParent; + currentClip = target; + break; + } + } + parent = newParent; + } + return found; + } + + /** + * Finds the nearest component to <code>c</code> (upwards in the containment + * hierarchy), that is opaque. If <code>c</code> itself is opaque, + * this returns <code>c</code> itself. + * + * @param c the start component for the search + * @return the nearest component to <code>c</code> (upwards in the containment + * hierarchy), that is opaque; If <code>c</code> itself is opaque, + * this returns <code>c</code> itself + */ + private Component findOpaqueParent(Component c) + { + Component found = c; + while (true) + { + if ((found instanceof JComponent) && ((JComponent) found).isOpaque()) + break; + else if (!(found instanceof JComponent)) + break; + Container p = found.getParent(); + if (p == null) + break; + else + found = p; + } + return found; + } + + /** + * 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 <code>changed</code> + // 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)); } } diff --git a/libjava/classpath/javax/swing/JDesktopPane.java b/libjava/classpath/javax/swing/JDesktopPane.java index f4c80eca7d6..43ab71e7e9f 100644 --- a/libjava/classpath/javax/swing/JDesktopPane.java +++ b/libjava/classpath/javax/swing/JDesktopPane.java @@ -97,6 +97,7 @@ public class JDesktopPane extends JLayeredPane implements Accessible */ protected AccessibleJDesktopPane() { + // Nothing to do here. } /** @@ -246,6 +247,7 @@ public class JDesktopPane extends JLayeredPane implements Accessible } catch (PropertyVetoException e) { + // We do nothing when the attempt is vetoed. } } selectedFrame = null; @@ -259,6 +261,7 @@ public class JDesktopPane extends JLayeredPane implements Accessible } catch (PropertyVetoException e) { + // We do nothing when the attempt is vetoed. } } diff --git a/libjava/classpath/javax/swing/JDialog.java b/libjava/classpath/javax/swing/JDialog.java index 0f528ab1b45..b3f7c011f68 100644 --- a/libjava/classpath/javax/swing/JDialog.java +++ b/libjava/classpath/javax/swing/JDialog.java @@ -66,6 +66,21 @@ import javax.accessibility.AccessibleContext; public class JDialog extends Dialog implements Accessible, WindowConstants, RootPaneContainer { + /** + * Provides accessibility support for <code>JDialog</code>s. + */ + protected class AccessibleJDialog extends Dialog.AccessibleAWTDialog + { + /** + * Creates a new instance of <code>AccessibleJDialog</code>. + */ + public AccessibleJDialog() + { + super(); + // Nothing to do here. + } + } + private static final long serialVersionUID = -864070866424508218L; /** DOCUMENT ME! */ @@ -87,13 +102,6 @@ public class JDialog extends Dialog implements Accessible, WindowConstants, /** Whether JDialogs are decorated by the Look and Feel. */ private static boolean decorated; - /** - * Whether we're in the init stage or not. - * If so, adds and layouts are for top-level, otherwise they're for the - * content pane - */ - private boolean initStageDone = false; - /* Creates a new non-modal JDialog with no title * using a shared Frame as the owner. */ @@ -244,7 +252,7 @@ public class JDialog extends Dialog implements Accessible, WindowConstants, invalidate(); // Now that initStageDone is true, adds and layouts apply to contentPane, // not top-level. - initStageDone = true; + setRootPaneCheckingEnabled(true); } /** @@ -315,13 +323,8 @@ public class JDialog extends Dialog implements Accessible, WindowConstants, { // Check if we're in initialization stage. If so, call super.setLayout // otherwise, valid calls go to the content pane. - if (initStageDone) - { - if (isRootPaneCheckingEnabled()) - throw new Error("Cannot set top-level layout. Use" - + " getConentPane().setLayout instead."); - getContentPane().setLayout(manager); - } + if (isRootPaneCheckingEnabled()) + getContentPane().setLayout(manager); else super.setLayout(manager); } @@ -445,15 +448,10 @@ public class JDialog extends Dialog implements Accessible, WindowConstants, { // If we're adding in the initialization stage use super.add. // Otherwise pass the add onto the content pane. - if (!initStageDone) - super.addImpl(comp, constraints, index); + if (isRootPaneCheckingEnabled()) + getContentPane().add(comp, constraints, index); else - { - if (isRootPaneCheckingEnabled()) - throw new Error("Do not add directly to JDialog." - + " Use getContentPane().add instead."); - getContentPane().add(comp, constraints, index); - } + super.addImpl(comp, constraints, index); } /** @@ -588,6 +586,8 @@ public class JDialog extends Dialog implements Accessible, WindowConstants, */ public AccessibleContext getAccessibleContext() { - return null; + if (accessibleContext == null) + accessibleContext = new AccessibleJDialog(); + return accessibleContext; } } diff --git a/libjava/classpath/javax/swing/JEditorPane.java b/libjava/classpath/javax/swing/JEditorPane.java index e2f1319a2a7..9ddf970deea 100644 --- a/libjava/classpath/javax/swing/JEditorPane.java +++ b/libjava/classpath/javax/swing/JEditorPane.java @@ -41,15 +41,27 @@ package javax.swing; import java.awt.Dimension; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.MalformedURLException; import java.net.URL; 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.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.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 @@ -76,6 +88,384 @@ import javax.swing.text.JTextComponent; */ public class JEditorPane extends JTextComponent { + /** + * Provides accessibility support for <code>JEditorPane</code>. + * + * @author Roman Kennke (kennke@aicas.com) + */ + protected class AccessibleJEditorPane extends AccessibleJTextComponent + { + + /** + * Creates a new <code>AccessibleJEditorPane</code> object. + */ + protected AccessibleJEditorPane() + { + super(); + } + + /** + * Returns a description of this <code>AccessibleJEditorPane</code>. 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 <code>AccessibleJEditorPane</code>. + * + * @return the accessible state of this <code>AccessibleJEditorPane</code> + */ + 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 <code>JEditorPane</code>s, 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 <code>JEditorPane</code>. This will + * be an instance of + * {@link JEditorPaneAccessibleHypertextSupport}. + * + * @return the accessible text of the <code>JEditorPane</code> + */ + 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 + { + + /** + * 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 <code>HTMLLink</code>. + * + * @param el the link element + */ + public HTMLLink(Element el) + { + this.element = el; + } + + /** + * Returns <code>true</code> if this <code>HTMLLink</code> is still + * valid. A <code>HTMLLink</code> can become invalid when the document + * changes. + * + * @return <code>true</code> if this <code>HTMLLink</code> 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 <code>true</code> if the action has been performed + * successfully, <code>false</code> 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 <code>i</code>. + * 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 <code>i</code> + */ + 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 <code>i</code>. + * + * @param i the action index + * + * @return an {@link URL} object, that represents the action at action + * index <code>i</code> + */ + 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 <code>String</code> object is + * returned, if the link encloses an <img> tag, then an + * <code>ImageIcon</code> 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 <code>i</code>-th hyperlink in the document or + * <code>null</code> if there is no hyperlink with the specified index. + * + * @param i the index of the hyperlink to return + * + * @return the <code>i</code>-th hyperlink in the document or + * <code>null</code> 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 + * <code>c</code> within the document, or <code>-1</code> 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 + * <code>c</code> within the document, or <code>-1</code> 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 <code>i</code>, or + * <code>null</code>, 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 <code>i</code>, or + * <code>null</code>, 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; + } + } + private static final long serialVersionUID = 3140472492599046285L; private URL page; @@ -128,9 +518,21 @@ public class JEditorPane extends JTextComponent 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() { - return null; + if (accessibleContext == null) + { + if (getEditorKit() instanceof HTMLEditorKit) + accessibleContext = new AccessibleJEditorPaneHTML(); + else + accessibleContext = new AccessibleJEditorPane(); + } + return accessibleContext; } public final String getContentType() @@ -169,12 +571,18 @@ public class JEditorPane extends JTextComponent public boolean getScrollableTracksViewportHeight() { - return false; + /* Container parent = getParent(); + return (parent instanceof JViewport && + parent.isValid());*/ + return isValid(); } public boolean getScrollableTracksViewportWidth() { - return false; + /*Container parent = getParent(); + return (parent instanceof JViewport && + parent.isValid());*/ + return isValid(); } public URL getPage() @@ -211,9 +619,26 @@ public class JEditorPane extends JTextComponent /** * This method initializes from a stream. */ - public void read(InputStream in, Object desc) - throws IOException + public void read(InputStream in, Object desc) throws IOException { + EditorKit kit = getEditorKit(); + if (kit instanceof HTMLEditorKit && desc instanceof HTMLDocument) + { + Document doc = (Document) desc; + try + { + kit.read(in, doc, 0); + } + catch (BadLocationException ex) + { + assert false : "BadLocationException must not be thrown here."; + } + } + else + { + Reader inRead = new InputStreamReader(in); + super.read(inRead, desc); + } } /** @@ -222,6 +647,7 @@ public class JEditorPane extends JTextComponent public static void registerEditorKitForContentType(String type, String classname) { + // TODO: Implement this properly. } /** @@ -231,6 +657,7 @@ public class JEditorPane extends JTextComponent String classname, ClassLoader loader) { + // TODO: Implement this properly. } /** @@ -239,6 +666,7 @@ public class JEditorPane extends JTextComponent */ public void replaceSelection(String content) { + // TODO: Implement this properly. } /** @@ -247,6 +675,7 @@ public class JEditorPane extends JTextComponent */ public void scrollToReference(String reference) { + // TODO: Implement this properly. } public final void setContentType(String type) @@ -281,6 +710,8 @@ public class JEditorPane extends JTextComponent firePropertyChange("editorKit", oldValue, newValue); invalidate(); repaint(); + // Reset the accessibleContext since this depends on the editorKit. + accessibleContext = null; } public void setEditorKitForContentType(String type, EditorKit k) diff --git a/libjava/classpath/javax/swing/JFileChooser.java b/libjava/classpath/javax/swing/JFileChooser.java index 7569061ab2e..1598641f1b9 100644 --- a/libjava/classpath/javax/swing/JFileChooser.java +++ b/libjava/classpath/javax/swing/JFileChooser.java @@ -42,10 +42,13 @@ import java.awt.Frame; import java.awt.HeadlessException; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +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.JDialog; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileSystemView; @@ -70,171 +73,324 @@ public class JFileChooser extends JComponent implements Accessible { private static final long serialVersionUID = 3162921138695327837L; - /** DOCUMENT ME! */ + /** + * A dialog type for selecting a file to open. + * @see #setDialogType(int) + */ public static final int OPEN_DIALOG = 0; - /** DOCUMENT ME! */ + /** + * A dialog type for selecting a file to save. + * @see #setDialogType(int) + */ public static final int SAVE_DIALOG = 1; - /** DOCUMENT ME! */ + /** + * A dialog type for some custom purpose. + * @see #setDialogType(int) + */ public static final int CUSTOM_DIALOG = 2; - /** DOCUMENT ME! */ + /** + * 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; - /** DOCUMENT ME! */ + /** + * 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; - /** DOCUMENT ME! */ + /** + * 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; - /** DOCUMENT ME! */ + /** + * A selection mode constant indicating acceptance of files only. + * @see #setFileSelectionMode(int) + */ public static final int FILES_ONLY = 0; - /** DOCUMENT ME! */ + /** + * A selection mode constant indicating acceptance of directories only. + * @see #setFileSelectionMode(int) + */ public static final int DIRECTORIES_ONLY = 1; - /** DOCUMENT ME! */ + /** + * A selection mode constant indicating acceptance of files and directories. + * @see #setFileSelectionMode(int) + */ public static final int FILES_AND_DIRECTORIES = 2; - /** DOCUMENT ME! */ + /** + * Action command string for cancelling the current selection. + * @see #cancelSelection() + */ public static final String CANCEL_SELECTION = "CancelSelection"; - /** DOCUMENT ME! */ + /** + * Action command string for approving the current selection. + * @see #cancelSelection() + */ public static final String APPROVE_SELECTION = "ApproveSelection"; - /** DOCUMENT ME! */ + /** + * The name of the property for the approve button text. + * @see #setApproveButtonText(String) + */ public static final String APPROVE_BUTTON_TEXT_CHANGED_PROPERTY = "ApproveButtonTextChangedProperty"; - /** DOCUMENT ME! */ + /** + * 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"; - /** DOCUMENT ME! */ + /** + * The name of the property for the approve button mnemonic. + * @see #setApproveButtonMnemonic(int) + */ public static final String APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY = "ApproveButtonMnemonicChangedProperty"; - /** DOCUMENT ME! */ + /** + * The name of the property for control button visibility. + * @see #setControlButtonsAreShown(boolean) + */ public static final String CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY = "ControlButtonsAreShownChangedProperty"; - /** DOCUMENT ME! */ + /** + * The name of the property for the current directory. + * @see #setCurrentDirectory(File) + */ public static final String DIRECTORY_CHANGED_PROPERTY = "directoryChanged"; - /** DOCUMENT ME! */ + /** + * The name of the property for the selected file. + * @see #setSelectedFile(File) + */ public static final String SELECTED_FILE_CHANGED_PROPERTY = "SelectedFileChangedProperty"; - /** DOCUMENT ME! */ + /** + * The name of the property for the selected files. + * @see #setSelectedFiles(File[]) + */ public static final String SELECTED_FILES_CHANGED_PROPERTY = "SelectedFilesChangedProperty"; - /** DOCUMENT ME! */ + /** + * The name of the property for multi-selection. + * @see #setMultiSelectionEnabled(boolean) + */ public static final String MULTI_SELECTION_ENABLED_CHANGED_PROPERTY = "MultiSelectionEnabledChangedProperty"; - /** DOCUMENT ME! */ + /** + * The name of the 'file system view' property. + * @see #setFileSystemView(FileSystemView) + */ public static final String FILE_SYSTEM_VIEW_CHANGED_PROPERTY = "FileSystemViewChanged"; - /** DOCUMENT ME! */ + /** + * The name of the 'file view' property. + * @see #setFileView(FileView) + */ public static final String FILE_VIEW_CHANGED_PROPERTY = "fileViewChanged"; - /** DOCUMENT ME! */ + /** + * The name of the 'file hiding enabled' property. + * @see #setFileHidingEnabled(boolean) + */ public static final String FILE_HIDING_CHANGED_PROPERTY = "FileHidingChanged"; - /** DOCUMENT ME! */ + /** + * The name of the 'file filter' property. + * @see #setFileFilter(FileFilter) + */ public static final String FILE_FILTER_CHANGED_PROPERTY = "fileFilterChanged"; - /** DOCUMENT ME! */ + /** + * The name of the 'file selection mode' property. + * @see #setFileSelectionMode(int) + */ public static final String FILE_SELECTION_MODE_CHANGED_PROPERTY = "fileSelectionChanged"; - /** DOCUMENT ME! */ + /** + * The name of the 'accessory' property. + * @see #setAccessory(JComponent) + */ public static final String ACCESSORY_CHANGED_PROPERTY = "AccessoryChangedProperty"; - /** DOCUMENT ME! */ + /** + * 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"; - /** DOCUMENT ME! */ + /** + * The name of the 'dialog title' property. + * @see #setDialogTitle(String) + */ public static final String DIALOG_TITLE_CHANGED_PROPERTY = "DialogTitleChangedProperty"; - /** DOCUMENT ME! */ + /** + * The name of the 'dialog type' property. + * @see #setDialogType(int) + */ public static final String DIALOG_TYPE_CHANGED_PROPERTY = "DialogTypeChangedProperty"; - /** DOCUMENT ME! */ + /** + * The name of the 'choosable file filters' property. + * @see #addChoosableFileFilter(FileFilter) + */ public static final String CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY = "ChoosableFileFilterChangedProperty"; - /** DOCUMENT ME! */ + /** + * The accessible context. + * @see #getAccessibleContext() + */ protected AccessibleContext accessibleContext; - /** DOCUMENT ME! */ + /** + * The file system view. + * @see #setFileSystemView(FileSystemView) + */ private FileSystemView fsv; - /** DOCUMENT ME! */ + /** + * The accessory component. + * @see #setAccessory(JComponent) + */ private JComponent accessory; - /** DOCUMENT ME! */ + /** + * The approve button mnemonic. + * @see #setApproveButtonMnemonic(int) + */ private int approveButtonMnemonic = 0; - /** DOCUMENT ME! */ + /** + * The approve button text. + * @see #setApproveButtonText(String) + */ private String approveButtonText; - /** DOCUMENT ME! */ + /** + * The approve button tool tip text. + * @see #setApproveButtonToolTipText(String) + */ private String approveButtonToolTipText; - /** DOCUMENT ME! */ + /** + * The choosable file filters. + * @see #addChoosableFileFilter(FileFilter) + */ private ArrayList choosableFilters = new ArrayList(); - /** DOCUMENT ME! */ + /** + * A flag controlling whether the accept all file filter is used. + * @see #setAcceptAllFileFilterUsed(boolean) + */ private boolean isAcceptAll = true; - /** DOCUMENT ME! */ + /** + * The dialog title. + * @see #setDialogTitle(String) + */ private String dialogTitle; - /** DOCUMENT ME! */ + /** + * The dialog type. + * @see #setDialogType(int) + */ private int dialogType = OPEN_DIALOG; - /** DOCUMENT ME! */ + /** + * The return value for the dialog. + * @see #showOpenDialog(Component) + * @see #showSaveDialog(Component) + */ private int retval = ERROR_OPTION; - /** DOCUMENT ME! */ + /** + * A flag indicating whether the file chooser allows multiple selection. + * @see #isMultiSelectionEnabled() + */ private boolean multiSelection = false; - /** DOCUMENT ME! */ + /** + * A flag indicating whether file hiding is enabled. + * @see #isFileHidingEnabled() + */ private boolean fileHiding = true; - /** DOCUMENT ME! */ + /** + * The file selection mode. + * @see #setFileSelectionMode(int) + */ private int fileSelectionMode = FILES_AND_DIRECTORIES; - /** DOCUMENT ME! */ + /** + * The file view. + * @see #setFileView(FileView) + */ private FileView fv = null; - /** DOCUMENT ME! */ + /** + * A flag controlling whether or not the control buttons are visible. + * @see #setControlButtonsAreShown(boolean) + */ private boolean controlButtonsShown = true; - /** DOCUMENT ME! */ + /** + * The current directory. + * @see #setCurrentDirectory(File) + */ private File currentDir = null; - /** DOCUMENT ME! */ + /** + * The current file filter. + * @see #setFileFilter(FileFilter) + */ private FileFilter currentFilter = null; - /** DOCUMENT ME! */ + /** + * An array of selected files. + * @see #setSelectedFiles(File[]) + */ private File[] selectedFiles; - /** DOCUMENT ME! */ + /** + * The selected file. + * @see #setSelectedFile(File) + */ private File selectedFile; /** - * Creates a new JFileChooser object. + * Creates a new <code>JFileChooser</code> object. */ public JFileChooser() { @@ -243,9 +399,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * Creates a new JFileChooser object. + * Creates a new <code>JFileChooser</code> object. * - * @param currentDirectoryPath DOCUMENT ME! + * @param currentDirectoryPath the directory that should initially be + * shown in the filechooser (if <code>null</code>, the user's home + * directory is used). */ public JFileChooser(String currentDirectoryPath) { @@ -254,12 +412,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * Creates a new JFileChooser object with the specified directory and - * FileSystemView. + * Creates a new <code>JFileChooser</code> object with the specified + * directory and {@link FileSystemView}. * - * @param currentDirectoryPath the directory that should initially be - * shown the filechooser - * @param fsv the FileSystemView object to use + * @param currentDirectoryPath the directory that should initially be + * shown in the filechooser (if <code>null</code>, the user's home + * directory is used). + * @param fsv the file system view (if <code>null</code>, the default file + * system view is used). */ public JFileChooser(String currentDirectoryPath, FileSystemView fsv) { @@ -268,9 +428,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * Creates a new JFileChooser object. + * Creates a new <code>JFileChooser</code> object. * - * @param currentDirectory DOCUMENT ME! + * @param currentDirectory the directory that should initially be + * shown in the filechooser (if <code>null</code>, the user's home + * directory is used). */ public JFileChooser(File currentDirectory) { @@ -279,9 +441,10 @@ public class JFileChooser extends JComponent implements Accessible } /** - * Creates a new JFileChooser object. + * Creates a new <code>JFileChooser</code> object. * - * @param fsv DOCUMENT ME! + * @param fsv the file system view (if <code>null</code>, the default file + * system view is used). */ public JFileChooser(FileSystemView fsv) { @@ -290,10 +453,13 @@ public class JFileChooser extends JComponent implements Accessible } /** - * Creates a new JFileChooser object. + * Creates a new <code>JFileChooser</code> object. * - * @param currentDirectory DOCUMENT ME! - * @param fsv DOCUMENT ME! + * @param currentDirectory the directory that should initially be + * shown in the filechooser (if <code>null</code>, the user's home + * directory is used). + * @param fsv the file system view (if <code>null</code>, the default file + * system view is used). */ public JFileChooser(File currentDirectory, FileSystemView fsv) { @@ -302,9 +468,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets up the file chooser. This method is called by all the constructors. * - * @param view DOCUMENT ME! + * @param view the file system view (if <code>null</code>, the default file + * system view is used). + * + * @see FileSystemView#getFileSystemView() */ protected void setup(FileSystemView view) { @@ -336,9 +505,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the selected file, if there is one. * - * @return DOCUMENT ME! + * @return The selected file (possibly <code>null</code>). + * + * @see #setSelectedFile(File) */ public File getSelectedFile() { @@ -346,9 +517,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the selected file and sends a {@link PropertyChangeEvent} to all + * registered listeners. The property name is + * {@link #SELECTED_FILE_CHANGED_PROPERTY}. * - * @param file DOCUMENT ME! + * @param file the file (<code>null</code> permitted). */ public void setSelectedFile(File file) { @@ -361,9 +534,10 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the selected file or files. * - * @return DOCUMENT ME! + * @return An array of the selected files, or <code>null</code> if there are + * no selected files. */ public File[] getSelectedFiles() { @@ -375,9 +549,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the selected files and sends a {@link PropertyChangeEvent} (with the + * name {@link #SELECTED_FILES_CHANGED_PROPERTY}) to all registered + * listeners. * - * @param selectedFiles DOCUMENT ME! + * @param selectedFiles the selected files (<code>null</code> permitted). */ public void setSelectedFiles(File[] selectedFiles) { @@ -393,9 +569,9 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the current directory. * - * @return DOCUMENT ME! + * @return The current directory. */ public File getCurrentDirectory() { @@ -403,9 +579,15 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the current directory and fires a {@link PropertyChangeEvent} (with + * the property name {@link #DIRECTORY_CHANGED_PROPERTY}) to all registered + * listeners. If <code>dir</code> is <code>null</code>, the current + * directory is set to the default directory returned by the file system + * view. * - * @param dir DOCUMENT ME! + * @param dir the new directory (<code>null</code> permitted). + * + * @see FileSystemView#getDefaultDirectory() */ public void setCurrentDirectory(File dir) { @@ -421,7 +603,7 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Called by the UI delegate when the parent directory is changed. */ public void changeToParentDirectory() { @@ -430,7 +612,7 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Rescans the current directory (this is handled by the UI delegate). */ public void rescanCurrentDirectory() { @@ -438,9 +620,10 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Ensures the the specified file is visible (this is handled by the + * UI delegate). * - * @param f DOCUMENT ME! + * @param f the file. */ public void ensureFileIsVisible(File f) { @@ -448,11 +631,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Displays the file chooser in a modal dialog using the + * {@link #OPEN_DIALOG} type. * - * @param parent DOCUMENT ME! + * @param parent the parent component. * - * @return DOCUMENT ME! + * @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! */ @@ -472,11 +658,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Displays the file chooser in a modal dialog using the + * {@link #SAVE_DIALOG} type. * - * @param parent DOCUMENT ME! + * @param parent the parent component. * - * @return DOCUMENT ME! + * @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! */ @@ -493,12 +682,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Displays the file chooser in a modal dialog using the + * {@link #CUSTOM_DIALOG} type. * - * @param parent DOCUMENT ME! - * @param approveButtonText DOCUMENT ME! + * @param parent the parent component. * - * @return DOCUMENT ME! + * @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! */ @@ -517,11 +708,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Creates a modal dialog in which to display the file chooser. * - * @param parent DOCUMENT ME! + * @param parent the parent component. * - * @return DOCUMENT ME! + * @return The dialog. * * @throws HeadlessException DOCUMENT ME! */ @@ -542,9 +733,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the flag that controls whether or not the control buttons are + * shown on the file chooser. * - * @return DOCUMENT ME! + * @return A boolean. + * + * @see #setControlButtonsAreShown(boolean) */ public boolean getControlButtonsAreShown() { @@ -552,9 +746,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * 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 DOCUMENT ME! + * @param b the new value for the flag. */ public void setControlButtonsAreShown(boolean b) { @@ -567,9 +764,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the type of file chooser. * - * @return DOCUMENT ME! + * @return {@link #OPEN_DIALOG}, {@link #SAVE_DIALOG} or + * {@link #CUSTOM_DIALOG}. + * + * @see #setDialogType(int) */ public int getDialogType() { @@ -577,9 +777,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the dialog type and fires a {@link PropertyChangeEvent} (with the + * property name {@link #DIALOG_TYPE_CHANGED_PROPERTY}) to all + * registered listeners. * - * @param dialogType DOCUMENT ME! + * @param dialogType the dialog type (one of: {@link #OPEN_DIALOG}, + * {@link #SAVE_DIALOG}, {@link #CUSTOM_DIALOG}). + * + * @throws IllegalArgumentException if <code>dialogType</code> is not valid. */ public void setDialogType(int dialogType) { @@ -596,9 +801,13 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the dialog title and sends a {@link PropertyChangeEvent} (with the + * property name {@link #DIALOG_TITLE_CHANGED_PROPERTY}) to all + * registered listeners. * - * @param dialogTitle DOCUMENT ME! + * @param dialogTitle the dialog title (<code>null</code> permitted). + * + * @see #getDialogTitle() */ public void setDialogTitle(String dialogTitle) { @@ -611,9 +820,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the dialog title. * - * @return DOCUMENT ME! + * @return The dialog title (possibly <code>null</code>). + * + * @see #setDialogTitle(String) */ public String getDialogTitle() { @@ -621,9 +832,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * 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 DOCUMENT ME! + * @param toolTipText the text. */ public void setApproveButtonToolTipText(String toolTipText) { @@ -637,9 +851,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the tool tip text for the approve button. * - * @return DOCUMENT ME! + * @return The tool tip text for the approve button. + * + * @see #setApproveButtonToolTipText(String) */ public String getApproveButtonToolTipText() { @@ -647,9 +863,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the approve button mnemonic, or zero if no mnemonic has been set. * - * @return DOCUMENT ME! + * @return The approve button mnemonic. + * + * @see #setApproveButtonMnemonic(int) */ public int getApproveButtonMnemonic() { @@ -657,9 +875,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * 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 DOCUMENT ME! + * @param mnemonic the mnemonic. + * + * @see #setApproveButtonMnemonic(char) */ public void setApproveButtonMnemonic(int mnemonic) { @@ -673,9 +896,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * 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 DOCUMENT ME! + * @param mnemonic the mnemonic. + * + * @see #setApproveButtonMnemonic(int) */ public void setApproveButtonMnemonic(char mnemonic) { @@ -683,9 +911,13 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * 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 DOCUMENT ME! + * @param approveButtonText the text (<code>null</code> permitted). + * + * @see #getApproveButtonText() */ public void setApproveButtonText(String approveButtonText) { @@ -699,9 +931,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the approve button text. * - * @return DOCUMENT ME! + * @return The approve button text (possibly <code>null</code>). + * + * @see #setApproveButtonText(String) */ public String getApproveButtonText() { @@ -709,19 +943,22 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the available file filters for this file chooser. * - * @return DOCUMENT ME! + * @return The available file filters. */ public FileFilter[] getChoosableFileFilters() { - return (FileFilter[]) choosableFilters.toArray(new FileFilter[0]); + return (FileFilter[]) choosableFilters.toArray(new FileFilter[choosableFilters.size()]); } /** - * DOCUMENT ME! + * 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 DOCUMENT ME! + * @param filter the filter. */ public void addChoosableFileFilter(FileFilter filter) { @@ -732,11 +969,15 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * 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 DOCUMENT ME! + * @param f the file filter. * - * @return DOCUMENT ME! + * @return <code>true</code> if the filter was removed and + * <code>false</code> otherwise. */ public boolean removeChoosableFileFilter(FileFilter f) { @@ -749,7 +990,8 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Clears the list of choosable file filters and installs the 'accept all' + * filter from the UI delegate. */ public void resetChoosableFileFilters() { @@ -759,9 +1001,9 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the 'accept all' file filter from the UI delegate. * - * @return DOCUMENT ME! + * @return The 'accept all' file filter. */ public FileFilter getAcceptAllFileFilter() { @@ -769,9 +1011,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the flag that controls whether or not the 'accept all' file + * filter is included in the list of filters. * - * @return DOCUMENT ME! + * @return A boolean. + * + * @see #setAcceptAllFileFilterUsed(boolean) */ public boolean isAcceptAllFileFilterUsed() { @@ -779,9 +1024,13 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * 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 DOCUMENT ME! + * @param b the new value of the flag. */ public void setAcceptAllFileFilterUsed(boolean b) { @@ -794,9 +1043,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the accessory component for the file chooser. The default + * value is <code>null</code>. * - * @return DOCUMENT ME! + * @return The accessory component (possibly <code>null</code>). + * + * @see #setAccessory(JComponent) */ public JComponent getAccessory() { @@ -804,9 +1056,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * 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 DOCUMENT ME! + * @param newAccessory the accessory component. */ public void setAccessory(JComponent newAccessory) { @@ -819,9 +1073,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * 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 DOCUMENT ME! + * @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) { @@ -838,9 +1097,13 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * 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 DOCUMENT ME! + * @return The file selection mode. + * + * @see #setFileSelectionMode(int) */ public int getFileSelectionMode() { @@ -848,9 +1111,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns <code>true</code> if file selection is enabled, and + * <code>false</code> otherwise. File selection is enabled when the + * file selection mode is {@link #FILES_ONLY} or + * {@link #FILES_AND_DIRECTORIES}. * - * @return DOCUMENT ME! + * @return <code>true</code> if file selection is enabled. + * + * @see #getFileSelectionMode() */ public boolean isFileSelectionEnabled() { @@ -859,9 +1127,14 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns <code>true</code> if directory selection is enabled, and + * <code>false</code> otherwise. Directory selection is enabled when the + * file selection mode is {@link #DIRECTORIES_ONLY} or + * {@link #FILES_AND_DIRECTORIES}. * - * @return DOCUMENT ME! + * @return <code>true</code> if file selection is enabled. + * + * @see #getFileSelectionMode() */ public boolean isDirectorySelectionEnabled() { @@ -870,9 +1143,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * 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 DOCUMENT ME! + * @param b the new value of the flag. */ public void setMultiSelectionEnabled(boolean b) { @@ -885,9 +1161,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns <code>true</code> if multiple selections are allowed within this + * file chooser, and <code>false</code> otherwise. * - * @return DOCUMENT ME! + * @return A boolean. + * + * @see #setMultiSelectionEnabled(boolean) */ public boolean isMultiSelectionEnabled() { @@ -895,9 +1174,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns <code>true</code> if hidden files are to be hidden, and + * <code>false</code> otherwise. * - * @return DOCUMENT ME! + * @return A boolean. + * + * @see #setFileHidingEnabled(boolean) */ public boolean isFileHidingEnabled() { @@ -905,9 +1187,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * 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 DOCUMENT ME! + * @param b the new value of the flag. */ public void setFileHidingEnabled(boolean b) { @@ -920,9 +1204,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the file filter and sends a {@link PropertyChangeEvent} (with the + * property name {@link #FILE_FILTER_CHANGED_PROPERTY}) to all registered + * listeners. * - * @param filter DOCUMENT ME! + * @param filter the filter. */ public void setFileFilter(FileFilter filter) { @@ -935,9 +1221,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the file filter. * - * @return DOCUMENT ME! + * @return The file filter. + * + * @see #setFileFilter(FileFilter) */ public FileFilter getFileFilter() { @@ -945,9 +1233,13 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * 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 (<code>null</code> permitted). * - * @param fileView DOCUMENT ME! + * @see #getFileView() */ public void setFileView(FileView fileView) { @@ -960,9 +1252,9 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the custom {@link FileView} for the file chooser. * - * @return DOCUMENT ME! + * @return The file view (possibly <code>null</code>). */ public FileView getFileView() { @@ -970,71 +1262,83 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the name of the file, generated by the current (or default) + * {@link FileView}. * - * @return DOCUMENT ME! - */ - private FileView getInternalFileView() - { - if (fv == null) - return getUI().getFileView(this); - return fv; - } - - /** - * DOCUMENT ME! - * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return The file name. */ public String getName(File f) { - return getInternalFileView().getName(f); + String name = null; + if (fv != null) + name = fv.getName(f); + if (name == null) + name = getUI().getFileView(this).getName(f); + return name; } /** - * DOCUMENT ME! + * Returns the description of the file, generated by the current (or default) + * {@link FileView}. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return The file description. */ public String getDescription(File f) { - return getInternalFileView().getDescription(f); + String result = null; + if (fv != null) + result = fv.getDescription(f); + if (result == null) + result = getUI().getFileView(this).getDescription(f); + return result; } /** - * DOCUMENT ME! + * Returns the type description for the file, generated by the current (or + * default) {@link FileView}. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return The file type description. */ public String getTypeDescription(File f) { - return getInternalFileView().getTypeDescription(f); + String result = null; + if (fv != null) + result = getFileView().getTypeDescription(f); + if (result == null) + result = getUI().getFileView(this).getTypeDescription(f); + return result; } /** - * DOCUMENT ME! + * Returns the icon provided by the current (or default) {@link FileView}. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return An icon representing the file. */ public Icon getIcon(File f) { - return getInternalFileView().getIcon(f); + Icon result = null; + if (fv != null) + result = fv.getIcon(f); + if (result == null) + result = getUI().getFileView(this).getIcon(f); + return result; } /** - * DOCUMENT ME! + * Returns <code>true</code> if the file is traversable, and + * <code>false</code> otherwise. * - * @param f DOCUMENT ME! + * @param f the file or directory. * - * @return DOCUMENT ME! + * @return A boolean. */ public boolean isTraversable(File f) { @@ -1042,11 +1346,12 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns <code>true</code> if the file is accepted by the current + * file filter. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return A boolean. */ public boolean accept(File f) { @@ -1056,9 +1361,10 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sets the file system view for the file chooser and sends a + * {@link PropertyChangeEvent} to all registered listeners. * - * @param fsv DOCUMENT ME! + * @param fsv the file system view. */ public void setFileSystemView(FileSystemView fsv) { @@ -1071,9 +1377,11 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the file system view being used by this file chooser. * - * @return DOCUMENT ME! + * @return The file system view. + * + * @see #setFileSystemView(FileSystemView) */ public FileSystemView getFileSystemView() { @@ -1081,7 +1389,8 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Approves the selection. An {@link ActionEvent} is sent to all registered + * listeners. */ public void approveSelection() { @@ -1090,7 +1399,8 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Cancels the selection. An {@link ActionEvent} is sent to all registered + * listeners. */ public void cancelSelection() { @@ -1099,9 +1409,9 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Adds an {@link ActionListener} to the file chooser. * - * @param l DOCUMENT ME! + * @param l the listener. */ public void addActionListener(ActionListener l) { @@ -1109,9 +1419,9 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Removes an {@link ActionListener} from this file chooser. * - * @param l DOCUMENT ME! + * @param l the listener. */ public void removeActionListener(ActionListener l) { @@ -1126,9 +1436,9 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the action listeners registered with this file chooser. * - * @return DOCUMENT ME! + * @return An array of listeners. */ public ActionListener[] getActionListeners() { @@ -1136,9 +1446,9 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Sends an @link {ActionEvent} to all registered listeners. * - * @param command DOCUMENT ME! + * @param command the action command. */ protected void fireActionPerformed(String command) { @@ -1151,7 +1461,7 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Installs the UI delegate for the current look and feel. */ public void updateUI() { @@ -1160,9 +1470,9 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the UI delegate class identifier. * - * @return DOCUMENT ME! + * @return <code>FileChooserUI</code>. */ public String getUIClassID() { @@ -1170,9 +1480,9 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the UI delegate for the component. * - * @return DOCUMENT ME! + * @return The UI delegate. */ public FileChooserUI getUI() { @@ -1190,12 +1500,29 @@ public class JFileChooser extends JComponent implements Accessible } /** - * DOCUMENT ME! + * Returns the accessible context. * - * @return DOCUMENT ME! + * @return The accessible context. */ public AccessibleContext getAccessibleContext() { - return null; + return new AccessibleJFileChooser(); + } + + /** + * Accessibility support for JFileChooser + */ + protected class AccessibleJFileChooser + extends JComponent.AccessibleJComponent + { + protected AccessibleJFileChooser() + { + // Nothing to do here. + } + + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.FILE_CHOOSER; + } } } diff --git a/libjava/classpath/javax/swing/JFrame.java b/libjava/classpath/javax/swing/JFrame.java index 7081f5980e4..8d4dcb53b3c 100644 --- a/libjava/classpath/javax/swing/JFrame.java +++ b/libjava/classpath/javax/swing/JFrame.java @@ -50,6 +50,7 @@ import java.awt.LayoutManager; import java.awt.event.KeyEvent; import java.awt.event.WindowEvent; +import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; /** @@ -65,8 +66,31 @@ import javax.accessibility.AccessibleContext; * @author Ronald Veldema (rveldema@cs.vu.nl) */ public class JFrame extends Frame - implements WindowConstants, RootPaneContainer + implements WindowConstants, RootPaneContainer, Accessible { + /** + * Provides accessibility support for <code>JFrame</code>s. + */ + protected class AccessibleJFrame extends Frame.AccessibleAWTFrame + { + /** + * Creates a new instance of <code>AccessibleJFrame</code>. + */ + public AccessibleJFrame() + { + super(); + // Nothing to do here. + } + } + + /** + * A flag for {@link #setDefaultCloseOperation(int)}, indicating that the + * application should be exited, when this <code>JFrame</code> is closed. + * + * @since 1.3 + */ + public static final int EXIT_ON_CLOSE = 3; + private static final long serialVersionUID = -3362141868504252139L; private static boolean defaultLookAndFeelDecorated; private int close_action = HIDE_ON_CLOSE; @@ -78,13 +102,6 @@ public class JFrame extends Frame */ protected boolean rootPaneCheckingEnabled = false; - /** - * Tells us if we're in the initialization stage. - * If so, adds go to top-level Container, otherwise they go - * to the content pane for this container. - */ - private boolean initStageDone = false; - public JFrame() { super("JFrame"); @@ -134,7 +151,7 @@ public class JFrame extends Frame enableEvents(AWTEvent.WINDOW_EVENT_MASK); getRootPane(); // will do set/create // We're now done the init stage. - initStageDone = true; + setRootPaneCheckingEnabled(true); } public Dimension getPreferredSize() @@ -156,13 +173,8 @@ public class JFrame extends Frame { // Check if we're in initialization stage. If so, call super.setLayout // otherwise, valid calls go to the content pane. - if (initStageDone) - { - if (isRootPaneCheckingEnabled()) - throw new Error("Cannot set layout. Use getContentPane().setLayout()" - + " instead."); - getContentPane().setLayout(manager); - } + if (isRootPaneCheckingEnabled()) + getContentPane().setLayout(manager); else super.setLayout(manager); } @@ -222,15 +234,10 @@ public class JFrame extends Frame { // If we're adding in the initialization stage use super.add. // Otherwise pass the add onto the content pane. - if (!initStageDone) - super.addImpl(comp, constraints, index); + if (isRootPaneCheckingEnabled()) + getContentPane().add(comp,constraints,index); else - { - if (isRootPaneCheckingEnabled()) - throw new Error("rootPaneChecking is enabled - adding components " - + "disallowed."); - getContentPane().add(comp,constraints,index); - } + super.addImpl(comp, constraints, index); } public void remove(Component comp) @@ -275,6 +282,8 @@ public class JFrame extends Frame public AccessibleContext getAccessibleContext() { + if (accessibleContext == null) + accessibleContext = new AccessibleJFrame(); return accessibleContext; } diff --git a/libjava/classpath/javax/swing/JInternalFrame.java b/libjava/classpath/javax/swing/JInternalFrame.java index b504aaaa5e3..479294b1377 100644 --- a/libjava/classpath/javax/swing/JInternalFrame.java +++ b/libjava/classpath/javax/swing/JInternalFrame.java @@ -437,13 +437,6 @@ public class JInternalFrame extends JComponent implements Accessible, */ protected boolean rootPaneCheckingEnabled = false; - /** - * Tells us if we're in the initialization stage. - * If so, adds go to top-level Container, otherwise they go - * to the content pane for this container. - */ - private boolean initStageDone = false; - /** Whether the JInternalFrame is resizable. */ protected boolean resizable; @@ -567,7 +560,7 @@ public class JInternalFrame extends JComponent implements Accessible, storedBounds = new Rectangle(); setRootPane(createRootPane()); updateUI(); - initStageDone = true; // Done the init stage, now adds go to content pane. + setRootPaneCheckingEnabled(true); // Done the init stage, now adds go to content pane. } /** @@ -587,15 +580,10 @@ public class JInternalFrame extends JComponent implements Accessible, // 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 (!initStageDone) - super.addImpl(comp,constraints, index); + if (isRootPaneCheckingEnabled()) + getContentPane().add(comp, constraints, index); else - { - if (isRootPaneCheckingEnabled()) - throw new Error("Do not use add() on JInternalFrame directly. Use " - + "getContentPane().add() instead"); - getContentPane().add(comp, constraints, index); - } + super.addImpl(comp,constraints, index); } /** @@ -1187,7 +1175,7 @@ public class JInternalFrame extends JComponent implements Accessible, */ protected String paramString() { - return "JInternalFrame"; + return super.paramString(); } /** @@ -1227,8 +1215,7 @@ public class JInternalFrame extends JComponent implements Accessible, public void reshape(int x, int y, int width, int height) { super.reshape(x, y, width, height); - invalidate(); - doLayout(); + revalidate(); } /** @@ -1489,13 +1476,8 @@ public class JInternalFrame extends JComponent implements Accessible, { // Check if we're in initialization stage. If so, call super.setLayout // otherwise, valid calls go to the content pane. - if (initStageDone) - { - if (isRootPaneCheckingEnabled()) - throw new Error("Cannot set layout. Use getContentPane().setLayout()" - + " instead."); - getContentPane().setLayout(manager); - } + if (isRootPaneCheckingEnabled()) + getContentPane().setLayout(manager); else super.setLayout(manager); } @@ -1678,7 +1660,12 @@ public class JInternalFrame extends JComponent implements Accessible, */ 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); } /** @@ -1704,7 +1691,13 @@ public class JInternalFrame extends JComponent implements Accessible, */ 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); } /** diff --git a/libjava/classpath/javax/swing/JLabel.java b/libjava/classpath/javax/swing/JLabel.java index 2e7ad98ddae..a9adc96b2f4 100644 --- a/libjava/classpath/javax/swing/JLabel.java +++ b/libjava/classpath/javax/swing/JLabel.java @@ -331,9 +331,6 @@ public class JLabel extends JComponent implements Accessible, SwingConstants /** The gap between the icon and the text. */ private transient int iconTextGap = 4; - /** The accessible context for this JLabel. */ - private AccessibleJLabel accessibleContext; - /** * Creates a new vertically centered, horizontally on the leading edge * JLabel object with text and no icon. @@ -403,6 +400,7 @@ public class JLabel extends JComponent implements Accessible, SwingConstants this.text = text; this.icon = icon; this.horizontalAlignment = horizontalAlignment; + setAlignmentX(0.0F); updateUI(); } @@ -477,12 +475,14 @@ public class JLabel extends JComponent implements Accessible, SwingConstants { if (text != newText) { - String oldText = text; - text = newText; - firePropertyChange("text", oldText, newText); - - if (text != null && text.length() <= displayedMnemonicIndex) - setDisplayedMnemonicIndex(text.length() - 1); + String oldText = text; + text = newText; + firePropertyChange("text", oldText, newText); + + if (text != null && text.length() <= displayedMnemonicIndex) + setDisplayedMnemonicIndex(text.length() - 1); + revalidate(); + repaint(); } } diff --git a/libjava/classpath/javax/swing/JLayeredPane.java b/libjava/classpath/javax/swing/JLayeredPane.java index 1ea39dc5007..346570d95b1 100644 --- a/libjava/classpath/javax/swing/JLayeredPane.java +++ b/libjava/classpath/javax/swing/JLayeredPane.java @@ -38,14 +38,20 @@ 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.awt.Shape; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import java.util.TreeMap; import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; /** * A container that adds depth to the usual <code>Container</code> semantics. @@ -116,6 +122,30 @@ import javax.accessibility.Accessible; */ public class JLayeredPane extends JComponent implements Accessible { + + /** + * Provides accessibility support for <code>JLayeredPane</code>. + */ + protected class AccessibleJLayeredPane extends AccessibleJComponent + { + /** + * Creates a new instance of <code>AccessibleJLayeredPane</code>. + */ + public AccessibleJLayeredPane() + { + // Nothing to do here. + } + + /** + * Returns the accessble role of <code>JLayeredPane</code>, + * {@link AccessibleRole#LAYERED_PANE}. + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.LAYERED_PANE; + } + } + private static final long serialVersionUID = 5534920399324590459L; public static final String LAYER_PROPERTY = "layeredContainerLayer"; @@ -131,13 +161,15 @@ public class JLayeredPane extends JComponent implements Accessible TreeMap layers; // Layer Number (Integer) -> Layer Size (Integer) Hashtable componentToLayer; // Component -> Layer Number (Integer) + private transient Rectangle rectCache; + public JLayeredPane() { layers = new TreeMap (); componentToLayer = new Hashtable (); + setLayout(null); } - /** * Looks up the layer a child component is currently assigned to. * @@ -223,29 +255,37 @@ public class JLayeredPane extends JComponent implements Accessible ret[1] = getComponents ().length; Iterator i = layers.entrySet ().iterator (); while (i.hasNext()) - { + { Map.Entry pair = (Map.Entry) i.next(); Integer layerNum = (Integer) pair.getKey (); Integer layerSz = (Integer) pair.getValue (); - if (layerNum.intValue() == layer.intValue()) + int layerInt = layerNum.intValue(); + if (layerInt == layer.intValue()) { ret[0] = ret[1] - layerSz.intValue (); - return ret; + break; + } + // In the following case there exists no layer with the specified + // number, so we return an empty interval here with the index at which + // such a layer would be inserted + else if (layerInt > layer.intValue()) + { + ret[1] = ret[0]; + break; } else { ret[1] -= layerSz.intValue (); } - } - // should have found the layer during iteration - throw new IllegalArgumentException (); + } + return ret; } /** * Increments the recorded size of a given layer. * * @param layer the layer number to increment. - * @see #incrLayer() + * @see #incrLayer */ private void incrLayer(Integer layer) { @@ -259,7 +299,7 @@ public class JLayeredPane extends JComponent implements Accessible * Decrements the recorded size of a given layer. * * @param layer the layer number to decrement. - * @see #decrLayer() + * @see #incrLayer */ private void decrLayer(Integer layer) { @@ -546,26 +586,15 @@ public class JLayeredPane extends JComponent implements Accessible * * @param index the index of the child component to remove. */ - public void remove (int index) + public void remove(int index) { - Component c = getComponent (index); - int layer = getLayer (c); - decrLayer (new Integer(layer)); - componentToLayer.remove (c); - super.remove (index); + Component c = getComponent(index); + int layer = getLayer(c); + decrLayer(new Integer(layer)); + componentToLayer.remove(c); + super.remove(index); + // FIXME: Figure out if this call is correct. revalidate(); - repaint(); - } - - /** - * Removes a child from this container. The child is specified directly. - * After removal, the child no longer occupies a layer. - * - * @param comp the child to remove. - */ - public void remove (Component comp) - { - remove (getIndexOf (comp)); } /** @@ -613,7 +642,7 @@ public class JLayeredPane extends JComponent implements Accessible * @param index an ignored parameter, for compatibility. */ protected void addImpl(Component comp, Object layerConstraint, int index) - { + { Integer layer; if (layerConstraint != null && layerConstraint instanceof Integer) layer = (Integer) layerConstraint; @@ -627,10 +656,8 @@ public class JLayeredPane extends JComponent implements Accessible componentToLayer.put (comp, layer); incrLayer (layer); - super.addImpl(comp, null, newIdx); - revalidate(); - repaint(); - } + super.addImpl(comp, null, newIdx); + } /** * Sets the layer property for a JComponent. @@ -642,4 +669,50 @@ public class JLayeredPane extends JComponent implements Accessible { getLayeredPaneAbove(component).setLayer(component, layer); } + + /** + * Returns the accessible context for this <code>JLayeredPane</code>. + * + * @return the accessible context for this <code>JLayeredPane</code> + */ + public AccessibleContext getAccessibleContext() + { + if (accessibleContext == null) + accessibleContext = new AccessibleJLayeredPane(); + return accessibleContext; + } + + /** + * This method is overridden order to provide a reasonable painting + * mechanism for <code>JLayeredPane</code>. This is necessary since + * <code>JLayeredPane</code>'s do not have an own UI delegate. + * + * Basically this method clears the background for the + * <code>JLayeredPane</code> and then calls <code>super.paint(g)</code>. + * + * @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); + } + + /** + * Overridden to return <code>false</code>, since <code>JLayeredPane</code> + * cannot guarantee that its children don't overlap. + * + * @return <code>false</code> + */ + public boolean isOptimizedDrawingEnabled() + { + return false; + } } diff --git a/libjava/classpath/javax/swing/JList.java b/libjava/classpath/javax/swing/JList.java index 92fe1ccfa67..4f5d3cc72c5 100644 --- a/libjava/classpath/javax/swing/JList.java +++ b/libjava/classpath/javax/swing/JList.java @@ -41,13 +41,25 @@ package javax.swing; 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; @@ -108,6 +120,736 @@ import javax.swing.text.Position; public class JList extends JComponent implements Accessible, Scrollable { + + /** + * Provides accessibility support for <code>JList</code>. + */ + protected class AccessibleJList extends AccessibleJComponent + implements AccessibleSelection, PropertyChangeListener, + ListSelectionListener, ListDataListener + { + + /** + * Provides accessibility support for list elements in <code>JList</code>s. + */ + 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 <code>AccessibleJListChild</code>. + * + * @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 + * <code>this</code> since <code>AccessibleJListChild</code>s are their + * own accessible contexts. + * + * @return the accessible context of this object, <code>this</code> + */ + public AccessibleContext getAccessibleContext() + { + return this; + } + + /** + * Returns the background color for this list child. This returns the + * background of the <code>JList</code> 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 <code>JList</code> 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 <code>JList</code> since it is not possible to + * set fonts for list children individually. + * + * @return the font of the <code>JList</code> + */ + 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 <code>JList</code>. + * + * @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 <code>true</code> if the parent <code>JList</code> is enabled, + * <code>false</code> otherwise. The list children cannot have an enabled + * flag set individually. + * + * @return <code>true</code> if the parent <code>JList</code> is enabled, + * <code>false</code> 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 <code>true</code> if this list child is visible, + * <code>false</code> otherwise. The value of this property depends + * on {@link JList#getFirstVisibleIndex()} and + * {@link JList#getLastVisibleIndex()}. + * + * @return <code>true</code> if this list child is visible, + * <code>false</code> 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 <code>true</code> if this list child is currently showing on + * screen and <code>false</code> otherwise. The list child is showing if + * it is visible and if it's parent JList is currently showing. + * + * @return <code>true</code> if this list child is currently showing on + * screen and <code>false</code> otherwise + */ + public boolean isShowing() + { + return isVisible() && parent.isShowing(); + } + + /** + * Returns <code>true</code> if this list child covers the screen location + * <code>point</code> (relative to the <code>JList</code> coordinate + * system, <code>false</code> otherwise. + * + * @return <code>true</code> if this list child covers the screen location + * <code>point</code> , <code>false</code> 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 <code>null</code> because list children do not have children + * themselves + * + * @return <code>null</code> + */ + public Accessible getAccessibleAt(Point point) + { + return null; + } + + /** + * Returns <code>true</code> 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 <code>0</code> since list children don't have children + * themselves. + * + * @return <code>0</code> + */ + public int getAccessibleChildrenCount() + { + return 0; + } + + /** + * Returns <code>null</code> since list children don't have children + * themselves. + * + * @return <code>null</code> + */ + 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 <code>true</code> if the n-th child is selected, + * <code>false</code> otherwise. + * + * @param n the index of the child of which the selected state is queried + * + * @return <code>true</code> if the n-th child is selected, + * <code>false</code> 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 + * <code>JList</code>. 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 + * <code>JList</code>. 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 + * <code>JList</code>. 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 <code>JList</code>'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 <code>JList</code>. + * + * @return the state set of the <code>JList</code> + */ + 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 <code>JList</code>, + * {@link AccessibleRole#LIST}. + * + * @return the accessible role for <code>JList</code> + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.LIST; + } + + /** + * Returns the accessible child at the visual location <code>p</code> + * (relative to the upper left corner of the <code>JList</code>). If there + * is no child at that location, this returns <code>null</code>. + * + * @param p the screen location for which to return the accessible child + * + * @return the accessible child at the specified location, or + * <code>null</code> 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 <code>JList</code>. + * + * @return the number of accessible children in the <code>JList</code> + */ + public int getAccessibleChildrenCount() + { + return getModel().getSize(); + } + + /** + * Returns the n-th accessible child of this <code>JList</code>. This will + * be an instance of {@link AccessibleJListChild}. If there is no child + * at that index, <code>null</code> is returned. + * + * @param n the index of the child to return + * + * @return the n-th accessible child of this <code>JList</code> + */ + public Accessible getAccessibleChild(int n) + { + if (getModel().getSize() <= n) + return null; + return new AccessibleJListChild(JList.this, n); + } + } + private static final long serialVersionUID = 4406629526391098046L; /** @@ -181,7 +923,7 @@ public class JList extends JComponent implements Accessible, Scrollable /** * This property specifies a foreground color for the selected cells in - * the list. When {@link ListCellRenderer.getListCellRendererComponent} + * the list. When {@link ListCellRenderer#getListCellRendererComponent} * is called with a selected cell object, the component returned will * have its "foreground" set to this color. */ @@ -189,7 +931,7 @@ public class JList extends JComponent implements Accessible, Scrollable /** * This property specifies a background color for the selected cells in - * the list. When {@link ListCellRenderer.getListCellRendererComponent} + * 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. */ @@ -216,9 +958,9 @@ public class JList extends JComponent implements Accessible, Scrollable /** * This property indicates a <em>preference</em> for the number of rows * displayed in the list, and will scale the - * {@link #preferredScrollableViewportSize} property accordingly. The actual + * {@link #getPreferredScrollableViewportSize} property accordingly. The actual * number of displayed rows, when the list is placed in a real {@link - * Viewport} or other component, may be greater or less than this number. + * JViewport} or other component, may be greater or less than this number. */ int visibleRowCount; @@ -270,7 +1012,7 @@ public class JList extends JComponent implements Accessible, Scrollable event.getValueIsAdjusting()); JList.this.repaint(); } - }; + } /** * Shared ListListener instance, subscribed to both the current {@link @@ -437,7 +1179,7 @@ public class JList extends JComponent implements Accessible, Scrollable /** * Sets the value of the {@link #visibleRowCount} property. * - * @param visibleRowCount The new property value + * @param vc The new property value */ public void setVisibleRowCount(int vc) { @@ -563,8 +1305,8 @@ public class JList extends JComponent implements Accessible, Scrollable /** * Returns the list index of the upper left or upper right corner of the - * {@link #visibleRect} property, depending on the {@link - * #componentOrientation} property. + * visible rectangle of this list, depending on the {@link + * Component#getComponentOrientation} property. * * @return The index of the first visible list cell, or <code>-1</code> * if none is visible. @@ -585,7 +1327,8 @@ public class JList extends JComponent implements Accessible, Scrollable * * @return index of the cell to which specified location is closest to. */ - public int locationToIndex(Point location) { + public int locationToIndex(Point location) + { return getUI().locationToIndex(this, location); } @@ -595,14 +1338,15 @@ public class JList extends JComponent implements Accessible, Scrollable * * @return location of the cell located at the specified index in the list. */ - public Point indexToLocation(int index){ - return getCellBounds(index, index).getLocation(); + public Point indexToLocation(int index) + { + return getUI().indexToLocation(this, index); } /** * Returns the list index of the lower right or lower left corner of the - * {@link #visibleRect} property, depending on the {@link - * #componentOrientation} property. + * visible rectangle of this list, depending on the {@link + * Component#getComponentOrientation} property. * * @return The index of the last visible list cell, or <code>-1</code> * if none is visible. @@ -625,7 +1369,7 @@ public class JList extends JComponent implements Accessible, Scrollable * selected. * * @return An array of model indices, each of which is selected according - * to the {@link #selection} property + * to the {@link #getSelectedValues} property */ public int[] getSelectedIndices() { @@ -640,7 +1384,7 @@ public class JList extends JComponent implements Accessible, Scrollable n++; int [] v = new int[n]; j = 0; - for (i = lo; i < hi; ++i) + for (i = lo; i <= hi; ++i) if (selectionModel.isSelectedIndex(i)) v[j++] = i; return v; @@ -670,7 +1414,7 @@ public class JList extends JComponent implements Accessible, Scrollable * @return The first selected element, or <code>null</code> if no element * is selected. * - * @see getSelectedValues + * @see #getSelectedValues */ public Object getSelectedValue() { @@ -686,7 +1430,7 @@ public class JList extends JComponent implements Accessible, Scrollable * * @return An array containing all the selected values * - * @see getSelectedValue + * @see #setSelectedValue */ public Object[] getSelectedValues() { @@ -845,7 +1589,7 @@ public class JList extends JComponent implements Accessible, Scrollable } /** - * Sets the value of the {@link #celLRenderer} property. + * Sets the value of the {@link #getCellRenderer} property. * * @param renderer The new property value */ @@ -876,10 +1620,15 @@ public class JList extends JComponent implements Accessible, Scrollable * #listListener} is unsubscribed from the existing model, if it exists, * and re-subscribed to the new model. * - * @param model The new property value + * @param model the new model (<code>null</code> not permitted). + * + * @throws IllegalArgumentException if <code>model</code> is + * <code>null</code>. */ public void setModel(ListModel model) { + if (model == null) + throw new IllegalArgumentException("Null 'model' argument."); if (this.model == model) return; @@ -1019,14 +1768,14 @@ public class JList extends JComponent implements Accessible, Scrollable public AccessibleContext getAccessibleContext() { - return null; + 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 Viewport} to define scrollable objects. + * ScrollPaneLayout} and {@link JViewport} to define scrollable objects. * * @return The preferred size */ @@ -1036,36 +1785,43 @@ public class JList extends JComponent implements Accessible, Scrollable //return the value from getPreferredSize. The current ListUI is //expected to override getPreferredSize to return an appropriate value. if (getLayoutOrientation() != VERTICAL) - return getPreferredSize(); + 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, getModel().getSize() * - fixedCellHeight); + 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()); + } - int prefWidth, prefHeight; + // Calculate the width: if fixedCellWidth was set use that, otherwise + // use the preferredWidth + int prefWidth; if (fixedCellWidth != -1) prefWidth = fixedCellWidth; else - { - prefWidth = 0; - int size = getModel().getSize(); - for (int i = 0; i < size; i++) - if (getCellBounds(i, i).width > prefWidth) - prefWidth = getCellBounds(i, i).width; - } - - if (getModel().getSize() == 0 && fixedCellWidth == -1) - return new Dimension(256, 16 * getVisibleRowCount()); - else if (getModel().getSize() == 0) - return new Dimension (fixedCellWidth, 16 * getVisibleRowCount()); - + 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 - (getFirstVisibleIndex(), getFirstVisibleIndex()).height; - } + prefHeight = getVisibleRowCount() * getCellBounds(0, 0).height; + return new Dimension (prefWidth, prefHeight); } @@ -1196,7 +1952,7 @@ public class JList extends JComponent implements Accessible, Scrollable } /** - * Gets the value of the {@link #scrollableTracksViewportWidth} property. + * Gets the value of the <code>scrollableTracksViewportWidth</code> property. * * @return <code>true</code> if the viewport is larger (horizontally) * than the list and the list should be expanded to fit the viewport; @@ -1221,7 +1977,7 @@ public class JList extends JComponent implements Accessible, Scrollable } /** - * Gets the value of the {@link #scrollableTracksViewportWidth} property. + * Gets the value of the </code>scrollableTracksViewportWidth</code> property. * * @return <code>true</code> if the viewport is larger (vertically) * than the list and the list should be expanded to fit the viewport; @@ -1373,7 +2129,7 @@ public class JList extends JComponent implements Accessible, Scrollable */ public Rectangle getCellBounds(int index0, int index1) { - return ((ListUI) ui).getCellBounds(this, index0, index1); + return getUI().getCellBounds(this, index0, index1); } /** @@ -1383,8 +2139,8 @@ public class JList extends JComponent implements Accessible, Scrollable * * @param prefix the prefix to search for in the cell values * @param startIndex the index where to start searching from - * @param bias the search direction, either {@link Position.Bias.Forward} - * or {@link Position.Bias.Backward} + * @param bias the search direction, either {@link Position.Bias#Forward} + * or {@link Position.Bias#Backward} * * @return the index of the found element or -1 if no such element has * been found diff --git a/libjava/classpath/javax/swing/JMenu.java b/libjava/classpath/javax/swing/JMenu.java index 8dcad8b77cd..9734eb8732f 100644 --- a/libjava/classpath/javax/swing/JMenu.java +++ b/libjava/classpath/javax/swing/JMenu.java @@ -45,8 +45,6 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.io.IOException; -import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.EventListener; @@ -137,10 +135,6 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement this(text); } - private void writeObject(ObjectOutputStream stream) throws IOException - { - } - /** * Adds specified menu item to this menu * @@ -768,6 +762,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ protected void processKeyEvent(KeyEvent event) { + // TODO: Implement this properly. } /** @@ -812,6 +807,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement return accessibleContext; } + // FIXME: This inner class is a complete stub and needs to be implemented. protected class AccessibleJMenu extends AccessibleJMenuItem implements AccessibleSelection { @@ -819,6 +815,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement protected AccessibleJMenu() { + // Nothing to do here. } public int getAccessibleChildrenCount() @@ -858,32 +855,48 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement public void addAccessibleSelection(int value0) { + // TODO: Implement this properly. } public void removeAccessibleSelection(int value0) { + // TODO: Implement this properly. } public void clearAccessibleSelection() { + // TODO: Implement this properly. } public void selectAllAccessibleSelection() { + // TODO: Implement this properly. } } protected class WinListener extends WindowAdapter implements Serializable { - JPopupMenu popupMenu; private static final long serialVersionUID = -6415815570638474823L; + /** + * Creates a new <code>WinListener</code>. + * + * @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); } } diff --git a/libjava/classpath/javax/swing/JMenuBar.java b/libjava/classpath/javax/swing/JMenuBar.java index eebb1a050be..f018daabf80 100644 --- a/libjava/classpath/javax/swing/JMenuBar.java +++ b/libjava/classpath/javax/swing/JMenuBar.java @@ -46,6 +46,9 @@ 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; /** @@ -59,6 +62,137 @@ import javax.swing.plaf.MenuBarUI; */ public class JMenuBar extends JComponent implements Accessible, MenuElement { + /** + * Provides accessibility support for <code>JMenuBar</code>. + * + * @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 <code>0</code> if nothing is selected, or <code>1</code> 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 <code>i</code> menu, or + * <code>null</code> if the specified menu is not selected. + * + * @param i the index of the menu to return + * + * @return the selected with index <code>i</code> menu, or + * <code>null</code> if the specified menu is not selected + */ + public Accessible getAccessibleSelection(int i) + { + if (getSelectionModel().getSelectedIndex() != i) + return null; + return getMenu(i); + } + + /** + * Returns <code>true</code> if the specified menu is selected, + * <code>false</code> otherwise. + * + * @param i the index of the menu to check + * + *@return <code>true</code> if the specified menu is selected, + * <code>false</code> otherwise + */ + public boolean isAccessibleChildSelected(int i) + { + return getSelectionModel().getSelectedIndex() == i; + } + + /** + * Selects the menu with index <code>i</code>. 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 <code>i</code>. + * + * @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 <code>JMenuBar</code>, which is + * {@link AccessibleRole#MENU_BAR}. + * + * @return the accessible role of <code>JMenuBar</code>, which is + * {@link AccessibleRole#MENU_BAR} + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.MENU_BAR; + } + + /** + * Returns the <code>AccessibleSelection</code> for this object. This + * method returns <code>this</code>, since the + * <code>AccessibleJMenuBar</code> manages its selection itself. + * + * @return the <code>AccessibleSelection</code> for this object + */ + public AccessibleSelection getAccessibleSelection() + { + return this; + } + + /** + * Returns the state of this <code>AccessibleJMenuBar</code>. + * + * @return the state of this <code>AccessibleJMenuBar</code>. + */ + 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 */ @@ -100,13 +234,15 @@ public class JMenuBar extends JComponent implements Accessible, MenuElement */ public void addNotify() { - // FIXME: Should register this menu bar with the keyboard manager super.addNotify(); + KeyboardManager.getManager().registerJMenuBar(this); } public AccessibleContext getAccessibleContext() { - return null; + if (accessibleContext == null) + accessibleContext = new AccessibleJMenuBar(); + return accessibleContext; } /** @@ -253,7 +389,7 @@ public class JMenuBar extends JComponent implements Accessible, MenuElement * 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. "MenuItemUI" + * @return The Look and Feel classID. "MenuBarUI" */ public String getUIClassID() { @@ -338,6 +474,63 @@ public class JMenuBar extends JComponent implements Accessible, MenuElement } /** + * 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 <code>menuElement</code> 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) + { + // 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. * @@ -358,7 +551,7 @@ public class JMenuBar extends JComponent implements Accessible, MenuElement */ public void removeNotify() { - // Must unregister this menu bar with the current keyboard manager. + KeyboardManager.getManager().unregisterJMenuBar(this); super.removeNotify(); } @@ -384,9 +577,14 @@ public class JMenuBar extends JComponent implements Accessible, MenuElement * 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."); } /** diff --git a/libjava/classpath/javax/swing/JMenuItem.java b/libjava/classpath/javax/swing/JMenuItem.java index 069b7bc86f8..c87a4dc2b20 100644 --- a/libjava/classpath/javax/swing/JMenuItem.java +++ b/libjava/classpath/javax/swing/JMenuItem.java @@ -44,9 +44,6 @@ import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.util.EventListener; import javax.accessibility.Accessible; @@ -84,6 +81,7 @@ public class JMenuItem extends AbstractButton implements Accessible, public JMenuItem() { super(); + init(null, null); } /** @@ -118,6 +116,7 @@ public class JMenuItem extends AbstractButton implements Accessible, { super(); super.setAction(action); + init(null, null); } /** @@ -147,15 +146,6 @@ public class JMenuItem extends AbstractButton implements Accessible, setMnemonic(mnemonic); } - private void readObject(ObjectInputStream stream) - throws IOException, ClassNotFoundException - { - } - - private void writeObject(ObjectOutputStream stream) throws IOException - { - } - /** * Initializes this menu item * @@ -176,7 +166,7 @@ public class JMenuItem extends AbstractButton implements Accessible, //borderPainted = false; focusPainted = false; horizontalAlignment = JButton.LEFT; - horizontalTextPosition = JButton.LEFT; + horizontalTextPosition = JButton.TRAILING; } /** @@ -189,7 +179,7 @@ public class JMenuItem extends AbstractButton implements Accessible, { super.setUI(ui); } - + /** * This method sets this menuItem's UI to the UIManager's default for the * current look and feel. @@ -255,13 +245,18 @@ public class JMenuItem extends AbstractButton implements Accessible, } /** - * Sets accelerator for this menu item. - * + * 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); } /** @@ -276,7 +271,11 @@ public class JMenuItem extends AbstractButton implements Accessible, super.configurePropertiesFromAction(action); if (! (this instanceof JMenu) && action != null) - setAccelerator((KeyStroke) (action.getValue(Action.ACCELERATOR_KEY))); + { + setAccelerator((KeyStroke) (action.getValue(Action.ACCELERATOR_KEY))); + super.registerKeyboardAction(action, accelerator, + JComponent.WHEN_IN_FOCUSED_WINDOW); + } } /** @@ -667,6 +666,7 @@ public class JMenuItem extends AbstractButton implements Accessible, public void stateChanged(ChangeEvent event) { + // TODO: What should be done here, if anything? } public AccessibleRole getAccessibleRole() diff --git a/libjava/classpath/javax/swing/JOptionPane.java b/libjava/classpath/javax/swing/JOptionPane.java index ad0772ab8d3..057326cd209 100644 --- a/libjava/classpath/javax/swing/JOptionPane.java +++ b/libjava/classpath/javax/swing/JOptionPane.java @@ -1,5 +1,5 @@ /* JOptionPane.java - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,7 +39,6 @@ exception statement from your version. */ package javax.swing; import java.awt.Component; -import java.awt.Dimension; import java.awt.Frame; import javax.accessibility.Accessible; @@ -60,16 +59,19 @@ public class JOptionPane extends JComponent implements Accessible /** * DOCUMENT ME! */ + // FIXME: This inner class is a complete stub and needs to be implemented + // properly. protected class AccessibleJOptionPane extends JComponent.AccessibleJComponent { /** DOCUMENT ME! */ private static final long serialVersionUID = 686071432213084821L; - + /** * Creates a new AccessibleJOptionPane object. */ protected AccessibleJOptionPane() { + // Nothing to do here. } /** @@ -343,8 +345,6 @@ public class JOptionPane extends JComponent implements Accessible setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); updateUI(); - invalidate(); - repaint(); } /** @@ -369,17 +369,12 @@ public class JOptionPane extends JComponent implements Accessible inputValue = UNINITIALIZED_VALUE; value = UNINITIALIZED_VALUE; - // FIXME: This dialog should be centered on the parent - // or at the center of the screen (if the parent is null) - // Need getGraphicsConfiguration to return non-null in - // order for that to work so we know how large the - // screen is. dialog.getContentPane().add(this); dialog.setModal(true); dialog.setResizable(false); - dialog.invalidate(); - dialog.repaint(); - + dialog.pack(); + dialog.setLocationRelativeTo(parentComponent); + return dialog; } @@ -513,6 +508,8 @@ public class JOptionPane extends JComponent implements Accessible */ public Object getInputValue() { + if (getValue().equals(new Integer(CANCEL_OPTION))) + setInputValue(null); return inputValue; } @@ -858,13 +855,13 @@ public class JOptionPane extends JComponent implements Accessible */ public static int showConfirmDialog(Component parentComponent, Object message) { - JOptionPane pane = new JOptionPane(message); + JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE); JDialog dialog = pane.createDialog(parentComponent, "Select an Option"); - - dialog.pack(); dialog.show(); - - return ((Integer) pane.getValue()).intValue(); + + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -886,10 +883,11 @@ public class JOptionPane extends JComponent implements Accessible { JOptionPane pane = new JOptionPane(message, PLAIN_MESSAGE, optionType); JDialog dialog = pane.createDialog(parentComponent, title); - dialog.pack(); dialog.show(); - return ((Integer) pane.getValue()).intValue(); + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -912,10 +910,11 @@ public class JOptionPane extends JComponent implements Accessible { JOptionPane pane = new JOptionPane(message, messageType, optionType); JDialog dialog = pane.createDialog(parentComponent, title); - dialog.pack(); dialog.show(); - return ((Integer) pane.getValue()).intValue(); + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -940,10 +939,11 @@ public class JOptionPane extends JComponent implements Accessible { JOptionPane pane = new JOptionPane(message, messageType, optionType, icon); JDialog dialog = pane.createDialog(parentComponent, title); - dialog.pack(); dialog.show(); - return ((Integer) pane.getValue()).intValue(); + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -964,9 +964,8 @@ public class JOptionPane extends JComponent implements Accessible JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE); pane.setWantsInput(true); JDialog dialog = pane.createDialog(parentComponent, null); - dialog.pack(); dialog.show(); - + return (String) pane.getInputValue(); } @@ -991,9 +990,8 @@ public class JOptionPane extends JComponent implements Accessible pane.setInitialSelectionValue(initialSelectionValue); pane.setWantsInput(true); JDialog dialog = pane.createDialog(parentComponent, null); - dialog.pack(); dialog.show(); - + return (String) pane.getInputValue(); } @@ -1017,9 +1015,8 @@ public class JOptionPane extends JComponent implements Accessible JOptionPane pane = new JOptionPane(message, messageType); pane.setWantsInput(true); JDialog dialog = pane.createDialog(parentComponent, title); - dialog.pack(); dialog.show(); - + return (String) pane.getInputValue(); } @@ -1050,10 +1047,9 @@ public class JOptionPane extends JComponent implements Accessible pane.setSelectionValues(selectionValues); pane.setInitialSelectionValue(initialSelectionValue); JDialog dialog = pane.createDialog(parentComponent, title); - dialog.pack(); dialog.show(); - - return (String) pane.getInputValue(); + + return pane.getInputValue(); } /** @@ -1071,9 +1067,8 @@ public class JOptionPane extends JComponent implements Accessible JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE); pane.setWantsInput(true); JDialog dialog = pane.createDialog(null, null); - dialog.pack(); dialog.show(); - + return (String) pane.getInputValue(); } @@ -1096,9 +1091,8 @@ public class JOptionPane extends JComponent implements Accessible pane.setWantsInput(true); pane.setInitialSelectionValue(initialSelectionValue); JDialog dialog = pane.createDialog(null, null); - dialog.pack(); dialog.show(); - + return (String) pane.getInputValue(); } @@ -1120,8 +1114,10 @@ public class JOptionPane extends JComponent implements Accessible JInternalFrame frame = pane.createInternalFrame(parentComponent, null); startModal(frame); - - return ((Integer) pane.getValue()).intValue(); + + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -1146,7 +1142,9 @@ public class JOptionPane extends JComponent implements Accessible startModal(frame); - return ((Integer) pane.getValue()).intValue(); + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -1172,7 +1170,9 @@ public class JOptionPane extends JComponent implements Accessible startModal(frame); - return ((Integer) pane.getValue()).intValue(); + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -1200,7 +1200,9 @@ public class JOptionPane extends JComponent implements Accessible startModal(frame); - return ((Integer) pane.getValue()).intValue(); + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -1222,7 +1224,7 @@ public class JOptionPane extends JComponent implements Accessible JInternalFrame frame = pane.createInternalFrame(parentComponent, null); startModal(frame); - + return (String) pane.getInputValue(); } @@ -1248,7 +1250,7 @@ public class JOptionPane extends JComponent implements Accessible JInternalFrame frame = pane.createInternalFrame(parentComponent, title); startModal(frame); - + return (String) pane.getInputValue(); } @@ -1283,8 +1285,8 @@ public class JOptionPane extends JComponent implements Accessible JInternalFrame frame = pane.createInternalFrame(parentComponent, title); startModal(frame); - - return (String) pane.getInputValue(); + + return pane.getInputValue(); } /** @@ -1376,8 +1378,10 @@ public class JOptionPane extends JComponent implements Accessible JInternalFrame frame = pane.createInternalFrame(parentComponent, title); startModal(frame); - - return ((Integer) pane.getValue()).intValue(); + + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -1391,8 +1395,7 @@ public class JOptionPane extends JComponent implements Accessible { JOptionPane pane = new JOptionPane(message, INFORMATION_MESSAGE); JDialog dialog = pane.createDialog(parentComponent, null); - dialog.pack(); - dialog.show(); + dialog.show(); } /** @@ -1410,7 +1413,6 @@ public class JOptionPane extends JComponent implements Accessible { JOptionPane pane = new JOptionPane(message, messageType); JDialog dialog = pane.createDialog(parentComponent, title); - dialog.pack(); dialog.show(); } @@ -1431,7 +1433,6 @@ public class JOptionPane extends JComponent implements Accessible JOptionPane pane = new JOptionPane(message, messageType); pane.setIcon(icon); JDialog dialog = pane.createDialog(parentComponent, title); - dialog.pack(); dialog.show(); } @@ -1461,10 +1462,11 @@ public class JOptionPane extends JComponent implements Accessible options, initialValue); JDialog dialog = pane.createDialog(parentComponent, title); - dialog.pack(); dialog.show(); - return ((Integer) pane.getValue()).intValue(); + if (pane.getValue() instanceof Integer) + return ((Integer) pane.getValue()).intValue(); + return -1; } /** @@ -1524,34 +1526,34 @@ public class JOptionPane extends JComponent implements Accessible * JInternalFrame's preferred size. * * @param f The JInternalFrame to make modal. - * @param pane The JOptionPane to add to the JInternalFrame. */ private static void startModal(JInternalFrame f) { synchronized (f) - { - final JInternalFrame tmp = f; - tmp.toFront(); - - f.addInternalFrameListener(new InternalFrameAdapter() - { - public void internalFrameClosed(InternalFrameEvent e) - { - synchronized (tmp) - { - tmp.removeInternalFrameListener(this); - tmp.notifyAll(); - } - } - }); - try - { - while (! f.isClosed()) - f.wait(); - } - catch (InterruptedException ignored) - { - } - } + { + final JInternalFrame tmp = f; + tmp.toFront(); + + f.addInternalFrameListener(new InternalFrameAdapter() + { + public void internalFrameClosed(InternalFrameEvent e) + { + synchronized (tmp) + { + tmp.removeInternalFrameListener(this); + tmp.notifyAll(); + } + } + }); + try + { + while (! f.isClosed()) + f.wait(); + } + catch (InterruptedException ignored) + { + // Ignore this Exception. + } + } } } diff --git a/libjava/classpath/javax/swing/JPanel.java b/libjava/classpath/javax/swing/JPanel.java index c7f7c448331..7805e92b6e9 100644 --- a/libjava/classpath/javax/swing/JPanel.java +++ b/libjava/classpath/javax/swing/JPanel.java @@ -43,6 +43,7 @@ import java.awt.LayoutManager; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; import javax.swing.plaf.PanelUI; /** @@ -52,63 +53,90 @@ import javax.swing.plaf.PanelUI; */ public class JPanel extends JComponent implements Accessible { - public JPanel() + /** + * Provides accessibility support for <code>JPanel</code>. + * + * @author Roman Kennke (roman@kennke.org) + */ + protected class AccessibleJPanel extends AccessibleJComponent + { + /** + * Creates a new instance of <code>AccessibleJPanel</code>. + */ + public AccessibleJPanel() { - this(new FlowLayout(), - true); + // Nothing to do here. } - - public JPanel(boolean double_buffered) - { - this(new FlowLayout(), - double_buffered); - } - - public JPanel(LayoutManager layout) - { - this(layout, - true); - } - - - public JPanel(LayoutManager layout, - boolean isDoubleBuffered) - { - if (layout == null) - { - System.err.println("NO LAYOUT SET !!!"); - layout = new FlowLayout(); - } - setLayout(layout); - setOpaque(true); - - updateUI(); - } - - public String getUIClassID() - { return "PanelUI"; } - - public void setUI(PanelUI ui) { - super.setUI(ui); - } - - public PanelUI getUI() { - return (PanelUI)ui; - } - - public void updateUI() { - setUI((PanelUI)UIManager.getUI(this)); - } - - - public AccessibleContext getAccessibleContext() + /** + * Returns the accessible role for <code>JPanel</code>, which is + * {@link AccessibleRole#PANEL}. + * + * @return the accessible role for <code>JPanel</code> + */ + public AccessibleRole getAccessibleRole() { - return null; + return AccessibleRole.PANEL; } + } + + public JPanel() + { + this(new FlowLayout(), true); + } + + public JPanel(boolean double_buffered) + { + this(new FlowLayout(), double_buffered); + } + + public JPanel(LayoutManager layout) + { + this(layout, true); + } + + public JPanel(LayoutManager layout, boolean isDoubleBuffered) + { + if (layout == null) + { + // TODO: Is this correct? Or should we throw a NPE? + layout = new FlowLayout(); + } + setLayout(layout); + setOpaque(true); + + updateUI(); + } + + public String getUIClassID() + { + return "PanelUI"; + } + + public void setUI(PanelUI ui) + { + super.setUI(ui); + } + + public PanelUI getUI() + { + return (PanelUI) ui; + } + + public void updateUI() + { + setUI((PanelUI) UIManager.getUI(this)); + } + + public AccessibleContext getAccessibleContext() + { + if (accessibleContext == null) + accessibleContext = new AccessibleJPanel(); + return accessibleContext; + } - protected String paramString() - { + protected String paramString() + { return "JPanel"; - } + } } diff --git a/libjava/classpath/javax/swing/JPasswordField.java b/libjava/classpath/javax/swing/JPasswordField.java index 151d2484a82..11e45e8a58a 100644 --- a/libjava/classpath/javax/swing/JPasswordField.java +++ b/libjava/classpath/javax/swing/JPasswordField.java @@ -67,6 +67,7 @@ public class JPasswordField extends JTextField */ protected AccessibleJPasswordField() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/JPopupMenu.java b/libjava/classpath/javax/swing/JPopupMenu.java index c4ee5fe7346..1f2282e2326 100644 --- a/libjava/classpath/javax/swing/JPopupMenu.java +++ b/libjava/classpath/javax/swing/JPopupMenu.java @@ -39,19 +39,13 @@ exception statement from your version. */ package javax.swing; import java.awt.Component; -import java.awt.Container; import java.awt.Dimension; -import java.awt.GridBagConstraints; import java.awt.Insets; -import java.awt.Panel; import java.awt.Point; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.EventListener; @@ -102,11 +96,11 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement 1. if DefaultLightWeightPopupEnabled true (i) use lightweight container if popup feets inside top-level window - (ii) only use heavyweight container (JWindow) if popup doesn't fit. + (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 JWindow (heavyWeight) + (ii) if popup doesn't fit, use JDialog (heavyWeight) */ private static boolean DefaultLightWeightPopupEnabled = true; @@ -130,8 +124,15 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement /* Popup that is used to display JPopupMenu */ private transient Popup popup; - /* Location of the popup */ - private Point popupLocation; + /** + * 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; @@ -158,15 +159,6 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement updateUI(); } - private void readObject(ObjectInputStream stream) - throws IOException, ClassNotFoundException - { - } - - private void writeObject(ObjectOutputStream stream) throws IOException - { - } - /** * Adds given menu item to the popup menu * @@ -220,19 +212,7 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement public void remove(int index) { super.remove(index); - - GridBagConstraints constraints = new GridBagConstraints(); - constraints.fill = GridBagConstraints.BOTH; - constraints.weightx = 100.0; - constraints.weighty = 100.0; - - Component[] items = getComponents(); - for (int i = index; i < items.length; i++) - { - constraints.gridy = i; - super.add(items[i], constraints, i); - } - this.setSize(this.getPreferredSize()); + revalidate(); } /** @@ -257,27 +237,7 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement */ public void insert(Component component, int index) { - GridBagConstraints constraints = new GridBagConstraints(); - constraints.fill = GridBagConstraints.BOTH; - constraints.weightx = 100.0; - constraints.weighty = 100.0; - - constraints.gridy = index; - super.add(component, constraints, index); - - // need to change constraints for the components that were moved by 1 - // due to the insertion - if (index != -1) - { - Component[] items = getComponents(); - - for (int i = index + 1; i < items.length; i++) - { - constraints.gridy = i; - super.add(items[i], constraints, i); - } - } - this.setSize(this.getPreferredSize()); + super.add(component, index); } /** @@ -527,7 +487,20 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement */ public void pack() { - super.setSize(null); + // 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()); } /** @@ -547,8 +520,21 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement * * @param visible true if popup menu will become visible and false otherwise. */ - public void setVisible(boolean visible) + 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; @@ -556,58 +542,21 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement this.visible = visible; if (old != isVisible()) { - firePropertyChange("visible", old, isVisible()); - if (visible) - { - firePopupMenuWillBecomeVisible(); - Container rootContainer = (Container) SwingUtilities.getRoot(invoker); - - boolean fit = true; - Dimension size; - - // Determine the size of the popup menu - if (this.getSize().width == 0 && this.getSize().width == 0) - size = this.getPreferredSize(); - else - size = this.getSize(); - - if ((size.width > (rootContainer.getWidth() - popupLocation.x)) - || (size.height > (rootContainer.getHeight() - popupLocation.y))) - fit = false; - if (lightWeightPopupEnabled && fit) - popup = new LightWeightPopup(this); - else - { - if (fit) - popup = new MediumWeightPopup(this); - else - popup = new HeavyWeightPopup(this); - } - if (popup instanceof LightWeightPopup - || popup instanceof MediumWeightPopup) - { - JLayeredPane layeredPane; - layeredPane = SwingUtilities.getRootPane(invoker) - .getLayeredPane(); - Point p = new Point(popupLocation.x, popupLocation.y); - SwingUtilities.convertPointFromScreen(p, layeredPane); - popup.show(p.x, p.y, size.width, size.height); - } - else - { - // Subtract insets of the top-level container if popup menu's - // top-left corner is inside it. - Insets insets = rootContainer.getInsets(); - popup.show(popupLocation.x - insets.left, - popupLocation.y - insets.top, size.width, - size.height); - } - } - else - { - firePopupMenuWillBecomeInvisible(); - popup.hide(); - } + firePropertyChange("visible", old, isVisible()); + if (visible) + { + firePopupMenuWillBecomeVisible(); + + PopupFactory pf = PopupFactory.getSharedInstance(); + pack(); + popup = pf.getPopup(invoker, this, popupLocationX, popupLocationY); + popup.show(); + } + else + { + firePopupMenuWillBecomeInvisible(); + popup.hide(); + } } } @@ -619,11 +568,11 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement */ public void setLocation(int x, int y) { - if (popupLocation == null) - popupLocation = new Point(); - - popupLocation.x = x; - popupLocation.y = 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. } /** @@ -657,11 +606,14 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement */ public void show(Component component, int x, int y) { - setInvoker(component); - Point p = new Point(x, y); - SwingUtilities.convertPointToScreen(p, component); - setLocation(p.x, p.y); - setVisible(true); + if (component.isShowing()) + { + setInvoker(component); + Point p = new Point(x, y); + SwingUtilities.convertPointToScreen(p, component); + setLocation(p.x, p.y); + setVisible(true); + } } /** @@ -882,162 +834,13 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement } /** - * This interface is used to display menu items of the JPopupMenu - */ - private interface Popup - { - /** - * Displays container on the screen - * - * @param x x-coordinate of popup menu's location on the screen - * @param y y-coordinate of popup menu's location on the screen - * @param width width of the container that is used to display menu - * item's for popup menu - * @param height height of the container that is used to display menu - * item's for popup menu - */ - void show(int x, int y, int width, int height); - - /** - * Hides container used to display popup menu item's from the screen - */ - void hide(); - } - - /** - * This class represents Popup menu that uses light weight container - * to display its contents. - */ - private class LightWeightPopup extends Container implements Popup - { - private Component c; - - /** - * Creates a new LightWeightPopup menu - * - * @param c Container containing menu items - */ - public LightWeightPopup(Container c) - { - this.c = c; - } - - /** - * Displayes lightweight container with menu items to the screen - * - * @param x x-coordinate of lightweight container on the screen - * @param y y-coordinate of lightweight container on the screen - * @param width width of the lightweight container - * @param height height of the lightweight container - */ - public void show(int x, int y, int width, int height) - { - JLayeredPane layeredPane; - layeredPane = SwingUtilities.getRootPane(invoker).getLayeredPane(); - c.setBounds(x, y, width, height); - layeredPane.add(c, JLayeredPane.POPUP_LAYER, 0); - } - - /** - * Hides lightweight container from the screen - */ - public void hide() - { - // FIXME: Right now the lightweight container is removed from JLayered - // pane. It is probably would be better in order to improve performance - // to make the container invisible instead of removing it everytime. - JLayeredPane layeredPane; - layeredPane = SwingUtilities.getRootPane(invoker).getLayeredPane(); - int index = layeredPane.getIndexOf(c); - layeredPane.remove(index); - } - } - - /** - * MediumWeightPopup is an AWT Panel with JPopupMenu's menu items. - * It is used to display JPopupMenu's menu items on the screen - */ - private class MediumWeightPopup extends Panel implements Popup - { - /** - * Creates a new MediumWeightPopup object. - * - * @param c Container with JPopupMenu's menu items - */ - public MediumWeightPopup(Container c) - { - this.add(c); - } - - /** - * Displays AWT Panel with its components on the screen - * - * @param x x-coordinate of the upper-left corner of the panel's - * @param y y-coordinate of the upper-left corner of the panel's - * @param width width of the panel - * @param height height of the panel - */ - public void show(int x, int y, int width, int height) - { - JLayeredPane layeredPane; - layeredPane = SwingUtilities.getRootPane(invoker).getLayeredPane(); - layeredPane.add(this, JLayeredPane.POPUP_LAYER, 0); - this.setBounds(x, y, width, height); - } - - /** - * Hides This panel from the screen - */ - public void hide() - { - // FIXME: Right now the lightweight container is removed from JLayered - // pane. It is probably would be better in order to improve performance - // to make the container invisible instead of removing it everytime. - JLayeredPane layeredPane; - layeredPane = SwingUtilities.getRootPane(invoker).getLayeredPane(); - int index = layeredPane.getIndexOf(this); - layeredPane.remove(index); - } - } - - /** - * HeavyWeightPopup is JWindow that is used to display JPopupMenu menu item's - * on the screen - */ - private class HeavyWeightPopup extends JWindow implements Popup - { - /** - * Creates a new HeavyWeightPopup object. - * - * @param c Container containing menu items - */ - public HeavyWeightPopup(Container c) - { - this.setContentPane(c); - } - - /** - * Displays JWindow container JPopupMenu's menu items to the screen - * - * @param x x-coordinate of JWindow containing menu items - * @param y y-coordinate of JWindow containing menu items - * @param width width of the JWindow - * @param height height of the JWindow - */ - public void show(int x, int y, int width, int height) - { - this.setBounds(x, y, width, height); - this.show(); - } - } - - /** * This is the separator that can be used in popup menu. */ public static class Separator extends JSeparator { public Separator() { + super(); } public String getUIClassID() @@ -1052,6 +855,7 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement protected AccessibleJPopupMenu() { + // Nothing to do here. } public AccessibleRole getAccessibleRole() @@ -1066,8 +870,9 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement { public void propertyChange(PropertyChangeEvent evt) { - JPopupMenu.this.revalidate(); - JPopupMenu.this.repaint(); + // 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 index 1b8fcea4672..0de9115dc7d 100644 --- a/libjava/classpath/javax/swing/JProgressBar.java +++ b/libjava/classpath/javax/swing/JProgressBar.java @@ -81,6 +81,8 @@ public class JProgressBar extends JComponent implements SwingConstants, /** * AccessibleJProgressBar */ + // FIXME: This inner class is a complete stub and needs to be implemented + // properly. protected class AccessibleJProgressBar extends AccessibleJComponent implements AccessibleValue { @@ -91,6 +93,7 @@ public class JProgressBar extends JComponent implements SwingConstants, */ protected AccessibleJProgressBar() { + // Nothing to do here. } /** @@ -243,7 +246,7 @@ public class JProgressBar extends JComponent implements SwingConstants, model = new DefaultBoundedRangeModel(minimum, 0, minimum, maximum); if (orientation != HORIZONTAL && orientation != VERTICAL) throw new IllegalArgumentException(orientation + " is not a legal orientation"); - this.orientation = orientation; + setOrientation(orientation); changeListener = createChangeListener(); model.addChangeListener(changeListener); updateUI(); diff --git a/libjava/classpath/javax/swing/JRadioButton.java b/libjava/classpath/javax/swing/JRadioButton.java index 66f5902e899..e0593f3a501 100644 --- a/libjava/classpath/javax/swing/JRadioButton.java +++ b/libjava/classpath/javax/swing/JRadioButton.java @@ -192,8 +192,8 @@ public class JRadioButton extends JToggleButton public JRadioButton(String text, Icon icon, boolean selected) { super(text, icon, selected); - borderPainted = false; - contentAreaFilled = false; + setBorderPainted(false); + setHorizontalAlignment(LEADING); } /** diff --git a/libjava/classpath/javax/swing/JRadioButtonMenuItem.java b/libjava/classpath/javax/swing/JRadioButtonMenuItem.java index 76a8fef640a..61a8dbab300 100644 --- a/libjava/classpath/javax/swing/JRadioButtonMenuItem.java +++ b/libjava/classpath/javax/swing/JRadioButtonMenuItem.java @@ -38,9 +38,6 @@ exception statement from your version. */ package javax.swing; -import java.io.IOException; -import java.io.ObjectOutputStream; - import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleRole; @@ -149,10 +146,6 @@ public class JRadioButtonMenuItem extends JMenuItem implements Accessible model.setSelected(selected); } - private void writeObject(ObjectOutputStream stream) throws IOException - { - } - /** * This method returns a name to identify which look and feel class will be * the UI delegate for the menuItem. @@ -202,6 +195,7 @@ public class JRadioButtonMenuItem extends JMenuItem implements Accessible */ protected AccessibleJRadioButtonMenuItem() { + // Nothing to do here. } public AccessibleRole getAccessibleRole() diff --git a/libjava/classpath/javax/swing/JRootPane.java b/libjava/classpath/javax/swing/JRootPane.java index cb0bafd84e0..dea4ee4b195 100644 --- a/libjava/classpath/javax/swing/JRootPane.java +++ b/libjava/classpath/javax/swing/JRootPane.java @@ -42,10 +42,14 @@ 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.AccessibleRole; import javax.swing.plaf.RootPaneUI; @@ -59,10 +63,10 @@ import javax.swing.plaf.RootPaneUI; * * @author Ronald Veldema (rveldema@cs.vu.nl) */ -public class JRootPane extends JComponent +public class JRootPane extends JComponent implements Accessible { // The class used to obtain the accessible role for this object. - protected static class AccessibleJRootPane + protected class AccessibleJRootPane extends AccessibleJComponent { /** * For compatability with Sun's JDK @@ -74,6 +78,7 @@ public class JRootPane extends JComponent */ protected AccessibleJRootPane() { + // Nothing to do here. } /** @@ -95,10 +100,36 @@ public class JRootPane extends JComponent 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; + + /** + * The cached preferred size. + */ + private Dimension prefSize; + + /** * Creates a new <code>RootLayout</code> object. */ protected RootLayout() { + // Nothing to do here. } /** @@ -109,6 +140,7 @@ public class JRootPane extends JComponent */ public void addLayoutComponent(Component comp, Object constraints) { + // Nothing to do here. } /** @@ -119,6 +151,7 @@ public class JRootPane extends JComponent */ public void addLayoutComponent(String name, Component comp) { + // Nothing to do here. } /** @@ -130,7 +163,7 @@ public class JRootPane extends JComponent */ public float getLayoutAlignmentX(Container target) { - return target.getAlignmentX(); + return 0.0F; } /** @@ -142,7 +175,7 @@ public class JRootPane extends JComponent */ public float getLayoutAlignmentY(Container target) { - return target.getAlignmentY(); + return 0.0F; } /** @@ -152,6 +185,14 @@ public class JRootPane extends JComponent */ public void invalidateLayout(Container target) { + synchronized (this) + { + glassPaneBounds = null; + layeredPaneBounds = null; + contentPaneBounds = null; + menuBarBounds = null; + prefSize = null; + } } /** @@ -161,81 +202,56 @@ public class JRootPane extends JComponent */ public void layoutContainer(Container c) { - Dimension menuBarSize; - Dimension containerSize = c.getSize(null); - Dimension contentPaneSize = contentPane.getPreferredSize(); - - /* - if size of top-level window wasn't set then just set - contentPane and menuBar to its preferred sizes. - Otherwise, if the size of top-level window was specified then - set menuBar to its preferred size and make content pane - to fit into the remaining space - - - +-------------------------------+ - | JLayeredPane | - | +--------------------------+ | - | | menuBar | | - | +--------------------------+ | - | +--------------------------+ | - | |contentPane | | - | | | | - | | | | - | | | | - | +--------------------------+ | - +-------------------------------+ - - */ - if (containerSize.width == 0 && containerSize.height == 0) + 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) { - int maxWidth; - menuBarSize = menuBar.getPreferredSize(); - maxWidth = Math.max(menuBarSize.width, contentPaneSize.width); - menuBar.setBounds(0, 0, maxWidth, menuBarSize.height); - glassPane.setBounds(0, menuBarSize.height, maxWidth, - contentPaneSize.height); - contentPane.setBounds(0, menuBarSize.height, maxWidth, - contentPaneSize.height); - layeredPane.setSize(maxWidth, - menuBarSize.height + contentPaneSize.height); + 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 - { - glassPane.setBounds(0, 0, contentPaneSize.width, - contentPaneSize.height); - contentPane.setBounds(0, 0, contentPaneSize.width, - contentPaneSize.height); - layeredPane.setSize(contentPaneSize.width, contentPaneSize.height); - } + 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); } - else - { - if (menuBar != null) - { - menuBarSize = menuBar.getPreferredSize(); - if (menuBarSize.height > containerSize.height) - menuBarSize.height = containerSize.height; - menuBar.setBounds(0, 0, containerSize.width, menuBarSize.height); - int remainingHeight = containerSize.height - menuBarSize.height; - glassPane.setBounds(0, menuBarSize.height, containerSize.width, - containerSize.height - menuBarSize.height); - contentPane.setBounds(0, menuBarSize.height, - containerSize.width, - (containerSize.height - menuBarSize.height)); - } - else - { - glassPane.setBounds(0, 0, containerSize.width, - containerSize.height); - contentPane.setBounds(0, 0, containerSize.width, - containerSize.height); - } - layeredPane.setSize(containerSize.width, containerSize.height); - } + glassPane.setBounds(glassPaneBounds); + layeredPane.setBounds(layeredPaneBounds); + if (menuBar != null) + menuBar.setBounds(menuBarBounds); + contentPane.setBounds(contentPaneBounds); } /** @@ -271,30 +287,29 @@ public class JRootPane extends JComponent */ public Dimension preferredLayoutSize(Container c) { - Dimension menuBarSize; - Dimension prefSize; - - Dimension containerSize = c.getSize(); - Dimension contentPaneSize = contentPane.getPreferredSize(); - - if (containerSize.width == 0 && containerSize.height == 0) + // We must synchronize here, otherwise we cannot guarantee that the + // prefSize is still non-null when returning. + synchronized (this) { - if (menuBar != null) + if (prefSize == null) { - int maxWidth; - menuBarSize = menuBar.getPreferredSize(); - maxWidth = Math.max(menuBarSize.width, contentPaneSize.width); - prefSize = new Dimension(maxWidth, - contentPaneSize.height - + menuBarSize.height); + Insets i = getInsets(); + prefSize = new Dimension(i.left + i.right, i.top + i.bottom); + Dimension contentPrefSize = contentPane.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; + } } - else - prefSize = contentPaneSize; - } - else - prefSize = c.getSize(); - - return prefSize; + // Return a copy here so the cached value won't get trashed by some + // other component. + return new Dimension(prefSize); + } } /** @@ -304,6 +319,7 @@ public class JRootPane extends JComponent */ public void removeLayoutComponent(Component comp) { + // Nothing to do here. } } @@ -335,6 +351,32 @@ public class JRootPane extends JComponent 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; @@ -403,14 +445,25 @@ public class JRootPane extends JComponent } /** - * DOCUMENT ME! + * 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 DOCUMENT ME! + * @param p the Container that will be the content pane + * @throws IllegalComponentStateException if p is null */ public void setContentPane(Container p) { - contentPane = p; - getLayeredPane().add(contentPane, JLayeredPane.FRAME_CONTENT_LAYER); + 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); + } } /** @@ -488,7 +541,6 @@ public class JRootPane extends JComponent getGlassPane(); getLayeredPane(); getContentPane(); - setDoubleBuffered(true); updateUI(); } @@ -524,7 +576,6 @@ public class JRootPane extends JComponent { JPanel p = new JPanel(); p.setName(this.getName() + ".glassPane"); - p.setLayout(new BorderLayout()); p.setVisible(false); p.setOpaque(false); return p; @@ -615,7 +666,8 @@ public class JRootPane extends JComponent && style != COLOR_CHOOSER_DIALOG && style != FILE_CHOOSER_DIALOG && style != QUESTION_DIALOG - && style != WARNING_DIALOG) + && style != WARNING_DIALOG + && style != PLAIN_DIALOG) throw new IllegalArgumentException("invalid style"); int oldStyle = windowDecorationStyle; diff --git a/libjava/classpath/javax/swing/JScrollPane.java b/libjava/classpath/javax/swing/JScrollPane.java index e83513f07de..45dfbf50619 100644 --- a/libjava/classpath/javax/swing/JScrollPane.java +++ b/libjava/classpath/javax/swing/JScrollPane.java @@ -40,13 +40,14 @@ package javax.swing; import java.awt.Component; import java.awt.ComponentOrientation; -import java.awt.Dimension; import java.awt.Insets; import java.awt.LayoutManager; -import java.awt.Point; 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; @@ -77,10 +78,73 @@ import javax.swing.plaf.UIResource; * <tr><td>wheelScrollingEnabled </td><td>scrollPane </td><td>yes </td></tr> * </table> */ -public class JScrollPane - extends JComponent +public class JScrollPane extends JComponent implements Accessible, ScrollPaneConstants { + /** + * Provides accessibility support for the <code>JScrollPane</code>. + * + * @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 <code>AccessibleJScrollPane</code> object. This + * initializes the <code>viewport</code> field with the current viewport + * from the scrollpane associated with this + * <code>AccessibleJScrollPane</code>. + */ + 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 <code>viewPort</code> field when the scrollpane's viewport + * changes. This method is called by + * {@link JScrollPane#setViewport(JViewport)} in order to update the + * <code>viewPort</code> 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; @@ -100,7 +164,6 @@ public class JScrollPane Border viewportBorder; boolean wheelScrollingEnabled; - ChangeListener scrollListener; public JViewport getColumnHeader() { @@ -228,10 +291,10 @@ public class JScrollPane remove(c); } - private void addNonNull(Component c) + private void addNonNull(Component c, Object constraints) { if (c != null) - add(c); + add(c, constraints); } public void setComponentOrientation(ComponentOrientation co) @@ -250,7 +313,7 @@ public class JScrollPane JViewport old = columnHeader; removeNonNull(old); columnHeader = h; - addNonNull(h); + addNonNull(h, JScrollPane.COLUMN_HEADER); firePropertyChange("columnHeader", old, h); sync(); } @@ -294,25 +357,25 @@ public class JScrollPane { removeNonNull(lowerRight); lowerRight = c; - addNonNull(c); + addNonNull(c, JScrollPane.LOWER_RIGHT_CORNER); } else if (key == UPPER_RIGHT_CORNER) { removeNonNull(upperRight); upperRight = c; - addNonNull(c); + addNonNull(c, JScrollPane.UPPER_RIGHT_CORNER); } else if (key == LOWER_LEFT_CORNER) { removeNonNull(lowerLeft); lowerLeft = c; - addNonNull(c); + addNonNull(c, JScrollPane.LOWER_LEFT_CORNER); } else if (key == UPPER_LEFT_CORNER) { removeNonNull(upperLeft); upperLeft = c; - addNonNull(c); + addNonNull(c, JScrollPane.UPPER_LEFT_CORNER); } else throw new IllegalArgumentException("unknown corner " + key); @@ -327,22 +390,10 @@ public class JScrollPane JScrollBar old = horizontalScrollBar; removeNonNull(old); horizontalScrollBar = h; - addNonNull(h); + addNonNull(h, JScrollPane.HORIZONTAL_SCROLLBAR); firePropertyChange("horizontalScrollBar", old, h); sync(); - if (old != null) - { - BoundedRangeModel model = old.getModel(); - if (model != null) - model.removeChangeListener(scrollListener); - } - if (h != null) - { - BoundedRangeModel model = h.getModel(); - if (model != null) - model.addChangeListener(scrollListener); - } } public void setHorizontalScrollBarPolicy(int h) @@ -359,6 +410,7 @@ public class JScrollPane horizontalScrollBarPolicy = h; firePropertyChange("horizontalScrollBarPolicy", old, h); sync(); + revalidate(); } public void setLayout(LayoutManager l) @@ -379,7 +431,7 @@ public class JScrollPane JViewport old = rowHeader; removeNonNull(old); rowHeader = v; - addNonNull(v); + addNonNull(v, JScrollPane.ROW_HEADER); firePropertyChange("rowHeader", old, v); sync(); } @@ -400,22 +452,9 @@ public class JScrollPane JScrollBar old = verticalScrollBar; removeNonNull(old); verticalScrollBar = v; - addNonNull(v); + addNonNull(v, JScrollPane.VERTICAL_SCROLLBAR); firePropertyChange("verticalScrollBar", old, v); sync(); - - if (old != null) - { - BoundedRangeModel model = old.getModel(); - if (model != null) - model.removeChangeListener(scrollListener); - } - if (v != null) - { - BoundedRangeModel model = v.getModel(); - if (model != null) - model.addChangeListener(scrollListener); - } } public void setVerticalScrollBarPolicy(int v) @@ -432,6 +471,7 @@ public class JScrollPane verticalScrollBarPolicy = v; firePropertyChange("verticalScrollBarPolicy", old, v); sync(); + revalidate(); } public void setWheelScrollingEnabled(boolean b) @@ -452,16 +492,17 @@ public class JScrollPane JViewport old = viewport; removeNonNull(old); - if (old != null) - old.removeChangeListener(scrollListener); viewport = v; - if (v != null) - v.addChangeListener(scrollListener); - addNonNull(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) @@ -494,79 +535,6 @@ public class JScrollPane return true; } - ChangeListener createScrollListener() - { - return new ChangeListener() - { - - public void stateChanged(ChangeEvent event) - { - JScrollBar vsb = JScrollPane.this.getVerticalScrollBar(); - JScrollBar hsb = JScrollPane.this.getHorizontalScrollBar(); - JViewport vp = JScrollPane.this.getViewport(); - - if (vp != null && event.getSource() == vp) - { - // if the viewport changed, we should update the VSB / HSB - // models according to the new vertical and horizontal sizes - - Rectangle vr = vp.getViewRect(); - Dimension vs = vp.getViewSize(); - if (vsb != null - && (vsb.getMinimum() != 0 - || vsb.getMaximum() != vs.height - || vsb.getValue() != vr.y - || vsb.getVisibleAmount() != vr.height)) - vsb.setValues(vr.y, vr.height, 0, vs.height); - - if (hsb != null - && (hsb.getMinimum() != 0 - || hsb.getMaximum() != vs.width - || hsb.getValue() != vr.width - || hsb.getVisibleAmount() != vr.height)) - hsb.setValues(vr.x, vr.width, 0, vs.width); - } - else - { - // otherwise we got a change update from either the VSB or - // HSB model, and we need to update the viewport positions of - // both the main viewport and any row or column headers to - // match. - - int xpos = 0; - int ypos = 0; - - if (vsb != null) - ypos = vsb.getValue(); - - if (hsb != null) - xpos = hsb.getValue(); - - Point pt = new Point(xpos, ypos); - - if (vp != null - && vp.getViewPosition() != pt) - vp.setViewPosition(pt); - - pt.x = 0; - - if (rowHeader != null - && rowHeader.getViewPosition() != pt) - rowHeader.setViewPosition(pt); - - pt.x = xpos; - pt.y = 0; - - if (columnHeader != null - && columnHeader.getViewPosition() != pt) - columnHeader.setViewPosition(pt); - - } - } - }; - } - - /** * Creates a new <code>JScrollPane</code> without a view. The scrollbar * policy is set to {@link #VERTICAL_SCROLLBAR_AS_NEEDED} and @@ -627,7 +595,6 @@ public class JScrollPane */ public JScrollPane(Component view, int vsbPolicy, int hsbPolicy) { - scrollListener = createScrollListener(); setVerticalScrollBarPolicy(vsbPolicy); setVerticalScrollBar(createVerticalScrollBar()); setHorizontalScrollBarPolicy(hsbPolicy); @@ -635,7 +602,6 @@ public class JScrollPane viewport = createViewport(); if (view != null) getViewport().setView(view); - viewport.addChangeListener(scrollListener); add(viewport,0); setLayout(new ScrollPaneLayout()); setOpaque(false); @@ -728,4 +694,18 @@ public class JScrollPane } } } + + /** + * Returns the accessible context associated with this + * <code>JScrollPane</code>. + * + * @return the accessible context associated with this + * <code>JScrollPane</code> + */ + 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 index 6a3b97d35c6..602af6a380c 100644 --- a/libjava/classpath/javax/swing/JSeparator.java +++ b/libjava/classpath/javax/swing/JSeparator.java @@ -62,6 +62,7 @@ public class JSeparator extends JComponent implements SwingConstants, */ protected AccessibleJSeparator() { + // Nothing to do here. } /** @@ -131,7 +132,6 @@ public class JSeparator extends JComponent implements SwingConstants, public void updateUI() { setUI((SeparatorUI) UIManager.getUI(this)); - invalidate(); } /** diff --git a/libjava/classpath/javax/swing/JSlider.java b/libjava/classpath/javax/swing/JSlider.java index 2caf509a1bb..b28b06abad7 100644 --- a/libjava/classpath/javax/swing/JSlider.java +++ b/libjava/classpath/javax/swing/JSlider.java @@ -118,6 +118,8 @@ public class JSlider extends JComponent implements SwingConstants, Accessible, /** * DOCUMENT ME! */ + // FIXME: This inner class is a complete stub and needs to be implemented + // properly. protected class AccessibleJSlider extends JComponent.AccessibleJComponent implements AccessibleValue { @@ -128,6 +130,7 @@ public class JSlider extends JComponent implements SwingConstants, Accessible, */ protected AccessibleJSlider() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/JSpinner.java b/libjava/classpath/javax/swing/JSpinner.java index fc2b13e81a9..af34d9cf67d 100644 --- a/libjava/classpath/javax/swing/JSpinner.java +++ b/libjava/classpath/javax/swing/JSpinner.java @@ -108,10 +108,10 @@ public class JSpinner extends JComponent /** * DOCUMENT ME! */ - public void commitEdit() - throws ParseException + public void commitEdit() throws ParseException { - } /* TODO */ + // TODO: Implement this properly. + } /** * DOCUMENT ME! @@ -184,7 +184,8 @@ public class JSpinner extends JComponent */ public void propertyChange(PropertyChangeEvent event) { - } /* TODO */ + // TODO: Implement this properly. + } /** * DOCUMENT ME! @@ -193,11 +194,12 @@ public class JSpinner extends JComponent */ public void stateChanged(ChangeEvent event) { - } /* TODO */ + // TODO: Implement this properly. + } - /* no-ops */ public void removeLayoutComponent(Component child) { + // Nothing to do here. } /** @@ -208,6 +210,7 @@ public class JSpinner extends JComponent */ public void addLayoutComponent(String name, Component child) { + // Nothing to do here. } } @@ -258,6 +261,31 @@ public class JSpinner extends JComponent } /** + * A <code>JSpinner</code> editor used for the {@link SpinnerListModel}. + * This editor uses a <code>JFormattedTextField</code> to edit the values + * of the spinner. + * + * @author Roman Kennke (kennke@aicas.com) + */ + public static class ListEditor extends DefaultEditor + { + /** + * Creates a new instance of <code>ListEditor</code>. + * + * @param spinner the spinner for which this editor is used + */ + public ListEditor(JSpinner spinner) + { + super(spinner); + } + + public SpinnerListModel getModel() + { + return (SpinnerListModel) getSpinner().getModel(); + } + } + + /** * An editor class for a <code>JSpinner</code> that is used * for displaying and editing dates (e.g. that uses * <code>SpinnerDateModel</code> as model). @@ -307,7 +335,7 @@ public class JSpinner extends JComponent /** * Initializes the JFormattedTextField for this editor. * - * @param the date format to use in the formatted text field + * @param format the date format to use in the formatted text field */ private void init(SimpleDateFormat format) { diff --git a/libjava/classpath/javax/swing/JSplitPane.java b/libjava/classpath/javax/swing/JSplitPane.java index cea5afef20a..cdab7bb6c4e 100644 --- a/libjava/classpath/javax/swing/JSplitPane.java +++ b/libjava/classpath/javax/swing/JSplitPane.java @@ -59,6 +59,8 @@ public class JSplitPane extends JComponent implements Accessible /** * DOCUMENT ME! */ + // FIXME: This inner class is a complete stub and must be implemented + // properly. protected class AccessibleJSplitPane extends JComponent.AccessibleJComponent implements AccessibleValue { @@ -69,6 +71,7 @@ public class JSplitPane extends JComponent implements Accessible */ protected AccessibleJSplitPane() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/JTabbedPane.java b/libjava/classpath/javax/swing/JTabbedPane.java index 828a69a24dc..27ba7bb82fc 100644 --- a/libjava/classpath/javax/swing/JTabbedPane.java +++ b/libjava/classpath/javax/swing/JTabbedPane.java @@ -56,8 +56,9 @@ import javax.swing.plaf.TabbedPaneUI; import javax.swing.plaf.UIResource; /** - * This is a container for components. One component is displayed at a time. - * Users can switch between components by clicking on tabs. + * 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. * * <p> * Tabs can be oriented in several ways. They can be above, below, left and @@ -72,12 +73,16 @@ public class JTabbedPane extends JComponent implements Serializable, SwingConstants { /** - * DOCUMENT ME! + * Accessibility support for <code>JTabbedPane</code>. */ + // FIXME: This inner class is a complete stub and must be implemented + // properly. protected class AccessibleJTabbedPane extends JComponent.AccessibleJComponent implements AccessibleSelection, ChangeListener { - /** DOCUMENT ME! */ + /** + * The serialization UID. + */ private static final long serialVersionUID = 7610530885966830483L; /** @@ -89,18 +94,21 @@ public class JTabbedPane extends JComponent implements Serializable, } /** - * DOCUMENT ME! + * Receives notification when the selection state of the + * <code>JTabbedPane</code> changes. * - * @param e DOCUMENT ME! + * @param e the change event describing the change */ public void stateChanged(ChangeEvent e) { + // Implement this properly. } /** - * DOCUMENT ME! + * Returns the accessible role of the <code>JTabbedPane</code>, which is + * {@link AccessibleRole#PAGE_TAB_LIST}. * - * @return DOCUMENT ME! + * @return the accessible role of the <code>JTabbedPane</code> */ public AccessibleRole getAccessibleRole() { @@ -108,9 +116,11 @@ public class JTabbedPane extends JComponent implements Serializable, } /** - * DOCUMENT ME! + * Returns the number of accessible child components of the + * <code>JTabbedPane</code>. * - * @return DOCUMENT ME! + * @return the number of accessible child components of the + * <code>JTabbedPane</code> */ public int getAccessibleChildrenCount() { @@ -118,11 +128,11 @@ public class JTabbedPane extends JComponent implements Serializable, } /** - * DOCUMENT ME! + * Returns the accessible child component at the specified index. * - * @param i DOCUMENT ME! + * @param i the index of the child component to fetch * - * @return DOCUMENT ME! + * @return the accessible child component at the specified index */ public Accessible getAccessibleChild(int i) { @@ -130,9 +140,10 @@ public class JTabbedPane extends JComponent implements Serializable, } /** - * DOCUMENT ME! + * Returns the current selection state of the <code>JTabbedPane</code> + * as AccessibleSelection object. * - * @return DOCUMENT ME! + * @return the current selection state of the <code>JTabbedPane</code> */ public AccessibleSelection getAccessibleSelection() { @@ -140,11 +151,15 @@ public class JTabbedPane extends JComponent implements Serializable, } /** - * DOCUMENT ME! + * 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 DOCUMENT ME! + * @param p the coordinates at which to look up the child component * - * @return DOCUMENT ME! + * @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) { @@ -152,9 +167,13 @@ public class JTabbedPane extends JComponent implements Serializable, } /** - * DOCUMENT ME! + * The number of selected child components of the + * <code>JTabbedPane</code>. This will be <code>0</code> if the + * <code>JTabbedPane</code> has no children, or <code>1</code> otherwise, + * since there is always exactly one tab selected. * - * @return DOCUMENT ME! + * @return number of selected child components of the + * <code>JTabbedPane</code> */ public int getAccessibleSelectionCount() { @@ -192,6 +211,7 @@ public class JTabbedPane extends JComponent implements Serializable, */ public void addAccessibleSelection(int i) { + // TODO: Implement this properly. } /** @@ -201,6 +221,7 @@ public class JTabbedPane extends JComponent implements Serializable, */ public void removeAccessibleSelection(int i) { + // TODO: Implement this properly. } /** @@ -208,6 +229,7 @@ public class JTabbedPane extends JComponent implements Serializable, */ public void clearAccessibleSelection() { + // TODO: Implement this properly. } /** @@ -215,6 +237,7 @@ public class JTabbedPane extends JComponent implements Serializable, */ public void selectAllAccessibleSelection() { + // TODO: Implement this properly. } } @@ -231,6 +254,7 @@ public class JTabbedPane extends JComponent implements Serializable, */ protected ModelListener() { + // Nothing to do here. } /** @@ -313,9 +337,10 @@ public class JTabbedPane extends JComponent implements Serializable, */ public void setComponent(Component c) { - remove(component); - this.component = c; - add(c); + int i = indexOfComponent(component); + insertTab(title, icon, c, tip, i); + component = c; + removeTabAt(i); } /** @@ -596,7 +621,7 @@ public class JTabbedPane extends JComponent implements Serializable, throw new IllegalArgumentException("tabLayoutPolicy is not valid."); this.tabPlacement = tabPlacement; layoutPolicy = tabLayoutPolicy; - + changeEvent = new ChangeEvent(this); changeListener = createChangeListener(); @@ -863,15 +888,17 @@ public class JTabbedPane extends JComponent implements Serializable, * This method inserts tabs into JTabbedPane. This includes adding the * component to the JTabbedPane and hiding it. * - * @param title The title of the tab. - * @param icon The tab's icon. - * @param component The component associated with the tab. - * @param tip The tooltip for the tab. - * @param index The index to insert the tab at. + * @param title the title of the tab; may be <code>null</code> + * @param icon the tab's icon; may be <code>null</code> + * @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); @@ -893,10 +920,10 @@ public class JTabbedPane extends JComponent implements Serializable, /** * This method adds a tab to the JTabbedPane. * - * @param title The title of the tab. - * @param icon The icon for the tab. - * @param component The associated component. - * @param tip The associated tooltip. + * @param title the title of the tab; may be <code>null</code> + * @param icon the icon for the tab; may be <code>null</code> + * @param component the associated component + * @param tip the associated tooltip */ public void addTab(String title, Icon icon, Component component, String tip) { @@ -906,9 +933,9 @@ public class JTabbedPane extends JComponent implements Serializable, /** * This method adds a tab to the JTabbedPane. * - * @param title The title of the tab. - * @param icon The icon for the tab. - * @param component The associated component. + * @param title the title of the tab; may be <code>null</code> + * @param icon the icon for the tab; may be <code>null</code> + * @param component the associated component */ public void addTab(String title, Icon icon, Component component) { @@ -918,8 +945,8 @@ public class JTabbedPane extends JComponent implements Serializable, /** * This method adds a tab to the JTabbedPane. * - * @param title The title of the tab. - * @param component The associated component. + * @param title the title of the tab; may be <code>null</code> + * @param component the associated component */ public void addTab(String title, Component component) { @@ -942,6 +969,7 @@ public class JTabbedPane extends JComponent implements Serializable, super.add(component); else insertTab(component.getName(), null, component, null, tabs.size()); + return component; } @@ -950,8 +978,8 @@ public class JTabbedPane extends JComponent implements Serializable, * 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. - * @param component The associated component. + * @param title the title of the tab; may be <code>null</code> + * @param component the associated component * * @return The Component that was added. */ @@ -1025,45 +1053,37 @@ public class JTabbedPane extends JComponent implements Serializable, } /** - * The tab and it's associated component are removed. After the component - * has been removed from the JTabbedPane, it's set visible to ensure that - * it can be seen. + * 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()); - Component c = getComponentAt(index); - super.remove(index); - c.show(); tabs.remove(index); + getComponentAt(index).show(); } /** - * This method removes the component from the JTabbedPane. After the - * component has been removed from the JTabbedPane, it's set visible to - * ensure that it can be seen. + * Removes the specified Component from the JTabbedPane. * * @param component The Component to remove. */ public void remove(Component component) { - // This simply removes the component. - int index = indexOfComponent(component); super.remove(component); - component.show(); - setComponentAt(index, null); } /** - * This method removes the tab and component from the JTabbedPane. It simply - * calls removeTabAt(int 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) { + remove(getComponentAt(index)); removeTabAt(index); } diff --git a/libjava/classpath/javax/swing/JTable.java b/libjava/classpath/javax/swing/JTable.java index 21680d567e2..69a865df9c0 100644 --- a/libjava/classpath/javax/swing/JTable.java +++ b/libjava/classpath/javax/swing/JTable.java @@ -40,22 +40,34 @@ 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.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; +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.AccessibleStateSet; +import javax.accessibility.AccessibleTable; +import javax.accessibility.AccessibleTableModelChange; import javax.swing.event.CellEditorListener; import javax.swing.event.ChangeEvent; import javax.swing.event.ListSelectionEvent; @@ -76,10 +88,874 @@ import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; import javax.swing.text.Caret; -public class JTable extends JComponent +public class JTable + extends JComponent implements TableModelListener, Scrollable, TableColumnModelListener, ListSelectionListener, CellEditorListener, Accessible { + /** + * Provides accessibility support for <code>JTable</code>. + * + * @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 <code>AccessibleJTableCell</code>. + * + * @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? + 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() + { + // TODO: What state shoiuld be returned here? + return new AccessibleStateSet(); + } + + /** + * 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 <code>0</code> here. + * + * @return <code>0</code> + */ + public int getAccessibleChildrenCount() + { + return 0; + } + + /** + * Returns the accessible child at index <code>i</code>. Table cells + * don't have children, so we return <code>null</code> here. + * + * @return <code>null</code> + */ + 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 + * <code>this</code>. + * + * @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 <code>true</code> if this table cell is enabled, + * <code>false</code> otherwise. + * + * @return <code>true</code> if this table cell is enabled, + * <code>false</code> 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 <code>true</code> if this cell is visible, <code>false</code> + * otherwise. + * + * @return <code>true</code> if this cell is visible, <code>false</code> + * 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 <code>true</code> if this table cell is currently showing on + * screen. + * + * @return <code>true</code> if this table cell is currently showing on + * screen + */ + public boolean isShowing() + { + return table.isShowing(); + } + + /** + * Returns <code>true</code> if this table cell contains the location + * at <code>point</code>, <code>false</code> otherwise. + * <code>point</code> is interpreted as relative to the coordinate system + * of the table cell. + * + * @return <code>true</code> if this table cell contains the location + * at <code>point</code>, <code>false</code> 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 <code>null</code> here. + * + * @return <code>null</code> + */ + public Accessible getAccessibleAt(Point point) + { + return null; + } + + /** + * Returns <code>true</code> if this table cell is focus traversable, + * <code>false</code> otherwise. + * + * @return <code>true</code> if this table cell is focus traversable, + * <code>false</code> 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; + } + } + + /** + * Creates a new <code>AccessibleJTable</code>. + * + * @since JDK1.5 + */ + protected AccessibleJTable() + { + getModel().addTableModelListener(this); + getSelectionModel().addListSelectionListener(this); + getColumnModel().addColumnModelListener(this); + getCellEditor().addCellEditorListener(this); + } + + /** + * Returns the number of selected items in this table. + */ + public int getAccessibleSelectionCount() + { + return getSelectedColumnCount(); + } + + public Accessible getAccessibleSelection(int i) + { + // TODO Auto-generated method stub + return null; + } + + public boolean isAccessibleChildSelected(int i) + { + // TODO Auto-generated method stub + return false; + } + + public void addAccessibleSelection(int i) + { + // TODO Auto-generated method stub + + } + + public void removeAccessibleSelection(int i) + { + // TODO Auto-generated method stub + + } + + public void clearAccessibleSelection() + { + // TODO Auto-generated method stub + + } + + public void selectAllAccessibleSelection() + { + // TODO Auto-generated method stub + + } + + public void valueChanged(ListSelectionEvent event) + { + // TODO Auto-generated method stub + + } + + /** + * 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. + * + * @param event the table model event + */ + public void tableRowsInserted(TableModelEvent event) + { + // TODO: What to do here, if anything? This might be a hook method for + // subclasses... + } + + /** + * Receives notification when one or more rows have been deleted from the + * table. + * + * @param event the table model event + */ + public void tableRowsDeleted(TableModelEvent event) + { + // TODO: What to do here, if anything? This might be a hook method for + // subclasses... + } + + public void columnAdded(TableColumnModelEvent event) + { + // TODO Auto-generated method stub + + } + + public void columnMarginChanged(ChangeEvent event) + { + // TODO Auto-generated method stub + + } + + public void columnMoved(TableColumnModelEvent event) + { + // TODO Auto-generated method stub + + } + + public void columnRemoved(TableColumnModelEvent event) + { + // TODO Auto-generated method stub + + } + + public void columnSelectionChanged(ListSelectionEvent event) + { + // TODO Auto-generated method stub + + } + + public void editingCanceled(ChangeEvent event) + { + // TODO Auto-generated method stub + + } + + public void editingStopped(ChangeEvent event) + { + // TODO Auto-generated method stub + + } + + /** + * 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); + } + } + + public int getAccessibleRow(int index) + { + // TODO Auto-generated method stub + return 0; + } + + public int getAccessibleColumn(int index) + { + // TODO Auto-generated method stub + return 0; + } + + public int getAccessibleIndex(int r, int c) + { + // TODO Auto-generated method stub + return 0; + } + + public Accessible getAccessibleCaption() + { + // TODO Auto-generated method stub + return null; + } + + public void setAccessibleCaption(Accessible caption) + { + // TODO Auto-generated method stub + + } + + public Accessible getAccessibleSummary() + { + // TODO Auto-generated method stub + return null; + } + + public void setAccessibleSummary(Accessible summary) + { + // TODO Auto-generated method stub + + } + + public int getAccessibleRowCount() + { + // TODO Auto-generated method stub + return 0; + } + + public int getAccessibleColumnCount() + { + // TODO Auto-generated method stub + return 0; + } + + public Accessible getAccessibleAt(int r, int c) + { + // TODO Auto-generated method stub + return null; + } + + 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; + } + + } + /** + * Handles property changes from the <code>TableColumn</code>s of this + * <code>JTable</code>. + * + * 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(); + TableColumn col = (TableColumn) ev.getSource(); + header.setResizingColumn(col); + doLayout(); + header.setResizingColumn(null); + } + } + } /** * A cell renderer for boolean values. @@ -364,7 +1240,7 @@ public class JTable extends JComponent * property when the {@link #dataModel} property is changed. * * @see #setModel(TableModel) - * @see #createColumnsFromModel() + * @see #createDefaultColumnsFromModel() * @see #setColumnModel(TableColumnModel) * @see #setAutoCreateColumnsFromModel(boolean) * @see #getAutoCreateColumnsFromModel() @@ -480,11 +1356,6 @@ public class JTable extends JComponent protected ListSelectionModel selectionModel; /** - * The accessibleContext property. - */ - protected AccessibleContext accessibleContext; - - /** * The current cell editor. */ protected TableCellEditor cellEditor; @@ -492,7 +1363,7 @@ public class JTable extends JComponent /** * Whether or not drag-and-drop is enabled on this table. * - * @see #setDragEnabled() + * @see #setDragEnabled(boolean) * @see #getDragEnabled() */ private boolean dragEnabled; @@ -584,6 +1455,12 @@ public class JTable extends JComponent Object oldCellValue; /** + * The property handler for this table's columns. + */ + TableColumnPropertyChangeHandler tableColumnPropertyChangeHandler = + new TableColumnPropertyChangeHandler(); + + /** * Creates a new <code>JTable</code> instance. */ public JTable () @@ -643,15 +1520,28 @@ public class JTable extends JComponent */ public JTable (TableModel dm, TableColumnModel cm, ListSelectionModel sm) { - setModel(dm == null ? createDefaultDataModel() : dm); + boolean autoCreate = false; + if (cm != null) + setColumnModel(cm); + else + { + setColumnModel(createDefaultColumnModel()); + autoCreate = true; + } setSelectionModel(sm == null ? createDefaultSelectionModel() : sm); - - this.columnModel = cm; + setModel(dm == null ? createDefaultDataModel() : dm); + setAutoCreateColumnsFromModel(autoCreate); initializeLocalVars(); - // The next two lines are for compliance with the JDK which starts - // the JLists associated with a JTable with both lead selection - // indices at 0, rather than -1 as in regular JLists + // 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. + selectionModel.setAnchorSelectionIndex(0); selectionModel.setLeadSelectionIndex(0); + columnModel.getSelectionModel().setAnchorSelectionIndex(0); columnModel.getSelectionModel().setLeadSelectionIndex(0); updateUI(); } @@ -659,12 +1549,8 @@ public class JTable extends JComponent protected void initializeLocalVars() { setTableHeader(createDefaultTableHeader()); - this.autoCreateColumnsFromModel = false; - if (columnModel == null) - { - this.autoCreateColumnsFromModel = true; - createColumnsFromModel(); - } + if (autoCreateColumnsFromModel) + createDefaultColumnsFromModel(); this.columnModel.addColumnModelListener(this); this.defaultRenderersByColumnClass = new Hashtable(); @@ -754,6 +1640,7 @@ public class JTable extends JComponent } columnModel.addColumn(column); + column.addPropertyChangeListener(tableColumnPropertyChangeHandler); } protected void createDefaultEditors() @@ -799,20 +1686,6 @@ public class JTable extends JComponent return new JTableHeader(columnModel); } - private void createColumnsFromModel() - { - if (dataModel == null) - return; - - TableColumnModel cm = createDefaultColumnModel(); - - for (int i = 0; i < dataModel.getColumnCount(); ++i) - { - cm.addColumn(new TableColumn(i)); - } - this.setColumnModel(cm); - } - // listener support public void columnAdded (TableColumnModelEvent event) @@ -890,8 +1763,19 @@ public class JTable extends JComponent if ((event.getFirstRow() ==TableModelEvent.HEADER_ROW) && autoCreateColumnsFromModel) - createColumnsFromModel(); + createDefaultColumnsFromModel(); + // If the structure changes, we need to revalidate, since that might + // affect the size parameters of the JTable. Otherwise we only need + // to perform a repaint to update the view. + if (event.getType() == TableModelEvent.INSERT) + revalidate(); + else if (event.getType() == TableModelEvent.DELETE) + { + if (dataModel.getRowCount() == 0) + clearSelection(); + revalidate(); + } repaint(); } @@ -944,8 +1828,7 @@ public class JTable extends JComponent { int y0 = getLocation().y; int nrows = getRowCount(); - Dimension gap = getIntercellSpacing(); - int height = getRowHeight() + (gap == null ? 0 : gap.height); + int height = getRowHeight(); int y = point.y; for (int i = 0; i < nrows; ++i) @@ -990,10 +1873,7 @@ public class JTable extends JComponent int y = (height + y_gap) * row; for (int i = 0; i < column; ++i) - { - x += columnModel.getColumn(i).getWidth(); - x += x_gap; - } + x += columnModel.getColumn(i).getWidth(); if (includeSpacing) return new Rectangle(x, y, width, height); @@ -1015,7 +1895,7 @@ public class JTable extends JComponent * @return The current value of the selectedRow property */ public int getSelectedRow () - { + { return selectionModel.getMinSelectionIndex(); } @@ -1069,13 +1949,14 @@ public class JTable extends JComponent // scroll direction. if (orientation == SwingConstants.VERTICAL) - return rowHeight; + return direction * rowHeight; else { int sum = 0; for (int i = 0; i < getColumnCount(); ++i) sum += columnModel.getColumn(0).getWidth(); - return getColumnCount() == 0 ? 10 : sum / getColumnCount(); + int inc = getColumnCount() == 0 ? 10 : sum / getColumnCount(); + return direction * inc; } } @@ -1656,24 +2537,29 @@ public class JTable extends JComponent // 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. + // Set property. dataModel = m; - // Add table as TableModelListener to new model. - dataModel.addTableModelListener(this); + // Add table as TableModelListener to new model. + dataModel.addTableModelListener(this); - // Automatically create columns. - if (autoCreateColumnsFromModel) - createColumnsFromModel(); + // 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(); @@ -1959,7 +2845,8 @@ public class JTable extends JComponent int average = spill / cols.length; for (int i = 0; i < cols.length; i++) { - cols[i].setWidth(cols[i].getWidth() + average); + if (cols[i] != null) + cols[i].setWidth(cols[i].getWidth() + average); } } @@ -2023,6 +2910,8 @@ public class JTable extends JComponent case AUTO_RESIZE_OFF: default: + int prefWidth = resizingColumn.getPreferredWidth(); + resizingColumn.setWidth(prefWidth); } } else @@ -2258,6 +3147,8 @@ public class JTable extends JComponent */ public void createDefaultColumnsFromModel() { + assert columnModel != null : "The columnModel must not be null."; + // remove existing columns int columnIndex = columnModel.getColumnCount() - 1; while (columnIndex >= 0) @@ -2272,7 +3163,9 @@ public class JTable extends JComponent { TableColumn column = new TableColumn(c); column.setIdentifier(dataModel.getColumnName(c)); + column.setHeaderValue(dataModel.getColumnName(c)); columnModel.addColumn(column); + column.addPropertyChangeListener(tableColumnPropertyChangeHandler); } } @@ -2372,4 +3265,13 @@ public class JTable extends JComponent return editor.getTableCellEditorComponent (this, getValueAt(row, column), isCellSelected(row, column), row, column); } + + /** + * This revalidates the <code>JTable</code> and queues a repaint. + */ + protected void resizeAndRepaint() + { + revalidate(); + repaint(); + } } diff --git a/libjava/classpath/javax/swing/JTextArea.java b/libjava/classpath/javax/swing/JTextArea.java index 53591ffcc3a..2fa185b6207 100644 --- a/libjava/classpath/javax/swing/JTextArea.java +++ b/libjava/classpath/javax/swing/JTextArea.java @@ -42,6 +42,8 @@ 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; @@ -92,6 +94,35 @@ import javax.swing.text.View; public class JTextArea extends JTextComponent { /** + * Provides accessibility support for <code>JTextArea</code>. + * + * @author Roman Kennke (kennke@aicas.com) + */ + protected class AccessibleJTextArea extends AccessibleJTextComponent + { + + /** + * Creates a new <code>AccessibleJTextArea</code> object. + */ + protected AccessibleJTextArea() + { + super(); + } + + /** + * Returns the accessible state of this <code>AccessibleJTextArea</code>. + * + * @return the accessible state of this <code>AccessibleJTextArea</code> + */ + 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; @@ -208,6 +239,8 @@ public class JTextArea extends JTextComponent /* This shouldn't happen in theory -- but, if it does... */ throw new RuntimeException("Unexpected exception occurred.", exception); } + if (toAppend != null && toAppend.length() > 0) + revalidate(); } /** @@ -312,8 +345,12 @@ public class JTextArea extends JTextComponent { if (columns < 0) throw new IllegalArgumentException(); - - this.columns = columns; + + if (columns != this.columns) + { + this.columns = columns; + revalidate(); + } } /** @@ -337,8 +374,12 @@ public class JTextArea extends JTextComponent { if (rows < 0) throw new IllegalArgumentException(); - - this.rows = rows; + + if (rows != this.rows) + { + this.rows = rows; + revalidate(); + } } /** @@ -547,4 +588,16 @@ public class JTextArea extends JTextComponent return new Dimension(Math.max(reqWidth, neededWidth), Math.max(reqHeight, neededHeight)); } + + /** + * Returns the accessible context associated with the <code>JTextArea</code>. + * + * @return the accessible context associated with the <code>JTextArea</code> + */ + 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 index 5ae9c9f1a82..8dc2f256914 100644 --- a/libjava/classpath/javax/swing/JTextField.java +++ b/libjava/classpath/javax/swing/JTextField.java @@ -46,9 +46,8 @@ 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.AttributeSet; -import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import javax.swing.text.PlainDocument; @@ -69,15 +68,19 @@ public class JTextField extends JTextComponent */ protected AccessibleJTextField() { + super(); } /** - * getAccessibleStateSet - * @return AccessibleStateSet + * Returns the accessible state of this <code>AccessibleJTextField</code>. + * + * @return the accessible state of this <code>AccessibleJTextField</code> */ public AccessibleStateSet getAccessibleStateSet() { - return null; + AccessibleStateSet state = super.getAccessibleStateSet(); + // TODO: Figure out what state must be added here to the super's state. + return state; } } @@ -92,17 +95,17 @@ public class JTextField extends JTextComponent public static final String notifyAction = "notify-field-accept"; static - { - actions = new Action[1]; - actions[0] = new TextAction(notifyAction) + { + actions = new Action[1]; + actions[0] = new TextAction(notifyAction) { - public void actionPerformed(ActionEvent event) - { - JTextField textField = (JTextField) event.getSource(); - textField.fireActionPerformed(); - } + public void actionPerformed(ActionEvent event) + { + JTextField textField = (JTextField) event.getSource(); + textField.fireActionPerformed(); + } }; - } + } private int columns; private int align; @@ -117,6 +120,11 @@ public class JTextField extends JTextComponent private PropertyChangeListener actionPropertyChangeListener; /** + * The horizontal visibility of the textfield. + */ + private BoundedRangeModel horizontalVisibility; + + /** * Creates a new instance of <code>JTextField</code>. */ public JTextField() @@ -172,9 +180,9 @@ public class JTextField extends JTextComponent { if (columns < 0) throw new IllegalArgumentException(); - + this.columns = columns; - + setDocument(doc == null ? createDefaultModel() : doc); if (text != null) @@ -182,6 +190,9 @@ public class JTextField extends JTextComponent // default value for alignment align = LEADING; + + // Initialize the horizontal visibility model. + horizontalVisibility = new DefaultBoundedRangeModel(); } /** @@ -192,15 +203,9 @@ public class JTextField extends JTextComponent */ protected Document createDefaultModel() { - // subclassed to swallow newlines - return new PlainDocument() { - public void insertString(int offset, String str, AttributeSet a) - throws BadLocationException - { - if (str != null && str.indexOf('\n') == -1) - super.insertString(offset, str, a); - } - }; + PlainDocument doc = new PlainDocument(); + doc.putProperty("filterNewlines", Boolean.TRUE); + return doc; } /** @@ -268,6 +273,11 @@ public class JTextField extends JTextComponent 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) @@ -275,16 +285,31 @@ public class JTextField extends JTextComponent 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; @@ -295,12 +320,20 @@ public class JTextField extends JTextComponent 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(); @@ -318,6 +351,7 @@ public class JTextField extends JTextComponent */ public int getScrollOffset() { + //FIXME: this should return horizontalVisibility's value return scrollOffset; } @@ -328,9 +362,15 @@ public class JTextField extends JTextComponent */ public void setScrollOffset(int offset) { + //FIXME: this should actualy scroll the field if needed scrollOffset = 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); @@ -364,26 +404,27 @@ public class JTextField extends JTextComponent if (action != null) { - removeActionListener(action); - action.removePropertyChangeListener(actionPropertyChangeListener); - actionPropertyChangeListener = null; + removeActionListener(action); + action.removePropertyChangeListener(actionPropertyChangeListener); + actionPropertyChangeListener = null; } - + Action oldAction = action; action = newAction; if (action != null) { - addActionListener(action); - actionPropertyChangeListener = - createActionPropertyChangeListener(action); - action.addPropertyChangeListener(actionPropertyChangeListener); + 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) @@ -397,45 +438,79 @@ public class JTextField extends JTextComponent protected PropertyChangeListener createActionPropertyChangeListener(Action action) { return new PropertyChangeListener() + { + public void propertyChange(PropertyChangeEvent event) { - 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()); - } - } - }; + // 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)); + setEnabled(action.isEnabled()); + setToolTipText((String) action.getValue(Action.SHORT_DESCRIPTION)); } else { - setEnabled(true); - setToolTipText(null); + 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 <code>JTextField</code>. + * + * @return the accessible context associated with the <code>JTextField</code> + */ + 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() + { + // TODO: The real implementation of this property is still missing. + // However, this is not done in JTextField but must instead be handled in + // javax.swing.text.FieldView. + return horizontalVisibility; + } } diff --git a/libjava/classpath/javax/swing/JTextPane.java b/libjava/classpath/javax/swing/JTextPane.java index 80632fff38e..1f5b99e43c5 100644 --- a/libjava/classpath/javax/swing/JTextPane.java +++ b/libjava/classpath/javax/swing/JTextPane.java @@ -39,8 +39,6 @@ exception statement from your version. */ package javax.swing; import java.awt.Component; -import java.io.IOException; -import java.io.ObjectOutputStream; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; @@ -49,7 +47,6 @@ 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.StyledDocument; import javax.swing.text.StyledEditorKit; @@ -106,7 +103,7 @@ public class JTextPane * @throws IllegalArgumentException if <code>document</code> is not an * instance of <code>StyledDocument</code> * - * @see {@link #setStyledDocument} + * @see #setStyledDocument */ public void setDocument(Document document) { @@ -120,7 +117,7 @@ public class JTextPane /** * Returns the {@link StyledDocument} that is the content model for * this <code>JTextPane</code>. This is a typed wrapper for - * {@link #getDocument}. + * {@link #getDocument()}. * * @return the content model of this <code>JTextPane</code> */ @@ -179,8 +176,6 @@ public class JTextPane doc.setCharacterAttributes(start, contentLength, getInputAttributes(), true); - // Set dot to new position. - setCaretPosition(start + contentLength); } catch (BadLocationException e) { @@ -300,7 +295,7 @@ public class JTextPane * @param replace if <code>true</code>, the attributes of the current * selection are overridden, otherwise they are merged * - * @see {@link #getInputAttributes} + * @see #getInputAttributes */ public void setCharacterAttributes(AttributeSet attribute, boolean replace) diff --git a/libjava/classpath/javax/swing/JToggleButton.java b/libjava/classpath/javax/swing/JToggleButton.java index 25d67f59e4f..077e5adb887 100644 --- a/libjava/classpath/javax/swing/JToggleButton.java +++ b/libjava/classpath/javax/swing/JToggleButton.java @@ -129,7 +129,7 @@ public class JToggleButton extends AbstractButton implements Accessible * 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. @@ -174,8 +174,27 @@ public class JToggleButton extends AbstractButton implements Accessible ActionEvent.ACTION_PERFORMED, actionCommand)); } - } + + /** + * Checks if the button is selected. + * + * @returns 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); + } } /** @@ -276,6 +295,7 @@ public class JToggleButton extends AbstractButton implements Accessible setModel(new ToggleButtonModel()); model.setSelected(selected); + setAlignmentX(LEFT_ALIGNMENT); } /** diff --git a/libjava/classpath/javax/swing/JToolBar.java b/libjava/classpath/javax/swing/JToolBar.java index 649919e0618..a508ee6d8e7 100644 --- a/libjava/classpath/javax/swing/JToolBar.java +++ b/libjava/classpath/javax/swing/JToolBar.java @@ -68,6 +68,8 @@ public class JToolBar extends JComponent implements SwingConstants, Accessible /** * AccessibleJToolBar */ + // FIXME: This inner class is a complete stub and must be implemented + // properly. protected class AccessibleJToolBar extends AccessibleJComponent { /** DOCUMENT ME! */ @@ -78,6 +80,7 @@ public class JToolBar extends JComponent implements SwingConstants, Accessible */ protected AccessibleJToolBar() { + // Nothing to do here. } /** @@ -749,7 +752,6 @@ public class JToolBar extends JComponent implements SwingConstants, Accessible { AbstractButton b = (AbstractButton) component; b.setRolloverEnabled(rollover); - b.updateUI(); } } // addImpl() diff --git a/libjava/classpath/javax/swing/JToolTip.java b/libjava/classpath/javax/swing/JToolTip.java index 8d774782780..6bc3e3fa287 100644 --- a/libjava/classpath/javax/swing/JToolTip.java +++ b/libjava/classpath/javax/swing/JToolTip.java @@ -58,6 +58,8 @@ public class JToolTip extends JComponent implements Accessible /** * DOCUMENT ME! */ + // FIXME: This inner class is a complete stub and must be implemented + // properly. protected class AccessibleJToolTip extends AccessibleJComponent { private static final long serialVersionUID = -6222548177795408476L; @@ -67,6 +69,7 @@ public class JToolTip extends JComponent implements Accessible */ protected AccessibleJToolTip() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/JTree.java b/libjava/classpath/javax/swing/JTree.java index bb24c7a459d..4422a193396 100644 --- a/libjava/classpath/javax/swing/JTree.java +++ b/libjava/classpath/javax/swing/JTree.java @@ -37,16 +37,32 @@ 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; @@ -57,7 +73,6 @@ import javax.swing.event.TreeWillExpandListener; import javax.swing.plaf.TreeUI; import javax.swing.text.Position; import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.DefaultTreeSelectionModel; import javax.swing.tree.ExpandVetoException; @@ -68,1754 +83,2830 @@ import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; -public class JTree - extends JComponent - implements Scrollable, Accessible +public class JTree extends JComponent implements Scrollable, Accessible { - /** - * Listens to the model of the JTree and updates the property - * <code>expandedState</code> if nodes are removed or changed. - */ - protected class TreeModelHandler - implements - TreeModelListener - { - - /** - * Creates a new instance of TreeModelHandler. - */ - protected TreeModelHandler() - { - } - - /** - * 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) - { - // TODO: The API docs suggest that this method should do something - // but I cannot really see what has to be done here ... - } - - /** - * 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) - { - // set state of new path - TreePath path = ev.getTreePath(); - setExpandedState(path, isExpanded(path)); - } - } // TreeModelHandler - - /** - * 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() - { - } - - /** - * 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); - JTree.this.repaint(); - } - } // TreeSelectionRedirector - - /** - * A TreeModel that does not allow anything to be selected. + + /** + * 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 <code>pos</code>. 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 + * <code>expandedState</code> 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 */ - 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() - { - } - - /** - * 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 - } - }// EmptySelectionModel - - 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"; + public void treeNodesRemoved(TreeModelEvent ev) + { + // TODO: The API docs suggest that this method should do something + // but I cannot really see what has to be done here ... + } - /** @since 1.3 */ - public static final String ANCHOR_SELECTION_PATH_PROPERTY = "anchorSelectionPath"; + /** + * 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) + { + // Set state of new path. + TreePath path = ev.getTreePath(); + setExpandedState(path, isExpanded(path)); + } + } - /** @since 1.3 */ - public static final String LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath"; + /** + * 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); + JTree.this.repaint(); + } + } + + /** + * 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 EXPANDS_SELECTED_PATHS_PROPERTY = "expandsSelectedPaths"; - private static final Object EXPANDED = new Object(); - private static final Object COLLAPSED = new Object(); - private boolean dragEnabled; - private boolean expandsSelectedPaths; - private TreePath anchorSelectionPath; - private TreePath leadSelectionPath; - - /* - * 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. - */ - private 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; + public static final String LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath"; - /** - * Redirects TreeSelectionEvents so that the source is this JTree. - */ - protected TreeSelectionRedirector selectionRedirector = - new TreeSelectionRedirector(); + /** @since 1.3 */ + public static final String EXPANDS_SELECTED_PATHS_PROPERTY = + "expandsSelectedPaths"; - /** - * Creates a new <code>JTree</code> object. - */ - public JTree() - { - this(createTreeModel(null)); - } - - /** - * Creates a new <code>JTree</code> object. - * - * @param value the initial nodes in the tree - */ - public JTree(Hashtable value) - { - this(createTreeModel(value)); - } - - /** - * Creates a new <code>JTree</code> object. - * - * @param value the initial nodes in the tree - */ - public JTree(Object[] value) - { - this(createTreeModel(value)); - } - - /** - * Creates a new <code>JTree</code> object. - * - * @param model the model to use - */ - public JTree(TreeModel model) - { - setModel(model); - setSelectionModel(EmptySelectionModel.sharedInstance()); - setCellRenderer(new DefaultTreeCellRenderer()); - updateUI(); - } - - /** - * Creates a new <code>JTree</code> object. - * - * @param root the root node - */ - public JTree(TreeNode root) - { - this(root, false); - } - - /** - * Creates a new <code>JTree</code> 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 <code>JTree</code> object. - * - * @param value the initial nodes in the tree - */ - public JTree(Vector value) - { - this(createTreeModel(value)); - } - - 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 <code>pos</code>. 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])); - } - } - } - - 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; - } - - 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 <code>TreeModel</code> 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 <code>JTree</code> object. - * - * @return the associated <code>TreeUI</code> object - */ - public TreeUI getUI() - { - return (TreeUI) ui; - } - - /** - * Sets the UI associated with this <code>JTree</code> object. - * - * @param ui the <code>TreeUI</code> to associate - */ - public void setUI(TreeUI ui) - { - super.setUI(ui); - } + private static final Object EXPANDED = new Object(); - /** - * This method resets the UI used to the Look and Feel defaults.. - */ - public void updateUI() - { - setUI((TreeUI) UIManager.getUI(this)); - revalidate(); - repaint(); - } - - /** - * 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 - * <code>JToggleButton</code>. - * - * @return the associated context - */ - public AccessibleContext getAccessibleContext() - { - return null; - } - - /** - * Returns the preferred viewport size. - * - * @return the preferred size - */ - public Dimension getPreferredScrollableViewportSize() - { - return new Dimension (getPreferredSize().width, getVisibleRowCount()*getRowHeight()); - } - - public int getScrollableUnitIncrement(Rectangle visibleRect, - int orientation, int direction) - { - return 1; - } - - public int getScrollableBlockIncrement(Rectangle visibleRect, - int orientation, int direction) - { - return 1; - } + private static final Object COLLAPSED = new Object(); - public boolean getScrollableTracksViewportWidth() + private boolean dragEnabled; + + private boolean expandsSelectedPaths; + + private TreePath anchorSelectionPath; + + private TreePath leadSelectionPath; + + /** + * 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. + */ + private 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(); + + /** + * Creates a new <code>JTree</code> object. + */ + public JTree() + { + this(createTreeModel(null)); + } + + /** + * Creates a new <code>JTree</code> object. + * + * @param value the initial nodes in the tree + */ + public JTree(Hashtable value) + { + this(createTreeModel(value)); + } + + /** + * Creates a new <code>JTree</code> object. + * + * @param value the initial nodes in the tree + */ + public JTree(Object[] value) + { + this(createTreeModel(value)); + } + + /** + * Creates a new <code>JTree</code> object. + * + * @param model the model to use + */ + public JTree(TreeModel model) + { + updateUI(); + setRootVisible(true); + setModel(model); + setSelectionModel(new EmptySelectionModel()); + } + + /** + * Creates a new <code>JTree</code> object. + * + * @param root the root node + */ + public JTree(TreeNode root) + { + this(root, false); + } + + /** + * Creates a new <code>JTree</code> 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 <code>JTree</code> 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; + } + + 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 <code>TreeModel</code> 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 <code>JTree</code> object. + * + * @return the associated <code>TreeUI</code> object + */ + public TreeUI getUI() + { + return (TreeUI) ui; + } + + /** + * Sets the UI associated with this <code>JTree</code> object. + * + * @param ui the <code>TreeUI</code> 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 + * <code>JTree</code>. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + return new AccessibleJTree(); + } + + /** + * Returns the preferred viewport size. + * + * @return the preferred size + */ + public Dimension getPreferredScrollableViewportSize() + { + return new Dimension (getPreferredSize().width, getVisibleRowCount()*getRowHeight()); + } + + public int getScrollableUnitIncrement(Rectangle visibleRect, + int orientation, int direction) + { + return 1; + } + + public int getScrollableBlockIncrement(Rectangle visibleRect, + int orientation, int direction) + { + return 1; + } + + public boolean getScrollableTracksViewportHeight() { if (getParent() instanceof JViewport) return ((JViewport) getParent()).getHeight() > getPreferredSize().height; return false; } - - public boolean getScrollableTracksViewportHeight() + + public boolean getScrollableTracksViewportWidth() { if (getParent() instanceof JViewport) return ((JViewport) getParent()).getWidth() > getPreferredSize().width; return false; } - /** - * Adds a <code>TreeExpansionListener</code> object to the tree. - * - * @param listener the listener to add - */ - public void addTreeExpansionListener(TreeExpansionListener listener) - { - listenerList.add(TreeExpansionListener.class, listener); - } - - /** - * Removes a <code>TreeExpansionListener</code> object from the tree. - * - * @param listener the listener to remove - */ - public void removeTreeExpansionListener(TreeExpansionListener listener) - { - listenerList.remove(TreeExpansionListener.class, listener); - } - - /** - * Returns all added <code>TreeExpansionListener</code> 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 <code>TreeSelctionListener</code> object to the tree. - * - * @param listener the listener to add - */ - public void addTreeSelectionListener(TreeSelectionListener listener) - { - listenerList.add(TreeSelectionListener.class, listener); - } - - /** - * Removes a <code>TreeSelectionListener</code> object from the tree. - * - * @param listener the listener to remove - */ - public void removeTreeSelectionListener(TreeSelectionListener listener) - { - listenerList.remove(TreeSelectionListener.class, listener); - } - - /** - * Returns all added <code>TreeSelectionListener</code> 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 <code>TreeWillExpandListener</code> object to the tree. - * - * @param listener the listener to add - */ - public void addTreeWillExpandListener(TreeWillExpandListener listener) - { - listenerList.add(TreeWillExpandListener.class, listener); - } - - /** - * Removes a <code>TreeWillExpandListener</code> object from the tree. - * - * @param listener the listener to remove - */ - public void removeTreeWillExpandListener(TreeWillExpandListener listener) - { - listenerList.remove(TreeWillExpandListener.class, listener); - } - - /** - * Returns all added <code>TreeWillExpandListener</code> 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 <code>JTree</code> object. - * - * @return the associated <code>TreeModel</code> - */ - public TreeModel getModel() - { - return treeModel; - } - - /** - * Sets the model to use in <code>JTree</code>. - * - * @param model the <code>TreeModel</code> to use - */ - public void setModel(TreeModel model) - { - if (treeModel == model) - return; - - // add treeModelListener to the new model - if (treeModelListener == null) - treeModelListener = createTreeModelListener(); - if (model != null) // as setModel(null) is allowed - model.addTreeModelListener(treeModelListener); - + /** + * Adds a <code>TreeExpansionListener</code> object to the tree. + * + * @param listener the listener to add + */ + public void addTreeExpansionListener(TreeExpansionListener listener) + { + listenerList.add(TreeExpansionListener.class, listener); + } + + /** + * Removes a <code>TreeExpansionListener</code> object from the tree. + * + * @param listener the listener to remove + */ + public void removeTreeExpansionListener(TreeExpansionListener listener) + { + listenerList.remove(TreeExpansionListener.class, listener); + } + + /** + * Returns all added <code>TreeExpansionListener</code> 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 <code>TreeSelctionListener</code> object to the tree. + * + * @param listener the listener to add + */ + public void addTreeSelectionListener(TreeSelectionListener listener) + { + listenerList.add(TreeSelectionListener.class, listener); + } + + /** + * Removes a <code>TreeSelectionListener</code> object from the tree. + * + * @param listener the listener to remove + */ + public void removeTreeSelectionListener(TreeSelectionListener listener) + { + listenerList.remove(TreeSelectionListener.class, listener); + } + + /** + * Returns all added <code>TreeSelectionListener</code> 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 <code>TreeWillExpandListener</code> object to the tree. + * + * @param listener the listener to add + */ + public void addTreeWillExpandListener(TreeWillExpandListener listener) + { + listenerList.add(TreeWillExpandListener.class, listener); + } + + /** + * Removes a <code>TreeWillExpandListener</code> object from the tree. + * + * @param listener the listener to remove + */ + public void removeTreeWillExpandListener(TreeWillExpandListener listener) + { + listenerList.remove(TreeWillExpandListener.class, listener); + } + + /** + * Returns all added <code>TreeWillExpandListener</code> 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 <code>JTree</code> object. + * + * @return the associated <code>TreeModel</code> + */ + public TreeModel getModel() + { + return treeModel; + } + + /** + * Sets the model to use in <code>JTree</code>. + * + * @param model the <code>TreeModel</code> to use + */ + public void setModel(TreeModel model) + { + if (treeModel == model) + return; + + // 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; firePropertyChange(TREE_MODEL_PROPERTY, oldValue, model); - } + updateUI(); + } - /** - * Checks if this <code>JTree</code> object is editable. - * - * @return <code>true</code> if this tree object is editable, - * <code>false</code> otherwise - */ - public boolean isEditable() - { - return editable; - } - - /** - * Sets the <code>editable</code> property. - * - * @param flag <code>true</code> to make this tree object editable, - * <code>false</code> 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 <code>true</code> if the root element is visible, - * <code>false</code> otherwise - */ - public boolean isRootVisible() - { - return rootVisible; - } - - public void setRootVisible(boolean flag) - { - if (rootVisible == flag) - return; - - boolean oldValue = rootVisible; - rootVisible = flag; - firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, flag); - } - - public boolean getShowsRootHandles() - { - return showsRootHandles; - } - - public void setShowsRootHandles(boolean flag) - { - 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 (selectionModel != null) - selectionModel.removeTreeSelectionListener(selectionRedirector); - - TreeSelectionModel oldValue = selectionModel; - selectionModel = model; - - if (selectionModel != null) - 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) - { - 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; - } + /** + * Checks if this <code>JTree</code> object is editable. + * + * @return <code>true</code> if this tree object is editable, + * <code>false</code> otherwise + */ + public boolean isEditable() + { + return editable; + } - /** - * @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; + /** + * Sets the <code>editable</code> property. + * + * @param flag <code>true</code> to make this tree object editable, + * <code>false</code> otherwise + */ + public void setEditable(boolean flag) + { + if (editable == flag) + return; - Rectangle rect = getPathBounds(path); - - if (rect == null) - return; - - scrollRectToVisible(rect); - } + boolean oldValue = editable; + editable = flag; + firePropertyChange(EDITABLE_PROPERTY, oldValue, editable); + } - public void scrollRowToVisible(int row) - { - scrollPathToVisible(getPathForRow(row)); - } - - public boolean getScrollsOnExpand() - { - return scrollsOnExpand; - } - - public void setScrollsOnExpand(boolean scroll) - { - if (scrollsOnExpand == scroll) - return; - - boolean oldValue = scrollsOnExpand; - scrollsOnExpand = scroll; - firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue, scroll); - } - - public void setSelectionPath(TreePath path) - { - selectionModel.setSelectionPath(path); - } - - public void setSelectionPaths(TreePath[] paths) - { - selectionModel.setSelectionPaths(paths); - } - - public void setSelectionRow(int row) - { - TreePath path = getPathForRow(row); - - if (path != null) - selectionModel.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]; + /** + * Checks if the root element is visible. + * + * @return <code>true</code> if the root element is visible, + * <code>false</code> otherwise + */ + public boolean isRootVisible() + { + return rootVisible; + } - for (int i = rows.length - 1; i >= 0; --i) - paths[i] = getPathForRow(rows[i]); - - addSelectionPaths(paths); - } + public void setRootVisible(boolean flag) + { + if (rootVisible == flag) + return; - public void addSelectionInterval(int index0, int index1) - { - TreePath[] paths = getPathBetweenRows(index0, index1); - - if (paths != null) - addSelectionPaths(paths); - } - - public void removeSelectionPath(TreePath path) - { - selectionModel.removeSelectionPath(path); - } - - public void removeSelectionPaths(TreePath[] paths) - { - selectionModel.removeSelectionPaths(paths); - } + boolean oldValue = rootVisible; + rootVisible = flag; + firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, flag); + } - public void removeSelectionRow(int row) - { - TreePath path = getPathForRow(row); + public boolean getShowsRootHandles() + { + return showsRootHandles; + } - if (path != null) - selectionModel.removeSelectionPath(path); - } + public void setShowsRootHandles(boolean flag) + { + if (showsRootHandles == flag) + return; + + boolean oldValue = showsRootHandles; + showsRootHandles = flag; + firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue, flag); + } - public void removeSelectionRows(int[] rows) - { - if (rows == null || getUI() == null) - return; + public TreeCellEditor getCellEditor() + { + return cellEditor; + } - TreePath[] paths = new TreePath[rows.length]; + public void setCellEditor(TreeCellEditor editor) + { + if (cellEditor == editor) + return; - for (int i = rows.length - 1; i >= 0; --i) - paths[i] = getPathForRow(rows[i]); + TreeCellEditor oldValue = cellEditor; + cellEditor = editor; + firePropertyChange(CELL_EDITOR_PROPERTY, oldValue, editor); + } - removeSelectionPaths(paths); - } + public TreeCellRenderer getCellRenderer() + { + return cellRenderer; + } - public void removeSelectionInterval(int index0, int index1) - { - TreePath[] paths = getPathBetweenRows(index0, index1); + public void setCellRenderer(TreeCellRenderer newRenderer) + { + if (cellRenderer == newRenderer) + return; - if (paths != null) - removeSelectionPaths(paths); - } + TreeCellRenderer oldValue = cellRenderer; + cellRenderer = newRenderer; + firePropertyChange(CELL_RENDERER_PROPERTY, oldValue, newRenderer); + } - public void clearSelection() - { - selectionModel.clearSelection(); - setLeadSelectionPath(null); - } + public TreeSelectionModel getSelectionModel() + { + return selectionModel; + } - public TreePath getLeadSelectionPath() - { - return leadSelectionPath; - } + public void setSelectionModel(TreeSelectionModel model) + { + if (selectionModel == model) + return; - /** - * @since 1.3 - */ - public void setLeadSelectionPath(TreePath path) - { - if (leadSelectionPath == path) - return; - - TreePath oldValue = leadSelectionPath; - leadSelectionPath = path; - firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, path); - } - - /** - * @since 1.3 - */ - public TreePath getAnchorSelectionPath() - { - return anchorSelectionPath; - } + if (selectionModel != null) + selectionModel.removeTreeSelectionListener(selectionRedirector); - /** - * @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); - } - - public boolean isRowSelected(int row) - { - return selectionModel.isPathSelected(getPathForRow(row)); - } - - public boolean isSelectionEmpty() - { - return selectionModel.isSelectionEmpty(); - } - - /** - * Return the value of the <code>dragEnabled</code> property. - * - * @return the value - * - * @since 1.4 - */ - public boolean getDragEnabled() - { - return dragEnabled; - } - - /** - * Set the <code>dragEnabled</code> property. - * - * @param enabled new value - * - * @since 1.4 - */ - public void setDragEnabled(boolean enabled) - { + TreeSelectionModel oldValue = selectionModel; + selectionModel = model; - dragEnabled = enabled; - } + if (selectionModel != null) + selectionModel.addTreeSelectionListener(selectionRedirector); - public int getRowCount() - { - TreeUI ui = getUI(); + firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue, model); + revalidate(); + repaint(); + } - if (ui != null) - return ui.getRowCount(this); + public int getVisibleRowCount() + { + return visibleRowCount; + } - return 0; - } + public void setVisibleRowCount(int rows) + { + if (visibleRowCount == rows) + return; - public void collapsePath(TreePath path) - { - try - { - fireTreeWillCollapse(path); - } - catch (ExpandVetoException ev) - { - } - setExpandedState(path, false); - fireTreeCollapsed(path); - } + int oldValue = visibleRowCount; + visibleRowCount = rows; + firePropertyChange(VISIBLE_ROW_COUNT_PROPERTY, oldValue, rows); + } - public void collapseRow(int row) - { - if (row < 0 || row >= getRowCount()) - return; + public boolean isLargeModel() + { + return largeModel; + } - TreePath path = getPathForRow(row); + public void setLargeModel(boolean large) + { + if (largeModel == large) + return; - if (path != null) - collapsePath(path); - } + boolean oldValue = largeModel; + largeModel = large; + firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, large); + } - public void expandPath(TreePath path) - { - // Don't expand if last path component is a leaf node. - if ((path == null) || (treeModel.isLeaf(path.getLastPathComponent()))) - return; - - try - { - fireTreeWillExpand(path); - } - catch (ExpandVetoException ev) - { - } - - setExpandedState(path, true); - fireTreeExpanded(path); - } + public int getRowHeight() + { + return rowHeight; + } - public void expandRow(int row) - { - if (row < 0 || row >= getRowCount()) - return; + public void setRowHeight(int height) + { + if (rowHeight == height) + return; - TreePath path = getPathForRow(row); + int oldValue = rowHeight; + rowHeight = height; + firePropertyChange(ROW_HEIGHT_PROPERTY, oldValue, height); + } - if (path != null) - expandPath(path); - } + public boolean isFixedRowHeight() + { + return rowHeight > 0; + } - public boolean isCollapsed(TreePath path) - { - return !isExpanded(path); - } + public boolean getInvokesStopCellEditing() + { + return invokesStopCellEditing; + } - public boolean isCollapsed(int row) - { - if (row < 0 || row >= getRowCount()) - return false; + public void setInvokesStopCellEditing(boolean invoke) + { + if (invokesStopCellEditing == invoke) + return; - TreePath path = getPathForRow(row); + boolean oldValue = invokesStopCellEditing; + invokesStopCellEditing = invoke; + firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY, + oldValue, invoke); + } - if (path != null) - return isCollapsed(path); + /** + * @since 1.3 + */ + public int getToggleClickCount() + { + return toggleClickCount; + } - return false; - } + /** + * @since 1.3 + */ + public void setToggleClickCount(int count) + { + if (toggleClickCount == count) + return; - public boolean isExpanded(TreePath path) - { - if (path == null) - return false; + int oldValue = toggleClickCount; + toggleClickCount = count; + firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldValue, count); + } - Object state = nodeStates.get(path); + public void scrollPathToVisible(TreePath path) + { + if (path == null) + return; + + Object[] oPath = path.getPath(); + TreePath temp = new TreePath(oPath[0]); + boolean stop = false; + int i = 1; + while (!stop) + { + while (isVisible(temp)) + if (i < oPath.length) + temp = temp.pathByAddingChild(oPath[i++]); + else + { + stop = true; + break; + } + makeVisible(temp); + } + Rectangle rect = getPathBounds(path); + scrollRectToVisible(rect); + revalidate(); + repaint(); + } - if ((state == null) || (state != EXPANDED)) - return false; + public void scrollRowToVisible(int row) + { + scrollPathToVisible(getPathForRow(row)); + } - TreePath parent = path.getParentPath(); + public boolean getScrollsOnExpand() + { + return scrollsOnExpand; + } - if (parent != null) - return isExpanded(parent); + public void setScrollsOnExpand(boolean scroll) + { + if (scrollsOnExpand == scroll) + return; - return true; - } + boolean oldValue = scrollsOnExpand; + scrollsOnExpand = scroll; + firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue, scroll); + } - public boolean isExpanded(int row) - { - if (row < 0 || row >= getRowCount()) - return false; + public void setSelectionPath(TreePath path) + { + selectionModel.setSelectionPath(path); + } - TreePath path = getPathForRow(row); + public void setSelectionPaths(TreePath[] paths) + { + selectionModel.setSelectionPaths(paths); + } - if (path != null) - return isExpanded(path); + public void setSelectionRow(int row) + { + TreePath path = getPathForRow(row); - return false; - } + if (path != null) + selectionModel.setSelectionPath(path); + } - /** - * @since 1.3 - */ - public boolean getExpandsSelectedPaths() - { - return expandsSelectedPaths; - } + public void setSelectionRows(int[] rows) + { + // Make sure we have an UI so getPathForRow() does not return null. + if (rows == null || getUI() == null) + return; - /** - * @since 1.3 - */ - public void setExpandsSelectedPaths(boolean flag) - { - if (expandsSelectedPaths == flag) - return; + TreePath[] paths = new TreePath[rows.length]; - boolean oldValue = expandsSelectedPaths; - expandsSelectedPaths = flag; - firePropertyChange(EXPANDS_SELECTED_PATHS_PROPERTY, oldValue, flag); - } + for (int i = rows.length - 1; i >= 0; --i) + paths[i] = getPathForRow(rows[i]); - public Rectangle getPathBounds(TreePath path) - { - TreeUI ui = getUI(); + setSelectionPaths(paths); + } - if (ui == null) - return null; + public void setSelectionInterval(int index0, int index1) + { + TreePath[] paths = getPathBetweenRows(index0, index1); - return ui.getPathBounds(this, path); - } + if (paths != null) + setSelectionPaths(paths); + } - public Rectangle getRowBounds(int row) - { - TreePath path = getPathForRow(row); + public void addSelectionPath(TreePath path) + { + selectionModel.addSelectionPath(path); + } - if (path != null) - return getPathBounds(path); + public void addSelectionPaths(TreePath[] paths) + { + selectionModel.addSelectionPaths(paths); + } - return null; - } + public void addSelectionRow(int row) + { + TreePath path = getPathForRow(row); - public boolean isEditing() - { - TreeUI ui = getUI(); + if (path != null) + selectionModel.addSelectionPath(path); + } - if (ui != null) - return ui.isEditing(this); + public void addSelectionRows(int[] rows) + { + // Make sure we have an UI so getPathForRow() does not return null. + if (rows == null || getUI() == null) + return; - return false; - } + TreePath[] paths = new TreePath[rows.length]; - public boolean stopEditing() - { - TreeUI ui = getUI(); + for (int i = rows.length - 1; i >= 0; --i) + paths[i] = getPathForRow(rows[i]); - if (ui != null) - return ui.stopEditing(this); + addSelectionPaths(paths); + } - return false; - } + public void addSelectionInterval(int index0, int index1) + { + TreePath[] paths = getPathBetweenRows(index0, index1); - public void cancelEditing() - { - TreeUI ui = getUI(); + if (paths != null) + addSelectionPaths(paths); + } - if (ui != null) - ui.cancelEditing(this); - } + public void removeSelectionPath(TreePath path) + { + selectionModel.removeSelectionPath(path); + } - public void startEditingAtPath(TreePath path) - { - TreeUI ui = getUI(); + public void removeSelectionPaths(TreePath[] paths) + { + selectionModel.removeSelectionPaths(paths); + } - if (ui != null) - ui.startEditingAtPath(this, path); - } + public void removeSelectionRow(int row) + { + TreePath path = getPathForRow(row); - public TreePath getEditingPath() - { - TreeUI ui = getUI(); + if (path != null) + selectionModel.removeSelectionPath(path); + } - if (ui != null) - return ui.getEditingPath(this); + public void removeSelectionRows(int[] rows) + { + if (rows == null || getUI() == null) + return; - return null; - } + TreePath[] paths = new TreePath[rows.length]; - public TreePath getPathForLocation(int x, int y) - { - TreePath path = getClosestPathForLocation(x, y); + for (int i = rows.length - 1; i >= 0; --i) + paths[i] = getPathForRow(rows[i]); - if (path != null) - { - Rectangle rect = getPathBounds(path); + removeSelectionPaths(paths); + } - if ((rect != null) && rect.contains(x, y)) - return path; - } + public void removeSelectionInterval(int index0, int index1) + { + TreePath[] paths = getPathBetweenRows(index0, index1); - return null; - } + if (paths != null) + removeSelectionPaths(paths); + } - public int getRowForLocation(int x, int y) - { - TreePath path = getPathForLocation(x, y); + public void clearSelection() + { + selectionModel.clearSelection(); + setLeadSelectionPath(null); + } - if (path != null) - return getRowForPath(path); + public TreePath getLeadSelectionPath() + { + return leadSelectionPath; + } - return -1; - } + /** + * @since 1.3 + */ + public void setLeadSelectionPath(TreePath path) + { + if (leadSelectionPath == path) + return; + + TreePath oldValue = leadSelectionPath; + leadSelectionPath = path; + firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, path); + } - public TreePath getClosestPathForLocation(int x, int y) - { - TreeUI ui = getUI(); + /** + * @since 1.3 + */ + public TreePath getAnchorSelectionPath() + { + return anchorSelectionPath; + } - if (ui != null) - return ui.getClosestPathForLocation(this, x, y); + /** + * @since 1.3 + */ + public void setAnchorSelectionPath(TreePath path) + { + if (anchorSelectionPath == path) + return; - return null; - } + TreePath oldValue = anchorSelectionPath; + anchorSelectionPath = path; + firePropertyChange(ANCHOR_SELECTION_PATH_PROPERTY, oldValue, path); + } - public int getClosestRowForLocation(int x, int y) - { - TreePath path = getClosestPathForLocation(x, y); + public int getLeadSelectionRow() + { + return selectionModel.getLeadSelectionRow(); + } - if (path != null) - return getRowForPath(path); + public int getMaxSelectionRow() + { + return selectionModel.getMaxSelectionRow(); + } - return -1; - } + public int getMinSelectionRow() + { + return selectionModel.getMinSelectionRow(); + } - public Object getLastSelectedPathComponent() - { - TreePath path = getSelectionPath(); + public int getSelectionCount() + { + return selectionModel.getSelectionCount(); + } - if (path != null) - return path.getLastPathComponent(); + public TreePath getSelectionPath() + { + return selectionModel.getSelectionPath(); + } - return null; - } + public TreePath[] getSelectionPaths() + { + return selectionModel.getSelectionPaths(); + } - private void doExpandParents(TreePath path, boolean state) - { - TreePath parent = path.getParentPath(); - - if (!isExpanded(parent) && parent != null) - doExpandParents(parent, false); + public int[] getSelectionRows() + { + return selectionModel.getSelectionRows(); + } - nodeStates.put(path, state ? EXPANDED : COLLAPSED); - } + public boolean isPathSelected(TreePath path) + { + return selectionModel.isPathSelected(path); + } - protected void setExpandedState(TreePath path, boolean state) - { - if (path == null) - return; - TreePath parent = path.getParentPath(); + public boolean isRowSelected(int row) + { + return selectionModel.isPathSelected(getPathForRow(row)); + } - doExpandParents(path, state); - } + public boolean isSelectionEmpty() + { + return selectionModel.isSelectionEmpty(); + } - protected void clearToggledPaths() - { - nodeStates.clear(); - } + /** + * Return the value of the <code>dragEnabled</code> property. + * + * @return the value + * + * @since 1.4 + */ + public boolean getDragEnabled() + { + return dragEnabled; + } - protected Enumeration getDescendantToggledPaths(TreePath parent) - { - if (parent == null) - return null; + /** + * Set the <code>dragEnabled</code> property. + * + * @param enabled new value + * + * @since 1.4 + */ + public void setDragEnabled(boolean enabled) + { + dragEnabled = enabled; + } - Enumeration nodes = nodeStates.keys(); - Vector result = new Vector(); + public int getRowCount() + { + TreeUI ui = getUI(); - while (nodes.hasMoreElements()) - { - TreePath path = (TreePath) nodes.nextElement(); + if (ui != null) + return ui.getRowCount(this); - if (path.isDescendant(parent)) - result.addElement(path); - } + return 0; + } - return result.elements(); - } + 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 boolean hasBeenExpanded(TreePath path) - { - if (path == null) - return false; + public void collapseRow(int row) + { + if (row < 0 || row >= getRowCount()) + return; - return nodeStates.get(path) != null; - } + TreePath path = getPathForRow(row); - public boolean isVisible(TreePath path) - { - if (path == null) - return false; + if (path != null) + collapsePath(path); + } - TreePath parent = path.getParentPath(); + public void expandPath(TreePath path) + { + // Don't expand if path is null + if (path == null) + return; + + try + { + fireTreeWillExpand(path); + } + catch (ExpandVetoException ev) + { + // We do nothing if attempt has been vetoed. + } + + setExpandedState(path, true); + fireTreeExpanded(path); + } - if (parent == null) - return true; // Is root node. + public void expandRow(int row) + { + if (row < 0 || row >= getRowCount()) + return; - return isExpanded(parent); - } + TreePath path = getPathForRow(row); - public void makeVisible(TreePath path) - { - if (path == null) - return; + if (path != null) + expandPath(path); + } - expandPath(path.getParentPath()); - } + public boolean isCollapsed(TreePath path) + { + return !isExpanded(path); + } - public boolean isPathEditable(TreePath path) - { - return isEditable(); - } + public boolean isCollapsed(int row) + { + if (row < 0 || row >= getRowCount()) + return false; - /** - * Creates and returns an instance of {@link TreeModelHandler}. - * - * @returns 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 <code>value.toString()</code> 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 - * <code>null</code>. - * - * @return a String representation of this JTree - */ - public 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 - * <code>startingRow</code> that starts with <code>prefix</code>. - * Searching is done in the direction specified by <code>bias</code>. - * - * @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 <code>null</code> 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 <code>path</code>. If <code>includePath</code> is set - * to <code>true</code> and <code>path</code> itself is selected, then - * it will be removed too. - * - * @param path the path from which selected descendants are to be removed - * @param includeSelected if <code>true</code> then <code>path</code> itself - * will also be remove if it's selected - * - * @return <code>true</code> if something has been removed, - * <code>false</code> 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; - } + 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 (ui != null) + return ui.stopEditing(this); + + return false; + } + + public void cancelEditing() + { + TreeUI ui = getUI(); + + 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 <code>value.toString()</code> 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 + * <code>null</code>. + * + * @return a String representation of this JTree + */ + public 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 + * <code>startingRow</code> that starts with <code>prefix</code>. + * Searching is done in the direction specified by <code>bias</code>. + * + * @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 <code>null</code> 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 <code>path</code>. If <code>includePath</code> is set + * to <code>true</code> and <code>path</code> itself is selected, then + * it will be removed too. + * + * @param path the path from which selected descendants are to be removed + * @param includeSelected if <code>true</code> then <code>path</code> itself + * will also be remove if it's selected + * + * @return <code>true</code> if something has been removed, + * <code>false</code> 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 = (TreePath) 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. + */ + public void treeDidChange() + { + repaint(); + } } diff --git a/libjava/classpath/javax/swing/JViewport.java b/libjava/classpath/javax/swing/JViewport.java index d750bad29cc..5f20f0aa60a 100644 --- a/libjava/classpath/javax/swing/JViewport.java +++ b/libjava/classpath/javax/swing/JViewport.java @@ -41,6 +41,7 @@ package javax.swing; 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; @@ -49,6 +50,9 @@ 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; @@ -95,17 +99,41 @@ import javax.swing.plaf.ViewportUI; * the underlying child at position <code>(-VX,-VY)</code></p> * */ -public class JViewport extends JComponent +public class JViewport extends JComponent implements Accessible { + /** + * Provides accessibility support for <code>JViewport</code>. + * + * @author Roman Kennke (roman@kennke.org) + */ + protected class AccessibleJViewport extends AccessibleJComponent + { + /** + * Creates a new instance of <code>AccessibleJViewport</code>. + */ + public AccessibleJViewport() + { + // Nothing to do here. + } + + /** + * Returns the accessible role of <code>JViewport</code>, which is + * {@link AccessibleRole#VIEWPORT}. + * + * @return the accessible role of <code>JViewport</code> + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.VIEWPORT; + } + } /** * A {@link java.awt.event.ComponentListener} that listens for - * changes of the view's size. This class forbids changes of the view - * component's size that would exceed the viewport's size. + * changes of the view's size. This triggers a revalidate() call on the + * viewport. */ - protected class ViewListener - extends ComponentAdapter - implements Serializable + protected class ViewListener extends ComponentAdapter implements Serializable { private static final long serialVersionUID = -2812489404285958070L; @@ -114,57 +142,53 @@ public class JViewport extends JComponent */ protected ViewListener() { + // Nothing to do here. } /** * Receives notification when a component (in this case: the view - * component) changes it's size. + * 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) { - // According to some tests that I did with Sun's implementation - // this class is supposed to make sure that the view component - // is not resized to a larger size than the viewport. - // This is not documented anywhere. What I did is: I subclassed JViewport - // and ViewListener and 'disabled' the componentResized method by - // overriding it and not calling super.componentResized(). - // When this method is disabled I can set the size on the view component - // normally, when it is enabled, it gets immediatly resized back, - // after a resize attempt that would exceed the Viewport's size. - Component comp = ev.getComponent(); - Dimension newSize = comp.getSize(); - Dimension viewportSize = getSize(); - boolean revert = false; - if (newSize.width > viewportSize.width) - { - newSize.width = viewportSize.width; - revert = true; - } - if (newSize.height > viewportSize.height) - { - newSize.height = viewportSize.height; - revert = true; - } - if (revert == true) - comp.setSize(newSize); + revalidate(); } } - private static final long serialVersionUID = -6925142919680527970L; - 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; + + 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; - protected boolean scrollUnderway; - protected boolean isViewSizeSet; - /** * The width and height of the Viewport's area in terms of view * coordinates. Typically this will be the same as the width and height @@ -172,29 +196,75 @@ public class JViewport extends JComponent * width and height, which it may do, for example if it magnifies or * rotates its view. * - * @see #toViewCoordinates + * @see #toViewCoordinates(Dimension) */ Dimension extentSize; /** * The width and height of the view in its own coordinate space. */ - Dimension viewSize; - Point lastPaintPosition; - /** * 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; + public JViewport() { setOpaque(true); - setScrollMode(BLIT_SCROLL_MODE); - setLayout(createLayoutManager()); + String scrollModeProp = + System.getProperty("gnu.javax.swing.JViewport.scrollMode", + "BLIT"); + int myScrollMode; + if (scrollModeProp.equalsIgnoreCase("simple")) + myScrollMode = SIMPLE_SCROLL_MODE; + else if (scrollModeProp.equalsIgnoreCase("backingstore")) + myScrollMode = BACKINGSTORE_SCROLL_MODE; + else + myScrollMode = BLIT_SCROLL_MODE; + setScrollMode(myScrollMode); + updateUI(); + setLayout(createLayoutManager()); + lastPaintPosition = new Point(); + cachedBlitFrom = new Point(); + cachedBlitTo = new Point(); + cachedBlitSize = new Dimension(); + cachedBlitPaint = new Rectangle(); } public Dimension getExtentSize() @@ -248,9 +318,14 @@ public class JViewport extends JComponent viewSize = newSize; Component view = getView(); if (view != null) - view.setSize(viewSize); + { + if (newSize != view.getSize()) + { + view.setSize(viewSize); + fireStateChanged(); + } + } isViewSizeSet = true; - fireStateChanged(); } /** @@ -275,13 +350,17 @@ public class JViewport extends JComponent public void setViewPosition(Point p) { + if (getViewPosition().equals(p)) + return; Component view = getView(); if (view != null) { Point q = new Point(-p.x, -p.y); view.setLocation(q); + isViewSizeSet = false; fireStateChanged(); } + repaint(); } public Rectangle getViewRect() @@ -331,12 +410,8 @@ public class JViewport extends JComponent public void setView(Component v) { - while (getComponentCount() > 0) - { - if (viewListener != null) - getView().removeComponentListener(viewListener); - remove(0); - } + if (viewListener != null) + getView().removeComponentListener(viewListener); if (v != null) { @@ -346,37 +421,25 @@ public class JViewport extends JComponent add(v); fireStateChanged(); } - } - - public void revalidate() - { - fireStateChanged(); - super.revalidate(); + revalidate(); + repaint(); } public void reshape(int x, int y, int w, int h) { - boolean changed = - (x != getX()) - || (y != getY()) - || (w != getWidth()) - || (h != getHeight()); + if (w != getWidth() || h != getHeight()) + sizeChanged = true; super.reshape(x, y, w, h); - if (changed) - fireStateChanged(); - } - - protected void addImpl(Component comp, Object constraints, int index) - { - if (getComponentCount() > 0) - remove(getComponents()[0]); - - super.addImpl(comp, constraints, index); + if (sizeChanged) + { + damaged = true; + fireStateChanged(); + } } - public final Insets getInsets() + public final Insets getInsets() { - return new Insets(0,0,0,0); + return new Insets(0, 0, 0, 0); } public final Insets getInsets(Insets insets) @@ -390,6 +453,14 @@ public class JViewport extends JComponent return insets; } + + /** + * Overridden to return <code>false</code>, 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 <code>false</code> + */ public boolean isOptimizedDrawingEnabled() { return false; @@ -397,7 +468,36 @@ public class JViewport extends JComponent public void paint(Graphics g) { - paintComponent(g); + Component view = getView(); + + if (view == null) + return; + + Point pos = getViewPosition(); + 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) @@ -415,13 +515,6 @@ public class JViewport extends JComponent return (ChangeListener[]) getListeners(ChangeListener.class); } - protected void fireStateChanged() - { - ChangeListener[] listeners = getChangeListeners(); - for (int i = 0; i < listeners.length; ++i) - listeners[i].stateChanged(changeEvent); - } - /** * This method returns the String ID of the UI class of Separator. * @@ -467,6 +560,90 @@ public class JViewport extends JComponent } /** + * 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(); + 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 (contentRect.y + contentRect.height + viewBounds.y > portBounds.height) + pos.y = contentRect.y + contentRect.height - portBounds.height; + // If contentRect.y is above the port boundaries, scroll down to + // contentRect.y. + if (contentRect.y + viewBounds.y < 0) + pos.y = contentRect.y; + // If the right boundary of contentRect is right from the port + // boundaries, scroll left as necessary. + if (contentRect.x + contentRect.width + viewBounds.x > portBounds.width) + pos.x = contentRect.x + contentRect.width - portBounds.width; + // If contentRect.x is left from the port boundaries, scroll right to + // contentRect.x. + if (contentRect.x + viewBounds.x < 0) + pos.x = contentRect.x; + setViewPosition(pos); + } + + /** + * Returns the accessible context for this <code>JViewport</code>. This + * will be an instance of {@link AccessibleJViewport}. + * + * @return the accessible context for this <code>JViewport</code> + */ + 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); + } + } + + 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. * @@ -489,43 +666,217 @@ public class JViewport extends JComponent } /** - * Scrolls the view so that contentRect becomes visible. + * Computes the parameters for the blitting scroll method. <code>dx</code> + * and <code>dy</code> specifiy the X and Y offset by which the viewport + * is scrolled. All other arguments are output parameters and are filled by + * this method. * - * @param contentRect the rectangle to make visible within the view + * <code>blitFrom</code> holds the position of the blit rectangle in the + * viewport rectangle before scrolling, <code>blitTo</code> where the blitArea + * is copied to. + * + * <code>blitSize</code> holds the size of the blit area and + * <code>blitPaint</code> is the area of the view that needs to be painted. + * + * This method returns <code>true</code> if blitting is possible and + * <code>false</code> 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 <code>true</code> if blitting is possible, + * <code>false</code> otherwise */ - public void scrollRectToVisible(Rectangle contentRect) + protected boolean computeBlit(int dx, int dy, Point blitFrom, Point blitTo, + Dimension blitSize, Rectangle blitPaint) + { + if ((dx != 0 && dy != 0) || damaged) + // We cannot blit if the viewport is scrolled in both directions at + // once. + 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) { Point pos = getViewPosition(); - Rectangle viewBounds = getView().getBounds(); - Rectangle portBounds = getBounds(); - - // FIXME: should validate the view if it is not valid, however - // this may cause excessive validation when the containment - // hierarchy is being created. - - // if contentRect is larger than the portBounds, center the view - if (contentRect.height > portBounds.height || - contentRect.width > portBounds.width) + Component view = getView(); + boolean translated = false; + try + { + g.translate(-pos.x, -pos.y); + translated = true; + view.paint(g); + } + finally { - setViewPosition(new Point(contentRect.x, contentRect.y)); - return; + if (translated) + g.translate (pos.x, pos.y); } - - // Y-DIRECTION - if (contentRect.y < -viewBounds.y) - setViewPosition(new Point(pos.x, contentRect.y)); - else if (contentRect.y + contentRect.height > - -viewBounds.y + portBounds.height) - setViewPosition (new Point(pos.x, contentRect.y - - (portBounds.height - contentRect.height))); - - // X-DIRECTION - pos = getViewPosition(); - if (contentRect.x < -viewBounds.x) - setViewPosition(new Point(contentRect.x, pos.y)); - else if (contentRect.x + contentRect.width > - -viewBounds.x + portBounds.width) - setViewPosition (new Point(contentRect.x - - (portBounds.width - contentRect.width), pos.y)); + } + + /** + * 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) + { + // Copy the part that remains visible during scrolling. + 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 + { + 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) + { + // We cannot perform blitted painting as it is described in Sun's API docs. + // There it is suggested that this painting method should blit directly + // on the parent window's surface. This is not possible because when using + // Swing's double buffering (at least our implementation), it would + // immediatly be painted when the buffer is painted on the screen. For this + // to work we would need a kind of hole in the buffer image. And honestly + // I find this method not very elegant. + // The alternative, blitting directly on the buffer image, is also not + // possible because the buffer image gets cleared everytime when an opaque + // parent component is drawn on it. + + // What we do instead is falling back to the backing store approach which + // is in fact a mixed blitting/backing store approach where the blitting + // is performed on the backing store image and this is then drawn to the + // graphics context. This is very robust and works independent of the + // painting mechanism that is used by Swing. And it should have comparable + // performance characteristics as the blitting method. + paintBackingStore(g); } } diff --git a/libjava/classpath/javax/swing/JWindow.java b/libjava/classpath/javax/swing/JWindow.java index 449900370c6..cc0ac7fd95a 100644 --- a/libjava/classpath/javax/swing/JWindow.java +++ b/libjava/classpath/javax/swing/JWindow.java @@ -60,6 +60,21 @@ import javax.accessibility.AccessibleContext; */ public class JWindow extends Window implements Accessible, RootPaneContainer { + /** + * Provides accessibility support for <code>JWindow</code>. + */ + protected class AccessibleJWindow extends Window.AccessibleAWTWindow + { + /** + * Creates a new instance of <code>AccessibleJWindow</code>. + */ + public AccessibleJWindow() + { + super(); + // Nothing to do here. + } + } + private static final long serialVersionUID = 5420698392125238833L; protected JRootPane rootPane; @@ -71,13 +86,6 @@ public class JWindow extends Window implements Accessible, RootPaneContainer protected AccessibleContext accessibleContext; - /** - * Tells us if we're in the initialization stage. - * If so, adds go to top-level Container, otherwise they go - * to the content pane for this container. - */ - private boolean initStageDone = false; - public JWindow() { super(SwingUtilities.getOwnerFrame()); @@ -113,7 +121,7 @@ public class JWindow extends Window implements Accessible, RootPaneContainer super.setLayout(new BorderLayout(1, 1)); getRootPane(); // will do set/create // Now we're done init stage, adds and layouts go to content pane. - initStageDone = true; + setRootPaneCheckingEnabled(true); } public Dimension getPreferredSize() @@ -125,13 +133,8 @@ public class JWindow extends Window implements Accessible, RootPaneContainer { // Check if we're in initialization stage. If so, call super.setLayout // otherwise, valid calls go to the content pane. - if (initStageDone) - { - if (isRootPaneCheckingEnabled()) - throw new Error("Cannot set layout. Use getContentPane().setLayout()" - + " instead."); - getContentPane().setLayout(manager); - } + if (isRootPaneCheckingEnabled()) + getContentPane().setLayout(manager); else super.setLayout(manager); } @@ -192,15 +195,10 @@ public class JWindow extends Window implements Accessible, RootPaneContainer { // If we're adding in the initialization stage use super.add. // otherwise pass the add onto the content pane. - if (!initStageDone) - super.addImpl(comp, constraints, index); + if (isRootPaneCheckingEnabled()) + getContentPane().add(comp, constraints, index); else - { - if (isRootPaneCheckingEnabled()) - throw new Error("Do not use add() on JWindow directly. Use " - + "getContentPane().add() instead"); - getContentPane().add(comp, constraints, index); - } + super.addImpl(comp, constraints, index); } public void remove(Component comp) @@ -235,7 +233,9 @@ public class JWindow extends Window implements Accessible, RootPaneContainer public AccessibleContext getAccessibleContext() { - return null; + if (accessibleContext == null) + accessibleContext = new AccessibleJWindow(); + return accessibleContext; } protected String paramString() diff --git a/libjava/classpath/javax/swing/KeyStroke.java b/libjava/classpath/javax/swing/KeyStroke.java index 12a280c217a..b57a7119207 100644 --- a/libjava/classpath/javax/swing/KeyStroke.java +++ b/libjava/classpath/javax/swing/KeyStroke.java @@ -51,6 +51,7 @@ public class KeyStroke // Called by java.awt.AWTKeyStroke.registerSubclass via reflection. private KeyStroke() { + // Nothing to do here. } private KeyStroke(char keyChar, int keyCode, int modifiers, diff --git a/libjava/classpath/javax/swing/KeyboardManager.java b/libjava/classpath/javax/swing/KeyboardManager.java new file mode 100644 index 00000000000..d3868309d08 --- /dev/null +++ b/libjava/classpath/javax/swing/KeyboardManager.java @@ -0,0 +1,280 @@ +/* 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; + +/** + * 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@redhat.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. + */ + Hashtable topLevelLookup = new Hashtable(); + + /** + * 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 <code>comp</code>. 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.keys(); + 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/ListCellRenderer.java b/libjava/classpath/javax/swing/ListCellRenderer.java index 6ce115ea70c..e234d184dfd 100644 --- a/libjava/classpath/javax/swing/ListCellRenderer.java +++ b/libjava/classpath/javax/swing/ListCellRenderer.java @@ -44,9 +44,7 @@ import java.awt.Component; */ public interface ListCellRenderer { - Component getListCellRendererComponent(JList list, - Object value, - int index, - boolean isSelected, - boolean cellHasFocus); + Component getListCellRendererComponent(JList list, Object value, int index, + boolean isSelected, + boolean cellHasFocus); } diff --git a/libjava/classpath/javax/swing/ListSelectionModel.java b/libjava/classpath/javax/swing/ListSelectionModel.java index f4680d737fb..324c056431b 100644 --- a/libjava/classpath/javax/swing/ListSelectionModel.java +++ b/libjava/classpath/javax/swing/ListSelectionModel.java @@ -46,41 +46,51 @@ import javax.swing.event.ListSelectionListener; */ public interface ListSelectionModel { + int SINGLE_SELECTION = 0; + int SINGLE_INTERVAL_SELECTION = 1; + int MULTIPLE_INTERVAL_SELECTION = 2; void setSelectionMode(int a); + int getSelectionMode(); - + void clearSelection(); - + int getMinSelectionIndex(); + int getMaxSelectionIndex(); boolean isSelectedIndex(int a); boolean isSelectionEmpty(); + void setSelectionInterval(int index0, int index1); - void addSelectionInterval(int index0, - int index1); - void removeSelectionInterval(int index0, - int index1); - void insertIndexInterval(int index, - int length, - boolean before); - void removeIndexInterval(int index0, - int index1); + + void addSelectionInterval(int index0, int index1); + + void removeSelectionInterval(int index0, int index1); + + void insertIndexInterval(int index, int length, boolean before); + + void removeIndexInterval(int index0, int index1); int getAnchorSelectionIndex(); + void setAnchorSelectionIndex(int index); + int getLeadSelectionIndex(); + void setLeadSelectionIndex(int index); void setValueIsAdjusting(boolean valueIsAdjusting); + boolean getValueIsAdjusting(); void addListSelectionListener(ListSelectionListener listener); - void removeListSelectionListener(ListSelectionListener listener); + + void removeListSelectionListener(ListSelectionListener listener); } diff --git a/libjava/classpath/javax/swing/LookAndFeel.java b/libjava/classpath/javax/swing/LookAndFeel.java index 8858742711d..1a67e849735 100644 --- a/libjava/classpath/javax/swing/LookAndFeel.java +++ b/libjava/classpath/javax/swing/LookAndFeel.java @@ -38,9 +38,17 @@ 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.text.JTextComponent; public abstract class LookAndFeel @@ -104,6 +112,8 @@ public abstract class LookAndFeel */ public void initialize() { + // We do nothing here. This method is meant to be overridden by + // LookAndFeel implementations. } /** @@ -113,14 +123,27 @@ public abstract class LookAndFeel */ 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. */ - public static void installColors(JComponent c, String defaultBgName, String defaultFgName) + 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)); } /** @@ -128,10 +151,16 @@ public abstract class LookAndFeel * and font properties with values from the current defaults table. */ public static void installColorsAndFont(JComponent component, - String defaultBgName, - String defaultFgName, - String defaultFontName) + 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)); } /** @@ -156,19 +185,47 @@ public abstract class LookAndFeel public abstract boolean isSupportedLookAndFeel(); /** - * Loads the bindings in keys into retMap. + * Loads the bindings in keys into retMap. Does not remove existing entries + * from retMap. <code>keys</code> 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. + * Creates a ComponentInputMap from keys. + * <code>keys</code> 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 */ public static ComponentInputMap makeComponentInputMap(JComponent c, Object[] keys) { - return null; + ComponentInputMap retMap = new ComponentInputMapUIResource(c); + loadKeyBindings(retMap, keys); + return retMap; } /** @@ -177,23 +234,55 @@ public abstract class LookAndFeel */ public static Object makeIcon(Class baseClass, String gifFile) { - return null; + 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. + * <code>keys</code> 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 */ public static InputMap makeInputMap(Object[] keys) { - return null; + InputMap retMap = new InputMapUIResource(); + loadKeyBindings(retMap, keys); + return retMap; } /** - * Convenience method for building lists of KeyBindings. + * Convenience method for building lists of KeyBindings. + * <code>keyBindingList</code> 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) { - return null; + 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; } /** @@ -224,6 +313,8 @@ public abstract class LookAndFeel */ public void uninitialize() { + // We do nothing here. This method is meant to be overridden by + // LookAndFeel implementations. } /** @@ -232,5 +323,7 @@ public abstract class LookAndFeel */ public static void uninstallBorder(JComponent c) { + if (c.getBorder() instanceof UIResource) + c.setBorder(null); } } diff --git a/libjava/classpath/javax/swing/MenuElement.java b/libjava/classpath/javax/swing/MenuElement.java index 46eb8c2a5fe..dab7b9cf1ad 100644 --- a/libjava/classpath/javax/swing/MenuElement.java +++ b/libjava/classpath/javax/swing/MenuElement.java @@ -47,47 +47,43 @@ import java.awt.event.MouseEvent; * * @author Andrew Selkirk */ -public interface MenuElement { - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * 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(); - - -} // MenuElement +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 index 32d56b958a1..4e52751065a 100644 --- a/libjava/classpath/javax/swing/MenuSelectionManager.java +++ b/libjava/classpath/javax/swing/MenuSelectionManager.java @@ -146,7 +146,9 @@ public class MenuSelectionManager { // Convert sourcePoint to screen coordinates. Point sourcePointOnScreen = sourcePoint; - SwingUtilities.convertPointToScreen(sourcePointOnScreen, source); + + if (source.isShowing()) + SwingUtilities.convertPointToScreen(sourcePointOnScreen, source); Point compPointOnScreen; Component resultComp = null; @@ -168,7 +170,10 @@ public class MenuSelectionManager && sourcePointOnScreen.y < compPointOnScreen.y + size.height) { Point p = sourcePointOnScreen; - SwingUtilities.convertPointFromScreen(p, comp); + + if (comp.isShowing()) + SwingUtilities.convertPointFromScreen(p, comp); + resultComp = SwingUtilities.getDeepestComponentAt(comp, p.x, p.y); break; } diff --git a/libjava/classpath/javax/swing/MutableComboBoxModel.java b/libjava/classpath/javax/swing/MutableComboBoxModel.java index ee79dac03a5..93091786e53 100644 --- a/libjava/classpath/javax/swing/MutableComboBoxModel.java +++ b/libjava/classpath/javax/swing/MutableComboBoxModel.java @@ -76,7 +76,7 @@ public interface MutableComboBoxModel extends ComboBoxModel /** * This method removes given element from the data model * - * @param element to remove. + * @param object element to remove. */ void removeElement(Object object); -} // MutableComboBoxModel +} diff --git a/libjava/classpath/javax/swing/OverlayLayout.java b/libjava/classpath/javax/swing/OverlayLayout.java index e8aef98a521..56b8c8bb67a 100644 --- a/libjava/classpath/javax/swing/OverlayLayout.java +++ b/libjava/classpath/javax/swing/OverlayLayout.java @@ -1,5 +1,5 @@ -/* OverlayLayout.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. +/* OverlayLayout.java -- A layout manager + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,155 +37,376 @@ 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; /** - * OverlayLayout - * @author Andrew Selkirk - * @version 1.0 + * 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 + * <code>alignmentX</code> and <code>alignmentY</code> 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: + * + * <pre> + * +-------+ + * | 1 | + * +-------+ + * +-------+ + * | 2 | + * +-------+ + * +---------+ + * | 3 + + * +---------+ + * </pre> + * 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 +public class OverlayLayout implements LayoutManager2, Serializable { private static final long serialVersionUID = 18082829169631543L; - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- - - /** - * target - */ - private Container target; - - /** - * xChildren - */ - private SizeRequirements[] xChildren; - - /** - * yChildren - */ - private SizeRequirements[] yChildren; - - /** - * xTotal - */ - private SizeRequirements xTotal; - - /** - * yTotal - */ - private SizeRequirements yTotal; - - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor OverlayLayout - * @param target TODO - */ - public OverlayLayout(Container target) { - // TODO - } // OverlayLayout() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * invalidateLayout - * @param target TODO - */ - public void invalidateLayout(Container target) { - // TODO - } // invalidateLayout() - - /** - * addLayoutComponent - * @param string TODO - * @param component TODO - */ - public void addLayoutComponent(String string, Component component) { - // TODO - } // addLayoutComponent() - - /** - * addLayoutComponent - * @param component TODO - * @param constraints TODO - */ - public void addLayoutComponent(Component component, Object constraints) { - // TODO - } // addLayoutComponent() - - /** - * removeLayoutComponent - * @param component TODO - */ - public void removeLayoutComponent(Component component) { - // TODO - } // removeLayoutComponent() - - /** - * preferredLayoutSize - * @param target TODO - * @returns Dimension - */ - public Dimension preferredLayoutSize(Container target) { - return null; // TODO - } // preferredLayoutSize() - - /** - * minimumLayoutSize - * @param target TODO - * @returns Dimension - */ - public Dimension minimumLayoutSize(Container target) { - return null; // TODO - } // minimumLayoutSize() - - /** - * maximumLayoutSize - * @param target TODO - * @returns Dimension - */ - public Dimension maximumLayoutSize(Container target) { - return null; // TODO - } // maximumLayoutSize() - - /** - * getLayoutAlignmentX - * @param target TODO - * @returns float - */ - public float getLayoutAlignmentX(Container target) { - return (float) 0.0; // TODO - } // getLayoutAlignmentX() - - /** - * getLayoutAlignmentY - * @param target TODO - * @returns float - */ - public float getLayoutAlignmentY(Container target) { - return (float) 0.0; // TODO - } // getLayoutAlignmentY() - - /** - * layoutContainer - * @param target TODO - */ - public void layoutContainer(Container target) { - // TODO - } // layoutContainer() - - -} // OverlayLayout + /** + * 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 + * + * @returns 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 + * + * @returns 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 + * + * @returns 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 + * + * @returns 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 + * + * @returns 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 + * <code>alignmentX</code> and <code>alignmentY</code> 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: + * + * <pre> + * +-------+ + * | 1 | + * +-------+ + * +-------+ + * | 2 | + * +-------+ + * +---------+ + * | 3 + + * +---------+ + * </pre> + * 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 index 69e1f516802..cbb243e285a 100644 --- a/libjava/classpath/javax/swing/Popup.java +++ b/libjava/classpath/javax/swing/Popup.java @@ -39,6 +39,8 @@ exception statement from your version. */ package javax.swing; import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.Point; /** @@ -89,6 +91,7 @@ public class Popup */ protected Popup() { + // Nothing to do here. } @@ -129,6 +132,7 @@ public class Popup */ JWindow window; + private Component contents; /** * Constructs a new <code>JWindowPopup</code> given its owner, @@ -155,10 +159,11 @@ public class Popup /* Checks whether contents is null. */ super(owner, contents, x, y); + this.contents = contents; window = new JWindow(); - window.getRootPane().add(contents); + window.getContentPane().add(contents); window.setLocation(x, y); - window.pack(); + window.setFocusableWindowState(false); } @@ -168,6 +173,7 @@ public class Popup */ public void show() { + window.setSize(contents.getSize()); window.show(); } @@ -186,4 +192,106 @@ public class Popup 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 <code>LightweightPopup</code> 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 <code>Popup</code>. + * + * @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 <code>contents</code> + * is <code>null</code>. + */ + 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); + } + + /** + * Removes the popup from the JLayeredPane thus making it invisible. + */ + public void hide() + { + layeredPane.remove(panel); + } + } } diff --git a/libjava/classpath/javax/swing/PopupFactory.java b/libjava/classpath/javax/swing/PopupFactory.java index 29cf86d5530..7bb2529cd54 100644 --- a/libjava/classpath/javax/swing/PopupFactory.java +++ b/libjava/classpath/javax/swing/PopupFactory.java @@ -39,6 +39,8 @@ exception statement from your version. */ package javax.swing; import java.awt.Component; +import java.awt.Dimension; +import java.awt.Point; /** @@ -55,8 +57,8 @@ public class PopupFactory /** * The shared factory object. * - * @see #getSharedFactory - * @see #setSharedFactory + * @see #getSharedInstance + * @see #setSharedInstance */ private static PopupFactory sharedFactory; @@ -69,6 +71,7 @@ public class PopupFactory */ public PopupFactory() { + // Nothing to do here. } @@ -134,6 +137,30 @@ public class PopupFactory public Popup getPopup(Component owner, Component contents, int x, int y) { - return new Popup.JWindowPopup(owner, contents, x, 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); + 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); + return popup; } } diff --git a/libjava/classpath/javax/swing/ProgressMonitor.java b/libjava/classpath/javax/swing/ProgressMonitor.java index 844258f1b90..60f1c7145c0 100644 --- a/libjava/classpath/javax/swing/ProgressMonitor.java +++ b/libjava/classpath/javax/swing/ProgressMonitor.java @@ -1,5 +1,5 @@ /* ProgressMonitor.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,191 +38,386 @@ exception statement from your version. */ package javax.swing; import java.awt.Component; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; /** - * ProgressMonitor - * @author Andrew Selkirk - * @version 1.0 + * <p>Using this class you can easily monitor tasks where you cannot + * estimate the duration exactly.</p> + * + * <p>A ProgressMonitor instance waits until the first time setProgress + * is called. When <code>millisToDecideToPopup</code> time elapsed the + * instance estimates the duration until the whole operation is completed. + * If this duration exceeds <code>millisToPopup</code> a non-modal dialog + * with a message and a progress bar is shown.</p> + * + * <p>The value of <code>millisToDecideToPopup</code> defaults to + * <code>500</code> and <code>millisToPopup</code> to + * <code>2000</code>.</p> + * + * @author Andrew Selkirk + * @author Robert Schuster (robertschuster@fsfe.org) + * @since 1.2 + * @status updated to 1.2 */ -public class ProgressMonitor { - - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- - - /** - * parentComponent - */ - private Component component; - - /** - * note - */ - private String note; - - /** - * message - */ - private Object message; - - /** - * millisToDecideToPopup - */ - private int millisToDecideToPopup; - - /** - * millisToPopup - */ - private int millisToPopup; - - /** - * min - */ - private int minimum; - - /** - * max - */ - private int maximum; - - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor ProgressMonitor - * @param component TODO - * @param message TODO - * @param note TODO - * @param minimum TODO - * @param maximum TODO - */ - public ProgressMonitor(Component component, Object message, - String note, int minimum, int maximum) { - - // Set Data - this.component = component; - this.message = message; - this.note = note; - this.minimum = minimum; - this.maximum = maximum; - - // TODO - } // ProgressMonitor() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * close - */ - public void close() { - // TODO - } // close() - - /** - * setProgress - * @param progress TODO - */ - public void setProgress(int progress) { - // TODO - } // setProgress() - - /** - * getMinimum - * @returns int - */ - public int getMinimum() { - return minimum; // TODO - } // getMinimum() - - /** - * setMinimum - * @param minimum TODO - */ - public void setMinimum(int minimum) { - this.minimum = minimum; - // TODO - } // setMinimum() - - /** - * getMaximum - * @returns int - */ - public int getMaximum() { - return maximum; // TODO - } // getMaximum() - - /** - * setMaximum - * @param maximum TODO - */ - public void setMaximum(int maximum) { - this.maximum = maximum; - // TODO - } // setMaximum() - - /** - * isCanceled - * @returns boolean - */ - public boolean isCanceled() { - return false; // TODO - } // isCanceled() - - /** - * getMillisToDecideToPopup - * @returns int - */ - public int getMillisToDecideToPopup() { - return millisToDecideToPopup; // TODO - } // getMillisToDecideToPopup() - - /** - * setMillisToDecideToPopup - * @param time TODO - */ - public void setMillisToDecideToPopup(int time) { - millisToDecideToPopup = time; - // TODO - } // setMillisToDecideToPopup() - - /** - * getMillisToPopup - * @returns int - */ - public int getMillisToPopup() { - return millisToPopup; // TODO - } // getMillisToPopup() - - /** - * setMillisToPopup - * @param time TODO - */ - public void setMillisToPopup(int time) { - millisToPopup = time; - // TODO - } // setMillisToPopup() - - /** - * getNote - * @returns String - */ - public String getNote() { - return note; // TODO - } // getNote() - - /** - * setNote - * @param note TODO - */ - public void setNote(String note) { - this.note = note; - // TODO - } // setNote() - - -} // ProgressMonitor +public class ProgressMonitor +{ + /** + * 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; + + /** + * Constructor ProgressMonitor + * @param component The parent component of the progress dialog or <code>null</code>. + * @param message A constant message object which works in the way it does in <code>JOptionPane</code>. + * @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; + } + + /** + * <p>Hides the dialog and stops any measurements.</p> + * + * <p>Has no effect when <code>setProgress</code> is not at least + * called once.</p> + */ + public void close() + { + if ( progressDialog != null ) + { + progressDialog.setVisible(false); + } + + if ( timer != null ) + { + timer.stop(); + timer = null; + } + } + + /** + * <p>Updates the progress value.</p> + * + * <p>When called for the first time this initializes a timer + * which decides after <code>millisToDecideToPopup</code> time + * whether to show a progress dialog or not.</p> + * + * <p>If the progress value equals or exceeds the maximum + * value the progress dialog is closed automatically.</p> + * + * @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. + * + * @returns Minimum or start value of the operation. + */ + public int getMinimum() + { + return min; + } + + /** + * <p>Use this method to set the minimum or start value of + * your operation.</p> + * + * <p>For typical application like copy operation this will be + * zero.</p> + * + * <p>Keep in mind that changing this value after the progress + * dialog is made visible has no effect upon the progress bar.</p> + * + * @param minimum The new minimum value. + */ + public void setMinimum(int minimum) + { + min = minimum; + } + + /** + * Return the maximum or end value of your operation. + * + * @returns Maximum or end value. + */ + public int getMaximum() + { + return max; + } + + /** + * <p>Sets the maximum or end value of the operation to the + * given integer.</p> + * + * @param maximum + */ + public void setMaximum(int maximum) + { + max = maximum; + } + + /** + * Returns whether the user canceled the operation. + * + * @returns 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. + * + * @returns 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. + * + * <p>This method has no effect when the progress dialog + * is already visible.</p> + * + * @param time The duration in milliseconds. + */ + public void setMillisToDecideToPopup(int time) + { + millisToDecideToPopup = time; + } + + /** + * getMillisToPopup + * @returns int + */ + public int getMillisToPopup() + { + return millisToPopup; + } + + /** + * setMillisToPopup + * @param time TODO + */ + public void setMillisToPopup(int time) + { + millisToPopup = time; + } + + /** + * Returns a message which is shown in the progress dialog. + * + * @returns The changeable message visible in the progress dialog. + */ + public String getNote() + { + return note; + } + + /** + * <p>Set the message shown in the progess dialog.</p> + * + * <p>Changing the note while the progress dialog is visible + * is possible.</p> + * + * @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 = ( 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; + } + } + +} diff --git a/libjava/classpath/javax/swing/ProgressMonitorInputStream.java b/libjava/classpath/javax/swing/ProgressMonitorInputStream.java index 2022a1c24a3..02ac597b3a4 100644 --- a/libjava/classpath/javax/swing/ProgressMonitorInputStream.java +++ b/libjava/classpath/javax/swing/ProgressMonitorInputStream.java @@ -1,5 +1,5 @@ /* ProgressMonitorInputStream.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,122 +39,187 @@ exception statement from your version. */ package javax.swing; import java.awt.Component; + import java.io.FilterInputStream; -import java.io.IOException; import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.IOException; /** * ProgressMonitorInputStream * @author Andrew Selkirk - * @version 1.0 + * @author Robert Schuster (robertschuster@fsfe.org) + * @status updated to 1.2 + * @since 1.2 */ -public class ProgressMonitorInputStream extends FilterInputStream { - - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- - - /** - * monitor - */ - private ProgressMonitor monitor; - - /** - * nread - */ - private int nread; - - /** - * size - */ - private int size; - - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor ProgressMonitorInputStream - * @param component TODO - * @param message TODO - * @param stream TODO - */ - public ProgressMonitorInputStream(Component component, Object message, - InputStream stream) { - super(stream); - // TODO - } // ProgressMonitorInputStream() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * reset - * @exception IOException TODO - */ - public synchronized void reset() throws IOException { - // TODO - } // reset() - - /** - * read - * @exception IOException TODO - * @returns int - */ - public int read() throws IOException { - return 0; // TODO - } // read() - - /** - * read - * @param data TODO - * @exception IOException TODO - * @returns int - */ - public int read(byte[] data) throws IOException { - return 0; // TODO - } // read() - - /** - * read - * @param data TODO - * @param offset TODO - * @param length TODO - * @exception IOException TODO - * @returns int - */ - public int read(byte[] data, int offset, int length) throws IOException { - return 0; // TODO - } // read() - - /** - * skip - * @param length TODO - * @exception IOException TODO - * @returns long - */ - public long skip(long length) throws IOException { - return 0; // TODO - } // skip() - - /** - * close - * @exception IOException TODO - */ - public void close() throws IOException { - // TODO - } // close() - - /** - * getProgressMonitor - * @returns ProgressMonitor - */ - public ProgressMonitor getProgressMonitor() { - return null; // TODO - } // getProgressMonitor() - - -} // ProgressMonitorInputStream +public class ProgressMonitorInputStream extends FilterInputStream +{ + + /** + * monitor + */ + private ProgressMonitor monitor; + + /** + * read + */ + private int read; + + /** + * Constructor ProgressMonitorInputStream + * @param component TODO + * @param message TODO + * @param stream TODO + */ + 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 ); + } + + /** + * reset + * @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) + } + + /** + * read + * @exception IOException TODO + * @returns int + */ + public int read() throws IOException + { + int t = super.read(); + + monitor.setProgress(++read); + + checkMonitorCanceled(); + + return t; + } + + /** + * read + * @param data TODO + * @exception IOException TODO + * @returns int + */ + 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; + } + + /** + * read + * @param data TODO + * @param offset TODO + * @param length TODO + * @exception IOException TODO + * @returns int + */ + 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; + } + + /** + * skip + * @param length TODO + * @exception IOException TODO + * @returns long + */ + 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; + } + + /** + * close + * @exception IOException TODO + */ + public void close() throws IOException + { + super.close(); + monitor.close(); + } + + /** + * getProgressMonitor + * @returns ProgressMonitor + */ + 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 index c803e38fcc0..9a0e81a9f36 100644 --- a/libjava/classpath/javax/swing/Renderer.java +++ b/libjava/classpath/javax/swing/Renderer.java @@ -49,24 +49,20 @@ import java.awt.Component; * * @author Andrew Selkirk */ -public interface Renderer { +public interface Renderer +{ + /** + * setValue + * @param value TODO + * @param selected TODO + */ + void setValue(Object value, boolean selected); - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- + /** + * getComponent + * @returns Component + */ + Component getComponent(); - /** - * setValue - * @param value TODO - * @param selected TODO - */ - void setValue(Object value, boolean selected); - /** - * getComponent - * @returns Component - */ - Component getComponent(); - - -} // Renderer +} diff --git a/libjava/classpath/javax/swing/RepaintManager.java b/libjava/classpath/javax/swing/RepaintManager.java index 698dbe8e898..b857b126180 100644 --- a/libjava/classpath/javax/swing/RepaintManager.java +++ b/libjava/classpath/javax/swing/RepaintManager.java @@ -1,5 +1,5 @@ /* RepaintManager.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -43,12 +43,11 @@ import java.awt.Dimension; import java.awt.Image; import java.awt.Rectangle; import java.awt.image.VolatileImage; -import java.util.Enumeration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; -import java.util.Hashtable; import java.util.Iterator; -import java.util.Map; -import java.util.Vector; /** * <p>The repaint manager holds a set of dirty regions, invalid components, @@ -66,7 +65,11 @@ import java.util.Vector; */ public class RepaintManager { - + /** + * The current repaint managers, indexed by their ThreadGroups. + */ + static HashMap currentRepaintManagers; + /** * <p>A helper class which is placed into the system event queue at * various times in order to facilitate repainting and layout. There is @@ -80,33 +83,95 @@ public class RepaintManager * swing paint thread, which revalidates all invalid components and * repaints any damage in the swing scene.</p> */ - protected 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() { - RepaintManager rm = RepaintManager.globalManager; + ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); + RepaintManager rm = + (RepaintManager) currentRepaintManagers.get(threadGroup); setLive(false); rm.validateInvalidComponents(); rm.paintDirtyRegions(); } + + } + + /** + * Compares two components using their depths in the component hierarchy. + * A component with a lesser depth (higher level components) are sorted + * before components with a deeper depth (low level components). This is used + * to order paint requests, so that the higher level components are painted + * before the low level components get painted. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class ComponentComparator implements Comparator + { + + /** + * Compares two components. + * + * @param o1 the first component + * @param o2 the second component + * + * @return a negative integer, if <code>o1</code> is higher in the + * hierarchy than <code>o2</code>, zero, if both are at the same + * level and a positive integer, if <code>o1</code> is deeper in + * the hierarchy than <code>o2</code> + */ + public int compare(Object o1, Object o2) + { + if (o1 instanceof JComponent && o2 instanceof JComponent) + { + JComponent c1 = (JComponent) o1; + JComponent c2 = (JComponent) o2; + return getDepth(c1) - getDepth(c2); + } + else + throw new ClassCastException("This comparator can only be used with " + + "JComponents"); + } + + /** + * Computes the depth for a given JComponent. + * + * @param c the component to compute the depth for + * + * @return the depth of the component + */ + private int getDepth(JComponent c) + { + Component comp = c; + int depth = 0; + while (comp != null) + { + comp = comp.getParent(); + depth++; + } + return depth; + } } - /** * A table storing the dirty regions of components. The keys of this * table are components, the values are rectangles. Each component maps @@ -119,7 +184,20 @@ public class RepaintManager * @see #markCompletelyClean * @see #markCompletelyDirty */ - Hashtable dirtyComponents; + HashMap dirtyComponents; + + HashMap workDirtyComponents; + + /** + * Stores the order in which the components get repainted. + */ + ArrayList repaintOrder; + ArrayList workRepaintOrder; + + /** + * The comparator used for ordered inserting into the repaintOrder list. + */ + Comparator comparator; /** * A single, shared instance of the helper class. Any methods which mark @@ -142,14 +220,15 @@ public class RepaintManager * @see #removeInvalidComponent * @see #validateInvalidComponents */ - Vector invalidComponents; + ArrayList invalidComponents; + ArrayList workInvalidComponents; /** * 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 #getDoubleBufferingEnabled + * @see #isDoubleBufferingEnabled * @see #setDoubleBufferingEnabled */ boolean doubleBufferingEnabled; @@ -176,53 +255,62 @@ public class RepaintManager /** - * The global, shared RepaintManager instance. This is reused for all - * components in all windows. This is package-private to avoid an accessor - * method. - * - * @see #currentManager - * @see #setCurrentManager - */ - static RepaintManager globalManager; - - /** * Create a new RepaintManager object. */ public RepaintManager() { - dirtyComponents = new Hashtable(); - invalidComponents = new Vector(); + dirtyComponents = new HashMap(); + workDirtyComponents = new HashMap(); + repaintOrder = new ArrayList(); + workRepaintOrder = new ArrayList(); + invalidComponents = new ArrayList(); + workInvalidComponents = new ArrayList(); repaintWorker = new RepaintWorker(); doubleBufferMaximumSize = new Dimension(2000,2000); doubleBufferingEnabled = true; } /** - * Get the value of the shared {@link #globalManager} instance, possibly - * returning a special manager associated with the specified - * component. The default implementaiton ignores the component parameter. + * Returns the <code>RepaintManager</code> for the current thread's + * thread group. The default implementation ignores the + * <code>component</code> parameter and returns the same repaint manager + * for all components. * - * @param component A component to look up the manager of + * @param component a component to look up the manager of * - * @return The current repaint manager + * @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 (globalManager == null) - globalManager = new RepaintManager(); - return globalManager; + if (currentRepaintManagers == null) + currentRepaintManagers = new HashMap(); + ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); + RepaintManager currentManager = + (RepaintManager) currentRepaintManagers.get(threadGroup); + if (currentManager == null) + { + currentManager = new RepaintManager(); + currentRepaintManagers.put(threadGroup, currentManager); + } + return currentManager; } /** - * Get the value of the shared {@link #globalManager} instance, possibly - * returning a special manager associated with the specified - * component. The default implementaiton ignores the component parameter. + * Returns the <code>RepaintManager</code> for the current thread's + * thread group. The default implementation ignores the + * <code>component</code> parameter and returns the same repaint manager + * for all components. * - * @param component A component to look up the manager of + * This method is only here for backwards compatibility with older versions + * of Swing and simply forwards to {@link #currentManager(Component)}. * - * @return The current repaint manager + * @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 */ @@ -232,15 +320,20 @@ public class RepaintManager } /** - * Set the value of the shared {@link #globalManager} instance. + * Sets the repaint manager for the calling thread's thread group. * - * @param manager The new value of the shared instance + * @param manager the repaint manager to set for the current thread's thread + * group * - * @see #currentManager(JComponent) + * @see #currentManager(Component) */ public static void setCurrentManager(RepaintManager manager) { - globalManager = manager; + if (currentRepaintManagers == null) + currentRepaintManagers = new HashMap(); + + ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); + currentRepaintManagers.put(threadGroup, manager); } /** @@ -287,7 +380,7 @@ public class RepaintManager */ public synchronized void removeInvalidComponent(JComponent component) { - invalidComponents.removeElement(component); + invalidComponents.remove(component); } /** @@ -311,12 +404,13 @@ public class RepaintManager public synchronized void addDirtyRegion(JComponent component, int x, int y, int w, int h) { - if (w == 0 || h == 0) + if (w == 0 || h == 0 || !component.isShowing()) return; - Rectangle r = new Rectangle(x, y, w, h); if (dirtyComponents.containsKey(component)) r = r.union((Rectangle)dirtyComponents.get(component)); + else + insertInRepaintOrder(component); dirtyComponents.put(component, r); if (! repaintWorker.isLive()) { @@ -324,7 +418,23 @@ public class RepaintManager SwingUtilities.invokeLater(repaintWorker); } } - + + /** + * Inserts a component into the repaintOrder list in an ordered fashion, + * using a binary search. + * + * @param c the component to be inserted + */ + private void insertInRepaintOrder(JComponent c) + { + if (comparator == null) + comparator = new ComponentComparator(); + int insertIndex = Collections.binarySearch(repaintOrder, c, comparator); + if (insertIndex < 0) + insertIndex = -(insertIndex + 1); + repaintOrder.add(insertIndex, c); + } + /** * Get the dirty region associated with a component, or <code>null</code> * if the component has no dirty region. @@ -341,7 +451,10 @@ public class RepaintManager */ public Rectangle getDirtyRegion(JComponent component) { - return (Rectangle) dirtyComponents.get(component); + Rectangle dirty = (Rectangle) dirtyComponents.get(component); + if (dirty == null) + dirty = new Rectangle(); + return dirty; } /** @@ -359,6 +472,7 @@ public class RepaintManager { Rectangle r = component.getBounds(); addDirtyRegion(component, r.x, r.y, r.width, r.height); + component.isCompletelyDirty = true; } /** @@ -374,7 +488,11 @@ public class RepaintManager */ public void markCompletelyClean(JComponent component) { - dirtyComponents.remove(component); + synchronized (this) + { + dirtyComponents.remove(component); + } + component.isCompletelyDirty = false; } /** @@ -393,13 +511,9 @@ public class RepaintManager */ public boolean isCompletelyDirty(JComponent component) { - Rectangle dirty = (Rectangle) dirtyComponents.get(component); - if (dirty == null) + if (! dirtyComponents.containsKey(component)) return false; - Rectangle r = component.getBounds(); - if (r == null) - return true; - return dirty.contains(r); + return component.isCompletelyDirty; } /** @@ -408,58 +522,56 @@ public class RepaintManager */ public void validateInvalidComponents() { - for (Enumeration e = invalidComponents.elements(); e.hasMoreElements(); ) + // In order to keep the blocking of application threads minimal, we switch + // the invalidComponents field with the workInvalidComponents field and + // work with the workInvalidComponents field. + synchronized(this) + { + ArrayList swap = invalidComponents; + invalidComponents = workInvalidComponents; + workInvalidComponents = swap; + } + for (Iterator i = workInvalidComponents.iterator(); i.hasNext(); ) { - JComponent comp = (JComponent) e.nextElement(); + JComponent comp = (JComponent) i.next(); if (! (comp.isVisible() && comp.isShowing())) continue; comp.validate(); } - invalidComponents.clear(); + workInvalidComponents.clear(); } /** * Repaint all regions of all components which have been marked dirty in * the {@link #dirtyComponents} table. */ - public void paintDirtyRegions() + public synchronized void paintDirtyRegions() { - // step 1: pull out roots and calculate spanning damage - - HashMap roots = new HashMap(); - for (Enumeration e = dirtyComponents.keys(); e.hasMoreElements(); ) + // In order to keep the blocking of application threads minimal, we switch + // the dirtyComponents field with the workdirtyComponents field and the + // repaintOrder field with the workRepaintOrder field and work with the + // work* fields. + synchronized(this) + { + ArrayList swap = workRepaintOrder; + workRepaintOrder = repaintOrder; + repaintOrder = swap; + HashMap swap2 = workDirtyComponents; + workDirtyComponents = dirtyComponents; + dirtyComponents = swap2; + } + for (Iterator i = workRepaintOrder.iterator(); i.hasNext();) { - JComponent comp = (JComponent) e.nextElement(); - if (! (comp.isVisible() && comp.isShowing())) - continue; - Rectangle damaged = getDirtyRegion(comp); - if (damaged.width == 0 || damaged.height == 0) + JComponent comp = (JComponent) i.next(); + // If a component is marked completely clean in the meantime, then skip + // it. + Rectangle damaged = (Rectangle) workDirtyComponents.get(comp); + if (damaged == null || damaged.isEmpty()) continue; - JRootPane root = comp.getRootPane(); - // If the component has no root, no repainting will occur. - if (root == null) - continue; - Rectangle rootDamage = SwingUtilities.convertRectangle(comp, damaged, root); - if (! roots.containsKey(root)) - { - roots.put(root, rootDamage); - } - else - { - roots.put(root, ((Rectangle)roots.get(root)).union(rootDamage)); - } - } - dirtyComponents.clear(); - - // step 2: paint those roots - Iterator i = roots.entrySet().iterator(); - while(i.hasNext()) - { - Map.Entry ent = (Map.Entry) i.next(); - JRootPane root = (JRootPane) ent.getKey(); - Rectangle rect = (Rectangle) ent.getValue(); - root.paintImmediately(rect); + comp.paintImmediately(damaged); } + workRepaintOrder.clear(); + workDirtyComponents.clear(); } /** diff --git a/libjava/classpath/javax/swing/RootPaneContainer.java b/libjava/classpath/javax/swing/RootPaneContainer.java index b121f958a92..4b1bece214d 100644 --- a/libjava/classpath/javax/swing/RootPaneContainer.java +++ b/libjava/classpath/javax/swing/RootPaneContainer.java @@ -48,53 +48,49 @@ import java.awt.Container; * * @author Andrew Selkirk */ -public interface RootPaneContainer { - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * 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(); - - -} // RootPaneContainer +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/ScrollPaneLayout.java b/libjava/classpath/javax/swing/ScrollPaneLayout.java index 75a6f9aa208..edf1f1f4292 100644 --- a/libjava/classpath/javax/swing/ScrollPaneLayout.java +++ b/libjava/classpath/javax/swing/ScrollPaneLayout.java @@ -43,12 +43,9 @@ import java.awt.Container; import java.awt.Dimension; import java.awt.Insets; import java.awt.LayoutManager; -import java.awt.Point; import java.awt.Rectangle; import java.io.Serializable; -import javax.swing.border.Border; - /** * ScrollPaneLayout * @author Andrew Selkirk @@ -60,8 +57,11 @@ public class ScrollPaneLayout private static final long serialVersionUID = -4480022884523193743L; public static class UIResource extends ScrollPaneLayout - implements javax.swing.plaf.UIResource { - public UIResource() { + implements javax.swing.plaf.UIResource + { + public UIResource() + { + super(); } } @@ -77,8 +77,9 @@ public class ScrollPaneLayout protected int vsbPolicy; protected int hsbPolicy; - public ScrollPaneLayout() { - + public ScrollPaneLayout() + { + // Nothing to do here. } public void syncWithScrollPane(JScrollPane scrollPane) { @@ -95,11 +96,31 @@ public class ScrollPaneLayout 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) { - return null; + 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) @@ -120,6 +141,8 @@ public class ScrollPaneLayout lowerLeft = component; else if (key == UPPER_LEFT_CORNER) upperLeft = component; + else + throw new IllegalArgumentException(); } public void removeLayoutComponent(Component component) { @@ -147,9 +170,20 @@ public class ScrollPaneLayout { 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; } @@ -158,8 +192,19 @@ public class ScrollPaneLayout 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; } @@ -188,6 +233,12 @@ public class ScrollPaneLayout 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) @@ -201,151 +252,53 @@ public class ScrollPaneLayout return null; } - private static void maybeSetPreferredSize(JComponent src, Dimension dim) - { - Dimension tmp = null; - if (src != null) - tmp = src.getPreferredSize(); - if (tmp != null) - dim.setSize(tmp); - } - - private static void maybeSetMinimumSize(JComponent src, Dimension dim) - { - Dimension tmp = null; - if (src != null) - tmp = src.getMinimumSize(); - if (tmp != null) - dim.setSize(tmp); - } - public Dimension preferredLayoutSize(Container parent) { - if (parent != null && parent instanceof JScrollPane) - { - JScrollPane sc = (JScrollPane) parent; - synchronized (sc.getTreeLock ()) - { - Dimension insetsSize = new Dimension(0,0); - Dimension viewportSize = new Dimension(0,0); - Dimension viewportInsetsSize = new Dimension(0,0); - Dimension columnHeaderSize = new Dimension(0,0); - Dimension rowHeaderSize = new Dimension(0,0); - Dimension verticalScrollBarSize = new Dimension(0,0); - Dimension horizontalScrollBarSize = new Dimension(0,0); - - Insets insets = sc.getInsets(); - Border viewportBorder = sc.getViewportBorder(); - Insets viewportInsets = null; - - if (viewportBorder != null) - { - viewportInsets = viewportBorder.getBorderInsets(parent); - if (viewportInsets != null) - viewportInsetsSize.setSize(viewportInsets.left + viewportInsets.right, - viewportInsets.top + viewportInsets.bottom); - } - - if (insets != null) - insetsSize.setSize(insets.left + insets.right, - insets.top + insets.bottom); - - if (viewport != null) - { - Component view = null; - Scrollable scr = null; - Dimension pref = null; - - view = viewport.getView(); - if (view != null && view instanceof Scrollable) - scr = (Scrollable) view; - if (scr != null) - pref = scr.getPreferredScrollableViewportSize(); - if (pref == null) - pref = viewport.getPreferredSize(); - if (pref != null) - viewportSize.setSize(pref); - } - - maybeSetPreferredSize(colHead, columnHeaderSize); - maybeSetPreferredSize(rowHead, rowHeaderSize); - maybeSetPreferredSize(vsb, verticalScrollBarSize); - maybeSetPreferredSize(hsb, horizontalScrollBarSize); - - return new Dimension(insetsSize.width - + viewportSize.width - + viewportInsetsSize.width - + rowHeaderSize.width - + verticalScrollBarSize.width, - insetsSize.height - + viewportSize.height - + viewportInsetsSize.height - + columnHeaderSize.height - + horizontalScrollBarSize.height); - } - } - else - { - return new Dimension(0,0); - } + // 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; + Insets i = sc.getInsets(); + return new Dimension(width + i.left + i.right, + height + i.left + i.right); } public Dimension minimumLayoutSize(Container parent) { - if (parent instanceof JScrollPane) - { - JScrollPane sc = (JScrollPane) parent; - synchronized (sc.getTreeLock ()) - { - Dimension insetsSize = new Dimension(0,0); - Dimension viewportSize = new Dimension(0,0); - Dimension viewportInsetsSize = new Dimension(0,0); - Dimension columnHeaderSize = new Dimension(0,0); - Dimension rowHeaderSize = new Dimension(0,0); - Dimension verticalScrollBarSize = new Dimension(0,0); - Dimension horizontalScrollBarSize = new Dimension(0,0); - - Insets insets = sc.getInsets(); - Border viewportBorder = sc.getViewportBorder(); - Insets viewportInsets = null; - - if (viewportBorder != null) - { - viewportInsets = viewportBorder.getBorderInsets(parent); - if (viewportInsets != null) - viewportInsetsSize.setSize(viewportInsets.left + viewportInsets.right, - viewportInsets.top + viewportInsets.bottom); - } - - if (insets != null) - insetsSize.setSize(insets.left + insets.right, - insets.top + insets.bottom); - - maybeSetMinimumSize(colHead, columnHeaderSize); - maybeSetMinimumSize(rowHead, rowHeaderSize); - - if (vsbPolicy != VERTICAL_SCROLLBAR_NEVER) - maybeSetMinimumSize(vsb, verticalScrollBarSize); - - if (hsbPolicy != HORIZONTAL_SCROLLBAR_NEVER) - maybeSetMinimumSize(hsb, horizontalScrollBarSize); - - return new Dimension(insetsSize.width - + viewportSize.width - + viewportInsetsSize.width - + rowHeaderSize.width - + verticalScrollBarSize.width, - insetsSize.height - + viewportSize.height - + viewportInsetsSize.height - + columnHeaderSize.height - + horizontalScrollBarSize.height); - } - } - else - { - return new Dimension(0,0); - } + // Sun's implementation simply throws a ClassCastException if + // parent is no JScrollPane, so do we. + JScrollPane sc = (JScrollPane) parent; + Dimension viewportSize = viewport.getMinimumSize(); + int width = viewportSize.width; + int height = viewportSize.height; + if (hsb != null && hsb.isVisible()) + height += hsb.getMinimumSize().height; + if (vsb != null && vsb.isVisible()) + width += vsb.getMinimumSize().width; + if (rowHead != null && rowHead.isVisible()) + width += rowHead.getMinimumSize().width; + if (colHead != null && colHead.isVisible()) + height += colHead.getMinimumSize().height; + Insets i = sc.getInsets(); + return new Dimension(width + i.left + i.right, + height + i.top + i.bottom); } /** @@ -371,100 +324,91 @@ public class ScrollPaneLayout */ public void layoutContainer(Container parent) { - if (parent instanceof JScrollPane) + // Sun's implementation simply throws a ClassCastException if + // parent is no JScrollPane, so do we. + JScrollPane sc = (JScrollPane) parent; + JViewport viewport = sc.getViewport(); + Dimension viewSize = viewport.getViewSize(); + + int x1 = 0, x2 = 0, x3 = 0, x4 = 0; + int y1 = 0, y2 = 0, y3 = 0, y4 = 0; + Rectangle scrollPaneBounds = SwingUtilities.calculateInnerArea(sc, null); + + 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(); + + boolean showVsb = + (vsb != null) + && ((vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS) + || (vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED + && viewSize.height > (y4 - y2))); + boolean showHsb = + (hsb != null) + && ((hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS) + || (hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED + && viewSize.width > (x4 - x2))); + + if (!showVsb) + x3 = x4; + else + x3 = x4 - vsb.getPreferredSize().width; + + if (!showHsb) + y3 = y4; + else + y3 = y4 - hsb.getPreferredSize().height; + + // now set the layout + if (viewport != null) + viewport.setBounds(new Rectangle(x2, y2, x3 - x2, y3 - y2)); + + 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) { - JScrollPane sc = (JScrollPane) parent; - synchronized (sc.getTreeLock ()) - { - JViewport viewport = sc.getViewport(); - Dimension viewSize = viewport.getViewSize(); - Point viewPos = viewport.getViewPosition(); - - int x1 = 0, x2 = 0, x3 = 0, x4 = 0; - int y1 = 0, y2 = 0, y3 = 0, y4 = 0; - - Rectangle scrollPaneBounds = SwingUtilities.calculateInnerArea(sc, null); - - 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(); - - x3 = x4 - vsb.getPreferredSize().width; - y3 = y4 - hsb.getPreferredSize().height; - - boolean showVsb = - (vsb != null) - && ((vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS) - || (vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED - && viewSize.height > (y3 - y2))); - - boolean showHsb = - (hsb != null) - && ((hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS) - || (hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED - && viewSize.width > (x3 - x2))); - - if (!showVsb) - x3 = x4; - - if (!showHsb) - y3 = y4; - - // now set the layout - - if (viewport != null) - viewport.setBounds(new Rectangle(x2, y2, x3-x2, y3-y2)); - - 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)); - - } + 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)); } /** diff --git a/libjava/classpath/javax/swing/Scrollable.java b/libjava/classpath/javax/swing/Scrollable.java index 19732192330..9dce665d626 100644 --- a/libjava/classpath/javax/swing/Scrollable.java +++ b/libjava/classpath/javax/swing/Scrollable.java @@ -48,9 +48,16 @@ import java.awt.Rectangle; */ public interface Scrollable { - Dimension getPreferredScrollableViewportSize(); - int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction); - int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction); - boolean getScrollableTracksViewportWidth(); - boolean getScrollableTracksViewportHeight(); + Dimension getPreferredScrollableViewportSize(); + + int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, + int direction); + + int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, + int direction); + + boolean getScrollableTracksViewportWidth(); + + boolean getScrollableTracksViewportHeight(); + } diff --git a/libjava/classpath/javax/swing/SingleSelectionModel.java b/libjava/classpath/javax/swing/SingleSelectionModel.java index b5380c857a3..d57443b56bb 100644 --- a/libjava/classpath/javax/swing/SingleSelectionModel.java +++ b/libjava/classpath/javax/swing/SingleSelectionModel.java @@ -46,46 +46,41 @@ import javax.swing.event.ChangeListener; * * @author Andrew Selkirk */ -public interface SingleSelectionModel { - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * getSelectedIndex - * @returns int - */ - int getSelectedIndex(); - - /** - * setSelectedIndex - * @param index TODO - */ - void setSelectedIndex(int index); - - /** - * clearSelection - */ - void clearSelection(); - - /** - * isSelected - * @returns boolean - */ - boolean isSelected(); - - /** - * addChangeListener - * @param listener TODO - */ - void addChangeListener(ChangeListener listener); - - /** - * removeChangeListener - * @param listener TODO - */ - void removeChangeListener(ChangeListener listener); - - -} // SingleSelectionModel +public interface SingleSelectionModel +{ + /** + * getSelectedIndex + * @returns int + */ + int getSelectedIndex(); + + /** + * setSelectedIndex + * @param index TODO + */ + void setSelectedIndex(int index); + + /** + * clearSelection + */ + void clearSelection(); + + /** + * isSelected + * @returns boolean + */ + boolean isSelected(); + + /** + * addChangeListener + * @param listener TODO + */ + void addChangeListener(ChangeListener listener); + + /** + * removeChangeListener + * @param listener TODO + */ + void removeChangeListener(ChangeListener listener); + +} diff --git a/libjava/classpath/javax/swing/SizeRequirements.java b/libjava/classpath/javax/swing/SizeRequirements.java index 77b42db1ce9..b9b5c322379 100644 --- a/libjava/classpath/javax/swing/SizeRequirements.java +++ b/libjava/classpath/javax/swing/SizeRequirements.java @@ -116,7 +116,17 @@ public class SizeRequirements implements Serializable */ public String toString() { - return null; // TODO + 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(); } /** @@ -132,13 +142,26 @@ public class SizeRequirements implements Serializable public static SizeRequirements getTiledSizeRequirements(SizeRequirements[] children) { - SizeRequirements result = new SizeRequirements(); + long minimum = 0; + long preferred = 0; + long maximum = 0; for (int i = 0; i < children.length; i++) { - result.minimum += children[i].minimum; - result.preferred += children[i].preferred; - result.maximum += children[i].maximum; + 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; } @@ -156,7 +179,34 @@ public class SizeRequirements implements Serializable public static SizeRequirements getAlignedSizeRequirements(SizeRequirements[] children) { - return null; // TODO + 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); } /** @@ -222,6 +272,7 @@ public class SizeRequirements implements Serializable int[] offsets, int[] spans, boolean forward) { + int span = 0; if (forward) { int offset = 0; @@ -229,6 +280,7 @@ public class SizeRequirements implements Serializable { offsets[i] = offset; spans[i] = children[i].preferred; + span += spans[i]; offset += children[i].preferred; } } @@ -239,9 +291,84 @@ public class SizeRequirements implements Serializable { 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); + } } /** @@ -271,7 +398,8 @@ public class SizeRequirements implements Serializable SizeRequirements[] children, int[] offsets, int[] spans) { - calculateTiledPositions(allocated, total, children, offsets, spans, true); + calculateAlignedPositions(allocated, total, children, offsets, spans, + true); } /** @@ -306,7 +434,74 @@ public class SizeRequirements implements Serializable int[] offset, int[] spans, boolean forward) { - // TODO + // 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); } /** diff --git a/libjava/classpath/javax/swing/SizeSequence.java b/libjava/classpath/javax/swing/SizeSequence.java index cf6e5f042a1..dff966b3e35 100644 --- a/libjava/classpath/javax/swing/SizeSequence.java +++ b/libjava/classpath/javax/swing/SizeSequence.java @@ -42,208 +42,197 @@ package javax.swing; * @author Andrew Selkirk * @version 1.0 */ -public class SizeSequence { - - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- - - /** - * sizes - */ - private int[] sizes = new int[0]; - - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor SizeSequence - */ - public SizeSequence() { - sizes = new int[0]; - } // SizeSequence() - - /** - * Constructor SizeSequence - * @param numEntries TODO - */ - public SizeSequence(int numEntries) { - this(numEntries, 0); - } // SizeSequence() - - /** - * Constructor SizeSequence - * @param numEntries TODO - * @param value TODO - */ - public SizeSequence(int numEntries, int value) { - insertEntries(0, numEntries, value); - } // SizeSequence() - - /** - * Constructor SizeSequence - * @param sizes TODO - */ - public SizeSequence(int[] sizes) { - setSizes(sizes); - } // SizeSequence() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * setSize - * @param index TODO - * @param size TODO - */ - public void setSize(int index, int size) { - sizes[index] = size; - } // setSize() - - /** - * getIndex - * @param position TODO - * @returns int - */ - public int getIndex(int position) { - return 0; // TODO - } // getIndex() - - /** - * getSize - * @param index TODO - * @returns int - */ - public int getSize(int index) { - return sizes[index]; - } // getSize() - - /** - * setSizes - * @param sizes TODO - */ - public void setSizes(int[] sizes) { - - // Variables - int index; - - // Initialize Sizes - this.sizes = new int[sizes.length]; - for (index = 0; index < sizes.length; index++) { - this.sizes[index] = sizes[index]; - } // for - - } // setSizes() - - /** - * getSizes - * @returns int[] - */ - public int[] getSizes() { - - // Variables - int[] array; - int index; - - // Create New Array - array = new int[sizes.length]; - for (index = 0; index < sizes.length; index++) { - array[index] = sizes[index]; - } // for - - // Return Newly created array - return array; - - } // getSizes() - - /** - * getPosition - * @param index TODO - * @returns int - */ - public int getPosition(int index) { - - // Variables - int position; - int loop; - - // Process Sizes - position = 0; - for (loop = 0; loop < index; loop++) { - position += sizes[loop]; - } // for - - // Return Position - return position; - - } // getPosition() - - /** - * insertEntries - * @param start TODO - * @param length TODO - * @param value TODO - */ - public void insertEntries(int start, int length, int value) { - - // Variables - int[] array; - int index; - int arrayIndex; - int loop; - - // Create New Array - array = new int[sizes.length + length]; - arrayIndex = 0; - for (index = 0; index < sizes.length; index++) { - if (index == start) { - for (loop = 0; loop < length; loop++) { - array[arrayIndex] = value; - arrayIndex++; - } // for - } else { - array[arrayIndex] = sizes[index]; - arrayIndex++; - } // if - } // for - - } // insertEntries() - - /** - * removeEntries - * @param start TODO - * @param length TODO - */ - public void removeEntries(int start, int length) { - - // Variables - int[] array; - int index; - int arrayIndex; - - // Sanity Check - if ((start + length) > sizes.length) { - throw new IllegalArgumentException("Specified start/length that " + - "is greater than available sizes"); - } // if - - // Create New Array - array = new int[sizes.length - length]; - arrayIndex = 0; - for (index = 0; index < sizes.length; index++) { - if (index == start) { - index += length - 1; - } else { - array[arrayIndex] = sizes[index]; - arrayIndex++; - } // if - } // for - - } // removeEntries() - - -} // SizeSequence +public class SizeSequence +{ + + /** + * sizes + */ + private int[] sizes = new int[0]; + + /** + * Constructor SizeSequence + */ + public SizeSequence() + { + sizes = new int[0]; + } + + /** + * Constructor SizeSequence + * @param numEntries TODO + */ + public SizeSequence(int numEntries) + { + this(numEntries, 0); + } + + /** + * Constructor SizeSequence + * @param numEntries TODO + * @param value TODO + */ + public SizeSequence(int numEntries, int value) + { + insertEntries(0, numEntries, value); + } + + /** + * Constructor SizeSequence + * @param sizes TODO + */ + public SizeSequence(int[] sizes) + { + setSizes(sizes); + } + + /** + * setSize + * @param index TODO + * @param size TODO + */ + public void setSize(int index, int size) + { + sizes[index] = size; + } + + /** + * getIndex + * @param position TODO + * @returns int + */ + public int getIndex(int position) + { + return 0; // TODO + } + + /** + * getSize + * @param index TODO + * @returns int + */ + public int getSize(int index) + { + return sizes[index]; + } + + /** + * setSizes + * @param sizes TODO + */ + public void setSizes(int[] sizes) + { + int index; + // Initialize sizes. + this.sizes = new int[sizes.length]; + for (index = 0; index < sizes.length; index++) + this.sizes[index] = sizes[index]; + + } + + /** + * getSizes + * @returns int[] + */ + public int[] getSizes() + { + int[] array; + int index; + + // Create new array. + array = new int[sizes.length]; + for (index = 0; index < sizes.length; index++) + array[index] = sizes[index]; + + // Return newly created array. + return array; + + } + + /** + * getPosition + * @param index TODO + * @returns int + */ + public int getPosition(int index) + { + int position; + int loop; + + // Process sizes. + position = 0; + for (loop = 0; loop < index; loop++) + position += sizes[loop]; + + // Return position. + return position; + + } + + /** + * insertEntries + * @param start TODO + * @param length TODO + * @param value TODO + */ + public void insertEntries(int start, int length, int value) + { + int[] array; + int index; + int arrayIndex; + int loop; + + // Create new array. + array = new int[sizes.length + length]; + arrayIndex = 0; + for (index = 0; index < sizes.length; index++) + { + if (index == start) + { + for (loop = 0; loop < length; loop++) + { + array[arrayIndex] = value; + arrayIndex++; + } + } + else + { + array[arrayIndex] = sizes[index]; + arrayIndex++; + } + } + + } + + /** + * removeEntries + * @param start TODO + * @param length TODO + */ + public void removeEntries(int start, int length) + { + int[] array; + int index; + int arrayIndex; + + // Sanity check. + if ((start + length) > sizes.length) + throw new IllegalArgumentException("Specified start/length that " + + "is greater than available sizes"); + + // Create new array. + array = new int[sizes.length - length]; + arrayIndex = 0; + for (index = 0; index < sizes.length; index++) + { + if (index == start) + index += length - 1; + else + { + array[arrayIndex] = sizes[index]; + arrayIndex++; + } + } + } + +} diff --git a/libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java b/libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java index fada17c6339..96ef3832955 100644 --- a/libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java +++ b/libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java @@ -72,7 +72,7 @@ public class SortingFocusTraversalPolicy * simply advance within the containing focus cycle, subject to the * {@link #comparator} order and the {@link #accept} judgment.</p> * - * @see #getNextFocusableComponent + * @see #getImplicitDownCycleTraversal() */ boolean implicitDownCycleTraversal = true; diff --git a/libjava/classpath/javax/swing/SpinnerListModel.java b/libjava/classpath/javax/swing/SpinnerListModel.java index 85dc4efa6f1..d8e2f22d585 100644 --- a/libjava/classpath/javax/swing/SpinnerListModel.java +++ b/libjava/classpath/javax/swing/SpinnerListModel.java @@ -68,231 +68,228 @@ import javax.swing.event.ChangeEvent; * @since 1.4 */ -public class SpinnerListModel - extends AbstractSpinnerModel - implements Serializable +public class SpinnerListModel extends AbstractSpinnerModel + implements Serializable { - /** - * For compatability with Sun's JDK - */ - private static final long serialVersionUID = 3358804052191994516L; + /** + * For compatability with Sun's JDK + */ + private static final long serialVersionUID = 3358804052191994516L; - /** - * The backing list for this model. - */ - private List list; + /** + * The backing list for this model. + */ + private List list; - /** - * The current index in the list. - */ - private transient int index; + /** + * The current index in the list. + */ + private transient int index; - /** - * Constructs a default <code>SpinnerListModel</code>. This - * is a model backed by a list containing only the single - * <code>String</code> element, "empty". - */ - public SpinnerListModel() - { - List defaultList; + /** + * Constructs a default <code>SpinnerListModel</code>. This + * is a model backed by a list containing only the single + * <code>String</code> 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); - } + // Create an empty list. + defaultList = new ArrayList(); + // Add the string "empty". + defaultList.add("empty"); + // Set the list. + setList(defaultList); + } - /** - * Constructs a <code>SpinnerListModel</code> using the supplied list. - * The model maintains a reference to this list, and returns - * consecutive elements in response to calls to <code>getNextValue()</code>. - * The initial value is that at position 0, so an initial call - * to <code>getValue()</code> returns the same as <code>list.get(0)</code>. - * - * @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 <code>SpinnerListModel</code> using the supplied list. + * The model maintains a reference to this list, and returns + * consecutive elements in response to calls to <code>getNextValue()</code>. + * The initial value is that at position 0, so an initial call + * to <code>getValue()</code> returns the same as <code>list.get(0)</code>. + * + * @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 <code>SpinnerListModel</code> using the supplied array. - * The model stores a reference to the wrapper list returned by - * <code>Arrays.asList()</code>. 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 <code>getNextValue()</code>. The initial - * value returned by <code>getValue()</code> is the same as - * <code>array[0]</code>. - * - * @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)); - } + /** + * Constructs a <code>SpinnerListModel</code> using the supplied array. + * The model stores a reference to the wrapper list returned by + * <code>Arrays.asList()</code>. 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 <code>getNextValue()</code>. The initial + * value returned by <code>getValue()</code> is the same as + * <code>array[0]</code>. + * + * @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."); + } - /** - * Returns the backing list for this model. - * - * @return The backing list. - */ - public List getList() - { - return list; - } + // 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 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); - } + /** + * 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; - } + // 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 <code>getNextValue()</code> - * or <code>getPreviousValue()</code>. - * - * @return The current value. - * @see SpinnerListModel#getPreviousValue() - * @see SpinnerListModel#getNextValue() - */ - public Object getValue() - { - return list.get(index); - } + /** + * 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 <code>getNextValue()</code> + * or <code>getPreviousValue()</code>. + * + * @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 - * <code>ChangeEvent</code> 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; - } + /** + * 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 + * <code>ChangeEvent</code> 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."); - /** - * 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, <code>getNextValue()</code> and - * <code>getPreviousValue()</code> 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; + // 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; + } - /* 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(); - } + /** + * 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, <code>getNextValue()</code> and + * <code>getPreviousValue()</code> 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/Spring.java b/libjava/classpath/javax/swing/Spring.java index 69c88c77d96..8f7105d496d 100644 --- a/libjava/classpath/javax/swing/Spring.java +++ b/libjava/classpath/javax/swing/Spring.java @@ -65,6 +65,7 @@ public abstract class Spring */ protected Spring() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/SpringLayout.java b/libjava/classpath/javax/swing/SpringLayout.java index df9ddffb654..592cc0e02a9 100644 --- a/libjava/classpath/javax/swing/SpringLayout.java +++ b/libjava/classpath/javax/swing/SpringLayout.java @@ -395,46 +395,40 @@ public class SpringLayout implements LayoutManager2 public SpringLayout.Constraints getConstraints(Component c) { Constraints constraints = (Constraints) constraintsMap.get(c); + if (constraints == null) { Container parent = c.getParent(); constraints = new Constraints(); + if (parent != null) { - constraints.setX - (Spring.constant(parent.getInsets().left)); - constraints.setY - (Spring.constant(parent.getInsets().top)); + constraints.setX(Spring.constant(parent.getInsets().left)); + constraints.setY(Spring.constant(parent.getInsets().top)); } else { - constraints.setX - (Spring.constant(0)); - constraints.setY - (Spring.constant(0)); - + constraints.setX(Spring.constant(0)); + constraints.setY(Spring.constant(0)); } - constraints.setWidth - (Spring.constant(c.getMinimumSize().width, - c.getPreferredSize().width, - c.getMaximumSize().width)); - constraints.setHeight - (Spring.constant(c.getMinimumSize().height, - c.getPreferredSize().height, - c.getMaximumSize().height)); - - constraintsMap.put(c, constraints); - } + constraints.setWidth(Spring.constant(c.getMinimumSize().width, + c.getPreferredSize().width, + c.getMaximumSize().width)); + constraints.setHeight(Spring.constant(c.getMinimumSize().height, + c.getPreferredSize().height, + c.getMaximumSize().height)); + constraintsMap.put(c, constraints); return constraints; } /** * Returns the X alignment of the Container <code>p</code>. - * - * @param p the {@link java.awt.Container} for which to determine the X - * alignment. + * + * @param p + * the {@link java.awt.Container} for which to determine the X + * alignment. * @return always 0.0 */ public float getLayoutAlignmentX(Container p) @@ -480,6 +474,7 @@ public class SpringLayout implements LayoutManager2 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(); @@ -597,7 +592,6 @@ public class SpringLayout implements LayoutManager2 if (bottomEdge > maxY) maxY = bottomEdge; } - return new Dimension(maxX, maxY); } @@ -621,7 +615,6 @@ public class SpringLayout implements LayoutManager2 Spring strut = Spring.constant(pad); Spring otherEdge = constraints2.getConstraint(e2); constraints1.setConstraint(e1, Spring.sum(strut, otherEdge)); - } /** diff --git a/libjava/classpath/javax/swing/SwingUtilities.java b/libjava/classpath/javax/swing/SwingUtilities.java index ee5fa74d47a..58b3a78d7aa 100644 --- a/libjava/classpath/javax/swing/SwingUtilities.java +++ b/libjava/classpath/javax/swing/SwingUtilities.java @@ -840,7 +840,7 @@ public class SwingUtilities iconR.width = icon.getIconWidth(); iconR.height = icon.getIconHeight(); } - if (text == null) + if (text == null || text.equals("")) { textIconGap = 0; textR.width = 0; @@ -890,7 +890,7 @@ public class SwingUtilities iconR.y = 0; textR.y = (horizontalTextPosition == CENTER ? iconR.height + textIconGap - : iconR.height - textR.height); + : Math.max(iconR.height - textR.height, 0)); break; case CENTER: int centerLine = Math.max(textR.height, iconR.height) / 2; @@ -1116,7 +1116,7 @@ public class SwingUtilities * <pre> * [{@link javax.swing.JComponent#getActionMap()}] * --> [{@link javax.swing.ActionMap}] - * parent --> [{@link javax.swing.text.KeymapActionMap}] + * parent --> [{@link javax.swing.text.JTextComponent.KeymapActionMap}] * parent --> [{@link javax.swing.plaf.ActionMapUIResource}] * </pre> * @@ -1138,14 +1138,12 @@ public class SwingUtilities else { ActionMap parent = child.getParent(); - while(parent != null) + while (parent != null && !(parent instanceof ActionMapUIResource)) { child = parent; parent = child.getParent(); } - - if (child != null) - child.setParent(uiActionMap); + child.setParent(uiActionMap); } } @@ -1159,7 +1157,7 @@ public class SwingUtilities * <pre> * [{@link javax.swing.JComponent#getInputMap()}] * --> [{@link javax.swing.InputMap}] - * parent --> [{@link javax.swing.text.KeymapWrapper}] + * parent --> [{@link javax.swing.text.JTextComponent.KeymapWrapper}] * parent --> [{@link javax.swing.plaf.InputMapUIResource}] * </pre> * @@ -1181,11 +1179,13 @@ public class SwingUtilities component.setInputMap(condition, uiInputMap); else { - while(child.getParent() != null - && !(child.getParent() instanceof InputMapUIResource)) - child = child.getParent(); - if (child != null) - child.setParent(uiInputMap); + InputMap parent = child.getParent(); + while (parent != null && !(parent instanceof InputMapUIResource)) + { + child = parent; + parent = parent.getParent(); + } + child.setParent(uiInputMap); } } diff --git a/libjava/classpath/javax/swing/Timer.java b/libjava/classpath/javax/swing/Timer.java index 87f420a4367..cf91c23e8ec 100644 --- a/libjava/classpath/javax/swing/Timer.java +++ b/libjava/classpath/javax/swing/Timer.java @@ -46,7 +46,12 @@ import java.util.EventListener; import javax.swing.event.EventListenerList; /** - * Fires one or more action events after the specified delay. + * Fires one or more action events after the specified delay. This is + * a specialised version of <code>java.util.Timer</code> just for + * firing <code>ActionEvent</code>s. 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 @@ -55,48 +60,19 @@ public class Timer implements Serializable { /** - * The timer thread + * Given to the shared java.util.Timer to (possibly repeatedly) call + * queueEvent(). */ - private class Waker - extends Thread + private class Task extends java.util.TimerTask { - /** - * Fires events, pausing for required intervals. - */ public void run() { - running = true; - try - { - sleep(initialDelay); - - queueEvent(); - - while (running) - { - try - { - sleep(delay); - } - catch (InterruptedException e) - { - return; - } - queueEvent(); - - if (logTimers) - System.out.println("javax.swing.Timer -> clocktick"); - - if ( ! repeats) - break; - } - running = false; - } - catch (Exception e) - { - // The timer is no longer running. - running = false; - } + if (logTimers) + System.out.println("javax.swing.Timer -> queueEvent()"); + queueEvent(); + + if (!repeats) + task = null; } } @@ -118,6 +94,14 @@ public class Timer }; /** + * 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 <code>true</code>, the timer prints a message to * {@link System#out} when firing each event. */ @@ -139,12 +123,6 @@ public class Timer boolean repeats = true; /** - * <code>true</code> if the timer is currently active, firing events - * as scheduled. - */ - boolean running; - - /** * The delay between subsequent repetetive events. */ int delay; @@ -162,10 +140,9 @@ public class Timer int ticks; /** - * Stores the thread that posts events to the queue at required time - * intervals. + * The task that calls queueEvent(). When null this Timer is stopped. */ - private Waker waker; + private Task task; /** * This object manages a "queue" of virtual actionEvents, maintained as a @@ -360,7 +337,7 @@ public class Timer */ public boolean isRunning() { - return running; + return task != null; } /** @@ -398,10 +375,16 @@ public class Timer */ public void start() { - if (isRunning()) - return; - waker = new Waker(); - waker.start(); + Task t = task; + if (t == null) + { + t = new Task(); + if (isRepeats()) + timer.schedule(t, getInitialDelay(), getDelay()); + else + timer.schedule(t, getInitialDelay()); + task = t; + } } /** @@ -409,12 +392,11 @@ public class Timer */ public void stop() { - running = false; - if (waker != null) - waker.interrupt(); - synchronized (queueLock) + Task t = task; + if (t != null) { - queue = 0; + t.cancel(); + task = null; } } @@ -475,11 +457,11 @@ public class Timer */ void queueEvent() { - synchronized (queueLock) + synchronized(queueLock) { - queue++; - if (queue == 1) - SwingUtilities.invokeLater(drainer); + queue++; + if (queue == 1) + SwingUtilities.invokeLater(drainer); } } } diff --git a/libjava/classpath/javax/swing/ToolTipManager.java b/libjava/classpath/javax/swing/ToolTipManager.java index 61693763898..03835794b68 100644 --- a/libjava/classpath/javax/swing/ToolTipManager.java +++ b/libjava/classpath/javax/swing/ToolTipManager.java @@ -44,7 +44,6 @@ import java.awt.FlowLayout; import java.awt.LayoutManager; import java.awt.Panel; import java.awt.Point; -import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; @@ -68,6 +67,7 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener */ protected stillInsideTimerAction() { + // Nothing to do here. } /** @@ -93,6 +93,7 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener */ protected outsideTimerAction() { + // Nothing to do here. } /** @@ -103,6 +104,7 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener */ public void actionPerformed(ActionEvent event) { + // TODO: What should be done here, if anything? } } @@ -118,6 +120,7 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener */ protected insideTimerAction() { + // Nothing to do here. } /** @@ -129,8 +132,6 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener public void actionPerformed(ActionEvent event) { showTip(); - if (insideTimer != null) - insideTimer.start(); } } @@ -183,7 +184,7 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener * The window used when the tooltip doesn't fit inside the current * container. */ - private static JWindow tooltipWindow; + private static JDialog tooltipWindow; /** * Creates a new ToolTipManager and sets up the timers. @@ -373,7 +374,6 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener if (exitTimer.isRunning()) { exitTimer.stop(); - showTip(); insideTimer.start(); return; } @@ -424,13 +424,6 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener insideTimer.stop(); hideTip(); } - - if (currentComponent == null) - currentComponent = (Component) event.getSource(); - - currentComponent.invalidate(); - currentComponent.validate(); - currentComponent.repaint(); } /** @@ -455,16 +448,8 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener public void mouseMoved(MouseEvent event) { currentPoint = event.getPoint(); - if (currentTip != null) - { - if (currentComponent == null) - currentComponent = (Component) event.getSource(); - - String text = ((JComponent) currentComponent).getToolTipText(event); - currentTip.setTipText(text); - } if (enterTimer.isRunning()) - enterTimer.restart(); + enterTimer.restart(); } /** @@ -474,70 +459,103 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener */ void showTip() { - if (! enabled || currentComponent == null) + if (!enabled || currentComponent == null || !currentComponent.isEnabled() + || (currentTip != null && currentTip.isVisible())) return; - if (currentTip == null - || currentTip.getComponent() != currentComponent + if (currentTip == null || currentTip.getComponent() != currentComponent && currentComponent instanceof JComponent) currentTip = ((JComponent) currentComponent).createToolTip(); + + currentTip.setVisible(true); + Container parent = currentComponent.getParent(); Point p = currentPoint; Dimension dims = currentTip.getPreferredSize(); - if (canToolTipFit(currentTip)) - { - JLayeredPane pane = ((JRootPane) SwingUtilities.getAncestorOfClass(JRootPane.class, - currentComponent)) - .getLayeredPane(); - - // This should never happen, but just in case. - if (pane == null) - return; - - if (containerPanel != null) - hideTip(); - if (isLightWeightPopupEnabled()) - { - containerPanel = new Panel(); - JRootPane root = new JRootPane(); - root.getContentPane().add(currentTip); - containerPanel.add(root); - } - else - { - containerPanel = new JPanel(); - containerPanel.add(currentTip); - } - LayoutManager lm = containerPanel.getLayout(); - if (lm instanceof FlowLayout) - { - FlowLayout fm = (FlowLayout) lm; - fm.setVgap(0); - fm.setHgap(0); - } - - p = getGoodPoint(p, pane, currentTip, dims); - - pane.add(containerPanel); - containerPanel.setBounds(p.x, p.y, dims.width, dims.height); - currentTip.setBounds(0, 0, dims.width, dims.height); - - pane.revalidate(); - pane.repaint(); - } + + if (parent instanceof JPopupMenu) + setLightWeightPopupEnabled(((JPopupMenu) parent).isLightWeightPopupEnabled()); else + setLightWeightPopupEnabled(true); + + if (isLightWeightPopupEnabled()) { - SwingUtilities.convertPointToScreen(p, currentComponent); - tooltipWindow = new JWindow(); - tooltipWindow.getContentPane().add(currentTip); - tooltipWindow.setFocusable(false); - tooltipWindow.pack(); - tooltipWindow.setBounds(p.x, p.y, dims.width, dims.height); - tooltipWindow.show(); + JLayeredPane pane = null; + JRootPane r = ((JRootPane) SwingUtilities. + getAncestorOfClass(JRootPane.class, currentComponent)); + if (r != null) + pane = r.getLayeredPane(); + if (pane == null) + return; + + if (containerPanel != null) + hideTip(); + + containerPanel = new Panel(); + JRootPane root = new JRootPane(); + root.getContentPane().add(currentTip); + containerPanel.add(root); + + LayoutManager lm = containerPanel.getLayout(); + if (lm instanceof FlowLayout) + { + FlowLayout fm = (FlowLayout) lm; + fm.setVgap(0); + fm.setHgap(0); + } + + p = SwingUtilities.convertPoint(currentComponent, p, pane); + p = adjustLocation(p, pane, dims); + + pane.add(containerPanel); + containerPanel.setBounds(p.x, p.y, dims.width, dims.height); + currentTip.setBounds(0, 0, dims.width, dims.height); + containerPanel.validate(); + containerPanel.repaint(); + } + else if (currentComponent.isShowing()) + { + SwingUtilities.convertPointToScreen(p, currentComponent); + p = adjustLocation(p, SwingUtilities.getWindowAncestor(currentComponent), + dims); + + tooltipWindow = new JDialog(); + tooltipWindow.setContentPane(currentTip); + tooltipWindow.setUndecorated(true); + tooltipWindow.getRootPane(). + setWindowDecorationStyle(JRootPane.PLAIN_DIALOG); + tooltipWindow.pack(); + tooltipWindow.setBounds(p.x, p.y, dims.width, dims.height); + tooltipWindow.show(); + tooltipWindow.validate(); + tooltipWindow.repaint(); + currentTip.revalidate(); + currentTip.repaint(); } - currentTip.setVisible(true); } /** + * 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*2; + + return p; + } + + /** * This method hides the ToolTip. * This is package-private to avoid an accessor method. */ @@ -552,15 +570,11 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener if (parent == null) return; parent.remove(containerPanel); - parent.invalidate(); - parent.validate(); - parent.repaint(); parent = currentTip.getParent(); if (parent == null) return; parent.remove(currentTip); - containerPanel = null; } if (tooltipWindow != null) @@ -569,35 +583,7 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener tooltipWindow.dispose(); tooltipWindow = null; } - } - - /** - * This method returns a point in the LayeredPane where the ToolTip can be - * shown. The point returned (if the ToolTip is to be displayed at the - * preferred dimensions) will always place the ToolTip inside the - * currentComponent if possible. - * - * @param p The last known good point for the mouse. - * @param c The JLayeredPane in the first RootPaneContainer up from the - * currentComponent. - * @param tip The ToolTip to display. - * @param dims The ToolTip preferred dimensions (can be null). - * - * @return A good point to place the ToolTip. - */ - private Point getGoodPoint(Point p, JLayeredPane c, JToolTip tip, - Dimension dims) - { - if (dims == null) - dims = tip.getPreferredSize(); - Rectangle bounds = currentComponent.getBounds(); - if (p.x + dims.width > bounds.width) - p.x = bounds.width - dims.width; - if (p.y + dims.height > bounds.height) - p.y = bounds.height - dims.height; - - p = SwingUtilities.convertPoint(currentComponent, p, c); - return p; + currentTip = null; } /** @@ -622,25 +608,4 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener Component target = SwingUtilities.getDeepestComponentAt(parent, p.x, p.y); return target; } - - /** - * This method returns whether the ToolTip can fit in the first - * RootPaneContainer up from the currentComponent. - * - * @param tip The ToolTip. - * - * @return Whether the ToolTip can fit. - */ - private boolean canToolTipFit(JToolTip tip) - { - JRootPane root = (JRootPane) SwingUtilities.getAncestorOfClass(JRootPane.class, - currentComponent); - if (root == null) - return false; - Dimension pref = tip.getPreferredSize(); - Dimension rootSize = root.getSize(); - if (rootSize.width > pref.width && rootSize.height > pref.height) - return true; - return false; - } } diff --git a/libjava/classpath/javax/swing/TransferHandler.java b/libjava/classpath/javax/swing/TransferHandler.java index 96cb9d42abf..4828fdbfa98 100644 --- a/libjava/classpath/javax/swing/TransferHandler.java +++ b/libjava/classpath/javax/swing/TransferHandler.java @@ -75,30 +75,38 @@ public class TransferHandler implements Serializable } } + /** + * Get the system cliboard. If not available, create and return the VM-local + * clipboard. + * + * @param component a component, used to get the toolkit. + * @return the clipboard + */ private static Clipboard getClipboard(JComponent component) { - SecurityManager sm = System.getSecurityManager(); - - if (sm != null) - { - try - { - sm.checkSystemClipboardAccess(); - - // We may access system clipboard. - return component.getToolkit().getSystemClipboard(); - } - catch (SecurityException e) - { - // We may not access system clipboard. - } - } - - // Create VM-local clipboard if non exists yet. - if (clipboard == null) - clipboard = new Clipboard("Clipboard"); - - return clipboard; + // Avoid throwing exception if the system clipboard access failed + // in the past. + if (clipboard != null) + return clipboard; + else + { + try + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSystemClipboardAccess(); + + // We may access system clipboard. + return component.getToolkit().getSystemClipboard(); + } + catch (Exception e) + { + // We may not access system clipboard. + // Create VM-local clipboard if none exists yet. + clipboard = new Clipboard("Clipboard"); + return clipboard; + } + } } } @@ -162,15 +170,18 @@ public class TransferHandler implements Serializable } public void exportAsDrag (JComponent c, InputEvent e, int action) - { + { + // TODO: Implement this properly } protected void exportDone (JComponent c, Transferable data, int action) { + // TODO: Implement this properly } public void exportToClipboard(JComponent c, Clipboard clip, int action) { + // TODO: Implement this properly } public int getSourceActions (JComponent c) diff --git a/libjava/classpath/javax/swing/UIDefaults.java b/libjava/classpath/javax/swing/UIDefaults.java index ab78ca6442f..f6aee1b944c 100644 --- a/libjava/classpath/javax/swing/UIDefaults.java +++ b/libjava/classpath/javax/swing/UIDefaults.java @@ -577,8 +577,8 @@ public class UIDefaults extends Hashtable * * @param key the key to the requested entry * - * @return the boolean entry for <code>key</code> or null if no such entry - * exists + * @return The boolean entry for <code>key</code> or <code>false</code> if no + * such entry exists. */ public boolean getBoolean(Object key) { @@ -674,9 +674,9 @@ public class UIDefaults extends Hashtable return null; try { - if (loader != null) - return loader.loadClass (className); - return Class.forName (className); + if (loader == null) + loader = ClassLoader.getSystemClassLoader(); + return loader.loadClass (className); } catch (Exception e) { diff --git a/libjava/classpath/javax/swing/UIManager.java b/libjava/classpath/javax/swing/UIManager.java index f4648f1e219..15a78190303 100644 --- a/libjava/classpath/javax/swing/UIManager.java +++ b/libjava/classpath/javax/swing/UIManager.java @@ -146,19 +146,29 @@ public class UIManager implements Serializable LookAndFeel laf = (LookAndFeel) lafClass.newInstance(); setLookAndFeel(laf); } + else + { + setLookAndFeel(new MetalLookAndFeel()); + } } catch (Exception ex) { System.err.println("cannot initialize Look and Feel: " + defaultlaf); - System.err.println("error: " + ex.getMessage()); + System.err.println("error: " + ex.toString()); System.err.println("falling back to Metal Look and Feel"); + try + { + setLookAndFeel(new MetalLookAndFeel()); + } + catch (Exception ex2) + { + throw (Error) new AssertionError("There must be no problem installing" + + " the MetalLookAndFeel.") + .initCause(ex2); + } } - currentLookAndFeel = new MetalLookAndFeel(); - currentLookAndFeel.initialize(); - currentUIDefaults = currentLookAndFeel.getDefaults(); - } - + /** * Creates a new instance of the <code>UIManager</code>. There is no need * to construct an instance of this class, since all methods are static. diff --git a/libjava/classpath/javax/swing/UnsupportedLookAndFeelException.java b/libjava/classpath/javax/swing/UnsupportedLookAndFeelException.java index 5abe45fe117..f99c0ac19f7 100644 --- a/libjava/classpath/javax/swing/UnsupportedLookAndFeelException.java +++ b/libjava/classpath/javax/swing/UnsupportedLookAndFeelException.java @@ -40,8 +40,8 @@ package javax.swing; public class UnsupportedLookAndFeelException extends Exception { - public UnsupportedLookAndFeelException(String a) - { - super(a); - } + public UnsupportedLookAndFeelException(String a) + { + super(a); + } } diff --git a/libjava/classpath/javax/swing/ViewportLayout.java b/libjava/classpath/javax/swing/ViewportLayout.java index 19735839387..884f7cb27a8 100644 --- a/libjava/classpath/javax/swing/ViewportLayout.java +++ b/libjava/classpath/javax/swing/ViewportLayout.java @@ -56,12 +56,17 @@ public class ViewportLayout implements LayoutManager, Serializable public ViewportLayout() { + // Nothing to do here. } + public void addLayoutComponent(String name, Component c) { + // Nothing to do here. } + public void removeLayoutComponent(Component c) { + // Nothing to do here. } public Dimension preferredLayoutSize(Container parent) diff --git a/libjava/classpath/javax/swing/WindowConstants.java b/libjava/classpath/javax/swing/WindowConstants.java index 5e364434484..aaa0cb9a3ab 100644 --- a/libjava/classpath/javax/swing/WindowConstants.java +++ b/libjava/classpath/javax/swing/WindowConstants.java @@ -43,31 +43,26 @@ package javax.swing; * * @author Andrew Selkirk */ -public interface WindowConstants { +public interface WindowConstants +{ + /** + * DO_NOTHING_ON_CLOSE + */ + int DO_NOTHING_ON_CLOSE = 0; - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- + /** + * HIDE_ON_CLOSE + */ + int HIDE_ON_CLOSE = 1; - /** - * DO_NOTHING_ON_CLOSE - */ - int DO_NOTHING_ON_CLOSE = 0; + /** + * DISPOSE_ON_CLOSE + */ + int DISPOSE_ON_CLOSE = 2; - /** - * HIDE_ON_CLOSE - */ - int HIDE_ON_CLOSE = 1; + /** + * EXIT_ON_CLOSE + */ + int EXIT_ON_CLOSE =3; - /** - * DISPOSE_ON_CLOSE - */ - int DISPOSE_ON_CLOSE = 2; - - /** - * EXIT_ON_CLOSE - */ - int EXIT_ON_CLOSE =3; - - -} // WindowConstants +} diff --git a/libjava/classpath/javax/swing/border/AbstractBorder.java b/libjava/classpath/javax/swing/border/AbstractBorder.java index 951debd5207..7cbbcdaa83b 100644 --- a/libjava/classpath/javax/swing/border/AbstractBorder.java +++ b/libjava/classpath/javax/swing/border/AbstractBorder.java @@ -52,20 +52,18 @@ import java.io.Serializable; * @author Sascha Brawer (brawer@dandelis.ch) * @author Ronald Veldema (rveldema@cs.vu.nl) */ -public abstract class AbstractBorder - implements Border, Serializable +public abstract class AbstractBorder implements Border, Serializable { static final long serialVersionUID = -545885975315191844L; - /** * Constructs a new AbstractBorder. */ - public AbstractBorder () + public AbstractBorder() { + // Nothing to do here. } - /** * Performs nothing, because the default implementation provided by * this class is an invisible, zero-width border. Subclasses will @@ -79,17 +77,15 @@ public abstract class AbstractBorder * @param width the width of the available area for painting the border. * @param height the height of the available area for painting the border. */ - public void paintBorder (Component c, Graphics g, - int x, int y, int width, int height) + public void paintBorder(Component c, Graphics g, int x, int y, int width, + int height) { - /* A previous version of Classpath had emitted a warning when - * this method was called. The warning was removed because it is - * perfectly legal for a subclass to not override the paintBorder - * method. An example would be EmptyBorder. - */ + // A previous version of Classpath had emitted a warning when + // this method was called. The warning was removed because it is + // perfectly legal for a subclass to not override the paintBorder + // method. An example would be EmptyBorder. } - /** * Measures the width of this border. * @@ -102,31 +98,29 @@ public abstract class AbstractBorder * * @see #getBorderInsets(java.awt.Component, java.awt.Insets) */ - public Insets getBorderInsets (Component c) + public Insets getBorderInsets(Component c) { - return new Insets (0, 0, 0, 0); + return new Insets(0, 0, 0, 0); } - /** * Determines the insets of this border. The implementation provided * by AbstractButton sets the <code>left</code>, <code>right</code>, * <code>top</code> and <code>bottom</code> fields of the passed * <code>insets</code> parameter to zero. * - * @param c the component whose border is to be measured. + * @param c the component whose border is to be measured * - * @return the same object that was passed for <code>insets</code>. + * @return the same object that was passed for <code>insets</code> * * @see #getBorderInsets(Component) */ - public Insets getBorderInsets (Component c, Insets insets) + public Insets getBorderInsets(Component c, Insets insets) { insets.left = insets.right = insets.top = insets.bottom = 0; return insets; } - /** * Determines whether or not this border is opaque. An opaque border * fills every pixel in its area when painting. Partially @@ -136,12 +130,11 @@ public abstract class AbstractBorder * * @return <code>false</code>. */ - public boolean isBorderOpaque () + public boolean isBorderOpaque() { return false; } - /** * Returns a rectangle that covers the specified area minus this * border. Components that wish to determine an area into which @@ -154,12 +147,11 @@ public abstract class AbstractBorder * @param width the width of the available area for the border. * @param height the height of the available area for the border. */ - public Rectangle getInteriorRectangle (Component c, - int x, int y, int width, int height) + public Rectangle getInteriorRectangle(Component c, int x, int y, int width, + int height) { return getInteriorRectangle (c, this, x, y, width, height); } - /** * Returns a rectangle that covers the specified area minus a @@ -173,8 +165,8 @@ public abstract class AbstractBorder * @param width the width of the available area for the border. * @param height the height of the available area for the border. */ - public static Rectangle getInteriorRectangle (Component c, Border b, - int x, int y, int width, int height) + public static Rectangle getInteriorRectangle(Component c, Border b, int x, + int y, int width, int height) { Insets borderInsets; diff --git a/libjava/classpath/javax/swing/border/BevelBorder.java b/libjava/classpath/javax/swing/border/BevelBorder.java index fcdc1c64675..45b758cae41 100644 --- a/libjava/classpath/javax/swing/border/BevelBorder.java +++ b/libjava/classpath/javax/swing/border/BevelBorder.java @@ -55,8 +55,7 @@ import java.awt.Insets; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public class BevelBorder - extends AbstractBorder +public class BevelBorder extends AbstractBorder { /** * Determined using the <code>serialver</code> tool @@ -508,11 +507,11 @@ public class BevelBorder * Paints a two-pixel bevel in four colors. * * <pre> - * @@@@@@@@@@@@ - * @..........# @ = color a - * @. X# . = color b - * @. X# X = color c - * @.XXXXXXXXX# # = color d + * ++++++++++++ + * +..........# + = color a + * +. X# . = color b + * +. X# X = color c + * +.XXXXXXXXX# # = color d * ############</pre> * * @param g the graphics for painting. diff --git a/libjava/classpath/javax/swing/border/Border.java b/libjava/classpath/javax/swing/border/Border.java index 11bddfe78b3..f4af3fb38bb 100644 --- a/libjava/classpath/javax/swing/border/Border.java +++ b/libjava/classpath/javax/swing/border/Border.java @@ -77,9 +77,8 @@ public interface Border * @param width the width of the available area for painting the border. * @param height the height of the available area for painting the border. */ - void paintBorder(Component c, Graphics g, - int x, int y, int width, int height); - + void paintBorder(Component c, Graphics g, int x, int y, int width, + int height); /** * Measures the width of this border. @@ -92,7 +91,6 @@ public interface Border */ Insets getBorderInsets(Component c); - /** * Determines whether this border fills every pixel in its area * when painting. diff --git a/libjava/classpath/javax/swing/border/CompoundBorder.java b/libjava/classpath/javax/swing/border/CompoundBorder.java index 2130a0e3447..998a9bab3bd 100644 --- a/libjava/classpath/javax/swing/border/CompoundBorder.java +++ b/libjava/classpath/javax/swing/border/CompoundBorder.java @@ -48,8 +48,7 @@ import java.awt.Insets; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public class CompoundBorder - extends AbstractBorder +public class CompoundBorder extends AbstractBorder { /** * Determined using the <code>serialver</code> tool @@ -57,7 +56,6 @@ public class CompoundBorder */ static final long serialVersionUID = 9054540377030555103L; - /** * The inside border, which is painted between the bordered * Component and the outside border. It is valid for @@ -65,7 +63,6 @@ public class CompoundBorder */ protected Border insideBorder; - /** * The outside border, which is painted outside both the * bordered Component and the inside border. It is valid for @@ -73,7 +70,6 @@ public class CompoundBorder */ protected Border outsideBorder; - /** * Constructs a CompoundBorder whose inside and outside borders * are both <code>null</code>. While this does not really make @@ -83,12 +79,11 @@ public class CompoundBorder * * @see EmptyBorder */ - public CompoundBorder () + public CompoundBorder() { this (null, null); } - /** * Constructs a CompoundBorder with the specified inside and * outside borders. @@ -103,13 +98,12 @@ public class CompoundBorder * component. It is acceptable to pass <code>null</code>, in * which case no inside border is painted. */ - public CompoundBorder (Border outsideBorder, Border insideBorder) + public CompoundBorder(Border outsideBorder, Border insideBorder) { this.outsideBorder = outsideBorder; this.insideBorder = insideBorder; } - /** * Determines whether or not this border is opaque. An opaque * border fills every pixel in its area when painting. Partially @@ -119,20 +113,18 @@ public class CompoundBorder * @return <code>true</code> if both the inside and outside borders * are opaque, or <code>false</code> otherwise. */ - public boolean isBorderOpaque () + public boolean isBorderOpaque() { - /* While it would be safe to assume true for the opacity of - * a null border, this behavior would not be according to - * the API specification. Also, it is pathological to have - * null borders anyway. - */ + // While it would be safe to assume true for the opacity of + // a null border, this behavior would not be according to + // the API specification. Also, it is pathological to have + // null borders anyway. if ((insideBorder == null) || (outsideBorder == null)) return false; return insideBorder.isBorderOpaque() && outsideBorder.isBorderOpaque(); } - /** * Paints the compound border by first painting the outside border, @@ -148,9 +140,9 @@ public class CompoundBorder public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { - /* If there is an outside border, paint it and reduce the - * bounding box by its insets. - */ + // If there is an outside border, paint it and reduce the + // bounding box by its insets. + // if (outsideBorder != null) { Insets outsideInsets; @@ -161,9 +153,8 @@ public class CompoundBorder x += outsideInsets.left; y += outsideInsets.top; - /* Reduce width and height by the respective extent of the - * outside border. - */ + // Reduce width and height by the respective extent of the + // outside border. width -= outsideInsets.left + outsideInsets.right; height -= outsideInsets.top + outsideInsets.bottom; } @@ -172,7 +163,6 @@ public class CompoundBorder insideBorder.paintBorder(c, g, x, y, width, height); } - /** * Changes the specified insets to the insets of this border, * which is the sum of the insets of the inside and the outside @@ -192,7 +182,7 @@ public class CompoundBorder else insets.left = insets.right = insets.top = insets.bottom = 0; - /* If there is an outside border, add it to insets. */ + // If there is an outside border, add it to insets. if (outsideBorder != null) { borderInsets = outsideBorder.getBorderInsets(c); @@ -202,7 +192,7 @@ public class CompoundBorder insets.bottom += borderInsets.bottom; } - /* If there is an inside border, add it to insets. */ + // If there is an inside border, add it to insets. if (insideBorder != null) { borderInsets = insideBorder.getBorderInsets(c); @@ -215,35 +205,31 @@ public class CompoundBorder return insets; } - /** * Determines the insets of this border, which is the sum of the * insets of the inside and the outside border. * * @param c the component in the center of this border. */ - public Insets getBorderInsets (Component c) + public Insets getBorderInsets(Component c) { - /* It is not clear why CompoundBorder does not simply inherit - * the implementation from AbstractBorder. However, we want - * to be compatible with the API specification, which overrides - * the getBorderInsets(Component) method. - */ + // It is not clear why CompoundBorder does not simply inherit + // the implementation from AbstractBorder. However, we want + // to be compatible with the API specification, which overrides + // the getBorderInsets(Component) method. return getBorderInsets (c, null); } - /** * Returns the outside border, which is painted outside both the * bordered Component and the inside border. It is valid for the * result to be <code>null</code>. */ - public Border getOutsideBorder () + public Border getOutsideBorder() { return outsideBorder; } - /** * Returns the inside border, which is painted between the bordered * Component and the outside border. It is valid for the result to @@ -254,4 +240,3 @@ public class CompoundBorder return insideBorder; } } - diff --git a/libjava/classpath/javax/swing/border/EmptyBorder.java b/libjava/classpath/javax/swing/border/EmptyBorder.java index 0f3b7b6931c..c8e9c604469 100644 --- a/libjava/classpath/javax/swing/border/EmptyBorder.java +++ b/libjava/classpath/javax/swing/border/EmptyBorder.java @@ -53,8 +53,7 @@ import java.awt.Insets; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public class EmptyBorder - extends AbstractBorder +public class EmptyBorder extends AbstractBorder { /** * Determined using the <code>serialver</code> tool @@ -142,6 +141,7 @@ public class EmptyBorder public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/border/EtchedBorder.java b/libjava/classpath/javax/swing/border/EtchedBorder.java index 0bd76ff0fb6..22882b78cb1 100644 --- a/libjava/classpath/javax/swing/border/EtchedBorder.java +++ b/libjava/classpath/javax/swing/border/EtchedBorder.java @@ -56,8 +56,7 @@ import java.awt.Insets; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public class EtchedBorder - extends AbstractBorder +public class EtchedBorder extends AbstractBorder { /** * Determined using the <code>serialver</code> tool @@ -199,8 +198,8 @@ public class EtchedBorder * @param width the width of the available area for painting the border. * @param height the height of the available area for painting the border. */ - public void paintBorder(Component c, Graphics g, - int x, int y, int width, int height) + public void paintBorder(Component c, Graphics g, int x, int y, int width, + int height) { switch (etchType) { @@ -270,16 +269,14 @@ public class EtchedBorder */ public boolean isBorderOpaque() { - /* If the colors are to be drived from the enclosed Component's - * background color, the border is guaranteed to be fully opaque - * because Color.brighten() and Color.darken() always return an - * opaque color. - */ + // If the colors are to be derived from the enclosed Component's + // background color, the border is guaranteed to be fully opaque + // because Color.brighten() and Color.darken() always return an + // opaque color. return ((highlight == null) || (highlight.getAlpha() == 255)) && ((shadow == null) || (shadow.getAlpha() == 255)); } - /** * Returns the appearance of this EtchedBorder, which is either @@ -310,8 +307,7 @@ public class EtchedBorder else return c.getBackground().brighter(); } - - + /** * Returns the color that will be used for highlighted parts when * painting the border, or <code>null</code> if that color will be @@ -359,11 +355,11 @@ public class EtchedBorder * Paints a two-pixel etching in two colors. * * <pre> - * @@@@@@@@@@@. - * @.........@. @ = color a - * @. @. . = color b - * @. @. - * @@@@@@@@@@@. + * +++++++++++. + * +.........+. + = color a + * +. +. . = color b + * +. +. + * +++++++++++. * ............</pre> * * @param g the graphics for painting. @@ -374,9 +370,8 @@ public class EtchedBorder * @param a one of the two colors. * @param b the second of the two colors. */ - private static void paintEtchedBorder(Graphics g, - int x, int y, int width, int height, - Color a, Color b) + private static void paintEtchedBorder(Graphics g, int x, int y, int width, + int height, Color a, Color b) { Color oldColor; @@ -387,11 +382,10 @@ public class EtchedBorder try { - /* To understand this code, it might be helpful to look at the - * images that are included with the JavaDoc. They are located - * in the "doc-files" subdirectory. EtchedBorder-2.png might - * be especially informative. - */ + // To understand this code, it might be helpful to look at the + // images that are included with the JavaDoc. They are located + // in the "doc-files" subdirectory. EtchedBorder-2.png might + // be especially informative. g.setColor(a); g.drawRect(0, 0, width - 1, height - 1); @@ -408,4 +402,3 @@ public class EtchedBorder } } } - diff --git a/libjava/classpath/javax/swing/border/LineBorder.java b/libjava/classpath/javax/swing/border/LineBorder.java index c34e38c5789..36abddd915d 100644 --- a/libjava/classpath/javax/swing/border/LineBorder.java +++ b/libjava/classpath/javax/swing/border/LineBorder.java @@ -50,8 +50,7 @@ import java.awt.Insets; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public class LineBorder - extends AbstractBorder +public class LineBorder extends AbstractBorder { /** * Determined using the <code>serialver</code> tool @@ -71,7 +70,7 @@ public class LineBorder /** * A shared instance of a gray, one pixel thick, plain LineBorder. * The singleton object is lazily created by {@link - * #createBlackGrayBorder()} upon its first invocation. + * #createGrayLineBorder()} upon its first invocation. */ private static LineBorder grayLineBorder; @@ -213,29 +212,27 @@ public class LineBorder { g.setColor(lineColor); - /* If width and height were not adjusted, the border would - * appear one pixel too large in both directions. - */ + // If width and height were not adjusted, the border would + // appear one pixel too large in both directions. width -= 1; height -= 1; - /* Blurred, too large appearance - * ----------------------------- - * While Java 2D has introduced line strokes of arbitrary width, - * it seems desirable to keep this code independent of Java 2D. - * Therefore, multiple nested rectangles (or rounded rectangles) - * are drawn in order to simulate a line whose thickness is - * greater than one pixel. - * - * This hack causes a blurred appearance when anti-aliasing is - * on. Interestingly enough, though, the Sun JDK 1.3.1 (at least - * on MacOS X 10.1.5) shows exactly the same appearance under - * this condition. It thus seems likely that Sun does the same - * hack for simulating thick lines. For this reason, the - * blurred appearance seems acceptable -- especially since GNU - * Classpath tries to be compatible with the Sun reference - * implementation. - */ + // Blurred, too large appearance + // ----------------------------- + // While Java 2D has introduced line strokes of arbitrary width, + // it seems desirable to keep this code independent of Java 2D. + // Therefore, multiple nested rectangles (or rounded rectangles) + // are drawn in order to simulate a line whose thickness is + // greater than one pixel. + // + // This hack causes a blurred appearance when anti-aliasing is + // on. Interestingly enough, though, the Sun JDK 1.3.1 (at least + // on MacOS X 10.1.5) shows exactly the same appearance under + // this condition. It thus seems likely that Sun does the same + // hack for simulating thick lines. For this reason, the + // blurred appearance seems acceptable -- especially since GNU + // Classpath tries to be compatible with the Sun reference + // implementation. for (int i = 0; i < thickness; i++) { if (roundedCorners) @@ -340,4 +337,3 @@ public class LineBorder return (!roundedCorners) && (lineColor.getAlpha() == 255); } } - diff --git a/libjava/classpath/javax/swing/border/MatteBorder.java b/libjava/classpath/javax/swing/border/MatteBorder.java index f7ff1ca01c3..4d5b8c25360 100644 --- a/libjava/classpath/javax/swing/border/MatteBorder.java +++ b/libjava/classpath/javax/swing/border/MatteBorder.java @@ -54,8 +54,7 @@ import javax.swing.Icon; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public class MatteBorder - extends EmptyBorder +public class MatteBorder extends EmptyBorder { /** * Determined using the <code>serialver</code> tool @@ -401,4 +400,3 @@ public class MatteBorder } } } - diff --git a/libjava/classpath/javax/swing/border/SoftBevelBorder.java b/libjava/classpath/javax/swing/border/SoftBevelBorder.java index 379ecb65a35..028fd00e021 100644 --- a/libjava/classpath/javax/swing/border/SoftBevelBorder.java +++ b/libjava/classpath/javax/swing/border/SoftBevelBorder.java @@ -55,8 +55,7 @@ import java.awt.Insets; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public class SoftBevelBorder - extends BevelBorder +public class SoftBevelBorder extends BevelBorder { /** * Determined using the <code>serialver</code> tool @@ -264,10 +263,10 @@ public class SoftBevelBorder * Paints a soft bevel in four colors. * * <pre> - * @@@@@@@@@@@. - * @@.........# @ = color a - * @.. # . = color b - * @. # X = color c + * +++++++++++. + * ++.........# + = color a + * +.. # . = color b + * +. # X = color c * .. X# # = color d * . ##########</pre> * @@ -326,4 +325,3 @@ public class SoftBevelBorder } } } - diff --git a/libjava/classpath/javax/swing/border/TitledBorder.java b/libjava/classpath/javax/swing/border/TitledBorder.java index ceae2b1c9e3..8d3ee13d4bb 100644 --- a/libjava/classpath/javax/swing/border/TitledBorder.java +++ b/libjava/classpath/javax/swing/border/TitledBorder.java @@ -1,5 +1,5 @@ /* TitledBorder.java -- - Copyright (C) 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -46,6 +46,9 @@ import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.LineMetrics; +import java.awt.geom.AffineTransform; import javax.swing.UIManager; @@ -55,8 +58,7 @@ import javax.swing.UIManager; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public class TitledBorder - extends AbstractBorder +public class TitledBorder extends AbstractBorder { /** * A value for the <code>titlePosition</code> property that vertically @@ -301,7 +303,7 @@ public class TitledBorder public TitledBorder(String title) { this(/* border */ null, - title, DEFAULT_JUSTIFICATION, DEFAULT_POSITION, + title, LEADING, TOP, /* titleFont */ null, /* titleColor */ null); } @@ -314,7 +316,7 @@ public class TitledBorder */ public TitledBorder(Border border) { - this(border, /* title */ "", DEFAULT_JUSTIFICATION, DEFAULT_POSITION, + this(border, /* title */ "", LEADING, TOP, /* titleFont */ null, /* titleColor */ null); } @@ -330,7 +332,7 @@ public class TitledBorder */ public TitledBorder(Border border, String title) { - this(border, title, DEFAULT_JUSTIFICATION, DEFAULT_POSITION, + this(border, title, LEADING, TOP, /* titleFont */ null, /* titleColor */ null); } @@ -504,7 +506,7 @@ public class TitledBorder public void paint(Graphics g) { if (b != null) - b.paintBorder(c, g, x, y, width - 1, height - 1); + b.paintBorder(c, g, x, y, width, height); } @@ -562,7 +564,7 @@ public class TitledBorder if (stripeHeight > 0) { paint(g, x, holeY, holeX - x, stripeHeight); // patches #2 and #3 - paint(g, holeX + holeWidth, holeY, width - (holeX + holeWidth), stripeHeight); + paint(g, holeX + holeWidth, holeY, x + width - (holeX + holeWidth), stripeHeight); } stripeHeight = height - (holeY - y + holeHeight); @@ -574,16 +576,16 @@ public class TitledBorder BorderPainter bp; int textX, textY, borderWidth, borderHeight; - borderWidth = width - (mes.borderSpacing.left + mes.borderSpacing.right); - borderHeight = height - (mes.borderSpacing.top + mes.borderSpacing.bottom); + borderWidth = width - (mes.outerSpacing.left + mes.outerSpacing.right); + borderHeight = height - (mes.outerSpacing.top + mes.outerSpacing.bottom); bp = new BorderPainter(c, getBorder(), - x + mes.borderSpacing.left, y + mes.borderSpacing.top, + x + mes.outerSpacing.left, y + mes.outerSpacing.top, borderWidth, borderHeight); switch (getRealTitleJustification(c)) { case LEFT: - textX = x + TEXT_INSET_H; + textX = x + EDGE_SPACING + TEXT_INSET_H; break; case CENTER: @@ -601,21 +603,22 @@ public class TitledBorder switch (titlePosition) { case ABOVE_TOP: - textY = y; + textY = y + EDGE_SPACING; break; case TOP: case DEFAULT_POSITION: default: - textY = y + mes.borderSpacing.top + mes.borderInsets.top - mes.textAscent; + textY = y + mes.outerSpacing.top + mes.borderInsets.top - mes.textAscent + + mes.lineHeight; break; case BELOW_TOP: - textY = y + mes.borderSpacing.top + mes.borderInsets.top + TEXT_SPACING; + textY = y + mes.outerSpacing.top + mes.borderInsets.top + TEXT_SPACING; break; case ABOVE_BOTTOM: - textY = y + height - mes.borderSpacing.bottom - mes.borderInsets.bottom + textY = y + height - mes.outerSpacing.bottom - mes.borderInsets.bottom - TEXT_SPACING - (mes.textAscent + mes.textDescent); break; @@ -640,8 +643,8 @@ public class TitledBorder g.setFont(oldFont); g.setColor(oldColor); } - bp.paintExcept(g, textX - 2, textY, - mes.textWidth + 2, mes.textAscent + mes.textDescent); + bp.paintExcept(g, textX, textY, + mes.textWidth, mes.textAscent + mes.textDescent); } } @@ -998,39 +1001,63 @@ public class TitledBorder m.trimmedText = null; } - m.textAscent = fmet.getAscent(); - m.textDescent = fmet.getDescent(); if (m.trimmedText != null) - m.textWidth = fmet.stringWidth(m.trimmedText) + 3; + { + m.textAscent = fmet.getAscent(); + m.textDescent = fmet.getDescent() + fmet.getLeading(); + + FontRenderContext frc = new FontRenderContext(new AffineTransform(), + false, false); + LineMetrics lmet = m.font.getLineMetrics(m.trimmedText, 0, + m.trimmedText.length(), frc); + m.lineHeight = (int) lmet.getStrikethroughOffset(); + + // Fallback in case that LineMetrics is not available/working. + if (m.lineHeight == 0) + m.lineHeight = (int) (0.3333 * (double) m.textAscent); + m.textWidth = fmet.stringWidth(m.trimmedText) + 3; + } + else + { + m.textAscent = 0; + m.textDescent = 0; + } - m.edgeSpacing = new Insets(EDGE_SPACING, EDGE_SPACING, EDGE_SPACING, EDGE_SPACING); - m.borderSpacing = new Insets(0, 0, 0, 0); + m.innerSpacing = new Insets(EDGE_SPACING, EDGE_SPACING, EDGE_SPACING, + EDGE_SPACING); + m.outerSpacing = new Insets(EDGE_SPACING, EDGE_SPACING, EDGE_SPACING, + EDGE_SPACING); switch (titlePosition) { case ABOVE_TOP: - m.borderSpacing.top += m.textAscent + m.textDescent + TEXT_SPACING; + m.outerSpacing.top += m.textAscent + m.textDescent + TEXT_SPACING; break; + case TOP: + m.outerSpacing.top += m.textDescent + m.lineHeight; + m.innerSpacing.top += m.textAscent - m.lineHeight; + break; + case BELOW_TOP: - m.edgeSpacing.top += m.textAscent + m.textDescent + TEXT_SPACING; + m.innerSpacing.top += m.textAscent + m.textDescent + TEXT_SPACING; break; case ABOVE_BOTTOM: - m.edgeSpacing.bottom += m.textAscent + m.textDescent + TEXT_SPACING; + m.innerSpacing.bottom += m.textAscent + m.textDescent + TEXT_SPACING; break; case BOTTOM: - m.edgeSpacing.bottom += Math.max(m.textAscent - m.borderInsets.bottom, 0); - m.borderSpacing.bottom += m.textDescent; + m.innerSpacing.bottom += Math.max(m.textAscent - m.lineHeight, 0); + m.outerSpacing.bottom += m.textDescent + m.lineHeight; break; case BELOW_BOTTOM: - m.borderSpacing.bottom += m.textAscent + m.textDescent + TEXT_SPACING; + m.outerSpacing.bottom += m.textAscent + m.textDescent; break; default: - m.borderSpacing.top += m.textAscent; + m.outerSpacing.top += m.textAscent; } return m; @@ -1053,7 +1080,7 @@ public class TitledBorder * which means that the font is to be retrieved from the current * LookAndFeel. In this case, this <code>font</code> field will * contain the result of the retrieval. Therefore, it is safe - * to assume that his <code>font</code> field will never have + * to assume that this <code>font</code> field will never have * a <code>null</code> value. */ Font font; @@ -1072,6 +1099,11 @@ public class TitledBorder */ int textDescent; + /** + * The number of pixels between the base line and the height where + * a strike-through would be drawn. + */ + int lineHeight; /** * The title text after removing leading and trailing white space @@ -1088,7 +1120,7 @@ public class TitledBorder /** - * The border that constitues the interior border + * The border that constitutes the interior border * underneath the title text. */ Border border; @@ -1097,8 +1129,7 @@ public class TitledBorder /** * The distance between the TitledBorder and the interior border. */ - Insets borderSpacing; - + Insets outerSpacing; /** * The width of the interior border, as returned by @@ -1111,7 +1142,7 @@ public class TitledBorder * The distance between the interior border and the nested * Component for which this TitledBorder is a border. */ - Insets edgeSpacing; + Insets innerSpacing; /** @@ -1130,10 +1161,10 @@ public class TitledBorder { if (i == null) i = new Insets(0, 0, 0, 0); - i.left = borderSpacing.left + borderInsets.left + edgeSpacing.left; - i.right = borderSpacing.right + borderInsets.right + edgeSpacing.right; - i.top = borderSpacing.top + borderInsets.top + edgeSpacing.top; - i.bottom = borderSpacing.bottom + borderInsets.bottom + edgeSpacing.bottom; + i.left = outerSpacing.left + borderInsets.left + innerSpacing.left; + i.right = outerSpacing.right + borderInsets.right + innerSpacing.right; + i.top = outerSpacing.top + borderInsets.top + innerSpacing.top; + i.bottom = outerSpacing.bottom + borderInsets.bottom + innerSpacing.bottom; return i; } @@ -1148,7 +1179,8 @@ public class TitledBorder Insets insets; insets = getContentInsets(null); - width = Math.max(insets.left + insets.right, textWidth + 2 * TEXT_INSET_H); + width = Math.max(insets.left + insets.right, textWidth + 2 + * TEXT_INSET_H); return new Dimension(width, insets.top + insets.bottom); } } diff --git a/libjava/classpath/javax/swing/colorchooser/AbstractColorChooserPanel.java b/libjava/classpath/javax/swing/colorchooser/AbstractColorChooserPanel.java index d55346aaf2c..efb527725fa 100644 --- a/libjava/classpath/javax/swing/colorchooser/AbstractColorChooserPanel.java +++ b/libjava/classpath/javax/swing/colorchooser/AbstractColorChooserPanel.java @@ -1,5 +1,5 @@ /* AbstractColorChooserPanel.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -64,7 +64,8 @@ public abstract class AbstractColorChooserPanel extends JPanel */ public AbstractColorChooserPanel() { - } // AbstractColorChooserPanel() + // Nothing to do here. + } /** * This method returns the name displayed in the tab for this chooser panel. @@ -74,6 +75,36 @@ public abstract class AbstractColorChooserPanel extends JPanel public abstract String getDisplayName(); /** + * Returns the key code for the mnemonic for this panel. This method returns + * zero to indicate no mnemonic, subclasses can override this. + * + * @return <code>0</code>, to indicate no mnemonic key code. + * + * @see #getDisplayedMnemonicIndex() + * @since 1.4 + */ + public int getMnemonic() + { + return 0; + } + + /** + * Returns the index of the character in the display name that is the + * mnemonic. This method returns <code>-1</code> to indicate no mnemonic, + * subclasses can override. + * + * @return <code>-1</code>, to indicate no mnemonic. + * + * @see #getDisplayName() + * @see #getMnemonic() + * @since 1.4 + */ + public int getDisplayedMnemonicIndex() + { + return -1; + } + + /** * This method updates the chooser panel when the JColorChooser's color has * changed. */ diff --git a/libjava/classpath/javax/swing/colorchooser/ColorChooserComponentFactory.java b/libjava/classpath/javax/swing/colorchooser/ColorChooserComponentFactory.java index 77e319c70e4..923ea531ffb 100644 --- a/libjava/classpath/javax/swing/colorchooser/ColorChooserComponentFactory.java +++ b/libjava/classpath/javax/swing/colorchooser/ColorChooserComponentFactory.java @@ -53,7 +53,8 @@ public class ColorChooserComponentFactory */ private ColorChooserComponentFactory() { - } // ColorChooserComponentFactory() + // Nothing to do here. + } /** * This method returns the three default chooser panels to be used in diff --git a/libjava/classpath/javax/swing/colorchooser/DefaultSwatchChooserPanel.java b/libjava/classpath/javax/swing/colorchooser/DefaultSwatchChooserPanel.java index f28af4cac7c..ff3436808ec 100644 --- a/libjava/classpath/javax/swing/colorchooser/DefaultSwatchChooserPanel.java +++ b/libjava/classpath/javax/swing/colorchooser/DefaultSwatchChooserPanel.java @@ -587,6 +587,7 @@ class DefaultSwatchChooserPanel extends AbstractColorChooserPanel */ public void addLayoutComponent(String name, Component comp) { + // Nothing to do here. } /** @@ -634,6 +635,7 @@ class DefaultSwatchChooserPanel extends AbstractColorChooserPanel */ public void removeLayoutComponent(Component comp) { + // Nothing to do here. } /** @@ -786,6 +788,7 @@ class DefaultSwatchChooserPanel extends AbstractColorChooserPanel */ public void updateChooser() { + // Nothing to do here yet. } /** diff --git a/libjava/classpath/javax/swing/event/EventListenerList.java b/libjava/classpath/javax/swing/event/EventListenerList.java index 3b9f4c807de..ee3f220f4d5 100644 --- a/libjava/classpath/javax/swing/event/EventListenerList.java +++ b/libjava/classpath/javax/swing/event/EventListenerList.java @@ -119,6 +119,7 @@ public class EventListenerList */ public EventListenerList() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/event/InternalFrameAdapter.java b/libjava/classpath/javax/swing/event/InternalFrameAdapter.java index a2878e76e79..dfa88c3d4fd 100644 --- a/libjava/classpath/javax/swing/event/InternalFrameAdapter.java +++ b/libjava/classpath/javax/swing/event/InternalFrameAdapter.java @@ -39,75 +39,88 @@ exception statement from your version. */ package javax.swing.event; /** - * InternalFrameAdapter + * InternalFrameAdapter. + * * @author Andrew Selkirk */ public abstract class InternalFrameAdapter implements InternalFrameListener { - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * InternalFrameAdapter constructor - */ - public InternalFrameAdapter() { - } // InternalFrameAdapter() - - - //------------------------------------------------------------- - // Interface: InternalFrameListener --------------------------- - //------------------------------------------------------------- + /** + * InternalFrameAdapter constructor. + */ + public InternalFrameAdapter() + { + // Nothing to do here. + } - /** - * Internal frame activated - * @param event Internal frame event - */ - public void internalFrameActivated(InternalFrameEvent event) { - } // internalFrameActivated() - - /** - * Internal frame closed - * @param event Internal frame event - */ - public void internalFrameClosed(InternalFrameEvent event) { - } // internalFrameClosed() - - /** - * Internal frame closing - * @param event Internal frame event - */ - public void internalFrameClosing(InternalFrameEvent event) { - } // internalFrameClosing() - - /** - * Internal frame deactivated - * @param event Internal frame event - */ - public void internalFrameDeactivated(InternalFrameEvent event) { - } // internalFrameDeactivated() - - /** - * Internal frame deiconified - * @param event Internal frame event - */ - public void internalFrameDeiconified(InternalFrameEvent event) { - } // internalFrameDeiconified() - - /** - * Internal frame iconified - * @param event Internal frame event - */ - public void internalFrameIconified(InternalFrameEvent event) { - } // internalFrameIconified() - - /** - * Internal frame opened - * @param event Internal frame event - */ - public void internalFrameOpened(InternalFrameEvent event) { - } // internalFrameOpened() - - -} // InternalFrameAdapter + /** + * Internal frame activated. + * + * @param event internal frame event + */ + public void internalFrameActivated(InternalFrameEvent event) + { + // Nothing to do here. + } + + /** + * Internal frame closed. + * + * @param event internal frame event + */ + public void internalFrameClosed(InternalFrameEvent event) + { + // Nothing to do here. + } + + /** + * Internal frame closing. + * + * @param event internal frame event + */ + public void internalFrameClosing(InternalFrameEvent event) + { + // Nothing to do here. + } + + /** + * Internal frame deactivated. + * + * @param event internal frame event + */ + public void internalFrameDeactivated(InternalFrameEvent event) + { + // Nothing to do here. + } + + /** + * Internal frame deiconified. + * + * @param event internal frame event + */ + public void internalFrameDeiconified(InternalFrameEvent event) + { + // Nothing to do here. + } + + /** + * Internal frame iconified. + * + * @param event internal frame event + */ + public void internalFrameIconified(InternalFrameEvent event) + { + // Nothing to do here. + } + + /** + * Internal frame opened. + * + * @param event internal frame event + */ + public void internalFrameOpened(InternalFrameEvent event) + { + // Nothing to do here. + } + +} diff --git a/libjava/classpath/javax/swing/event/ListDataListener.java b/libjava/classpath/javax/swing/event/ListDataListener.java index 7ce17d86fa6..f42777d09d6 100644 --- a/libjava/classpath/javax/swing/event/ListDataListener.java +++ b/libjava/classpath/javax/swing/event/ListDataListener.java @@ -1,5 +1,5 @@ /* ListDataListener.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,33 +37,46 @@ exception statement from your version. */ package javax.swing.event; -// Imports import java.util.EventListener; +import javax.swing.ListModel; + /** - * ListDataListener public interface + * A <code>ListDataListener</code> can register with a {@link ListModel} and + * receive notification of updates to the model. + * * @author Andrew Selkirk * @author Ronald Veldema */ -public interface ListDataListener extends EventListener { - - /** - * Contents Changed - * @param event ListDataEvent Event - */ - void contentsChanged(ListDataEvent event); +public interface ListDataListener extends EventListener +{ - /** - * Interval Added - * @param event ListDataEvent Event - */ - void intervalAdded(ListDataEvent event); + /** + * Notifies the listener that the contents of the list have changed + * in some way. This method will be called if the change cannot be + * notified via the {@link #intervalAdded(ListDataEvent)} or the + * {@link #intervalRemoved(ListDataEvent)} methods. + * + * @param event the event. + */ + void contentsChanged(ListDataEvent event); - /** - * Interval Removed - * @param event ListDataEvent Event - */ - void intervalRemoved(ListDataEvent event); + /** + * Notifies the listener that one or more items have been added to the + * list. The <code>event</code> argument can supply the indices for the + * range of items added. + * + * @param event the event. + */ + void intervalAdded(ListDataEvent event); + /** + * Notifies the listener that one or more items have been removed from + * the list. The <code>event</code> argument can supply the indices for + * the range of items removed. + * + * @param event the event. + */ + void intervalRemoved(ListDataEvent event); -} // ListDataListener +} diff --git a/libjava/classpath/javax/swing/event/MouseInputListener.java b/libjava/classpath/javax/swing/event/MouseInputListener.java index 3c3ca2347fe..3d879b9e746 100644 --- a/libjava/classpath/javax/swing/event/MouseInputListener.java +++ b/libjava/classpath/javax/swing/event/MouseInputListener.java @@ -37,16 +37,17 @@ exception statement from your version. */ package javax.swing.event; -// Imports import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; /** - * MouseInputListener public interface + * MouseInputListener public interface. + * * @author Andrew Selkirk */ public interface MouseInputListener extends MouseListener, - MouseMotionListener { - -} // MouseInputListener - + MouseMotionListener +{ + // This interface only pulls together MouseListener and MouseMotionListener + // without adding any methods on its own. +} diff --git a/libjava/classpath/javax/swing/event/SwingPropertyChangeSupport.java b/libjava/classpath/javax/swing/event/SwingPropertyChangeSupport.java index 408ca957e95..7e8ff0dc2e9 100644 --- a/libjava/classpath/javax/swing/event/SwingPropertyChangeSupport.java +++ b/libjava/classpath/javax/swing/event/SwingPropertyChangeSupport.java @@ -289,10 +289,9 @@ public final class SwingPropertyChangeSupport int index; PropertyChangeListener listener; - // Check Values if they are equal - if (event.getOldValue() == null && event.getNewValue() == null || - (event.getOldValue() != null && event.getNewValue() != null && - event.getOldValue().equals(event.getNewValue()))) + // if the old and new values are non-null and equal, don't notify listeners + if (event.getOldValue() != null && event.getNewValue() != null && + event.getOldValue().equals(event.getNewValue())) return; // Process Main Listener List diff --git a/libjava/classpath/javax/swing/event/TreeModelEvent.java b/libjava/classpath/javax/swing/event/TreeModelEvent.java index a217e3b4053..8fa28a7eadb 100644 --- a/libjava/classpath/javax/swing/event/TreeModelEvent.java +++ b/libjava/classpath/javax/swing/event/TreeModelEvent.java @@ -55,12 +55,12 @@ public class TreeModelEvent extends EventObject { /** * childIndices */ - protected int[] childIndices = new int[0]; + protected int[] childIndices = null; /** * children */ - protected Object[] children = new Object[0]; + protected Object[] children = null; /** * path @@ -164,7 +164,9 @@ public class TreeModelEvent extends EventObject { * @returns String representation */ public String toString() { - return null; // TODO + return getClass() + " [Source: " + getSource() + ", TreePath: " + getTreePath() + + ", Child Indicies: " + getChildIndices() + ", Children: " + getChildren() + + ", Path: " + getPath() +"]"; } // toString() diff --git a/libjava/classpath/javax/swing/filechooser/FileFilter.java b/libjava/classpath/javax/swing/filechooser/FileFilter.java index 42770d98186..ecfa54b5814 100644 --- a/libjava/classpath/javax/swing/filechooser/FileFilter.java +++ b/libjava/classpath/javax/swing/filechooser/FileFilter.java @@ -1,5 +1,5 @@ /* FileFilter.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,41 +40,46 @@ package javax.swing.filechooser; import java.io.File; +import javax.swing.JFileChooser; + /** - * FileFilter + * The base class for filters that control the visibility of files in the + * {@link JFileChooser} component. + * + * @see JFileChooser#addChoosableFileFilter(FileFilter) + * * @author Andrew Selkirk - * @version 1.0 */ -public abstract class FileFilter { - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor FileFilter - */ - public FileFilter() { - // TODO - } // FileFilter() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * accept - * @param file TODO - * @returns boolean - */ - public abstract boolean accept(File file); - - /** - * getDescription - * @returns String - */ - public abstract String getDescription(); - - -} // FileFilter +public abstract class FileFilter +{ + + /** + * Default constructor. + */ + public FileFilter() + { + // Nothing to do here. + } + + /** + * Returns <code>true</code> if the specified file matches the filter, and + * <code>false</code> otherwise. + * + * @param file the file. + * + * @returns A boolean. + */ + public abstract boolean accept(File file); + + /** + * Returns a description of the files that will be selected by the filter + * (for example, "Java source files"). This description will usually be + * displayed on the {@link JFileChooser} component, often in a combo box that + * is used to select the appropriate filter (in cases where more than one + * filter is available). + * + * @returns A description of the filter. + */ + public abstract String getDescription(); + +} diff --git a/libjava/classpath/javax/swing/filechooser/FileSystemView.java b/libjava/classpath/javax/swing/filechooser/FileSystemView.java index ffa41ca07de..f51b745c892 100644 --- a/libjava/classpath/javax/swing/filechooser/FileSystemView.java +++ b/libjava/classpath/javax/swing/filechooser/FileSystemView.java @@ -40,21 +40,27 @@ package javax.swing.filechooser; import java.io.File; import java.io.IOException; import java.util.ArrayList; + import javax.swing.Icon; +import javax.swing.JFileChooser; /** - * DOCUMENT ME! + * The base class providing a view of the file system for use by the + * {@link JFileChooser} component. */ public abstract class FileSystemView { + /** The instance returned by {@link #getFileSystemView()}. */ + private static FileSystemView defaultFileSystemView; + /** - * DOCUMENT ME! + * Creates a new file object with the given name in the specified directory. * - * @param dir DOCUMENT ME! - * @param filename DOCUMENT ME! + * @param dir the directory (<code>null</code> permitted). + * @param filename the file name. * - * @return DOCUMENT ME! + * @return A new file object. */ public File createFileObject(File dir, String filename) { @@ -62,11 +68,11 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Creates a new file object from the specified path. * - * @param path DOCUMENT ME! + * @param path the path. * - * @return DOCUMENT ME! + * @return A new file object. */ public File createFileObject(String path) { @@ -89,13 +95,16 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Creates a new folder with a unique name in the specified directory and + * returns a {@link File} object representing the new directory. * - * @param containingDir DOCUMENT ME! + * @param containingDir the directory to contain the new folder + * (<code>null</code> not permitted). * - * @return DOCUMENT ME! + * @return A {@link File} object representing the new directory. * - * @throws IOException DOCUMENT ME! + * @throws IOException if an exception occurs while creating the new + * directory. */ public abstract File createNewFolder(File containingDir) throws IOException; @@ -115,9 +124,9 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns the default directory. * - * @return DOCUMENT ME! + * @return The default directory. */ public File getDefaultDirectory() { @@ -125,12 +134,16 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns an array containing the files in the given directory. The + * <code>useFileHiding</code> controls whether or not hidden files are + * included in the result. * - * @param dir DOCUMENT ME! - * @param useFileHiding DOCUMENT ME! + * @param dir the directory (if <code>null</code> + * @param useFileHiding a flag that controls whether or not hidden files are + * included in the result (pass in <code>true</code> to + * exclude hidden files). * - * @return DOCUMENT ME! + * @return The files in the given directory (possibly <code>null</code>). */ public File[] getFiles(File dir, boolean useFileHiding) { @@ -143,31 +156,34 @@ public abstract class FileSystemView for (int i = 0; i < files.length; i++) if (! files[i].isHidden()) trim.add(files[i]); - File[] value = (File[]) trim.toArray(new File[0]); + File[] value = (File[]) trim.toArray(new File[trim.size()]); return value; } /** - * DOCUMENT ME! + * Returns a default {@link FileSystemView} appropriate for the platform. * - * @return DOCUMENT ME! + * @return A default {@link FileSystemView} appropriate for the platform. */ public static FileSystemView getFileSystemView() { - if (File.separator.equals("/")) - return new UnixFileSystemView(); - - // else if (File.Separator.equals("\")) - // return new Win32FileSystemView(); - // else - // return new GenericFileSystemView(); - return null; + if (defaultFileSystemView == null) + { + if (File.separator.equals("/")) + defaultFileSystemView = new UnixFileSystemView(); + // FIXME: need to implement additional views + // else if (File.Separator.equals("\")) + // return new Win32FileSystemView(); + // else + // return new GenericFileSystemView(); + } + return defaultFileSystemView; } /** - * DOCUMENT ME! + * Returns the home directory for the current user. * - * @return DOCUMENT ME! + * @return The home directory for the current user. */ public File getHomeDirectory() { @@ -175,11 +191,12 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns the parent directory for the given file/directory. * - * @param f DOCUMENT ME! + * @param f the file/directory. * - * @return DOCUMENT ME! + * @return The parent directory (or <code>null</code> if there is no parent + * directory). */ public File getParentDirectory(File f) { @@ -189,9 +206,14 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! - * - * @return DOCUMENT ME! + * Returns an array containing the file system roots. On Unix-like platforms, + * this array will contain just a single item ("/"), while other platforms + * may return multiple roots. + * <p> + * This method is implemented to return <code>null</code>, subclasses must + * override this method. + * + * @return An array containing the file system roots. */ public File[] getRoots() { @@ -200,11 +222,13 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns the name of a file as it would be displayed by the underlying + * system. This implementation returns <code>null</code>, subclasses must + * override. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return <code>null</code>. */ public String getSystemDisplayName(File f) { @@ -212,11 +236,13 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns the icon that would be displayed for the given file by the + * underlying system. This implementation returns <code>null</code>, + * subclasses must override. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return <code>null</code>. */ public Icon getSystemIcon(File f) { @@ -224,11 +250,13 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns the type description of a file that would be displayed by the + * underlying system. This implementation returns <code>null</code>, + * subclasses must override. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return <code>null</code>. */ public String getSystemTypeDescription(File f) { @@ -248,11 +276,13 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns <code>true</code> if the given directory represents a disk + * drive, and <code>false</code> otherwise. This default implementation + * always returns <code>false</code>. * - * @param dir DOCUMENT ME! + * @param dir the directory. * - * @return DOCUMENT ME! + * @return <code>false</code>. */ public boolean isDrive(File dir) { @@ -260,11 +290,13 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns <code>true</code> if <code>f</code> is a file or directory, and + * <code>false</code> otherwise. * - * @param f DOCUMENT ME! + * @param f the file/directory. * - * @return DOCUMENT ME! + * @return <code>true</code> if <code>f</code> is a file or directory, and + * <code>false</code> otherwise. */ public boolean isFileSystem(File f) { @@ -272,11 +304,13 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns <code>true</code> if the given directory is a file system root, + * and <code>false</code> otherwise. * - * @param dir DOCUMENT ME! + * @param dir the directory. * - * @return DOCUMENT ME! + * @return <code>true</code> if the given directory is a file system root, + * and <code>false</code> otherwise. */ public boolean isFileSystemRoot(File dir) { @@ -291,11 +325,13 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns <code>true</code> if the given directory represents a floppy + * drive, and <code>false</code> otherwise. This default implementation + * always returns <code>false</code>. * - * @param dir DOCUMENT ME! + * @param dir the directory. * - * @return DOCUMENT ME! + * @return <code>false</code>. */ public boolean isFloppyDrive(File dir) { @@ -303,11 +339,13 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns <code>true</code> if the given file is hidden, and + * <code>false</code> otherwise. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return <code>true</code> if the given file is hidden, and + * <code>false</code> otherwise. */ public boolean isHiddenFile(File f) { @@ -315,12 +353,14 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns <code>true</code> if <code>folder</code> is the parent of + * <code>file</code>, and <code>false</code> otherwise. * - * @param folder DOCUMENT ME! - * @param file DOCUMENT ME! + * @param folder the folder (<code>null</code> not permitted). + * @param file the file (<code>null</code> not permitted). * - * @return DOCUMENT ME! + * @return <code>true</code> if <code>folder</code> is the parent of + * <code>file</code>, and <code>false</code> otherwise. */ public boolean isParent(File folder, File file) { @@ -344,11 +384,14 @@ public abstract class FileSystemView } /** - * DOCUMENT ME! + * Returns <code>true</code> if the file is traversable, and + * <code>false</code> otherwise. Here, all directories are considered + * traversable, and files are considered non-traversable. * - * @param f DOCUMENT ME! + * @param f the file or directory (<code>null</code> not permitted). * - * @return DOCUMENT ME! + * @return <code>true</code> if the file is traversable, and + * <code>false</code> otherwise. */ public Boolean isTraversable(File f) { @@ -356,6 +399,6 @@ public abstract class FileSystemView // traversable. (No files are listed when you traverse the directory) // My best guess is that as long as it's a directory, the file is // traversable. - return new Boolean(f.isDirectory()); + return Boolean.valueOf(f.isDirectory()); } } diff --git a/libjava/classpath/javax/swing/filechooser/FileView.java b/libjava/classpath/javax/swing/filechooser/FileView.java index c431fd46127..ea1989f9353 100644 --- a/libjava/classpath/javax/swing/filechooser/FileView.java +++ b/libjava/classpath/javax/swing/filechooser/FileView.java @@ -1,5 +1,5 @@ /* FileView.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -43,72 +43,86 @@ import java.io.File; import javax.swing.Icon; /** - * FileView - * @author Andrew Selkirk - * @version 1.0 + * An abstract class that provides presentation information about files and + * directories. . + * + * @author Andrew Selkirk */ -public abstract class FileView { - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor FileView - */ - public FileView() { - // TODO - } // FileView() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * getName - * @param file TODO - * @returns String - */ - public String getName(File file) { - return null; // TODO - } // getName() - - /** - * getDescription - * @param value0 TODO - * @returns String - */ - public String getDescription(File value0) { - return null; // TODO - } // getDescription() - - /** - * getTypeDescription - * @param value0 TODO - * @returns String - */ - public String getTypeDescription(File value0) { - return null; // TODO - } // getTypeDescription() - - /** - * getIcon - * @param value0 TODO - * @returns Icon - */ - public Icon getIcon(File value0) { - return null; // TODO - } // getIcon() - - /** - * isTraversable - * @param value0 TODO - * @returns Boolean - */ - public Boolean isTraversable(File value0) { - return null; // TODO - } // isTraversable() - - -} // FileView +public abstract class FileView +{ + + /** + * Creates a new <code>FileView</code> instance. + */ + public FileView() + { + // Nothing to do here. + } + + /** + * Returns the name for the specified file. This method always returns + * <code>null</code> and should be overridden by subclasses. + * + * @param file the file. + * + * @return Always <code>null</code>. + */ + public String getName(File file) + { + return null; + } + + /** + * Returns a description for the specified file. This method always returns + * <code>null</code> and should be overridden by subclasses. + * + * @param file the file. + * + * @return Always <code>null</code>. + */ + public String getDescription(File file) + { + return null; + } + + /** + * Returns a description for the type of the specified file. This method + * always returns <code>null</code> and should be overridden by subclasses. + * + * @param file the file. + * + * @return Always <code>null</code>. + */ + public String getTypeDescription(File file) + { + return null; + } + + /** + * Returns an {@link Icon} to represent the specified file. This method + * always returns <code>null</code> and should be overridden by subclasses. + * + * @param file the file. + * + * @return Always <code>null</code>. + */ + public Icon getIcon(File file) + { + return null; + } + + /** + * Returns {@link Boolean#TRUE} if the given directory is traversable, and + * {@link Boolean#FALSE} if it is not. This method always returns + * <code>null</code> and should be overridden by subclasses. + * + * @param directory the directory. + * + * @returns Always <code>null</code>. + */ + public Boolean isTraversable(File directory) + { + return null; + } + +} diff --git a/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java b/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java index f2360ec52f8..c2f65965e09 100644 --- a/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java +++ b/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java @@ -43,21 +43,31 @@ import javax.swing.Icon; /** - * DOCUMENT ME! + * A concrete implementation of {@link FileSystemView} that is appropriate for + * Unix-like systems. + * + * @see FileSystemView#getFileSystemView() */ class UnixFileSystemView extends FileSystemView { - /** DOCUMENT ME! */ + /** The default name for new folders. */ private static final String NEW_FOLDER_NAME = "NewFolder"; /** - * DOCUMENT ME! + * Creates a new folder with a unique name in the specified directory and + * returns a {@link File} object representing the new directory. The name + * of the new folder is <code>NewFolder</code> or, if a directory or file + * with that name already exists, <code>NewFolder.n</code> where + * <code>n</code> is the lowest integer greater than zero that results in + * a unique directory name. * - * @param containingDir DOCUMENT ME! + * @param containingDir the directory to contain the new folder + * (<code>null</code> not permitted). * - * @return DOCUMENT ME! + * @return A {@link File} object representing the new directory. * - * @throws IOException DOCUMENT ME! + * @throws IOException if an exception occurs while creating the new + * directory. */ public File createNewFolder(File containingDir) throws IOException { @@ -82,9 +92,9 @@ class UnixFileSystemView extends FileSystemView } /** - * DOCUMENT ME! + * Returns an array containing the file system root. * - * @return DOCUMENT ME! + * @return An array containing the file system root. */ public File[] getRoots() { @@ -92,11 +102,12 @@ class UnixFileSystemView extends FileSystemView } /** - * DOCUMENT ME! + * Returns the name of a file as it would be displayed by the underlying + * system. This method is NOT YET IMPLEMENTED. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return <code>null</code>. */ public String getSystemDisplayName(File f) { @@ -105,11 +116,12 @@ class UnixFileSystemView extends FileSystemView } /** - * DOCUMENT ME! + * Returns the icon that would be displayed for the given file by the + * underlying system. This method is NOT YET IMPLEMENTED. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return <code>null</code>. */ public Icon getSystemIcon(File f) { @@ -118,11 +130,12 @@ class UnixFileSystemView extends FileSystemView } /** - * DOCUMENT ME! + * Returns the description of a file that would be displayed by the + * underlying system. This method is NOT YET IMPLEMENTED. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return <code>null</code>. */ public String getSystemTypeDescription(File f) { diff --git a/libjava/classpath/javax/swing/plaf/ActionMapUIResource.java b/libjava/classpath/javax/swing/plaf/ActionMapUIResource.java index f6af0880ddb..07292fe24a4 100644 --- a/libjava/classpath/javax/swing/plaf/ActionMapUIResource.java +++ b/libjava/classpath/javax/swing/plaf/ActionMapUIResource.java @@ -50,9 +50,7 @@ import javax.swing.ActionMap; * @author Andrew Selkirk * @author Sascha Brawer (brawer@dandelis.ch) */ -public class ActionMapUIResource - extends ActionMap - implements UIResource +public class ActionMapUIResource extends ActionMap implements UIResource { /** * Constructs a new ActionMapUIResource. diff --git a/libjava/classpath/javax/swing/plaf/BorderUIResource.java b/libjava/classpath/javax/swing/plaf/BorderUIResource.java index 4402bbb48b6..317cb09ac57 100644 --- a/libjava/classpath/javax/swing/plaf/BorderUIResource.java +++ b/libjava/classpath/javax/swing/plaf/BorderUIResource.java @@ -71,9 +71,7 @@ import javax.swing.border.TitledBorder; * @author Brian Jones (cbj@gnu.org) * @author Sascha Brawer (brawer@dandelis.ch) */ -public class BorderUIResource - extends Object - implements Border, UIResource, Serializable +public class BorderUIResource implements Border, UIResource, Serializable { /** * Verified using the <code>serialver</code> tool diff --git a/libjava/classpath/javax/swing/plaf/ButtonUI.java b/libjava/classpath/javax/swing/plaf/ButtonUI.java index 197299e0c95..6910e42989d 100644 --- a/libjava/classpath/javax/swing/plaf/ButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/ButtonUI.java @@ -46,7 +46,7 @@ package javax.swing.plaf; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ButtonUI - extends ComponentUI +public abstract class ButtonUI extends ComponentUI { + // This abstract class does not define any methods of its own. } diff --git a/libjava/classpath/javax/swing/plaf/ColorChooserUI.java b/libjava/classpath/javax/swing/plaf/ColorChooserUI.java index 68ffd916d21..16091416a32 100644 --- a/libjava/classpath/javax/swing/plaf/ColorChooserUI.java +++ b/libjava/classpath/javax/swing/plaf/ColorChooserUI.java @@ -46,8 +46,7 @@ package javax.swing.plaf; * @author Andrew Selkirk * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ColorChooserUI - extends ComponentUI +public abstract class ColorChooserUI extends ComponentUI { /** * Constructs a ColorChooserUI. diff --git a/libjava/classpath/javax/swing/plaf/ColorUIResource.java b/libjava/classpath/javax/swing/plaf/ColorUIResource.java index 33b1676e0fd..36e10f2d186 100644 --- a/libjava/classpath/javax/swing/plaf/ColorUIResource.java +++ b/libjava/classpath/javax/swing/plaf/ColorUIResource.java @@ -50,9 +50,7 @@ import java.awt.Color; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public class ColorUIResource - extends Color - implements UIResource +public class ColorUIResource extends Color implements UIResource { /** * Constructs a <code>ColorUIResource</code> using the specified diff --git a/libjava/classpath/javax/swing/plaf/ComboBoxUI.java b/libjava/classpath/javax/swing/plaf/ComboBoxUI.java index 9498a48153a..3e81ed75a6b 100644 --- a/libjava/classpath/javax/swing/plaf/ComboBoxUI.java +++ b/libjava/classpath/javax/swing/plaf/ComboBoxUI.java @@ -48,14 +48,14 @@ import javax.swing.JComboBox; * @author Andrew Selkirk * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ComboBoxUI - extends ComponentUI +public abstract class ComboBoxUI extends ComponentUI { /** * Constructs a new <code>ComboBoxUI</code>. */ public ComboBoxUI() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/plaf/ComponentInputMapUIResource.java b/libjava/classpath/javax/swing/plaf/ComponentInputMapUIResource.java index e1418710fb2..cfc43e4fc78 100644 --- a/libjava/classpath/javax/swing/plaf/ComponentInputMapUIResource.java +++ b/libjava/classpath/javax/swing/plaf/ComponentInputMapUIResource.java @@ -52,8 +52,7 @@ import javax.swing.JComponent; * @author Andrew Selkirk * @author Sascha Brawer (brawer@dandelis.ch) */ -public class ComponentInputMapUIResource - extends ComponentInputMap +public class ComponentInputMapUIResource extends ComponentInputMap implements UIResource { /** diff --git a/libjava/classpath/javax/swing/plaf/ComponentUI.java b/libjava/classpath/javax/swing/plaf/ComponentUI.java index 0e7680542f7..6a736f258a2 100644 --- a/libjava/classpath/javax/swing/plaf/ComponentUI.java +++ b/libjava/classpath/javax/swing/plaf/ComponentUI.java @@ -38,8 +38,10 @@ exception statement from your version. */ package javax.swing.plaf; +import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; +import java.awt.Rectangle; import javax.accessibility.Accessible; import javax.swing.JComponent; @@ -86,6 +88,7 @@ public abstract class ComponentUI */ public ComponentUI() { + // Nothing to do here. } @@ -157,6 +160,8 @@ public abstract class ComponentUI */ public void paint(Graphics g, JComponent c) { + // Nothing is done here. This method is meant to be overridden by + // subclasses. } @@ -181,13 +186,14 @@ public abstract class ComponentUI { if (c.isOpaque()) { + Color oldColor = g.getColor(); g.setColor(c.getBackground()); g.fillRect(0, 0, c.getWidth(), c.getHeight()); + g.setColor(oldColor); } paint(g, c); } - - + /** * Determines the preferred size of a component. The default * implementation returns <code>null</code>, which means that diff --git a/libjava/classpath/javax/swing/plaf/DesktopIconUI.java b/libjava/classpath/javax/swing/plaf/DesktopIconUI.java index 2e44088cadd..676233ec2cf 100644 --- a/libjava/classpath/javax/swing/plaf/DesktopIconUI.java +++ b/libjava/classpath/javax/swing/plaf/DesktopIconUI.java @@ -44,13 +44,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class DesktopIconUI - extends ComponentUI +public abstract class DesktopIconUI extends ComponentUI { /** * Constructs a new <code>DesktopIconUI</code>. */ public DesktopIconUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/DesktopPaneUI.java b/libjava/classpath/javax/swing/plaf/DesktopPaneUI.java index de553eaf4de..3d4cfc830f5 100644 --- a/libjava/classpath/javax/swing/plaf/DesktopPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/DesktopPaneUI.java @@ -46,14 +46,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class DesktopPaneUI - extends ComponentUI +public abstract class DesktopPaneUI extends ComponentUI { /** * Constructs a new <code>DesktopPaneUI</code>. */ public DesktopPaneUI() { + // Nothing to do here. } } - diff --git a/libjava/classpath/javax/swing/plaf/DimensionUIResource.java b/libjava/classpath/javax/swing/plaf/DimensionUIResource.java index 63c6838c4d1..618c220369e 100644 --- a/libjava/classpath/javax/swing/plaf/DimensionUIResource.java +++ b/libjava/classpath/javax/swing/plaf/DimensionUIResource.java @@ -51,9 +51,7 @@ import java.awt.Dimension; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public class DimensionUIResource - extends Dimension - implements UIResource +public class DimensionUIResource extends Dimension implements UIResource { /** * Constructs a new DimensionUIResource, given its width and height. diff --git a/libjava/classpath/javax/swing/plaf/FileChooserUI.java b/libjava/classpath/javax/swing/plaf/FileChooserUI.java index 8b661e399a5..e9be8f2ba49 100644 --- a/libjava/classpath/javax/swing/plaf/FileChooserUI.java +++ b/libjava/classpath/javax/swing/plaf/FileChooserUI.java @@ -53,14 +53,14 @@ import javax.swing.filechooser.FileView; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class FileChooserUI - extends ComponentUI +public abstract class FileChooserUI extends ComponentUI { /** * Constructs a new <code>FileChooserUI</code>. */ public FileChooserUI() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/plaf/FontUIResource.java b/libjava/classpath/javax/swing/plaf/FontUIResource.java index 1c1731048e8..c54f987fdb7 100644 --- a/libjava/classpath/javax/swing/plaf/FontUIResource.java +++ b/libjava/classpath/javax/swing/plaf/FontUIResource.java @@ -50,9 +50,7 @@ import java.awt.Font; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public class FontUIResource - extends Font - implements UIResource +public class FontUIResource extends Font implements UIResource { /** * Constructs a new <code>FontUIResource</code> given diff --git a/libjava/classpath/javax/swing/plaf/IconUIResource.java b/libjava/classpath/javax/swing/plaf/IconUIResource.java index 1b09ed31f39..659c8e7bab7 100644 --- a/libjava/classpath/javax/swing/plaf/IconUIResource.java +++ b/libjava/classpath/javax/swing/plaf/IconUIResource.java @@ -53,8 +53,7 @@ import javax.swing.Icon; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public class IconUIResource - implements Icon, UIResource, Serializable +public class IconUIResource implements Icon, UIResource, Serializable { /** * Verified using the <code>serialver</code> tool of Sun JDK 1.4.1_01 diff --git a/libjava/classpath/javax/swing/plaf/InputMapUIResource.java b/libjava/classpath/javax/swing/plaf/InputMapUIResource.java index ae032e51fa8..0c5f6f9e97e 100644 --- a/libjava/classpath/javax/swing/plaf/InputMapUIResource.java +++ b/libjava/classpath/javax/swing/plaf/InputMapUIResource.java @@ -49,15 +49,13 @@ import javax.swing.InputMap; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public class InputMapUIResource - extends InputMap - implements UIResource +public class InputMapUIResource extends InputMap implements UIResource { /** * Constructs a new <code>InputMapUIResource</code>. */ public InputMapUIResource() { + // Nothing to do here. } } - diff --git a/libjava/classpath/javax/swing/plaf/InsetsUIResource.java b/libjava/classpath/javax/swing/plaf/InsetsUIResource.java index 755d8add1b2..d64feb44c1a 100644 --- a/libjava/classpath/javax/swing/plaf/InsetsUIResource.java +++ b/libjava/classpath/javax/swing/plaf/InsetsUIResource.java @@ -50,8 +50,7 @@ import java.io.Serializable; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public class InsetsUIResource - extends Insets +public class InsetsUIResource extends Insets implements Cloneable, UIResource, Serializable { /** diff --git a/libjava/classpath/javax/swing/plaf/InternalFrameUI.java b/libjava/classpath/javax/swing/plaf/InternalFrameUI.java index fd1e3374c13..0b2f77caa15 100644 --- a/libjava/classpath/javax/swing/plaf/InternalFrameUI.java +++ b/libjava/classpath/javax/swing/plaf/InternalFrameUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class InternalFrameUI - extends ComponentUI +public abstract class InternalFrameUI extends ComponentUI { /** * Constructs a new <code>InternalFrameUI</code>. */ public InternalFrameUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/LabelUI.java b/libjava/classpath/javax/swing/plaf/LabelUI.java index 8fc1d711b0e..f4b74d59e81 100644 --- a/libjava/classpath/javax/swing/plaf/LabelUI.java +++ b/libjava/classpath/javax/swing/plaf/LabelUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class LabelUI - extends ComponentUI +public abstract class LabelUI extends ComponentUI { /** * Constructs a new <code>LabelUI</code>. */ public LabelUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/ListUI.java b/libjava/classpath/javax/swing/plaf/ListUI.java index 66d5cf588bb..bdfe4b3078f 100644 --- a/libjava/classpath/javax/swing/plaf/ListUI.java +++ b/libjava/classpath/javax/swing/plaf/ListUI.java @@ -49,14 +49,14 @@ import javax.swing.JList; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ListUI - extends ComponentUI +public abstract class ListUI extends ComponentUI { /** * Constructs a new <code>ListUI</code>. */ public ListUI() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/plaf/MenuBarUI.java b/libjava/classpath/javax/swing/plaf/MenuBarUI.java index 8835571ac75..2c82adfa0a0 100644 --- a/libjava/classpath/javax/swing/plaf/MenuBarUI.java +++ b/libjava/classpath/javax/swing/plaf/MenuBarUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class MenuBarUI - extends ComponentUI +public abstract class MenuBarUI extends ComponentUI { /** * Constructs a new <code>MenuBarUI</code>. */ public MenuBarUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/MenuItemUI.java b/libjava/classpath/javax/swing/plaf/MenuItemUI.java index 31d73194a18..83ad52fb103 100644 --- a/libjava/classpath/javax/swing/plaf/MenuItemUI.java +++ b/libjava/classpath/javax/swing/plaf/MenuItemUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class MenuItemUI - extends ButtonUI +public abstract class MenuItemUI extends ButtonUI { /** * Constructs a new <code>MenuItemUI</code>. */ public MenuItemUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/PanelUI.java b/libjava/classpath/javax/swing/plaf/PanelUI.java index b1171b80d30..12a6f52cfa8 100644 --- a/libjava/classpath/javax/swing/plaf/PanelUI.java +++ b/libjava/classpath/javax/swing/plaf/PanelUI.java @@ -46,13 +46,13 @@ package javax.swing.plaf; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class PanelUI - extends ComponentUI +public abstract class PanelUI extends ComponentUI { /** * Constructs a new <code>PanelUI</code>. */ public PanelUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/PopupMenuUI.java b/libjava/classpath/javax/swing/plaf/PopupMenuUI.java index c70ad2a4e9b..de351f2ef79 100644 --- a/libjava/classpath/javax/swing/plaf/PopupMenuUI.java +++ b/libjava/classpath/javax/swing/plaf/PopupMenuUI.java @@ -53,14 +53,14 @@ import javax.swing.PopupFactory; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class PopupMenuUI - extends ComponentUI +public abstract class PopupMenuUI extends ComponentUI { /** * Constructs a new <code>PopupMenuUI</code>. */ public PopupMenuUI() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/plaf/ProgressBarUI.java b/libjava/classpath/javax/swing/plaf/ProgressBarUI.java index 79c1b95a34a..013b8c5c2e7 100644 --- a/libjava/classpath/javax/swing/plaf/ProgressBarUI.java +++ b/libjava/classpath/javax/swing/plaf/ProgressBarUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ProgressBarUI - extends ComponentUI +public abstract class ProgressBarUI extends ComponentUI { /** * Constructs a new <code>ProgressBarUI</code>. */ public ProgressBarUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/RootPaneUI.java b/libjava/classpath/javax/swing/plaf/RootPaneUI.java index ff7d0a6e78a..9637c9cf218 100644 --- a/libjava/classpath/javax/swing/plaf/RootPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/RootPaneUI.java @@ -46,13 +46,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class RootPaneUI - extends ComponentUI +public abstract class RootPaneUI extends ComponentUI { /** * Constructs a new <code>RootPaneUI</code>. */ public RootPaneUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/ScrollBarUI.java b/libjava/classpath/javax/swing/plaf/ScrollBarUI.java index 3cad3932720..51b4bf2d853 100644 --- a/libjava/classpath/javax/swing/plaf/ScrollBarUI.java +++ b/libjava/classpath/javax/swing/plaf/ScrollBarUI.java @@ -46,13 +46,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ScrollBarUI - extends ComponentUI +public abstract class ScrollBarUI extends ComponentUI { /** * Constructs a new <code>ScrollBarUI</code>. */ public ScrollBarUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/ScrollPaneUI.java b/libjava/classpath/javax/swing/plaf/ScrollPaneUI.java index 14d2ac61ef2..8b37fed2246 100644 --- a/libjava/classpath/javax/swing/plaf/ScrollPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/ScrollPaneUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ScrollPaneUI - extends ComponentUI +public abstract class ScrollPaneUI extends ComponentUI { /** * Constructs a new <code>ScrollPaneUI</code>. */ public ScrollPaneUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/SeparatorUI.java b/libjava/classpath/javax/swing/plaf/SeparatorUI.java index 6855bd0357e..8a9f8cf4d49 100644 --- a/libjava/classpath/javax/swing/plaf/SeparatorUI.java +++ b/libjava/classpath/javax/swing/plaf/SeparatorUI.java @@ -54,5 +54,6 @@ public abstract class SeparatorUI */ public SeparatorUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/SliderUI.java b/libjava/classpath/javax/swing/plaf/SliderUI.java index 775f19620a8..570e962aa3e 100644 --- a/libjava/classpath/javax/swing/plaf/SliderUI.java +++ b/libjava/classpath/javax/swing/plaf/SliderUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class SliderUI - extends ComponentUI +public abstract class SliderUI extends ComponentUI { /** * Constructs a new <code>SliderUI</code>. */ public SliderUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/SpinnerUI.java b/libjava/classpath/javax/swing/plaf/SpinnerUI.java index fb4a3b13a93..ca29ddb8e4f 100644 --- a/libjava/classpath/javax/swing/plaf/SpinnerUI.java +++ b/libjava/classpath/javax/swing/plaf/SpinnerUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class SpinnerUI - extends ComponentUI +public abstract class SpinnerUI extends ComponentUI { /** * Constructs a new <code>SpinnerUI</code>. */ public SpinnerUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/SplitPaneUI.java b/libjava/classpath/javax/swing/plaf/SplitPaneUI.java index ea9af2b1716..59ededf5847 100644 --- a/libjava/classpath/javax/swing/plaf/SplitPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/SplitPaneUI.java @@ -51,14 +51,14 @@ import javax.swing.JSplitPane; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class SplitPaneUI - extends ComponentUI +public abstract class SplitPaneUI extends ComponentUI { /** * Constructs a new <code>SplitPaneUI</code>. */ public SplitPaneUI() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/plaf/TabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/TabbedPaneUI.java index 6ab823b50a8..01a7720148f 100644 --- a/libjava/classpath/javax/swing/plaf/TabbedPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/TabbedPaneUI.java @@ -51,14 +51,14 @@ import javax.swing.JTabbedPane; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class TabbedPaneUI - extends ComponentUI +public abstract class TabbedPaneUI extends ComponentUI { /** * Constructs a new <code>TabbedPaneUI</code>. */ public TabbedPaneUI() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/plaf/TableHeaderUI.java b/libjava/classpath/javax/swing/plaf/TableHeaderUI.java index f23ca74d7ed..34ac0e0fc55 100644 --- a/libjava/classpath/javax/swing/plaf/TableHeaderUI.java +++ b/libjava/classpath/javax/swing/plaf/TableHeaderUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class TableHeaderUI - extends ComponentUI +public abstract class TableHeaderUI extends ComponentUI { /** * Constructs a new <code>TableHeaderUI</code>. */ public TableHeaderUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/TableUI.java b/libjava/classpath/javax/swing/plaf/TableUI.java index e56bcd13160..a8c6bf909d9 100644 --- a/libjava/classpath/javax/swing/plaf/TableUI.java +++ b/libjava/classpath/javax/swing/plaf/TableUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class TableUI - extends ComponentUI +public abstract class TableUI extends ComponentUI { /** * Constructs a new <code>TableUI</code>. */ public TableUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/TextUI.java b/libjava/classpath/javax/swing/plaf/TextUI.java index dcabdfcdbf3..9f2737cc26f 100644 --- a/libjava/classpath/javax/swing/plaf/TextUI.java +++ b/libjava/classpath/javax/swing/plaf/TextUI.java @@ -57,14 +57,14 @@ import javax.swing.text.View; * @author Ronald Veldema (rveldema@cs.vu.nl) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class TextUI - extends ComponentUI +public abstract class TextUI extends ComponentUI { /** * Constructs a new <code>TextUI</code>. */ public TextUI() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/plaf/ToolBarUI.java b/libjava/classpath/javax/swing/plaf/ToolBarUI.java index 730cf4887db..9a26e7b59d5 100644 --- a/libjava/classpath/javax/swing/plaf/ToolBarUI.java +++ b/libjava/classpath/javax/swing/plaf/ToolBarUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ToolBarUI - extends ComponentUI +public abstract class ToolBarUI extends ComponentUI { /** * Constructs a new <code>ToolBarUI</code>. */ public ToolBarUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/ToolTipUI.java b/libjava/classpath/javax/swing/plaf/ToolTipUI.java index 4383d0edd11..ae2d465e51c 100644 --- a/libjava/classpath/javax/swing/plaf/ToolTipUI.java +++ b/libjava/classpath/javax/swing/plaf/ToolTipUI.java @@ -47,13 +47,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ToolTipUI - extends ComponentUI +public abstract class ToolTipUI extends ComponentUI { /** * Constructs a new <code>ToolTipUI</code>. */ public ToolTipUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/TreeUI.java b/libjava/classpath/javax/swing/plaf/TreeUI.java index e32952de70f..308ec63e2a8 100644 --- a/libjava/classpath/javax/swing/plaf/TreeUI.java +++ b/libjava/classpath/javax/swing/plaf/TreeUI.java @@ -51,14 +51,14 @@ import javax.swing.tree.TreePath; * * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class TreeUI - extends ComponentUI +public abstract class TreeUI extends ComponentUI { /** * Constructs a new <code>TreeUI</code>. */ public TreeUI() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/plaf/UIResource.java b/libjava/classpath/javax/swing/plaf/UIResource.java index 59edf566605..fe398fe5474 100644 --- a/libjava/classpath/javax/swing/plaf/UIResource.java +++ b/libjava/classpath/javax/swing/plaf/UIResource.java @@ -50,6 +50,10 @@ package javax.swing.plaf; * they are initialized or set to <code>null</code>. * * @author Brian Jones + * * @see ComponentUI */ -public interface UIResource { } +public interface UIResource +{ + // This is a marker interface and declares no methods. +} diff --git a/libjava/classpath/javax/swing/plaf/ViewportUI.java b/libjava/classpath/javax/swing/plaf/ViewportUI.java index 087938f1ed2..db514de1a4e 100644 --- a/libjava/classpath/javax/swing/plaf/ViewportUI.java +++ b/libjava/classpath/javax/swing/plaf/ViewportUI.java @@ -48,13 +48,13 @@ package javax.swing.plaf; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public abstract class ViewportUI - extends ComponentUI +public abstract class ViewportUI extends ComponentUI { /** * Constructs a new <code>ViewportUI</code>. */ public ViewportUI() { + // Nothing to do here. } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java b/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java index 836ef223494..69d4415371f 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java @@ -39,42 +39,30 @@ exception statement from your version. */ package javax.swing.plaf.basic; import java.awt.Color; -import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; -import java.awt.Insets; import java.awt.Polygon; import java.awt.Rectangle; +import javax.swing.ButtonModel; import javax.swing.JButton; import javax.swing.SwingConstants; -import javax.swing.border.Border; /** - * This class draws simple arrow buttons for the Basic Look and Feel. + * A button that displays an arrow (triangle) that points {@link #NORTH}, + * {@link #SOUTH}, {@link #EAST} or {@link #WEST}. This button is used by + * the {@link BasicComboBoxUI} class. + * + * @see BasicComboBoxUI#createArrowButton */ public class BasicArrowButton extends JButton implements SwingConstants { - /** The default size of the Arrow buttons. */ - private static int defaultSize = 12; - /** The Polygon that points up. */ - private static Polygon upIcon = new Polygon(new int[] { 0, 5, 9 }, - new int[] { 7, 2, 7 }, 3); - - /** The Polygon that points down. */ - private static Polygon downIcon = new Polygon(new int[] { 1, 5, 9 }, - new int[] { 3, 7, 3 }, 3); - - /** The Polygon that points left. */ - private static Polygon leftIcon = new Polygon(new int[] { 7, 3, 7 }, - new int[] { 1, 5, 9 }, 3); - - /** The Polygon that points right. */ - private static Polygon rightIcon = new Polygon(new int[] { 3, 7, 3 }, - new int[] { 1, 5, 9 }, 3); - - /** The direction to point in. */ + /** + * The direction that the arrow points. + * + * @see #getDirection() + */ protected int direction; /** @@ -89,7 +77,7 @@ public class BasicArrowButton extends JButton implements SwingConstants * edges of the button. * This is package-private to avoid an accessor method. */ - transient Color darkShadow = Color.DARK_GRAY; + transient Color darkShadow = new Color(102, 102, 102); /** * The top and left edges of the button. @@ -97,51 +85,15 @@ public class BasicArrowButton extends JButton implements SwingConstants */ transient Color highlight = Color.WHITE; - /** The border around the ArrowButton. */ - private transient Border buttonBorder = new Border() - { - public Insets getBorderInsets(Component c) - { - return new Insets(2, 2, 2, 2); - } - - public boolean isBorderOpaque() - { - return true; - } - - public void paintBorder(Component c, Graphics g, int x, int y, int w, - int h) - { - Color saved = g.getColor(); - g.setColor(highlight); - - g.drawLine(x + 1, y + 1, x + w - 1, y + 1); - g.drawLine(x + 1, y + 1, x + 1, y + h - 1); - - g.setColor(shadow); - - g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); - g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); - - g.setColor(darkShadow); - - g.drawLine(x, y + h, x + w, y + h); - g.drawLine(x + w, y, x + w, y + h); - - g.setColor(saved); - } - }; - /** - * Creates a new BasicArrowButton object. + * Creates a new <code>BasicArrowButton</code> object. * - * @param direction The direction the arrow points in. + * @param direction The direction the arrow points in (one of: + * {@link #NORTH}, {@link #SOUTH}, {@link #EAST} and {@link #WEST}). */ public BasicArrowButton(int direction) { super(); - setBorder(buttonBorder); setDirection(direction); } @@ -149,7 +101,8 @@ public class BasicArrowButton extends JButton implements SwingConstants * Creates a new BasicArrowButton object with the given colors and * direction. * - * @param direction The direction to point in. + * @param direction The direction to point in (one of: + * {@link #NORTH}, {@link #SOUTH}, {@link #EAST} and {@link #WEST}). * @param background The background color. * @param shadow The shadow color. * @param darkShadow The dark shadow color. @@ -166,9 +119,10 @@ public class BasicArrowButton extends JButton implements SwingConstants } /** - * This method returns whether the focus can traverse to this component. + * Returns whether the focus can traverse to this component. This method + * always returns <code>false</code>. * - * @return Whether the focus can traverse to this component. + * @return <code>false</code>. */ public boolean isFocusTraversable() { @@ -176,7 +130,8 @@ public class BasicArrowButton extends JButton implements SwingConstants } /** - * This method returns the direction of the arrow. + * Returns the direction of the arrow (one of: {@link #NORTH}, + * {@link #SOUTH}, {@link #EAST} and {@link #WEST}). * * @return The direction of the arrow. */ @@ -186,9 +141,10 @@ public class BasicArrowButton extends JButton implements SwingConstants } /** - * This method changes the direction of the arrow. + * Sets the direction of the arrow. * - * @param dir The new direction of the arrow. + * @param dir The new direction of the arrow (one of: {@link #NORTH}, + * {@link #SOUTH}, {@link #EAST} and {@link #WEST}). */ public void setDirection(int dir) { @@ -196,7 +152,7 @@ public class BasicArrowButton extends JButton implements SwingConstants } /** - * This method paints the arrow button. The painting is delegated to the + * Paints the arrow button. The painting is delegated to the * paintTriangle method. * * @param g The Graphics object to paint with. @@ -204,147 +160,257 @@ public class BasicArrowButton extends JButton implements SwingConstants public void paint(Graphics g) { super.paint(g); - Insets insets = getInsets(); Rectangle bounds = getBounds(); - int x = insets.left - + (bounds.width - insets.left - insets.right - defaultSize) / 2; - int y = insets.top - + (bounds.height - insets.left - insets.right - defaultSize) / 2; - paintTriangle(g, x, y, defaultSize, direction, isEnabled()); + int size = bounds.height / 4; + int x = (bounds.width - size) / 2; + int y = (bounds.height - size) / 2; + ButtonModel m = getModel(); + if (m.isArmed()) + { + x++; + y++; + } + paintTriangle(g, x, y, size, direction, isEnabled()); } + /** The preferred size for the button. */ + private static final Dimension PREFERRED_SIZE = new Dimension(16, 16); + + /** The minimum size for the button. */ + private static final Dimension MINIMUM_SIZE = new Dimension(5, 5); + + /** The maximum size for the button. */ + private static final Dimension MAXIMUM_SIZE + = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + /** - * This method returns the preferred size of the arrow button. + * Returns the preferred size of the arrow button. * - * @return The preferred size. + * @return The preferred size (always 16 x 16). */ public Dimension getPreferredSize() { - Insets insets = getInsets(); - int w = defaultSize + insets.left + insets.right; - int h = defaultSize + insets.top + insets.bottom; - - return new Dimension(w, h); + return PREFERRED_SIZE; } /** - * This method returns the minimum size of the arrow button. + * Returns the minimum size of the arrow button. * - * @return The minimum size. + * @return The minimum size (always 5 x 5). */ public Dimension getMinimumSize() { - return getPreferredSize(); + return MINIMUM_SIZE; } /** - * This method returns the maximum size of the arrow button. + * Returns the maximum size of the arrow button. * * @return The maximum size. */ public Dimension getMaximumSize() { - return getPreferredSize(); + return MAXIMUM_SIZE; } /** - * The method paints a triangle with the given size and direction at the - * given x and y coordinates. + * Paints a triangle with the given size, location and direction. It is + * difficult to explain the rationale behind the positioning of the triangle + * relative to the given (x, y) position - by trial and error we seem to + * match the behaviour of the reference implementation (which is missing a + * specification for this method). * - * @param g The Graphics object to paint with. - * @param x The x coordinate to paint at. - * @param y The y coordinate to paint at. - * @param size The size of the icon. - * @param direction The direction of the icon. - * @param isEnabled Whether it is enabled. + * @param g the graphics device. + * @param x the x-coordinate for the triangle's location. + * @param y the y-coordinate for the triangle's location. + * @param size the arrow size (depth). + * @param direction the direction of the arrow (one of: {@link #NORTH}, + * {@link #SOUTH}, {@link #EAST} and {@link #WEST}). + * @param isEnabled if <code>true</code> the arrow is drawn in the enabled + * state, otherwise it is drawn in the disabled state. */ public void paintTriangle(Graphics g, int x, int y, int size, int direction, boolean isEnabled) { - Polygon arrow = null; - switch (direction) - { - case NORTH: - arrow = upIcon; - break; - case SOUTH: - arrow = downIcon; - break; - case EAST: - case RIGHT: - arrow = rightIcon; - break; - case WEST: - case LEFT: - arrow = leftIcon; - break; - } - - int[] xPoints = arrow.xpoints; - int[] yPoints = arrow.ypoints; - int x1; - int y1; - int x2; - int y2; - x1 = y1 = x2 = y2 = 0; - - if (size != defaultSize) - { - float scale = size * 1f / defaultSize; - for (int i = 0; i < 3; i++) - { - xPoints[i] *= scale; - yPoints[i] *= scale; - } - } - g.translate(x, y); - + Color savedColor = g.getColor(); switch (direction) { case NORTH: - x1 = xPoints[0] + 2; - y1 = yPoints[0]; - y2 = y1; - x2 = xPoints[2] - 1; - break; + paintTriangleNorth(g, x, y, size, isEnabled); + break; case SOUTH: - x1 = xPoints[1]; - y1 = yPoints[1] + 1; - x2 = xPoints[2] - 1; - y2 = yPoints[2]; - break; + paintTriangleSouth(g, x, y, size, isEnabled); + break; case LEFT: case WEST: - x1 = xPoints[0] + 1; - y1 = yPoints[0] + 1; - x2 = x1; - y2 = yPoints[2] + 1; - break; + paintTriangleWest(g, x, y, size, isEnabled); + break; case RIGHT: case EAST: - x1 = xPoints[2]; - y1 = yPoints[2] + 1; - x2 = xPoints[1] - 1; - y2 = yPoints[1] + 1; - break; + paintTriangleEast(g, x, y, size, isEnabled); + break; } - Color saved = g.getColor(); - + g.setColor(savedColor); + } + + /** + * Paints an upward-pointing triangle. This method is called by the + * {@link #paintTriangle(Graphics, int, int, int, int, boolean)} method. + * + * @param g the graphics device. + * @param x the x-coordinate for the anchor point. + * @param y the y-coordinate for the anchor point. + * @param size the arrow size (depth). + * @param isEnabled if <code>true</code> the arrow is drawn in the enabled + * state, otherwise it is drawn in the disabled state. + */ + private void paintTriangleNorth(Graphics g, int x, int y, int size, + boolean isEnabled) + { + int tipX = x + (size - 2) / 2; + int tipY = y; + int baseX1 = tipX - (size - 1); + int baseX2 = tipX + (size - 1); + int baseY = y + (size - 1); + Polygon triangle = new Polygon(); + triangle.addPoint(tipX, tipY); + triangle.addPoint(baseX1, baseY); + triangle.addPoint(baseX2, baseY); if (isEnabled) - { - g.setColor(Color.DARK_GRAY); - - if (arrow != null) - g.fillPolygon(xPoints, yPoints, 3); - } + { + g.setColor(Color.DARK_GRAY); + g.fillPolygon(triangle); + g.drawPolygon(triangle); + } else - { - g.setColor(Color.GRAY); - g.fillPolygon(xPoints, yPoints, 3); - g.setColor(Color.WHITE); - g.drawLine(x1, y1, x2, y2); - } - g.setColor(saved); - g.translate(-x, -y); + { + g.setColor(Color.GRAY); + g.fillPolygon(triangle); + g.drawPolygon(triangle); + g.setColor(Color.WHITE); + g.drawLine(baseX1 + 1, baseY + 1, baseX2 + 1, baseY + 1); + } + } + + /** + * Paints an downward-pointing triangle. This method is called by the + * {@link #paintTriangle(Graphics, int, int, int, int, boolean)} method. + * + * @param g the graphics device. + * @param x the x-coordinate for the anchor point. + * @param y the y-coordinate for the anchor point. + * @param size the arrow size (depth). + * @param isEnabled if <code>true</code> the arrow is drawn in the enabled + * state, otherwise it is drawn in the disabled state. + */ + private void paintTriangleSouth(Graphics g, int x, int y, int size, + boolean isEnabled) + { + int tipX = x + (size - 2) / 2; + int tipY = y + (size - 1); + int baseX1 = tipX - (size - 1); + int baseX2 = tipX + (size - 1); + int baseY = y; + Polygon triangle = new Polygon(); + triangle.addPoint(tipX, tipY); + triangle.addPoint(baseX1, baseY); + triangle.addPoint(baseX2, baseY); + if (isEnabled) + { + g.setColor(Color.DARK_GRAY); + g.fillPolygon(triangle); + g.drawPolygon(triangle); + } + else + { + g.setColor(Color.GRAY); + g.fillPolygon(triangle); + g.drawPolygon(triangle); + g.setColor(Color.WHITE); + g.drawLine(tipX + 1, tipY, baseX2, baseY + 1); + g.drawLine(tipX + 1, tipY + 1, baseX2 + 1, baseY + 1); + } + } + + /** + * Paints a right-pointing triangle. This method is called by the + * {@link #paintTriangle(Graphics, int, int, int, int, boolean)} method. + * + * @param g the graphics device. + * @param x the x-coordinate for the anchor point. + * @param y the y-coordinate for the anchor point. + * @param size the arrow size (depth). + * @param isEnabled if <code>true</code> the arrow is drawn in the enabled + * state, otherwise it is drawn in the disabled state. + */ + private void paintTriangleEast(Graphics g, int x, int y, int size, + boolean isEnabled) + { + int tipX = x + (size - 1); + int tipY = y + (size - 2) / 2; + int baseX = x; + int baseY1 = tipY - (size - 1); + int baseY2 = tipY + (size - 1); + + Polygon triangle = new Polygon(); + triangle.addPoint(tipX, tipY); + triangle.addPoint(baseX, baseY1); + triangle.addPoint(baseX, baseY2); + if (isEnabled) + { + g.setColor(Color.DARK_GRAY); + g.fillPolygon(triangle); + g.drawPolygon(triangle); + } + else + { + g.setColor(Color.GRAY); + g.fillPolygon(triangle); + g.drawPolygon(triangle); + g.setColor(Color.WHITE); + g.drawLine(baseX + 1, baseY2, tipX, tipY + 1); + g.drawLine(baseX + 1, baseY2 + 1, tipX + 1, tipY + 1); + } + } + + /** + * Paints a left-pointing triangle. This method is called by the + * {@link #paintTriangle(Graphics, int, int, int, int, boolean)} method. + * + * @param g the graphics device. + * @param x the x-coordinate for the anchor point. + * @param y the y-coordinate for the anchor point. + * @param size the arrow size (depth). + * @param isEnabled if <code>true</code> the arrow is drawn in the enabled + * state, otherwise it is drawn in the disabled state. + */ + private void paintTriangleWest(Graphics g, int x, int y, int size, + boolean isEnabled) + { + int tipX = x; + int tipY = y + (size - 2) / 2; + int baseX = x + (size - 1); + int baseY1 = tipY - (size - 1); + int baseY2 = tipY + (size - 1); + + Polygon triangle = new Polygon(); + triangle.addPoint(tipX, tipY); + triangle.addPoint(baseX, baseY1); + triangle.addPoint(baseX, baseY2); + if (isEnabled) + { + g.setColor(Color.DARK_GRAY); + g.fillPolygon(triangle); + g.drawPolygon(triangle); + } + else + { + g.setColor(Color.GRAY); + g.fillPolygon(triangle); + g.drawPolygon(triangle); + g.setColor(Color.WHITE); + g.drawLine(baseX + 1, baseY1 + 1, baseX + 1, baseY2 + 1); + } } + } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicBorders.java b/libjava/classpath/javax/swing/plaf/basic/BasicBorders.java index e7d6e433877..cec7bec8501 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicBorders.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicBorders.java @@ -806,9 +806,9 @@ public class BasicBorders */ public MarginBorder() { + // Nothing to do here. } - /** * Measures the width of this border. * @@ -1313,33 +1313,32 @@ public class BasicBorders * * @author Sascha Brawer (brawer@dandelis.ch) */ - public static class SplitPaneBorder - implements Border, UIResource + public static class SplitPaneBorder implements Border, UIResource { /** * Indicates that the top edge shall be not be painted - * by {@link #paintRect(java.awt.Graphics, int, int, int, int, int)}. + * by {@link #paintRect}. */ private static final int SUPPRESS_TOP = 1; /** * Indicates that the left edge shall be not be painted - * by {@link #paintRect(java.awt.Graphics, int, int, int, int, int)}. + * by {@link #paintRect}. */ private static final int SUPPRESS_LEFT = 2; /** * Indicates that the bottom edge shall be not be painted - * by {@link #paintRect(java.awt.Graphics, int, int, int, int, int)}. + * by {@link #paintRect}. */ private static final int SUPPRESS_BOTTOM = 4; /** * Indicates that the right edge shall be not be painted - * by {@link #paintRect(java.awt.Graphics, int, int, int, int, int)}. + * by {@link #paintRect}. */ private static final int SUPPRESS_RIGHT = 8; diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java b/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java index 5349f524049..1fca694519f 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java @@ -55,9 +55,8 @@ import javax.swing.JComponent; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -public class BasicButtonListener - implements MouseListener, MouseMotionListener, FocusListener, - ChangeListener, PropertyChangeListener +public class BasicButtonListener implements MouseListener, MouseMotionListener, + FocusListener, ChangeListener, PropertyChangeListener { public BasicButtonListener(AbstractButton b) { @@ -66,10 +65,12 @@ public class BasicButtonListener public void propertyChange(PropertyChangeEvent e) { + // TODO: What should be done here, if anything? } protected void checkOpacity(AbstractButton b) { + // TODO: What should be done here? } public void focusGained(FocusEvent e) @@ -129,18 +130,22 @@ public class BasicButtonListener public void stateChanged(ChangeEvent e) { + // TODO: What should be done here, if anything? } public void mouseMoved(MouseEvent e) { + // TODO: What should be done here, if anything? } public void mouseDragged(MouseEvent e) { + // TODO: What should be done here, if anything? } public void mouseClicked(MouseEvent e) { + // TODO: What should be done here, if anything? } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java index 6c80f14b6fc..2d3dbd350e0 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java @@ -38,7 +38,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; @@ -49,13 +48,19 @@ import javax.swing.AbstractButton; import javax.swing.ButtonModel; import javax.swing.Icon; import javax.swing.InputMap; +import javax.swing.JButton; import javax.swing.JComponent; +import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.plaf.ButtonUI; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; +/** + * A UI delegate for the {@link JButton} component. + */ public class BasicButtonUI extends ButtonUI { /** @@ -72,13 +77,11 @@ public class BasicButtonUI extends ButtonUI private int textShiftOffset; - private Color focusColor; - /** * Factory method to create an instance of BasicButtonUI for a given * {@link JComponent}, which should be an {@link AbstractButton}. * - * @param c The component to create a UI got + * @param c The component. * * @return A new UI capable of drawing the component */ @@ -87,21 +90,46 @@ public class BasicButtonUI extends ButtonUI return new BasicButtonUI(); } + /** + * Returns the default gap between the button's text and icon (in pixels). + * + * @param b the button (ignored). + * + * @return The gap. + */ public int getDefaultTextIconGap(AbstractButton b) { return defaultTextIconGap; } + /** + * Sets the text shift offset to zero. + * + * @see #setTextShiftOffset() + */ protected void clearTextShiftOffset() { textShiftOffset = 0; } + /** + * Returns the text shift offset. + * + * @return The text shift offset. + * + * @see #clearTextShiftOffset() + * @see #setTextShiftOffset() + */ protected int getTextShiftOffset() { return textShiftOffset; } + /** + * Sets the text shift offset to the value in {@link #defaultTextShiftOffset}. + * + * @see #clearTextShiftOffset() + */ protected void setTextShiftOffset() { textShiftOffset = defaultTextShiftOffset; @@ -118,23 +146,33 @@ public class BasicButtonUI extends ButtonUI return "Button."; } + /** + * Installs the default settings. + * + * @param b the button (<code>null</code> not permitted). + */ protected void installDefaults(AbstractButton b) { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); String prefix = getPropertyPrefix(); - focusColor = defaults.getColor(prefix + "focus"); - b.setForeground(defaults.getColor(prefix + "foreground")); - b.setBackground(defaults.getColor(prefix + "background")); - b.setMargin(defaults.getInsets(prefix + "margin")); - b.setBorder(defaults.getBorder(prefix + "border")); - b.setIconTextGap(defaults.getInt(prefix + "textIconGap")); + LookAndFeel.installColorsAndFont(b, prefix + "background", + prefix + "foreground", prefix + "font"); + LookAndFeel.installBorder(b, prefix + "border"); + b.setMargin(UIManager.getInsets(prefix + "margin")); + b.setIconTextGap(UIManager.getInt(prefix + "textIconGap")); b.setInputMap(JComponent.WHEN_FOCUSED, - (InputMap) defaults.get(prefix + "focusInputMap")); - b.setOpaque(true); + (InputMap) UIManager.get(prefix + "focusInputMap")); + b.setRolloverEnabled(UIManager.getBoolean(prefix + "rollover")); } + /** + * Removes the defaults added by {@link #installDefaults(AbstractButton)}. + * + * @param b the button (<code>null</code> not permitted). + */ protected void uninstallDefaults(AbstractButton b) { + if (b.getFont() instanceof UIResource) + b.setFont(null); b.setForeground(null); b.setBackground(null); b.setBorder(null); @@ -144,11 +182,25 @@ public class BasicButtonUI extends ButtonUI protected BasicButtonListener listener; + /** + * Creates and returns a new instance of {@link BasicButtonListener}. This + * method provides a hook to make it easy for subclasses to install a + * different listener. + * + * @param b the button. + * + * @return A new listener. + */ protected BasicButtonListener createButtonListener(AbstractButton b) { return new BasicButtonListener(b); } + /** + * Installs listeners for the button. + * + * @param b the button (<code>null</code> not permitted). + */ protected void installListeners(AbstractButton b) { listener = createButtonListener(b); @@ -159,6 +211,11 @@ public class BasicButtonUI extends ButtonUI b.addMouseMotionListener(listener); } + /** + * Uninstalls listeners for the button. + * + * @param b the button (<code>null</code> not permitted). + */ protected void uninstallListeners(AbstractButton b) { b.removeChangeListener(listener); @@ -215,12 +272,12 @@ public class BasicButtonUI extends ButtonUI return d; } - private static Icon currentIcon(AbstractButton b) + static Icon currentIcon(AbstractButton b) { Icon i = b.getIcon(); ButtonModel model = b.getModel(); - if (model.isPressed() && b.getPressedIcon() != null) + if (model.isPressed() && b.getPressedIcon() != null && b.isEnabled()) i = b.getPressedIcon(); else if (model.isRollover()) @@ -231,7 +288,7 @@ public class BasicButtonUI extends ButtonUI i = b.getRolloverIcon(); } - else if (b.isSelected()) + else if (b.isSelected() && b.isEnabled()) { if (b.isEnabled() && b.getSelectedIcon() != null) i = b.getSelectedIcon(); @@ -264,7 +321,10 @@ public class BasicButtonUI extends ButtonUI g.setFont(f); - SwingUtilities.calculateInnerArea(b, vr); + if (b.isBorderPainted()) + SwingUtilities.calculateInnerArea(b, vr); + else + vr = SwingUtilities.getLocalBounds(b); String text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f), b.getText(), currentIcon(b), @@ -279,13 +339,11 @@ public class BasicButtonUI extends ButtonUI if ((b.getModel().isArmed() && b.getModel().isPressed()) || b.isSelected()) paintButtonPressed(g, b); - else - paintButtonNormal(g, vr, c); paintIcon(g, c, ir); if (text != null) paintText(g, b, tr, text); - if (b.isFocusOwner()) + if (b.isFocusOwner() && b.isFocusPainted()) paintFocus(g, b, vr, tr, ir); } @@ -331,44 +389,25 @@ public class BasicButtonUI extends ButtonUI /** * Paints the background area of an {@link AbstractButton} in the pressed - * state. This means filling the supplied area with the {@link - * pressedBackgroundColor}. + * state. This means filling the supplied area with a darker than normal + * background. * * @param g The graphics context to paint with * @param b The button to paint the state of */ protected void paintButtonPressed(Graphics g, AbstractButton b) { - if (b.isContentAreaFilled()) - { - Rectangle area = new Rectangle(); - SwingUtilities.calculateInnerArea(b, area); - g.setColor(b.getBackground().darker()); - g.fillRect(area.x, area.y, area.width, area.height); - } - } - - /** - * Paints the background area of an {@link AbstractButton} in the normal, - * non-pressed state. This means filling the supplied area with the - * {@link normalBackgroundColor}. - * - * @param g The graphics context to paint with - * @param area The area in which to paint - * @param b The component to paint the state of - */ - private void paintButtonNormal(Graphics g, Rectangle area, JComponent b) - { - if (((AbstractButton)b).isContentAreaFilled() && b.isOpaque()) + if (b.isContentAreaFilled() && b.isOpaque()) { - g.setColor(b.getBackground()); + Rectangle area = new Rectangle(); + SwingUtilities.calculateInnerArea(b, area); + g.setColor(UIManager.getColor(getPropertyPrefix() + "shadow")); g.fillRect(area.x, area.y, area.width, area.height); } } /** - * Paints the "text" property of an {@link AbstractButton}, using the - * {@link textColor} color. + * Paints the "text" property of an {@link AbstractButton}. * * @param g The graphics context to paint with * @param c The component to paint the state of @@ -382,8 +421,7 @@ public class BasicButtonUI extends ButtonUI } /** - * Paints the "text" property of an {@link AbstractButton}, using the - * {@link textColor} color. + * Paints the "text" property of an {@link AbstractButton}. * * @param g The graphics context to paint with * @param b The button to paint the state of @@ -401,15 +439,15 @@ public class BasicButtonUI extends ButtonUI if (b.isEnabled()) { - g.setColor(b.getForeground()); - g.drawString(text, textRect.x, textRect.y + fm.getAscent()); + g.setColor(b.getForeground()); + g.drawString(text, textRect.x, textRect.y + fm.getAscent()); } else { - g.setColor(b.getBackground().brighter()); - g.drawString(text, textRect.x, textRect.y + fm.getAscent()); - g.setColor(b.getBackground().darker()); - g.drawString(text, textRect.x + 1, textRect.y + fm.getAscent() + 1); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + String prefix = getPropertyPrefix(); + g.setColor(defaults.getColor(prefix + "disabledText")); + g.drawString(text, textRect.x, textRect.y + fm.getAscent()); } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java index 945aea53dc3..95e0dc98257 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java @@ -53,6 +53,15 @@ import javax.swing.plaf.ComponentUI; */ public class BasicCheckBoxMenuItemUI extends BasicMenuItemUI { + + /** + * Creates a new BasicCheckBoxMenuItemUI object. + */ + public BasicCheckBoxMenuItemUI() + { + super(); + } + /** * Factory method to create a BasicCheckBoxMenuItemUI for the given {@link * JComponent}, which should be a JCheckBoxMenuItem @@ -77,18 +86,6 @@ public class BasicCheckBoxMenuItemUI extends BasicMenuItemUI } /** - * This method installs the defaults that are defined in the Basic look and - * feel for this JRadioButtonMenuItem - */ - protected void installDefaults() - { - super.installDefaults(); - - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - checkIcon = defaults.getIcon("CheckBoxMenuItem.checkIcon"); - } - - /** * DOCUMENT ME! * * @param item DOCUMENT ME! @@ -100,5 +97,7 @@ public class BasicCheckBoxMenuItemUI extends BasicMenuItemUI MenuElement[] path, MenuSelectionManager manager) { + // TODO: May not be implemented properly. + item.processMouseEvent(e, path, manager); } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java index 4e6d3815453..5a872ae6368 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java @@ -47,8 +47,7 @@ import javax.swing.JColorChooser; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JTabbedPane; -import javax.swing.UIDefaults; -import javax.swing.UIManager; +import javax.swing.LookAndFeel; import javax.swing.colorchooser.AbstractColorChooserPanel; import javax.swing.colorchooser.ColorChooserComponentFactory; import javax.swing.event.ChangeEvent; @@ -243,12 +242,21 @@ public class BasicColorChooserUI extends ColorChooserUI { uninstallListeners(); uninstallDefaults(); + uninstallDefaultChoosers(); pane = null; chooser = null; } /** + * Uninstalls the default color choosers that have been installed by this UI. + */ + protected void uninstallDefaultChoosers() + { + defaultChoosers = null; + } + + /** * This method installs the preview panel for the JColorChooser. */ protected void installPreviewPanel() @@ -281,11 +289,9 @@ public class BasicColorChooserUI extends ColorChooserUI */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - chooser.setFont(defaults.getFont("ColorChooser.font")); - chooser.setForeground(defaults.getColor("ColorChooser.foreground")); - chooser.setBackground(defaults.getColor("ColorChooser.background")); + LookAndFeel.installColorsAndFont(chooser, "ColorChooser.background", + "ColorChooser.foreground", + "ColorChooser.font"); } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxEditor.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxEditor.java index dd867f0dc55..831dde8c336 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxEditor.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxEditor.java @@ -45,35 +45,34 @@ import java.awt.event.FocusListener; import javax.swing.ComboBoxEditor; import javax.swing.JTextField; -import javax.swing.border.EmptyBorder; /** - * This is a component that is responsible for displaying/editting selected - * item in comboBox. By default, the JTextField is returned as - * BasicComboBoxEditor. + * An editor used by the {@link BasicComboBoxUI} class. This editor uses a + * {@link JTextField} as the editor component. * * @author Olga Rodimina */ public class BasicComboBoxEditor extends Object implements ComboBoxEditor, FocusListener { + /** The editor component. */ protected JTextField editor; /** - * Creates a new BasicComboBoxEditor object. + * Creates a new <code>BasicComboBoxEditor</code> instance. */ public BasicComboBoxEditor() { editor = new JTextField(); - editor.setBorder(new EmptyBorder(1, 1, 1, 1)); + editor.setBorder(null); + editor.setColumns(9); } /** - * This method returns textfield that will be used by the combo box to - * display/edit currently selected item in the combo box. + * Returns the component that will be used by the combo box to display and + * edit the currently selected item in the combo box. * - * @return textfield that will be used by the combo box to display/edit - * currently selected item + * @return The editor component, which is a {@link JTextField} in this case. */ public Component getEditorComponent() { @@ -98,15 +97,18 @@ public class BasicComboBoxEditor extends Object implements ComboBoxEditor, } /** - * This method returns item that is currently editable. + * Returns the text from the editor component. * - * @return item in the combo box that is currently editable + * @return The text from the editor component. */ public Object getItem() { return editor.getText(); } + /** + * Selects all the text in the editor component. + */ public void selectAll() { editor.selectAll(); @@ -136,8 +138,8 @@ public class BasicComboBoxEditor extends Object implements ComboBoxEditor, } /** - * This method adds actionListener to the editor. If the user will edit - * currently selected item in the textfield and pressEnter, then action + * Adds an {@link ActionListener} to the editor component. If the user will + * edit currently selected item in the textfield and pressEnter, then action * will be performed. The actionPerformed of this ActionListener should * change the selected item of the comboBox to the newly editted selected * item. @@ -147,27 +149,32 @@ public class BasicComboBoxEditor extends Object implements ComboBoxEditor, */ public void addActionListener(ActionListener l) { - // FIXME: Need to implement + editor.addActionListener(l); } /** - * This method removes actionListener from the textfield. + * Removes the {@link ActionListener} from the editor component. * - * @param l the ActionListener to remove from the textfield. + * @param l the listener to remove. */ public void removeActionListener(ActionListener l) { - // FIXME: Need to implement + editor.removeActionListener(l); } + /** + * A subclass of {@link BasicComboBoxEditor} that implements the + * {@link UIResource} interface. + */ public static class UIResource extends BasicComboBoxEditor implements javax.swing.plaf.UIResource { /** - * Creates a new UIResource object. + * Creates a new <code>BasicComboBoxEditor.UIResource</code> instance. */ public UIResource() { + // Nothing to do here. } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxRenderer.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxRenderer.java index e4fbb8352a9..8115605b77a 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxRenderer.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxRenderer.java @@ -1,5 +1,5 @@ /* BasicComboBoxRenderer.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,36 +40,39 @@ package javax.swing.plaf.basic; import java.awt.Component; import java.awt.Dimension; +import java.awt.FontMetrics; import java.io.Serializable; +import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.ListCellRenderer; import javax.swing.SwingConstants; -import javax.swing.UIDefaults; -import javax.swing.UIManager; +import javax.swing.SwingUtilities; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; /** - * This class is renderer for the combo box. + * A renderer for a {@link JComboBox}. * * @author Olga Rodimina */ -public class BasicComboBoxRenderer extends JLabel implements ListCellRenderer, - Serializable +public class BasicComboBoxRenderer + extends JLabel + implements ListCellRenderer, Serializable { /** - * This border is used whenever renderer doesn't have a focus. + * A shared border instance for all renderers. */ protected static Border noFocusBorder = new EmptyBorder(0, 0, 0, 0); /** - * Creates a new BasicComboBoxRenderer object. + * Creates a new <code>BasicComboBoxRenderer</code> object. */ public BasicComboBoxRenderer() { setHorizontalAlignment(SwingConstants.LEFT); + setBorder(noFocusBorder); } /** @@ -83,7 +86,8 @@ public class BasicComboBoxRenderer extends JLabel implements ListCellRenderer, } /** - * getListCellRendererComponent + * Returns a component that has been configured to display the given + * <code>value</code>. * * @param list List of items for which to the background and foreground * colors @@ -100,43 +104,59 @@ public class BasicComboBoxRenderer extends JLabel implements ListCellRenderer, boolean cellHasFocus) { String s = value.toString(); - setText(s); + + // String maybe larger than comboBox. + FontMetrics fm = getToolkit().getFontMetrics(list.getFont()); + int strWidth = SwingUtilities.computeStringWidth(fm, s); + int cbWidth = getSize().width; + if (cbWidth != 0 && strWidth > cbWidth) + { + char[] str = s.toCharArray(); + int currWidth = 0; + int i = 0; + String postStr = "... "; + cbWidth -= SwingUtilities.computeStringWidth(fm, postStr); + while (i < str.length && currWidth < cbWidth) + { + ++i; + currWidth = SwingUtilities.computeStringWidth(fm, new String(str, 0, i)); + } + setText(new String(str, 0, i) + postStr); + } + else + setText(s); + setOpaque(true); - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - if (isSelected) + if (isSelected || cellHasFocus) { - setBackground(list.getSelectionBackground()); - setForeground(list.getSelectionForeground()); + setBackground(list.getSelectionBackground()); + setForeground(list.getSelectionForeground()); } else { - setBackground(list.getBackground()); - setForeground(list.getForeground()); + 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; } + /** + * A subclass of {@link BasicComboBoxRenderer} that implements the + * {@link javax.swing.plaf.UIResource} interface. + */ public static class UIResource extends BasicComboBoxRenderer implements javax.swing.plaf.UIResource { /** - * Creates a new UIResource object. + * Creates a new <code>UIResource</code> object. */ public UIResource() { + // Nothing to do here. } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java index 68e18a6ab01..464c8dd9f63 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java @@ -42,6 +42,8 @@ import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; import java.awt.LayoutManager; @@ -69,15 +71,17 @@ import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.ListCellRenderer; -import javax.swing.UIDefaults; +import javax.swing.LookAndFeel; +import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; import javax.swing.plaf.ComboBoxUI; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; /** - * UI Delegate for JComboBox + * A UI delegate for the {@link JComboBox} component. * * @author Olga Rodimina * @author Robert Schuster @@ -85,52 +89,54 @@ import javax.swing.plaf.ComponentUI; public class BasicComboBoxUI extends ComboBoxUI { /** - * This arrow button that is displayed in the rigth side of JComboBox. This - * button is used to hide and show combo box's list of items + * The arrow button that is displayed in the right side of JComboBox. This + * button is used to hide and show combo box's list of items. */ protected JButton arrowButton; /** - * The combo box for which this UI delegate is for + * The combo box represented by this UI delegate. */ protected JComboBox comboBox; /** - * Component that is responsible for displaying/editting selected item of - * the combo box. By default JTextField is used as an editor for the - * JComboBox + * The component that is responsible for displaying/editing the selected + * item of the combo box. + * + * @see BasicComboBoxEditor#getEditorComponent() */ protected Component editor; /** - * Listener listening to focus events occuring in the JComboBox + * A listener listening to focus events occurring in the {@link JComboBox}. */ protected FocusListener focusListener; /** - * tells whether JComboBox currently has focus + * A flag indicating whether JComboBox currently has the focus. */ protected boolean hasFocus; /** - * Listener listening to item events fired by the JComboBox + * A listener listening to item events fired by the {@link JComboBox}. */ protected ItemListener itemListener; /** - * KeyListener listening to key events that occur while JComboBox has focus + * A listener listening to key events that occur while {@link JComboBox} has + * the focus. */ protected KeyListener keyListener; /** - * MouseListener listening to mouse events occuring in the combo box + * A listener listening to mouse events occuring in the {@link JComboBox}. */ private MouseListener mouseListener; /** * List used when rendering selected item of the combo box. The selection - * and foreground colors for combo box renderer are configured from this - * list + * and foreground colors for combo box renderer are configured from this + * list. */ protected JList listBox; @@ -140,11 +146,14 @@ public class BasicComboBoxUI extends ComboBoxUI protected ListDataListener listDataListener; /** - * Popup list containing combo box's menu items + * Popup list containing the combo box's menu items. */ protected ComboPopup popup; + protected KeyListener popupKeyListener; + protected MouseListener popupMouseListener; + protected MouseMotionListener popupMouseMotionListener; /** @@ -152,30 +161,34 @@ public class BasicComboBoxUI extends ComboBoxUI */ protected PropertyChangeListener propertyChangeListener; + /** + * The button background. + * @see #installDefaults() + */ + private Color buttonBackground; + + /** + * The button shadow. + * @see #installDefaults() + */ + private Color buttonShadow; + /** - * Colors that are used to render selected item in the combo box. + * The button dark shadow. + * @see #installDefaults() */ - private Color shadow; - private Color darkShadow; - private Color highlight; - private Color lightHighlight; + private Color buttonDarkShadow; + + /** + * The button highlight. + * @see #installDefaults() + */ + private Color buttonHighlight; /* Size of the largest item in the comboBox * This is package-private to avoid an accessor method. */ - Dimension largestItemSize; - - // It seems that JComboBox doesn't have a border set explicitely. So we just - // paint the border everytime combo box is displayed. - - /* border insets for this JComboBox - * This is package-private to avoid an accessor method. */ - static final Insets borderInsets = new Insets(2, 2, 2, 2); - - // Width of the arrow button - // This is package-private to avoid an accessor method. - // FIXME: has wrong name for a constant. - static final int arrowButtonWidth = 15; + Dimension displaySize; // FIXME: This fields aren't used anywhere at this moment. protected Dimension cachedMinimumSize; @@ -183,19 +196,20 @@ public class BasicComboBoxUI extends ComboBoxUI protected boolean isMinimumSizeDirty; /** - * Creates a new BasicComboBoxUI object. + * Creates a new <code>BasicComboBoxUI</code> object. */ public BasicComboBoxUI() { + // Nothing to do here. } /** - * Factory method to create a BasicComboBoxUI for the given {@link - * JComponent}, which should be a {@link JComboBox}. + * A factory method to create a UI delegate for the given + * {@link JComponent}, which should be a {@link JComboBox}. * * @param c The {@link JComponent} a UI is being created for. * - * @return A BasicComboBoxUI for the {@link JComponent}. + * @return A UI delegate for the {@link JComponent}. */ public static ComponentUI createUI(JComponent c) { @@ -203,9 +217,11 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method installs the UI for the given JComponent. + * Installs the UI for the given {@link JComponent}. * - * @param c The JComponent to install a UI for. + * @param c the JComponent to install a UI for. + * + * @see #uninstallUI(JComponent) */ public void installUI(JComponent c) { @@ -213,20 +229,22 @@ public class BasicComboBoxUI extends ComboBoxUI if (c instanceof JComboBox) { - comboBox = (JComboBox) c; - comboBox.setOpaque(true); - comboBox.setLayout(createLayoutManager()); - installDefaults(); - installComponents(); - installListeners(); - installKeyboardActions(); + comboBox = (JComboBox) c; + comboBox.setOpaque(true); + comboBox.setLayout(createLayoutManager()); + installDefaults(); + installComponents(); + installListeners(); + installKeyboardActions(); } } /** - * This method uninstalls the UI. + * Uninstalls the UI for the given {@link JComponent}. * * @param c The JComponent that is having this UI removed. + * + * @see #installUI(JComponent) */ public void uninstallUI(JComponent c) { @@ -238,27 +256,27 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method installs the defaults that are defined in the Basic look and - * feel for this {@link JComboBox}. + * Installs the defaults that are defined in the {@link BasicLookAndFeel} + * for this {@link JComboBox}. + * + * @see #uninstallDefaults() */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - comboBox.setBackground(defaults.getColor("ComboBox.background")); - comboBox.setFont(defaults.getFont("ComboBox.font")); - comboBox.setForeground(defaults.getColor("ComboBox.foreground")); - - // Set default color that should be used to to render selected item - // of the combo box. - shadow = defaults.getColor("Button.shadow"); - darkShadow = defaults.getColor("Button.darkShadow"); - lightHighlight = defaults.getColor("Button.light"); - highlight = defaults.getColor("Button.highlight"); + LookAndFeel.installColorsAndFont(comboBox, "ComboBox.background", + "ComboBox.foreground", "ComboBox.font"); + + // fetch the button color scheme + buttonBackground = UIManager.getColor("ComboBox.buttonBackground"); + buttonShadow = UIManager.getColor("ComboBox.buttonShadow"); + buttonDarkShadow = UIManager.getColor("ComboBox.buttonDarkShadow"); + buttonHighlight = UIManager.getColor("ComboBox.buttonHighlight"); } /** - * This method creates and installs the listeners for this UI. + * Creates and installs the listeners for this UI. + * + * @see #uninstallListeners() */ protected void installListeners() { @@ -268,6 +286,7 @@ public class BasicComboBoxUI extends ComboBoxUI focusListener = createFocusListener(); comboBox.addFocusListener(focusListener); + listBox.addFocusListener(focusListener); itemListener = createItemListener(); comboBox.addItemListener(itemListener); @@ -276,35 +295,40 @@ public class BasicComboBoxUI extends ComboBoxUI comboBox.addKeyListener(keyListener); mouseListener = createMouseListener(); - comboBox.addMouseListener(mouseListener); + arrowButton.addMouseListener(mouseListener); // install listeners that listen to combo box model listDataListener = createListDataListener(); comboBox.getModel().addListDataListener(listDataListener); - - configureArrowButton(); } /** - * This method uninstalls the defaults and sets any objects created during - * install to null + * Uninstalls the defaults and sets any objects created during + * install to <code>null</code>. + * + * @see #installDefaults() */ protected void uninstallDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - comboBox.setBackground(null); - comboBox.setFont(null); - comboBox.setForeground(null); - - shadow = null; - darkShadow = null; - lightHighlight = null; - highlight = null; + if (comboBox.getFont() instanceof UIResource) + comboBox.setFont(null); + + if (comboBox.getForeground() instanceof UIResource) + comboBox.setForeground(null); + + if (comboBox.getBackground() instanceof UIResource) + comboBox.setBackground(null); + + buttonBackground = null; + buttonShadow = null; + buttonDarkShadow = null; + buttonHighlight = null; } /** * Detaches all the listeners we attached in {@link #installListeners}. + * + * @see #installListeners() */ protected void uninstallListeners() { @@ -312,6 +336,7 @@ public class BasicComboBoxUI extends ComboBoxUI propertyChangeListener = null; comboBox.removeFocusListener(focusListener); + listBox.removeFocusListener(focusListener); focusListener = null; comboBox.removeItemListener(itemListener); @@ -320,17 +345,15 @@ public class BasicComboBoxUI extends ComboBoxUI comboBox.removeKeyListener(keyListener); keyListener = null; - comboBox.removeMouseListener(mouseListener); + arrowButton.removeMouseListener(mouseListener); mouseListener = null; comboBox.getModel().removeListDataListener(listDataListener); listDataListener = null; - - unconfigureArrowButton(); } /** - * This method creates popup that will contain list of combo box's items + * Creates the popup that will contain list of combo box's items. * * @return popup containing list of combo box's items */ @@ -340,7 +363,7 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * Creates KeyListener to listen to key events. + * Creates a {@link KeyListener} to listen to key events. * * @return KeyListener that listens to key events. */ @@ -350,8 +373,8 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method create MouseListener that will listen to mouse event occuring - * in combo box. + * Creates a {@link MouseListener} that will listen to mouse events occurring + * in the combo box. * * @return the MouseListener */ @@ -361,10 +384,10 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method create FocusListener that will listen to changes in this + * Creates the {@link FocusListener} that will listen to changes in this * JComboBox's focus. * - * @return theFocusListener + * @return the FocusListener. */ protected FocusListener createFocusListener() { @@ -372,9 +395,9 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method create ListDataListener to listen to ComboBox's data model + * Creates a {@link ListDataListener} to listen to the combo box's data model. * - * @return ListDataListener + * @return The new listener. */ protected ListDataListener createListDataListener() { @@ -382,10 +405,10 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method creates ItemListener that will listen to to the changes in + * Creates an {@link ItemListener} that will listen to the changes in * the JComboBox's selection. * - * @return the ItemListener + * @return The ItemListener */ protected ItemListener createItemListener() { @@ -393,10 +416,10 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method creates PropertyChangeListener to listen to the changes in + * Creates a {@link PropertyChangeListener} to listen to the changes in * the JComboBox's bound properties. * - * @return the PropertyChangeListener + * @return The PropertyChangeListener */ protected PropertyChangeListener createPropertyChangeListener() { @@ -404,9 +427,10 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method returns layout manager for the combo box. + * Creates and returns a layout manager for the combo box. Subclasses can + * override this method to provide a different layout. * - * @return layout manager for the combo box + * @return a layout manager for the combo box. */ protected LayoutManager createLayoutManager() { @@ -414,10 +438,10 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method creates component that will be responsible for rendering the + * Creates a component that will be responsible for rendering the * selected component in the combo box. * - * @return render for the combo box + * @return A renderer for the combo box. */ protected ListCellRenderer createRenderer() { @@ -425,58 +449,54 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * Creates component that will be responsible for displaying/editting - * selected item in the combo box. This editor is used only when combo box - * is editable. + * Creates the component that will be responsible for displaying/editing + * the selected item in the combo box. This editor is used only when combo + * box is editable. * - * @return component that will be responsible for displaying/editting - * selected item in the combo box. + * @return A new component that will be responsible for displaying/editing + * the selected item in the combo box. */ protected ComboBoxEditor createEditor() { - return new BasicComboBoxEditor(); + return new BasicComboBoxEditor.UIResource(); } /** - * This method installs components for this JComboBox. ArrowButton, main - * part of combo box (upper part) and popup list of items are created and + * Installs the components for this JComboBox. ArrowButton, main + * part of combo box (upper part) and popup list of items are created and * configured here. */ protected void installComponents() { - // create and install arrow button - arrowButton = createArrowButton(); - - comboBox.add(arrowButton); - - // Set list that will be used by BasicComboBoxRender - // in order to determine the right colors when rendering - listBox = new JList(); - - Color background = arrowButton.getBackground(); - listBox.setBackground(background); - listBox.setSelectionBackground(background.darker()); - - Color foreground = arrowButton.getForeground(); - listBox.setForeground(foreground); - listBox.setSelectionForeground(foreground); + // create drop down list of items + popup = createPopup(); + listBox = popup.getList(); // set editor and renderer for the combo box. Editor is used // only if combo box becomes editable, otherwise renderer is used // to paint the selected item; combobox is not editable by default. comboBox.setRenderer(createRenderer()); - comboBox.setEditor(createEditor()); - editor = comboBox.getEditor().getEditorComponent(); + // create and install arrow button + arrowButton = createArrowButton(); + configureArrowButton(); + comboBox.add(arrowButton); - // create drop down list of items - popup = createPopup(); + ComboBoxEditor currentEditor = comboBox.getEditor(); + if (currentEditor == null || currentEditor instanceof UIResource) + { + currentEditor = createEditor(); + comboBox.setEditor(currentEditor); + } + editor = currentEditor.getEditorComponent(); comboBox.revalidate(); } /** - * This method uninstalls components from this JComboBox + * Uninstalls components from this {@link JComboBox}. + * + * @see #installComponents() */ protected void uninstallComponents() { @@ -490,12 +510,18 @@ public class BasicComboBoxUI extends ComboBoxUI comboBox.setRenderer(null); - comboBox.setEditor(null); - editor = null; + // if the editor is not an instanceof UIResource, it was not set by the + // UI delegate, so don't clear it... + ComboBoxEditor currentEditor = comboBox.getEditor(); + if (currentEditor instanceof UIResource) + { + comboBox.setEditor(null); + editor = null; + } } /** - * This method adds editor to the combo box + * Adds the current editor to the combo box. */ public void addEditor() { @@ -503,7 +529,7 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method removes editor from the combo box + * Removes the current editor from the combo box. */ public void removeEditor() { @@ -511,15 +537,17 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method configures editor for this combo box. + * Configures the editor for this combo box. */ protected void configureEditor() { + editor.setFont(comboBox.getFont()); + comboBox.getEditor().setItem(comboBox.getSelectedItem()); // FIXME: Need to implement. Set font and add listeners. } /** - * This method removes all the listeners for the editor. + * Unconfigures the editor for this combo nox. This method is not implemented. */ protected void unconfigureEditor() { @@ -527,42 +555,50 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method adds listeners to the arrow button part of the combo box. + * Configures the arrow button. + * + * @see #configureArrowButton() */ public void configureArrowButton() { - arrowButton.addMouseListener(mouseListener); + arrowButton.setEnabled(comboBox.isEnabled()); + arrowButton.setFont(comboBox.getFont()); } /** - * This method removes listeners from the arrow button part of the combo - * box. + * Unconfigures the arrow button. + * + * @see #configureArrowButton() + * + * @specnote The specification says this method is implementation specific + * and should not be used or overridden. */ public void unconfigureArrowButton() { - arrowButton.removeMouseListener(mouseListener); + // Nothing to do here yet. } /** - * This method create arrow button for this JComboBox. Arrow button is - * responsible for displaying / hiding drop down list of items when it is - * clicked. + * Creates an arrow button for this {@link JComboBox}. The arrow button is + * displayed at the right end of the combo box and is used to display/hide + * the drop down list of items. * - * @return JButton arrow button for this JComboBox. + * @return A new button. */ protected JButton createArrowButton() { - return new BasicArrowButton(BasicArrowButton.SOUTH); + return new BasicArrowButton(BasicArrowButton.SOUTH, buttonBackground, + buttonShadow, buttonDarkShadow, buttonHighlight); } /** - * This method checks if popup part of the combo box is visible on the - * screen + * Returns <code>true</code> if the popup is visible, and <code>false</code> + * otherwise. * * @param c The JComboBox to check * - * @return true if popup part of the JComboBox is visible and false - * otherwise. + * @return <code>true</code> if popup part of the JComboBox is visible and + * <code>false</code> otherwise. */ public boolean isPopupVisible(JComboBox c) { @@ -570,7 +606,7 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * Displays/Hides JComboBox's list of items on the screen. + * Displays/hides the {@link JComboBox}'s list of items on the screen. * * @param c The combo box, for which list of items should be * displayed/hidden @@ -579,7 +615,10 @@ public class BasicComboBoxUI extends ComboBoxUI public void setPopupVisible(JComboBox c, boolean v) { if (v) - popup.show(); + { + popup.show(); + popup.getList().requestFocus(); + } else popup.hide(); } @@ -593,7 +632,7 @@ public class BasicComboBoxUI extends ComboBoxUI */ public boolean isFocusTraversable(JComboBox c) { - if (comboBox.isEditable()) + if (!comboBox.isEditable()) return true; return false; @@ -607,56 +646,28 @@ public class BasicComboBoxUI extends ComboBoxUI */ public void paint(Graphics g, JComponent c) { - if (c instanceof JComboBox) - { - JComboBox cb = (JComboBox) c; - - paintBorder(g, comboBox.getBounds(), hasFocus); - - Rectangle rect = rectangleForCurrentValue(); - paintCurrentValueBackground(g, rect, hasFocus); - paintCurrentValue(g, rect, hasFocus); - } - } - - private void paintBorder(Graphics g, Rectangle bounds, boolean hasFocus) - { - int x = 0; - int y = 0; - int width = bounds.width; - int height = bounds.height; - - Color oldColor = g.getColor(); - - if (! arrowButton.getModel().isPressed()) - BasicGraphicsUtils.drawEtchedRect(g, x, y, width, height, Color.gray, - Color.white, Color.gray, Color.white); - else - { - g.setColor(darkShadow); - g.drawRect(x, y, width, height); - g.setColor(shadow); - g.drawRect(x + 1, y + 1, width - 3, height - 3); - } - g.setColor(oldColor); + Rectangle rect = rectangleForCurrentValue(); + paintCurrentValueBackground(g, rect, hasFocus); + paintCurrentValue(g, rect, hasFocus); } /** - * Returns preferred size for the given menu item. + * Returns preferred size for the combo box. * * @param c comboBox for which to get preferred size * - * @return $Dimension$ preferred size for the given combo box + * @return The preferred size for the given combo box */ public Dimension getPreferredSize(JComponent c) { - // return null to indicate that combo box's layout will determin its - // preferred size - return null; + // note: overriding getMinimumSize() (for example in the MetalComboBoxUI + // class) affects the getPreferredSize() result, so it seems logical that + // this method is implemented by delegating to the getMinimumSize() method + return getMinimumSize(c); } /** - * This method returns the minimum size for this {@link JComboBox} for this + * Returns the minimum size for this {@link JComboBox} for this * look and feel. * * @param c The {@link JComponent} to find the minimum size for. @@ -665,20 +676,26 @@ public class BasicComboBoxUI extends ComboBoxUI */ public Dimension getMinimumSize(JComponent c) { - return null; + Dimension d = getDisplaySize(); + int arrowButtonWidth = d.height; + Dimension result = new Dimension(d.width + arrowButtonWidth, d.height); + return result; } + /** The value returned by the getMaximumSize() method. */ + private static final Dimension MAXIMUM_SIZE = new Dimension(32767, 32767); + /** - * This method returns the maximum size for this {@link JComboBox} for this + * Returns the maximum size for this {@link JComboBox} for this * look and feel. * * @param c The {@link JComponent} to find the maximum size for * - * @return The dimensions of the minimum size. + * @return The maximum size (<code>Dimension(32767, 32767)</code>). */ public Dimension getMaximumSize(JComponent c) { - return null; + return MAXIMUM_SIZE; } public int getAccessibleChildrenCount(JComponent c) @@ -707,7 +724,7 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method selects next possible item relative to the current selection + * Selects next possible item relative to the current selection * to be next selected item in the combo box. */ protected void selectNextPossibleValue() @@ -718,7 +735,7 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method selects previous item relative to current selection to be + * Selects previous item relative to current selection to be * next selected item. */ protected void selectPreviousPossibleValue() @@ -729,8 +746,8 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method displays combo box popup if the popup is not currently shown - * on the screen and hides it if it is currently shown + * Displays combo box popup if the popup is not currently shown + * on the screen and hides it if it is currently shown */ protected void toggleOpenClose() { @@ -738,34 +755,23 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method returns bounds in which comboBox's selected Item will be - * displayed + * Returns the bounds in which comboBox's selected item will be + * displayed. * * @return rectangle bounds in which comboBox's selected Item will be * displayed */ protected Rectangle rectangleForCurrentValue() { - Rectangle cbBounds = comboBox.getBounds(); - - // Subtract width or the arrow button and border insets - Rectangle rectForCurrentValue = new Rectangle(cbBounds.x - + borderInsets.left, - cbBounds.y - + borderInsets.top, - cbBounds.width - - arrowButtonWidth - - borderInsets.left - - borderInsets.right, - cbBounds.height - - borderInsets.top - - borderInsets.bottom); - + Rectangle cbBounds = SwingUtilities.getLocalBounds(comboBox); + Rectangle abBounds = arrowButton.getBounds(); + Rectangle rectForCurrentValue = new Rectangle(cbBounds.x, cbBounds.y, + cbBounds.width - abBounds.width, cbBounds.height); return rectForCurrentValue; } /** - * This method returns insets of the current border. + * Returns the insets of the current border. * * @return Insets representing space between combo box and its border */ @@ -775,7 +781,7 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method paints currently selected value in the main part of the combo + * Paints currently selected value in the main part of the combo * box (part without popup). * * @param g graphics context @@ -794,28 +800,29 @@ public class BasicComboBoxUI extends ComboBoxUI * If there is currently no selected item we will take an empty * String as replacement. */ - Component comp = comboBox.getRenderer() - .getListCellRendererComponent(listBox, - (currentValue != null ? currentValue : ""), - -1, - isPressed, - hasFocus); - if (! comboBox.isEnabled()) - comp.setEnabled(false); - - g.translate(borderInsets.left, borderInsets.top); - comp.setBounds(0, 0, bounds.width, bounds.height); - comp.paint(g); - g.translate(-borderInsets.left, -borderInsets.top); - - comboBox.revalidate(); + Component comp = comboBox.getRenderer().getListCellRendererComponent( + listBox, (currentValue != null ? currentValue : ""), -1, + isPressed, hasFocus); + if (! comboBox.isEnabled()) + { + comp.setBackground(UIManager.getLookAndFeelDefaults().getColor( + "ComboBox.disabledBackground")); + comp.setForeground(UIManager.getLookAndFeelDefaults().getColor( + "ComboBox.disabledForeground")); + comp.setEnabled(false); + } + comp.setBounds(0, 0, bounds.width, bounds.height); + comp.setFont(comboBox.getFont()); + comp.paint(g); + + comboBox.revalidate(); } else comboBox.getEditor().setItem(comboBox.getSelectedItem()); } /** - * This method paints background of part of the combo box, where currently + * Paints the background of part of the combo box, where currently * selected value is displayed. If the combo box has focus this method * should also paint focus rectangle around the combo box. * @@ -832,58 +839,102 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * Returns default size for the combo box that doesn't contain any elements - * in it + * Returns the default size for the display area of a combo box that does + * not contain any elements. This method returns the width and height of + * a single space in the current font, plus a margin of 1 pixel. * - * @return Default size of the combo box with no elements in it. + * @return The default display size. + * + * @see #getDisplaySize() */ protected Dimension getDefaultSize() { - return new Dimension(6, 17); + // There is nothing in the spec to say how this method should be + // implemented...so I've done some guessing, written some Mauve tests, + // and written something that gives dimensions that are close to the + // reference implementation. + FontMetrics fm = comboBox.getFontMetrics(comboBox.getFont()); + int w = fm.charWidth(' ') + 2; + int h = fm.getHeight() + 2; + return new Dimension(w, h); } /** - * Returns size of the largest item in the combo box. This size will be the - * size of the combo box, not including the arrowButton. + * Returns the size of the display area for the combo box. This size will be + * the size of the combo box, not including the arrowButton. * - * @return dimensions of the largest item in the combo box. + * @return The size of the display area for the combo box. */ - protected Dimension getLargestItemSize() + protected Dimension getDisplaySize() { - ComboBoxModel model = comboBox.getModel(); - int numItems = model.getSize(); - - // if combo box doesn't have any items then simply - // return its default size - if (numItems == 0) + if (!comboBox.isEditable()) { - largestItemSize = getDefaultSize(); - return largestItemSize; + Object prototype = comboBox.getPrototypeDisplayValue(); + if (prototype != null) + { + // calculate result based on prototype + ListCellRenderer renderer = comboBox.getRenderer(); + Component comp = renderer.getListCellRendererComponent(listBox, + prototype, -1, false, false); + Dimension compSize = comp.getPreferredSize(); + compSize.width += 2; // add 1 pixel margin around area + compSize.height += 2; + return compSize; + } + else + { + ComboBoxModel model = comboBox.getModel(); + int numItems = model.getSize(); + + // if combo box doesn't have any items then simply + // return its default size + if (numItems == 0) + { + displaySize = getDefaultSize(); + return displaySize; + } + + Dimension size = new Dimension(0, 0); + + // ComboBox's display size should be equal to the + // size of the largest item in the combo box. + ListCellRenderer renderer = comboBox.getRenderer(); + + for (int i = 0; i < numItems; i++) + { + Object item = model.getElementAt(i); + Component comp = renderer.getListCellRendererComponent(listBox, + item, -1, false, false); + + Dimension compSize = comp.getPreferredSize(); + if (compSize.width + 2 > size.width) + size.width = compSize.width + 2; + if (compSize.height + 2 > size.height) + size.height = compSize.height + 2; + } + displaySize = size; + return displaySize; + } } - - Dimension size = new Dimension(0, 0); - - // ComboBox's display size should be equal to the - // size of the largest item in the combo box. - ListCellRenderer renderer = comboBox.getRenderer(); - - for (int i = 0; i < numItems; i++) + else // an editable combo, { - Object item = model.getElementAt(i); - String s = item.toString(); - Component comp = renderer.getListCellRendererComponent(listBox, item, - -1, false, false); - - if (comp.getPreferredSize().getWidth() > size.getWidth()) - size = comp.getPreferredSize(); + Component comp = comboBox.getEditor().getEditorComponent(); + Dimension prefSize = comp.getPreferredSize(); + int width = prefSize.width; + int height = prefSize.height + 2; + Object prototype = comboBox.getPrototypeDisplayValue(); + if (prototype != null) + { + FontMetrics fm = comboBox.getFontMetrics(comboBox.getFont()); + width = Math.max(width, fm.stringWidth(prototype.toString()) + 2); + } + displaySize = new Dimension(width, height); + return displaySize; } - - largestItemSize = size; - return largestItemSize; } /** - * This method installs the keyboard actions for the JComboBox as specified + * Installs the keyboard actions for the {@link JComboBox} as specified * by the look and feel. */ protected void installKeyboardActions() @@ -892,7 +943,7 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method uninstalls the keyboard actions for the JComboBox there were + * Uninstalls the keyboard actions for the {@link JComboBox} there were * installed by in {@link #installListeners}. */ protected void uninstallKeyboardActions() @@ -901,22 +952,39 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This class is Layout Manager for this combo box. + * A {@link LayoutManager} used to position the sub-components of the + * {@link JComboBox}. + * + * @see BasicComboBoxUI#createLayoutManager() */ - public class ComboBoxLayoutManager extends Object implements LayoutManager + public class ComboBoxLayoutManager implements LayoutManager { /** * Creates a new ComboBoxLayoutManager object. */ public ComboBoxLayoutManager() { + // Nothing to do here. } + /** + * Adds a component to the layout. This method does nothing, since the + * layout manager doesn't need to track the components. + * + * @param name the name to associate the component with (ignored). + * @param comp the component (ignored). + */ public void addLayoutComponent(String name, Component comp) { // Do nothing } + /** + * Removes a component from the layout. This method does nothing, since + * the layout manager doesn't need to track the components. + * + * @param comp the component. + */ public void removeLayoutComponent(Component comp) { // Do nothing @@ -925,43 +993,30 @@ public class BasicComboBoxUI extends ComboBoxUI /** * Returns preferred layout size of the JComboBox. * - * @param parent Container for which preferred size should be calculated + * @param parent the Container for which the preferred size should be + * calculated. * - * @return preferred size for the given container + * @return The preferred size for the given container */ public Dimension preferredLayoutSize(Container parent) { - Dimension d = new Dimension(0, 0); - - if (largestItemSize == null) - largestItemSize = getLargestItemSize(); - - // add size for the area that will display selected item - d.width += largestItemSize.getWidth(); - d.height += largestItemSize.getHeight(); - - // add size of the arrow button - d.width += arrowButtonWidth; - - // add width and height of the border - d.width += borderInsets.left + borderInsets.right; - d.height += borderInsets.left + borderInsets.right; - - // Add combo box's insets - Insets insets = parent.getInsets(); - d.width += insets.left + insets.right; - d.width += insets.left + insets.right; - - return d; + return getPreferredSize((JComponent) parent); } + /** + * Returns the minimum layout size. + * + * @param parent the container. + * + * @return The minimum size. + */ public Dimension minimumLayoutSize(Container parent) { return preferredLayoutSize(parent); } /** - * This method layouts out the components in the container. It puts arrow + * Arranges the components in the container. It puts arrow * button right end part of the comboBox. If the comboBox is editable * then editor is placed to the left of arrow button, starting from the * beginning. @@ -972,21 +1027,19 @@ public class BasicComboBoxUI extends ComboBoxUI { // Position editor component to the left of arrow button if combo box is // editable - int editorWidth = comboBox.getBounds().width - arrowButtonWidth - 2; + int arrowSize = comboBox.getHeight(); + int editorWidth = comboBox.getBounds().width - arrowSize; if (comboBox.isEditable()) - editor.setBounds(borderInsets.left, borderInsets.top, editorWidth, - comboBox.getBounds().height - borderInsets.left - - borderInsets.top); - - arrowButton.setBounds(editorWidth, 2, arrowButtonWidth, - comboBox.getBounds().height - 4); + editor.setBounds(0, 0, editorWidth, comboBox.getBounds().height); + + arrowButton.setBounds(editorWidth, 0, arrowSize, arrowSize); comboBox.revalidate(); } } /** - * This class handles focus changes occuring in the combo box. This class is + * Handles focus changes occuring in the combo box. This class is * responsible for repainting combo box whenever focus is gained or lost * and also for hiding popup list of items whenever combo box loses its * focus. @@ -998,11 +1051,12 @@ public class BasicComboBoxUI extends ComboBoxUI */ public FocusHandler() { + // Nothing to do here. } /** - * This mehtod is invoked when combo box gains focus. It repaints main - * part of combo box accordingally. + * Invoked when combo box gains focus. It repaints main + * part of combo box accordingly. * * @param e the FocusEvent */ @@ -1013,22 +1067,22 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method is invoked when combo box loses focus It repaint main part - * of combo box accordingally and hides popup list of items. + * Invoked when the combo box loses focus. It repaints the main part + * of the combo box accordingly and hides the popup list of items. * * @param e the FocusEvent */ public void focusLost(FocusEvent e) { hasFocus = false; + setPopupVisible(comboBox, false); comboBox.repaint(); - popup.hide(); } } /** - * This class handles ItemEvent fired by the JComboBox when its selected - * item changes. + * Handles {@link ItemEvent}s fired by the {@link JComboBox} when its + * selected item changes. */ public class ItemHandler extends Object implements ItemListener { @@ -1037,16 +1091,19 @@ public class BasicComboBoxUI extends ComboBoxUI */ public ItemHandler() { + // Nothing to do here. } /** - * This method is invoked when selected item becomes deselected or when + * Invoked when selected item becomes deselected or when * new item becomes selected. * * @param e the ItemEvent representing item's state change. */ public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED && comboBox.isEditable()) + comboBox.getEditor().setItem(e.getItem()); comboBox.repaint(); } } @@ -1058,11 +1115,11 @@ public class BasicComboBoxUI extends ComboBoxUI { public KeyHandler() { + // Nothing to do here. } - /* - * This method is invoked whenever key is pressed while JComboBox is in - * focus. + /** + * Invoked whenever key is pressed while JComboBox is in focus. */ public void keyPressed(KeyEvent e) { @@ -1072,7 +1129,7 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This class handles to the changes occuring in the JComboBox's data model + * Handles the changes occurring in the JComboBox's data model. */ public class ListDataHandler extends Object implements ListDataListener { @@ -1081,10 +1138,11 @@ public class BasicComboBoxUI extends ComboBoxUI */ public ListDataHandler() { + // Nothing to do here. } /** - * This method is invoked content's of JComboBox's data model are changed + * Invoked if the content's of JComboBox's data model are changed. * * @param e ListDataEvent describing the change. */ @@ -1094,36 +1152,27 @@ public class BasicComboBoxUI extends ComboBoxUI } /** - * This method is invoked when items were added to the JComboBox's data - * model. + * Invoked when items are added to the JComboBox's data model. * * @param e ListDataEvent describing the change. */ public void intervalAdded(ListDataEvent e) { - // must determine if the size of the combo box should change - int start = e.getIndex0(); - int end = e.getIndex1(); - ComboBoxModel model = comboBox.getModel(); ListCellRenderer renderer = comboBox.getRenderer(); - if (largestItemSize == null) - largestItemSize = new Dimension(0, 0); + if (displaySize == null) + displaySize = getDisplaySize(); + if (displaySize.width < getDefaultSize().width) + displaySize.width = getDefaultSize().width; + if (displaySize.height < getDefaultSize().height) + displaySize.height = getDefaultSize().height; - for (int i = start; i < end; i++) - { - Object item = model.getElementAt(i); - Component comp = renderer.getListCellRendererComponent(new JList(), - item, -1, - false, false); - if (comp.getPreferredSize().getWidth() > largestItemSize.getWidth()) - largestItemSize = comp.getPreferredSize(); - } + comboBox.repaint(); } /** - * This method is invoked when items were removed from the JComboBox's + * Invoked when items are removed from the JComboBox's * data model. * * @param e ListDataEvent describing the change. @@ -1131,23 +1180,29 @@ public class BasicComboBoxUI extends ComboBoxUI public void intervalRemoved(ListDataEvent e) { // recalculate display size of the JComboBox. - largestItemSize = getLargestItemSize(); + displaySize = getDisplaySize(); comboBox.repaint(); } } /** - * This class handles PropertyChangeEvents fired by JComboBox. + * Handles {@link PropertyChangeEvent}s fired by the {@link JComboBox}. */ public class PropertyChangeHandler extends Object implements PropertyChangeListener { + /** + * Creates a new instance. + */ public PropertyChangeHandler() { + // Nothing to do here. } /** - * This method is invoked whenever bound property of JComboBox changes. + * Invoked whenever bound property of JComboBox changes. + * + * @param e the event. */ public void propertyChange(PropertyChangeEvent e) { @@ -1185,59 +1240,36 @@ public class BasicComboBoxUI extends ComboBoxUI if ((ComboBoxModel) e.getNewValue() != null) comboBox.getModel().addListDataListener(listDataListener); } + else if (e.getPropertyName().equals("font")) + { + Font font = (Font) e.getNewValue(); + editor.setFont(font); + listBox.setFont(font); + arrowButton.setFont(font); + comboBox.revalidate(); + comboBox.repaint(); + } // FIXME: Need to handle changes in other bound properties. } } /** - * MouseHandler listens to mouse events occuring in the combo box. This - * class is responsible for repainting this JComboBox whenever the mouse is - * being pressed or released over it. + * A handler for mouse events occurring in the combo box. An instance of + * this class is returned by the <code>createMouseListener()</code> method. */ private class MouseHandler extends MouseAdapter { /** - * This method is invoked when mouse is pressed over the combo box. It - * repaints the combo box accordinglly + * Invoked when mouse is pressed over the combo box. It toggles the + * visibility of the popup list. * - * @param e the MouseEvent + * @param e the event */ public void mousePressed(MouseEvent e) { if (comboBox.isEnabled()) - { - if (e.getSource() instanceof JComboBox) - { - arrowButton.getModel().setPressed(true); - arrowButton.getModel().setArmed(true); - } - - comboBox.repaint(); - - if (e.getSource() instanceof BasicArrowButton) - toggleOpenClose(); - } - } - - /** - * This method is invoked when mouse is released over the combo box. It - * repaints the combo box accordinglly - * - * @param e the MouseEvent - */ - public void mouseReleased(MouseEvent e) - { - if (comboBox.isEnabled()) - { - if (e.getSource() instanceof JComboBox) - { - arrowButton.getModel().setPressed(false); - arrowButton.getModel().setArmed(false); - } - - comboBox.repaint(); - } + toggleOpenClose(); } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java index 73aac8d4e65..73979bb89c1 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Point; @@ -55,6 +56,7 @@ import java.awt.event.MouseMotionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import javax.swing.BorderFactory; import javax.swing.ComboBoxModel; import javax.swing.JComboBox; import javax.swing.JLabel; @@ -178,8 +180,8 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup // to display number of rows equal to 'maximumRowCount' property int popupHeight = getPopupHeightForRowCount(comboBox.getMaximumRowCount()); - list.setPreferredSize(new Dimension(cbBounds.width, popupHeight)); - super.setPopupSize(cbBounds.width, popupHeight); + scroller.setPreferredSize(new Dimension(cbBounds.width, popupHeight)); + pack(); // Highlight selected item in the combo box's drop down list if (comboBox.getSelectedIndex() != -1) @@ -478,6 +480,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup */ protected void configureScroller() { + scroller.setBorder(null); scroller.getViewport().setView(list); scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); } @@ -488,6 +491,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup */ protected void configurePopup() { + setBorder(BorderFactory.createLineBorder(Color.BLACK)); // initialize list that will be used to display combo box's items this.list = createList(); ((JLabel) list.getCellRenderer()).setHorizontalAlignment(SwingConstants.LEFT); @@ -714,7 +718,11 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup protected void updateListBoxSelectionForEvent(MouseEvent anEvent, boolean shouldScroll) { - // FIXME: Need to implement + // TODO: We need to handle the shouldScroll parameter somehow. + int index = list.locationToIndex(anEvent.getPoint()); + // Check for valid index. + if (index >= 0) + list.setSelectedIndex(index); } /** @@ -732,6 +740,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup */ protected InvocationMouseHandler() { + // Nothing to do here. } /** @@ -744,7 +753,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup public void mousePressed(MouseEvent e) { if (comboBox.isEnabled()) - togglePopup(); + togglePopup(); } /** @@ -768,15 +777,15 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup // then change selection and close popup if (! (releasedComponent instanceof JComboBox)) { - // List model contains the item over which mouse is released, - // since it is updated every time the mouse is moved over a different - // item in the list. Now that the mouse is released we need to - // update model of the combo box as well. - comboBox.setSelectedIndex(list.getSelectedIndex()); - - if (isAutoScrolling) - stopAutoScrolling(); - hide(); + // List model contains the item over which mouse is released, + // since it is updated every time the mouse is moved over a different + // item in the list. Now that the mouse is released we need to + // update model of the combo box as well. + comboBox.setSelectedIndex(list.getSelectedIndex()); + + if (isAutoScrolling) + stopAutoScrolling(); + hide(); } } } @@ -792,6 +801,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup */ protected InvocationMouseMotionHandler() { + // Nothing to do here. } /** @@ -868,6 +878,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup */ protected ItemHandler() { + // Nothing to do here. } /** @@ -877,6 +888,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup */ public void itemStateChanged(ItemEvent e) { + // TODO: What should be done here? } } @@ -890,16 +902,20 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { protected ListMouseHandler() { + // Nothing to do here. } public void mousePressed(MouseEvent e) { + // TODO: What should be do here? } public void mouseReleased(MouseEvent anEvent) { int index = list.locationToIndex(anEvent.getPoint()); - comboBox.setSelectedIndex(index); + // Check for valid index. + if (index >= 0) + comboBox.setSelectedIndex(index); hide(); } } @@ -913,15 +929,12 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { protected ListMouseMotionHandler() { + // Nothing to do here. } public void mouseMoved(MouseEvent anEvent) { - // Highlight list cells over which the mouse is located. - // This changes list model, but has no effect on combo box's data model - int index = list.locationToIndex(anEvent.getPoint()); - list.setSelectedIndex(index); - list.repaint(); + updateListBoxSelectionForEvent(anEvent, false); } } @@ -934,6 +947,7 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { protected PropertyChangeHandler() { + // Nothing to do here. } public void propertyChange(PropertyChangeEvent e) @@ -1009,18 +1023,22 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { public ListDataHandler() { + // Nothing to do here. } public void contentsChanged(ListDataEvent e) { + // Nothing to do here. } public void intervalAdded(ListDataEvent e) { + // Nothing to do here. } public void intervalRemoved(ListDataEvent e) { + // Nothing to do here. } } @@ -1032,10 +1050,12 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { protected ListSelectionHandler() { + // Nothing to do here. } public void valueChanged(ListSelectionEvent e) { + // Nothing to do here. } } @@ -1046,10 +1066,12 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup { public InvocationKeyHandler() { + // Nothing to do here. } public void keyReleased(KeyEvent e) { + // Nothing to do here. } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicDesktopIconUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicDesktopIconUI.java index 561b497f1c3..3f8730249da 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicDesktopIconUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicDesktopIconUI.java @@ -365,6 +365,7 @@ public class BasicDesktopIconUI extends DesktopIconUI */ public BasicDesktopIconUI() { + // Nothing to do here. } /** @@ -585,6 +586,7 @@ public class BasicDesktopIconUI extends DesktopIconUI } catch (PropertyVetoException pve) { + // We do nothing if the attempt has been vetoed. } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicDesktopPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicDesktopPaneUI.java index b59261b17f3..4116858da9d 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicDesktopPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicDesktopPaneUI.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Color; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.beans.PropertyVetoException; @@ -49,10 +50,10 @@ import javax.swing.JComponent; import javax.swing.JDesktopPane; import javax.swing.JInternalFrame; import javax.swing.KeyStroke; -import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.DesktopPaneUI; +import javax.swing.plaf.UIResource; /** * This class is the UI delegate for JDesktopPane for the Basic look and feel. @@ -74,13 +75,14 @@ public class BasicDesktopPaneUI extends DesktopPaneUI { if (desktop.getSelectedFrame() != null) { - try - { - desktop.getSelectedFrame().setClosed(true); - } - catch (PropertyVetoException pve) - { - } + try + { + desktop.getSelectedFrame().setClosed(true); + } + catch (PropertyVetoException pve) + { + // We do nothing if the attempts has been vetoed. + } } } @@ -112,13 +114,14 @@ public class BasicDesktopPaneUI extends DesktopPaneUI { if (desktop.getSelectedFrame() != null) { - try - { - desktop.getSelectedFrame().setMaximum(true); - } - catch (PropertyVetoException pve) - { - } + try + { + desktop.getSelectedFrame().setMaximum(true); + } + catch (PropertyVetoException pve) + { + // We do nothing if the attempts has been vetoed. + } } } @@ -150,13 +153,14 @@ public class BasicDesktopPaneUI extends DesktopPaneUI { if (desktop.getSelectedFrame() != null) { - try - { - desktop.getSelectedFrame().setIcon(true); - } - catch (PropertyVetoException pve) - { - } + try + { + desktop.getSelectedFrame().setIcon(true); + } + catch (PropertyVetoException pve) + { + // We do nothing if the attempt has been vetoed. + } } } @@ -236,16 +240,17 @@ public class BasicDesktopPaneUI extends DesktopPaneUI JInternalFrame frame = desktop.getSelectedFrame(); if (frame != null) { - try - { - if (frame.isIcon()) - frame.setIcon(false); - else if (frame.isMaximum()) - frame.setMaximum(false); - } - catch (PropertyVetoException pve) - { - } + try + { + if (frame.isIcon()) + frame.setIcon(false); + else if (frame.isMaximum()) + frame.setMaximum(false); + } + catch (PropertyVetoException pve) + { + // We do nothing if the attempt has been vetoed. + } } } @@ -304,6 +309,7 @@ public class BasicDesktopPaneUI extends DesktopPaneUI */ public BasicDesktopPaneUI() { + // Nothing to do here. } /** @@ -361,9 +367,9 @@ public class BasicDesktopPaneUI extends DesktopPaneUI */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - desktop.setBackground(defaults.getColor("desktop")); + Color bg = desktop.getBackground(); + if (bg == null || bg instanceof UIResource) + desktop.setBackground(UIManager.getColor("desktop")); } /** @@ -381,7 +387,7 @@ public class BasicDesktopPaneUI extends DesktopPaneUI protected void installKeyboardActions() { // FIXME: create actions and keystrokes. - registerKeyboardAction(); + registerKeyboardActions(); } /** @@ -405,7 +411,7 @@ public class BasicDesktopPaneUI extends DesktopPaneUI * This method registers the actions to the appropriate Action and Input * maps. */ - protected void registerKeyboardAction() + protected void registerKeyboardActions() { // FIXME: Do the binding. // XXX: the gtk windows tend to intercept a lot of the diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java index f74e9229893..9c639656545 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java @@ -48,7 +48,6 @@ import java.awt.Point; import java.awt.Polygon; import java.awt.Window; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseAdapter; @@ -60,9 +59,9 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Hashtable; + import javax.swing.AbstractAction; import javax.swing.Action; -import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.Icon; import javax.swing.JButton; @@ -92,25 +91,31 @@ import javax.swing.plaf.FileChooserUI; /** - * DOCUMENT ME! + * A UI delegate for the {@link JFileChooser} component under the + * {@link BasicLookAndFeel}. */ public class BasicFileChooserUI extends FileChooserUI { /** - * DOCUMENT ME! + * A file filter that accepts all files. */ protected class AcceptAllFileFilter extends FileFilter { + /** + * Creates a new instance. + */ public AcceptAllFileFilter() { + // Nothing to do here. } /** - * DOCUMENT ME! + * Returns <code>true</code> always, as all files are accepted by this + * filter. * - * @param f DOCUMENT ME! + * @param f the file. * - * @return DOCUMENT ME! + * @return Always <code>true</code>. */ public boolean accept(File f) { @@ -118,9 +123,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns a description for this filter. * - * @return DOCUMENT ME! + * @return A description for the file filter. */ public String getDescription() { @@ -129,7 +134,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Handles a user action to approve the dialog selection. + * + * @see BasicFileChooserUI#getApproveSelectionAction() */ protected class ApproveSelectionAction extends AbstractAction { @@ -138,50 +145,55 @@ public class BasicFileChooserUI extends FileChooserUI */ protected ApproveSelectionAction() { + // Nothing to do here. } /** - * DOCUMENT ME! - * - * @param e DOCUMENT ME! + * Sets the current selection and closes the dialog. + * + * @param e the action event. */ public void actionPerformed(ActionEvent e) { - Object obj = filelist.getSelectedValue(); + Object obj = new String(parentPath + entry.getText()); if (obj != null) { - File f = filechooser.getFileSystemView().createFileObject(obj - .toString()); - if (filechooser.isTraversable(f) && - filechooser.getFileSelectionMode() == JFileChooser.FILES_ONLY) + File f = filechooser.getFileSystemView().createFileObject( + obj.toString()); + if (filechooser.isTraversable(f) + && filechooser.isDirectorySelectionEnabled()) filechooser.setCurrentDirectory(f); - else - { - filechooser.setSelectedFile(f); - filechooser.approveSelection(); - closeDialog(); - } + else + { + filechooser.setSelectedFile(f); + filechooser.approveSelection(); + closeDialog(); + } } } } /** - * DOCUMENT ME! + * Provides presentation information about files and directories. */ protected class BasicFileView extends FileView { - /** DOCUMENT ME! */ + /** Storage for cached icons. */ protected Hashtable iconCache = new Hashtable(); + /** + * Creates a new instance. + */ public BasicFileView() { + // Nothing to do here. } /** - * DOCUMENT ME! + * Adds an icon to the cache, associating it with the given file/directory. * - * @param f DOCUMENT ME! - * @param i DOCUMENT ME! + * @param f the file/directory. + * @param i the icon. */ public void cacheIcon(File f, Icon i) { @@ -189,7 +201,7 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Clears the icon cache. */ public void clearIconCache() { @@ -197,11 +209,12 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Retrieves the icon associated with the specified file/directory, if + * there is one. * - * @param f DOCUMENT ME! + * @param f the file/directory. * - * @return DOCUMENT ME! + * @return The cached icon (or <code>null</code>). */ public Icon getCachedIcon(File f) { @@ -209,11 +222,13 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns a description of the given file/directory. In this + * implementation, the description is the same as the name returned by + * {@link #getName(File)}. * - * @param f DOCUMENT ME! + * @param f the file/directory. * - * @return DOCUMENT ME! + * @return A description of the given file/directory. */ public String getDescription(File f) { @@ -221,11 +236,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns an icon appropriate for the given file or directory. * - * @param f DOCUMENT ME! + * @param f the file/directory. * - * @return DOCUMENT ME! + * @return An icon. */ public Icon getIcon(File f) { @@ -241,11 +256,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the name for the given file/directory. * - * @param f DOCUMENT ME! + * @param f the file/directory. * - * @return DOCUMENT ME! + * @return The name of the file/directory. */ public String getName(File f) { @@ -253,11 +268,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns a localised description for the type of file/directory. * - * @param f DOCUMENT ME! + * @param f the file/directory. * - * @return DOCUMENT ME! + * @return A type description for the given file/directory. */ public String getTypeDescription(File f) { @@ -268,34 +283,38 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns {@link Boolean#TRUE} if the given file/directory is hidden, + * and {@link Boolean#FALSE} otherwise. * - * @param f DOCUMENT ME! + * @param f the file/directory. * - * @return DOCUMENT ME! + * @return {@link Boolean#TRUE} or {@link Boolean#FALSE}. */ public Boolean isHidden(File f) { - return new Boolean(filechooser.getFileSystemView().isHiddenFile(f)); + return Boolean.valueOf(filechooser.getFileSystemView().isHiddenFile(f)); } } /** - * DOCUMENT ME! + * Handles an action to cancel the file chooser. + * + * @see BasicFileChooserUI#getCancelSelectionAction() */ protected class CancelSelectionAction extends AbstractAction { /** - * Creates a new CancelSelectionAction object. + * Creates a new <code>CancelSelectionAction</code> object. */ protected CancelSelectionAction() { + // Nothing to do here. } /** - * DOCUMENT ME! + * Cancels the selection and closes the dialog. * - * @param e DOCUMENT ME! + * @param e the action event (ignored). */ public void actionPerformed(ActionEvent e) { @@ -305,21 +324,25 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * An action to handle changes to the parent directory (for example, via + * a click on the "up folder" button). + * + * @see BasicFileChooserUI#getChangeToParentDirectoryAction() */ protected class ChangeToParentDirectoryAction extends AbstractAction { /** - * Creates a new ChangeToParentDirectoryAction object. + * Creates a new <code>ChangeToParentDirectoryAction</code> object. */ protected ChangeToParentDirectoryAction() { + // Nothing to do here. } /** - * DOCUMENT ME! + * Handles the action event. * - * @param e DOCUMENT ME! + * @param e the action event. */ public void actionPerformed(ActionEvent e) { @@ -330,11 +353,13 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * A mouse listener that handles double-click events. + * + * @see BasicFileChooserUI#createDoubleClickListener(JFileChooser, JList) */ protected class DoubleClickListener extends MouseAdapter { - /** DOCUMENT ME! */ + /** A timer. */ private Timer timer = null; /** DOCUMENT ME! */ @@ -358,54 +383,57 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! - * - * @param e DOCUMENT ME! + * Handles a mouse click event. + * + * @param e the event. */ public void mouseClicked(MouseEvent e) { if (list.getSelectedValue() == null) - return; + return; FileSystemView fsv = filechooser.getFileSystemView(); if (timer.isRunning() && list.getSelectedValue().toString().equals(lastSelected.toString())) { - File f = fsv.createFileObject(lastSelected.toString()); - timer.stop(); - if (filechooser.isTraversable(f)) - { - filechooser.setCurrentDirectory(f); - filechooser.rescanCurrentDirectory(); - } - else - { - filechooser.setSelectedFile(f); - filechooser.approveSelection(); - closeDialog(); - } + File f = fsv.createFileObject(lastSelected.toString()); + timer.stop(); + if (filechooser.isTraversable(f)) + { + filechooser.setCurrentDirectory(f); + filechooser.rescanCurrentDirectory(); + } + else + { + filechooser.setSelectedFile(f); + filechooser.approveSelection(); + closeDialog(); + } } else { - File f = fsv.createFileObject(list.getSelectedValue().toString()); - if (filechooser.isTraversable(f)) - { - setDirectorySelected(true); - setDirectory(f); - } - else - { - setDirectorySelected(false); - setDirectory(null); - } - lastSelected = list.getSelectedValue().toString(); - timer.restart(); + String path = list.getSelectedValue().toString(); + File f = fsv.createFileObject(path); + if (filechooser.isTraversable(f)) + { + setDirectorySelected(true); + setDirectory(f); + } + else + { + setDirectorySelected(false); + setDirectory(null); + } + lastSelected = path; + parentPath = path.substring(0, path.lastIndexOf("/") + 1); + entry.setText(path.substring(path.lastIndexOf("/") + 1)); + timer.restart(); } } /** - * DOCUMENT ME! - * - * @param e DOCUMENT ME! + * Handles a mouse entered event (NOT IMPLEMENTED). + * + * @param e the mouse event. */ public void mouseEntered(MouseEvent e) { @@ -414,21 +442,26 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * An action that changes the file chooser to display the user's home + * directory. + * + * @see BasicFileChooserUI#getGoHomeAction() */ protected class GoHomeAction extends AbstractAction { /** - * Creates a new GoHomeAction object. + * Creates a new <code>GoHomeAction</code> object. */ protected GoHomeAction() { + // Nothing to do here. } /** - * DOCUMENT ME! + * Sets the directory to the user's home directory, and repaints the + * file chooser component. * - * @param e DOCUMENT ME! + * @param e the action event (ignored). */ public void actionPerformed(ActionEvent e) { @@ -440,21 +473,24 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * An action that handles the creation of a new folder/directory. + * + * @see BasicFileChooserUI#getNewFolderAction() */ protected class NewFolderAction extends AbstractAction { /** - * Creates a new NewFolderAction object. + * Creates a new <code>NewFolderAction</code> object. */ protected NewFolderAction() { + // Nothing to do here. } /** - * DOCUMENT ME! + * Handles the event by creating a new folder. * - * @param e DOCUMENT ME! + * @param e the action event (ignored). */ public void actionPerformed(ActionEvent e) { @@ -473,15 +509,18 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * A listener for selection events in the file list. + * + * @see BasicFileChooserUI#createListSelectionListener(JFileChooser) */ protected class SelectionListener implements ListSelectionListener { /** - * Creates a new SelectionListener object. + * Creates a new <code>SelectionListener</code> object. */ protected SelectionListener() { + // Nothing to do here. } /** @@ -504,6 +543,8 @@ public class BasicFileChooserUI extends FileChooserUI /** * DOCUMENT ME! + * + * @see BasicFileChooserUI#getUpdateAction() */ protected class UpdateAction extends AbstractAction { @@ -512,28 +553,30 @@ public class BasicFileChooserUI extends FileChooserUI */ protected UpdateAction() { + // Nothing to do here. } /** - * DOCUMENT ME! + * NOT YET IMPLEMENTED. * - * @param e DOCUMENT ME! + * @param e the action event. */ public void actionPerformed(ActionEvent e) { + // FIXME: implement this } } - /** DOCUMENT ME! */ + /** The localised mnemonic for the cancel button. */ protected int cancelButtonMnemonic; - /** DOCUMENT ME! */ + /** The localised text for the cancel button. */ protected String cancelButtonText; - /** DOCUMENT ME! */ + /** The localised tool tip text for the cancel button. */ protected String cancelButtonToolTipText; - /** DOCUMENT ME! */ + /** An icon representing a computer. */ protected Icon computerIcon = new Icon() { public int getIconHeight() @@ -548,10 +591,11 @@ public class BasicFileChooserUI extends FileChooserUI public void paintIcon(Component c, Graphics g, int x, int y) { + // FIXME: is this not implemented, or is the icon intentionally blank? } }; - /** DOCUMENT ME! */ + /** An icon for the "details view" button. */ protected Icon detailsViewIcon = new Icon() { public int getIconHeight() @@ -580,7 +624,7 @@ public class BasicFileChooserUI extends FileChooserUI } }; - /** DOCUMENT ME! */ + /** An icon representing a directory. */ protected Icon directoryIcon = new Icon() { public int getIconHeight() @@ -619,16 +663,16 @@ public class BasicFileChooserUI extends FileChooserUI } }; - /** DOCUMENT ME! */ + /** The localised Mnemonic for the open button. */ protected int directoryOpenButtonMnemonic; - /** DOCUMENT ME! */ + /** The localised text for the open button. */ protected String directoryOpenButtonText; - /** DOCUMENT ME! */ + /** The localised tool tip text for the open button. */ protected String directoryOpenButtonToolTipText; - /** DOCUMENT ME! */ + /** An icon representing a file. */ protected Icon fileIcon = new Icon() { public int getIconHeight() @@ -668,7 +712,7 @@ public class BasicFileChooserUI extends FileChooserUI } }; - /** DOCUMENT ME! */ + /** An icon representing a floppy drive. */ protected Icon floppyDriveIcon = new Icon() { public int getIconHeight() @@ -683,10 +727,11 @@ public class BasicFileChooserUI extends FileChooserUI public void paintIcon(Component c, Graphics g, int x, int y) { + // FIXME: is this not implemented, or is the icon intentionally blank? } }; - /** DOCUMENT ME! */ + /** An icon representing a hard drive. */ protected Icon hardDriveIcon = new Icon() { public int getIconHeight() @@ -701,19 +746,20 @@ public class BasicFileChooserUI extends FileChooserUI public void paintIcon(Component c, Graphics g, int x, int y) { + // FIXME: is this not implemented, or is the icon intentionally blank? } }; - /** DOCUMENT ME! */ + /** The localised mnemonic for the "help" button. */ protected int helpButtonMnemonic; - /** DOCUMENT ME! */ + /** The localised text for the "help" button. */ protected String helpButtonText; - /** DOCUMENT ME! */ + /** The localised tool tip text for the help button. */ protected String helpButtonToolTipText; - /** DOCUMENT ME! */ + /** An icon representing the user's home folder. */ protected Icon homeFolderIcon = new Icon() { public int getIconHeight() @@ -753,7 +799,7 @@ public class BasicFileChooserUI extends FileChooserUI } }; - /** DOCUMENT ME! */ + /** An icon for the "list view" button. */ protected Icon listViewIcon = new Icon() { public int getIconHeight() @@ -795,37 +841,37 @@ public class BasicFileChooserUI extends FileChooserUI } }; - /** DOCUMENT ME! */ + /** An icon for the "new folder" button. */ protected Icon newFolderIcon = directoryIcon; - /** DOCUMENT ME! */ + /** The localised mnemonic for the "open" button. */ protected int openButtonMnemonic; - /** DOCUMENT ME! */ + /** The localised text for the "open" button. */ protected String openButtonText; - /** DOCUMENT ME! */ + /** The localised tool tip text for the "open" button. */ protected String openButtonToolTipText; - /** DOCUMENT ME! */ + /** The localised mnemonic for the "save" button. */ protected int saveButtonMnemonic; - /** DOCUMENT ME! */ + /** The localised text for the "save" button. */ protected String saveButtonText; - /** DOCUMENT ME! */ + /** The localised tool tip text for the save button. */ protected String saveButtonToolTipText; - /** DOCUMENT ME! */ + /** The localised mnemonic for the "update" button. */ protected int updateButtonMnemonic; - /** DOCUMENT ME! */ + /** The localised text for the "update" button. */ protected String updateButtonText; - /** DOCUMENT ME! */ + /** The localised tool tip text for the "update" button. */ protected String updateButtonToolTipText; - /** DOCUMENT ME! */ + /** An icon for the "up folder" button. */ protected Icon upFolderIcon = new Icon() { public int getIconHeight() @@ -876,77 +922,84 @@ public class BasicFileChooserUI extends FileChooserUI // -- begin private, but package local since used in inner classes -- + /** The file chooser component represented by this UI delegate. */ JFileChooser filechooser; - /** DOCUMENT ME! */ + /** The file list. */ JList filelist; - /** DOCUMENT ME! */ + /** The combo box used to display/select file filters. */ JComboBox filters; - /** DOCUMENT ME! */ + /** The model for the directory list. */ BasicDirectoryModel model; - /** DOCUMENT ME! */ + /** The file filter for all files. */ FileFilter acceptAll = new AcceptAllFileFilter(); - /** DOCUMENT ME! */ + /** The default file view. */ FileView fv = new BasicFileView(); - /** DOCUMENT ME! */ + /** The icon size. */ static final int ICON_SIZE = 24; - /** DOCUMENT ME! */ + /** A combo box for display/selection of parent directories. */ JComboBox parents; - /** DOCUMENT ME! */ + /** The current file name. */ String filename; - /** DOCUMENT ME! */ + /** The accept (open/save) button. */ JButton accept; - /** DOCUMENT ME! */ + /** The cancel button. */ JButton cancel; - /** DOCUMENT ME! */ + /** The button to move up to the parent directory. */ JButton upFolderButton; - /** DOCUMENT ME! */ + /** The button to create a new directory. */ JButton newFolderButton; - /** DOCUMENT ME! */ + /** The button to move to the user's home directory. */ JButton homeFolderButton; - /** DOCUMENT ME! */ + /** An optional accessory panel. */ JPanel accessoryPanel; - /** DOCUMENT ME! */ + /** A property change listener. */ PropertyChangeListener propertyChangeListener; - /** DOCUMENT ME! */ + /** The text describing the filter for "all files". */ String acceptAllFileFilterText; - /** DOCUMENT ME! */ + /** The text describing a directory type. */ String dirDescText; - /** DOCUMENT ME! */ + /** The text describing a file type. */ String fileDescText; - /** DOCUMENT ME! */ + /** Is a directory selected? */ boolean dirSelected = false; - /** DOCUMENT ME! */ + /** The current directory. */ File currDir = null; + // FIXME: describe what is contained in the bottom panel + /** The bottom panel. */ JPanel bottomPanel; - - /** DOCUMENT ME! */ + + /** The close panel. */ JPanel closePanel; + /** Text box that displays file name */ + JTextField entry; + + /** Current parent path */ + String parentPath; + // -- end private -- - private class ListLabelRenderer - extends JLabel - implements ListCellRenderer + private class ListLabelRenderer extends JLabel implements ListCellRenderer { /** DOCUMENT ME! */ final Color selected = new Color(153, 204, 255); @@ -988,45 +1041,8 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Closes the dialog. */ - public class CBLabelRenderer extends JLabel implements ListCellRenderer - { - /** - * Creates a new CBLabelRenderer object. - */ - public CBLabelRenderer() - { - super(); - setOpaque(true); - } - - /** - * DOCUMENT ME! - * - * @param list DOCUMENT ME! - * @param value DOCUMENT ME! - * @param index DOCUMENT ME! - * @param isSelected DOCUMENT ME! - * @param cellHasFocus DOCUMENT ME! - * - * @return DOCUMENT ME! - */ - public Component getListCellRendererComponent(JList list, Object value, - int index, - boolean isSelected, - boolean cellHasFocus) - { - setHorizontalAlignment(SwingConstants.LEFT); - setIcon(directoryIcon); - setText(value.toString()); - setForeground(Color.BLACK); - setBackground(Color.WHITE); - - return this; - } - } - void closeDialog() { Window owner = SwingUtilities.windowForComponent(filechooser); @@ -1035,9 +1051,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * Creates a new BasicFileChooserUI object. + * Creates a new <code>BasicFileChooserUI</code> object. * - * @param b DOCUMENT ME! + * @param b the file chooser component. */ public BasicFileChooserUI(JFileChooser b) { @@ -1045,11 +1061,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns a UI delegate for the given component. * - * @param c DOCUMENT ME! + * @param c the component (should be a {@link JFileChooser}). * - * @return DOCUMENT ME! + * @return A new UI delegate. */ public static ComponentUI createUI(JComponent c) { @@ -1057,28 +1073,32 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! - * - * @param c DOCUMENT ME! + * Installs the UI for the specified component. + * + * @param c the component (should be a {@link JFileChooser}). */ public void installUI(JComponent c) { if (c instanceof JFileChooser) { - JFileChooser fc = (JFileChooser) c; - fc.resetChoosableFileFilters(); - createModel(); - clearIconCache(); - installDefaults(fc); - installComponents(fc); - installListeners(fc); + JFileChooser fc = (JFileChooser) c; + fc.resetChoosableFileFilters(); + createModel(); + clearIconCache(); + installDefaults(fc); + installComponents(fc); + installListeners(fc); + + Object path = filechooser.getCurrentDirectory(); + if (path != null) + parentPath = path.toString().substring(path.toString().lastIndexOf("/")); } } /** - * DOCUMENT ME! - * - * @param c DOCUMENT ME! + * Uninstalls this UI from the given component. + * + * @param c the component (should be a {@link JFileChooser}). */ public void uninstallUI(JComponent c) { @@ -1185,16 +1205,16 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates and install the subcomponents for the file chooser. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ public void installComponents(JFileChooser fc) { JLabel look = new JLabel("Look In:"); parents = new JComboBox(); - parents.setRenderer(new CBLabelRenderer()); + parents.setRenderer(new BasicComboBoxRenderer()); boxEntries(); look.setLabelFor(parents); JPanel parentsPanel = new JPanel(); @@ -1226,9 +1246,9 @@ public class BasicFileChooserUI extends FileChooserUI buttonPanel.add(detailsViewButton); JPanel topPanel = new JPanel(); - topPanel.setLayout(new java.awt.FlowLayout()); + parentsPanel.add(buttonPanel); + topPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT, 0, 0)); topPanel.add(parentsPanel); - topPanel.add(buttonPanel); accessoryPanel = new JPanel(); if (filechooser.getAccessory() != null) @@ -1260,7 +1280,7 @@ public class BasicFileChooserUI extends FileChooserUI JLabel fileNameLabel = new JLabel("File Name:"); JLabel fileTypesLabel = new JLabel("Files of Type:"); - JTextField entry = new JTextField(); + entry = new JTextField(); filters = new JComboBox(); filterEntries(); @@ -1309,9 +1329,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Uninstalls the components from the file chooser. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ public void uninstallComponents(JFileChooser fc) { @@ -1327,9 +1347,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Installs the listeners required by this UI delegate. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ protected void installListeners(JFileChooser fc) { @@ -1349,9 +1369,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Uninstalls the listeners previously installed by this UI delegate. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ protected void uninstallListeners(JFileChooser fc) { @@ -1360,9 +1380,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Installs the defaults for this UI delegate. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ protected void installDefaults(JFileChooser fc) { @@ -1371,9 +1391,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Uninstalls the defaults previously added by this UI delegate. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ protected void uninstallDefaults(JFileChooser fc) { @@ -1382,9 +1402,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Installs the icons for this UI delegate (NOT YET IMPLEMENTED). * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ protected void installIcons(JFileChooser fc) { @@ -1392,9 +1412,10 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Uninstalls the icons previously added by this UI delegate (NOT YET + * IMPLEMENTED). * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ protected void uninstallIcons(JFileChooser fc) { @@ -1402,9 +1423,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Installs the strings used by this UI delegate. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ protected void installStrings(JFileChooser fc) { @@ -1432,9 +1453,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Uninstalls the strings previously added by this UI delegate. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ protected void uninstallStrings(JFileChooser fc) { @@ -1460,7 +1481,7 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates a new directory model. */ protected void createModel() { @@ -1468,9 +1489,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the directory model. * - * @return DOCUMENT ME! + * @return The directory model. */ public BasicDirectoryModel getModel() { @@ -1478,115 +1499,131 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! - * - * @param fc DOCUMENT ME! - * - * @return DOCUMENT ME! + * Creates a listener to handle changes to the properties of the given + * file chooser component. + * + * @param fc the file chooser component. + * + * @return A new listener. */ public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) { return new PropertyChangeListener() + { + public void propertyChange(PropertyChangeEvent e) { - public void propertyChange(PropertyChangeEvent e) - { - // FIXME: Multiple file selection waiting on JList multiple selection bug. - if (e.getPropertyName().equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) - { - if (filechooser.getSelectedFile() == null) - setFileName(null); - else - setFileName(filechooser.getSelectedFile().toString()); - int index = -1; - File file = filechooser.getSelectedFile(); - for (index = 0; index < model.getSize(); index++) - if (((File) model.getElementAt(index)).equals(file)) - break; - if (index == -1) - return; - filelist.setSelectedIndex(index); - filelist.ensureIndexIsVisible(index); - filelist.revalidate(); - filelist.repaint(); - } - else if (e.getPropertyName().equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) - { - filelist.clearSelection(); - filelist.revalidate(); - filelist.repaint(); - setDirectorySelected(false); - setDirectory(filechooser.getCurrentDirectory()); - boxEntries(); - } - else if (e.getPropertyName().equals(JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) - || e.getPropertyName().equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) - filterEntries(); - else if (e.getPropertyName().equals(JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY) - || e.getPropertyName().equals(JFileChooser.DIALOG_TITLE_CHANGED_PROPERTY)) - { - Window owner = SwingUtilities.windowForComponent(filechooser); - if (owner instanceof JDialog) - ((JDialog) owner).setTitle(getDialogTitle(filechooser)); - accept.setText(getApproveButtonText(filechooser)); - accept.setToolTipText(getApproveButtonToolTipText(filechooser)); - accept.setMnemonic(getApproveButtonMnemonic(filechooser)); - } - else if (e.getPropertyName().equals(JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY)) - accept.setText(getApproveButtonText(filechooser)); - else if (e.getPropertyName().equals(JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY)) - accept.setToolTipText(getApproveButtonToolTipText(filechooser)); - else if (e.getPropertyName().equals(JFileChooser.APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY)) - accept.setMnemonic(getApproveButtonMnemonic(filechooser)); - else if (e.getPropertyName().equals(JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY)) - { - if (filechooser.getControlButtonsAreShown()) - { - GridBagConstraints c = new GridBagConstraints(); - c.gridy = 1; - bottomPanel.add(filters, c); - - c.fill = GridBagConstraints.BOTH; - c.gridy = 2; - c.anchor = GridBagConstraints.EAST; - bottomPanel.add(closePanel, c); - bottomPanel.revalidate(); - bottomPanel.repaint(); - bottomPanel.doLayout(); - } - else - bottomPanel.remove(closePanel); - } - else if (e.getPropertyName().equals(JFileChooser.ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY)) - { - if (filechooser.isAcceptAllFileFilterUsed()) - filechooser.addChoosableFileFilter(getAcceptAllFileFilter(filechooser)); - else - filechooser.removeChoosableFileFilter(getAcceptAllFileFilter(filechooser)); - } - else if (e.getPropertyName().equals(JFileChooser.ACCESSORY_CHANGED_PROPERTY)) - { - JComponent old = (JComponent) e.getOldValue(); - if (old != null) - getAccessoryPanel().remove(old); - JComponent newval = (JComponent) e.getNewValue(); - if (newval != null) - getAccessoryPanel().add(newval); - } - if (e.getPropertyName().equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY) - || e.getPropertyName().equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY) - || e.getPropertyName().equals(JFileChooser.FILE_HIDING_CHANGED_PROPERTY)) - rescanCurrentDirectory(filechooser); - - filechooser.revalidate(); - filechooser.repaint(); - } - }; + // FIXME: Multiple file selection waiting on JList multiple selection + // bug. + if (e.getPropertyName().equals( + JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) + { + if (filechooser.getSelectedFile() == null) + setFileName(null); + else + setFileName(filechooser.getSelectedFile().toString()); + int index = -1; + File file = filechooser.getSelectedFile(); + for (index = 0; index < model.getSize(); index++) + if (((File) model.getElementAt(index)).equals(file)) + break; + if (index == -1) + return; + filelist.setSelectedIndex(index); + filelist.ensureIndexIsVisible(index); + filelist.revalidate(); + filelist.repaint(); + } + else if (e.getPropertyName().equals( + JFileChooser.DIRECTORY_CHANGED_PROPERTY)) + { + filelist.clearSelection(); + filelist.revalidate(); + filelist.repaint(); + setDirectorySelected(false); + setDirectory(filechooser.getCurrentDirectory()); + boxEntries(); + } + else if (e.getPropertyName().equals( + JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) + || e.getPropertyName().equals( + JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) + filterEntries(); + else if (e.getPropertyName().equals( + JFileChooser.DIALOG_TYPE_CHANGED_PROPERTY) + || e.getPropertyName().equals( + JFileChooser.DIALOG_TITLE_CHANGED_PROPERTY)) + { + Window owner = SwingUtilities.windowForComponent(filechooser); + if (owner instanceof JDialog) + ((JDialog) owner).setTitle(getDialogTitle(filechooser)); + accept.setText(getApproveButtonText(filechooser)); + accept.setToolTipText(getApproveButtonToolTipText(filechooser)); + accept.setMnemonic(getApproveButtonMnemonic(filechooser)); + } + else if (e.getPropertyName().equals( + JFileChooser.APPROVE_BUTTON_TEXT_CHANGED_PROPERTY)) + accept.setText(getApproveButtonText(filechooser)); + else if (e.getPropertyName().equals( + JFileChooser.APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY)) + accept.setToolTipText(getApproveButtonToolTipText(filechooser)); + else if (e.getPropertyName().equals( + JFileChooser.APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY)) + accept.setMnemonic(getApproveButtonMnemonic(filechooser)); + else if (e.getPropertyName().equals( + JFileChooser.CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY)) + { + if (filechooser.getControlButtonsAreShown()) + { + GridBagConstraints c = new GridBagConstraints(); + c.gridy = 1; + bottomPanel.add(filters, c); + + c.fill = GridBagConstraints.BOTH; + c.gridy = 2; + c.anchor = GridBagConstraints.EAST; + bottomPanel.add(closePanel, c); + bottomPanel.revalidate(); + bottomPanel.repaint(); + bottomPanel.doLayout(); + } + else + bottomPanel.remove(closePanel); + } + else if (e.getPropertyName().equals( + JFileChooser.ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY)) + { + if (filechooser.isAcceptAllFileFilterUsed()) + filechooser.addChoosableFileFilter(getAcceptAllFileFilter(filechooser)); + else + filechooser.removeChoosableFileFilter(getAcceptAllFileFilter(filechooser)); + } + else if (e.getPropertyName().equals( + JFileChooser.ACCESSORY_CHANGED_PROPERTY)) + { + JComponent old = (JComponent) e.getOldValue(); + if (old != null) + getAccessoryPanel().remove(old); + JComponent newval = (JComponent) e.getNewValue(); + if (newval != null) + getAccessoryPanel().add(newval); + } + if (e.getPropertyName().equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY) + || e.getPropertyName().equals( + JFileChooser.FILE_FILTER_CHANGED_PROPERTY) + || e.getPropertyName().equals( + JFileChooser.FILE_HIDING_CHANGED_PROPERTY)) + rescanCurrentDirectory(filechooser); + + filechooser.revalidate(); + filechooser.repaint(); + } + }; } /** - * DOCUMENT ME! - * - * @return DOCUMENT ME! + * Returns the current file name. + * + * @return The current file name. */ public String getFileName() { @@ -1594,9 +1631,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the current directory name. * - * @return DOCUMENT ME! + * @return The directory name. + * + * @see #setDirectoryName(String) */ public String getDirectoryName() { @@ -1605,9 +1644,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Sets the file name. * - * @param filename DOCUMENT ME! + * @param filename the file name. + * + * @see #getFileName() */ public void setFileName(String filename) { @@ -1615,9 +1656,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Sets the directory name (NOT IMPLEMENTED). * - * @param dirname DOCUMENT ME! + * @param dirname the directory name. + * + * @see #getDirectoryName() */ public void setDirectoryName(String dirname) { @@ -1625,9 +1668,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Rescans the current directory. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. */ public void rescanCurrentDirectory(JFileChooser fc) { @@ -1636,10 +1679,10 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * NOT YET IMPLEMENTED. * - * @param fc DOCUMENT ME! - * @param f DOCUMENT ME! + * @param fc the file chooser. + * @param f the file. */ public void ensureFileIsVisible(JFileChooser fc, File f) { @@ -1647,9 +1690,10 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the {@link JFileChooser} component that this UI delegate + * represents. * - * @return DOCUMENT ME! + * @return The component represented by this UI delegate. */ public JFileChooser getFileChooser() { @@ -1657,9 +1701,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the optional accessory panel. * - * @return DOCUMENT ME! + * @return The optional accessory panel. */ public JPanel getAccessoryPanel() { @@ -1667,11 +1711,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates and returns an approve (open or save) button for the dialog. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. * - * @return DOCUMENT ME! + * @return The button. */ public JButton getApproveButton(JFileChooser fc) { @@ -1682,11 +1726,14 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the tool tip text for the approve (open/save) button. This first + * checks the file chooser to see if a value has been explicitly set - if + * not, a default value appropriate for the type of file chooser is + * returned. * - * @param fc DOCUMENT ME! + * @param fc the file chooser. * - * @return DOCUMENT ME! + * @return The tool tip text. */ public String getApproveButtonToolTipText(JFileChooser fc) { @@ -1699,7 +1746,7 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Clears the icon cache. */ public void clearIconCache() { @@ -1708,11 +1755,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates a new listener to handle selections in the file list. * - * @param fc DOCUMENT ME! + * @param fc the file chooser component. * - * @return DOCUMENT ME! + * @return A new instance of {@link SelectionListener}. */ public ListSelectionListener createListSelectionListener(JFileChooser fc) { @@ -1720,12 +1767,12 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates a new listener to handle double-click events. * - * @param fc DOCUMENT ME! - * @param list DOCUMENT ME! + * @param fc the file chooser component. + * @param list the list. * - * @return DOCUMENT ME! + * @return A new instance of {@link DoubleClickListener}. */ protected MouseListener createDoubleClickListener(JFileChooser fc, JList list) { @@ -1733,9 +1780,10 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns <code>true</code> if a directory is selected, and + * <code>false</code> otherwise. * - * @return DOCUMENT ME! + * @return A boolean. */ protected boolean isDirectorySelected() { @@ -1743,9 +1791,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Sets the flag that indicates whether the current directory is selected. * - * @param selected DOCUMENT ME! + * @param selected the new flag value. */ protected void setDirectorySelected(boolean selected) { @@ -1753,9 +1801,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the current directory. * - * @return DOCUMENT ME! + * @return The current directory. */ protected File getDirectory() { @@ -1763,9 +1811,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Sets the current directory. * - * @param f DOCUMENT ME! + * @param f the directory. */ protected void setDirectory(File f) { @@ -1773,11 +1821,11 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the "accept all" file filter. * - * @param fc DOCUMENT ME! + * @param fc the file chooser component. * - * @return DOCUMENT ME! + * @return The "accept all" file filter. */ public FileFilter getAcceptAllFileFilter(JFileChooser fc) { @@ -1785,25 +1833,29 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the file view for the file chooser. This returns either the + * file view that has been explicitly set for the {@link JFileChooser}, or + * a default file view. * - * @param fc DOCUMENT ME! + * @param fc the file chooser component. * - * @return DOCUMENT ME! + * @return The file view. + * + * @see JFileChooser#getFileView() */ public FileView getFileView(JFileChooser fc) { - if (fc.getFileView() != null) - return fc.getFileView(); return fv; } /** - * DOCUMENT ME! + * Returns the dialog title. * - * @param fc DOCUMENT ME! + * @param fc the file chooser (<code>null</code> not permitted). * - * @return DOCUMENT ME! + * @return The dialog title. + * + * @see JFileChooser#getDialogTitle() */ public String getDialogTitle(JFileChooser fc) { @@ -1828,11 +1880,13 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the approve button mnemonic. * - * @param fc DOCUMENT ME! + * @param fc the file chooser (<code>null</code> not permitted). * - * @return DOCUMENT ME! + * @return The approve button mnemonic. + * + * @see JFileChooser#getApproveButtonMnemonic() */ public int getApproveButtonMnemonic(JFileChooser fc) { @@ -1845,11 +1899,13 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Returns the approve button text. * - * @param fc DOCUMENT ME! + * @param fc the file chooser (<code>null</code> not permitted). * - * @return DOCUMENT ME! + * @return The approve button text. + * + * @see JFileChooser#getApproveButtonText() */ public String getApproveButtonText(JFileChooser fc) { @@ -1862,9 +1918,10 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates and returns a new action that will be used with the "new folder" + * button. * - * @return DOCUMENT ME! + * @return A new instance of {@link GoHomeAction}. */ public Action getNewFolderAction() { @@ -1872,9 +1929,10 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates and returns a new action that will be used with the "home folder" + * button. * - * @return DOCUMENT ME! + * @return A new instance of {@link GoHomeAction}. */ public Action getGoHomeAction() { @@ -1882,9 +1940,10 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates and returns a new action that will be used with the "up folder" + * button. * - * @return DOCUMENT ME! + * @return A new instance of {@link ChangeToParentDirectoryAction}. */ public Action getChangeToParentDirectoryAction() { @@ -1892,9 +1951,10 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates and returns a new action that will be used with the "approve" + * button. * - * @return DOCUMENT ME! + * @return A new instance of {@link ApproveSelectionAction}. */ public Action getApproveSelectionAction() { @@ -1902,9 +1962,10 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates and returns a new action that will be used with the "cancel" + * button. * - * @return DOCUMENT ME! + * @return A new instance of {@link CancelSelectionAction}. */ public Action getCancelSelectionAction() { @@ -1912,9 +1973,9 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Creates and returns a new instance of {@link UpdateAction}. * - * @return DOCUMENT ME! + * @return An action. */ public Action getUpdateAction() { diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java index 3abd76fe2d3..9c7f1c4c5d1 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java @@ -1,5 +1,5 @@ /* BasicFormattedTextFieldUI.java - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,6 +39,7 @@ exception statement from your version. */ package javax.swing.plaf.basic; import javax.swing.JComponent; +import javax.swing.UIDefaults; import javax.swing.plaf.ComponentUI; /** @@ -48,6 +49,7 @@ public class BasicFormattedTextFieldUI extends BasicTextFieldUI { public BasicFormattedTextFieldUI() { + // Nothing to do here. } public static ComponentUI createUI(JComponent c) @@ -55,6 +57,11 @@ public class BasicFormattedTextFieldUI extends BasicTextFieldUI return new BasicFormattedTextFieldUI(); } + /** + * Returns the prefix for entries in the {@link UIDefaults} table. + * + * @return "FormattedTextField" + */ protected String getPropertyPrefix() { return "FormattedTextField"; diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java b/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java index 757ac47c903..068de345bec 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java @@ -71,6 +71,7 @@ public class BasicGraphicsUtils */ public BasicGraphicsUtils() { + // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicIconFactory.java b/libjava/classpath/javax/swing/plaf/basic/BasicIconFactory.java index 56a67b02935..6debd649509 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicIconFactory.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicIconFactory.java @@ -41,7 +41,6 @@ package javax.swing.plaf.basic; import java.awt.Color; import java.awt.Component; import java.awt.Graphics; -import java.awt.Polygon; import java.io.Serializable; import javax.swing.Icon; @@ -241,34 +240,33 @@ public class BasicIconFactory implements Serializable { return new DummyIcon(); } + + /** + * Returns a new instance of a 4 x 8 icon showing a small black triangle that + * points to the right. This is displayed in menu items that have a + * sub menu. + * + * @return The icon. + */ public static Icon getMenuArrowIcon() { return new Icon() { public int getIconHeight() { - return 12; + return 8; } - public int getIconWidth() { - return 12; + return 4; } - public void paintIcon(Component c, Graphics g, int x, int y) { - g.translate(x, y); - Color saved = g.getColor(); - g.setColor(Color.BLACK); - - g.fillPolygon(new Polygon(new int[] { 3, 9, 3 }, - new int[] { 2, 6, 10 }, - 3)); - + for (int i = 0; i < 4; i++) + g.drawLine(x + i, y + i, x + i, y + 7 - i); g.setColor(saved); - g.translate(-x, -y); } }; } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java index cc262948ded..73d3e6173d3 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java @@ -84,6 +84,14 @@ public class BasicInternalFrameTitlePane extends JComponent public class CloseAction extends AbstractAction { /** + * Creates a new action. + */ + public CloseAction() + { + super("Close"); + } + + /** * This method is called when something closes the JInternalFrame. * * @param e The ActionEvent. @@ -92,13 +100,14 @@ public class BasicInternalFrameTitlePane extends JComponent { if (frame.isClosable()) { - try - { - frame.setClosed(true); - } - catch (PropertyVetoException pve) - { - } + try + { + frame.setClosed(true); + } + catch (PropertyVetoException pve) + { + // We do nothing if the attempt has been vetoed. + } } } } @@ -113,6 +122,14 @@ public class BasicInternalFrameTitlePane extends JComponent public class IconifyAction extends AbstractAction { /** + * Creates a new action. + */ + public IconifyAction() + { + super("Minimize"); + } + + /** * This method is called when the user wants to iconify the * JInternalFrame. * @@ -122,13 +139,14 @@ public class BasicInternalFrameTitlePane extends JComponent { if (frame.isIconifiable() && ! frame.isIcon()) { - try - { - frame.setIcon(true); - } - catch (PropertyVetoException pve) - { - } + try + { + frame.setIcon(true); + } + catch (PropertyVetoException pve) + { + // We do nothing if the attempt has been vetoed. + } } } } @@ -143,6 +161,13 @@ public class BasicInternalFrameTitlePane extends JComponent public class MaximizeAction extends AbstractAction { /** + * Creates a new action. + */ + public MaximizeAction() + { + super("Maximize"); + } + /** * This method is called when the user wants to maximize the * JInternalFrame. * @@ -152,13 +177,14 @@ public class BasicInternalFrameTitlePane extends JComponent { try { - if (frame.isMaximizable() && ! frame.isMaximum()) - frame.setMaximum(true); - else if (frame.isMaximum()) - frame.setMaximum(false); + if (frame.isMaximizable() && ! frame.isMaximum()) + frame.setMaximum(true); + else if (frame.isMaximum()) + frame.setMaximum(false); } catch (PropertyVetoException pve) { + // We do nothing if the attempt has been vetoed. } } } @@ -173,6 +199,13 @@ public class BasicInternalFrameTitlePane extends JComponent public class MoveAction extends AbstractAction { /** + * Creates a new action. + */ + public MoveAction() + { + super("Move"); + } + /** * This method is called when the user wants to drag the JInternalFrame. * * @param e The ActionEvent. @@ -194,6 +227,13 @@ public class BasicInternalFrameTitlePane extends JComponent public class RestoreAction extends AbstractAction { /** + * Creates a new action. + */ + public RestoreAction() + { + super("Restore"); + } + /** * This method is called when the user wants to restore the * JInternalFrame. * @@ -203,13 +243,14 @@ public class BasicInternalFrameTitlePane extends JComponent { if (frame.isMaximum()) { - try - { - frame.setMaximum(false); - } - catch (PropertyVetoException pve) - { - } + try + { + frame.setMaximum(false); + } + catch (PropertyVetoException pve) + { + // We do nothing if the attempt has been vetoed. + } } } } @@ -224,6 +265,13 @@ public class BasicInternalFrameTitlePane extends JComponent public class SizeAction extends AbstractAction { /** + * Creates a new action. + */ + public SizeAction() + { + super("Size"); + } + /** * This method is called when the user wants to resize the JInternalFrame. * * @param e The ActionEvent. @@ -377,24 +425,26 @@ public class BasicInternalFrameTitlePane extends JComponent int loc = width + insets.left - 1; int top = insets.top + 1; - int buttonWidth = height - 2; int buttonHeight = height - 4; if (closeButton.isVisible()) { - loc -= buttonWidth + 2; - closeButton.setBounds(loc, top, buttonWidth, buttonHeight); + int buttonWidth = closeIcon.getIconWidth(); + loc -= buttonWidth + 2; + closeButton.setBounds(loc, top, buttonWidth, buttonHeight); } if (maxButton.isVisible()) { - loc -= buttonWidth + 2; - maxButton.setBounds(loc, top, buttonWidth, buttonHeight); + int buttonWidth = maxIcon.getIconWidth(); + loc -= buttonWidth + 2; + maxButton.setBounds(loc, top, buttonWidth, buttonHeight); } if (iconButton.isVisible()) { - loc -= buttonWidth + 2; - iconButton.setBounds(loc, top, buttonWidth, buttonHeight); + int buttonWidth = iconIcon.getIconWidth(); + loc -= buttonWidth + 2; + iconButton.setBounds(loc, top, buttonWidth, buttonHeight); } if (title != null) @@ -435,6 +485,7 @@ public class BasicInternalFrameTitlePane extends JComponent */ public void removeLayoutComponent(Component c) { + // Nothing to do here. } } @@ -466,6 +517,7 @@ public class BasicInternalFrameTitlePane extends JComponent // These buttons cannot be given focus. return false; } + } /** The action command for the Close action. */ @@ -522,6 +574,9 @@ public class BasicInternalFrameTitlePane extends JComponent /** The icon displayed in the iconify button. */ protected Icon iconIcon = BasicIconFactory.createEmptyFrameIcon(); + /** The icon displayed in the close button. */ + protected Icon closeIcon; + /** The JInternalFrame that this TitlePane is used in. */ protected JInternalFrame frame; @@ -645,7 +700,7 @@ public class BasicInternalFrameTitlePane extends JComponent */ protected void installListeners() { - propertyChangeListener = new PropertyChangeHandler(); + propertyChangeListener = createPropertyChangeListener(); frame.addPropertyChangeListener(propertyChangeListener); } @@ -663,14 +718,17 @@ public class BasicInternalFrameTitlePane extends JComponent */ protected void installDefaults() { - // FIXME: move icons to defaults. UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - setFont(defaults.getFont("InternalFrame.titleFont")); + title.setFont(defaults.getFont("InternalFrame.titleFont")); selectedTextColor = defaults.getColor("InternalFrame.activeTitleForeground"); selectedTitleColor = defaults.getColor("InternalFrame.activeTitleBackground"); notSelectedTextColor = defaults.getColor("InternalFrame.inactiveTitleForeground"); notSelectedTitleColor = defaults.getColor("InternalFrame.inactiveTitleBackground"); + + closeIcon = UIManager.getIcon("InternalFrame.closeIcon"); + iconIcon = UIManager.getIcon("InternalFrame.iconifyIcon"); + maxIcon = UIManager.getIcon("InternalFrame.maximizeIcon"); } /** @@ -683,6 +741,10 @@ public class BasicInternalFrameTitlePane extends JComponent selectedTitleColor = null; notSelectedTextColor = null; notSelectedTitleColor = null; + + closeIcon = null; + iconIcon = null; + maxIcon = null; } /** @@ -691,12 +753,15 @@ public class BasicInternalFrameTitlePane extends JComponent protected void createButtons() { closeButton = new PaneButton(closeAction); + closeButton.setText(null); if (!frame.isClosable()) closeButton.setVisible(false); iconButton = new PaneButton(iconifyAction); + iconButton.setText(null); if (!frame.isIconifiable()) iconButton.setVisible(false); maxButton = new PaneButton(maximizeAction); + maxButton.setText(null); if (!frame.isMaximizable()) maxButton.setVisible(false); } @@ -706,15 +771,12 @@ public class BasicInternalFrameTitlePane extends JComponent */ protected void setButtonIcons() { - Icon icon = UIManager.getIcon("InternalFrame.closeIcon"); - if (icon != null) - closeButton.setIcon(icon); - icon = UIManager.getIcon("InternalFrame.iconifyIcon"); - if (icon != null) - iconButton.setIcon(icon); - icon = UIManager.getIcon("InternalFrame.maximizeIcon"); - if (icon != null) - maxButton.setIcon(icon); + if (closeIcon != null && closeButton != null) + closeButton.setIcon(closeIcon); + if (iconIcon != null && iconButton != null) + iconButton.setIcon(iconIcon); + if (maxIcon != null && maxButton != null) + maxButton.setIcon(maxIcon); } /** @@ -816,11 +878,12 @@ public class BasicInternalFrameTitlePane extends JComponent public void paintComponent(Graphics g) { paintTitleBackground(g); - Font f = g.getFont(); - FontMetrics fm = g.getFontMetrics(f); if (frame.getTitle() != null && title != null) { Color saved = g.getColor(); + Font f = title.getFont(); + g.setFont(f); + FontMetrics fm = g.getFontMetrics(f); if (frame.isSelected()) g.setColor(selectedTextColor); else diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java index 8f76ea0cc19..d9dadda688a 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java @@ -56,20 +56,17 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyVetoException; import java.beans.VetoableChangeListener; -import javax.swing.BorderFactory; import javax.swing.DefaultDesktopManager; import javax.swing.DesktopManager; import javax.swing.JComponent; import javax.swing.JDesktopPane; import javax.swing.JInternalFrame; import javax.swing.KeyStroke; +import javax.swing.LookAndFeel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.border.AbstractBorder; -import javax.swing.border.BevelBorder; -import javax.swing.border.Border; import javax.swing.event.InternalFrameEvent; import javax.swing.event.InternalFrameListener; import javax.swing.event.MouseInputAdapter; @@ -202,67 +199,66 @@ public class BasicInternalFrameUI extends InternalFrameUI */ public void mouseDragged(MouseEvent e) { - // If the frame is maximized, there is nothing that + // If the frame is maximized, there is nothing that // can be dragged around. if (frame.isMaximum()) - return; + return; DesktopManager dm = getDesktopManager(); Rectangle b = frame.getBounds(); Dimension min = frame.getMinimumSize(); if (min == null) - min = new Dimension(0, 0); + min = new Dimension(0, 0); Insets insets = frame.getInsets(); int x = e.getX(); int y = e.getY(); if (e.getSource() == frame && frame.isResizable()) { - switch (direction) - { - case NORTH: - cacheRect.setBounds(b.x, - Math.min(b.y + y, b.y + b.height - - min.height), b.width, b.height - - y); - break; - case NORTH_EAST: - cacheRect.setBounds(b.x, - Math.min(b.y + y, b.y + b.height - - min.height), x, b.height - y); - break; - case EAST: - cacheRect.setBounds(b.x, b.y, x, b.height); - break; - case SOUTH_EAST: - cacheRect.setBounds(b.x, b.y, x, y); - break; - case SOUTH: - cacheRect.setBounds(b.x, b.y, b.width, y); - break; - case SOUTH_WEST: - cacheRect.setBounds(Math.min(b.x + x, b.x + b.width - min.width), - b.y, b.width - x, y); - break; - case WEST: - cacheRect.setBounds(Math.min(b.x + x, b.x + b.width - min.width), - b.y, b.width - x, b.height); - break; - case NORTH_WEST: - cacheRect.setBounds(Math.min(b.x + x, b.x + b.width - min.width), - Math.min(b.y + y, b.y + b.height - - min.height), b.width - x, - b.height - y); - break; - } - dm.resizeFrame(frame, cacheRect.x, cacheRect.y, - Math.max(min.width, cacheRect.width), - Math.max(min.height, cacheRect.height)); + switch (direction) + { + case NORTH: + cacheRect.setBounds(b.x, Math.min(b.y + y, b.y + b.height + - min.height), + b.width, b.height - y); + break; + case NORTH_EAST: + cacheRect.setBounds(b.x, Math.min(b.y + y, b.y + b.height + - min.height), x, + b.height - y); + break; + case EAST: + cacheRect.setBounds(b.x, b.y, x, b.height); + break; + case SOUTH_EAST: + cacheRect.setBounds(b.x, b.y, x, y); + break; + case SOUTH: + cacheRect.setBounds(b.x, b.y, b.width, y); + break; + case SOUTH_WEST: + cacheRect.setBounds(Math.min(b.x + x, b.x + b.width - min.width), + b.y, b.width - x, y); + break; + case WEST: + cacheRect.setBounds(Math.min(b.x + x, b.x + b.width - min.width), + b.y, b.width - x, b.height); + break; + case NORTH_WEST: + cacheRect.setBounds( + Math.min(b.x + x, b.x + b.width - min.width), + Math.min(b.y + y, b.y + b.height - min.height), + b.width - x, b.height - y); + break; + } + dm.resizeFrame(frame, cacheRect.x, cacheRect.y, + Math.max(min.width, cacheRect.width), + Math.max(min.height, cacheRect.height)); } else if (e.getSource() == titlePane) { - Rectangle fBounds = frame.getBounds(); + Rectangle fBounds = frame.getBounds(); - dm.dragFrame(frame, e.getX() - xOffset + b.x, - e.getY() - yOffset + b.y); + dm.dragFrame(frame, e.getX() - xOffset + b.x, e.getY() - yOffset + + b.y); } } @@ -304,17 +300,17 @@ public class BasicInternalFrameUI extends InternalFrameUI if (e.getSource() == frame && frame.isResizable()) { - direction = sectionOfClick(x, y); - dm.beginResizingFrame(frame, direction); + direction = sectionOfClick(x, y); + dm.beginResizingFrame(frame, direction); } else if (e.getSource() == titlePane) { - Rectangle tBounds = titlePane.getBounds(); + Rectangle tBounds = titlePane.getBounds(); - xOffset = e.getX() - tBounds.x + insets.left; - yOffset = e.getY() - tBounds.y + insets.top; + xOffset = e.getX() - tBounds.x + insets.left; + yOffset = e.getY() - tBounds.y + insets.top; - dm.beginDraggingFrame(frame); + dm.beginDraggingFrame(frame); } } @@ -329,9 +325,9 @@ public class BasicInternalFrameUI extends InternalFrameUI xOffset = 0; yOffset = 0; if (e.getSource() == frame && frame.isResizable()) - dm.endResizingFrame(frame); + dm.endResizingFrame(frame); else if (e.getSource() == titlePane) - dm.endDraggingFrame(frame); + dm.endDraggingFrame(frame); } /** @@ -348,21 +344,21 @@ public class BasicInternalFrameUI extends InternalFrameUI Insets insets = frame.getInsets(); Rectangle b = frame.getBounds(); if (x < insets.left && y < insets.top) - return NORTH_WEST; + return NORTH_WEST; else if (x > b.width - insets.right && y < insets.top) - return NORTH_EAST; + return NORTH_EAST; else if (x > b.width - insets.right && y > b.height - insets.bottom) - return SOUTH_EAST; + return SOUTH_EAST; else if (x < insets.left && y > b.height - insets.bottom) - return SOUTH_WEST; + return SOUTH_WEST; else if (y < insets.top) - return NORTH; + return NORTH; else if (x < insets.left) - return WEST; + return WEST; else if (y > b.height - insets.bottom) - return SOUTH; + return SOUTH; else if (x > b.width - insets.right) - return EAST; + return EAST; return -1; } @@ -377,8 +373,9 @@ public class BasicInternalFrameUI extends InternalFrameUI { /** * This method is called when the JDesktopPane is hidden. - * - * @param e The ComponentEvent fired. + * + * @param e + * The ComponentEvent fired. */ public void componentHidden(ComponentEvent e) { @@ -387,8 +384,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method is called when the JDesktopPane is moved. - * - * @param e The ComponentEvent fired. + * + * @param e + * The ComponentEvent fired. */ public void componentMoved(ComponentEvent e) { @@ -397,22 +395,23 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method is called when the JDesktopPane is resized. - * - * @param e The ComponentEvent fired. + * + * @param e + * The ComponentEvent fired. */ public void componentResized(ComponentEvent e) { if (frame.isMaximum()) { - JDesktopPane pane = (JDesktopPane) e.getSource(); - Insets insets = pane.getInsets(); - Rectangle bounds = pane.getBounds(); - - frame.setBounds(bounds.x + insets.left, bounds.y + insets.top, - bounds.width - insets.left - insets.right, - bounds.height - insets.top - insets.bottom); - frame.revalidate(); - frame.repaint(); + JDesktopPane pane = (JDesktopPane) e.getSource(); + Insets insets = pane.getInsets(); + Rectangle bounds = pane.getBounds(); + + frame.setBounds(bounds.x + insets.left, bounds.y + insets.top, + bounds.width - insets.left - insets.right, + bounds.height - insets.top - insets.bottom); + frame.revalidate(); + frame.repaint(); } // Sun also resizes the icons. but it doesn't seem to do anything. @@ -420,8 +419,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method is called when the JDesktopPane is shown. - * - * @param e The ComponentEvent fired. + * + * @param e + * The ComponentEvent fired. */ public void componentShown(ComponentEvent e) { @@ -435,21 +435,25 @@ public class BasicInternalFrameUI extends InternalFrameUI public class InternalFrameLayout implements LayoutManager { /** - * This method is called when the given Component is added to the + * This method is called when the given Component is added to the * JInternalFrame. - * - * @param name The name of the Component. - * @param c The Component added. + * + * @param name + * The name of the Component. + * @param c + * The Component added. */ public void addLayoutComponent(String name, Component c) { + // Nothing to do here. } /** * This method is used to set the bounds of the children of the * JInternalFrame. - * - * @param c The Container to lay out. + * + * @param c + * The Container to lay out. */ public void layoutContainer(Container c) { @@ -468,38 +472,38 @@ public class BasicInternalFrameUI extends InternalFrameUI if (northPane != null) { - Dimension nDims = northPane.getPreferredSize(); - nh = Math.min(nDims.height, dims.height); + Dimension nDims = northPane.getPreferredSize(); + nh = Math.min(nDims.height, dims.height); - northPane.setBounds(insets.left, insets.top, dims.width, nh); + northPane.setBounds(insets.left, insets.top, dims.width, nh); } if (southPane != null) { - Dimension sDims = southPane.getPreferredSize(); - sh = Math.min(sDims.height, dims.height - nh); + Dimension sDims = southPane.getPreferredSize(); + sh = Math.min(sDims.height, dims.height - nh); - southPane.setBounds(insets.left, insets.top + dims.height - sh, - dims.width, sh); + southPane.setBounds(insets.left, insets.top + dims.height - sh, + dims.width, sh); } int remHeight = dims.height - sh - nh; if (westPane != null) { - Dimension wDims = westPane.getPreferredSize(); - ww = Math.min(dims.width, wDims.width); + Dimension wDims = westPane.getPreferredSize(); + ww = Math.min(dims.width, wDims.width); - westPane.setBounds(insets.left, insets.top + nh, ww, remHeight); + westPane.setBounds(insets.left, insets.top + nh, ww, remHeight); } if (eastPane != null) { - Dimension eDims = eastPane.getPreferredSize(); - ew = Math.min(eDims.width, dims.width - ww); + Dimension eDims = eastPane.getPreferredSize(); + ew = Math.min(eDims.width, dims.width - ww); - eastPane.setBounds(insets.left + dims.width - ew, insets.top + nh, - ew, remHeight); + eastPane.setBounds(insets.left + dims.width - ew, insets.top + nh, + ew, remHeight); } int remWidth = dims.width - ww - ew; @@ -510,9 +514,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method returns the minimum layout size. - * - * @param c The Container to find a minimum layout size for. - * + * + * @param c + * The Container to find a minimum layout size for. * @return The minimum dimensions for the JInternalFrame. */ public Dimension minimumLayoutSize(Container c) @@ -522,9 +526,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method returns the maximum layout size. - * - * @param c The Container to find a maximum layout size for. - * + * + * @param c + * The Container to find a maximum layout size for. * @return The maximum dimensions for the JInternalFrame. */ public Dimension maximumLayoutSize(Container c) @@ -534,9 +538,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * Th8is method returns the preferred layout size. - * - * @param c The Container to find a preferred layout size for. - * + * + * @param c + * The Container to find a preferred layout size for. * @return The preferred dimensions for the JInternalFrame. */ public Dimension preferredLayoutSize(Container c) @@ -546,10 +550,11 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * DOCUMENT ME! - * - * @param c DOCUMENT ME! - * @param min DOCUMENT ME! - * + * + * @param c + * DOCUMENT ME! + * @param min + * DOCUMENT ME! * @return DOCUMENT ME! */ private Dimension getSize(Container c, boolean min) @@ -558,7 +563,7 @@ public class BasicInternalFrameUI extends InternalFrameUI Dimension contentDims = frame.getContentPane().getPreferredSize(); if (min) - contentDims.width = contentDims.height = 0; + contentDims.width = contentDims.height = 0; int nWidth = 0; int nHeight = 0; int sWidth = 0; @@ -571,42 +576,42 @@ public class BasicInternalFrameUI extends InternalFrameUI if (northPane != null) { - dims = northPane.getPreferredSize(); - if (dims != null) - { - nWidth = dims.width; - nHeight = dims.height; - } + dims = northPane.getPreferredSize(); + if (dims != null) + { + nWidth = dims.width; + nHeight = dims.height; + } } if (southPane != null) { - dims = southPane.getPreferredSize(); - if (dims != null) - { - sWidth = dims.width; - sHeight = dims.height; - } + dims = southPane.getPreferredSize(); + if (dims != null) + { + sWidth = dims.width; + sHeight = dims.height; + } } if (eastPane != null) { - dims = eastPane.getPreferredSize(); - if (dims != null) - { - sWidth = dims.width; - sHeight = dims.height; - } + dims = eastPane.getPreferredSize(); + if (dims != null) + { + sWidth = dims.width; + sHeight = dims.height; + } } if (westPane != null) { - dims = westPane.getPreferredSize(); - if (dims != null) - { - wWidth = dims.width; - wHeight = dims.height; - } + dims = westPane.getPreferredSize(); + if (dims != null) + { + wWidth = dims.width; + wHeight = dims.height; + } } int width = Math.max(sWidth, nWidth); @@ -630,6 +635,7 @@ public class BasicInternalFrameUI extends InternalFrameUI */ public void removeLayoutComponent(Component c) { + // Nothing to do here. } } @@ -657,8 +663,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method is called when the mouse enters the glass pane. - * - * @param e The MouseEvent. + * + * @param e + * The MouseEvent. */ public void mouseEntered(MouseEvent e) { @@ -667,8 +674,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method is called when the mouse is clicked on the glass pane. - * - * @param e The MouseEvent. + * + * @param e + * The MouseEvent. */ public void mouseClicked(MouseEvent e) { @@ -677,8 +685,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method is called when the mouse is dragged in the glass pane. - * - * @param e The MouseEvent. + * + * @param e + * The MouseEvent. */ public void mouseDragged(MouseEvent e) { @@ -687,8 +696,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method is called when the mouse exits the glass pane. - * - * @param e The MouseEvent. + * + * @param e + * The MouseEvent. */ public void mouseExited(MouseEvent e) { @@ -697,8 +707,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method is called when the mouse is moved in the glass pane. - * - * @param e The MouseEvent. + * + * @param e + * The MouseEvent. */ public void mouseMoved(MouseEvent e) { @@ -706,9 +717,10 @@ public class BasicInternalFrameUI extends InternalFrameUI } /** - * This method is called when the mouse is pressed in the glass pane. - * - * @param e The MouseEvent. + * This method is called when the mouse is pressed in the glass pane. + * + * @param e + * The MouseEvent. */ public void mousePressed(MouseEvent e) { @@ -717,9 +729,10 @@ public class BasicInternalFrameUI extends InternalFrameUI } /** - * This method is called when the mouse is released in the glass pane. - * - * @param e The MouseEvent. + * This method is called when the mouse is released in the glass pane. + * + * @param e + * The MouseEvent. */ public void mouseReleased(MouseEvent e) { @@ -727,10 +740,10 @@ public class BasicInternalFrameUI extends InternalFrameUI } /** - * This method acquires a candidate component to dispatch the MouseEvent - * to. - * - * @param me The MouseEvent to acquire a component for. + * This method acquires a candidate component to dispatch the MouseEvent to. + * + * @param me + * The MouseEvent to acquire a component for. */ private void acquireComponentForMouseEvent(MouseEvent me) { @@ -738,134 +751,137 @@ public class BasicInternalFrameUI extends InternalFrameUI int y = me.getY(); // Find the candidate which should receive this event. - Component parent = frame.getContentPane(); + Component parent = frame.getLayeredPane(); if (parent == null) - return; + return; Component candidate = null; Point p = me.getPoint(); while (candidate == null && parent != null) { - candidate = SwingUtilities.getDeepestComponentAt(parent, p.x, p.y); - if (candidate == null) - { - p = SwingUtilities.convertPoint(parent, p.x, p.y, - parent.getParent()); - parent = parent.getParent(); - } + candidate = SwingUtilities.getDeepestComponentAt(parent, p.x, p.y); + if (candidate == null) + { + p = SwingUtilities.convertPoint(parent, p.x, p.y, + parent.getParent()); + parent = parent.getParent(); + } } // If the only candidate we found was the native container itself, - // don't dispatch any event at all. We only care about the lightweight + // don't dispatch any event at all. We only care about the lightweight // children here. if (candidate == frame.getContentPane()) - candidate = null; + candidate = null; // If our candidate is new, inform the old target we're leaving. if (lastComponentEntered != null && lastComponentEntered.isShowing() && lastComponentEntered != candidate) { - Point tp = SwingUtilities.convertPoint(frame.getContentPane(), x, y, - lastComponentEntered); - MouseEvent exited = new MouseEvent(lastComponentEntered, - MouseEvent.MOUSE_EXITED, - me.getWhen(), me.getModifiersEx(), - tp.x, tp.y, me.getClickCount(), - me.isPopupTrigger(), - me.getButton()); + Point tp = SwingUtilities.convertPoint(frame.getContentPane(), x, y, + lastComponentEntered); + MouseEvent exited = new MouseEvent(lastComponentEntered, + MouseEvent.MOUSE_EXITED, + me.getWhen(), me.getModifiersEx(), + tp.x, tp.y, me.getClickCount(), + me.isPopupTrigger(), + me.getButton()); tempComponent = lastComponentEntered; - lastComponentEntered = null; - tempComponent.dispatchEvent(exited); + lastComponentEntered = null; + tempComponent.dispatchEvent(exited); } // If we have a candidate, maybe enter it. if (candidate != null) { - mouseEventTarget = candidate; - if (candidate.isLightweight() && candidate.isShowing() - && candidate != frame.getContentPane() - && candidate != lastComponentEntered) - { - lastComponentEntered = mouseEventTarget; - Point cp = SwingUtilities.convertPoint(frame.getContentPane(), - x, y, lastComponentEntered); - MouseEvent entered = new MouseEvent(lastComponentEntered, - MouseEvent.MOUSE_ENTERED, - me.getWhen(), - me.getModifiersEx(), cp.x, - cp.y, me.getClickCount(), - me.isPopupTrigger(), - me.getButton()); - lastComponentEntered.dispatchEvent(entered); - } + mouseEventTarget = candidate; + if (candidate.isLightweight() && candidate.isShowing() + && candidate != frame.getContentPane() + && candidate != lastComponentEntered) + { + lastComponentEntered = mouseEventTarget; + Point cp = SwingUtilities.convertPoint(frame.getContentPane(), x, + y, lastComponentEntered); + MouseEvent entered = new MouseEvent(lastComponentEntered, + MouseEvent.MOUSE_ENTERED, + me.getWhen(), + me.getModifiersEx(), cp.x, + cp.y, me.getClickCount(), + me.isPopupTrigger(), + me.getButton()); + lastComponentEntered.dispatchEvent(entered); + } } if (me.getID() == MouseEvent.MOUSE_RELEASED || me.getID() == MouseEvent.MOUSE_PRESSED && pressCount > 0 || me.getID() == MouseEvent.MOUSE_DRAGGED) - // If any of the following events occur while a button is held down, - // they should be dispatched to the same component to which the - // original MOUSE_PRESSED event was dispatched: - // - MOUSE_RELEASED - // - MOUSE_PRESSED: another button pressed while the first is held down - // - MOUSE_DRAGGED - mouseEventTarget = pressedComponent; + // If any of the following events occur while a button is held down, + // they should be dispatched to the same component to which the + // original MOUSE_PRESSED event was dispatched: + // - MOUSE_RELEASED + // - MOUSE_PRESSED: another button pressed while the first is held down + // - MOUSE_DRAGGED + mouseEventTarget = pressedComponent; else if (me.getID() == MouseEvent.MOUSE_CLICKED) { - // Don't dispatch CLICKED events whose target is not the same as the - // target for the original PRESSED event. - if (candidate != pressedComponent) - mouseEventTarget = null; - else if (pressCount == 0) - pressedComponent = null; + // Don't dispatch CLICKED events whose target is not the same as the + // target for the original PRESSED event. + if (candidate != pressedComponent) + mouseEventTarget = null; + else if (pressCount == 0) + pressedComponent = null; } } /** - * This is a helper method that dispatches the GlassPane MouseEvents to - * the proper component. - * - * @param e The AWTEvent to be dispatched. Usually an instance of - * MouseEvent. + * This is a helper method that dispatches the GlassPane MouseEvents to the + * proper component. + * + * @param e + * The AWTEvent to be dispatched. Usually an instance of + * MouseEvent. */ private void handleEvent(AWTEvent e) { if (e instanceof MouseEvent) { - MouseEvent me = SwingUtilities.convertMouseEvent(frame.getRootPane() - .getGlassPane(), - (MouseEvent) e, - frame.getRootPane() - .getGlassPane()); - - acquireComponentForMouseEvent(me); - - // Avoid dispatching ENTERED and EXITED events twice. - if (mouseEventTarget != null && mouseEventTarget.isShowing() - && e.getID() != MouseEvent.MOUSE_ENTERED - && e.getID() != MouseEvent.MOUSE_EXITED) - { - MouseEvent newEvt = SwingUtilities.convertMouseEvent(frame - .getContentPane(), - me, - mouseEventTarget); - mouseEventTarget.dispatchEvent(newEvt); - - switch (e.getID()) - { - case MouseEvent.MOUSE_PRESSED: - if (pressCount++ == 0) - pressedComponent = mouseEventTarget; - break; - case MouseEvent.MOUSE_RELEASED: - // Clear our memory of the original PRESSED event, only if - // we're not expecting a CLICKED event after this. If - // there is a CLICKED event after this, it will do clean up. - if (--pressCount == 0 - && mouseEventTarget != pressedComponent) - pressedComponent = null; - break; - } - } + MouseEvent me = (MouseEvent) e; + acquireComponentForMouseEvent(me); + + //If there is no target, return + if (mouseEventTarget == null) + return; + + //Avoid re-dispatching to ourselves and causing an infinite loop + if (mouseEventTarget.equals(frame.getGlassPane())) + return; + + // Avoid dispatching ENTERED and EXITED events twice. + if (mouseEventTarget.isShowing() + && e.getID() != MouseEvent.MOUSE_ENTERED + && e.getID() != MouseEvent.MOUSE_EXITED) + { + MouseEvent newEvt = SwingUtilities.convertMouseEvent( + frame.getGlassPane(), + me, + mouseEventTarget); + mouseEventTarget.dispatchEvent(newEvt); + + switch (e.getID()) + { + case MouseEvent.MOUSE_PRESSED: + if (pressCount++ == 0) + pressedComponent = mouseEventTarget; + break; + case MouseEvent.MOUSE_RELEASED: + // Clear our memory of the original PRESSED event, only if + // we're not expecting a CLICKED event after this. If + // there is a CLICKED event after this, it will do clean up. + if (--pressCount == 0 && mouseEventTarget != pressedComponent) + pressedComponent = null; + break; + } + } } } } @@ -874,17 +890,18 @@ public class BasicInternalFrameUI extends InternalFrameUI * This helper class listens for PropertyChangeEvents from the * JInternalFrame. */ - public class InternalFramePropertyChangeListener - implements PropertyChangeListener, VetoableChangeListener + public class InternalFramePropertyChangeListener implements + PropertyChangeListener, VetoableChangeListener { /** - * This method is called when one of the JInternalFrame's properties - * change. This method is to allow JInternalFrame to veto an attempt - * to close the internal frame. This allows JInternalFrame to honour - * its defaultCloseOperation if that is DO_NOTHING_ON_CLOSE. + * This method is called when one of the JInternalFrame's properties change. + * This method is to allow JInternalFrame to veto an attempt to close the + * internal frame. This allows JInternalFrame to honour its + * defaultCloseOperation if that is DO_NOTHING_ON_CLOSE. */ - public void vetoableChange(PropertyChangeEvent e) throws PropertyVetoException + public void vetoableChange(PropertyChangeEvent e) + throws PropertyVetoException { if (e.getPropertyName().equals(JInternalFrame.IS_CLOSED_PROPERTY)) { @@ -892,75 +909,78 @@ public class BasicInternalFrameUI extends InternalFrameUI { frame.setVisible(false); frame.getDesktopPane().repaint(); - throw new PropertyVetoException ("close operation is HIDE_ON_CLOSE\n", e); + throw new PropertyVetoException( + "close operation is HIDE_ON_CLOSE\n", + e); } else if (frame.getDefaultCloseOperation() == JInternalFrame.DISPOSE_ON_CLOSE) closeFrame(frame); else - throw new PropertyVetoException ("close operation is DO_NOTHING_ON_CLOSE\n", e); + throw new PropertyVetoException( + "close operation is DO_NOTHING_ON_CLOSE\n", + e); } } - + /** - * This method is called when one of the JInternalFrame's properties - * change. - * - * @param evt The PropertyChangeEvent. + * This method is called when one of the JInternalFrame's properties change. + * + * @param evt + * The PropertyChangeEvent. */ public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(JInternalFrame.IS_MAXIMUM_PROPERTY)) { - if (frame.isMaximum()) - maximizeFrame(frame); - else - minimizeFrame(frame); + if (frame.isMaximum()) + maximizeFrame(frame); + else + minimizeFrame(frame); } else if (evt.getPropertyName().equals(JInternalFrame.IS_ICON_PROPERTY)) { - if (frame.isIcon()) - iconifyFrame(frame); - else - deiconifyFrame(frame); + if (frame.isIcon()) + iconifyFrame(frame); + else + deiconifyFrame(frame); } else if (evt.getPropertyName().equals(JInternalFrame.IS_SELECTED_PROPERTY)) { - if (frame.isSelected()) - activateFrame(frame); - else - getDesktopManager().deactivateFrame(frame); + if (frame.isSelected()) + activateFrame(frame); + else + deactivateFrame(frame); } else if (evt.getPropertyName().equals(JInternalFrame.ROOT_PANE_PROPERTY) - || evt.getPropertyName().equals(JInternalFrame.GLASS_PANE_PROPERTY)) + || evt.getPropertyName().equals( + JInternalFrame.GLASS_PANE_PROPERTY)) { - Component old = (Component) evt.getOldValue(); - old.removeMouseListener(glassPaneDispatcher); - old.removeMouseMotionListener(glassPaneDispatcher); + Component old = (Component) evt.getOldValue(); + old.removeMouseListener(glassPaneDispatcher); + old.removeMouseMotionListener(glassPaneDispatcher); - Component newPane = (Component) evt.getNewValue(); - newPane.addMouseListener(glassPaneDispatcher); - newPane.addMouseMotionListener(glassPaneDispatcher); + Component newPane = (Component) evt.getNewValue(); + newPane.addMouseListener(glassPaneDispatcher); + newPane.addMouseMotionListener(glassPaneDispatcher); - frame.revalidate(); + frame.revalidate(); } - /* FIXME: need to add ancestor properties to JComponents. - else if (evt.getPropertyName().equals(JComponent.ANCESTOR_PROPERTY)) - { - if (desktopPane != null) - desktopPane.removeComponentListener(componentListener); - desktopPane = frame.getDesktopPane(); - if (desktopPane != null) - desktopPane.addComponentListener(componentListener); - } - */ + /* + * FIXME: need to add ancestor properties to JComponents. else if + * (evt.getPropertyName().equals(JComponent.ANCESTOR_PROPERTY)) { if + * (desktopPane != null) + * desktopPane.removeComponentListener(componentListener); desktopPane = + * frame.getDesktopPane(); if (desktopPane != null) + * desktopPane.addComponentListener(componentListener); } + */ } } /** * This helper class is the border for the JInternalFrame. */ - private class InternalFrameBorder extends AbstractBorder - implements UIResource + private class InternalFrameBorder extends AbstractBorder implements + UIResource { /** The width of the border. */ private static final int bSize = 5; @@ -970,7 +990,7 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method returns whether the border is opaque. - * + * * @return Whether the border is opaque. */ public boolean isBorderOpaque() @@ -980,9 +1000,9 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method returns the insets of the border. - * - * @param c The Component to find border insets for. - * + * + * @param c + * The Component to find border insets for. * @return The border insets. */ public Insets getBorderInsets(Component c) @@ -992,13 +1012,19 @@ public class BasicInternalFrameUI extends InternalFrameUI /** * This method paints the border. - * - * @param c The Component that owns the border. - * @param g The Graphics object to paint with. - * @param x The x coordinate to paint at. - * @param y The y coordinate to paint at. - * @param width The width of the Component. - * @param height The height of the Component. + * + * @param c + * The Component that owns the border. + * @param g + * The Graphics object to paint with. + * @param x + * The x coordinate to paint at. + * @param y + * The y coordinate to paint at. + * @param width + * The width of the Component. + * @param height + * The height of the Component. */ public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) @@ -1111,6 +1137,7 @@ public class BasicInternalFrameUI extends InternalFrameUI */ public BasicInternalFrameUI(JInternalFrame b) { + // Nothing to do here. } /** @@ -1135,21 +1162,21 @@ public class BasicInternalFrameUI extends InternalFrameUI { if (c instanceof JInternalFrame) { - frame = (JInternalFrame) c; + frame = (JInternalFrame) c; - internalFrameLayout = createLayoutManager(); - frame.setLayout(internalFrameLayout); + internalFrameLayout = createLayoutManager(); + frame.setLayout(internalFrameLayout); - ((JComponent) frame.getRootPane().getGlassPane()).setOpaque(false); - frame.getRootPane().getGlassPane().setVisible(true); + ((JComponent) frame.getRootPane().getGlassPane()).setOpaque(false); + frame.getRootPane().getGlassPane().setVisible(true); - installDefaults(); - installListeners(); - installComponents(); - installKeyboardActions(); + installDefaults(); + installListeners(); + installComponents(); + installKeyboardActions(); - frame.setOpaque(true); - frame.invalidate(); + frame.setOpaque(true); + frame.invalidate(); } } @@ -1177,10 +1204,8 @@ public class BasicInternalFrameUI extends InternalFrameUI */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - Border border = defaults.getBorder("InternalFrame.border"); - frame.setBorder(border); - frame.setFrameIcon(defaults.getIcon("InternalFrame.icon")); + LookAndFeel.installBorder(frame, "InternalFrame.border"); + frame.setFrameIcon(UIManager.getIcon("InternalFrame.icon")); // InternalFrames are invisible by default. frame.setVisible(false); } @@ -1343,14 +1368,14 @@ public class BasicInternalFrameUI extends InternalFrameUI { if (currentPane != null) { - deinstallMouseHandlers(currentPane); - frame.remove(currentPane); + deinstallMouseHandlers(currentPane); + frame.remove(currentPane); } if (newPane != null) { - installMouseHandlers(newPane); - frame.add(newPane); + installMouseHandlers(newPane); + frame.add(newPane); } } @@ -1678,6 +1703,16 @@ public class BasicInternalFrameUI extends InternalFrameUI } /** + * This is a convenience method that deactivates the JInternalFrame. + * + * @param f the JInternalFrame to deactivate + */ + protected void deactivateFrame(JInternalFrame f) + { + getDesktopManager().deactivateFrame(f); + } + + /** * This method returns a new ComponentListener for the JDesktopPane. * * @return A new ComponentListener. diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java index bb9ce6cb1d9..c8f677fa0a0 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java @@ -50,9 +50,8 @@ import java.beans.PropertyChangeListener; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JLabel; +import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; -import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.LabelUI; @@ -60,9 +59,7 @@ import javax.swing.plaf.LabelUI; * This is the Basic Look and Feel class for the JLabel. One BasicLabelUI * object is used to paint all JLabels that utilize the Basic Look and Feel. */ -public class BasicLabelUI - extends LabelUI - implements PropertyChangeListener +public class BasicLabelUI extends LabelUI implements PropertyChangeListener { /** The labelUI that is shared by all labels. */ protected static BasicLabelUI labelUI; @@ -345,11 +342,8 @@ public class BasicLabelUI */ protected void installDefaults(JLabel c) { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - c.setForeground(defaults.getColor("Label.foreground")); - c.setBackground(defaults.getColor("Label.background")); - c.setFont(defaults.getFont("Label.font")); + LookAndFeel.installColorsAndFont(c, "Label.background", "Label.foreground", + "Label.font"); //XXX: There are properties we don't use called disabledForeground //and disabledShadow. } @@ -417,8 +411,6 @@ public class BasicLabelUI */ public void propertyChange(PropertyChangeEvent e) { - JLabel c = (JLabel) e.getSource(); - c.revalidate(); - c.repaint(); + // What to do here? } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java index 841bd670f67..33932991473 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java @@ -38,31 +38,35 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Point; import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; -import java.awt.event.InputEvent; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import javax.swing.AbstractAction; +import javax.swing.ActionMap; import javax.swing.CellRendererPane; +import javax.swing.DefaultListSelectionModel; +import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.JViewport; +import javax.swing.KeyStroke; import javax.swing.ListCellRenderer; import javax.swing.ListModel; import javax.swing.ListSelectionModel; +import javax.swing.LookAndFeel; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.event.ListDataEvent; @@ -70,7 +74,9 @@ import javax.swing.event.ListDataListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.MouseInputListener; +import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.InputMapUIResource; import javax.swing.plaf.ListUI; /** @@ -125,8 +131,9 @@ public class BasicListUI extends ListUI * Helper method to repaint the focused cell's * lost or acquired focus state. */ - void repaintCellFocus() + protected void repaintCellFocus() { + // TODO: Implement this properly. } } @@ -183,141 +190,231 @@ public class BasicListUI extends ListUI */ public void valueChanged(ListSelectionEvent e) { + int index1 = e.getFirstIndex(); + int index2 = e.getLastIndex(); + Rectangle damaged = getCellBounds(list, index1, index2); + list.repaint(damaged); } } /** - * A helper class which listens for {@link KeyEvents}s - * from the {@link JList}. + * This class is used to mimmic the behaviour of the JDK when registering + * keyboard actions. It is the same as the private class used in JComponent + * for the same reason. This class receives an action event and dispatches + * it to the true receiver after altering the actionCommand property of the + * event. */ - private class KeyHandler extends KeyAdapter + private static class ActionListenerProxy + extends AbstractAction { - public KeyHandler() + ActionListener target; + String bindingCommandName; + + public ActionListenerProxy(ActionListener li, + String cmd) { + target = li; + bindingCommandName = cmd; } - - public void keyPressed( KeyEvent evt ) + + public void actionPerformed(ActionEvent e) + { + ActionEvent derivedEvent = new ActionEvent(e.getSource(), + e.getID(), + bindingCommandName, + e.getModifiers()); + target.actionPerformed(derivedEvent); + } + } + + class ListAction extends AbstractAction + { + public void actionPerformed (ActionEvent e) { - int lead = BasicListUI.this.list.getLeadSelectionIndex(); - int max = BasicListUI.this.list.getModel().getSize() - 1; + int lead = list.getLeadSelectionIndex(); + int max = list.getModel().getSize() - 1; + DefaultListSelectionModel selModel = (DefaultListSelectionModel)list.getSelectionModel(); + String command = e.getActionCommand(); // Do nothing if list is empty if (max == -1) return; - - // Process the key event. Bindings can be found in - // javax.swing.plaf.basic.BasicLookAndFeel.java - if ((evt.getKeyCode() == KeyEvent.VK_DOWN) - || (evt.getKeyCode() == KeyEvent.VK_KP_DOWN)) + + if (command.equals("selectNextRow")) { - if (evt.getModifiers() == 0) - { - BasicListUI.this.list.clearSelection(); - BasicListUI.this.list.setSelectedIndex(Math.min(lead+1,max)); - } - else if (evt.getModifiers() == InputEvent.SHIFT_MASK) + selectNextIndex(); + } + else if (command.equals("selectPreviousRow")) + { + selectPreviousIndex(); + } + else if (command.equals("clearSelection")) + { + list.clearSelection(); + } + else if (command.equals("selectAll")) + { + list.setSelectionInterval(0, max); + // this next line is to restore the lead selection index to the old + // position, because select-all should not change the lead index + list.addSelectionInterval(lead, lead); + } + else if (command.equals("selectLastRow")) + { + list.setSelectedIndex(list.getModel().getSize() - 1); + } + else if (command.equals("selectLastRowChangeLead")) + { + selModel.moveLeadSelectionIndex(list.getModel().getSize() - 1); + } + else if (command.equals("scrollDownExtendSelection")) + { + int target; + if (lead == list.getLastVisibleIndex()) { - BasicListUI.this.list.getSelectionModel(). - setLeadSelectionIndex(Math.min(lead+1,max)); + target = Math.min + (max, lead + (list.getLastVisibleIndex() - + list.getFirstVisibleIndex() + 1)); } + else + target = list.getLastVisibleIndex(); + selModel.setLeadSelectionIndex(target); } - else if ((evt.getKeyCode() == KeyEvent.VK_UP) - || (evt.getKeyCode() == KeyEvent.VK_KP_UP)) + else if (command.equals("scrollDownChangeLead")) { - if (evt.getModifiers() == 0) + int target; + if (lead == list.getLastVisibleIndex()) { - BasicListUI.this.list.clearSelection(); - BasicListUI.this.list.setSelectedIndex(Math.max(lead-1,0)); + target = Math.min + (max, lead + (list.getLastVisibleIndex() - + list.getFirstVisibleIndex() + 1)); } - else if (evt.getModifiers() == InputEvent.SHIFT_MASK) + else + target = list.getLastVisibleIndex(); + selModel.moveLeadSelectionIndex(target); + } + else if (command.equals("scrollUpExtendSelection")) + { + int target; + if (lead == list.getFirstVisibleIndex()) { - BasicListUI.this.list.getSelectionModel(). - setLeadSelectionIndex(Math.max(lead-1,0)); + target = Math.max + (0, lead - (list.getLastVisibleIndex() - + list.getFirstVisibleIndex() + 1)); } + else + target = list.getFirstVisibleIndex(); + selModel.setLeadSelectionIndex(target); } - else if (evt.getKeyCode() == KeyEvent.VK_PAGE_UP) + else if (command.equals("scrollUpChangeLead")) { int target; - if (lead == BasicListUI.this.list.getFirstVisibleIndex()) + if (lead == list.getFirstVisibleIndex()) { target = Math.max - (0, lead - (BasicListUI.this.list.getLastVisibleIndex() - - BasicListUI.this.list.getFirstVisibleIndex() + 1)); + (0, lead - (list.getLastVisibleIndex() - + list.getFirstVisibleIndex() + 1)); } else + target = list.getFirstVisibleIndex(); + selModel.moveLeadSelectionIndex(target); + } + else if (command.equals("selectNextRowExtendSelection")) + { + selModel.setLeadSelectionIndex(Math.min(lead + 1,max)); + } + else if (command.equals("selectFirstRow")) + { + list.setSelectedIndex(0); + } + else if (command.equals("selectFirstRowChangeLead")) + { + selModel.moveLeadSelectionIndex(0); + } + else if (command.equals("selectFirstRowExtendSelection")) + { + selModel.setLeadSelectionIndex(0); + } + else if (command.equals("selectPreviousRowExtendSelection")) + { + selModel.setLeadSelectionIndex(Math.max(0,lead - 1)); + } + else if (command.equals("scrollUp")) + { + int target; + if (lead == list.getFirstVisibleIndex()) { - target = BasicListUI.this.list.getFirstVisibleIndex(); + target = Math.max + (0, lead - (list.getLastVisibleIndex() - + list.getFirstVisibleIndex() + 1)); } - if (evt.getModifiers() == 0) - BasicListUI.this.list.setSelectedIndex(target); - else if (evt.getModifiers() == InputEvent.SHIFT_MASK) - BasicListUI.this.list.getSelectionModel(). - setLeadSelectionIndex(target); + else + target = list.getFirstVisibleIndex(); + list.setSelectedIndex(target); } - else if (evt.getKeyCode() == KeyEvent.VK_PAGE_DOWN) + else if (command.equals("selectLastRowExtendSelection")) + { + selModel.setLeadSelectionIndex(list.getModel().getSize() - 1); + } + else if (command.equals("scrollDown")) { int target; - if (lead == BasicListUI.this.list.getLastVisibleIndex()) + if (lead == list.getLastVisibleIndex()) { target = Math.min - (max, lead + (BasicListUI.this.list.getLastVisibleIndex() - - BasicListUI.this.list.getFirstVisibleIndex() + 1)); + (max, lead + (list.getLastVisibleIndex() - + list.getFirstVisibleIndex() + 1)); } else + target = list.getLastVisibleIndex(); + list.setSelectedIndex(target); + } + else if (command.equals("selectNextRowChangeLead")) + { + if (selModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) + selectNextIndex(); + else + { + selModel.moveLeadSelectionIndex(Math.min(max, lead + 1)); + } + } + else if (command.equals("selectPreviousRowChangeLead")) + { + if (selModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) + selectPreviousIndex(); + else { - target = BasicListUI.this.list.getLastVisibleIndex(); + selModel.moveLeadSelectionIndex(Math.max(0, lead - 1)); } - if (evt.getModifiers() == 0) - BasicListUI.this.list.setSelectedIndex(target); - else if (evt.getModifiers() == InputEvent.SHIFT_MASK) - BasicListUI.this.list.getSelectionModel(). - setLeadSelectionIndex(target); - } - else if (evt.getKeyCode() == KeyEvent.VK_BACK_SLASH - && (evt.getModifiers() == InputEvent.CTRL_MASK)) + } + else if (command.equals("addToSelection")) { - BasicListUI.this.list.clearSelection(); + list.addSelectionInterval(lead, lead); } - else if ((evt.getKeyCode() == KeyEvent.VK_HOME) - || evt.getKeyCode() == KeyEvent.VK_END) + else if (command.equals("extendTo")) { - if (evt.getModifiers() != 0 && - evt.getModifiers() != InputEvent.SHIFT_MASK) - return; - // index is either 0 for HOME, or last cell for END - int index = (evt.getKeyCode() == KeyEvent.VK_HOME) ? 0 : max; - - if (!evt.isShiftDown() ||(BasicListUI.this.list.getSelectionMode() - == ListSelectionModel.SINGLE_SELECTION)) - BasicListUI.this.list.setSelectedIndex(index); - else if (BasicListUI.this.list.getSelectionMode() == - ListSelectionModel.SINGLE_INTERVAL_SELECTION) - BasicListUI.this.list.setSelectionInterval - (BasicListUI.this.list.getAnchorSelectionIndex(), index); - else - BasicListUI.this.list.getSelectionModel(). - setLeadSelectionIndex(index); + selModel.setSelectionInterval(selModel.getAnchorSelectionIndex(), + lead); } - else if ((evt.getKeyCode() == KeyEvent.VK_A || evt.getKeyCode() - == KeyEvent.VK_SLASH) && (evt.getModifiers() == - InputEvent.CTRL_MASK)) + else if (command.equals("toggleAndAnchor")) { - BasicListUI.this.list.setSelectionInterval(0, max); - // this next line is to restore the lead selection index to the old - // position, because select-all should not change the lead index - BasicListUI.this.list.addSelectionInterval(lead, lead); + if (!list.isSelectedIndex(lead)) + list.addSelectionInterval(lead, lead); + else + list.removeSelectionInterval(lead, lead); + selModel.setAnchorSelectionIndex(lead); } - else if (evt.getKeyCode() == KeyEvent.VK_SPACE && - (evt.getModifiers() == InputEvent.CTRL_MASK)) + else { - BasicListUI.this.list.getSelectionModel(). - setLeadSelectionIndex(Math.min(lead+1,max)); + // DEBUG: uncomment the following line to print out + // key bindings that aren't implemented yet + + // System.out.println ("not implemented: "+e.getActionCommand()); } - - BasicListUI.this.list.ensureIndexIsVisible - (BasicListUI.this.list.getLeadSelectionIndex()); + + list.ensureIndexIsVisible(list.getLeadSelectionIndex()); } } - + /** * A helper class which listens for {@link MouseEvent}s * from the {@link JList}. @@ -333,48 +430,46 @@ public class BasicListUI extends ListUI public void mouseClicked(MouseEvent event) { Point click = event.getPoint(); - int index = BasicListUI.this.locationToIndex(list, click); + int index = locationToIndex(list, click); if (index == -1) return; if (event.isShiftDown()) { - if (BasicListUI.this.list.getSelectionMode() == - ListSelectionModel.SINGLE_SELECTION) - BasicListUI.this.list.setSelectedIndex(index); - else if (BasicListUI.this.list.getSelectionMode() == + if (list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION) + list.setSelectedIndex(index); + else if (list.getSelectionMode() == ListSelectionModel.SINGLE_INTERVAL_SELECTION) // COMPAT: the IBM VM is compatible with the following line of code. // However, compliance with Sun's VM would correspond to replacing // getAnchorSelectionIndex() with getLeadSelectionIndex().This is // both unnatural and contradictory to the way they handle other // similar UI interactions. - BasicListUI.this.list.setSelectionInterval - (BasicListUI.this.list.getAnchorSelectionIndex(), index); + list.setSelectionInterval(list.getAnchorSelectionIndex(), index); else // COMPAT: both Sun and IBM are compatible instead with: - // BasicListUI.this.list.setSelectionInterval - // (BasicListUI.this.list.getLeadSelectionIndex(),index); + // list.setSelectionInterval + // (list.getLeadSelectionIndex(),index); // Note that for IBM this is contradictory to what they did in // the above situation for SINGLE_INTERVAL_SELECTION. // The most natural thing to do is the following: - BasicListUI.this.list.getSelectionModel(). - setLeadSelectionIndex(index); + if (list.isSelectedIndex(list.getAnchorSelectionIndex())) + list.getSelectionModel().setLeadSelectionIndex(index); + else + list.addSelectionInterval(list.getAnchorSelectionIndex(), index); } else if (event.isControlDown()) { - if (BasicListUI.this.list.getSelectionMode() == - ListSelectionModel.SINGLE_SELECTION) - BasicListUI.this.list.setSelectedIndex(index); - else if (BasicListUI.this.list.isSelectedIndex(index)) - BasicListUI.this.list.removeSelectionInterval(index,index); + if (list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION) + list.setSelectedIndex(index); + else if (list.isSelectedIndex(index)) + list.removeSelectionInterval(index,index); else - BasicListUI.this.list.addSelectionInterval(index,index); + list.addSelectionInterval(index,index); } else - BasicListUI.this.list.setSelectedIndex(index); + list.setSelectedIndex(index); - BasicListUI.this.list.ensureIndexIsVisible - (BasicListUI.this.list.getLeadSelectionIndex()); + list.ensureIndexIsVisible(list.getLeadSelectionIndex()); } /** @@ -385,6 +480,7 @@ public class BasicListUI extends ListUI */ public void mousePressed(MouseEvent event) { + // TODO: What should be done here, if anything? } /** @@ -395,6 +491,7 @@ public class BasicListUI extends ListUI */ public void mouseReleased(MouseEvent event) { + // TODO: What should be done here, if anything? } /** @@ -405,6 +502,7 @@ public class BasicListUI extends ListUI */ public void mouseEntered(MouseEvent event) { + // TODO: What should be done here, if anything? } /** @@ -415,6 +513,7 @@ public class BasicListUI extends ListUI */ public void mouseExited(MouseEvent event) { + // TODO: What should be done here, if anything? } /** @@ -425,6 +524,7 @@ public class BasicListUI extends ListUI */ public void mouseDragged(MouseEvent event) { + // TODO: What should be done here, if anything? } /** @@ -435,6 +535,7 @@ public class BasicListUI extends ListUI */ public void mouseMoved(MouseEvent event) { + // TODO: What should be done here, if anything? } } @@ -459,11 +560,61 @@ public class BasicListUI extends ListUI if (e.getNewValue() != null && e.getNewValue() instanceof ListModel) ((ListModel) e.getNewValue()).addListDataListener(BasicListUI.this.listDataListener); } + // Update the updateLayoutStateNeeded flag. + if (e.getPropertyName().equals("model")) + updateLayoutStateNeeded += modelChanged; + else if (e.getPropertyName().equals("selectionModel")) + updateLayoutStateNeeded += selectionModelChanged; + else if (e.getPropertyName().equals("font")) + updateLayoutStateNeeded += fontChanged; + else if (e.getPropertyName().equals("fixedCellWidth")) + updateLayoutStateNeeded += fixedCellWidthChanged; + else if (e.getPropertyName().equals("fixedCellHeight")) + updateLayoutStateNeeded += fixedCellHeightChanged; + else if (e.getPropertyName().equals("prototypeCellValue")) + updateLayoutStateNeeded += prototypeCellValueChanged; + else if (e.getPropertyName().equals("cellRenderer")) + updateLayoutStateNeeded += cellRendererChanged; BasicListUI.this.damageLayout(); } } /** + * A constant to indicate that the model has changed. + */ + protected static final int modelChanged = 1; + + /** + * A constant to indicate that the selection model has changed. + */ + protected static final int selectionModelChanged = 2; + + /** + * A constant to indicate that the font has changed. + */ + protected static final int fontChanged = 4; + + /** + * A constant to indicate that the fixedCellWidth has changed. + */ + protected static final int fixedCellWidthChanged = 8; + + /** + * A constant to indicate that the fixedCellHeight has changed. + */ + protected static final int fixedCellHeightChanged = 16; + + /** + * A constant to indicate that the prototypeCellValue has changed. + */ + protected static final int prototypeCellValueChanged = 32; + + /** + * A constant to indicate that the cellRenderer has changed. + */ + protected static final int cellRendererChanged = 64; + + /** * Creates a new BasicListUI for the component. * * @param c The component to create a UI for @@ -487,9 +638,6 @@ public class BasicListUI extends ListUI /** The mouse listener listening to the list. */ protected MouseInputListener mouseInputListener; - /** The key listener listening to the list */ - private KeyHandler keyListener; - /** The property change listener listening to the list. */ protected PropertyChangeListener propertyChangeListener; @@ -501,7 +649,11 @@ public class BasicListUI extends ListUI /** Saved reference to the list this UI was created for. */ protected JList list; - /** The height of a single cell in the list. */ + /** + * The height of a single cell in the list. This field is used when the + * fixedCellHeight property of the list is set. Otherwise this field is + * set to <code>-1</code> and {@link #cellHeights} is used instead. + */ protected int cellHeight; /** The width of a single cell in the list. */ @@ -509,14 +661,25 @@ public class BasicListUI extends ListUI /** * An array of varying heights of cells in the list, in cases where each - * cell might have a different height. + * cell might have a different height. This field is used when the + * <code>fixedCellHeight</code> property of the list is not set. Otherwise + * this field is <code>null</code> and {@link #cellHeight} is used. */ protected int[] cellHeights; /** - * A simple counter. When nonzero, indicates that the UI class is out of + * A bitmask that indicates which properties of the JList have changed. + * When nonzero, indicates that the UI class is out of * date with respect to the underlying list, and must recalculate the * list layout before painting or performing size calculations. + * + * @see #modelChanged + * @see #selectionModelChanged + * @see #fontChanged + * @see #fixedCellWidthChanged + * @see #fixedCellHeightChanged + * @see #prototypeCellValueChanged + * @see #cellRendererChanged */ protected int updateLayoutStateNeeded; @@ -524,6 +687,9 @@ public class BasicListUI extends ListUI * The {@link CellRendererPane} that is used for painting. */ protected CellRendererPane rendererPane; + + /** The action bound to KeyStrokes. */ + ListAction action; /** * Calculate the height of a particular row. If there is a fixed {@link @@ -611,19 +777,51 @@ public class BasicListUI extends ListUI * @param y0 The Y coordinate to calculate the row number for * * @return The row number containing the specified Y value, or <code>-1</code> - * if the specified Y coordinate is invalid + * if the list model is empty + * + * @specnote This method is specified to return -1 for an invalid Y + * coordinate. However, some simple tests show that the behaviour + * is to return the index of the last list element for an Y + * coordinate that lies outside of the list bounds (even for + * negative indices). <code>-1</code> + * is only returned if the list model is empty. */ protected int convertYToRow(int y0) { - for (int row = 0; row < cellHeights.length; ++row) - { - int h = getRowHeight(row); + if (list.getModel().getSize() == 0) + return -1; + + // When y0 < 0, then the JDK returns the maximum row index of the list. So + // do we. + if (y0 < 0) + return list.getModel().getSize() - 1; + + // Update the layout if necessary. + maybeUpdateLayoutState(); + + int index = list.getModel().getSize() - 1;; - if (y0 < h) - return row; - y0 -= h; + // If a fixed cell height is set, then we can work more efficient. + if (cellHeight > 0) + { + index = Math.max(y0 / cellHeight, index); + } + // If we have no fixed cell height, we must add up each cell height up + // to y0. + else + { + int h = 0; + for (int row = 0; row < cellHeights.length; ++row) + { + h += cellHeights[row]; + if (y0 < h) + { + index = row; + break; + } + } } - return -1; + return index; } /** @@ -638,29 +836,47 @@ public class BasicListUI extends ListUI cellWidth = -1; if (cellHeights == null || cellHeights.length != nrows) cellHeights = new int[nrows]; - if (list.getFixedCellHeight() == -1 || list.getFixedCellWidth() == -1) + ListCellRenderer rend = list.getCellRenderer(); + // Update the cellHeight(s) fields. + int fixedCellHeight = list.getFixedCellHeight(); + if (fixedCellHeight > 0) + { + cellHeight = fixedCellHeight; + cellHeights = null; + } + else { - ListCellRenderer rend = list.getCellRenderer(); + cellHeight = -1; for (int i = 0; i < nrows; ++i) { - Component flyweight = rend.getListCellRendererComponent(list, - list.getModel() - .getElementAt(i), - 0, false, - false); + Component flyweight = + rend.getListCellRendererComponent(list, + list.getModel().getElementAt(i), + i, list.isSelectedIndex(i), + list.getSelectionModel().getAnchorSelectionIndex() == i); Dimension dim = flyweight.getPreferredSize(); cellHeights[i] = dim.height; - // compute average cell height (little hack here) - cellHeight = (cellHeight * i + cellHeights[i]) / (i + 1); - cellWidth = Math.max(cellWidth, dim.width); - if (list.getLayoutOrientation() == JList.VERTICAL) - cellWidth = Math.max(cellWidth, list.getSize().width); } } + + // Update the cellWidth field. + int fixedCellWidth = list.getFixedCellWidth(); + if (fixedCellWidth > 0) + cellWidth = fixedCellWidth; else { - cellHeight = list.getFixedCellHeight(); - cellWidth = list.getFixedCellWidth(); + for (int i = 0; i < nrows; ++i) + { + Component flyweight = + rend.getListCellRendererComponent(list, + list.getModel().getElementAt(i), + i, list.isSelectedIndex(i), + list.getSelectionModel().getAnchorSelectionIndex() == i); + Dimension dim = flyweight.getPreferredSize(); + cellWidth = Math.max(cellWidth, dim.width); + } + if (list.getLayoutOrientation() == JList.VERTICAL) + cellWidth = Math.max(cellWidth, list.getSize().width); } } @@ -694,13 +910,6 @@ public class BasicListUI extends ListUI */ public BasicListUI() { - focusListener = new FocusHandler(); - listDataListener = new ListDataHandler(); - listSelectionListener = new ListSelectionHandler(); - mouseInputListener = new MouseInputHandler(); - keyListener = new KeyHandler(); - propertyChangeListener = new PropertyChangeHandler(); - componentListener = new ComponentHandler(); updateLayoutStateNeeded = 1; rendererPane = new CellRendererPane(); } @@ -713,11 +922,10 @@ public class BasicListUI extends ListUI */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - list.setForeground(defaults.getColor("List.foreground")); - list.setBackground(defaults.getColor("List.background")); - list.setSelectionForeground(defaults.getColor("List.selectionForeground")); - list.setSelectionBackground(defaults.getColor("List.selectionBackground")); + LookAndFeel.installColorsAndFont(list, "List.background", + "List.foreground", "List.font"); + list.setSelectionForeground(UIManager.getColor("List.selectionForeground")); + list.setSelectionBackground(UIManager.getColor("List.selectionBackground")); list.setOpaque(true); } @@ -727,7 +935,6 @@ public class BasicListUI extends ListUI */ protected void uninstallDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); list.setForeground(null); list.setBackground(null); list.setSelectionForeground(null); @@ -742,14 +949,28 @@ public class BasicListUI extends ListUI */ protected void installListeners() { + if (focusListener == null) + focusListener = createFocusListener(); list.addFocusListener(focusListener); + if (listDataListener == null) + listDataListener = createListDataListener(); list.getModel().addListDataListener(listDataListener); + if (listSelectionListener == null) + listSelectionListener = createListSelectionListener(); list.addListSelectionListener(listSelectionListener); + if (mouseInputListener == null) + mouseInputListener = createMouseInputListener(); list.addMouseListener(mouseInputListener); - list.addKeyListener(keyListener); list.addMouseMotionListener(mouseInputListener); + if (propertyChangeListener == null) + propertyChangeListener = createPropertyChangeListener(); list.addPropertyChangeListener(propertyChangeListener); + + // FIXME: Are these two really needed? At least they are not documented. + //keyListener = new KeyHandler(); + componentListener = new ComponentHandler(); list.addComponentListener(componentListener); + //list.addKeyListener(keyListener); } /** @@ -761,16 +982,41 @@ public class BasicListUI extends ListUI list.getModel().removeListDataListener(listDataListener); list.removeListSelectionListener(listSelectionListener); list.removeMouseListener(mouseInputListener); - list.removeKeyListener(keyListener); + //list.removeKeyListener(keyListener); list.removeMouseMotionListener(mouseInputListener); list.removePropertyChangeListener(propertyChangeListener); } - + /** * Installs keyboard actions for this UI in the {@link JList}. */ protected void installKeyboardActions() { + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + InputMap focusInputMap = (InputMap)defaults.get("List.focusInputMap"); + InputMapUIResource parentInputMap = new InputMapUIResource(); + // FIXME: The JDK uses a LazyActionMap for parentActionMap + ActionMap parentActionMap = new ActionMapUIResource(); + action = new ListAction(); + Object keys[] = focusInputMap.allKeys(); + // Register key bindings in the UI InputMap-ActionMap pair + for (int i = 0; i < keys.length; i++) + { + KeyStroke stroke = (KeyStroke)keys[i]; + String actionString = (String) focusInputMap.get(stroke); + parentInputMap.put(KeyStroke.getKeyStroke(stroke.getKeyCode(), + stroke.getModifiers()), + actionString); + + parentActionMap.put (actionString, + new ActionListenerProxy(action, actionString)); + } + // Register the new InputMap-ActionMap as the parents of the list's + // InputMap and ActionMap + parentInputMap.setParent(list.getInputMap().getParent()); + parentActionMap.setParent(list.getActionMap().getParent()); + list.getInputMap().setParent(parentInputMap); + list.getActionMap().setParent(parentActionMap); } /** @@ -778,6 +1024,7 @@ public class BasicListUI extends ListUI */ protected void uninstallKeyboardActions() { + // TODO: Implement this properly. } /** @@ -855,22 +1102,6 @@ public class BasicListUI extends ListUI } /** - * Paints the packground of the list using the background color - * of the specified component. - * - * @param g The graphics context to paint in - * @param c The component to paint the background of - */ - private void paintBackground(Graphics g, JComponent c) - { - Dimension size = getPreferredSize(c); - Color save = g.getColor(); - g.setColor(c.getBackground()); - g.fillRect(0, 0, size.width, size.height); - g.setColor(save); - } - - /** * Paints a single cell in the list. * * @param g The graphics context to paint in @@ -892,14 +1123,12 @@ public class BasicListUI extends ListUI Component comp = rend.getListCellRendererComponent(list, data.getElementAt(row), 0, isSel, hasFocus); - //comp.setBounds(new Rectangle(0, 0, bounds.width, bounds.height)); - //comp.paint(g); rendererPane.paintComponent(g, comp, list, bounds); } /** - * Paints the list by calling {@link #paintBackground} and then repeatedly - * calling {@link #paintCell} for each visible cell in the list. + * Paints the list by repeatedly calling {@link #paintCell} for each visible + * cell in the list. * * @param g The graphics context to paint with * @param c Ignored; uses the saved {@link JList} reference @@ -916,9 +1145,12 @@ public class BasicListUI extends ListUI ListSelectionModel sel = list.getSelectionModel(); int lead = sel.getLeadSelectionIndex(); Rectangle clip = g.getClipBounds(); - paintBackground(g, list); - for (int row = 0; row < nrows; ++row) + int startIndex = list.locationToIndex(new Point(clip.x, clip.y)); + int endIndex = list.locationToIndex(new Point(clip.x + clip.width, + clip.y + clip.height)); + + for (int row = startIndex; row <= endIndex; ++row) { Rectangle bounds = getCellBounds(list, row, row); if (bounds.intersects(clip)) @@ -927,13 +1159,15 @@ public class BasicListUI extends ListUI } /** - * Computes the index of a list cell given a point within the list. + * Computes the index of a list cell given a point within the list. If the + * location lies outside the bounds of the list, the greatest index in the + * list model is returned. * * @param list the list which on which the computation is based on * @param location the coordinates * * @return the index of the list item that is located at the given - * coordinates or <code>null</code> if the location is invalid + * coordinates or <code>-1</code> if the list model is empty */ public int locationToIndex(JList list, Point location) { @@ -983,7 +1217,6 @@ public class BasicListUI extends ListUI int numberOfItems2 = list.getModel().getSize(); int cellsPerRow2 = numberOfItems2 / visibleRows2 + 1; - Dimension listDim2 = list.getSize(); int gridX2 = Math.min(location.x / cellWidth, cellsPerRow2 - 1); int gridY2 = Math.min(location.y / cellHeight, visibleRows2); index = gridY2 + gridX2 * visibleRows2; @@ -1045,4 +1278,82 @@ public class BasicListUI extends ListUI } return loc; } + + /** + * Creates and returns the focus listener for this UI. + * + * @return the focus listener for this UI + */ + protected FocusListener createFocusListener() + { + return new FocusHandler(); + } + + /** + * Creates and returns the list data listener for this UI. + * + * @return the list data listener for this UI + */ + protected ListDataListener createListDataListener() + { + return new ListDataHandler(); + } + + /** + * Creates and returns the list selection listener for this UI. + * + * @return the list selection listener for this UI + */ + protected ListSelectionListener createListSelectionListener() + { + return new ListSelectionHandler(); + } + + /** + * Creates and returns the mouse input listener for this UI. + * + * @return the mouse input listener for this UI + */ + protected MouseInputListener createMouseInputListener() + { + return new MouseInputHandler(); + } + + /** + * Creates and returns the property change listener for this UI. + * + * @return the property change listener for this UI + */ + protected PropertyChangeListener createPropertyChangeListener() + { + return new PropertyChangeHandler(); + } + + /** + * Selects the next list item and force it to be visible. + */ + protected void selectNextIndex() + { + int index = list.getSelectionModel().getLeadSelectionIndex(); + if (index < list.getModel().getSize() - 1) + { + index++; + list.setSelectedIndex(index); + } + list.ensureIndexIsVisible(index); + } + + /** + * Selects the previous list item and force it to be visible. + */ + protected void selectPreviousIndex() + { + int index = list.getSelectionModel().getLeadSelectionIndex(); + if (index > 0) + { + index--; + list.setSelectedIndex(index); + } + list.ensureIndexIsVisible(index); + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java b/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java index d35ac9eb926..8ebe650350c 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java @@ -273,7 +273,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel "Button.foreground", new ColorUIResource(Color.BLACK), "Button.highlight", new ColorUIResource(Color.WHITE), "Button.light", new ColorUIResource(Color.LIGHT_GRAY), - "Button.margin", new InsetsUIResource(2, 2, 2, 2), + "Button.margin", new InsetsUIResource(2, 14, 2, 14), "Button.shadow", new ColorUIResource(Color.GRAY), "Button.textIconGap", new Integer(4), "Button.textShiftOffset", new Integer(0), @@ -362,16 +362,16 @@ public abstract class BasicLookAndFeel extends LookAndFeel "HOME", "homePassThrough", "END", "endPassThrough" }), - "ComboBox.background", new ColorUIResource(light), + "ComboBox.background", new ColorUIResource(Color.white), "ComboBox.buttonBackground", new ColorUIResource(light), - "ComboBox.buttonDarkShadow", new ColorUIResource(shadow), + "ComboBox.buttonDarkShadow", new ColorUIResource(darkShadow), "ComboBox.buttonHighlight", new ColorUIResource(highLight), "ComboBox.buttonShadow", new ColorUIResource(shadow), "ComboBox.disabledBackground", new ColorUIResource(light), "ComboBox.disabledForeground", new ColorUIResource(Color.gray), "ComboBox.font", new FontUIResource("SansSerif", Font.PLAIN, 12), "ComboBox.foreground", new ColorUIResource(Color.black), - "ComboBox.selectionBackground", new ColorUIResource(Color.black), + "ComboBox.selectionBackground", new ColorUIResource(0, 0, 128), "ComboBox.selectionForeground", new ColorUIResource(Color.white), "Desktop.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] { "KP_LEFT", "left", @@ -397,7 +397,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel "DesktopIcon.border", new BorderUIResource.CompoundBorderUIResource(null, null), "EditorPane.background", new ColorUIResource(Color.white), - "EditorPane.border", new BasicBorders.MarginBorder(), + "EditorPane.border", BasicBorders.getMarginBorder(), "EditorPane.caretBlinkRate", new Integer(500), "EditorPane.caretForeground", new ColorUIResource(Color.black), "EditorPane.font", new FontUIResource("Serif", Font.PLAIN, 12), @@ -466,6 +466,8 @@ public abstract class BasicLookAndFeel extends LookAndFeel "FocusManagerClassName", "TODO", "FormattedTextField.background", new ColorUIResource(light), "FormattedTextField.caretForeground", new ColorUIResource(Color.black), + "FormattedTextField.font", + new FontUIResource("SansSerif", Font.PLAIN, 12), "FormattedTextField.foreground", new ColorUIResource(Color.black), "FormattedTextField.inactiveBackground", new ColorUIResource(light), "FormattedTextField.inactiveForeground", new ColorUIResource(Color.gray), @@ -528,30 +530,74 @@ public abstract class BasicLookAndFeel extends LookAndFeel "Label.disabledShadow", new ColorUIResource(shadow), "Label.font", new FontUIResource("Dialog", Font.PLAIN, 12), "Label.foreground", new ColorUIResource(darkShadow), - "List.background", new ColorUIResource(light), + "List.background", new ColorUIResource(Color.white), "List.border", new BasicBorders.MarginBorder(), "List.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { - "PAGE_UP", "scrollUp", - "ctrl \\", "clearSelection", - "PAGE_DOWN", "scrollDown", - "shift PAGE_DOWN","scrollDownExtendSelection", + "ctrl DOWN", "selectNextRowChangeLead", + "shift UP", "selectPreviousRowExtendSelection", + "ctrl RIGHT", "selectNextColumnChangeLead", + "shift ctrl LEFT", "selectPreviousColumnExtendSelection", + "shift KP_UP", "selectPreviousRowChangeLead", + "DOWN", "selectNextRow", + "ctrl UP", "selectPreviousRowChangeLead", + "ctrl LEFT", "selectPreviousColumnChangeLead", + "CUT", "cut", "END", "selectLastRow", - "HOME", "selectFirstRow", - "shift END", "selectLastRowExtendSelection", + "shift PAGE_UP","scrollUpExtendSelection", + "KP_UP", "selectPreviousRow", + "shift ctrl UP", "selectPreviousRowExtendSelection", + "ctrl HOME", "selectFirstRowChangeLead", + "shift LEFT", "selectPreviousColumnExtendSelection", + "ctrl END", "selectLastRowChangeLead", + "ctrl PAGE_DOWN", "scrollDownChangeLead", + "shift ctrl RIGHT", "selectNextColumnExtendSelection", + "LEFT", "selectPreviousColumn", + "ctrl PAGE_UP", "scrollUpChangeLead", + "KP_LEFT", "selectPreviousColumn", + "shift KP_RIGHT", "selectNextColumnExtendSelection", + "SPACE", "addToSelection", + "ctrl SPACE", "toggleAndAnchor", + "shift SPACE", "extendTo", + "shift ctrl SPACE", "moveSelectionTo", + "shift ctrl DOWN", "selectNextRowExtendSelection", + "ctrl BACK_SLASH", "clearSelection", "shift HOME", "selectFirstRowExtendSelection", - "UP", "selectPreviousRow", - "ctrl /", "selectAll", - "ctrl A", "selectAll", - "DOWN", "selectNextRow", - "shift UP", "selectPreviousRowExtendSelection", - "ctrl SPACE", "selectNextRowExtendSelection", + "RIGHT", "selectNextColumn", + "shift ctrl PAGE_UP", "scrollUpExtendSelection", "shift DOWN", "selectNextRowExtendSelection", - "KP_UP", "selectPreviousRow", - "shift PAGE_UP","scrollUpExtendSelection", - "KP_DOWN", "selectNextRow" + "PAGE_DOWN", "scrollDown", + "shift ctrl KP_UP", "selectPreviousRowExtendSelection", + "shift KP_LEFT", "selectPreviousColumnExtendSelection", + "ctrl X", "cut", + "shift ctrl PAGE_DOWN", "scrollDownExtendSelection", + "ctrl SLASH", "selectAll", + "ctrl C", "copy", + "ctrl KP_RIGHT", "selectNextColumnChangeLead", + "shift END", "selectLastRowExtendSelection", + "shift ctrl KP_DOWN", "selectNextRowExtendSelection", + "ctrl KP_LEFT", "selectPreviousColumnChangeLead", + "HOME", "selectFirstRow", + "ctrl V", "paste", + "KP_DOWN", "selectNextRow", + "ctrl KP_DOWN", "selectNextRowChangeLead", + "shift RIGHT", "selectNextColumnExtendSelection", + "ctrl A", "selectAll", + "shift ctrl END", "selectLastRowExtendSelection", + "COPY", "copy", + "ctrl KP_UP", "selectPreviousRowChangeLead", + "shift ctrl KP_LEFT", "selectPreviousColumnExtendSelection", + "shift KP_DOWN", "selectNextRowExtendSelection", + "UP", "selectPreviousRow", + "shift ctrl HOME", "selectFirstRowExtendSelection", + "shift PAGE_DOWN", "scrollDownExtendSelection", + "KP_RIGHT", "selectNextColumn", + "shift ctrl KP_RIGHT", "selectNextColumnExtendSelection", + "PAGE_UP", "scrollUp", + "PASTE", "paste" }), - "List.foreground", new ColorUIResource(darkShadow), - "List.selectionBackground", new ColorUIResource(Color.black), + "List.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "List.foreground", new ColorUIResource(Color.black), + "List.selectionBackground", new ColorUIResource(0, 0, 128), "List.selectionForeground", new ColorUIResource(Color.white), "List.focusCellHighlightBorder", new BorderUIResource. @@ -601,7 +647,6 @@ public abstract class BasicLookAndFeel extends LookAndFeel "MenuItem.background", new ColorUIResource(light), "MenuItem.border", new BasicBorders.MarginBorder(), "MenuItem.borderPainted", Boolean.FALSE, - "MenuItem.checkIcon", BasicIconFactory.getMenuItemCheckIcon(), "MenuItem.font", new FontUIResource("Dialog", Font.PLAIN, 12), "MenuItem.foreground", new ColorUIResource(darkShadow), "MenuItem.margin", new InsetsUIResource(2, 2, 2, 2), @@ -624,7 +669,9 @@ public abstract class BasicLookAndFeel extends LookAndFeel "OptionPane.messageAreaBorder", new BorderUIResource.EmptyBorderUIResource(0, 0, 0, 0), "OptionPane.messageForeground", new ColorUIResource(darkShadow), - "OptionPane.minimumSize", new DimensionUIResource(262, 90), + "OptionPane.minimumSize", + new DimensionUIResource(BasicOptionPaneUI.MinimumWidth, + BasicOptionPaneUI.MinimumHeight), "OptionPane.noButtonText", "No", "OptionPane.okButtonText", "OK", // XXX Don't use gif @@ -660,16 +707,17 @@ public abstract class BasicLookAndFeel extends LookAndFeel "PopupMenu.border", new BorderUIResource.BevelBorderUIResource(0), "PopupMenu.font", new FontUIResource("Dialog", Font.PLAIN, 12), "PopupMenu.foreground", new ColorUIResource(darkShadow), - "ProgressBar.background", new ColorUIResource(light), - "ProgressBar.border", new BorderUIResource.LineBorderUIResource(Color.darkGray), + "ProgressBar.background", new ColorUIResource(Color.LIGHT_GRAY), + "ProgressBar.border", + new BorderUIResource.LineBorderUIResource(Color.GREEN, 2), "ProgressBar.cellLength", new Integer(1), "ProgressBar.cellSpacing", new Integer(0), "ProgressBar.font", new FontUIResource("Dialog", Font.PLAIN, 12), - "ProgressBar.foreground", new ColorUIResource(Color.black), - "ProgressBar.selectionBackground", new ColorUIResource(Color.black), - "ProgressBar.selectionForeground", new ColorUIResource(light), - "ProgressBar.repaintInterval", new Integer(250), - "ProgressBar.cycleTime", new Integer(6000), + "ProgressBar.foreground", new ColorUIResource(0, 0, 128), + "ProgressBar.selectionBackground", new ColorUIResource(0, 0, 128), + "ProgressBar.selectionForeground", new ColorUIResource(Color.LIGHT_GRAY), + "ProgressBar.repaintInterval", new Integer(50), + "ProgressBar.cycleTime", new Integer(3000), "RadioButton.background", new ColorUIResource(light), "RadioButton.border", new BorderUIResource.CompoundBorderUIResource(null, null), @@ -742,6 +790,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel "ScrollBar.thumbShadow", new ColorUIResource(shadow), "ScrollBar.track", new ColorUIResource(light), "ScrollBar.trackHighlight", new ColorUIResource(shadow), + "ScrollBar.width", new Integer(16), "ScrollPane.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] { "PAGE_UP", "scrollUp", "KP_LEFT", "unitScrollLeft", @@ -846,6 +895,24 @@ public abstract class BasicLookAndFeel extends LookAndFeel "TabbedPane.tabRunOverlay", new Integer(2), "TabbedPane.textIconGap", new Integer(4), "Table.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] { + "ctrl DOWN", "selectNextRowChangeLead", + "ctrl RIGHT", "selectNextColumnChangeLead", + "ctrl UP", "selectPreviousRowChangeLead", + "ctrl LEFT", "selectPreviousColumnChangeLead", + "CUT", "cut", + "SPACE", "addToSelection", + "ctrl SPACE", "toggleAndAnchor", + "shift SPACE", "extendTo", + "shift ctrl SPACE", "moveSelectionTo", + "ctrl X", "cut", + "ctrl C", "copy", + "ctrl KP_RIGHT", "selectNextColumnChangeLead", + "ctrl KP_LEFT", "selectPreviousColumnChangeLead", + "ctrl V", "paste", + "ctrl KP_DOWN", "selectNextRowChangeLead", + "COPY", "copy", + "ctrl KP_UP", "selectPreviousRowChangeLead", + "PASTE", "paste", "shift PAGE_DOWN","scrollDownExtendSelection", "PAGE_DOWN", "scrollDownChangeSelection", "END", "selectLastColumn", @@ -896,25 +963,26 @@ public abstract class BasicLookAndFeel extends LookAndFeel "ctrl SLASH", "selectAll", "ctrl shift KP_DOWN", "selectNextRowExtendSelection", }), - "Table.background", new ColorUIResource(light), - "Table.focusCellBackground", new ColorUIResource(light), - "Table.focusCellForeground", new ColorUIResource(darkShadow), + "Table.background", new ColorUIResource(new ColorUIResource(255, 255, 255)), + "Table.focusCellBackground", new ColorUIResource(new ColorUIResource(255, 255, 255)), + "Table.focusCellForeground", new ColorUIResource(new ColorUIResource(0, 0, 0)), "Table.focusCellHighlightBorder", new BorderUIResource.LineBorderUIResource( new ColorUIResource(255, 255, 0)), "Table.font", new FontUIResource("Dialog", Font.PLAIN, 12), - "Table.foreground", new ColorUIResource(darkShadow), - "Table.gridColor", new ColorUIResource(Color.gray), + "Table.foreground", new ColorUIResource(new ColorUIResource(0, 0, 0)), + "Table.gridColor", new ColorUIResource(new ColorUIResource(128, 128, 128)), "Table.scrollPaneBorder", new BorderUIResource.BevelBorderUIResource(0), - "Table.selectionBackground", new ColorUIResource(Color.black), - "Table.selectionForeground", new ColorUIResource(Color.white), - "TableHeader.background", new ColorUIResource(light), + "Table.selectionBackground", new ColorUIResource(new ColorUIResource(0, 0, 128)), + "Table.selectionForeground", new ColorUIResource(new ColorUIResource(255, 255, 255)), + "TableHeader.background", new ColorUIResource(new ColorUIResource(192, 192, 192)), "TableHeader.cellBorder", new BorderUIResource.BevelBorderUIResource(0), "TableHeader.font", new FontUIResource("Dialog", Font.PLAIN, 12), - "TableHeader.foreground", new ColorUIResource(darkShadow), + "TableHeader.foreground", new ColorUIResource(new ColorUIResource(0, 0, 0)), "TextArea.background", new ColorUIResource(light), - "TextArea.border", new BasicBorders.MarginBorder(), + "TextArea.border", + new BorderUIResource(BasicBorders.getMarginBorder()), "TextArea.caretBlinkRate", new Integer(500), "TextArea.caretForeground", new ColorUIResource(Color.black), "TextArea.font", new FontUIResource("MonoSpaced", Font.PLAIN, 12), @@ -945,8 +1013,8 @@ public abstract class BasicLookAndFeel extends LookAndFeel "TextField.font", new FontUIResource("SansSerif", Font.PLAIN, 12), "TextField.foreground", new ColorUIResource(Color.black), "TextField.highlight", new ColorUIResource(highLight), - "TextField.inactiveBackground", new ColorUIResource(light), - "TextField.inactiveForeground", new ColorUIResource(Color.gray), + "TextField.inactiveBackground", new ColorUIResource(Color.LIGHT_GRAY), + "TextField.inactiveForeground", new ColorUIResource(Color.GRAY), "TextField.light", new ColorUIResource(highLight), "TextField.highlight", new ColorUIResource(light), "TextField.keyBindings", new JTextComponent.KeyBinding[] { @@ -964,7 +1032,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel "TextField.selectionBackground", new ColorUIResource(Color.black), "TextField.selectionForeground", new ColorUIResource(Color.white), "TextPane.background", new ColorUIResource(Color.white), - "TextPane.border", new BasicBorders.MarginBorder(), + "TextPane.border", BasicBorders.getMarginBorder(), "TextPane.caretBlinkRate", new Integer(500), "TextPane.caretForeground", new ColorUIResource(Color.black), "TextPane.font", new FontUIResource("Serif", Font.PLAIN, 12), @@ -1036,7 +1104,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel "Tree.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] { "ESCAPE", "cancel" }), - "Tree.background", new ColorUIResource(light), + "Tree.background", new ColorUIResource(new Color(255, 255, 255)), "Tree.changeSelectionWithFocus", Boolean.TRUE, // "Tree.closedIcon", new IconUIResource(new ImageIcon("icons/TreeClosed.png")), // "Tree.collapsedIcon", new IconUIResource(new ImageIcon("icons/TreeCollapsed.png")), @@ -1086,20 +1154,20 @@ public abstract class BasicLookAndFeel extends LookAndFeel "PAGE_UP", "scrollUpChangeSelection", "ctrl PAGE_DOWN", "scrollDownChangeLead" }), - "Tree.font", new FontUIResource(new Font("Helvetica", Font.PLAIN, 12)), + "Tree.font", new FontUIResource("Dialog", Font.PLAIN, 12), "Tree.foreground", new ColorUIResource(Color.black), "Tree.hash", new ColorUIResource(new Color(128, 128, 128)), "Tree.leftChildIndent", new Integer(7), "Tree.rightChildIndent", new Integer(13), - "Tree.rowHeight", new Integer(20), // FIXME + "Tree.rowHeight", new Integer(16), "Tree.scrollsOnExpand", Boolean.TRUE, "Tree.selectionBackground", new ColorUIResource(Color.black), - "Tree.nonSelectionBackground", new ColorUIResource(new Color(239, 235, 231)), + "Tree.nonSelectionBackground", new ColorUIResource(new Color(255, 255, 255)), "Tree.selectionBorderColor", new ColorUIResource(Color.black), "Tree.selectionBorder", new BorderUIResource.LineBorderUIResource(Color.black), "Tree.selectionForeground", new ColorUIResource(new Color(255, 255, 255)), - "Tree.textBackground", new ColorUIResource(new Color(255, 255, 255)), - "Tree.textForeground", new ColorUIResource(Color.black), + "Tree.textBackground", new ColorUIResource(new Color(192, 192, 192)), + "Tree.textForeground", new ColorUIResource(new Color(0, 0, 0)), "Viewport.background", new ColorUIResource(light), "Viewport.foreground", new ColorUIResource(Color.black), "Viewport.font", new FontUIResource("Dialog", Font.PLAIN, 12) diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java index 95f6b84fb7c..daa9b0d6b63 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java @@ -41,16 +41,19 @@ package javax.swing.plaf.basic; import java.awt.Dimension; import java.awt.event.ContainerEvent; import java.awt.event.ContainerListener; +import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.BoxLayout; import javax.swing.JComponent; +import javax.swing.JMenu; import javax.swing.JMenuBar; -import javax.swing.UIDefaults; -import javax.swing.UIManager; +import javax.swing.LookAndFeel; +import javax.swing.MenuElement; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import javax.swing.event.MouseInputListener; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.MenuBarUI; @@ -63,12 +66,15 @@ public class BasicMenuBarUI extends MenuBarUI /*ContainerListener that listens to the ContainerEvents fired from menu bar*/ protected ContainerListener containerListener; - + /*Property change listeners that listener to PropertyChangeEvent from menu bar*/ protected PropertyChangeListener propertyChangeListener; /* menu bar for which this UI delegate is for*/ protected JMenuBar menuBar; + + /* MouseListener that listens to the mouseEvents fired from menu bar*/ + private MouseInputListener mouseListener; /** * Creates a new BasicMenuBarUI object. @@ -78,6 +84,7 @@ public class BasicMenuBarUI extends MenuBarUI changeListener = createChangeListener(); containerListener = createContainerListener(); propertyChangeListener = new PropertyChangeHandler(); + mouseListener = new MouseInputHandler(); } /** @@ -159,12 +166,9 @@ public class BasicMenuBarUI extends MenuBarUI */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - menuBar.setBackground(defaults.getColor("MenuBar.background")); - menuBar.setBorder(defaults.getBorder("MenuBar.border")); - menuBar.setFont(defaults.getFont("MenuBar.font")); - menuBar.setForeground(defaults.getColor("MenuBar.foreground")); + LookAndFeel.installBorder(menuBar, "MenuBar.border"); + LookAndFeel.installColorsAndFont(menuBar, "MenuBar.background", + "MenuBar.foreground", "MenuBar.font"); menuBar.setOpaque(true); } @@ -183,6 +187,7 @@ public class BasicMenuBarUI extends MenuBarUI { menuBar.addContainerListener(containerListener); menuBar.addPropertyChangeListener(propertyChangeListener); + menuBar.addMouseListener(mouseListener); } /** @@ -229,6 +234,7 @@ public class BasicMenuBarUI extends MenuBarUI { menuBar.removeContainerListener(containerListener); menuBar.removePropertyChangeListener(propertyChangeListener); + menuBar.removeMouseListener(mouseListener); } /** @@ -250,6 +256,7 @@ public class BasicMenuBarUI extends MenuBarUI { public void stateChanged(ChangeEvent event) { + // TODO: What should be done here, if anything? } } @@ -301,4 +308,84 @@ public class BasicMenuBarUI extends MenuBarUI menuBar.repaint(); } } + + private class MouseInputHandler implements MouseInputListener + { + /** + * Handles mouse clicked event + * + * @param e Mouse event + */ + public void mouseClicked(MouseEvent e) + { + MenuElement[] me = menuBar.getSubElements(); + + for (int i = 0; i < me.length; i++) + { + JMenu menu = menuBar.getMenu(i); + if (menu != null) + menu.setSelected(false); + } + } + + /** + * Handles mouse pressed event + * + * @param e Mouse event + */ + public void mousePressed(MouseEvent e) + { + // TODO: What should be done here, if anything? + } + + /** + * Handles mouse released event + * + * @param e Mouse event + */ + public void mouseReleased(MouseEvent e) + { + // TODO: What should be done here, if anything? + } + + /** + * Handles mouse exited event + * + * @param e Mouse event + */ + public void mouseExited(MouseEvent e) + { + // TODO: What should be done here, if anything? + } + + /** + * Handles mouse dragged event + * + * @param e Mouse event + */ + public void mouseDragged(MouseEvent e) + { + // TODO: What should be done here, if anything? + } + + /** + * Handles mouse moved event + * + * @param e Mouse event + */ + public void mouseMoved(MouseEvent e) + { + // TODO: What should be done here, if anything? + } + + /** + * Handles mouse entered event + * + * @param e Mouse event + */ + public void mouseEntered(MouseEvent e) + { + // TODO: What should be done here, if anything? + } + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java index 8aa1193bcc4..2a3556a5db9 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java @@ -7,7 +7,7 @@ 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 @@ -46,18 +46,27 @@ import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; +import javax.swing.AbstractAction; +import javax.swing.ActionMap; +import javax.swing.ButtonModel; import javax.swing.Icon; +import javax.swing.InputMap; +import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.KeyStroke; +import javax.swing.LookAndFeel; import javax.swing.MenuElement; import javax.swing.MenuSelectionManager; import javax.swing.SwingConstants; @@ -69,6 +78,8 @@ import javax.swing.event.MenuDragMouseListener; import javax.swing.event.MenuKeyEvent; import javax.swing.event.MenuKeyListener; import javax.swing.event.MouseInputListener; +import javax.swing.plaf.ActionMapUIResource; +import javax.swing.plaf.ComponentInputMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.MenuItemUI; @@ -109,7 +120,7 @@ public class BasicMenuItemUI extends MenuItemUI * Number of spaces between icon and text. */ protected int defaultTextIconGap = 4; - + /** * Color of the text when menu item is disabled */ @@ -156,16 +167,69 @@ public class BasicMenuItemUI extends MenuItemUI private String acceleratorDelimiter; /** - * PropertyChangeListener to listen for property changes in the menu item + * ItemListener to listen for item changes in the menu item */ - private PropertyChangeListener propertyChangeListener; + private ItemListener itemListener; /** * Number of spaces between accelerator and menu item's label. */ - private int defaultAcceleratorLabelGap = 4; + private int defaultAcceleratorLabelGap = 10; /** + * The gap between different menus on the MenuBar. + */ + private int MenuGap = 10; + + /** A PropertyChangeListener to make UI updates after property changes **/ + PropertyChangeHandler propertyChangeListener; + + /** + * A class to handle PropertChangeEvents for the JMenuItem + * @author Anthony Balkissoon abalkiss at redhat dot com. + */ + class PropertyChangeHandler implements PropertyChangeListener + { + /** + * This method is called when a property of the menuItem is changed. + * Currently it is only used to update the accelerator key bindings. + * + * @param e + * the PropertyChangeEvent + */ + public void propertyChange(PropertyChangeEvent e) + { + if (e.getPropertyName() == "accelerator") + { + InputMap map = SwingUtilities.getUIInputMap(menuItem, JComponent.WHEN_IN_FOCUSED_WINDOW); + if (map != null) + map.remove((KeyStroke)e.getOldValue()); + else + map = new ComponentInputMapUIResource(menuItem); + map.put((KeyStroke)e.getNewValue(), "doClick"); + } + } + } + + /** + * A class to handle accelerator keys. This is the Action we will + * perform when the accelerator key for this JMenuItem is pressed. + * @author Anthony Balkissoon abalkiss at redhat dot com + * + */ + class ClickAction extends AbstractAction + { + /** + * This is what is done when the accelerator key for the JMenuItem is + * pressed. + */ + public void actionPerformed(ActionEvent event) + { + doClick(MenuSelectionManager.defaultManager()); + } + } + + /** * Creates a new BasicMenuItemUI object. */ public BasicMenuItemUI() @@ -173,14 +237,15 @@ public class BasicMenuItemUI extends MenuItemUI mouseInputListener = createMouseInputListener(menuItem); menuDragMouseListener = createMenuDragMouseListener(menuItem); menuKeyListener = createMenuKeyListener(menuItem); + itemListener = new ItemHandler(); propertyChangeListener = new PropertyChangeHandler(); } /** * Create MenuDragMouseListener to listen for mouse dragged events. - * - * @param c menu item to listen to - * + * + * @param c + * menu item to listen to * @return The MenuDragMouseListener */ protected MenuDragMouseListener createMenuDragMouseListener(JComponent c) @@ -189,11 +254,11 @@ public class BasicMenuItemUI extends MenuItemUI } /** - * Creates MenuKeyListener to listen to key events occuring when menu item - * is visible on the screen. - * - * @param c menu item to listen to - * + * Creates MenuKeyListener to listen to key events occuring when menu item is + * visible on the screen. + * + * @param c + * menu item to listen to * @return The MenuKeyListener */ protected MenuKeyListener createMenuKeyListener(JComponent c) @@ -203,9 +268,9 @@ public class BasicMenuItemUI extends MenuItemUI /** * Handles mouse input events occuring for this menu item - * - * @param c menu item to listen to - * + * + * @param c + * menu item to listen to * @return The MouseInputListener */ protected MouseInputListener createMouseInputListener(JComponent c) @@ -216,9 +281,9 @@ public class BasicMenuItemUI extends MenuItemUI /** * Factory method to create a BasicMenuItemUI for the given {@link * JComponent}, which should be a {@link JMenuItem}. - * - * @param c The {@link JComponent} a UI is being created for. - * + * + * @param c + * The {@link JComponent} a UI is being created for. * @return A BasicMenuItemUI for the {@link JComponent}. */ public static ComponentUI createUI(JComponent c) @@ -228,8 +293,9 @@ public class BasicMenuItemUI extends MenuItemUI /** * Programatically clicks menu item. - * - * @param msm MenuSelectionManager for the menu hierarchy + * + * @param msm + * MenuSelectionManager for the menu hierarchy */ protected void doClick(MenuSelectionManager msm) { @@ -239,9 +305,9 @@ public class BasicMenuItemUI extends MenuItemUI /** * Returns maximum size for the specified menu item - * - * @param c component for which to get maximum size - * + * + * @param c + * component for which to get maximum size * @return Maximum size for the specified menu item. */ public Dimension getMaximumSize(JComponent c) @@ -251,9 +317,9 @@ public class BasicMenuItemUI extends MenuItemUI /** * Returns minimum size for the specified menu item - * - * @param c component for which to get minimum size - * + * + * @param c + * component for which to get minimum size * @return Minimum size for the specified menu item. */ public Dimension getMinimumSize(JComponent c) @@ -263,9 +329,9 @@ public class BasicMenuItemUI extends MenuItemUI /** * Returns path to this menu item. - * - * @return $MenuElement[]$ Returns array of menu elements - * that constitute a path to this menu item. + * + * @return $MenuElement[]$ Returns array of menu elements that constitute a + * path to this menu item. */ public MenuElement[] getPath() { @@ -278,12 +344,12 @@ public class BasicMenuItemUI extends MenuItemUI Component c = menuItem; while (c instanceof MenuElement) { - path.add(0, (MenuElement) c); + path.add(0, (MenuElement) c); - if (c instanceof JPopupMenu) - c = ((JPopupMenu) c).getInvoker(); - else - c = c.getParent(); + if (c instanceof JPopupMenu) + c = ((JPopupMenu) c).getInvoker(); + else + c = c.getParent(); } MenuElement[] pathArray = new MenuElement[path.size()]; @@ -293,12 +359,15 @@ public class BasicMenuItemUI extends MenuItemUI /** * Returns preferred size for the given menu item. - * - * @param c menu item for which to get preferred size - * @param checkIcon check icon displayed in the given menu item - * @param arrowIcon arrow icon displayed in the given menu item - * @param defaultTextIconGap space between icon and text in the given menuItem - * + * + * @param c + * menu item for which to get preferred size + * @param checkIcon + * check icon displayed in the given menu item + * @param arrowIcon + * arrow icon displayed in the given menu item + * @param defaultTextIconGap + * space between icon and text in the given menuItem * @return $Dimension$ preferred size for the given menu item */ protected Dimension getPreferredMenuItemSize(JComponent c, Icon checkIcon, @@ -308,7 +377,7 @@ public class BasicMenuItemUI extends MenuItemUI JMenuItem m = (JMenuItem) c; Dimension d = BasicGraphicsUtils.getPreferredButtonSize(m, defaultTextIconGap); - + // if menu item has accelerator then take accelerator's size into account // when calculating preferred size. KeyStroke accelerator = m.getAccelerator(); @@ -316,52 +385,57 @@ public class BasicMenuItemUI extends MenuItemUI if (accelerator != null) { - rect = getAcceleratorRect(accelerator, - m.getToolkit().getFontMetrics(acceleratorFont)); + rect = getAcceleratorRect( + accelerator, + m.getToolkit().getFontMetrics(acceleratorFont)); - // add width of accelerator's text - d.width = d.width + rect.width + defaultAcceleratorLabelGap; + // add width of accelerator's text + d.width += rect.width + defaultAcceleratorLabelGap; - // adjust the heigth of the preferred size if necessary - if (d.height < rect.height) - d.height = rect.height; + // adjust the heigth of the preferred size if necessary + if (d.height < rect.height) + d.height = rect.height; } if (checkIcon != null) { - d.width = d.width + checkIcon.getIconWidth() + defaultTextIconGap; + d.width += checkIcon.getIconWidth() + defaultTextIconGap; - if (checkIcon.getIconHeight() > d.height) - d.height = checkIcon.getIconHeight(); + if (checkIcon.getIconHeight() > d.height) + d.height = checkIcon.getIconHeight(); } if (arrowIcon != null && (c instanceof JMenu)) { - d.width = d.width + arrowIcon.getIconWidth() + defaultTextIconGap; - - if (arrowIcon.getIconHeight() > d.height) - d.height = arrowIcon.getIconHeight(); + int pWidth = m.getParent().getWidth(); + if (!((JMenu)c).isTopLevelMenu() && d.width < pWidth) + d.width = pWidth + - m.getInsets().left - m.getInsets().right; + else + d.width += arrowIcon.getIconWidth() + MenuGap; + + if (arrowIcon.getIconHeight() > d.height) + d.height = arrowIcon.getIconHeight(); } - + return d; } /** * Returns preferred size of the given component - * - * @param c component for which to return preferred size - * + * + * @param c + * component for which to return preferred size * @return $Dimension$ preferred size for the given component */ public Dimension getPreferredSize(JComponent c) { - return getPreferredMenuItemSize(c, checkIcon, arrowIcon, - defaultTextIconGap); + return getPreferredMenuItemSize(c, checkIcon, arrowIcon, defaultTextIconGap); } /** * Returns the prefix for entries in the {@link UIDefaults} table. - * + * * @return "MenuItem" */ protected String getPropertyPrefix() @@ -371,8 +445,9 @@ public class BasicMenuItemUI extends MenuItemUI /** * This method installs the components for this {@link JMenuItem}. - * - * @param menuItem The {@link JMenuItem} to install components for. + * + * @param menuItem + * The {@link JMenuItem} to install components for. */ protected void installComponents(JMenuItem menuItem) { @@ -380,28 +455,27 @@ public class BasicMenuItemUI extends MenuItemUI } /** - * This method installs the defaults that are defined in the Basic look and + * This method installs the defaults that are defined in the Basic look and * feel for this {@link JMenuItem}. */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - menuItem.setBackground(defaults.getColor("MenuItem.background")); - menuItem.setBorder(defaults.getBorder("MenuItem.border")); - menuItem.setFont(defaults.getFont("MenuItem.font")); - menuItem.setForeground(defaults.getColor("MenuItem.foreground")); - menuItem.setMargin(defaults.getInsets("MenuItem.margin")); - menuItem.setOpaque(true); - acceleratorFont = defaults.getFont("MenuItem.acceleratorFont"); - acceleratorForeground = defaults.getColor("MenuItem.acceleratorForeground"); - acceleratorSelectionForeground = defaults.getColor("MenuItem.acceleratorSelectionForeground"); - selectionBackground = defaults.getColor("MenuItem.selectionBackground"); - selectionForeground = defaults.getColor("MenuItem.selectionForeground"); - acceleratorDelimiter = defaults.getString("MenuItem.acceleratorDelimiter"); - + String prefix = getPropertyPrefix(); + LookAndFeel.installBorder(menuItem, prefix + ".border"); + LookAndFeel.installColorsAndFont(menuItem, prefix + ".background", + prefix + ".foreground", prefix + ".font"); + menuItem.setMargin(UIManager.getInsets(prefix + ".margin")); + acceleratorFont = UIManager.getFont(prefix + ".acceleratorFont"); + acceleratorForeground = UIManager.getColor(prefix + ".acceleratorForeground"); + acceleratorSelectionForeground = UIManager.getColor(prefix + ".acceleratorSelectionForeground"); + selectionBackground = UIManager.getColor(prefix + ".selectionBackground"); + selectionForeground = UIManager.getColor(prefix + ".selectionForeground"); + acceleratorDelimiter = UIManager.getString(prefix + ".acceleratorDelimiter"); + checkIcon = UIManager.getIcon(prefix + ".checkIcon"); + menuItem.setHorizontalTextPosition(SwingConstants.TRAILING); menuItem.setHorizontalAlignment(SwingConstants.LEADING); + menuItem.setOpaque(true); } /** @@ -409,7 +483,17 @@ public class BasicMenuItemUI extends MenuItemUI */ protected void installKeyboardActions() { - // FIXME: Need to implement + InputMap focusedWindowMap = SwingUtilities.getUIInputMap(menuItem, JComponent.WHEN_IN_FOCUSED_WINDOW); + if (focusedWindowMap == null) + focusedWindowMap = new ComponentInputMapUIResource(menuItem); + focusedWindowMap.put(menuItem.getAccelerator(), "doClick"); + SwingUtilities.replaceUIInputMap(menuItem, JComponent.WHEN_IN_FOCUSED_WINDOW, focusedWindowMap); + + ActionMap UIActionMap = SwingUtilities.getUIActionMap(menuItem); + if (UIActionMap == null) + UIActionMap = new ActionMapUIResource(); + UIActionMap.put("doClick", new ClickAction()); + SwingUtilities.replaceUIActionMap(menuItem, UIActionMap); } /** @@ -421,15 +505,17 @@ public class BasicMenuItemUI extends MenuItemUI menuItem.addMouseMotionListener(mouseInputListener); menuItem.addMenuDragMouseListener(menuDragMouseListener); menuItem.addMenuKeyListener(menuKeyListener); + menuItem.addItemListener(itemListener); menuItem.addPropertyChangeListener(propertyChangeListener); } /** - * Installs and initializes all fields for this UI delegate. Any properties - * of the UI that need to be initialized and/or set to defaults will be - * done now. It will also install any listeners necessary. - * - * @param c The {@link JComponent} that is having this UI installed. + * Installs and initializes all fields for this UI delegate. Any properties of + * the UI that need to be initialized and/or set to defaults will be done now. + * It will also install any listeners necessary. + * + * @param c + * The {@link JComponent} that is having this UI installed. */ public void installUI(JComponent c) { @@ -438,13 +524,16 @@ public class BasicMenuItemUI extends MenuItemUI installDefaults(); installComponents(menuItem); installListeners(); + installKeyboardActions(); } /** * Paints given menu item using specified graphics context - * - * @param g The graphics context used to paint this menu item - * @param c Menu Item to paint + * + * @param g + * The graphics context used to paint this menu item + * @param c + * Menu Item to paint */ public void paint(Graphics g, JComponent c) { @@ -454,10 +543,13 @@ public class BasicMenuItemUI extends MenuItemUI /** * Paints background of the menu item - * - * @param g The graphics context used to paint this menu item - * @param menuItem menu item to paint - * @param bgColor Background color to use when painting menu item + * + * @param g + * The graphics context used to paint this menu item + * @param menuItem + * menu item to paint + * @param bgColor + * Background color to use when painting menu item */ protected void paintBackground(Graphics g, JMenuItem menuItem, Color bgColor) { @@ -470,15 +562,21 @@ public class BasicMenuItemUI extends MenuItemUI /** * Paints specified menu item - * - * @param g The graphics context used to paint this menu item - * @param c menu item to paint - * @param checkIcon check icon to use when painting menu item - * @param arrowIcon arrow icon to use when painting menu item - * @param background Background color of the menu item - * @param foreground Foreground color of the menu item - * @param defaultTextIconGap space to use between icon and - * text when painting menu item + * + * @param g + * The graphics context used to paint this menu item + * @param c + * menu item to paint + * @param checkIcon + * check icon to use when painting menu item + * @param arrowIcon + * arrow icon to use when painting menu item + * @param background + * Background color of the menu item + * @param foreground + * Foreground color of the menu item + * @param defaultTextIconGap + * space to use between icon and text when painting menu item */ protected void paintMenuItem(Graphics g, JComponent c, Icon checkIcon, Icon arrowIcon, Color background, @@ -496,7 +594,7 @@ public class BasicMenuItemUI extends MenuItemUI int horAlign = m.getHorizontalAlignment(); int vertTextPos = m.getVerticalTextPosition(); int horTextPos = m.getHorizontalTextPosition(); - + Font f = m.getFont(); g.setFont(f); FontMetrics fm = g.getFontMetrics(f); @@ -504,8 +602,10 @@ public class BasicMenuItemUI extends MenuItemUI SwingUtilities.calculateInsetArea(br, m.getInsets(), vr); paintBackground(g, m, m.getBackground()); - /* MenuItems insets are equal to menuItems margin, space between text and - menuItems border. We need to paint insets region as well. */ + /* + * MenuItems insets are equal to menuItems margin, space between text and + * menuItems border. We need to paint insets region as well. + */ Insets insets = m.getInsets(); br.x -= insets.left; br.y -= insets.top; @@ -514,89 +614,93 @@ public class BasicMenuItemUI extends MenuItemUI // Menu item is considered to be highlighted when it is selected. // But we don't want to paint the background of JCheckBoxMenuItems - if ((m.isSelected() && checkIcon == null) || m.getModel().isArmed() && - (m.getParent() instanceof MenuElement)) + ButtonModel mod = m.getModel(); + if ((m.isSelected() && checkIcon == null) || (mod != null && + mod.isArmed()) + && (m.getParent() instanceof MenuElement)) { - if (m.isContentAreaFilled()) - { - g.setColor(selectionBackground); - g.fillRect(br.x, br.y, br.width, br.height); - } + if (m.isContentAreaFilled()) + { + g.setColor(selectionBackground); + g.fillRect(br.x, br.y, br.width, br.height); + } } else { - if (m.isContentAreaFilled()) - { - g.setColor(m.getBackground()); - g.fillRect(br.x, br.y, br.width, br.height); - } + if (m.isContentAreaFilled()) + { + g.setColor(m.getBackground()); + g.fillRect(br.x, br.y, br.width, br.height); + } } // If this menu item is a JCheckBoxMenuItem then paint check icon if (checkIcon != null) { - SwingUtilities.layoutCompoundLabel(m, fm, null, checkIcon, vertAlign, - horAlign, vertTextPos, horTextPos, - vr, cr, tr, defaultTextIconGap); + SwingUtilities.layoutCompoundLabel(m, fm, null, checkIcon, vertAlign, + horAlign, vertTextPos, horTextPos, + vr, cr, tr, defaultTextIconGap); checkIcon.paintIcon(m, g, cr.x, cr.y); - // We need to calculate position of the menu text and position of - // user menu icon if there exists one relative to the check icon. - // So we need to adjust view rectangle s.t. its starting point is at - // checkIcon.width + defaultTextIconGap. - vr.x = cr.x + cr.width + defaultTextIconGap; + // We need to calculate position of the menu text and position of + // user menu icon if there exists one relative to the check icon. + // So we need to adjust view rectangle s.t. its starting point is at + // checkIcon.width + defaultTextIconGap. + vr.x = cr.x + cr.width + defaultTextIconGap; } // if this is a submenu, then paint arrow icon to indicate it. if (arrowIcon != null && (c instanceof JMenu)) { - if (! ((JMenu) c).isTopLevelMenu()) - { - int width = arrowIcon.getIconWidth(); - int height = arrowIcon.getIconHeight(); - - arrowIcon.paintIcon(m, g, vr.width - width + defaultTextIconGap, - vr.y + 2); - } + if (!((JMenu) c).isTopLevelMenu()) + { + int width = arrowIcon.getIconWidth(); + int height = arrowIcon.getIconHeight(); + int offset = (vr.height - height) / 2; + arrowIcon.paintIcon(m, g, vr.width - width, vr.y + offset); + } } - // paint text and user menu icon if it exists + // paint text and user menu icon if it exists Icon i = m.getIcon(); - SwingUtilities.layoutCompoundLabel(c, fm, m.getText(), i, - vertAlign, horAlign, vertTextPos, - horTextPos, vr, ir, tr, - defaultTextIconGap); + SwingUtilities.layoutCompoundLabel(c, fm, m.getText(), i, vertAlign, + horAlign, vertTextPos, horTextPos, vr, + ir, tr, defaultTextIconGap); if (i != null) i.paintIcon(c, g, ir.x, ir.y); paintText(g, m, tr, m.getText()); - // paint accelerator + // paint accelerator String acceleratorText = ""; if (m.getAccelerator() != null) { - acceleratorText = getAcceleratorText(m.getAccelerator()); - fm = g.getFontMetrics(acceleratorFont); - ar.width = fm.stringWidth(acceleratorText); - ar.x = br.width - ar.width; - vr.x = br.width - ar.width; - - SwingUtilities.layoutCompoundLabel(m, fm, acceleratorText, null, - vertAlign, horAlign, vertTextPos, - horTextPos, vr, ir, ar, - defaultTextIconGap); - - paintAccelerator(g, m, ar, acceleratorText); + acceleratorText = getAcceleratorText(m.getAccelerator()); + fm = g.getFontMetrics(acceleratorFont); + ar.width = fm.stringWidth(acceleratorText); + ar.x = br.width - ar.width; + vr.x = br.width - ar.width - defaultTextIconGap; + + SwingUtilities.layoutCompoundLabel(m, fm, acceleratorText, null, + vertAlign, horAlign, vertTextPos, + horTextPos, vr, ir, ar, + defaultTextIconGap); + + paintAccelerator(g, m, ar, acceleratorText); } } /** * Paints label for the given menu item - * - * @param g The graphics context used to paint this menu item - * @param menuItem menu item for which to draw its label - * @param textRect rectangle specifiying position of the text relative to - * the given menu item - * @param text label of the menu item + * + * @param g + * The graphics context used to paint this menu item + * @param menuItem + * menu item for which to draw its label + * @param textRect + * rectangle specifiying position of the text relative to the given + * menu item + * @param text + * label of the menu item */ protected void paintText(Graphics g, JMenuItem menuItem, Rectangle textRect, String text) @@ -605,43 +709,46 @@ public class BasicMenuItemUI extends MenuItemUI g.setFont(f); FontMetrics fm = g.getFontMetrics(f); - if (text != null && ! text.equals("")) + if (text != null && !text.equals("")) { - if (menuItem.isEnabled()) + if (menuItem.isEnabled()) { // Menu item is considered to be highlighted when it is selected. // But not if it's a JCheckBoxMenuItem - if ((menuItem.isSelected() && checkIcon == null) || menuItem.getModel().isArmed() && - (menuItem.getParent() instanceof MenuElement)) + ButtonModel mod = menuItem.getModel(); + if ((menuItem.isSelected() && checkIcon == null) + || (mod != null && mod.isArmed()) + && (menuItem.getParent() instanceof MenuElement)) g.setColor(selectionForeground); else g.setColor(menuItem.getForeground()); } - else - // FIXME: should fix this to use 'disabledForeground', but its - // default value in BasicLookAndFeel is null. - + else + // FIXME: should fix this to use 'disabledForeground', but its + // default value in BasicLookAndFeel is null. + // FIXME: should there be different foreground colours for selected // or deselected, when disabled? g.setColor(Color.gray); - int mnemonicIndex = menuItem.getDisplayedMnemonicIndex(); + int mnemonicIndex = menuItem.getDisplayedMnemonicIndex(); - if (mnemonicIndex != -1) - BasicGraphicsUtils.drawStringUnderlineCharAt(g, text, mnemonicIndex, - textRect.x, - textRect.y - + fm.getAscent()); - else - BasicGraphicsUtils.drawString(g, text, 0, textRect.x, - textRect.y + fm.getAscent()); + if (mnemonicIndex != -1) + BasicGraphicsUtils.drawStringUnderlineCharAt(g, text, mnemonicIndex, + textRect.x, + textRect.y + + fm.getAscent()); + else + BasicGraphicsUtils.drawString(g, text, 0, textRect.x, + textRect.y + fm.getAscent()); } } /** * This method uninstalls the components for this {@link JMenuItem}. - * - * @param menuItem The {@link JMenuItem} to uninstall components for. + * + * @param menuItem + * The {@link JMenuItem} to uninstall components for. */ protected void uninstallComponents(JMenuItem menuItem) { @@ -676,8 +783,9 @@ public class BasicMenuItemUI extends MenuItemUI * Uninstalls any keyboard actions. */ protected void uninstallKeyboardActions() - { - // FIXME: need to implement + { + SwingUtilities.replaceUIInputMap(menuItem, + JComponent.WHEN_IN_FOCUSED_WINDOW, null); } /** @@ -688,15 +796,17 @@ public class BasicMenuItemUI extends MenuItemUI menuItem.removeMouseListener(mouseInputListener); menuItem.removeMenuDragMouseListener(menuDragMouseListener); menuItem.removeMenuKeyListener(menuKeyListener); + menuItem.removeItemListener(itemListener); menuItem.removePropertyChangeListener(propertyChangeListener); } /** * Performs the opposite of installUI. Any properties or resources that need - * to be cleaned up will be done now. It will also uninstall any listeners - * it has. In addition, any properties of this UI will be nulled. - * - * @param c The {@link JComponent} that is having this UI uninstalled. + * to be cleaned up will be done now. It will also uninstall any listeners it + * has. In addition, any properties of this UI will be nulled. + * + * @param c + * The {@link JComponent} that is having this UI uninstalled. */ public void uninstallUI(JComponent c) { @@ -708,9 +818,11 @@ public class BasicMenuItemUI extends MenuItemUI /** * This method calls paint. - * - * @param g The graphics context used to paint this menu item - * @param c The menu item to paint + * + * @param g + * The graphics context used to paint this menu item + * @param c + * The menu item to paint */ public void update(Graphics g, JComponent c) { @@ -719,9 +831,9 @@ public class BasicMenuItemUI extends MenuItemUI /** * Return text representation of the specified accelerator - * - * @param accelerator Accelerator for which to return string representation - * + * + * @param accelerator + * Accelerator for which to return string representation * @return $String$ Text representation of the given accelerator */ private String getAcceleratorText(KeyStroke accelerator) @@ -744,10 +856,11 @@ public class BasicMenuItemUI extends MenuItemUI /** * Calculates and return rectange in which accelerator should be displayed - * - * @param accelerator accelerator for which to return the display rectangle - * @param fm The font metrics used to measure the text - * + * + * @param accelerator + * accelerator for which to return the display rectangle + * @param fm + * The font metrics used to measure the text * @return $Rectangle$ reactangle which will be used to display accelerator */ private Rectangle getAcceleratorRect(KeyStroke accelerator, FontMetrics fm) @@ -759,12 +872,16 @@ public class BasicMenuItemUI extends MenuItemUI /** * Paints accelerator inside menu item - * - * @param g The graphics context used to paint the border - * @param menuItem Menu item for which to draw accelerator - * @param acceleratorRect rectangle representing position - * of the accelerator relative to the menu item - * @param acceleratorText accelerator's text + * + * @param g + * The graphics context used to paint the border + * @param menuItem + * Menu item for which to draw accelerator + * @param acceleratorRect + * rectangle representing position of the accelerator relative to the + * menu item + * @param acceleratorText + * accelerator's text */ private void paintAccelerator(Graphics g, JMenuItem menuItem, Rectangle acceleratorRect, @@ -785,10 +902,9 @@ public class BasicMenuItemUI extends MenuItemUI } /** - * This class handles mouse events occuring inside the menu item. - * Most of the events are forwarded for processing to MenuSelectionManager - * of the current menu hierarchy. - * + * This class handles mouse events occuring inside the menu item. Most of the + * events are forwarded for processing to MenuSelectionManager of the current + * menu hierarchy. */ protected class MouseInputHandler implements MouseInputListener { @@ -797,13 +913,15 @@ public class BasicMenuItemUI extends MenuItemUI */ protected MouseInputHandler() { + // Nothing to do here. } /** - * This method is called when mouse is clicked on the menu item. - * It forwards this event to MenuSelectionManager. - * - * @param e A {@link MouseEvent}. + * This method is called when mouse is clicked on the menu item. It forwards + * this event to MenuSelectionManager. + * + * @param e + * A {@link MouseEvent}. */ public void mouseClicked(MouseEvent e) { @@ -812,10 +930,11 @@ public class BasicMenuItemUI extends MenuItemUI } /** - * This method is called when mouse is dragged inside the menu item. - * It forwards this event to MenuSelectionManager. - * - * @param e A {@link MouseEvent}. + * This method is called when mouse is dragged inside the menu item. It + * forwards this event to MenuSelectionManager. + * + * @param e + * A {@link MouseEvent}. */ public void mouseDragged(MouseEvent e) { @@ -824,29 +943,31 @@ public class BasicMenuItemUI extends MenuItemUI } /** - * This method is called when mouse enters menu item. - * When this happens menu item is considered to be selected and selection path - * in MenuSelectionManager is set. This event is also forwarded to MenuSelection - * Manager for further processing. - * - * @param e A {@link MouseEvent}. + * This method is called when mouse enters menu item. When this happens menu + * item is considered to be selected and selection path in + * MenuSelectionManager is set. This event is also forwarded to + * MenuSelection Manager for further processing. + * + * @param e + * A {@link MouseEvent}. */ public void mouseEntered(MouseEvent e) { Component source = (Component) e.getSource(); if (source.getParent() instanceof MenuElement) { - MenuSelectionManager manager = MenuSelectionManager.defaultManager(); - manager.setSelectedPath(getPath()); - manager.processMouseEvent(e); + MenuSelectionManager manager = MenuSelectionManager.defaultManager(); + manager.setSelectedPath(getPath()); + manager.processMouseEvent(e); } } /** - * This method is called when mouse exits menu item. The event is - * forwarded to MenuSelectionManager for processing. - * - * @param e A {@link MouseEvent}. + * This method is called when mouse exits menu item. The event is forwarded + * to MenuSelectionManager for processing. + * + * @param e + * A {@link MouseEvent}. */ public void mouseExited(MouseEvent e) { @@ -855,10 +976,11 @@ public class BasicMenuItemUI extends MenuItemUI } /** - * This method is called when mouse is inside the menu item. - * This event is forwarder to MenuSelectionManager for further processing. - * - * @param e A {@link MouseEvent}. + * This method is called when mouse is inside the menu item. This event is + * forwarder to MenuSelectionManager for further processing. + * + * @param e + * A {@link MouseEvent}. */ public void mouseMoved(MouseEvent e) { @@ -869,8 +991,9 @@ public class BasicMenuItemUI extends MenuItemUI /** * This method is called when mouse is pressed. This event is forwarded to * MenuSelectionManager for further processing. - * - * @param e A {@link MouseEvent}. + * + * @param e + * A {@link MouseEvent}. */ public void mousePressed(MouseEvent e) { @@ -882,8 +1005,9 @@ public class BasicMenuItemUI extends MenuItemUI * This method is called when mouse is released. If the mouse is released * inside this menuItem, then this menu item is considered to be chosen and * the menu hierarchy should be closed. - * - * @param e A {@link MouseEvent}. + * + * @param e + * A {@link MouseEvent}. */ public void mouseReleased(MouseEvent e) { @@ -892,24 +1016,25 @@ public class BasicMenuItemUI extends MenuItemUI if (e.getX() > 0 && e.getX() < size.width && e.getY() > 0 && e.getY() < size.height) { - manager.clearSelectedPath(); - menuItem.doClick(); + manager.clearSelectedPath(); + menuItem.doClick(); } else - manager.processMouseEvent(e); + manager.processMouseEvent(e); } } /** * This class handles mouse dragged events. */ - protected class MenuDragMouseHandler implements MenuDragMouseListener + private class MenuDragMouseHandler implements MenuDragMouseListener { /** * Tbis method is invoked when mouse is dragged over the menu item. - * - * @param e The MenuDragMouseEvent + * + * @param e + * The MenuDragMouseEvent */ public void menuDragMouseDragged(MenuDragMouseEvent e) { @@ -918,10 +1043,11 @@ public class BasicMenuItemUI extends MenuItemUI } /** - * Tbis method is invoked when mouse enters the menu item while it is - * being dragged. - * - * @param e The MenuDragMouseEvent + * Tbis method is invoked when mouse enters the menu item while it is being + * dragged. + * + * @param e + * The MenuDragMouseEvent */ public void menuDragMouseEntered(MenuDragMouseEvent e) { @@ -930,27 +1056,29 @@ public class BasicMenuItemUI extends MenuItemUI } /** - * Tbis method is invoked when mouse exits the menu item while - * it is being dragged - * - * @param e The MenuDragMouseEvent + * Tbis method is invoked when mouse exits the menu item while it is being + * dragged + * + * @param e the MenuDragMouseEvent */ public void menuDragMouseExited(MenuDragMouseEvent e) { + // TODO: What should be done here, if anything? } /** - * Tbis method is invoked when mouse was dragged and released - * inside the menu item. - * - * @param e The MenuDragMouseEvent + * Tbis method is invoked when mouse was dragged and released inside the + * menu item. + * + * @param e + * The MenuDragMouseEvent */ public void menuDragMouseReleased(MenuDragMouseEvent e) { MenuElement[] path = e.getPath(); if (path[path.length - 1] instanceof JMenuItem) - ((JMenuItem) path[path.length - 1]).doClick(); + ((JMenuItem) path[path.length - 1]).doClick(); MenuSelectionManager manager = MenuSelectionManager.defaultManager(); manager.clearSelectedPath(); @@ -961,50 +1089,63 @@ public class BasicMenuItemUI extends MenuItemUI * This class handles key events occuring when menu item is visible on the * screen. */ - protected class MenuKeyHandler implements MenuKeyListener + private class MenuKeyHandler implements MenuKeyListener { /** * This method is invoked when key has been pressed - * - * @param e A {@link MenuKeyEvent}. + * + * @param e + * A {@link MenuKeyEvent}. */ public void menuKeyPressed(MenuKeyEvent e) { + // TODO: What should be done here, if anything? } /** * This method is invoked when key has been pressed - * - * @param e A {@link MenuKeyEvent}. + * + * @param e + * A {@link MenuKeyEvent}. */ public void menuKeyReleased(MenuKeyEvent e) { + // TODO: What should be done here, if anything? } /** - * This method is invoked when key has been typed - * It handles the mnemonic key for the menu item. - * - * @param e A {@link MenuKeyEvent}. + * This method is invoked when key has been typed It handles the mnemonic + * key for the menu item. + * + * @param e + * A {@link MenuKeyEvent}. */ public void menuKeyTyped(MenuKeyEvent e) { + // TODO: What should be done here, if anything? } } - + /** - * Helper class that listens for changes to the properties of the {@link + * Helper class that listens for item changes to the properties of the {@link * JMenuItem}. */ - protected class PropertyChangeHandler implements PropertyChangeListener + private class ItemHandler implements ItemListener { /** - * This method is called when one of the menu item's properties change. + * This method is called when one of the menu item changes. * - * @param evt A {@link PropertyChangeEvent}. + * @param evt A {@link ItemEvent}. */ - public void propertyChange(PropertyChangeEvent evt) + public void itemStateChanged(ItemEvent evt) { + boolean state = false; + if (menuItem instanceof JCheckBoxMenuItem) + { + if (evt.getStateChange() == ItemEvent.SELECTED) + state = true; + ((JCheckBoxMenuItem) menuItem).setState(state); + } menuItem.revalidate(); menuItem.repaint(); } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java index 30be592ee79..827cbb0f50d 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Component; import java.awt.Dimension; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; @@ -46,8 +47,8 @@ import java.beans.PropertyChangeListener; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JMenuBar; -import javax.swing.JMenuItem; import javax.swing.JPopupMenu; +import javax.swing.LookAndFeel; import javax.swing.MenuSelectionManager; import javax.swing.UIDefaults; import javax.swing.UIManager; @@ -92,7 +93,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ protected ChangeListener createChangeListener(JComponent c) { - return new ChangeHandler(); + return new ChangeHandler((JMenu) c, this); } /** @@ -180,12 +181,6 @@ public class BasicMenuUI extends BasicMenuItemUI */ public Dimension getMaximumSize(JComponent c) { - // If this menu is in a popup menu, treat it like a regular JMenuItem - if (!((JMenu)c).isTopLevelMenu()) - { - JMenuItem menuItem = new JMenuItem(((JMenu)c).getText(), ((JMenu)c).getIcon()); - return menuItem.getMaximumSize(); - } return c.getPreferredSize(); } @@ -205,20 +200,17 @@ public class BasicMenuUI extends BasicMenuItemUI */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - menuItem.setBackground(defaults.getColor("Menu.background")); - menuItem.setBorder(defaults.getBorder("Menu.border")); - menuItem.setFont(defaults.getFont("Menu.font")); - menuItem.setForeground(defaults.getColor("Menu.foreground")); - menuItem.setMargin(defaults.getInsets("Menu.margin")); - acceleratorFont = defaults.getFont("Menu.acceleratorFont"); - acceleratorForeground = defaults.getColor("Menu.acceleratorForeground"); - acceleratorSelectionForeground = defaults.getColor("Menu.acceleratorSelectionForeground"); - selectionBackground = defaults.getColor("Menu.selectionBackground"); - selectionForeground = defaults.getColor("Menu.selectionForeground"); - arrowIcon = defaults.getIcon("Menu.arrowIcon"); - oldBorderPainted = defaults.getBoolean("Menu.borderPainted"); + LookAndFeel.installBorder(menuItem, "Menu.border"); + LookAndFeel.installColorsAndFont(menuItem, "Menu.background", + "Menu.foreground", "Menu.font"); + menuItem.setMargin(UIManager.getInsets("Menu.margin")); + acceleratorFont = UIManager.getFont("Menu.acceleratorFont"); + acceleratorForeground = UIManager.getColor("Menu.acceleratorForeground"); + acceleratorSelectionForeground = UIManager.getColor("Menu.acceleratorSelectionForeground"); + selectionBackground = UIManager.getColor("Menu.selectionBackground"); + selectionForeground = UIManager.getColor("Menu.selectionForeground"); + arrowIcon = UIManager.getIcon("Menu.arrowIcon"); + oldBorderPainted = UIManager.getBoolean("Menu.borderPainted"); menuItem.setOpaque(true); } @@ -245,6 +237,7 @@ public class BasicMenuUI extends BasicMenuItemUI protected void setupPostTimer(JMenu menu) { + // TODO: Implement this properly. } /** @@ -356,6 +349,7 @@ public class BasicMenuUI extends BasicMenuItemUI public void mouseMoved(MouseEvent e) { + // TODO: What should be done here, if anything? } public void mousePressed(MouseEvent e) @@ -421,10 +415,13 @@ public class BasicMenuUI extends BasicMenuItemUI public void menuDeselected(MenuEvent e) { JMenu menu = (JMenu) menuItem; - if (menu.isTopLevelMenu()) - ((JMenuBar) menu.getParent()).getSelectionModel().clearSelection(); - else - ((JPopupMenu) menu.getParent()).getSelectionModel().clearSelection(); + if (menu.getParent() != null) + { + if (menu.isTopLevelMenu()) + ((JMenuBar) menu.getParent()).getSelectionModel().clearSelection(); + else + ((JPopupMenu) menu.getParent()).getSelectionModel().clearSelection(); + } } /** @@ -456,6 +453,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void propertyChange(PropertyChangeEvent e) { + // TODO: Implement this properly. } } @@ -464,9 +462,40 @@ public class BasicMenuUI extends BasicMenuItemUI */ public class ChangeHandler implements ChangeListener { + /** + * Not used. + */ + public boolean isSelected; + + /** + * Not used. + */ + public JMenu menu; + + /** + * Not used. + */ + public BasicMenuUI ui; + + /** + * Not used. + */ + public Component wasFocused; + + /** + * Not used. + */ + public ChangeHandler(JMenu m, BasicMenuUI ui) + { + // Not used. + } + + /** + * Not used. + */ public void stateChanged(ChangeEvent e) { - // FIXME: It seems that this class is not used anywhere + // Not used. } } @@ -506,6 +535,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void menuDragMouseExited(MenuDragMouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -516,6 +546,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void menuDragMouseReleased(MenuDragMouseEvent e) { + // TODO: What should be done here, if anything? } } @@ -532,6 +563,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void menuKeyPressed(MenuKeyEvent e) { + // TODO: What should be done here, if anything? } /** @@ -541,6 +573,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void menuKeyReleased(MenuKeyEvent e) { + // TODO: What should be done here, if anything? } /** @@ -551,6 +584,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void menuKeyTyped(MenuKeyEvent e) { + // TODO: What should be done here, if anything? } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java index c9f623259ba..6b37d315fa8 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java @@ -70,8 +70,8 @@ import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; +import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.plaf.ComponentUI; @@ -141,13 +141,14 @@ public class BasicOptionPaneUI extends OptionPaneUI optionPane); if (inf != null) { - try - { - inf.setClosed(true); - } - catch (PropertyVetoException pve) - { - } + try + { + inf.setClosed(true); + } + catch (PropertyVetoException pve) + { + // We do nothing if attempt has been vetoed. + } } } } @@ -405,16 +406,30 @@ public class BasicOptionPaneUI extends OptionPaneUI || e.getPropertyName().equals(JOptionPane.WANTS_INPUT_PROPERTY) || e.getPropertyName().equals(JOptionPane.SELECTION_VALUES_PROPERTY)) { - optionPane.removeAll(); - messageAreaContainer = createMessageArea(); - optionPane.add(messageAreaContainer); - optionPane.add(buttonContainer); + optionPane.remove(messageAreaContainer); + messageAreaContainer = createMessageArea(); + optionPane.add(messageAreaContainer); + Container newButtons = createButtonArea(); + optionPane.remove(buttonContainer); + optionPane.add(newButtons); + buttonContainer = newButtons; + optionPane.add(buttonContainer); } optionPane.invalidate(); optionPane.repaint(); } } + /** + * The minimum width for JOptionPanes. + */ + public static final int MinimumWidth = 262; + + /** + * The minimum height for JOptionPanes. + */ + public static final int MinimumHeight = 90; + /** Whether the JOptionPane contains custom components. */ protected boolean hasCustomComponents = false; @@ -433,12 +448,6 @@ public class BasicOptionPaneUI extends OptionPaneUI /** The component that receives input when the JOptionPane needs it. */ protected JComponent inputComponent; - /** The minimum height of the JOptionPane. */ - public static int minimumHeight; - - /** The minimum width of the JOptionPane. */ - public static int minimumWidth; - /** The minimum dimensions of the JOptionPane. */ protected Dimension minimumSize; @@ -518,6 +527,7 @@ public class BasicOptionPaneUI extends OptionPaneUI */ public void paintIcon(Component c, Graphics g, int x, int y) { + // Nothing to do here. } } @@ -637,6 +647,7 @@ public class BasicOptionPaneUI extends OptionPaneUI */ public BasicOptionPaneUI() { + // Nothing to do here. } /** @@ -860,10 +871,10 @@ public class BasicOptionPaneUI extends OptionPaneUI addIcon(messageArea); JPanel rightSide = new JPanel(); - rightSide.setBorder(BorderFactory.createEmptyBorder(0, 11, 17, 0)); + rightSide.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); rightSide.setLayout(new GridBagLayout()); GridBagConstraints con = createConstraints(); - + addMessageComponents(rightSide, con, getMessage(), getMaxCharactersPerLineCount(), false); @@ -886,7 +897,7 @@ public class BasicOptionPaneUI extends OptionPaneUI } } - messageArea.add(rightSide, BorderLayout.EAST); + messageArea.add(rightSide, BorderLayout.CENTER); return messageArea; } @@ -944,8 +955,14 @@ public class BasicOptionPaneUI extends OptionPaneUI case JOptionPane.YES_NO_CANCEL_OPTION: return new Object[] { YES_STRING, NO_STRING, CANCEL_STRING }; case JOptionPane.OK_CANCEL_OPTION: - case JOptionPane.DEFAULT_OPTION: return new Object[] { OK_STRING, CANCEL_STRING }; + case JOptionPane.DEFAULT_OPTION: + return (optionPane.getWantsInput() ) ? + new Object[] { OK_STRING, CANCEL_STRING } : + ( optionPane.getMessageType() == JOptionPane.QUESTION_MESSAGE ) ? + new Object[] { YES_STRING, NO_STRING, CANCEL_STRING } : + // ERROR_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, PLAIN_MESSAGE + new Object[] { OK_STRING }; } return null; } @@ -1142,21 +1159,17 @@ public class BasicOptionPaneUI extends OptionPaneUI */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - optionPane.setFont(defaults.getFont("OptionPane.font")); - optionPane.setBackground(defaults.getColor("OptionPane.background")); - optionPane.setForeground(defaults.getColor("OptionPane.foreground")); - optionPane.setBorder(defaults.getBorder("OptionPane.border")); + LookAndFeel.installColorsAndFont(optionPane, "OptionPane.background", + "OptionPane.foreground", + "OptionPane.font"); + LookAndFeel.installBorder(optionPane, "OptionPane.border"); optionPane.setOpaque(true); - messageBorder = defaults.getBorder("OptionPane.messageAreaBorder"); - messageForeground = defaults.getColor("OptionPane.messageForeground"); - buttonBorder = defaults.getBorder("OptionPane.buttonAreaBorder"); + messageBorder = UIManager.getBorder("OptionPane.messageAreaBorder"); + messageForeground = UIManager.getColor("OptionPane.messageForeground"); + buttonBorder = UIManager.getBorder("OptionPane.buttonAreaBorder"); - minimumSize = defaults.getDimension("OptionPane.minimumSize"); - minimumWidth = minimumSize.width; - minimumHeight = minimumSize.height; + minimumSize = UIManager.getDimension("OptionPane.minimumSize"); // FIXME: Image icons don't seem to work properly right now. // Once they do, replace the synthetic icons with these ones. diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicPanelUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicPanelUI.java index b715c57b360..783cec473bc 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicPanelUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicPanelUI.java @@ -40,8 +40,7 @@ package javax.swing.plaf.basic; import javax.swing.JComponent; import javax.swing.JPanel; -import javax.swing.UIDefaults; -import javax.swing.UIManager; +import javax.swing.LookAndFeel; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.PanelUI; @@ -64,8 +63,29 @@ public class BasicPanelUI extends PanelUI public void installDefaults(JPanel p) { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - p.setBackground(defaults.getColor("Panel.background")); + LookAndFeel.installColorsAndFont(p, "Panel.background", "Panel.foreground", + "Panel.font"); p.setOpaque(true); } + + /** + * Uninstalls this UI from the JPanel. + * + * @param c the JPanel from which to uninstall this UI + */ + public void uninstallUI(JComponent c) + { + uninstallDefaults((JPanel) c); + } + + /** + * Uninstalls the UI defaults that have been install through + * {@link #installDefaults}. + * + * @param p the panel from which to uninstall the UI defaults + */ + protected void uninstallDefaults(JPanel p) + { + // Nothing to do here. + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicPasswordFieldUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicPasswordFieldUI.java index 044027b0b4a..76dcfc43559 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicPasswordFieldUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicPasswordFieldUI.java @@ -39,6 +39,7 @@ exception statement from your version. */ package javax.swing.plaf.basic; import javax.swing.JComponent; +import javax.swing.UIDefaults; import javax.swing.plaf.ComponentUI; import javax.swing.text.Element; import javax.swing.text.PasswordView; @@ -48,6 +49,7 @@ public class BasicPasswordFieldUI extends BasicTextFieldUI { public BasicPasswordFieldUI() { + // Nothing to do here. } public View create(Element elem) @@ -60,6 +62,11 @@ public class BasicPasswordFieldUI extends BasicTextFieldUI return new BasicPasswordFieldUI(); } + /** + * Returns the prefix for entries in the {@link UIDefaults} table. + * + * @return "PasswordField" + */ protected String getPropertyPrefix() { return "PasswordField"; diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java index 247117bc983..e15a17bab28 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java @@ -53,12 +53,11 @@ import javax.swing.JLayeredPane; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; +import javax.swing.LookAndFeel; import javax.swing.MenuElement; import javax.swing.MenuSelectionManager; import javax.swing.RootPaneContainer; import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; -import javax.swing.UIManager; import javax.swing.event.MouseInputListener; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; @@ -131,12 +130,9 @@ public class BasicPopupMenuUI extends PopupMenuUI */ public void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - popupMenu.setBackground(defaults.getColor("PopupMenu.background")); - popupMenu.setBorder(defaults.getBorder("PopupMenu.border")); - popupMenu.setFont(defaults.getFont("PopupMenu.font")); - popupMenu.setForeground(defaults.getColor("PopupMenu.foreground")); + LookAndFeel.installColorsAndFont(popupMenu, "PopupMenu.background", + "PopupMenu.foreground", "PopupMenu.font"); + LookAndFeel.installBorder(popupMenu, "PopupMenu.border"); popupMenu.setOpaque(true); } @@ -277,23 +273,26 @@ public class BasicPopupMenuUI extends PopupMenuUI RootPaneContainer rootContainer = (RootPaneContainer) SwingUtilities .getRoot(invoker); - ((Container) rootContainer).removeComponentListener(topWindowListener); - - // If this popup menu is the last popup menu visible on the screen, then - // stop interrupting mouse events in the glass pane before hiding this - // last popup menu. - boolean topLevelMenu = (popupMenu.getInvoker() instanceof JMenu) - && ((JMenu) popupMenu.getInvoker()) - .isTopLevelMenu(); - - if (topLevelMenu || ! (popupMenu.getInvoker() instanceof MenuElement)) + if (rootContainer != null) { - // set glass pane not to interrupt mouse events and remove - // mouseInputListener - Container glassPane = (Container) rootContainer.getGlassPane(); - glassPane.setVisible(false); - glassPane.removeMouseListener(mouseInputListener); - mouseInputListener = null; + ((Container) rootContainer).removeComponentListener(topWindowListener); + + // If this popup menu is the last popup menu visible on the screen, + // then + // stop interrupting mouse events in the glass pane before hiding this + // last popup menu. + boolean topLevelMenu = (popupMenu.getInvoker() instanceof JMenu) + && ((JMenu) popupMenu.getInvoker()).isTopLevelMenu(); + + if (topLevelMenu || !(popupMenu.getInvoker() instanceof MenuElement)) + { + // set glass pane not to interrupt mouse events and remove + // mouseInputListener + Container glassPane = (Container) rootContainer.getGlassPane(); + glassPane.setVisible(false); + glassPane.removeMouseListener(mouseInputListener); + mouseInputListener = null; + } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicProgressBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicProgressBarUI.java index d00628f53d4..88d949b1caf 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicProgressBarUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicProgressBarUI.java @@ -46,21 +46,24 @@ import java.awt.Graphics; import java.awt.Insets; 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.font.FontRenderContext; -import java.awt.geom.AffineTransform; -import java.awt.geom.Rectangle2D; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.JComponent; import javax.swing.JProgressBar; +import javax.swing.LookAndFeel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.Timer; -import javax.swing.UIDefaults; import javax.swing.UIManager; +import javax.swing.event.AncestorEvent; +import javax.swing.event.AncestorListener; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.ComponentUI; @@ -110,14 +113,56 @@ public class BasicProgressBarUI extends ProgressBarUI { // Only need to listen for indeterminate changes. // All other things are done on a repaint. - if (e.getPropertyName().equals("inderterminate")) - if (((Boolean) e.getNewValue()).booleanValue()) - startAnimationTimer(); - else - stopAnimationTimer(); - else - progressBar.repaint(); + if (e.getPropertyName().equals("indeterminate")) + if (((Boolean) e.getNewValue()).booleanValue() + && progressBar.isShowing()) + startAnimationTimer(); + else + stopAnimationTimer(); + } + } + + /** + * Receives notification when the progressbar is becoming visible or + * invisible and starts/stops the animation timer accordingly. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class AncestorHandler implements AncestorListener + { + + /** + * Receives notification when the progressbar is becoming visible. This + * starts the animation timer if the progressbar is indeterminate. + * + * @param event the ancestor event + */ + public void ancestorAdded(AncestorEvent event) + { + if (progressBar.isIndeterminate()) + startAnimationTimer(); + } + + /** + * Receives notification when the progressbar is becoming invisible. This + * stops the animation timer if the progressbar is indeterminate. + * + * @param event the ancestor event + */ + public void ancestorRemoved(AncestorEvent event) + { + stopAnimationTimer(); + } + + /** + * Receives notification when an ancestor has been moved. We don't need to + * do anything here. + */ + public void ancestorMoved(AncestorEvent event) + { + // Nothing to do here. } + } /** @@ -141,6 +186,35 @@ public class BasicProgressBarUI extends ProgressBarUI } } + /** + * Receives notification when the size of the progress bar changes and + * invalidates the layout information for the box calculation in + * {@link BasicProgressBarUI#getBox(Rectangle)}. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class ComponentHandler extends ComponentAdapter + { + /** + * Receives notification when the size of the progress bar changes and + * invalidates the layout information for the box calculation in + * {@link BasicProgressBarUI#getBox}. + * + * @param e the component event + */ + public void componentResized(ComponentEvent e) + { + boxDependent = -1; + boxIndependent = -1; + incr = -1; + } + } + + /** + * Holds the value of the bouncing box that is returned by {@link #getBox}. + */ + protected Rectangle boxRect; + /** The timer used to move the bouncing box. */ private transient Timer animationTimer; @@ -172,6 +246,27 @@ public class BasicProgressBarUI extends ProgressBarUI /** The progressBar for this UI. */ protected JProgressBar progressBar; + + /** + * The size of the box returned by {@link #getBox} in the orientation + * direction of the progress bar. This is package private to avoid accessor + * method. + */ + transient double boxDependent = - 1; + + /** + * The size of the box returned by {@link #getBox} against the orientation + * direction of the progress bar. This is package private to avoid accessor + * method. + */ + transient int boxIndependent = - 1; + + /** + * The increment for box animation. This is package private to avoid accessor + * method. + */ + transient double incr = -1; + /** The length of the cell. The cell is the painted part. */ private transient int cellLength; @@ -185,6 +280,18 @@ public class BasicProgressBarUI extends ProgressBarUI private transient Color selectionForeground; /** + * Listens for notification when the component becomes showing and + * starts/stops the animation timer. + */ + private AncestorListener ancestorListener; + + /** + * Listens for resize events on the progress bar and invalidates some + * layout info. + */ + private ComponentListener componentListener; + + /** * Creates a new BasicProgressBarUI object. */ public BasicProgressBarUI() @@ -248,48 +355,49 @@ public class BasicProgressBarUI extends ProgressBarUI { if (!progressBar.isIndeterminate()) return null; - //numFrames has to be an even number as defined by spec. - int iterations = numFrames / 2 + 1; + if (r == null) + r = new Rectangle(); - double boxDependent; - double boxIndependent; + Rectangle vr = new Rectangle(); + SwingUtilities.calculateInnerArea(progressBar, vr); - if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) + // Recalculate the metrics only when size of the progressbar has changed. + if (incr == -1 || boxDependent == -1 || boxIndependent == -1) { - Dimension dims = getPreferredInnerHorizontal(); - boxDependent = (double) dims.width / iterations; - boxIndependent = dims.height; - } - else - { - Dimension dims = getPreferredInnerVertical(); - boxDependent = (double) dims.height / iterations; - boxIndependent = dims.width; + //numFrames has to be an even number as defined by spec. + int iterations = numFrames / 2; + if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) + { + boxDependent = vr.width / 6.; + incr = ((double) (vr.width - boxDependent)) / (double) iterations; + boxIndependent = vr.height; + } + else + { + boxDependent = vr.height / 6.; + incr = ((double) (vr.height - boxDependent)) / (double) iterations; + boxIndependent = vr.width; + } } - Rectangle vr = new Rectangle(); - SwingUtilities.calculateInnerArea(progressBar, vr); - int index = getAnimationIndex(); - if (animationIndex > (numFrames + 1) / 2) + if (animationIndex > (numFrames) / 2) index = numFrames - getAnimationIndex(); if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) { - r.x = vr.x + (int) (index * boxDependent); - r.y = vr.y; - r.width = (int) boxDependent; - r.height = (int) boxIndependent; + r.x = vr.x + (int) (incr * index); + r.y = vr.y; + r.width = (int) boxDependent; + r.height = (int) boxIndependent; } else { - index++; - r.x = vr.x; - r.y = vr.height - (int) (index * boxDependent) + vr.y; - r.width = (int) boxIndependent; - r.height = (int) boxDependent; + r.x = vr.x; + r.y = vr.height - (int) (incr * index) + vr.y - (int) boxDependent; + r.width = (int) boxIndependent; + r.height = (int) boxDependent; } - return r; } @@ -324,7 +432,22 @@ public class BasicProgressBarUI extends ProgressBarUI */ public Dimension getMaximumSize(JComponent c) { - return getPreferredSize(c); + Insets insets = c.getInsets(); + Dimension ret; + int orientation = progressBar.getOrientation(); + if (orientation == JProgressBar.VERTICAL) + { + ret = getPreferredInnerVertical(); + ret.height = Short.MAX_VALUE; + ret.width += insets.left + insets.right; + } + else + { + ret = getPreferredInnerHorizontal(); + ret.width = Short.MAX_VALUE; + ret.height += insets.top + insets.bottom; + } + return ret; } /** @@ -338,7 +461,22 @@ public class BasicProgressBarUI extends ProgressBarUI */ public Dimension getMinimumSize(JComponent c) { - return getPreferredSize(c); + Insets insets = c.getInsets(); + Dimension ret; + int orientation = progressBar.getOrientation(); + if (orientation == JProgressBar.VERTICAL) + { + ret = getPreferredInnerVertical(); + ret.height = 10; + ret.width += insets.left + insets.right; + } + else + { + ret = getPreferredInnerHorizontal(); + ret.width = 10; + ret.height += insets.top + insets.bottom; + } + return ret; } /** @@ -351,11 +489,22 @@ public class BasicProgressBarUI extends ProgressBarUI */ protected Dimension getPreferredInnerHorizontal() { - Rectangle vr = new Rectangle(); + Font font = progressBar.getFont(); + FontMetrics fm = progressBar.getFontMetrics(font); - SwingUtilities.calculateInnerArea(progressBar, vr); + int stringWidth = 0; + String str = progressBar.getString(); + if (str != null) + stringWidth = fm.stringWidth(progressBar.getString()); + Insets i = progressBar.getInsets(); + int prefWidth = Math.max(200 - i.left - i.right, stringWidth); - return new Dimension(vr.width, vr.height); + int stringHeight = 0; + if (str != null) + stringHeight = fm.getHeight(); + int prefHeight = Math.max(16 - i.top - i.bottom, stringHeight); + + return new Dimension(prefWidth, prefHeight); } /** @@ -368,11 +517,22 @@ public class BasicProgressBarUI extends ProgressBarUI */ protected Dimension getPreferredInnerVertical() { - Rectangle vr = new Rectangle(); + Font font = progressBar.getFont(); + FontMetrics fm = progressBar.getFontMetrics(font); - SwingUtilities.calculateInnerArea(progressBar, vr); + int stringWidth = 0; + String str = progressBar.getString(); + if (str != null) + stringWidth = fm.stringWidth(progressBar.getString()); + Insets i = progressBar.getInsets(); + int prefHeight = Math.max(200 - i.left - i.right, stringWidth); + + int stringHeight = 0; + if (str != null) + stringHeight = fm.getHeight(); + int prefWidth = Math.max(16 - i.top - i.bottom, stringHeight); - return new Dimension(vr.width, vr.height); + return new Dimension(prefWidth, prefHeight); } /** @@ -386,36 +546,16 @@ public class BasicProgressBarUI extends ProgressBarUI */ public Dimension getPreferredSize(JComponent c) { - // The only thing we need to worry about is - // the text size. Insets insets = c.getInsets(); - - // make a fontrenderer context so that we can make assumptions about - // the string bounds - FontRenderContext ctx = new FontRenderContext(new AffineTransform(), - false, false); - Rectangle2D bounds = c.getFont().getStringBounds(progressBar.getString(), - ctx); - int textW = (int) bounds.getWidth(); - int textH = (int) bounds.getHeight(); - - if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) - { - if (textH < 20) - textH = 20; - if (textW < 200) - textW = 200; - } + Dimension ret; + int orientation = progressBar.getOrientation(); + if (orientation == JProgressBar.VERTICAL) + ret = getPreferredInnerVertical(); else - { - if (textH < 200) - textH = 200; - if (textW < 20) - textW = 20; - } - textW += insets.left + insets.right; - textH += insets.top + insets.bottom; - return new Dimension(textW, textH); + ret = getPreferredInnerHorizontal(); + ret.width += insets.left + insets.right; + ret.height += insets.top + insets.bottom; + return ret; } /** @@ -514,66 +654,22 @@ public class BasicProgressBarUI extends ProgressBarUI int min = progressBar.getMinimum(); int value = progressBar.getValue(); - Rectangle vr = new Rectangle(); - SwingUtilities.calculateInnerArea(c, vr); - - Rectangle or = c.getBounds(); - + Rectangle vr = SwingUtilities.calculateInnerArea(c, new Rectangle()); + Rectangle or = progressBar.getBounds(); Insets insets = c.getInsets(); int amountFull = getAmountFull(insets, or.width, or.height); - g.setColor(c.getBackground()); - g.fill3DRect(vr.x, vr.y, vr.width, vr.height, false); - - if (max != min && len != 0 && value > min) - { - int iterations = value / (space + len); - if (progressBar.getOrientation() == JProgressBar.HORIZONTAL) { - double spaceInUnits = space * (double) vr.width / (max - min); - double lenInUnits = len * (double) vr.width / (max - min); - double currX = vr.x; - g.setColor(c.getForeground()); - g.fill3DRect(vr.x, vr.y, amountFull, vr.height, true); - - g.setColor(c.getBackground()); - if (spaceInUnits != 0) - { - for (int i = 0; i < iterations; i++) - { - currX += lenInUnits; - g.fill3DRect((int) currX, vr.y, (int) spaceInUnits, - vr.height, true); - currX += spaceInUnits; - } - } + g.fillRect(vr.x, vr.y, amountFull, vr.height); } else { - double currY = vr.y; - double spaceInUnits = space * (double) vr.height / (max - min); - double lenInUnits = len * (double) vr.height / (max - min); - g.setColor(c.getForeground()); - g.fill3DRect(vr.x, vr.y + vr.height - amountFull, vr.width, - amountFull, true); - - g.setColor(c.getBackground()); - - if (spaceInUnits != 0) - { - for (int i = 0; i < iterations; i++) - { - currY -= lenInUnits + spaceInUnits; - g.fill3DRect(vr.x, (int) currY, vr.width, - (int) spaceInUnits, true); - } - } + g.fillRect(vr.x, vr.y + vr.height - amountFull, vr.width, amountFull); } - } if (progressBar.isStringPainted() && !progressBar.getString().equals("")) paintString(g, 0, 0, or.width, or.height, amountFull, insets); @@ -599,13 +695,12 @@ public class BasicProgressBarUI extends ProgressBarUI SwingUtilities.calculateInnerArea(c, vr); g.setColor(c.getBackground()); - g.fill3DRect(vr.x, vr.y, vr.width, vr.height, false); + g.fillRect(vr.x, vr.y, vr.width, vr.height); - Rectangle box = new Rectangle(); - getBox(box); + boxRect = getBox(boxRect); g.setColor(c.getForeground()); - g.fill3DRect(box.x, box.y, box.width, box.height, true); + g.fillRect(boxRect.x, boxRect.y, boxRect.width, boxRect.height); if (progressBar.isStringPainted() && !progressBar.getString().equals("")) paintString(g, 0, 0, or.width, or.height, @@ -628,23 +723,34 @@ public class BasicProgressBarUI extends ProgressBarUI protected void paintString(Graphics g, int x, int y, int width, int height, int amountFull, Insets b) { + // FIXME: We do not support vertical text painting because Java2D is needed + // for this. + if (progressBar.getOrientation() == JProgressBar.VERTICAL) + return; + // We want to place in the exact center of the bar. Point placement = getStringPlacement(g, progressBar.getString(), x + b.left, y + b.top, width - b.left - b.right, height - b.top - b.bottom); - Color saved = g.getColor(); - - // FIXME: The Color of the text should use selectionForeground and selectionBackground - // but that can't be done right now, so we'll use white in the mean time. - g.setColor(Color.WHITE); + Color savedColor = g.getColor(); + Shape savedClip = g.getClip(); FontMetrics fm = g.getFontMetrics(progressBar.getFont()); + int full = getAmountFull(b, width, height); + String str = progressBar.getString(); - g.drawString(progressBar.getString(), placement.x, - placement.y + fm.getAscent()); - - g.setColor(saved); + // We draw this string two times with different clips so that the text + // over the filled area is painted with selectionForeground and over + // the clear area with selectionBackground. + g.setColor(getSelectionForeground()); + g.setClip(0, 0, full + b.left, height); + g.drawString(str, placement.x, placement.y + fm.getAscent()); + g.setColor(getSelectionBackground()); + g.setClip(full + b.left, 0, width - full, height); + g.drawString(str, placement.x, placement.y + fm.getAscent()); + g.setClip(savedClip); + g.setColor(savedColor); } /** @@ -712,21 +818,19 @@ public class BasicProgressBarUI extends ProgressBarUI */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - progressBar.setFont(defaults.getFont("ProgressBar.font")); - progressBar.setForeground(defaults.getColor("ProgressBar.foreground")); - progressBar.setBackground(defaults.getColor("ProgressBar.background")); - progressBar.setBorder(defaults.getBorder("ProgressBar.border")); + LookAndFeel.installColorsAndFont(progressBar, "ProgressBar.background", + "ProgressBar.foreground", + "ProgressBar.font"); + LookAndFeel.installBorder(progressBar, "ProgressBar.border"); progressBar.setOpaque(true); - selectionForeground = defaults.getColor("ProgressBar.selectionForeground"); - selectionBackground = defaults.getColor("ProgressBar.selectionBackground"); - cellLength = defaults.getInt("ProgressBar.cellLength"); - cellSpacing = defaults.getInt("ProgressBar.cellSpacing"); + selectionForeground = UIManager.getColor("ProgressBar.selectionForeground"); + selectionBackground = UIManager.getColor("ProgressBar.selectionBackground"); + cellLength = UIManager.getInt("ProgressBar.cellLength"); + cellSpacing = UIManager.getInt("ProgressBar.cellSpacing"); - int repaintInterval = defaults.getInt("ProgressBar.repaintInterval"); - int cycleTime = defaults.getInt("ProgressBar.cycleTime"); + int repaintInterval = UIManager.getInt("ProgressBar.repaintInterval"); + int cycleTime = UIManager.getInt("ProgressBar.cycleTime"); if (cycleTime % repaintInterval != 0 && (cycleTime / repaintInterval) % 2 != 0) @@ -768,6 +872,12 @@ public class BasicProgressBarUI extends ProgressBarUI progressBar.addChangeListener(changeListener); progressBar.addPropertyChangeListener(propertyListener); animationTimer.addActionListener(animation); + + ancestorListener = new AncestorHandler(); + progressBar.addAncestorListener(ancestorListener); + + componentListener = new ComponentHandler(); + progressBar.addComponentListener(componentListener); } /** @@ -783,6 +893,14 @@ public class BasicProgressBarUI extends ProgressBarUI changeListener = null; propertyListener = null; animation = null; + + if (ancestorListener != null) + progressBar.removeAncestorListener(ancestorListener); + ancestorListener = null; + + if (componentListener != null) + progressBar.removeComponentListener(componentListener); + componentListener = null; } /** @@ -806,6 +924,8 @@ public class BasicProgressBarUI extends ProgressBarUI installDefaults(); installListeners(); } + if (progressBar.isIndeterminate()) + startAnimationTimer(); } /** @@ -824,4 +944,5 @@ public class BasicProgressBarUI extends ProgressBarUI animationTimer = null; progressBar = null; } + } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java index fa74a008bae..8af5ff7f95c 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java @@ -59,8 +59,6 @@ public class BasicRadioButtonMenuItemUI extends BasicMenuItemUI public BasicRadioButtonMenuItemUI() { super(); - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - checkIcon = defaults.getIcon("RadioButtonMenuItem.checkIcon"); } /** @@ -98,5 +96,7 @@ public class BasicRadioButtonMenuItemUI extends BasicMenuItemUI MenuElement[] path, MenuSelectionManager manager) { + // TODO: May not be implemented properly. + item.processMouseEvent(e, path, manager); } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java index fbd21241a0d..f3698e85908 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Color; +import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Rectangle; @@ -93,6 +95,10 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI b.setIcon(icon); if (b.getSelectedIcon() == null) b.setSelectedIcon(icon); + if (b.getDisabledIcon() == null) + b.setDisabledIcon(icon); + if (b.getDisabledSelectedIcon() == null) + b.setDisabledSelectedIcon(icon); } /** @@ -139,10 +145,14 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI g.setFont(f); Icon currentIcon = null; - if (b.isSelected()) + if (b.isSelected() && b.isEnabled()) currentIcon = b.getSelectedIcon(); - else + else if (!b.isSelected() && b.isEnabled()) currentIcon = b.getIcon(); + else if (b.isSelected() && !b.isEnabled()) + currentIcon = b.getDisabledSelectedIcon(); + else // (!b.isSelected() && !b.isEnabled()) + currentIcon = b.getDisabledIcon(); SwingUtilities.calculateInnerArea(b, vr); String text = SwingUtilities.layoutCompoundLabel @@ -157,6 +167,25 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI } if (text != null) paintText(g, b, tr, text); - paintFocus(g, b, vr, tr, ir); + // TODO: Figure out what is the size parameter? + if (b.hasFocus() && b.isFocusPainted() && b.isEnabled()) + paintFocus(g, tr, null); + } + + /** + * Paints the focus indicator for JRadioButtons. + * + * @param g the graphics context + * @param tr the rectangle for the text label + * @param size the size (??) + */ + // TODO: Figure out what for is the size parameter. + protected void paintFocus(Graphics g, Rectangle tr, Dimension size) + { + Color focusColor = UIManager.getColor(getPropertyPrefix() + ".focus"); + Color saved = g.getColor(); + g.setColor(focusColor); + g.drawRect(tr.x, tr.y, tr.width, tr.height); + g.setColor(saved); } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java index d97f7baea67..2a698e8a162 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java @@ -42,6 +42,7 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.JComponent; +import javax.swing.JRootPane; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.RootPaneUI; @@ -56,11 +57,127 @@ public class BasicRootPaneUI extends RootPaneUI public void installUI(JComponent c) { - c.setBackground(UIManager.getColor("control")); super.installUI(c); + if (c instanceof JRootPane) + { + JRootPane rp = (JRootPane) c; + installDefaults(rp); + installComponents(rp); + installListeners(rp); + installKeyboardActions(rp); + } + } + + /** + * Installs the look and feel defaults for JRootPane. + * + * @param rp the root pane to install the defaults to + */ + protected void installDefaults(JRootPane rp) + { + // Is this ok? + rp.setBackground(UIManager.getColor("control")); + } + + /** + * Installs additional look and feel components to the root pane. + * + * @param rp the root pane to install the components to + */ + protected void installComponents(JRootPane rp) + { + // All components are initialized in the JRootPane constructor, and since + // the createXXXPane methods are protected, I see no reasonable way, + // and no need to initialize them here. This method is here anyway + // for compatibility and to provide the necessary hooks to subclasses. + } + + /** + * Installs any look and feel specific listeners on the root pane. + * + * @param rp the root pane to install the listeners to + */ + protected void installListeners(JRootPane rp) + { + rp.addPropertyChangeListener(this); + } + + /** + * Installs look and feel keyboard actions on the root pane. + * + * @param rp the root pane to install the keyboard actions to + */ + protected void installKeyboardActions(JRootPane rp) + { + // We currently do not install any keyboard actions here. + // This method is here anyway for compatibility and to provide + // the necessary hooks to subclasses. } public void propertyChange(PropertyChangeEvent event) { + // TODO: Implement this properly. + } + + /** + * Uninstalls this UI from the root pane. This calls + * {@link #uninstallDefaults}, {@link #uninstallComponents}, + * {@link #uninstallListeners}, {@link #uninstallKeyboardActions} + * in this order. + * + * @param c the root pane to uninstall the UI from + */ + public void uninstallUI(JComponent c) + { + super.uninstallUI(c); + if (c instanceof JRootPane) + { + JRootPane rp = (JRootPane) c; + uninstallDefaults(rp); + uninstallComponents(rp); + uninstallListeners(rp); + uninstallKeyboardActions(rp); + } + } + + /** + * Uninstalls the look and feel defaults that have been installed in + * {@link #installDefaults}. + * + * @param rp the root pane to uninstall the defaults from + */ + protected void uninstallDefaults(JRootPane rp) + { + // We do nothing here. + } + + /** + * Uninstalls look and feel components from the root pane. + * + * @param rp the root pane to uninstall the components from + */ + protected void uninstallComponents(JRootPane rp) + { + // We do nothing here. + } + + /** + * Uninstalls any look and feel specific listeners from the root pane. + * + * @param rp the root pane to uninstall the listeners from + */ + protected void uninstallListeners(JRootPane rp) + { + rp.removePropertyChangeListener(this); + } + + /** + * Uninstalls look and feel keyboard actions from the root pane. + * + * @param rp the root pane to uninstall the keyboard actions from + */ + protected void uninstallKeyboardActions(JRootPane rp) + { + // We do nothing here. } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java index 22242afcd8a..2f5eaf391e1 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java @@ -58,6 +58,7 @@ import javax.swing.BoundedRangeModel; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JScrollBar; +import javax.swing.LookAndFeel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.Timer; @@ -80,6 +81,7 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected class ArrowButtonListener extends MouseAdapter { + /** * Move the thumb in the direction specified by the button's arrow. If * this button is held down, then it should keep moving the thumb. @@ -91,9 +93,10 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, scrollTimer.stop(); scrollListener.setScrollByBlock(false); if (e.getSource() == incrButton) - scrollListener.setDirection(POSITIVE_SCROLL); - else - scrollListener.setDirection(NEGATIVE_SCROLL); + scrollListener.setDirection(POSITIVE_SCROLL); + else if (e.getSource() == decrButton) + scrollListener.setDirection(NEGATIVE_SCROLL); + scrollTimer.setDelay(100); scrollTimer.start(); } @@ -105,6 +108,11 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, public void mouseReleased(MouseEvent e) { scrollTimer.stop(); + scrollTimer.setDelay(300); + if (e.getSource() == incrButton) + scrollByUnit(POSITIVE_SCROLL); + else if (e.getSource() == decrButton) + scrollByUnit(NEGATIVE_SCROLL); } } @@ -120,9 +128,8 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ public void stateChanged(ChangeEvent e) { - // System.err.println(this + ".stateChanged()"); calculatePreferredSize(); - getThumbBounds(); + updateThumbRect(); scrollbar.repaint(); } } @@ -141,31 +148,27 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, { if (e.getPropertyName().equals("model")) { - ((BoundedRangeModel) e.getOldValue()).removeChangeListener(modelListener); - scrollbar.getModel().addChangeListener(modelListener); - getThumbBounds(); + ((BoundedRangeModel) e.getOldValue()).removeChangeListener(modelListener); + scrollbar.getModel().addChangeListener(modelListener); + updateThumbRect(); } else if (e.getPropertyName().equals("orientation")) { - incrButton.removeMouseListener(buttonListener); - decrButton.removeMouseListener(buttonListener); - int orientation = scrollbar.getOrientation(); - switch (orientation) - { - case (JScrollBar.HORIZONTAL): - incrButton = createIncreaseButton(EAST); - decrButton = createDecreaseButton(WEST); - break; - default: - incrButton = createIncreaseButton(SOUTH); - decrButton = createDecreaseButton(NORTH); - break; - } - incrButton.addMouseListener(buttonListener); - decrButton.addMouseListener(buttonListener); - calculatePreferredSize(); + uninstallListeners(); + uninstallComponents(); + uninstallDefaults(); + installDefaults(); + installComponents(); + installListeners(); + } + else if (e.getPropertyName().equals("enabled")) + { + Boolean b = (Boolean) e.getNewValue(); + if (incrButton != null) + incrButton.setEnabled(b.booleanValue()); + if (decrButton != null) + decrButton.setEnabled(b.booleanValue()); } - scrollbar.repaint(); } } @@ -233,19 +236,19 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, { if (block) { - // Only need to check it if it's block scrolling - // We only block scroll if the click occurs - // in the track. - if (! trackListener.shouldScroll(direction)) - { - trackHighlight = NO_HIGHLIGHT; - scrollbar.repaint(); - return; - } - scrollByBlock(direction); + // Only need to check it if it's block scrolling + // We only block scroll if the click occurs + // in the track. + if (!trackListener.shouldScroll(direction)) + { + trackHighlight = NO_HIGHLIGHT; + scrollbar.repaint(); + return; + } + scrollByBlock(direction); } else - scrollByUnit(direction); + scrollByUnit(direction); } } @@ -316,9 +319,6 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, else value = valueForYPosition(currentMouseY); - if (value == scrollbar.getValue()) - return; - if (! thumbRect.contains(e.getPoint())) { scrollTimer.stop(); @@ -333,6 +333,7 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, trackHighlight = DECREASE_HIGHLIGHT; scrollListener.setDirection(NEGATIVE_SCROLL); } + scrollTimer.setDelay(100); scrollTimer.start(); } else @@ -343,8 +344,10 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, // "lower" edge of the thumb. The value at which // the cursor is at must be greater or equal // to that value. + + scrollListener.setScrollByBlock(false); scrollbar.setValueIsAdjusting(true); - offset = value - scrollbar.getValue(); + offset = value - scrollbar.getValue(); } scrollbar.repaint(); } @@ -357,11 +360,19 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ public void mouseReleased(MouseEvent e) { - trackHighlight = NO_HIGHLIGHT; scrollTimer.stop(); + scrollTimer.setDelay(300); + currentMouseX = e.getX(); + currentMouseY = e.getY(); - if (scrollbar.getValueIsAdjusting()) - scrollbar.setValueIsAdjusting(false); + if (shouldScroll(POSITIVE_SCROLL)) + scrollByBlock(POSITIVE_SCROLL); + else if (shouldScroll(NEGATIVE_SCROLL)) + scrollByBlock(NEGATIVE_SCROLL); + + trackHighlight = NO_HIGHLIGHT; + scrollListener.setScrollByBlock(false); + scrollbar.setValueIsAdjusting(true); scrollbar.repaint(); } @@ -381,6 +392,9 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, else value = valueForYPosition(currentMouseY); + if (thumbRect.contains(currentMouseX, currentMouseY)) + return false; + if (direction == POSITIVE_SCROLL) return (value > scrollbar.getValue()); else @@ -517,11 +531,7 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected JButton createIncreaseButton(int orientation) { - if (incrButton == null) - incrButton = new BasicArrowButton(orientation); - else - ((BasicArrowButton) incrButton).setDirection(orientation); - return incrButton; + return new BasicArrowButton(orientation); } /** @@ -534,11 +544,7 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected JButton createDecreaseButton(int orientation) { - if (decrButton == null) - decrButton = new BasicArrowButton(orientation); - else - ((BasicArrowButton) decrButton).setDirection(orientation); - return decrButton; + return new BasicArrowButton(orientation); } /** @@ -602,7 +608,7 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ public Dimension getMaximumSize(JComponent c) { - return getPreferredSize(c); + return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); } /** @@ -644,7 +650,6 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ void calculatePreferredSize() { - // System.err.println(this + ".calculatePreferredSize()"); int height; int width; height = width = 0; @@ -707,48 +712,6 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected Rectangle getThumbBounds() { - int max = scrollbar.getMaximum(); - int min = scrollbar.getMinimum(); - int value = scrollbar.getValue(); - int extent = scrollbar.getVisibleAmount(); - - // System.err.println(this + ".getThumbBounds()"); - if (max == min) - { - thumbRect.x = trackRect.x; - thumbRect.y = trackRect.y; - if (scrollbar.getOrientation() == HORIZONTAL) - { - thumbRect.width = getMinimumThumbSize().width; - thumbRect.height = trackRect.height; - } - else - { - thumbRect.width = trackRect.width; - thumbRect.height = getMinimumThumbSize().height; - } - return thumbRect; - } - - if (scrollbar.getOrientation() == HORIZONTAL) - { - thumbRect.x = trackRect.x; - thumbRect.x += (value - min) * trackRect.width / (max - min); - thumbRect.y = trackRect.y; - - thumbRect.width = Math.max(extent * trackRect.width / (max - min), - getMinimumThumbSize().width); - thumbRect.height = trackRect.height; - } - else - { - thumbRect.x = trackRect.x; - thumbRect.y = trackRect.y + value * trackRect.height / (max - min); - - thumbRect.width = trackRect.width; - thumbRect.height = Math.max(extent * trackRect.height / (max - min), - getMinimumThumbSize().height); - } return thumbRect; } @@ -760,22 +723,6 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected Rectangle getTrackBounds() { - SwingUtilities.calculateInnerArea(scrollbar, trackRect); - - if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL) - { - trackRect.width -= incrButton.getPreferredSize().getWidth(); - trackRect.width -= decrButton.getPreferredSize().getWidth(); - - trackRect.x += decrButton.getPreferredSize().getWidth(); - } - else - { - trackRect.height -= incrButton.getPreferredSize().getHeight(); - trackRect.height -= decrButton.getPreferredSize().getHeight(); - - trackRect.y += incrButton.getPreferredSize().getHeight(); - } return trackRect; } @@ -785,6 +732,18 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected void installComponents() { + if (incrButton != null) + scrollbar.add(incrButton); + if (decrButton != null) + scrollbar.add(decrButton); + } + + /** + * This method installs the defaults for the scrollbar specified by the + * Basic Look and Feel. + */ + protected void installDefaults() + { int orientation = scrollbar.getOrientation(); switch (orientation) { @@ -797,31 +756,20 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, decrButton = createDecreaseButton(NORTH); break; } - scrollbar.add(incrButton); - scrollbar.add(decrButton); - } - - /** - * This method installs the defaults for the scrollbar specified by the - * Basic Look and Feel. - */ - protected void installDefaults() - { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - scrollbar.setForeground(defaults.getColor("ScrollBar.foreground")); - scrollbar.setBackground(defaults.getColor("ScrollBar.background")); - scrollbar.setBorder(defaults.getBorder("ScrollBar.border")); + LookAndFeel.installColors(scrollbar, "ScrollBar.background", + "ScrollBar.foreground"); + LookAndFeel.installBorder(scrollbar, "ScrollBar.border"); scrollbar.setOpaque(true); scrollbar.setLayout(this); - thumbColor = defaults.getColor("ScrollBar.thumb"); - thumbDarkShadowColor = defaults.getColor("ScrollBar.thumbDarkShadow"); - thumbHighlightColor = defaults.getColor("ScrollBar.thumbHighlight"); - thumbLightShadowColor = defaults.getColor("ScrollBar.thumbShadow"); + thumbColor = UIManager.getColor("ScrollBar.thumb"); + thumbDarkShadowColor = UIManager.getColor("ScrollBar.thumbDarkShadow"); + thumbHighlightColor = UIManager.getColor("ScrollBar.thumbHighlight"); + thumbLightShadowColor = UIManager.getColor("ScrollBar.thumbShadow"); - maximumThumbSize = defaults.getDimension("ScrollBar.maximumThumbSize"); - minimumThumbSize = defaults.getDimension("ScrollBar.minimumThumbSize"); + maximumThumbSize = UIManager.getDimension("ScrollBar.maximumThumbSize"); + minimumThumbSize = UIManager.getDimension("ScrollBar.minimumThumbSize"); } /** @@ -873,11 +821,10 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, trackRect = new Rectangle(); thumbRect = new Rectangle(); - scrollTimer = new Timer(200, null); - scrollTimer.setRepeats(true); + scrollTimer = new Timer(300, null); + installDefaults(); installComponents(); - installDefaults(); configureScrollBarColors(); installListeners(); @@ -908,17 +855,20 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected void layoutHScrollbar(JScrollBar sb) { - // All we have to do is layout the 2 buttons? Rectangle vr = new Rectangle(); SwingUtilities.calculateInnerArea(scrollbar, vr); - // Update the rectangles. - getTrackBounds(); - getThumbBounds(); - Dimension incrDims = incrButton.getPreferredSize(); Dimension decrDims = decrButton.getPreferredSize(); + + // calculate and update the track bounds + SwingUtilities.calculateInnerArea(scrollbar, trackRect); + trackRect.width -= incrDims.getWidth(); + trackRect.width -= decrDims.getWidth(); + trackRect.x += decrDims.getWidth(); + updateThumbRect(); + decrButton.setBounds(vr.x, vr.y, decrDims.width, trackRect.height); incrButton.setBounds(trackRect.x + trackRect.width, vr.y, incrDims.width, trackRect.height); @@ -934,12 +884,16 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, Rectangle vr = new Rectangle(); SwingUtilities.calculateInnerArea(scrollbar, vr); - // Update rectangles - getTrackBounds(); - getThumbBounds(); - Dimension incrDims = incrButton.getPreferredSize(); Dimension decrDims = decrButton.getPreferredSize(); + + // Update rectangles + SwingUtilities.calculateInnerArea(scrollbar, trackRect); + trackRect.height -= incrDims.getHeight(); + trackRect.height -= decrDims.getHeight(); + trackRect.y += decrDims.getHeight(); + + updateThumbRect(); decrButton.setBounds(vr.x, vr.y, trackRect.width, decrDims.height); incrButton.setBounds(vr.x, trackRect.y + trackRect.height, @@ -947,6 +901,58 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, } /** + * Updates the thumb rect. + */ + void updateThumbRect() + { + int max = scrollbar.getMaximum(); + int min = scrollbar.getMinimum(); + int value = scrollbar.getValue(); + int extent = scrollbar.getVisibleAmount(); + if (max - extent <= min) + { + if (scrollbar.getOrientation() == JScrollBar.HORIZONTAL) + { + thumbRect.x = trackRect.x; + thumbRect.y = trackRect.y; + thumbRect.width = getMinimumThumbSize().width; + thumbRect.height = trackRect.height; + } + else + { + thumbRect.x = trackRect.x; + thumbRect.y = trackRect.y; + thumbRect.width = trackRect.width; + thumbRect.height = getMinimumThumbSize().height; + } + } + else + { + if (scrollbar.getOrientation() == JScrollBar.HORIZONTAL) + { + thumbRect.x = trackRect.x; + thumbRect.width = Math.max(extent * trackRect.width / (max - min), + getMinimumThumbSize().width); + int availableWidth = trackRect.width - thumbRect.width; + thumbRect.x += (value - min) * availableWidth / (max - min - extent); + thumbRect.y = trackRect.y; + thumbRect.height = trackRect.height; + } + else + { + thumbRect.x = trackRect.x; + thumbRect.height = Math.max(extent * trackRect.height / (max - min), + getMinimumThumbSize().height); + int availableHeight = trackRect.height - thumbRect.height; + thumbRect.y = trackRect.y + + (value - min) * availableHeight / (max - min - extent); + thumbRect.width = trackRect.width; + } + } + + } + + /** * This method returns the minimum size required for the layout. * * @param scrollbarContainer The Container that is laid out. @@ -1124,10 +1130,10 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected void uninstallComponents() { - scrollbar.remove(incrButton); - scrollbar.remove(decrButton); - incrButton = null; - decrButton = null; + if (incrButton != null) + scrollbar.remove(incrButton); + if (decrButton != null) + scrollbar.remove(decrButton); } /** @@ -1138,7 +1144,9 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, { scrollbar.setForeground(null); scrollbar.setBackground(null); - scrollbar.setBorder(null); + LookAndFeel.uninstallBorder(scrollbar); + incrButton = null; + decrButton = null; } /** @@ -1155,17 +1163,22 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected void uninstallListeners() { - scrollTimer.removeActionListener(scrollListener); + if (scrollTimer != null) + scrollTimer.removeActionListener(scrollListener); - scrollbar.getModel().removeChangeListener(modelListener); - scrollbar.removePropertyChangeListener(propertyChangeListener); - - decrButton.removeMouseListener(buttonListener); - incrButton.removeMouseListener(buttonListener); - - scrollbar.removeMouseListener(trackListener); - scrollbar.removeMouseMotionListener(trackListener); + if (scrollbar != null) + { + scrollbar.getModel().removeChangeListener(modelListener); + scrollbar.removePropertyChangeListener(propertyChangeListener); + scrollbar.removeMouseListener(trackListener); + scrollbar.removeMouseMotionListener(trackListener); + } + if (decrButton != null) + decrButton.removeMouseListener(buttonListener); + if (incrButton != null) + incrButton.removeMouseListener(buttonListener); + propertyChangeListener = null; modelListener = null; buttonListener = null; @@ -1182,8 +1195,8 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ public void uninstallUI(JComponent c) { - uninstallDefaults(); uninstallListeners(); + uninstallDefaults(); uninstallComponents(); scrollTimer = null; diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java index bd1576f37a5..808ed2763e9 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java @@ -40,13 +40,21 @@ package javax.swing.plaf.basic; import java.awt.Dimension; import java.awt.Graphics; +import java.awt.Point; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import javax.swing.JComponent; +import javax.swing.JScrollBar; import javax.swing.JScrollPane; +import javax.swing.JViewport; +import javax.swing.LookAndFeel; import javax.swing.ScrollPaneConstants; import javax.swing.ScrollPaneLayout; -import javax.swing.UIDefaults; -import javax.swing.UIManager; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ScrollPaneUI; @@ -54,9 +62,207 @@ public class BasicScrollPaneUI extends ScrollPaneUI implements ScrollPaneConstants { + /** + * Listens for changes in the state of the horizontal scrollbar's model and + * updates the scrollpane accordingly. + * + * @author Roman Kennke (kennke@aicas.com) + */ + public class HSBChangeListener implements ChangeListener + { + + /** + * Receives notification when the state of the horizontal scrollbar + * model has changed. + * + * @param event the change event + */ + public void stateChanged(ChangeEvent event) + { + JScrollBar hsb = scrollpane.getHorizontalScrollBar(); + JViewport vp = scrollpane.getViewport(); + Point viewPosition = vp.getViewPosition(); + int xpos = hsb.getValue(); + + if (xpos != viewPosition.x) + { + viewPosition.x = xpos; + vp.setViewPosition(viewPosition); + } + + viewPosition.y = 0; + JViewport columnHeader = scrollpane.getColumnHeader(); + if (columnHeader != null + && !columnHeader.getViewPosition().equals(viewPosition)) + columnHeader.setViewPosition(viewPosition); + } + + } + + /** + * Listens for changes in the state of the vertical scrollbar's model and + * updates the scrollpane accordingly. + * + * @author Roman Kennke (kennke@aicas.com) + */ + public class VSBChangeListener implements ChangeListener + { + + /** + * Receives notification when the state of the vertical scrollbar + * model has changed. + * + * @param event the change event + */ + public void stateChanged(ChangeEvent event) + { + JScrollBar vsb = scrollpane.getVerticalScrollBar(); + JViewport vp = scrollpane.getViewport(); + Point viewPosition = vp.getViewPosition(); + int ypos = vsb.getValue(); + if (ypos != viewPosition.y) + { + viewPosition.y = ypos; + vp.setViewPosition(viewPosition); + } + + viewPosition.x = 0; + JViewport rowHeader = scrollpane.getRowHeader(); + if (rowHeader != null + && !rowHeader.getViewPosition().equals(viewPosition)) + rowHeader.setViewPosition(viewPosition); + } + + } + + /** + * Listens for changes of the viewport's extent size and updates the + * scrollpane accordingly. + * + * @author Roman Kennke (kennke@aicas.com) + */ + public class ViewportChangeHandler implements ChangeListener + { + + /** + * Receives notification when the view's size, position or extent size + * changes. When the extents size has changed, this method calls + * {@link BasicScrollPaneUI#syncScrollPaneWithViewport()} to adjust the + * scrollbars extents as well. + * + * @param event the change event + */ + public void stateChanged(ChangeEvent event) + { + JViewport vp = scrollpane.getViewport(); + JScrollBar hsb = scrollpane.getHorizontalScrollBar(); + JScrollBar vsb = scrollpane.getVerticalScrollBar(); + syncScrollPaneWithViewport(); + } + + } + + /** + * Listens for property changes on the scrollpane and update the view + * accordingly. + * + * @author Roman Kennke (kennke@aicas.com) + */ + public class PropertyChangeHandler implements PropertyChangeListener + { + + /** + * Receives notification when any of the scrollpane's bound property + * changes. This method calls the appropriate update method on the + * <code>ScrollBarUI</code>. + * + * @param e the property change event + * + * @see BasicScrollPaneUI#updateColumnHeader(PropertyChangeEvent) + * @see BasicScrollPaneUI#updateRowHeader(PropertyChangeEvent) + * @see BasicScrollPaneUI#updateScrollBarDisplayPolicy(PropertyChangeEvent) + * @see BasicScrollPaneUI#updateViewport(PropertyChangeEvent) + */ + public void propertyChange(PropertyChangeEvent e) + { + String propName = e.getPropertyName(); + if (propName.equals("viewport")) + updateViewport(e); + else if (propName.equals("rowHeader")) + updateRowHeader(e); + else if (propName.equals("columnHeader")) + updateColumnHeader(e); + else if (propName.equals("horizontalScrollBarPolicy") + || e.getPropertyName().equals("verticalScrollBarPolicy")) + updateScrollBarDisplayPolicy(e); + else if (propName.equals("verticalScrollBar")) + { + JScrollBar oldSb = (JScrollBar) e.getOldValue(); + oldSb.getModel().removeChangeListener(vsbChangeListener); + JScrollBar newSb = (JScrollBar) e.getNewValue(); + newSb.getModel().addChangeListener(vsbChangeListener); + } + else if (propName.equals("horizontalScrollBar")) + { + JScrollBar oldSb = (JScrollBar) e.getOldValue(); + oldSb.getModel().removeChangeListener(hsbChangeListener); + JScrollBar newSb = (JScrollBar) e.getNewValue(); + newSb.getModel().addChangeListener(hsbChangeListener); + } + } + + } + + /** + * Listens for mouse wheel events and update the scrollpane accordingly. + * + * @author Roman Kennke (kennke@aicas.com) + * + * @since 1.4 + */ + protected class MouseWheelHandler implements MouseWheelListener + { + + /** + * Receives notification whenever the mouse wheel is moved. + * + * @param event the mouse wheel event + */ + public void mouseWheelMoved(MouseWheelEvent event) + { + // TODO: Implement this properly. + } + + } + /** The Scrollpane for which the UI is provided by this class. */ protected JScrollPane scrollpane; + /** + * The horizontal scrollbar listener. + */ + protected ChangeListener hsbChangeListener; + + /** + * The vertical scrollbar listener. + */ + protected ChangeListener vsbChangeListener; + + /** + * The viewport listener. + */ + protected ChangeListener viewportChangeListener; + + /** + * The scrollpane property change listener. + */ + protected PropertyChangeListener spPropertyChangeListener; + + /** + * The mousewheel listener for the scrollpane. + */ + MouseWheelListener mouseWheelListener; + public static ComponentUI createUI(final JComponent c) { return new BasicScrollPaneUI(); @@ -65,11 +271,10 @@ public class BasicScrollPaneUI extends ScrollPaneUI protected void installDefaults(JScrollPane p) { scrollpane = p; - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - p.setForeground(defaults.getColor("ScrollPane.foreground")); - p.setBackground(defaults.getColor("ScrollPane.background")); - p.setFont(defaults.getFont("ScrollPane.font")); - p.setBorder(defaults.getBorder("ScrollPane.border")); + LookAndFeel.installColorsAndFont(p, "ScrollPane.background", + "ScrollPane.foreground", + "ScrollPane.font"); + LookAndFeel.installBorder(p, "ScrollPane.border"); p.setOpaque(true); } @@ -85,16 +290,141 @@ public class BasicScrollPaneUI extends ScrollPaneUI public void installUI(final JComponent c) { super.installUI(c); - this.installDefaults((JScrollPane)c); + installDefaults((JScrollPane) c); + installListeners((JScrollPane) c); + installKeyboardActions((JScrollPane) c); + } + + /** + * Installs the listeners on the scrollbars, the viewport and the scrollpane. + * + * @param sp the scrollpane on which to install the listeners + */ + protected void installListeners(JScrollPane sp) + { + if (spPropertyChangeListener == null) + spPropertyChangeListener = createPropertyChangeListener(); + sp.addPropertyChangeListener(spPropertyChangeListener); + + if (hsbChangeListener == null) + hsbChangeListener = createHSBChangeListener(); + sp.getHorizontalScrollBar().getModel().addChangeListener(hsbChangeListener); + + if (vsbChangeListener == null) + vsbChangeListener = createVSBChangeListener(); + sp.getVerticalScrollBar().getModel().addChangeListener(vsbChangeListener); + + if (viewportChangeListener == null) + viewportChangeListener = createViewportChangeListener(); + sp.getViewport().addChangeListener(viewportChangeListener); + + if (mouseWheelListener == null) + mouseWheelListener = createMouseWheelListener(); + sp.addMouseWheelListener(mouseWheelListener); + } + + /** + * Installs additional keyboard actions on the scrollpane. This is a hook + * method provided to subclasses in order to install their own keyboard + * actions. + * + * @param sp the scrollpane to install keyboard actions on + */ + protected void installKeyboardActions(JScrollPane sp) + { + // TODO: Is this only a hook method or should we actually do something + // here? If the latter, than figure out what and implement this. + } + + /** + * Creates and returns the change listener for the horizontal scrollbar. + * + * @return the change listener for the horizontal scrollbar + */ + protected ChangeListener createHSBChangeListener() + { + return new HSBChangeListener(); + } + + /** + * Creates and returns the change listener for the vertical scrollbar. + * + * @return the change listener for the vertical scrollbar + */ + protected ChangeListener createVSBChangeListener() + { + return new VSBChangeListener(); + } + + /** + * Creates and returns the change listener for the viewport. + * + * @return the change listener for the viewport + */ + protected ChangeListener createViewportChangeListener() + { + return new ViewportChangeHandler(); + } + + /** + * Creates and returns the property change listener for the scrollpane. + * + * @return the property change listener for the scrollpane + */ + protected PropertyChangeListener createPropertyChangeListener() + { + return new PropertyChangeHandler(); + } + + /** + * Creates and returns the mouse wheel listener for the scrollpane. + * + * @return the mouse wheel listener for the scrollpane + */ + protected MouseWheelListener createMouseWheelListener() + { + return new MouseWheelHandler(); } public void uninstallUI(final JComponent c) { super.uninstallUI(c); this.uninstallDefaults((JScrollPane)c); + uninstallListeners((JScrollPane) c); + installKeyboardActions((JScrollPane) c); + } + + /** + * Uninstalls all the listeners that have been installed in + * {@link #installListeners(JScrollPane)}. + * + * @param c the scrollpane from which to uninstall the listeners + */ + protected void uninstallListeners(JComponent c) + { + JScrollPane sp = (JScrollPane) c; + sp.removePropertyChangeListener(spPropertyChangeListener); + sp.getHorizontalScrollBar().getModel() + .removeChangeListener(hsbChangeListener); + sp.getVerticalScrollBar().getModel() + .removeChangeListener(vsbChangeListener); + sp.getViewport().removeChangeListener(viewportChangeListener); + sp.removeMouseWheelListener(mouseWheelListener); + } + + /** + * Uninstalls all keyboard actions from the JScrollPane that have been + * installed by {@link #installKeyboardActions}. This is a hook method + * provided to subclasses to add their own keyboard actions. + * + * @param sp the scrollpane to uninstall keyboard actions from + */ + protected void uninstallKeyboardActions(JScrollPane sp) + { + // TODO: Is this only a hook method or should we actually do something + // here? If the latter, than figure out what and implement this. } - public Dimension getMinimumSize(JComponent c) { JScrollPane p = (JScrollPane ) c; @@ -107,6 +437,76 @@ public class BasicScrollPaneUI extends ScrollPaneUI // do nothing; the normal painting-of-children algorithm, along with // ScrollPaneLayout, does all the relevant work. } + + /** + * Synchronizes the scrollbars with the viewport's extents. + */ + protected void syncScrollPaneWithViewport() + { + JViewport vp = scrollpane.getViewport(); + + // Update the horizontal scrollbar. + JScrollBar hsb = scrollpane.getHorizontalScrollBar(); + hsb.setMaximum(vp.getViewSize().width); + hsb.setValue(vp.getViewPosition().x); + hsb.setVisibleAmount(vp.getExtentSize().width); + + // Update the vertical scrollbar. + JScrollBar vsb = scrollpane.getVerticalScrollBar(); + vsb.setMaximum(vp.getViewSize().height); + vsb.setValue(vp.getViewPosition().y); + vsb.setVisibleAmount(vp.getExtentSize().height); + } + + /** + * Receives notification when the <code>columnHeader</code> property has + * changed on the scrollpane. + * + * @param ev the property change event + */ + protected void updateColumnHeader(PropertyChangeEvent ev) + { + // TODO: Find out what should be done here. Or is this only a hook? + } + + /** + * Receives notification when the <code>rowHeader</code> property has changed + * on the scrollpane. + * + * @param ev the property change event + */ + protected void updateRowHeader(PropertyChangeEvent ev) + { + // TODO: Find out what should be done here. Or is this only a hook? + } + + /** + * Receives notification when the <code>scrollBarDisplayPolicy</code> + * property has changed on the scrollpane. + * + * @param ev the property change event + */ + protected void updateScrollBarDisplayPolicy(PropertyChangeEvent ev) + { + // TODO: Find out what should be done here. Or is this only a hook? + } + + /** + * Receives notification when the <code>viewport</code> property has changed + * on the scrollpane. + * + * This method sets removes the viewportChangeListener from the old viewport + * and adds it to the new viewport. + * + * @param ev the property change event + */ + protected void updateViewport(PropertyChangeEvent ev) + { + JViewport oldViewport = (JViewport) ev.getOldValue(); + oldViewport.removeChangeListener(viewportChangeListener); + JViewport newViewport = (JViewport) ev.getNewValue(); + oldViewport.addChangeListener(viewportChangeListener); + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSeparatorUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSeparatorUI.java index 38c9c7a2820..97caa3af7bd 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicSeparatorUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicSeparatorUI.java @@ -41,13 +41,11 @@ package javax.swing.plaf.basic; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; -import java.awt.Insets; import java.awt.Rectangle; import javax.swing.JComponent; import javax.swing.JSeparator; import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.SeparatorUI; @@ -121,10 +119,8 @@ public class BasicSeparatorUI extends SeparatorUI */ protected void installDefaults(JSeparator s) { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - shadow = defaults.getColor("Separator.shadow"); - highlight = defaults.getColor("Separator.highlight"); + shadow = UIManager.getColor("Separator.shadow"); + highlight = UIManager.getColor("Separator.highlight"); s.setOpaque(false); } @@ -165,8 +161,8 @@ public class BasicSeparatorUI extends SeparatorUI /** * The separator is made of two lines. The top line will be - * the highlight color (or left line if it's vertical). The bottom - * or right line will be the shadow color. The two lines will + * the shadow color (or left line if it's vertical). The bottom + * or right line will be the highlight color. The two lines will * be centered inside the bounds box. If the separator is horizontal, * then it will be vertically centered, or if it's vertical, it will * be horizontally centered. @@ -180,9 +176,6 @@ public class BasicSeparatorUI extends SeparatorUI SwingUtilities.calculateInnerArea(c, r); Color saved = g.getColor(); - int midAB = r.width / 2 + r.x; - int midAD = r.height / 2 + r.y; - JSeparator s; if (c instanceof JSeparator) s = (JSeparator) c; @@ -190,21 +183,24 @@ public class BasicSeparatorUI extends SeparatorUI return; if (s.getOrientation() == JSeparator.HORIZONTAL) - { - g.setColor(highlight); - g.drawLine(r.x, midAD, r.x + r.width, midAD); - - g.setColor(shadow); - g.drawLine(r.x, midAD + 1, r.x + r.width, midAD + 1); - } - else - { - g.setColor(highlight); - g.drawLine(midAB, r.y, midAB, r.y + r.height); - - g.setColor(shadow); - g.drawLine(midAB + 1, r.y, midAB + 1, r.y + r.height); - } + { + int midAB = r.height / 2; + g.setColor(shadow); + g.drawLine(r.x, r.y + midAB - 1, r.x + r.width, r.y + midAB - 1); + + g.setColor(highlight); + g.fillRect(r.x, r.y + midAB, r.x + r.width, r.y + midAB); + } + else + { + int midAD = r.height / 2 + r.y; + g.setColor(shadow); + g.drawLine(r.x, r.y, r.x, r.y + r.height); + + g.setColor(highlight); + g.fillRect(r.x + midAD, r.y + r.height, r.x + midAD, r.y + r.height); + } + g.setColor(saved); } /** @@ -217,28 +213,14 @@ public class BasicSeparatorUI extends SeparatorUI */ public Dimension getPreferredSize(JComponent c) { - Dimension dims = new Dimension(0, 0); - Insets insets = c.getInsets(); - + Dimension pref = new Dimension(2, 0); if (c instanceof JSeparator) { JSeparator s = (JSeparator) c; - if (s.getOrientation() == JSeparator.HORIZONTAL) - { - dims.height = 2; - dims.width = 40; - } - else - { - dims.width = 2; - dims.height = 40; - } + pref = new Dimension(0, 2); } - dims.width += insets.left + insets.right; - dims.height += insets.top + insets.bottom; - - return dims; + return pref; } /** @@ -251,7 +233,7 @@ public class BasicSeparatorUI extends SeparatorUI */ public Dimension getMinimumSize(JComponent c) { - return getPreferredSize(c); + return new Dimension(0, 0); } /** @@ -264,6 +246,7 @@ public class BasicSeparatorUI extends SeparatorUI */ public Dimension getMaximumSize(JComponent c) { - return getPreferredSize(c); + return new Dimension(Short.MAX_VALUE, + Short.MAX_VALUE); } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java index 0b4058429c5..26f58051902 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java @@ -60,13 +60,14 @@ import java.beans.PropertyChangeListener; import java.util.Dictionary; import java.util.Enumeration; +import javax.swing.AbstractAction; import javax.swing.BoundedRangeModel; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JSlider; +import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; import javax.swing.Timer; -import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -470,6 +471,34 @@ public class BasicSliderUI extends SliderUI } } + /** + * This class is no longer used as of JDK1.3. + */ + public class ActionScroller extends AbstractAction + { + /** + * Not used. + * + * @param slider not used + * @param dir not used + * @param block not used + */ + public ActionScroller(JSlider slider, int dir, boolean block) + { + // Not used. + } + + /** + * Not used. + * + * @param event not used + */ + public void actionPerformed(ActionEvent event) + { + // Not used. + } + } + /** Listener for changes from the model. */ protected ChangeListener changeListener; @@ -680,16 +709,14 @@ public class BasicSliderUI extends SliderUI */ protected void installDefaults(JSlider slider) { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - slider.setForeground(defaults.getColor("Slider.foreground")); - slider.setBackground(defaults.getColor("Slider.background")); - shadowColor = defaults.getColor("Slider.shadow"); - highlightColor = defaults.getColor("Slider.highlight"); - focusColor = defaults.getColor("Slider.focus"); - slider.setBorder(defaults.getBorder("Slider.border")); + LookAndFeel.installColors(slider, "Slider.background", + "Slider.foreground"); + LookAndFeel.installBorder(slider, "Slider.border"); + shadowColor = UIManager.getColor("Slider.shadow"); + highlightColor = UIManager.getColor("Slider.highlight"); + focusColor = UIManager.getColor("Slider.focus"); + focusInsets = UIManager.getInsets("Slider.focusInsets"); slider.setOpaque(true); - focusInsets = defaults.getInsets("Slider.focusInsets"); } /** @@ -1465,7 +1492,7 @@ public class BasicSliderUI extends SliderUI // FIXME: Move this to propertyChangeEvent handler, when we get those. leftToRightCache = slider.getComponentOrientation() != ComponentOrientation.RIGHT_TO_LEFT; // FIXME: This next line is only here because the above line is here. - calculateThumbLocation(); + calculateGeometry(); if (slider.getPaintTrack()) paintTrack(g); @@ -1958,7 +1985,7 @@ public class BasicSliderUI extends SliderUI public void paintThumb(Graphics g) { Color saved_color = g.getColor(); - + Point a = new Point(thumbRect.x, thumbRect.y); Point b = new Point(a); Point c = new Point(a); diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java index 97ab97b8972..3b7399eafaa 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java @@ -53,9 +53,8 @@ import java.beans.PropertyChangeListener; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JSpinner; +import javax.swing.LookAndFeel; import javax.swing.Timer; -import javax.swing.UIDefaults; -import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.SpinnerUI; @@ -167,16 +166,9 @@ public class BasicSpinnerUI extends SpinnerUI */ protected void installDefaults() { - /* most of it copied from BasicLabelUI, I don't know what keys are - available, so someone may want to update this. Hence: TODO - */ - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - /* - spinner.setForeground(defaults.getColor("Spinner.foreground")); - spinner.setBackground(defaults.getColor("Spinner.background")); - spinner.setFont(defaults.getFont("Spinner.font")); - spinner.setBorder(defaults.getBorder("Spinner.border")); - */ + LookAndFeel.installColorsAndFont(spinner, "Spinner.background", + "Spinner.foreground", "Spinner.font"); + LookAndFeel.installBorder(spinner, "Spinner.border"); spinner.setLayout(createLayout()); spinner.setOpaque(true); } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java index b8674ed2f08..69ed2be7c61 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java @@ -802,6 +802,7 @@ public class BasicSplitPaneDivider extends Container */ protected DividerLayout() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java index ef8e2282349..746f628df6f 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java @@ -58,7 +58,7 @@ import java.beans.PropertyChangeListener; import javax.swing.JComponent; import javax.swing.JSplitPane; import javax.swing.KeyStroke; -import javax.swing.UIDefaults; +import javax.swing.LookAndFeel; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.SplitPaneUI; @@ -404,7 +404,7 @@ public class BasicSplitPaneUI extends SplitPaneUI */ protected void setComponentToSize(Component c, int size, int location, Insets insets, Dimension containerSize) - { + { int w = size; int h = containerSize.height - insets.top - insets.bottom; int x = location; @@ -637,7 +637,6 @@ public class BasicSplitPaneUI extends SplitPaneUI int x = insets.left; int h = size; int w = containerSize.width - insets.left - insets.right; - c.setBounds(x, y, w, h); } @@ -817,15 +816,12 @@ public class BasicSplitPaneUI extends SplitPaneUI int newSize = splitPane.getDividerSize(); int[] tmpSizes = layoutManager.getSizes(); dividerSize = tmpSizes[2]; - Component left = splitPane.getLeftComponent(); - Component right = splitPane.getRightComponent(); - int newSpace = newSize - tmpSizes[2]; - + int newSpace = newSize - tmpSizes[2]; tmpSizes[2] = newSize; tmpSizes[0] += newSpace / 2; tmpSizes[1] += newSpace / 2; - + layoutManager.setSizes(tmpSizes); } else if (e.getPropertyName().equals(JSplitPane.ORIENTATION_PROPERTY)) @@ -942,6 +938,7 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public BasicSplitPaneUI() { + // Nothing to do here. } /** @@ -991,16 +988,16 @@ public class BasicSplitPaneUI extends SplitPaneUI */ protected void installDefaults() { + LookAndFeel.installColors(splitPane, "SplitPane.background", + "SplitPane.foreground"); + LookAndFeel.installBorder(splitPane, "SplitPane.border"); divider = createDefaultDivider(); resetLayoutManager(); nonContinuousLayoutDivider = createDefaultNonContinuousLayoutDivider(); splitPane.add(divider, JSplitPane.DIVIDER); // There is no need to add the nonContinuousLayoutDivider - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - splitPane.setBackground(defaults.getColor("SplitPane.background")); - splitPane.setBorder(defaults.getBorder("SplitPane.border")); - splitPane.setDividerSize(defaults.getInt("SplitPane.dividerSize")); + splitPane.setDividerSize(UIManager.getInt("SplitPane.dividerSize")); splitPane.setOpaque(true); } @@ -1301,15 +1298,41 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public void setDividerLocation(JSplitPane jc, int location) { + location = validLocation(location); + Container p = jc.getParent(); + Dimension rightPrefSize = jc.getRightComponent().getPreferredSize(); + Dimension size = jc.getSize(); + // check if the size has been set for the splitpane + if (size.width == 0 && size.height == 0) + size = jc.getPreferredSize(); + + if (getOrientation() == 0 && location > size.height) + { + location = size.height; + while (p != null) + { + p.setSize(p.getWidth(), p.getHeight() + rightPrefSize.height); + p = p.getParent(); + } + } + else if (location > size.width) + { + location = size.width; + while (p != null) + { + p.setSize(p.getWidth() + rightPrefSize.width, p.getHeight()); + p = p.getParent(); + } + } + setLastDragLocation(getDividerLocation(splitPane)); splitPane.setLastDividerLocation(getDividerLocation(splitPane)); int[] tmpSizes = layoutManager.getSizes(); - tmpSizes[0] = location + tmpSizes[0] = location - layoutManager.getInitialLocation(splitPane.getInsets()); tmpSizes[1] = layoutManager.getAvailableSize(splitPane.getSize(), splitPane.getInsets()) - - tmpSizes[0] - tmpSizes[1]; - + - tmpSizes[0]; layoutManager.setSizes(tmpSizes); splitPane.revalidate(); splitPane.repaint(); @@ -1338,11 +1361,9 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public int getMinimumDividerLocation(JSplitPane jc) { - int value = layoutManager.getInitialLocation(jc.getInsets()) - - layoutManager.getAvailableSize(jc.getSize(), jc.getInsets()) - + splitPane.getDividerSize(); - if (layoutManager.components[1] != null) - value += layoutManager.minimumSizeOfComponent(1); + int value = layoutManager.getInitialLocation(jc.getInsets()); + if (layoutManager.components[0] != null) + value -= layoutManager.minimumSizeOfComponent(0); return value; } @@ -1388,6 +1409,7 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public void paint(Graphics g, JComponent jc) { + // TODO: What should be done here? } /** @@ -1550,10 +1572,12 @@ public class BasicSplitPaneUI extends SplitPaneUI */ private int validLocation(int location) { - if (location < getMinimumDividerLocation(splitPane)) - return getMinimumDividerLocation(splitPane); - if (location > getMaximumDividerLocation(splitPane)) - return getMaximumDividerLocation(splitPane); + int min = getMinimumDividerLocation(splitPane); + int max = getMaximumDividerLocation(splitPane); + if (min > 0 && location < min) + return min; + if (max > 0 && location > max) + return max; return location; } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java index 7e9d9b9820c..ce9ea3ec7f1 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java @@ -64,9 +64,9 @@ import javax.swing.JPanel; import javax.swing.JTabbedPane; import javax.swing.JViewport; import javax.swing.KeyStroke; +import javax.swing.LookAndFeel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -136,36 +136,36 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) { - if (e.getSource() == incrButton) - { - if (++currentScrollLocation >= tabCount) - currentScrollLocation = tabCount - 1; - - int width = 0; - for (int i = currentScrollLocation - 1; i < tabCount; i++) - width += rects[i].width; - if (width < viewport.getWidth()) - // FIXME: Still getting mouse events after the button is disabled. - // incrButton.setEnabled(false); - currentScrollLocation--; - else if (! decrButton.isEnabled()) - decrButton.setEnabled(true); - tabPane.revalidate(); - tabPane.repaint(); - return; - } - else if (e.getSource() == decrButton) - { - if (--currentScrollLocation < 0) - currentScrollLocation = 0; - if (currentScrollLocation == 0) - decrButton.setEnabled(false); - else if (! incrButton.isEnabled()) - incrButton.setEnabled(true); - tabPane.revalidate(); - tabPane.repaint(); - return; - } + if (e.getSource() == incrButton) + { + if (++currentScrollLocation >= tabCount) + currentScrollLocation = tabCount - 1; + + int width = 0; + for (int i = currentScrollLocation - 1; i < tabCount; i++) + width += rects[i].width; + if (width < viewport.getWidth()) + // FIXME: Still getting mouse events after the button is disabled. + // incrButton.setEnabled(false); + currentScrollLocation--; + else if (! decrButton.isEnabled()) + decrButton.setEnabled(true); + tabPane.revalidate(); + tabPane.repaint(); + return; + } + else if (e.getSource() == decrButton) + { + if (--currentScrollLocation < 0) + currentScrollLocation = 0; + if (currentScrollLocation == 0) + decrButton.setEnabled(false); + else if (! incrButton.isEnabled()) + incrButton.setEnabled(true); + tabPane.revalidate(); + tabPane.repaint(); + return; + } } int index = tabForCoordinate(tabPane, x, y); @@ -173,7 +173,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // We need to check since there are areas where tabs cannot be // e.g. in the inset area. if (index != -1 && tabPane.isEnabledAt(index)) - tabPane.setSelectedIndex(index); + tabPane.setSelectedIndex(index); tabPane.revalidate(); tabPane.repaint(); } @@ -198,15 +198,15 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { if (e.getPropertyName().equals("tabLayoutPolicy")) { - layoutManager = createLayoutManager(); - - tabPane.setLayout(layoutManager); + layoutManager = createLayoutManager(); + + tabPane.setLayout(layoutManager); } else if (e.getPropertyName().equals("tabPlacement") - && tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + && tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) { - incrButton = createIncreaseButton(); - decrButton = createDecreaseButton(); + incrButton = createIncreaseButton(); + decrButton = createDecreaseButton(); } tabPane.layout(); tabPane.repaint(); @@ -245,13 +245,13 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPane.getSelectedIndex() != -1) { - Component visible = getVisibleComponent(); - Insets insets = getContentBorderInsets(tabPane.getTabPlacement()); - if (visible != null) - visible.setBounds(contentRect.x + insets.left, - contentRect.y + insets.top, - contentRect.width - insets.left - insets.right, - contentRect.height - insets.top - insets.bottom); + Component visible = getVisibleComponent(); + Insets insets = getContentBorderInsets(tabPane.getTabPlacement()); + if (visible != null) + visible.setBounds(contentRect.x + insets.left, + contentRect.y + insets.top, + contentRect.width - insets.left - insets.right, + contentRect.height - insets.top - insets.bottom); } } @@ -275,35 +275,35 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants Dimension dims; for (int i = 0; i < tabPane.getTabCount(); i++) { - c = tabPane.getComponentAt(i); - if (c == null) - continue; - calcRect = c.getBounds(); - dims = c.getPreferredSize(); - if (dims != null) - { - componentHeight = Math.max(componentHeight, dims.height); - componentWidth = Math.max(componentWidth, dims.width); - } + c = tabPane.getComponentAt(i); + if (c == null) + continue; + calcRect = c.getBounds(); + dims = c.getPreferredSize(); + if (dims != null) + { + componentHeight = Math.max(componentHeight, dims.height); + componentWidth = Math.max(componentWidth, dims.width); + } } Insets insets = tabPane.getInsets(); if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { - int min = calculateMaxTabWidth(tabPlacement); - width = Math.max(min, componentWidth); - - int tabAreaHeight = preferredTabAreaHeight(tabPlacement, width); - height = tabAreaHeight + componentHeight; + int min = calculateMaxTabWidth(tabPlacement); + width = Math.max(min, componentWidth); + + int tabAreaHeight = preferredTabAreaHeight(tabPlacement, width); + height = tabAreaHeight + componentHeight; } else { - int min = calculateMaxTabHeight(tabPlacement); - height = Math.max(min, componentHeight); - - int tabAreaWidth = preferredTabAreaWidth(tabPlacement, height); - width = tabAreaWidth + componentWidth; + int min = calculateMaxTabHeight(tabPlacement); + height = Math.max(min, componentHeight); + + int tabAreaWidth = preferredTabAreaWidth(tabPlacement, height); + width = tabAreaWidth + componentWidth; } return new Dimension(width, height); @@ -330,7 +330,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected void calculateTabRects(int tabPlacement, int tabCount) { if (tabCount == 0) - return; + return; assureRectsCreated(tabCount); FontMetrics fm = getFontMetrics(); @@ -343,113 +343,112 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { - int maxHeight = calculateMaxTabHeight(tabPlacement); - - calcRect.width -= tabAreaInsets.left + tabAreaInsets.right; - max = calcRect.width + tabAreaInsets.left + insets.left; - start += tabAreaInsets.left + insets.left; - int width = 0; - int runWidth = start; - - for (int i = 0; i < tabCount; i++) - { - width = calculateTabWidth(tabPlacement, i, fm); - - if (runWidth + width > max) - { - runWidth = tabAreaInsets.left + insets.left - + getTabRunIndent(tabPlacement, ++runs); - rects[i] = new Rectangle(runWidth, - insets.top + tabAreaInsets.top, - width, maxHeight); - runWidth += width; - if (runs > tabRuns.length - 1) - expandTabRunsArray(); - tabRuns[runs] = i; - } - else - { - rects[i] = new Rectangle(runWidth, - insets.top + tabAreaInsets.top, - width, maxHeight); - runWidth += width; - } - } - runs++; - tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right; - tabAreaRect.height = runs * maxTabHeight - - (runs - 1) * tabRunOverlay - + tabAreaInsets.top + tabAreaInsets.bottom; - contentRect.width = tabAreaRect.width; - contentRect.height = tabPane.getHeight() - insets.top - - insets.bottom - tabAreaRect.height; - contentRect.x = insets.left; - tabAreaRect.x = insets.left; - if (tabPlacement == SwingConstants.BOTTOM) - { - contentRect.y = insets.top; - tabAreaRect.y = contentRect.y + contentRect.height; - } - else - { - tabAreaRect.y = insets.top; - contentRect.y = tabAreaRect.y + tabAreaRect.height; - } + int maxHeight = calculateMaxTabHeight(tabPlacement); + + calcRect.width -= tabAreaInsets.left + tabAreaInsets.right; + max = calcRect.width + tabAreaInsets.left + insets.left; + start += tabAreaInsets.left + insets.left; + int width = 0; + int runWidth = start; + + for (int i = 0; i < tabCount; i++) + { + width = calculateTabWidth(tabPlacement, i, fm); + if (runWidth + width > max) + { + runWidth = tabAreaInsets.left + insets.left + + getTabRunIndent(tabPlacement, ++runs); + rects[i] = new Rectangle(runWidth, + insets.top + tabAreaInsets.top, + width, maxHeight); + runWidth += width; + if (runs > tabRuns.length - 1) + expandTabRunsArray(); + tabRuns[runs] = i; + } + else + { + rects[i] = new Rectangle(runWidth, + insets.top + tabAreaInsets.top, + width, maxHeight); + runWidth += width; + } + } + runs++; + tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right; + tabAreaRect.height = runs * maxTabHeight + - (runs - 1) * tabRunOverlay + + tabAreaInsets.top + tabAreaInsets.bottom; + contentRect.width = tabAreaRect.width; + contentRect.height = tabPane.getHeight() - insets.top + - insets.bottom - tabAreaRect.height; + contentRect.x = insets.left; + tabAreaRect.x = insets.left; + if (tabPlacement == SwingConstants.BOTTOM) + { + contentRect.y = insets.top; + tabAreaRect.y = contentRect.y + contentRect.height; + } + else + { + tabAreaRect.y = insets.top; + contentRect.y = tabAreaRect.y + tabAreaRect.height; + } } else { - int maxWidth = calculateMaxTabWidth(tabPlacement); - calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom; - max = calcRect.height + tabAreaInsets.top + insets.top; - - int height = 0; - start += tabAreaInsets.top + insets.top; - int runHeight = start; - - int fontHeight = fm.getHeight(); - - for (int i = 0; i < tabCount; i++) - { - height = calculateTabHeight(tabPlacement, i, fontHeight); - if (runHeight + height > max) - { - runHeight = tabAreaInsets.top + insets.top - + getTabRunIndent(tabPlacement, ++runs); - rects[i] = new Rectangle(insets.left + tabAreaInsets.left, - runHeight, maxWidth, height); - runHeight += height; - if (runs > tabRuns.length - 1) - expandTabRunsArray(); - tabRuns[runs] = i; - } - else - { - rects[i] = new Rectangle(insets.left + tabAreaInsets.left, - runHeight, maxWidth, height); - runHeight += height; - } - } - runs++; - - tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay - + tabAreaInsets.left + tabAreaInsets.right; - tabAreaRect.height = tabPane.getHeight() - insets.top - - insets.bottom; - tabAreaRect.y = insets.top; - contentRect.width = tabPane.getWidth() - insets.left - insets.right - - tabAreaRect.width; - contentRect.height = tabAreaRect.height; - contentRect.y = insets.top; - if (tabPlacement == SwingConstants.LEFT) - { - tabAreaRect.x = insets.left; - contentRect.x = tabAreaRect.x + tabAreaRect.width; - } - else - { - contentRect.x = insets.left; - tabAreaRect.x = contentRect.x + contentRect.width; - } + int maxWidth = calculateMaxTabWidth(tabPlacement); + calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom; + max = calcRect.height + tabAreaInsets.top + insets.top; + + int height = 0; + start += tabAreaInsets.top + insets.top; + int runHeight = start; + + int fontHeight = fm.getHeight(); + + for (int i = 0; i < tabCount; i++) + { + height = calculateTabHeight(tabPlacement, i, fontHeight); + if (runHeight + height > max) + { + runHeight = tabAreaInsets.top + insets.top + + getTabRunIndent(tabPlacement, ++runs); + rects[i] = new Rectangle(insets.left + tabAreaInsets.left, + runHeight, maxWidth, height); + runHeight += height; + if (runs > tabRuns.length - 1) + expandTabRunsArray(); + tabRuns[runs] = i; + } + else + { + rects[i] = new Rectangle(insets.left + tabAreaInsets.left, + runHeight, maxWidth, height); + runHeight += height; + } + } + runs++; + + tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay + + tabAreaInsets.left + tabAreaInsets.right; + tabAreaRect.height = tabPane.getHeight() - insets.top + - insets.bottom; + tabAreaRect.y = insets.top; + contentRect.width = tabPane.getWidth() - insets.left - insets.right + - tabAreaRect.width; + contentRect.height = tabAreaRect.height; + contentRect.y = insets.top; + if (tabPlacement == SwingConstants.LEFT) + { + tabAreaRect.x = insets.left; + contentRect.x = tabAreaRect.x + tabAreaRect.width; + } + else + { + contentRect.x = insets.left; + tabAreaRect.x = contentRect.x + contentRect.width; + } } runCount = runs; @@ -457,62 +456,62 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants normalizeTabRuns(tabPlacement, tabCount, start, max); selectedRun = getRunForTab(tabCount, tabPane.getSelectedIndex()); if (shouldRotateTabRuns(tabPlacement)) - rotateTabRuns(tabPlacement, selectedRun); + rotateTabRuns(tabPlacement, selectedRun); // Need to pad the runs and move them to the correct location. for (int i = 0; i < runCount; i++) { - int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1; - if (first == tabCount) - first = 0; - int last = lastTabInRun(tabCount, i); - if (shouldPadTabRun(tabPlacement, i)) - padTabRun(tabPlacement, first, last, max); - - // Done padding, now need to move it. - if (tabPlacement == SwingConstants.TOP && i > 0) - { - for (int j = first; j <= last; j++) - rects[j].y += (runCount - i) * maxTabHeight - - (runCount - i) * tabRunOverlay; - } - - if (tabPlacement == SwingConstants.BOTTOM) - { - int height = tabPane.getBounds().height - insets.bottom - - tabAreaInsets.bottom; - int adjustment; - if (i == 0) - adjustment = height - maxTabHeight; - else - adjustment = height - (runCount - i + 1) * maxTabHeight - - (runCount - i) * tabRunOverlay; - - for (int j = first; j <= last; j++) - rects[j].y = adjustment; - } - - if (tabPlacement == SwingConstants.LEFT && i > 0) - { - for (int j = first; j <= last; j++) - rects[j].x += (runCount - i) * maxTabWidth - - (runCount - i) * tabRunOverlay; - } - - if (tabPlacement == SwingConstants.RIGHT) - { - int width = tabPane.getBounds().width - insets.right - - tabAreaInsets.right; - int adjustment; - if (i == 0) - adjustment = width - maxTabWidth; - else - adjustment = width - (runCount - i + 1) * maxTabWidth - + (runCount - i) * tabRunOverlay; - - for (int j = first; j <= last; j++) - rects[j].x = adjustment; - } + int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1; + if (first == tabCount) + first = 0; + int last = lastTabInRun(tabCount, i); + if (shouldPadTabRun(tabPlacement, i)) + padTabRun(tabPlacement, first, last, max); + + // Done padding, now need to move it. + if (tabPlacement == SwingConstants.TOP && i > 0) + { + for (int j = first; j <= last; j++) + rects[j].y += (runCount - i) * maxTabHeight + - (runCount - i) * tabRunOverlay; + } + + if (tabPlacement == SwingConstants.BOTTOM) + { + int height = tabPane.getBounds().height - insets.bottom + - tabAreaInsets.bottom; + int adjustment; + if (i == 0) + adjustment = height - maxTabHeight; + else + adjustment = height - (runCount - i + 1) * maxTabHeight + - (runCount - i) * tabRunOverlay; + + for (int j = first; j <= last; j++) + rects[j].y = adjustment; + } + + if (tabPlacement == SwingConstants.LEFT && i > 0) + { + for (int j = first; j <= last; j++) + rects[j].x += (runCount - i) * maxTabWidth + - (runCount - i) * tabRunOverlay; + } + + if (tabPlacement == SwingConstants.RIGHT) + { + int width = tabPane.getBounds().width - insets.right + - tabAreaInsets.right; + int adjustment; + if (i == 0) + adjustment = width - maxTabWidth; + else + adjustment = width - (runCount - i + 1) * maxTabWidth + + (runCount - i) * tabRunOverlay; + + for (int j = first; j <= last; j++) + rects[j].x = adjustment; + } } padSelectedTab(tabPlacement, tabPane.getSelectedIndex()); } @@ -565,74 +564,74 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingUtilities.TOP || tabPlacement == SwingUtilities.BOTTOM) { - // We should only do this for runCount - 1, cause we can only shift that many times between - // runs. - for (int i = 1; i < runCount; i++) - { - Rectangle currRun = rects[lastTabInRun(tabCount, i)]; - Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))]; - int spaceInCurr = currRun.x + currRun.width; - int spaceInNext = nextRun.x + nextRun.width; - - int diffNow = spaceInCurr - spaceInNext; - int diffLater = (spaceInCurr - currRun.width) - - (spaceInNext + currRun.width); - while (Math.abs(diffLater) < Math.abs(diffNow) - && spaceInNext + currRun.width < max) - { - tabRuns[i]--; - spaceInNext += currRun.width; - spaceInCurr -= currRun.width; - currRun = rects[lastTabInRun(tabCount, i)]; - diffNow = spaceInCurr - spaceInNext; - diffLater = (spaceInCurr - currRun.width) - - (spaceInNext + currRun.width); - } - - // Fix the bounds. - int first = lastTabInRun(tabCount, i) + 1; - int last = lastTabInRun(tabCount, getNextTabRun(i)); - int currX = tabAreaInsets.left; - for (int j = first; j <= last; j++) - { - rects[j].x = currX; - currX += rects[j].width; - } - } + // We should only do this for runCount - 1, cause we can only shift that many times between + // runs. + for (int i = 1; i < runCount; i++) + { + Rectangle currRun = rects[lastTabInRun(tabCount, i)]; + Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))]; + int spaceInCurr = currRun.x + currRun.width; + int spaceInNext = nextRun.x + nextRun.width; + + int diffNow = spaceInCurr - spaceInNext; + int diffLater = (spaceInCurr - currRun.width) + - (spaceInNext + currRun.width); + while (Math.abs(diffLater) < Math.abs(diffNow) + && spaceInNext + currRun.width < max) + { + tabRuns[i]--; + spaceInNext += currRun.width; + spaceInCurr -= currRun.width; + currRun = rects[lastTabInRun(tabCount, i)]; + diffNow = spaceInCurr - spaceInNext; + diffLater = (spaceInCurr - currRun.width) + - (spaceInNext + currRun.width); + } + + // Fix the bounds. + int first = lastTabInRun(tabCount, i) + 1; + int last = lastTabInRun(tabCount, getNextTabRun(i)); + int currX = tabAreaInsets.left; + for (int j = first; j <= last; j++) + { + rects[j].x = currX; + currX += rects[j].width; + } + } } else { - for (int i = 1; i < runCount; i++) - { - Rectangle currRun = rects[lastTabInRun(tabCount, i)]; - Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))]; - int spaceInCurr = currRun.y + currRun.height; - int spaceInNext = nextRun.y + nextRun.height; - - int diffNow = spaceInCurr - spaceInNext; - int diffLater = (spaceInCurr - currRun.height) - - (spaceInNext + currRun.height); - while (Math.abs(diffLater) < Math.abs(diffNow) - && spaceInNext + currRun.height < max) - { - tabRuns[i]--; - spaceInNext += currRun.height; - spaceInCurr -= currRun.height; - currRun = rects[lastTabInRun(tabCount, i)]; - diffNow = spaceInCurr - spaceInNext; - diffLater = (spaceInCurr - currRun.height) - - (spaceInNext + currRun.height); - } - - int first = lastTabInRun(tabCount, i) + 1; - int last = lastTabInRun(tabCount, getNextTabRun(i)); - int currY = tabAreaInsets.top; - for (int j = first; j <= last; j++) - { - rects[j].y = currY; - currY += rects[j].height; - } - } + for (int i = 1; i < runCount; i++) + { + Rectangle currRun = rects[lastTabInRun(tabCount, i)]; + Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))]; + int spaceInCurr = currRun.y + currRun.height; + int spaceInNext = nextRun.y + nextRun.height; + + int diffNow = spaceInCurr - spaceInNext; + int diffLater = (spaceInCurr - currRun.height) + - (spaceInNext + currRun.height); + while (Math.abs(diffLater) < Math.abs(diffNow) + && spaceInNext + currRun.height < max) + { + tabRuns[i]--; + spaceInNext += currRun.height; + spaceInCurr -= currRun.height; + currRun = rects[lastTabInRun(tabCount, i)]; + diffNow = spaceInCurr - spaceInNext; + diffLater = (spaceInCurr - currRun.height) + - (spaceInNext + currRun.height); + } + + int first = lastTabInRun(tabCount, i) + 1; + int last = lastTabInRun(tabCount, getNextTabRun(i)); + int currY = tabAreaInsets.top; + for (int j = first; j <= last; j++) + { + rects[j].y = currY; + currY += rects[j].height; + } + } } } @@ -673,42 +672,42 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { - int runWidth = rects[end].x + rects[end].width; - int spaceRemaining = max - runWidth; - int numTabs = end - start + 1; - - // now divvy up the space. - int spaceAllocated = spaceRemaining / numTabs; - int currX = rects[start].x; - for (int i = start; i <= end; i++) - { - rects[i].x = currX; - rects[i].width += spaceAllocated; - currX += rects[i].width; - // This is used because since the spaceAllocated - // variable is an int, it rounds down. Sometimes, - // we don't fill an entire row, so we make it do - // so now. - if (i == end && rects[i].x + rects[i].width != max) - rects[i].width = max - rects[i].x; - } + int runWidth = rects[end].x + rects[end].width; + int spaceRemaining = max - runWidth; + int numTabs = end - start + 1; + + // now divvy up the space. + int spaceAllocated = spaceRemaining / numTabs; + int currX = rects[start].x; + for (int i = start; i <= end; i++) + { + rects[i].x = currX; + rects[i].width += spaceAllocated; + currX += rects[i].width; + // This is used because since the spaceAllocated + // variable is an int, it rounds down. Sometimes, + // we don't fill an entire row, so we make it do + // so now. + if (i == end && rects[i].x + rects[i].width != max) + rects[i].width = max - rects[i].x; + } } else { - int runHeight = rects[end].y + rects[end].height; - int spaceRemaining = max - runHeight; - int numTabs = end - start + 1; - - int spaceAllocated = spaceRemaining / numTabs; - int currY = rects[start].y; - for (int i = start; i <= end; i++) - { - rects[i].y = currY; - rects[i].height += spaceAllocated; - currY += rects[i].height; - if (i == end && rects[i].y + rects[i].height != max) - rects[i].height = max - rects[i].y; - } + int runHeight = rects[end].y + rects[end].height; + int spaceRemaining = max - runHeight; + int numTabs = end - start + 1; + + int spaceAllocated = spaceRemaining / numTabs; + int currY = rects[start].y; + for (int i = start; i <= end; i++) + { + rects[i].y = currY; + rects[i].height += spaceAllocated; + currY += rects[i].height; + if (i == end && rects[i].y + rects[i].height != max) + rects[i].height = max - rects[i].y; + } } } @@ -736,7 +735,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected int preferredTabAreaHeight(int tabPlacement, int width) { if (tabPane.getTabCount() == 0) - return calculateTabAreaHeight(tabPlacement, 0, 0); + return calculateTabAreaHeight(tabPlacement, 0, 0); int runs = 0; int runWidth = 0; @@ -758,14 +757,14 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // be IF we got our desired width. for (int i = 0; i < tabPane.getTabCount(); i++) { - tabWidth = calculateTabWidth(tabPlacement, i, fm); - if (runWidth + tabWidth > width) - { - runWidth = tabWidth; - runs++; - } - else - runWidth += tabWidth; + tabWidth = calculateTabWidth(tabPlacement, i, fm); + if (runWidth + tabWidth > width) + { + runWidth = tabWidth; + runs++; + } + else + runWidth += tabWidth; } runs++; @@ -787,7 +786,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected int preferredTabAreaWidth(int tabPlacement, int height) { if (tabPane.getTabCount() == 0) - return calculateTabAreaHeight(tabPlacement, 0, 0); + return calculateTabAreaHeight(tabPlacement, 0, 0); int runs = 0; int runHeight = 0; @@ -804,14 +803,14 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants for (int i = 0; i < tabPane.getTabCount(); i++) { - tabHeight = calculateTabHeight(tabPlacement, i, fontHeight); - if (runHeight + tabHeight > height) - { - runHeight = tabHeight; - runs++; - } - else - runHeight += tabHeight; + tabHeight = calculateTabHeight(tabPlacement, i, fontHeight); + if (runHeight + tabHeight > height) + { + runHeight = tabHeight; + runs++; + } + else + runHeight += tabHeight; } runs++; @@ -831,19 +830,19 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected void rotateTabRuns(int tabPlacement, int selectedRun) { if (runCount == 1 || selectedRun == 1 || selectedRun == -1) - return; + return; int[] newTabRuns = new int[tabRuns.length]; int currentRun = selectedRun; int i = 1; do { - newTabRuns[i] = tabRuns[currentRun]; - currentRun = getNextTabRun(currentRun); - i++; + newTabRuns[i] = tabRuns[currentRun]; + currentRun = getNextTabRun(currentRun); + i++; } while (i < runCount); if (runCount > 1) - newTabRuns[0] = tabRuns[currentRun]; + newTabRuns[0] = tabRuns[currentRun]; tabRuns = newTabRuns; BasicTabbedPaneUI.this.selectedRun = 1; @@ -902,7 +901,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected int preferredTabAreaHeight(int tabPlacement, int width) { if (tabPane.getTabCount() == 0) - return calculateTabAreaHeight(tabPlacement, 0, 0); + return calculateTabAreaHeight(tabPlacement, 0, 0); int runs = 1; @@ -923,7 +922,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected int preferredTabAreaWidth(int tabPlacement, int height) { if (tabPane.getTabCount() == 0) - return calculateTabAreaHeight(tabPlacement, 0, 0); + return calculateTabAreaHeight(tabPlacement, 0, 0); int runs = 1; @@ -944,7 +943,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected void calculateTabRects(int tabPlacement, int tabCount) { if (tabCount == 0) - return; + return; assureRectsCreated(tabCount); FontMetrics fm = getFontMetrics(); @@ -958,76 +957,76 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { - int maxHeight = calculateMaxTabHeight(tabPlacement); - calcRect.width -= tabAreaInsets.left + tabAreaInsets.right; - max = calcRect.width + tabAreaInsets.left + insets.left; - start = tabAreaInsets.left + insets.left; - int width = 0; - int runWidth = start; - top = insets.top + tabAreaInsets.top; - for (int i = 0; i < tabCount; i++) - { - width = calculateTabWidth(tabPlacement, i, fm); - - rects[i] = new Rectangle(runWidth, top, width, maxHeight); - runWidth += width; - } - tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right; - tabAreaRect.height = runs * maxTabHeight - - (runs - 1) * tabRunOverlay - + tabAreaInsets.top + tabAreaInsets.bottom; - contentRect.width = tabAreaRect.width; - contentRect.height = tabPane.getHeight() - insets.top - - insets.bottom - tabAreaRect.height; - contentRect.x = insets.left; - tabAreaRect.x = insets.left; - if (tabPlacement == SwingConstants.BOTTOM) - { - contentRect.y = insets.top; - tabAreaRect.y = contentRect.y + contentRect.height; - } - else - { - tabAreaRect.y = insets.top; - contentRect.y = tabAreaRect.y + tabAreaRect.height; - } + int maxHeight = calculateMaxTabHeight(tabPlacement); + calcRect.width -= tabAreaInsets.left + tabAreaInsets.right; + max = calcRect.width + tabAreaInsets.left + insets.left; + start = tabAreaInsets.left + insets.left; + int width = 0; + int runWidth = start; + top = insets.top + tabAreaInsets.top; + for (int i = 0; i < tabCount; i++) + { + width = calculateTabWidth(tabPlacement, i, fm); + + rects[i] = new Rectangle(runWidth, top, width, maxHeight); + runWidth += width; + } + tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right; + tabAreaRect.height = runs * maxTabHeight + - (runs - 1) * tabRunOverlay + + tabAreaInsets.top + tabAreaInsets.bottom; + contentRect.width = tabAreaRect.width; + contentRect.height = tabPane.getHeight() - insets.top + - insets.bottom - tabAreaRect.height; + contentRect.x = insets.left; + tabAreaRect.x = insets.left; + if (tabPlacement == SwingConstants.BOTTOM) + { + contentRect.y = insets.top; + tabAreaRect.y = contentRect.y + contentRect.height; + } + else + { + tabAreaRect.y = insets.top; + contentRect.y = tabAreaRect.y + tabAreaRect.height; + } } else { - int maxWidth = calculateMaxTabWidth(tabPlacement); - - calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom; - max = calcRect.height + tabAreaInsets.top; - int height = 0; - start = tabAreaInsets.top + insets.top; - int runHeight = start; - int fontHeight = fm.getHeight(); - top = insets.left + tabAreaInsets.left; - for (int i = 0; i < tabCount; i++) - { - height = calculateTabHeight(tabPlacement, i, fontHeight); - rects[i] = new Rectangle(top, runHeight, maxWidth, height); - runHeight += height; - } - tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay - + tabAreaInsets.left + tabAreaInsets.right; - tabAreaRect.height = tabPane.getHeight() - insets.top - - insets.bottom; - tabAreaRect.y = insets.top; - contentRect.width = tabPane.getWidth() - insets.left - insets.right - - tabAreaRect.width; - contentRect.height = tabAreaRect.height; - contentRect.y = insets.top; - if (tabPlacement == SwingConstants.LEFT) - { - tabAreaRect.x = insets.left; - contentRect.x = tabAreaRect.x + tabAreaRect.width; - } - else - { - contentRect.x = insets.left; - tabAreaRect.x = contentRect.x + contentRect.width; - } + int maxWidth = calculateMaxTabWidth(tabPlacement); + + calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom; + max = calcRect.height + tabAreaInsets.top; + int height = 0; + start = tabAreaInsets.top + insets.top; + int runHeight = start; + int fontHeight = fm.getHeight(); + top = insets.left + tabAreaInsets.left; + for (int i = 0; i < tabCount; i++) + { + height = calculateTabHeight(tabPlacement, i, fontHeight); + rects[i] = new Rectangle(top, runHeight, maxWidth, height); + runHeight += height; + } + tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay + + tabAreaInsets.left + tabAreaInsets.right; + tabAreaRect.height = tabPane.getHeight() - insets.top + - insets.bottom; + tabAreaRect.y = insets.top; + contentRect.width = tabPane.getWidth() - insets.left - insets.right + - tabAreaRect.width; + contentRect.height = tabAreaRect.height; + contentRect.y = insets.top; + if (tabPlacement == SwingConstants.LEFT) + { + tabAreaRect.x = insets.left; + contentRect.x = tabAreaRect.x + tabAreaRect.width; + } + else + { + contentRect.x = insets.left; + tabAreaRect.x = contentRect.x + contentRect.width; + } } runCount = runs; @@ -1047,68 +1046,68 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int tabCount = tabPane.getTabCount(); Point p = null; if (tabCount == 0) - return; + return; int tabPlacement = tabPane.getTabPlacement(); incrButton.hide(); decrButton.hide(); if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { - if (tabAreaRect.x + tabAreaRect.width < rects[tabCount - 1].x - + rects[tabCount - 1].width) - { - Dimension incrDims = incrButton.getPreferredSize(); - Dimension decrDims = decrButton.getPreferredSize(); - - decrButton.setBounds(tabAreaRect.x + tabAreaRect.width - - incrDims.width - decrDims.width, - tabAreaRect.y, decrDims.width, - tabAreaRect.height); - incrButton.setBounds(tabAreaRect.x + tabAreaRect.width - - incrDims.width, tabAreaRect.y, - decrDims.width, tabAreaRect.height); - - tabAreaRect.width -= decrDims.width + incrDims.width; - incrButton.show(); - decrButton.show(); - } + if (tabAreaRect.x + tabAreaRect.width < rects[tabCount - 1].x + + rects[tabCount - 1].width) + { + Dimension incrDims = incrButton.getPreferredSize(); + Dimension decrDims = decrButton.getPreferredSize(); + + decrButton.setBounds(tabAreaRect.x + tabAreaRect.width + - incrDims.width - decrDims.width, + tabAreaRect.y, decrDims.width, + tabAreaRect.height); + incrButton.setBounds(tabAreaRect.x + tabAreaRect.width + - incrDims.width, tabAreaRect.y, + decrDims.width, tabAreaRect.height); + + tabAreaRect.width -= decrDims.width + incrDims.width; + incrButton.show(); + decrButton.show(); + } } if (tabPlacement == SwingConstants.LEFT || tabPlacement == SwingConstants.RIGHT) { - if (tabAreaRect.y + tabAreaRect.height < rects[tabCount - 1].y - + rects[tabCount - 1].height) - { - Dimension incrDims = incrButton.getPreferredSize(); - Dimension decrDims = decrButton.getPreferredSize(); - - decrButton.setBounds(tabAreaRect.x, - tabAreaRect.y + tabAreaRect.height - - incrDims.height - decrDims.height, - tabAreaRect.width, decrDims.height); - incrButton.setBounds(tabAreaRect.x, - tabAreaRect.y + tabAreaRect.height - - incrDims.height, tabAreaRect.width, - incrDims.height); - - tabAreaRect.height -= decrDims.height + incrDims.height; - incrButton.show(); - decrButton.show(); - } + if (tabAreaRect.y + tabAreaRect.height < rects[tabCount - 1].y + + rects[tabCount - 1].height) + { + Dimension incrDims = incrButton.getPreferredSize(); + Dimension decrDims = decrButton.getPreferredSize(); + + decrButton.setBounds(tabAreaRect.x, + tabAreaRect.y + tabAreaRect.height + - incrDims.height - decrDims.height, + tabAreaRect.width, decrDims.height); + incrButton.setBounds(tabAreaRect.x, + tabAreaRect.y + tabAreaRect.height + - incrDims.height, tabAreaRect.width, + incrDims.height); + + tabAreaRect.height -= decrDims.height + incrDims.height; + incrButton.show(); + decrButton.show(); + } } viewport.setBounds(tabAreaRect.x, tabAreaRect.y, tabAreaRect.width, tabAreaRect.height); int tabC = tabPane.getTabCount() - 1; if (tabCount > 0) { - int w = Math.max(rects[tabC].width + rects[tabC].x, tabAreaRect.width); - int h = Math.max(rects[tabC].height, tabAreaRect.height); - p = findPointForIndex(currentScrollLocation); - - // we want to cover that entire space so that borders that run under - // the tab area don't show up when we move the viewport around. - panel.setSize(w + p.x, h + p.y); + int w = Math.max(rects[tabC].width + rects[tabC].x, tabAreaRect.width); + int h = Math.max(rects[tabC].height, tabAreaRect.height); + p = findPointForIndex(currentScrollLocation); + + // we want to cover that entire space so that borders that run under + // the tab area don't show up when we move the viewport around. + panel.setSize(w + p.x, h + p.y); } viewport.setViewPosition(p); viewport.repaint(); @@ -1160,7 +1159,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public void paint(Graphics g, JComponent c) { - paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex()); + paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex()); } } @@ -1182,6 +1181,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ private class ScrollingViewport extends JViewport implements UIResource { + // TODO: Maybe remove this inner class. } /** @@ -1407,22 +1407,22 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == TOP || tabPlacement == BOTTOM) { - if (index > 0) - { - w += rects[index - 1].x + rects[index - 1].width; - if (index > selectedIndex) - w -= insets.left + insets.right; - } + if (index > 0) + { + w += rects[index - 1].x + rects[index - 1].width; + if (index > selectedIndex) + w -= insets.left + insets.right; + } } else { - if (index > 0) - { - h += rects[index - 1].y + rects[index - 1].height; - if (index > selectedIndex) - h -= insets.top + insets.bottom; - } + if (index > 0) + { + h += rects[index - 1].y + rects[index - 1].height; + if (index > selectedIndex) + h -= insets.top + insets.bottom; + } } Point p = new Point(w, h); @@ -1451,16 +1451,16 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants super.installUI(c); if (c instanceof JTabbedPane) { - tabPane = (JTabbedPane) c; - - installComponents(); - installDefaults(); - installListeners(); - installKeyboardActions(); - - layoutManager = createLayoutManager(); - tabPane.setLayout(layoutManager); - tabPane.layout(); + tabPane = (JTabbedPane) c; + + installComponents(); + installDefaults(); + installListeners(); + installKeyboardActions(); + + layoutManager = createLayoutManager(); + tabPane.setLayout(layoutManager); + tabPane.layout(); } } @@ -1495,23 +1495,23 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants return new TabbedPaneLayout(); else { - incrButton = createIncreaseButton(); - decrButton = createDecreaseButton(); - viewport = new ScrollingViewport(); - viewport.setLayout(null); - panel = new ScrollingPanel(); - viewport.setView(panel); - tabPane.add(incrButton); - tabPane.add(decrButton); - tabPane.add(viewport); - currentScrollLocation = 0; - decrButton.setEnabled(false); - panel.addMouseListener(mouseListener); - incrButton.addMouseListener(mouseListener); - decrButton.addMouseListener(mouseListener); - viewport.setBackground(Color.LIGHT_GRAY); - - return new TabbedPaneScrollLayout(); + incrButton = createIncreaseButton(); + decrButton = createDecreaseButton(); + viewport = new ScrollingViewport(); + viewport.setLayout(null); + panel = new ScrollingPanel(); + viewport.setView(panel); + tabPane.add(incrButton); + tabPane.add(decrButton); + tabPane.add(viewport); + currentScrollLocation = 0; + decrButton.setEnabled(false); + panel.addMouseListener(mouseListener); + incrButton.addMouseListener(mouseListener); + decrButton.addMouseListener(mouseListener); + viewport.setBackground(Color.LIGHT_GRAY); + + return new TabbedPaneScrollLayout(); } } @@ -1536,28 +1536,26 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - tabPane.setFont(defaults.getFont("TabbedPane.font")); - tabPane.setForeground(defaults.getColor("TabbedPane.foreground")); - tabPane.setBackground(defaults.getColor("TabbedPane.background")); + LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background", + "TabbedPane.foreground", + "TabbedPane.font"); tabPane.setOpaque(false); - highlight = defaults.getColor("TabbedPane.highlight"); - lightHighlight = defaults.getColor("TabbedPane.lightHighlight"); + highlight = UIManager.getColor("TabbedPane.highlight"); + lightHighlight = UIManager.getColor("TabbedPane.lightHighlight"); - shadow = defaults.getColor("TabbedPane.shadow"); - darkShadow = defaults.getColor("TabbedPane.darkShadow"); + shadow = UIManager.getColor("TabbedPane.shadow"); + darkShadow = UIManager.getColor("TabbedPane.darkShadow"); - focus = defaults.getColor("TabbedPane.focus"); + focus = UIManager.getColor("TabbedPane.focus"); - textIconGap = defaults.getInt("TabbedPane.textIconGap"); - tabRunOverlay = defaults.getInt("TabbedPane.tabRunOverlay"); + textIconGap = UIManager.getInt("TabbedPane.textIconGap"); + tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay"); - tabInsets = defaults.getInsets("TabbedPane.tabbedPaneTabInsets"); - selectedTabPadInsets = defaults.getInsets("TabbedPane.tabbedPaneTabPadInsets"); - tabAreaInsets = defaults.getInsets("TabbedPane.tabbedPaneTabAreaInsets"); - contentBorderInsets = defaults.getInsets("TabbedPane.tabbedPaneContentBorderInsets"); + tabInsets = UIManager.getInsets("TabbedPane.tabbedPaneTabInsets"); + selectedTabPadInsets = UIManager.getInsets("TabbedPane.tabbedPaneTabPadInsets"); + tabAreaInsets = UIManager.getInsets("TabbedPane.tabbedPaneTabAreaInsets"); + contentBorderInsets = UIManager.getInsets("TabbedPane.tabbedPaneContentBorderInsets"); calcRect = new Rectangle(); tabRuns = new int[10]; @@ -1737,38 +1735,42 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // we WANT to paint the outermost run first and then work our way in. int tabCount = tabPane.getTabCount(); int currRun = 1; + + if (tabCount > runCount) + runCount = tabCount; + if (tabCount < 1) return; - + if (runCount > 1) - currRun = 0; + currRun = 0; for (int i = 0; i < runCount; i++) { - int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1; - if (isScroll) - first = currentScrollLocation; - else if (first == tabCount) - first = 0; - int last = lastTabInRun(tabCount, currRun); - if (isScroll) - { - for (int k = first; k < tabCount; k++) - { - if (rects[k].x + rects[k].width - rects[first].x > viewport - .getWidth()) - { - last = k; - break; - } - } - } - - for (int j = first; j <= last; j++) - { - if (j != selectedIndex || isScroll) - paintTab(g, tabPlacement, rects, j, ir, tr); - } - currRun = getPreviousTabRun(currRun); + int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1; + if (isScroll) + first = currentScrollLocation; + else if (first == tabCount) + first = 0; + int last = lastTabInRun(tabCount, currRun); + if (isScroll) + { + for (int k = first; k < tabCount; k++) + { + if (rects[k].x + rects[k].width - rects[first].x > viewport + .getWidth()) + { + last = k; + break; + } + } + } + + for (int j = first; j <= last; j++) + { + if (j != selectedIndex || isScroll) + paintTab(g, tabPlacement, rects, j, ir, tr); + } + currRun = getPreviousTabRun(currRun); } if (! isScroll) paintTab(g, tabPlacement, rects, selectedIndex, ir, tr); @@ -1800,24 +1802,24 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int h = calcRect.height; if (getRunForTab(tabPane.getTabCount(), tabIndex) == 1) { - Insets insets = getTabAreaInsets(tabPlacement); - switch (tabPlacement) - { - case TOP: - h += insets.bottom; - break; - case LEFT: - w += insets.right; - break; - case BOTTOM: - y -= insets.top; - h += insets.top; - break; - case RIGHT: - x -= insets.left; - w += insets.left; - break; - } + Insets insets = getTabAreaInsets(tabPlacement); + switch (tabPlacement) + { + case TOP: + h += insets.bottom; + break; + case LEFT: + w += insets.right; + break; + case BOTTOM: + y -= insets.top; + h += insets.top; + break; + case RIGHT: + x -= insets.left; + w += insets.left; + break; + } } layoutLabel(tabPlacement, fm, tabIndex, title, icon, calcRect, iconRect, @@ -1856,7 +1858,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants SwingConstants.CENTER, SwingConstants.CENTER, SwingConstants.CENTER, - SwingConstants.CENTER, tabRect, + SwingConstants.RIGHT, tabRect, iconRect, textRect, textIconGap); int shiftX = getTabLabelShiftX(tabPlacement, tabIndex, isSelected); @@ -1904,8 +1906,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants View textView = getTextViewForTab(tabIndex); if (textView != null) { - textView.paint(g, textRect); - return; + textView.paint(g, textRect); + return; } Color fg = tabPane.getForegroundAt(tabIndex); @@ -1921,37 +1923,37 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPane.isEnabledAt(tabIndex)) { - g.setColor(fg); + g.setColor(fg); - int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex); + int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex); - if (mnemIndex != -1) - BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, - textRect.x, - textRect.y - + metrics.getAscent()); - else - g.drawString(title, textRect.x, textRect.y + metrics.getAscent()); + if (mnemIndex != -1) + BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, + textRect.x, + textRect.y + + metrics.getAscent()); + else + g.drawString(title, textRect.x, textRect.y + metrics.getAscent()); } else { - g.setColor(bg.brighter()); - - int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex); - - if (mnemIndex != -1) - BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, - textRect.x, textRect.y); - else - g.drawString(title, textRect.x, textRect.y); - - g.setColor(bg.darker()); - if (mnemIndex != -1) - BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, - textRect.x + 1, - textRect.y + 1); - else - g.drawString(title, textRect.x + 1, textRect.y + 1); + g.setColor(bg.brighter()); + + int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex); + + if (mnemIndex != -1) + BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, + textRect.x, textRect.y); + else + g.drawString(title, textRect.x, textRect.y); + + g.setColor(bg.darker()); + if (mnemIndex != -1) + BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex, + textRect.x + 1, + textRect.y + 1); + else + g.drawString(title, textRect.x + 1, textRect.y + 1); } g.setColor(saved_color); @@ -2037,30 +2039,30 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (! isSelected || tabPlacement != SwingConstants.TOP) { - g.setColor(shadow); - g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); - g.setColor(darkShadow); - g.drawLine(x, y + h, x + w, y + h); + g.setColor(shadow); + g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); + g.setColor(darkShadow); + g.drawLine(x, y + h, x + w, y + h); } if (! isSelected || tabPlacement != SwingConstants.LEFT) { - g.setColor(darkShadow); - g.drawLine(x + w, y, x + w, y + h); - g.setColor(shadow); - g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); + g.setColor(darkShadow); + g.drawLine(x + w, y, x + w, y + h); + g.setColor(shadow); + g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); } if (! isSelected || tabPlacement != SwingConstants.RIGHT) { - g.setColor(lightHighlight); - g.drawLine(x, y, x, y + h); + g.setColor(lightHighlight); + g.drawLine(x, y, x, y + h); } if (! isSelected || tabPlacement != SwingConstants.BOTTOM) { - g.setColor(lightHighlight); - g.drawLine(x, y, x + w, y); + g.setColor(lightHighlight); + g.drawLine(x, y, x + w, y); } g.setColor(saved); @@ -2087,10 +2089,10 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants g.setColor(Color.LIGHT_GRAY); else { - Color bg = tabPane.getBackgroundAt(tabIndex); - if (bg == null) - bg = Color.GRAY; - g.setColor(bg); + Color bg = tabPane.getBackgroundAt(tabIndex); + if (bg == null) + bg = Color.GRAY; + g.setColor(bg); } g.fillRect(x, y, w, h); @@ -2144,14 +2146,14 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingConstants.TOP) { - if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) - { - Point p = findPointForIndex(currentScrollLocation); - diff = p.x; - } - - g.drawLine(x, y, startgap - diff, y); - g.drawLine(endgap - diff, y, x + w, y); + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + { + Point p = findPointForIndex(currentScrollLocation); + diff = p.x; + } + + g.drawLine(x, y, startgap - diff, y); + g.drawLine(endgap - diff, y, x + w, y); } else g.drawLine(x, y, x + w, y); @@ -2184,14 +2186,14 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingConstants.LEFT) { - if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) - { - Point p = findPointForIndex(currentScrollLocation); - diff = p.y; - } - - g.drawLine(x, y, x, startgap - diff); - g.drawLine(x, endgap - diff, x, y + h); + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + { + Point p = findPointForIndex(currentScrollLocation); + diff = p.y; + } + + g.drawLine(x, y, x, startgap - diff); + g.drawLine(x, endgap - diff, x, y + h); } else g.drawLine(x, y, x, y + h); @@ -2223,26 +2225,26 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingConstants.BOTTOM) { - if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) - { - Point p = findPointForIndex(currentScrollLocation); - diff = p.x; - } - - g.setColor(shadow); - g.drawLine(x + 1, y + h - 1, startgap - diff, y + h - 1); - g.drawLine(endgap - diff, y + h - 1, x + w - 1, y + h - 1); - - g.setColor(darkShadow); - g.drawLine(x, y + h, startgap - diff, y + h); - g.drawLine(endgap - diff, y + h, x + w, y + h); + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + { + Point p = findPointForIndex(currentScrollLocation); + diff = p.x; + } + + g.setColor(shadow); + g.drawLine(x + 1, y + h - 1, startgap - diff, y + h - 1); + g.drawLine(endgap - diff, y + h - 1, x + w - 1, y + h - 1); + + g.setColor(darkShadow); + g.drawLine(x, y + h, startgap - diff, y + h); + g.drawLine(endgap - diff, y + h, x + w, y + h); } else { - g.setColor(shadow); - g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); - g.setColor(darkShadow); - g.drawLine(x, y + h, x + w, y + h); + g.setColor(shadow); + g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); + g.setColor(darkShadow); + g.drawLine(x, y + h, x + w, y + h); } g.setColor(saved); @@ -2271,26 +2273,26 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingConstants.RIGHT) { - if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) - { - Point p = findPointForIndex(currentScrollLocation); - diff = p.y; - } - - g.setColor(shadow); - g.drawLine(x + w - 1, y + 1, x + w - 1, startgap - diff); - g.drawLine(x + w - 1, endgap - diff, x + w - 1, y + h - 1); - - g.setColor(darkShadow); - g.drawLine(x + w, y, x + w, startgap - diff); - g.drawLine(x + w, endgap - diff, x + w, y + h); + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + { + Point p = findPointForIndex(currentScrollLocation); + diff = p.y; + } + + g.setColor(shadow); + g.drawLine(x + w - 1, y + 1, x + w - 1, startgap - diff); + g.drawLine(x + w - 1, endgap - diff, x + w - 1, y + h - 1); + + g.setColor(darkShadow); + g.drawLine(x + w, y, x + w, startgap - diff); + g.drawLine(x + w, endgap - diff, x + w, y + h); } else { - g.setColor(shadow); - g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); - g.setColor(darkShadow); - g.drawLine(x + w, y, x + w, y + h); + g.setColor(shadow); + g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); + g.setColor(darkShadow); + g.drawLine(x + w, y, x + w, y + h); } g.setColor(saved); @@ -2337,16 +2339,16 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int currRun = 1; for (int i = 0; i < runCount; i++) { - int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1; - if (first == tabCount) - first = 0; - int last = lastTabInRun(tabCount, currRun); - for (int j = first; j <= last; j++) - { - if (getTabBounds(pane, j).contains(p)) - return j; - } - currRun = getNextTabRun(currRun); + int first = lastTabInRun(tabCount, getPreviousTabRun(currRun)) + 1; + if (first == tabCount) + first = 0; + int last = lastTabInRun(tabCount, currRun); + for (int j = first; j <= last; j++) + { + if (getTabBounds(pane, j).contains(p)) + return j; + } + currRun = getNextTabRun(currRun); } return -1; } @@ -2400,10 +2402,10 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants return; else { - int numToCopy = Math.min(tabCount, rects.length); - Rectangle[] tmp = new Rectangle[tabCount]; - System.arraycopy(rects, 0, tmp, 0, numToCopy); - rects = tmp; + int numToCopy = Math.min(tabCount, rects.length); + Rectangle[] tmp = new Rectangle[tabCount]; + System.arraycopy(rects, 0, tmp, 0, numToCopy); + rects = tmp; } } @@ -2418,9 +2420,9 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants tabRuns = new int[10]; else { - int[] newRuns = new int[tabRuns.length + 10]; - System.arraycopy(tabRuns, 0, newRuns, 0, tabRuns.length); - tabRuns = newRuns; + int[] newRuns = new int[tabRuns.length + 10]; + System.arraycopy(tabRuns, 0, newRuns, 0, tabRuns.length); + tabRuns = newRuns; } } @@ -2438,12 +2440,12 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants return 1; for (int i = 0; i < runCount; i++) { - int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1; - if (first == tabCount) - first = 0; - int last = lastTabInRun(tabCount, i); - if (last >= tabIndex && first <= tabIndex) - return i; + int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1; + if (first == tabCount) + first = 0; + int last = lastTabInRun(tabCount, i); + if (last >= tabIndex && first <= tabIndex) + return i; } return -1; } @@ -2560,21 +2562,22 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants Icon icon = getIconForTab(tabIndex); Insets insets = getTabInsets(tabPlacement, tabIndex); + int height = 0; if (icon != null) { - Rectangle vr = new Rectangle(); - Rectangle ir = new Rectangle(); - Rectangle tr = new Rectangle(); - layoutLabel(tabPlacement, getFontMetrics(), tabIndex, - tabPane.getTitleAt(tabIndex), icon, vr, ir, tr, - tabIndex == tabPane.getSelectedIndex()); - calcRect = tr.union(ir); + Rectangle vr = new Rectangle(); + Rectangle ir = new Rectangle(); + Rectangle tr = new Rectangle(); + layoutLabel(tabPlacement, getFontMetrics(), tabIndex, + tabPane.getTitleAt(tabIndex), icon, vr, ir, tr, + tabIndex == tabPane.getSelectedIndex()); + height = tr.union(ir).height; } else - calcRect.height = fontHeight; + height = fontHeight; - calcRect.height += insets.top + insets.bottom; - return calcRect.height; + height += insets.top + insets.bottom; + return height; } /** @@ -2614,21 +2617,22 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants Icon icon = getIconForTab(tabIndex); Insets insets = getTabInsets(tabPlacement, tabIndex); + int width = 0; if (icon != null) { - Rectangle vr = new Rectangle(); - Rectangle ir = new Rectangle(); - Rectangle tr = new Rectangle(); - layoutLabel(tabPlacement, getFontMetrics(), tabIndex, - tabPane.getTitleAt(tabIndex), icon, vr, ir, tr, - tabIndex == tabPane.getSelectedIndex()); - calcRect = tr.union(ir); + Rectangle vr = new Rectangle(); + Rectangle ir = new Rectangle(); + Rectangle tr = new Rectangle(); + layoutLabel(tabPlacement, getFontMetrics(), tabIndex, + tabPane.getTitleAt(tabIndex), icon, vr, ir, tr, + tabIndex == tabPane.getSelectedIndex()); + width = tr.union(ir).width; } else - calcRect.width = metrics.stringWidth(tabPane.getTitleAt(tabIndex)); + width = metrics.stringWidth(tabPane.getTitleAt(tabIndex)); - calcRect.width += insets.left + insets.right; - return calcRect.width; + width += insets.left + insets.right; + return width; } /** @@ -2775,37 +2779,37 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { - if (direction == SwingConstants.WEST) - selectPreviousTabInRun(tabPane.getSelectedIndex()); - else if (direction == SwingConstants.EAST) - selectNextTabInRun(tabPane.getSelectedIndex()); - - else - { - int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(), - tabPane.getSelectedIndex(), - (tabPlacement == SwingConstants.RIGHT) - ? true : false); - selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(), - offset); - } + if (direction == SwingConstants.WEST) + selectPreviousTabInRun(tabPane.getSelectedIndex()); + else if (direction == SwingConstants.EAST) + selectNextTabInRun(tabPane.getSelectedIndex()); + + else + { + int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(), + tabPane.getSelectedIndex(), + (tabPlacement == SwingConstants.RIGHT) + ? true : false); + selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(), + offset); + } } if (tabPlacement == SwingConstants.LEFT || tabPlacement == SwingConstants.RIGHT) { - if (direction == SwingConstants.NORTH) - selectPreviousTabInRun(tabPane.getSelectedIndex()); - else if (direction == SwingConstants.SOUTH) - selectNextTabInRun(tabPane.getSelectedIndex()); - else - { - int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(), - tabPane.getSelectedIndex(), - (tabPlacement == SwingConstants.RIGHT) - ? true : false); - selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(), - offset); - } + if (direction == SwingConstants.NORTH) + selectPreviousTabInRun(tabPane.getSelectedIndex()); + else if (direction == SwingConstants.SOUTH) + selectNextTabInRun(tabPane.getSelectedIndex()); + else + { + int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(), + tabPane.getSelectedIndex(), + (tabPlacement == SwingConstants.RIGHT) + ? true : false); + selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(), + offset); + } } } @@ -2869,16 +2873,16 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int y = rects[tabIndex].y + rects[tabIndex].height / 2; switch (tabPlacement) - { - case SwingConstants.TOP: - case SwingConstants.BOTTOM: - y += offset; - break; - case SwingConstants.RIGHT: - case SwingConstants.LEFT: - x += offset; - break; - } + { + case SwingConstants.TOP: + case SwingConstants.BOTTOM: + y += offset; + break; + case SwingConstants.RIGHT: + case SwingConstants.LEFT: + x += offset; + break; + } int index = tabForCoordinate(tabPane, x, y); if (index != -1) @@ -3042,31 +3046,31 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // Sun's version will happily throw an NPE if params are null, // so I won't check it either. switch (targetPlacement) - { - case SwingConstants.TOP: - targetInsets.top = topInsets.top; - targetInsets.left = topInsets.left; - targetInsets.right = topInsets.right; - targetInsets.bottom = topInsets.bottom; - break; - case SwingConstants.LEFT: - targetInsets.left = topInsets.top; - targetInsets.top = topInsets.left; - targetInsets.right = topInsets.bottom; - targetInsets.bottom = topInsets.right; - break; - case SwingConstants.BOTTOM: - targetInsets.top = topInsets.bottom; - targetInsets.bottom = topInsets.top; - targetInsets.left = topInsets.left; - targetInsets.right = topInsets.right; - break; - case SwingConstants.RIGHT: - targetInsets.top = topInsets.left; - targetInsets.left = topInsets.bottom; - targetInsets.bottom = topInsets.right; - targetInsets.right = topInsets.top; - break; - } + { + case SwingConstants.TOP: + targetInsets.top = topInsets.top; + targetInsets.left = topInsets.left; + targetInsets.right = topInsets.right; + targetInsets.bottom = topInsets.bottom; + break; + case SwingConstants.LEFT: + targetInsets.left = topInsets.top; + targetInsets.top = topInsets.left; + targetInsets.right = topInsets.bottom; + targetInsets.bottom = topInsets.right; + break; + case SwingConstants.BOTTOM: + targetInsets.top = topInsets.bottom; + targetInsets.bottom = topInsets.top; + targetInsets.left = topInsets.left; + targetInsets.right = topInsets.right; + break; + case SwingConstants.RIGHT: + targetInsets.top = topInsets.left; + targetInsets.left = topInsets.bottom; + targetInsets.bottom = topInsets.right; + targetInsets.right = topInsets.top; + break; + } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java index 700b406d076..ec0467a74f3 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java @@ -46,7 +46,7 @@ import java.awt.event.MouseEvent; import javax.swing.CellRendererPane; import javax.swing.JComponent; -import javax.swing.UIDefaults; +import javax.swing.LookAndFeel; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.event.MouseInputListener; @@ -57,8 +57,7 @@ import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; -public class BasicTableHeaderUI - extends TableHeaderUI +public class BasicTableHeaderUI extends TableHeaderUI { public static ComponentUI createUI(JComponent h) @@ -71,16 +70,42 @@ public class BasicTableHeaderUI protected CellRendererPane rendererPane; protected Border cellBorder; - class MouseInputHandler - implements MouseInputListener + public class MouseInputHandler implements MouseInputListener { - public void mouseClicked(MouseEvent e) {} - public void mouseDragged(MouseEvent e) {} - public void mouseEntered(MouseEvent e) {} - public void mouseExited(MouseEvent e) {} - public void mouseMoved(MouseEvent e) {} - public void mousePressed(MouseEvent e) {} - public void mouseReleased(MouseEvent e) {} + public void mouseClicked(MouseEvent e) + { + // TODO: Implement this properly. + } + + public void mouseDragged(MouseEvent e) + { + // TODO: Implement this properly. + } + + public void mouseEntered(MouseEvent e) + { + // TODO: Implement this properly. + } + + public void mouseExited(MouseEvent e) + { + // TODO: Implement this properly. + } + + public void mouseMoved(MouseEvent e) + { + // TODO: Implement this properly. + } + + public void mousePressed(MouseEvent e) + { + // TODO: Implement this properly. + } + + public void mouseReleased(MouseEvent e) + { + // TODO: Implement this properly. + } } protected MouseInputListener createMouseInputListener() @@ -95,15 +120,15 @@ public class BasicTableHeaderUI protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - header.setBackground(defaults.getColor("TableHeader.background")); - header.setForeground(defaults.getColor("TableHeader.foreground")); - header.setFont(defaults.getFont("TableHeader.font")); - cellBorder = defaults.getBorder("TableHeader.cellBorder"); + LookAndFeel.installColorsAndFont(header, "TableHeader.background", + "TableHeader.foreground", + "TableHeader.font"); + cellBorder = UIManager.getBorder("TableHeader.cellBorder"); } protected void installKeyboardActions() { + // TODO: Implement this properly. } protected void installListeners() @@ -128,6 +153,7 @@ public class BasicTableHeaderUI protected void uninstallKeyboardActions() { + // TODO: Implement this properly. } protected void uninstallListeners() @@ -157,6 +183,7 @@ public class BasicTableHeaderUI Rectangle bounds = header.getHeaderRect(i); if (bounds.intersects(clip)) { + Rectangle oldClip = gfx.getClipBounds(); TableColumn col = cmod.getColumn(i); TableCellRenderer rend = col.getHeaderRenderer(); if (rend == null) @@ -173,10 +200,12 @@ public class BasicTableHeaderUI if (comp instanceof JComponent) ((JComponent)comp).setBorder(cellBorder); gfx.translate(bounds.x, bounds.y); + gfx.setClip(0, 0, bounds.width, bounds.height); comp.setSize(bounds.width, bounds.height); comp.setLocation(0,0); comp.paint(gfx); gfx.translate(-bounds.x, -bounds.y); + gfx.setClip(oldClip); } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java index 4559937eb69..25a845b36b8 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java @@ -49,35 +49,38 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; -import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import javax.swing.AbstractAction; import javax.swing.ActionMap; -import javax.swing.BorderFactory; import javax.swing.CellRendererPane; +import javax.swing.DefaultListSelectionModel; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.ListSelectionModel; +import javax.swing.LookAndFeel; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.event.ChangeEvent; import javax.swing.event.MouseInputListener; +import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.InputMapUIResource; import javax.swing.plaf.TableUI; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; +import javax.swing.table.TableModel; -public class BasicTableUI - extends TableUI +public class BasicTableUI extends TableUI { public static ComponentUI createUI(JComponent comp) { @@ -93,23 +96,72 @@ public class BasicTableUI /** The normal cell border. */ Border cellBorder; - /** The cell border for selected/highlighted cells. */ - Border highlightCellBorder; - /** The action bound to KeyStrokes. */ TableAction action; - class FocusHandler implements FocusListener + /** + * Listens for changes to the tables properties. + */ + private PropertyChangeListener propertyChangeListener; + + /** + * Handles key events for the JTable. Key events should be handled through + * the InputMap/ActionMap mechanism since JDK1.3. This class is only there + * for backwards compatibility. + * + * @author Roman Kennke (kennke@aicas.com) + */ + public class KeyHandler implements KeyListener + { + + /** + * Receives notification that a key has been pressed and released. + * + * @param event the key event + */ + public void keyTyped(KeyEvent event) + { + // Key events should be handled through the InputMap/ActionMap mechanism + // since JDK1.3. This class is only there for backwards compatibility. + } + + /** + * Receives notification that a key has been pressed. + * + * @param event the key event + */ + public void keyPressed(KeyEvent event) + { + // Key events should be handled through the InputMap/ActionMap mechanism + // since JDK1.3. This class is only there for backwards compatibility. + } + + /** + * Receives notification that a key has been released. + * + * @param event the key event + */ + public void keyReleased(KeyEvent event) + { + // Key events should be handled through the InputMap/ActionMap mechanism + // since JDK1.3. This class is only there for backwards compatibility. + } + } + + public class FocusHandler implements FocusListener { public void focusGained(FocusEvent e) { + // TODO: Implement this properly. } + public void focusLost(FocusEvent e) { + // TODO: Implement this properly. } } - class MouseInputHandler implements MouseInputListener + public class MouseInputHandler implements MouseInputListener { Point begin, curr; @@ -145,54 +197,123 @@ public class BasicTableUI public void mouseClicked(MouseEvent e) { + // TODO: What should be done here, if anything? } + public void mouseDragged(MouseEvent e) { - curr = new Point(e.getX(), e.getY()); - updateSelection(e.isControlDown()); + if (table.isEnabled()) + { + curr = new Point(e.getX(), e.getY()); + updateSelection(e.isControlDown()); + } } + public void mouseEntered(MouseEvent e) { + // TODO: What should be done here, if anything? } + public void mouseExited(MouseEvent e) { + // TODO: What should be done here, if anything? } + public void mouseMoved(MouseEvent e) { + // TODO: What should be done here, if anything? } + public void mousePressed(MouseEvent e) { - ListSelectionModel rowModel = table.getSelectionModel(); - ListSelectionModel colModel = table.getColumnModel().getSelectionModel(); - int rowLead = rowModel.getLeadSelectionIndex(); - int colLead = colModel.getLeadSelectionIndex(); + if (table.isEnabled()) + { + ListSelectionModel rowModel = table.getSelectionModel(); + ListSelectionModel colModel = table.getColumnModel().getSelectionModel(); + int rowLead = rowModel.getLeadSelectionIndex(); + int colLead = colModel.getLeadSelectionIndex(); - begin = new Point(e.getX(), e.getY()); - curr = new Point(e.getX(), e.getY()); - //if control is pressed and the cell is already selected, deselect it - if (e.isControlDown() && table. - isCellSelected(table.rowAtPoint(begin),table.columnAtPoint(begin))) - { - table.getSelectionModel(). - removeSelectionInterval(table.rowAtPoint(begin), - table.rowAtPoint(begin)); - table.getColumnModel().getSelectionModel(). - removeSelectionInterval(table.columnAtPoint(begin), - table.columnAtPoint(begin)); - } - else - updateSelection(e.isControlDown()); + begin = new Point(e.getX(), e.getY()); + curr = new Point(e.getX(), e.getY()); + //if control is pressed and the cell is already selected, deselect it + if (e.isControlDown() && table. + isCellSelected(table.rowAtPoint(begin),table.columnAtPoint(begin))) + { + table.getSelectionModel(). + removeSelectionInterval(table.rowAtPoint(begin), + table.rowAtPoint(begin)); + table.getColumnModel().getSelectionModel(). + removeSelectionInterval(table.columnAtPoint(begin), + table.columnAtPoint(begin)); + } + else + updateSelection(e.isControlDown()); - // If we were editing, but the moved to another cell, stop editing - if (rowLead != rowModel.getLeadSelectionIndex() || - colLead != colModel.getLeadSelectionIndex()) - if (table.isEditing()) - table.editingStopped(new ChangeEvent(e)); + // If we were editing, but the moved to another cell, stop editing + if (rowLead != rowModel.getLeadSelectionIndex() || + colLead != colModel.getLeadSelectionIndex()) + if (table.isEditing()) + table.editingStopped(new ChangeEvent(e)); + } } + public void mouseReleased(MouseEvent e) { - begin = null; - curr = null; + if (table.isEnabled()) + { + begin = null; + curr = null; + } + } + } + + /** + * Listens for changes to the model property of the JTable and adjusts some + * settings. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class PropertyChangeHandler implements PropertyChangeListener + { + /** + * Receives notification if one of the JTable's properties changes. + * + * @param ev the property change event + */ + public void propertyChange(PropertyChangeEvent ev) + { + String propName = ev.getPropertyName(); + if (propName.equals("model")) + { + ListSelectionModel rowSel = table.getSelectionModel(); + rowSel.clearSelection(); + ListSelectionModel colSel = table.getColumnModel().getSelectionModel(); + colSel.clearSelection(); + TableModel model = table.getModel(); + + // Adjust lead and anchor selection indices of the row and column + // selection models. + if (model.getRowCount() > 0) + { + rowSel.setAnchorSelectionIndex(0); + rowSel.setLeadSelectionIndex(0); + } + else + { + rowSel.setAnchorSelectionIndex(-1); + rowSel.setLeadSelectionIndex(-1); + } + if (model.getColumnCount() > 0) + { + colSel.setAnchorSelectionIndex(0); + colSel.setLeadSelectionIndex(0); + } + else + { + colSel.setAnchorSelectionIndex(-1); + colSel.setLeadSelectionIndex(-1); + } + } } } @@ -206,6 +327,17 @@ public class BasicTableUI return new MouseInputHandler(); } + + /** + * Creates and returns a key listener for the JTable. + * + * @return a key listener for the JTable + */ + protected KeyListener createKeyListener() + { + return new KeyHandler(); + } + /** * Return the maximum size of the table. The maximum height is the row * height times the number of rows. The maximum width is the sum of @@ -255,47 +387,13 @@ public class BasicTableUI protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - table.setFont(defaults.getFont("Table.font")); - table.setGridColor(defaults.getColor("Table.gridColor")); - table.setForeground(defaults.getColor("Table.foreground")); - table.setBackground(defaults.getColor("Table.background")); - table.setSelectionForeground(defaults.getColor("Table.selectionForeground")); - table.setSelectionBackground(defaults.getColor("Table.selectionBackground")); + LookAndFeel.installColorsAndFont(table, "Table.background", + "Table.foreground", "Table.font"); + table.setGridColor(UIManager.getColor("Table.gridColor")); + table.setSelectionForeground(UIManager.getColor("Table.selectionForeground")); + table.setSelectionBackground(UIManager.getColor("Table.selectionBackground")); table.setOpaque(true); - - highlightCellBorder = defaults.getBorder("Table.focusCellHighlightBorder"); - cellBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1); - } - - private int convertModifiers(int mod) - { - if ((mod & KeyEvent.SHIFT_DOWN_MASK) != 0) - { - mod |= KeyEvent.SHIFT_MASK; - mod &= ~KeyEvent.SHIFT_DOWN_MASK; - } - if ((mod & KeyEvent.CTRL_DOWN_MASK) != 0) - { - mod |= KeyEvent.CTRL_MASK; - mod &= ~KeyEvent.CTRL_DOWN_MASK; - } - if ((mod & KeyEvent.META_DOWN_MASK) != 0) - { - mod |= KeyEvent.META_MASK; - mod &= ~KeyEvent.META_DOWN_MASK; - } - if ((mod & KeyEvent.ALT_DOWN_MASK) != 0) - { - mod |= KeyEvent.ALT_MASK; - mod &= ~KeyEvent.ALT_DOWN_MASK; - } - if ((mod & KeyEvent.ALT_GRAPH_DOWN_MASK) != 0) - { - mod |= KeyEvent.ALT_GRAPH_MASK; - mod &= ~KeyEvent.ALT_GRAPH_DOWN_MASK; - } - return mod; + rendererPane = new CellRendererPane(); } protected void installKeyboardActions() @@ -304,27 +402,21 @@ public class BasicTableUI InputMap ancestorMap = (InputMap)defaults.get("Table.ancestorInputMap"); InputMapUIResource parentInputMap = new InputMapUIResource(); // FIXME: The JDK uses a LazyActionMap for parentActionMap - ActionMap parentActionMap = new ActionMap(); + ActionMap parentActionMap = new ActionMapUIResource(); action = new TableAction(); Object keys[] = ancestorMap.allKeys(); // Register key bindings in the UI InputMap-ActionMap pair - // Note that we register key bindings with both the old and new modifier - // masks: InputEvent.SHIFT_MASK and InputEvent.SHIFT_DOWN_MASK and so on. for (int i = 0; i < keys.length; i++) { - parentInputMap.put(KeyStroke.getKeyStroke - (((KeyStroke)keys[i]).getKeyCode(), convertModifiers - (((KeyStroke)keys[i]).getModifiers())), - (String)ancestorMap.get((KeyStroke)keys[i])); + KeyStroke stroke = (KeyStroke)keys[i]; + String actionString = (String) ancestorMap.get(stroke); - parentInputMap.put(KeyStroke.getKeyStroke - (((KeyStroke)keys[i]).getKeyCode(), - ((KeyStroke)keys[i]).getModifiers()), - (String)ancestorMap.get((KeyStroke)keys[i])); + parentInputMap.put(KeyStroke.getKeyStroke(stroke.getKeyCode(), + stroke.getModifiers()), + actionString); - parentActionMap.put - ((String)ancestorMap.get((KeyStroke)keys[i]), new ActionListenerProxy - (action, (String)ancestorMap.get((KeyStroke)keys[i]))); + parentActionMap.put (actionString, + new ActionListenerProxy (action, actionString)); } // Set the UI InputMap-ActionMap pair to be the parents of the @@ -383,8 +475,8 @@ public class BasicTableUI */ public void actionPerformed (ActionEvent e) { - ListSelectionModel rowModel = table.getSelectionModel(); - ListSelectionModel colModel = table.getColumnModel().getSelectionModel(); + DefaultListSelectionModel rowModel = (DefaultListSelectionModel) table.getSelectionModel(); + DefaultListSelectionModel colModel = (DefaultListSelectionModel) table.getColumnModel().getSelectionModel(); int rowLead = rowModel.getLeadSelectionIndex(); int rowMax = table.getModel().getRowCount() - 1; @@ -392,74 +484,75 @@ public class BasicTableUI int colLead = colModel.getLeadSelectionIndex(); int colMax = table.getModel().getColumnCount() - 1; - if (e.getActionCommand().equals("selectPreviousRowExtendSelection")) + String command = e.getActionCommand(); + + if (command.equals("selectPreviousRowExtendSelection")) { rowModel.setLeadSelectionIndex(Math.max(rowLead - 1, 0)); colModel.setLeadSelectionIndex(colLead); } - else if (e.getActionCommand().equals("selectLastColumn")) + else if (command.equals("selectLastColumn")) { - table.clearSelection(); rowModel.setSelectionInterval(rowLead, rowLead); colModel.setSelectionInterval(colMax, colMax); } - else if (e.getActionCommand().equals("startEditing")) + else if (command.equals("startEditing")) { if (table.isCellEditable(rowLead, colLead)) table.editCellAt(rowLead,colLead); } - else if (e.getActionCommand().equals("selectFirstRowExtendSelection")) + else if (command.equals("selectFirstRowExtendSelection")) { rowModel.setLeadSelectionIndex(0); colModel.setLeadSelectionIndex(colLead); } - else if (e.getActionCommand().equals("selectFirstColumn")) + else if (command.equals("selectFirstColumn")) { rowModel.setSelectionInterval(rowLead, rowLead); colModel.setSelectionInterval(0, 0); } - else if (e.getActionCommand().equals("selectFirstColumnExtendSelection")) + else if (command.equals("selectFirstColumnExtendSelection")) { colModel.setLeadSelectionIndex(0); rowModel.setLeadSelectionIndex(rowLead); - } - else if (e.getActionCommand().equals("selectLastRow")) + } + else if (command.equals("selectLastRow")) { rowModel.setSelectionInterval(rowMax,rowMax); colModel.setSelectionInterval(colLead, colLead); } - else if (e.getActionCommand().equals("selectNextRowExtendSelection")) + else if (command.equals("selectNextRowExtendSelection")) { rowModel.setLeadSelectionIndex(Math.min(rowLead + 1, rowMax)); colModel.setLeadSelectionIndex(colLead); } - else if (e.getActionCommand().equals("selectFirstRow")) + else if (command.equals("selectFirstRow")) { rowModel.setSelectionInterval(0,0); colModel.setSelectionInterval(colLead, colLead); } - else if (e.getActionCommand().equals("selectNextColumnExtendSelection")) + else if (command.equals("selectNextColumnExtendSelection")) { colModel.setLeadSelectionIndex(Math.min(colLead + 1, colMax)); rowModel.setLeadSelectionIndex(rowLead); } - else if (e.getActionCommand().equals("selectLastColumnExtendSelection")) + else if (command.equals("selectLastColumnExtendSelection")) { colModel.setLeadSelectionIndex(colMax); rowModel.setLeadSelectionIndex(rowLead); } - else if (e.getActionCommand().equals("selectPreviousColumnExtendSelection")) + else if (command.equals("selectPreviousColumnExtendSelection")) { colModel.setLeadSelectionIndex(Math.max(colLead - 1, 0)); rowModel.setLeadSelectionIndex(rowLead); } - else if (e.getActionCommand().equals("selectNextRow")) + else if (command.equals("selectNextRow")) { rowModel.setSelectionInterval(Math.min(rowLead + 1, rowMax), Math.min(rowLead + 1, rowMax)); colModel.setSelectionInterval(colLead,colLead); } - else if (e.getActionCommand().equals("scrollUpExtendSelection")) + else if (command.equals("scrollUpExtendSelection")) { int target; if (rowLead == getFirstVisibleRowIndex()) @@ -472,13 +565,13 @@ public class BasicTableUI rowModel.setLeadSelectionIndex(target); colModel.setLeadSelectionIndex(colLead); } - else if (e.getActionCommand().equals("selectPreviousRow")) + else if (command.equals("selectPreviousRow")) { rowModel.setSelectionInterval(Math.max(rowLead - 1, 0), Math.max(rowLead - 1, 0)); colModel.setSelectionInterval(colLead,colLead); } - else if (e.getActionCommand().equals("scrollRightChangeSelection")) + else if (command.equals("scrollRightChangeSelection")) { int target; if (colLead == getLastVisibleColumnIndex()) @@ -491,13 +584,13 @@ public class BasicTableUI colModel.setSelectionInterval(target, target); rowModel.setSelectionInterval(rowLead, rowLead); } - else if (e.getActionCommand().equals("selectPreviousColumn")) + else if (command.equals("selectPreviousColumn")) { rowModel.setSelectionInterval(rowLead,rowLead); colModel.setSelectionInterval(Math.max(colLead - 1, 0), Math.max(colLead - 1, 0)); } - else if (e.getActionCommand().equals("scrollLeftChangeSelection")) + else if (command.equals("scrollLeftChangeSelection")) { int target; if (colLead == getFirstVisibleColumnIndex()) @@ -510,11 +603,11 @@ public class BasicTableUI colModel.setSelectionInterval(target, target); rowModel.setSelectionInterval(rowLead, rowLead); } - else if (e.getActionCommand().equals("clearSelection")) + else if (command.equals("clearSelection")) { table.clearSelection(); } - else if (e.getActionCommand().equals("cancel")) + else if (command.equals("cancel")) { // FIXME: implement other parts of "cancel" like undo-ing last // selection. Right now it just calls editingCancelled if @@ -522,10 +615,10 @@ public class BasicTableUI if (table.isEditing()) table.editingCanceled(new ChangeEvent("cancel")); } - else if (e.getActionCommand().equals("selectNextRowCell") - || e.getActionCommand().equals("selectPreviousRowCell") - || e.getActionCommand().equals("selectNextColumnCell") - || e.getActionCommand().equals("selectPreviousColumnCell")) + else if (command.equals("selectNextRowCell") + || command.equals("selectPreviousRowCell") + || command.equals("selectNextColumnCell") + || command.equals("selectPreviousColumnCell")) { // If nothing is selected, select the first cell in the table if (table.getSelectedRowCount() == 0 && @@ -561,13 +654,13 @@ public class BasicTableUI // when you get to the edges of the table. if (!multColsSelected && !multRowsSelected) { - if (e.getActionCommand().indexOf("Column") != -1) + if (command.indexOf("Column") != -1) advanceSingleSelection(colModel, colMax, rowModel, rowMax, - (e.getActionCommand().equals + (command.equals ("selectPreviousColumnCell"))); else advanceSingleSelection(rowModel, rowMax, colModel, colMax, - (e.getActionCommand().equals + (command.equals ("selectPreviousRowCell"))); return; } @@ -588,25 +681,25 @@ public class BasicTableUI // If there are multiple rows and columns selected, select the next // cell and wrap at the edges of the selection. - if (e.getActionCommand().indexOf("Column") != -1) + if (command.indexOf("Column") != -1) advanceMultipleSelection(colModel, colMinSelected, colMaxSelected, rowModel, rowMinSelected, rowMaxSelected, - (e.getActionCommand().equals + (command.equals ("selectPreviousColumnCell")), true); else advanceMultipleSelection(rowModel, rowMinSelected, rowMaxSelected, colModel, colMinSelected, colMaxSelected, - (e.getActionCommand().equals + (command.equals ("selectPreviousRowCell")), false); } - else if (e.getActionCommand().equals("selectNextColumn")) + else if (command.equals("selectNextColumn")) { rowModel.setSelectionInterval(rowLead,rowLead); colModel.setSelectionInterval(Math.min(colLead + 1, colMax), Math.min(colLead + 1, colMax)); } - else if (e.getActionCommand().equals("scrollLeftExtendSelection")) + else if (command.equals("scrollLeftExtendSelection")) { int target; if (colLead == getFirstVisibleColumnIndex()) @@ -619,7 +712,7 @@ public class BasicTableUI colModel.setLeadSelectionIndex(target); rowModel.setLeadSelectionIndex(rowLead); } - else if (e.getActionCommand().equals("scrollDownChangeSelection")) + else if (command.equals("scrollDownChangeSelection")) { int target; if (rowLead == getLastVisibleRowIndex()) @@ -632,7 +725,7 @@ public class BasicTableUI rowModel.setSelectionInterval(target, target); colModel.setSelectionInterval(colLead, colLead); } - else if (e.getActionCommand().equals("scrollRightExtendSelection")) + else if (command.equals("scrollRightExtendSelection")) { int target; if (colLead == getLastVisibleColumnIndex()) @@ -645,16 +738,16 @@ public class BasicTableUI colModel.setLeadSelectionIndex(target); rowModel.setLeadSelectionIndex(rowLead); } - else if (e.getActionCommand().equals("selectAll")) + else if (command.equals("selectAll")) { table.selectAll(); } - else if (e.getActionCommand().equals("selectLastRowExtendSelection")) + else if (command.equals("selectLastRowExtendSelection")) { rowModel.setLeadSelectionIndex(rowMax); colModel.setLeadSelectionIndex(colLead); } - else if (e.getActionCommand().equals("scrollDownExtendSelection")) + else if (command.equals("scrollDownExtendSelection")) { int target; if (rowLead == getLastVisibleRowIndex()) @@ -666,8 +759,8 @@ public class BasicTableUI rowModel.setLeadSelectionIndex(target); colModel.setLeadSelectionIndex(colLead); - } - else if (e.getActionCommand().equals("scrollUpChangeSelection")) + } + else if (command.equals("scrollUpChangeSelection")) { int target; if (rowLead == getFirstVisibleRowIndex()) @@ -680,22 +773,119 @@ public class BasicTableUI rowModel.setSelectionInterval(target, target); colModel.setSelectionInterval(colLead, colLead); } + else if (command.equals("selectNextRowChangeLead")) + { + if (rowModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) + { + // just "selectNextRow" + rowModel.setSelectionInterval(Math.min(rowLead + 1, rowMax), + Math.min(rowLead + 1, rowMax)); + colModel.setSelectionInterval(colLead,colLead); + } + else + rowModel.moveLeadSelectionIndex(Math.min(rowLead + 1, rowMax)); + } + else if (command.equals("selectPreviousRowChangeLead")) + { + if (rowModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) + { + // just selectPreviousRow + rowModel.setSelectionInterval(Math.max(rowLead - 1, 0), + Math.min(rowLead -1, 0)); + colModel.setSelectionInterval(colLead,colLead); + } + else + rowModel.moveLeadSelectionIndex(Math.max(rowLead - 1, 0)); + } + else if (command.equals("selectNextColumnChangeLead")) + { + if (colModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) + { + // just selectNextColumn + rowModel.setSelectionInterval(rowLead,rowLead); + colModel.setSelectionInterval(Math.min(colLead + 1, colMax), + Math.min(colLead + 1, colMax)); + } + else + colModel.moveLeadSelectionIndex(Math.min(colLead + 1, colMax)); + } + else if (command.equals("selectPreviousColumnChangeLead")) + { + if (colModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) + { + // just selectPreviousColumn + rowModel.setSelectionInterval(rowLead,rowLead); + colModel.setSelectionInterval(Math.max(colLead - 1, 0), + Math.max(colLead - 1, 0)); + + } + else + colModel.moveLeadSelectionIndex(Math.max(colLead - 1, 0)); + } + else if (command.equals("addToSelection")) + { + if (!table.isEditing()) + { + int oldRowAnchor = rowModel.getAnchorSelectionIndex(); + int oldColAnchor = colModel.getAnchorSelectionIndex(); + rowModel.addSelectionInterval(rowLead, rowLead); + colModel.addSelectionInterval(colLead, colLead); + rowModel.setAnchorSelectionIndex(oldRowAnchor); + colModel.setAnchorSelectionIndex(oldColAnchor); + } + } + else if (command.equals("extendTo")) + { + rowModel.setSelectionInterval(rowModel.getAnchorSelectionIndex(), + rowLead); + colModel.setSelectionInterval(colModel.getAnchorSelectionIndex(), + colLead); + } + else if (command.equals("toggleAndAnchor")) + { + if (rowModel.isSelectedIndex(rowLead)) + rowModel.removeSelectionInterval(rowLead, rowLead); + else + rowModel.addSelectionInterval(rowLead, rowLead); + + if (colModel.isSelectedIndex(colLead)) + colModel.removeSelectionInterval(colLead, colLead); + else + colModel.addSelectionInterval(colLead, colLead); + + rowModel.setAnchorSelectionIndex(rowLead); + colModel.setAnchorSelectionIndex(colLead); + } else { // If we're here that means we bound this TableAction class // to a keyboard input but we either want to ignore that input // or we just haven't implemented its action yet. + + // Uncomment the following line to print the names of unused bindings + // when their keys are pressed + + // System.out.println ("not implemented: "+e.getActionCommand()); } - if (table.isEditing() && e.getActionCommand() != "startEditing") - table.editingCanceled(new ChangeEvent("update")); - table.repaint(); - + // Any commands whose keyStrokes should be used by the Editor should not + // cause editing to be stopped: ie, the SPACE sends "addToSelection" but + // if the table is in editing mode, the space should not cause us to stop + // editing because it should be used by the Editor. + if (table.isEditing() && command != "startEditing" + && command != "addToSelection") + table.editingStopped(new ChangeEvent("update")); + table.scrollRectToVisible (table.getCellRect(rowModel.getLeadSelectionIndex(), colModel.getLeadSelectionIndex(), false)); + table.repaint(); } + /** + * Returns the column index of the first visible column. + * @return the column index of the first visible column. + */ int getFirstVisibleColumnIndex() { ComponentOrientation or = table.getComponentOrientation(); @@ -922,10 +1112,19 @@ public class BasicTableUI protected void installListeners() { - table.addFocusListener(focusListener); + if (focusListener == null) + focusListener = createFocusListener(); + table.addFocusListener(focusListener); + if (keyListener == null) + keyListener = createKeyListener(); table.addKeyListener(keyListener); + if (mouseInputListener == null) + mouseInputListener = createMouseInputListener(); table.addMouseListener(mouseInputListener); table.addMouseMotionListener(mouseInputListener); + if (propertyChangeListener == null) + propertyChangeListener = new PropertyChangeHandler(); + table.addPropertyChangeListener(propertyChangeListener); } protected void uninstallDefaults() @@ -950,6 +1149,7 @@ public class BasicTableUI protected void uninstallKeyboardActions() { + // TODO: Implement this properly. } protected void uninstallListeners() @@ -958,13 +1158,13 @@ public class BasicTableUI table.removeKeyListener(keyListener); table.removeMouseListener(mouseInputListener); table.removeMouseMotionListener(mouseInputListener); + table.removePropertyChangeListener(propertyChangeListener); + propertyChangeListener = null; } public void installUI(JComponent comp) { table = (JTable)comp; - focusListener = createFocusListener(); - mouseInputListener = createMouseInputListener(); installDefaults(); installKeyboardActions(); installListeners(); @@ -977,6 +1177,60 @@ public class BasicTableUI uninstallDefaults(); } + /** + * Paints a single cell in the table. + * + * @param g The graphics context to paint in + * @param row The row number to paint + * @param col The column number to paint + * @param bounds The bounds of the cell to paint, assuming a coordinate + * system beginning at <code>(0,0)</code> in the upper left corner of the + * table + * @param rend A cell renderer to paint with + * @param data The data to provide to the cell renderer + * @param rowLead The lead selection for the rows of the table. + * @param colLead The lead selection for the columns of the table. + */ + void paintCell(Graphics g, int row, int col, Rectangle bounds, + TableCellRenderer rend, TableModel data, + int rowLead, int colLead) + { + boolean rowSelAllowed = table.getRowSelectionAllowed(); + boolean colSelAllowed = table.getColumnSelectionAllowed(); + boolean isSel = false; + if (rowSelAllowed && colSelAllowed || !rowSelAllowed && !colSelAllowed) + isSel = table.isCellSelected(row, col); + else + isSel = table.isRowSelected(row) && table.getRowSelectionAllowed() + || table.isColumnSelected(col) && table.getColumnSelectionAllowed(); + + // Determine the focused cell. The focused cell is the cell at the + // leadSelectionIndices of the row and column selection model. + ListSelectionModel rowSel = table.getSelectionModel(); + ListSelectionModel colSel = table.getColumnModel().getSelectionModel(); + boolean hasFocus = table.hasFocus() && table.isEnabled() + && rowSel.getLeadSelectionIndex() == row + && colSel.getLeadSelectionIndex() == col; + + Component comp = rend.getTableCellRendererComponent(table, + data.getValueAt(row, col), + isSel, hasFocus, row, col); + + rendererPane.paintComponent(g, comp, table, bounds); + + // FIXME: this is manual painting of the Caret, why doesn't the + // JTextField take care of this itself? + if (comp instanceof JTextField) + { + Rectangle oldClip = g.getClipBounds(); + g.translate(bounds.x, bounds.y); + g.clipRect(0, 0, bounds.width, bounds.height); + ((JTextField)comp).getCaret().paint(g); + g.translate(-bounds.x, -bounds.y); + g.setClip(oldClip); + } + } + public void paint(Graphics gfx, JComponent ignored) { int ncols = table.getColumnCount(); @@ -1002,40 +1256,24 @@ public class BasicTableUI y = y0; TableColumn col = cols.getColumn(c); int width = col.getWidth(); - int modelCol = col.getModelIndex(); - + int halfGapWidth = gap.width / 2; + int halfGapHeight = gap.height / 2; for (int r = 0; r < nrows && y < ymax; ++r) { - Rectangle bounds = new Rectangle(x, y, width, height); - if (bounds.intersects(clip)) - { - TableCellRenderer rend = table.getCellRenderer(r, c); - Component comp = table.prepareRenderer(rend, r, c); - gfx.translate(x, y); - comp.setBounds(new Rectangle(0, 0, width, height)); - // Set correct border on cell renderer. - // Only the lead selection cell gets a border - if (comp instanceof JComponent) - { - if (table.getSelectionModel().getLeadSelectionIndex() == r - && table.getColumnModel().getSelectionModel(). - getLeadSelectionIndex() == c) - ((JComponent) comp).setBorder(highlightCellBorder); - else - ((JComponent) comp).setBorder(cellBorder); - } - comp.paint(gfx); - if (comp instanceof JTextField) - ((JTextField)comp).getCaret().paint(gfx); - gfx.translate(-x, -y); + Rectangle bounds = new Rectangle(x + halfGapWidth, + y + halfGapHeight + 1, + width - gap.width + 1, + height - gap.height); + if (bounds.intersects(clip)) + { + paintCell(gfx, r, c, bounds, table.getCellRenderer(r, c), + table.getModel(), + table.getSelectionModel().getLeadSelectionIndex(), + table.getColumnModel().getSelectionModel().getLeadSelectionIndex()); } - y += height; - if (gap != null) - y += gap.height; + y += height; } x += width; - if (gap != null) - x += gap.width; } // tighten up the x and y max bounds @@ -1044,7 +1282,7 @@ public class BasicTableUI Color grid = table.getGridColor(); - // paint vertical grid lines + // paint vertical grid lines if (grid != null && table.getShowVerticalLines()) { x = x0; @@ -1053,9 +1291,7 @@ public class BasicTableUI boolean paintedLine = false; for (int c = 0; c < ncols && x < xmax; ++c) { - x += cols.getColumn(c).getWidth();; - if (gap != null) - x += gap.width; + x += cols.getColumn(c).getWidth(); gfx.drawLine(x, y0, x, ymax); paintedLine = true; } @@ -1072,8 +1308,6 @@ public class BasicTableUI for (int r = 0; r < nrows && y < ymax; ++r) { y += height; - if (gap != null) - y += gap.height; gfx.drawLine(x0, y, xmax, y); paintedLine = true; } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextAreaUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextAreaUI.java index 97b0ccb6ee6..36854e07fe0 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTextAreaUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextAreaUI.java @@ -39,11 +39,16 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.beans.PropertyChangeEvent; + import javax.swing.JComponent; +import javax.swing.JTextArea; +import javax.swing.UIDefaults; import javax.swing.plaf.ComponentUI; import javax.swing.text.Element; import javax.swing.text.PlainView; import javax.swing.text.View; +import javax.swing.text.WrappedPlainView; public class BasicTextAreaUI extends BasicTextUI { @@ -54,15 +59,55 @@ public class BasicTextAreaUI extends BasicTextUI public BasicTextAreaUI() { + // Nothing to do here. } + /** + * Create the view. Returns a WrappedPlainView if the text area + * has lineWrap set to true, otherwise returns a PlainView. If + * lineWrap is true has to check whether the wrap style is word + * or character and return an appropriate WrappedPlainView. + * + * @param elem the element to create a View for + * @return an appropriate View for the element + */ public View create(Element elem) { - return new PlainView(elem); + JTextArea comp = (JTextArea)getComponent(); + if (comp.getLineWrap()) + { + if (comp.getWrapStyleWord()) + return new WrappedPlainView(elem, true); + else + return new WrappedPlainView(elem, false); + } + else + return new PlainView(elem); } + /** + * Returns the prefix for entries in the {@link UIDefaults} table. + * + * @return "TextArea" + */ protected String getPropertyPrefix() { return "TextArea"; } + + /** + * Receives notification whenever one of the text component's bound + * properties changes. This changes the view to WrappedPlainView + * if setLineWrap(true) is called, and back to PlainView if + * setLineWrap(false) is called. + * + * @param ev the property change event + */ + protected void propertyChange(PropertyChangeEvent ev) + { + JTextArea comp = (JTextArea)getComponent(); + if (ev.getPropertyName() == "lineWrap" + || ev.getPropertyName() == "wrapStyleWord") + modelChanged(); + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java index a300446c262..4e2ca9f93df 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java @@ -41,6 +41,7 @@ package javax.swing.plaf.basic; import java.beans.PropertyChangeEvent; import javax.swing.JComponent; +import javax.swing.UIDefaults; import javax.swing.plaf.ComponentUI; import javax.swing.text.Element; import javax.swing.text.FieldView; @@ -63,6 +64,11 @@ public class BasicTextFieldUI extends BasicTextUI return new BasicTextFieldUI(); } + /** + * Returns the prefix for entries in the {@link UIDefaults} table. + * + * @return "TextField" + */ protected String getPropertyPrefix() { return "TextField"; @@ -73,8 +79,22 @@ public class BasicTextFieldUI extends BasicTextUI super.installUI(c); } + /** + * Receives notification whenever one of the text component's bound + * properties changes. Here we check for the editable and enabled + * properties and adjust the background color accordingly. + * + * @param event the property change event + */ protected void propertyChange(PropertyChangeEvent event) { - // Does nothing by default. + if (event.getPropertyName().equals("editable")) + { + boolean editable = ((Boolean) event.getNewValue()).booleanValue(); + if (editable) + textComponent.setBackground(background); + else + textComponent.setBackground(inactiveBackground); + } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextPaneUI.java index 55d908e1b88..decbed56829 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTextPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextPaneUI.java @@ -38,10 +38,18 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Color; + import javax.swing.JComponent; +import javax.swing.JTextPane; +import javax.swing.plaf.ColorUIResource; +import javax.swing.UIDefaults; import javax.swing.plaf.ComponentUI; import javax.swing.text.Element; import javax.swing.text.PlainView; +import javax.swing.text.Style; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyleContext; import javax.swing.text.View; public class BasicTextPaneUI extends BasicEditorPaneUI @@ -61,8 +69,32 @@ public class BasicTextPaneUI extends BasicEditorPaneUI return new PlainView(elem); } + /** + * Returns the prefix for entries in the {@link UIDefaults} table. + * + * @return "TextPane" + */ protected String getPropertyPrefix() { return "TextPane"; } + + /** + * Installs this UI on the specified <code>JTextPane</code>. This calls the + * super implementation and then adds a default style to the text pane. + * + * @param c the text pane to install the UI to + */ + public void installUI(JComponent c) + { + super.installUI(c); + JTextPane tp = (JTextPane) c; + Style defaultStyle = tp.getStyle(StyleContext.DEFAULT_STYLE); + defaultStyle.addAttribute(StyleConstants.Foreground, + new ColorUIResource(Color.BLACK)); + defaultStyle.addAttribute(StyleConstants.FontFamily, "Serif"); + defaultStyle.addAttribute(StyleConstants.Italic, Boolean.FALSE); + defaultStyle.addAttribute(StyleConstants.Bold, Boolean.FALSE); + defaultStyle.addAttribute(StyleConstants.FontSize, new Integer(12)); + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java index 91ccb0056bb..b9de92640c8 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; @@ -54,6 +55,8 @@ import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JComponent; +import javax.swing.LookAndFeel; +import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.UIDefaults; import javax.swing.UIManager; @@ -73,7 +76,6 @@ import javax.swing.text.Element; import javax.swing.text.Highlighter; import javax.swing.text.JTextComponent; import javax.swing.text.Keymap; -import javax.swing.text.PlainView; import javax.swing.text.Position; import javax.swing.text.View; import javax.swing.text.ViewFactory; @@ -92,11 +94,11 @@ public abstract class BasicTextUI extends TextUI /** * A {@link DefaultCaret} that implements {@link UIResource}. */ - public static class BasicCaret extends DefaultCaret - implements UIResource + public static class BasicCaret extends DefaultCaret implements UIResource { public BasicCaret() { + // Nothing to do here. } } @@ -108,6 +110,7 @@ public abstract class BasicTextUI extends TextUI { public BasicHighlighter() { + // Nothing to do here. } } @@ -241,7 +244,7 @@ public abstract class BasicTextUI extends TextUI public void paint(Graphics g, Shape s) { if (view != null) - view.paint(g, s); + view.paint(g, s); } @@ -252,10 +255,10 @@ public abstract class BasicTextUI extends TextUI * * This is delegated to the real root view. * - * @param pos the position of the character in the model + * @param position the position of the character in the model * @param a the area that is occupied by the view - * @param bias either {@link Position.Bias.Forward} or - * {@link Position.Bias.Backward} depending on the preferred + * @param bias either {@link Position.Bias#Forward} or + * {@link Position.Bias#Backward} depending on the preferred * direction bias. If <code>null</code> this defaults to * <code>Position.Bias.Forward</code> * @@ -327,12 +330,41 @@ public abstract class BasicTextUI extends TextUI { view.changedUpdate(ev, shape, vf); } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset in + * the document model + */ + public int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException + { + return view.getNextVisualPositionFrom(c, pos, b, d, biasRet); + } } /** * Receives notifications when properties of the text component change. */ - class UpdateHandler implements PropertyChangeListener + class PropertyChangeHandler implements PropertyChangeListener { /** * Notifies when a property of the text component changes. @@ -342,10 +374,12 @@ public abstract class BasicTextUI extends TextUI public void propertyChange(PropertyChangeEvent event) { if (event.getPropertyName().equals("document")) - { + { // Document changed. - modelChanged(); - } + modelChanged(); + } + + BasicTextUI.this.propertyChange(event); } } @@ -364,11 +398,10 @@ public abstract class BasicTextUI extends TextUI */ public void changedUpdate(DocumentEvent ev) { - Dimension size = textComponent.getSize(); - rootView.changedUpdate(ev, new Rectangle(0, 0, size.width, size.height), + rootView.changedUpdate(ev, getVisibleEditorRect(), rootView.getViewFactory()); } - + /** * Notification about a document insert event. * @@ -376,12 +409,8 @@ public abstract class BasicTextUI extends TextUI */ public void insertUpdate(DocumentEvent ev) { - Dimension size = textComponent.getSize(); - rootView.insertUpdate(ev, new Rectangle(0, 0, size.width, size.height), + rootView.insertUpdate(ev, getVisibleEditorRect(), rootView.getViewFactory()); - int caretPos = textComponent.getCaretPosition(); - if (caretPos >= ev.getOffset()) - textComponent.setCaretPosition(caretPos + ev.getLength()); } /** @@ -391,12 +420,8 @@ public abstract class BasicTextUI extends TextUI */ public void removeUpdate(DocumentEvent ev) { - Dimension size = textComponent.getSize(); - rootView.removeUpdate(ev, new Rectangle(0, 0, size.width, size.height), + rootView.removeUpdate(ev, getVisibleEditorRect(), rootView.getViewFactory()); - int caretPos = textComponent.getCaretPosition(); - if (caretPos >= ev.getOffset()) - textComponent.setCaretPosition(ev.getOffset()); } } @@ -419,16 +444,29 @@ public abstract class BasicTextUI extends TextUI /** * Receives notification when the model changes. */ - UpdateHandler updateHandler = new UpdateHandler(); + PropertyChangeHandler updateHandler = new PropertyChangeHandler(); /** The DocumentEvent handler. */ DocumentHandler documentHandler = new DocumentHandler(); /** + * The standard background color. This is the color which is used to paint + * text in enabled text components. + */ + Color background; + + /** + * The inactive background color. This is the color which is used to paint + * text in disabled text components. + */ + Color inactiveBackground; + + /** * Creates a new <code>BasicTextUI</code> instance. */ public BasicTextUI() { + // Nothing to do here. } /** @@ -506,14 +544,20 @@ public abstract class BasicTextUI extends TextUI textComponent.setHighlighter(createHighlighter()); String prefix = getPropertyPrefix(); - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - textComponent.setBackground(defaults.getColor(prefix + ".background")); - textComponent.setForeground(defaults.getColor(prefix + ".foreground")); - textComponent.setMargin(defaults.getInsets(prefix + ".margin")); - textComponent.setBorder(defaults.getBorder(prefix + ".border")); - textComponent.setFont(defaults.getFont(prefix + ".font")); - - caret.setBlinkRate(defaults.getInt(prefix + ".caretBlinkRate")); + LookAndFeel.installColorsAndFont(textComponent, prefix + ".background", + prefix + ".foreground", prefix + ".font"); + LookAndFeel.installBorder(textComponent, prefix + ".border"); + textComponent.setMargin(UIManager.getInsets(prefix + ".margin")); + + caret.setBlinkRate(UIManager.getInt(prefix + ".caretBlinkRate")); + + // Fetch the colors for enabled/disabled text components. + background = UIManager.getColor(prefix + ".background"); + inactiveBackground = UIManager.getColor(prefix + ".inactiveBackground"); + textComponent.setDisabledTextColor + (UIManager.getColor(prefix + ".inactiveForeground")); + textComponent.setSelectedTextColor(UIManager.getColor(prefix + ".selectionForeground")); + textComponent.setSelectionColor(UIManager.getColor(prefix + ".selectionBackground")); } /** @@ -704,6 +748,7 @@ public abstract class BasicTextUI extends TextUI protected void uninstallListeners() { textComponent.removeFocusListener(focuslistener); + textComponent.getDocument().removeDocumentListener(documentHandler); } /** @@ -757,6 +802,18 @@ public abstract class BasicTextUI extends TextUI } /** + * Returns the minimum size for text components. This returns the size + * of the component's insets. + * + * @return the minimum size for text components + */ + public Dimension getMinimumSize(JComponent c) + { + Insets i = c.getInsets(); + return new Dimension(i.left + i.right, i.top + i.bottom); + } + + /** * Paints the text component. * * @param g the <code>Graphics</code> context to paint to @@ -776,10 +833,10 @@ public abstract class BasicTextUI extends TextUI { Caret caret = textComponent.getCaret(); Highlighter highlighter = textComponent.getHighlighter(); - + if (textComponent.isOpaque()) paintBackground(g); - + if (highlighter != null && textComponent.getSelectionStart() != textComponent.getSelectionEnd()) highlighter.paint(g); @@ -797,8 +854,10 @@ public abstract class BasicTextUI extends TextUI */ protected void paintBackground(Graphics g) { - g.setColor(textComponent.getBackground()); - g.fillRect(0, 0, textComponent.getWidth(), textComponent.getHeight()); + // This method does nothing. All the background filling is done by the + // ComponentUI update method. However, the method is called by paint + // to provide a way for subclasses to draw something different (e.g. + // background images etc) on the background. } /** @@ -885,10 +944,10 @@ public abstract class BasicTextUI extends TextUI /** * Maps a position in the document into the coordinate space of the View. * The output rectangle usually reflects the font height but has a width - * of zero. A bias of {@link Position.Bias.Forward} is used in this method. + * of zero. A bias of {@link Position.Bias#Forward} is used in this method. * + * @param t the text component * @param pos the position of the character in the model - * @param a the area that is occupied by the view * * @return a rectangle that gives the location of the document position * inside the view coordinate space @@ -908,10 +967,10 @@ public abstract class BasicTextUI extends TextUI * The output rectangle usually reflects the font height but has a width * of zero. * + * @param t the text component * @param pos the position of the character in the model - * @param a the area that is occupied by the view - * @param bias either {@link Position.Bias.Forward} or - * {@link Position.Bias.Backward} depending on the preferred + * @param bias either {@link Position.Bias#Forward} or + * {@link Position.Bias#Backward} depending on the preferred * direction bias. If <code>null</code> this defaults to * <code>Position.Bias.Forward</code> * @@ -957,7 +1016,7 @@ public abstract class BasicTextUI extends TextUI */ public int viewToModel(JTextComponent t, Point pt, Position.Bias[] biasReturn) { - return 0; // FIXME: Implement me. + return rootView.viewToModel(pt.x, pt.y, getVisibleEditorRect(), biasReturn); } /** @@ -999,16 +1058,17 @@ public abstract class BasicTextUI extends TextUI */ protected Rectangle getVisibleEditorRect() { + JTextComponent textComponent = getComponent(); int width = textComponent.getWidth(); int height = textComponent.getHeight(); if (width <= 0 || height <= 0) - return null; + return new Rectangle(0, 0, 0, 0); Insets insets = textComponent.getInsets(); return new Rectangle(insets.left, insets.top, - width - insets.left + insets.right, - height - insets.top + insets.bottom); + width - insets.left - insets.right, + height - insets.top - insets.bottom); } /** @@ -1020,6 +1080,8 @@ public abstract class BasicTextUI extends TextUI { rootView.setView(view); view.setParent(rootView); + textComponent.revalidate(); + textComponent.repaint(); } /** @@ -1043,4 +1105,17 @@ public abstract class BasicTextUI extends TextUI View view = factory.create(elem); setView(view); } + + /** + * Receives notification whenever one of the text component's bound + * properties changes. This default implementation does nothing. + * It is a hook that enables subclasses to react to property changes + * on the text component. + * + * @param ev the property change event + */ + protected void propertyChange(PropertyChangeEvent ev) + { + // The default implementation does nothing. + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToggleButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToggleButtonUI.java index 9106b0b6673..896ea0c89dc 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicToggleButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicToggleButtonUI.java @@ -38,7 +38,13 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Rectangle; + +import javax.swing.AbstractButton; import javax.swing.JComponent; +import javax.swing.SwingUtilities; import javax.swing.plaf.ComponentUI; public class BasicToggleButtonUI extends BasicButtonUI @@ -58,5 +64,62 @@ public class BasicToggleButtonUI extends BasicButtonUI { return "ToggleButton."; } -} + /** + * Paint the component, which is an {@link AbstractButton}, according to + * its current state. + * + * @param g The graphics context to paint with + * @param c The component to paint the state of + */ + public void paint(Graphics g, JComponent c) + { + AbstractButton b = (AbstractButton) c; + + Rectangle tr = new Rectangle(); + Rectangle ir = new Rectangle(); + Rectangle vr = new Rectangle(); + + Font f = c.getFont(); + + g.setFont(f); + + if (b.isBorderPainted()) + SwingUtilities.calculateInnerArea(b, vr); + else + vr = SwingUtilities.getLocalBounds(b); + String text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f), + b.getText(), + currentIcon(b), + b.getVerticalAlignment(), + b.getHorizontalAlignment(), + b.getVerticalTextPosition(), + b.getHorizontalTextPosition(), + vr, ir, tr, + b.getIconTextGap() + + defaultTextShiftOffset); + + if ((b.getModel().isArmed() && b.getModel().isPressed()) + || b.isSelected()) + paintButtonPressed(g, b); + + paintIcon(g, b, ir); + if (text != null) + paintText(g, b, tr, text); + if (b.isFocusOwner() && b.isFocusPainted()) + paintFocus(g, b, vr, tr, ir); + } + + /** + * Paints the icon for the toggle button. This delegates to + * {@link BasicButtonUI#paintIcon(Graphics, JComponent, Rectangle)}. + * + * @param g the graphics context + * @param b the button to paint the icon for + * @param iconRect the area allocated for the icon + */ + protected void paintIcon(Graphics g, AbstractButton b, Rectangle iconRect) + { + super.paintIcon(g, b, iconRect); + } +} diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarSeparatorUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarSeparatorUI.java index db29fdca583..79cf0b0c213 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarSeparatorUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarSeparatorUI.java @@ -43,7 +43,6 @@ import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.JSeparator; -import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; @@ -73,9 +72,7 @@ public class BasicToolBarSeparatorUI extends BasicSeparatorUI */ protected void installDefaults(JSeparator s) { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - size = defaults.getDimension("ToolBar.separatorSize"); + size = UIManager.getDimension("ToolBar.separatorSize"); } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java index 8be89efcfa6..ef4ed835f86 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java @@ -65,10 +65,11 @@ import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JToolBar; +import javax.swing.KeyStroke; +import javax.swing.LookAndFeel; import javax.swing.RootPaneContainer; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.event.MouseInputListener; @@ -133,6 +134,26 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants protected FocusListener toolBarFocusListener; /** + * @deprecated since JDK1.3. + */ + protected KeyStroke leftKey; + + /** + * @deprecated since JDK1.3. + */ + protected KeyStroke rightKey; + + /** + * @deprecated since JDK1.3. + */ + protected KeyStroke upKey; + + /** + * @deprecated since JDK1.3. + */ + protected KeyStroke downKey; + + /** * The floating window that is responsible for holding the JToolBar when it * is dragged outside of its original parent. */ @@ -566,18 +587,16 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants */ protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + LookAndFeel.installBorder(toolBar, "ToolBar.border"); + LookAndFeel.installColorsAndFont(toolBar, "ToolBar.background", + "ToolBar.foreground", "ToolBar.font"); - toolBar.setBorder(new ToolBarBorder()); - toolBar.setBackground(defaults.getColor("ToolBar.background")); - toolBar.setForeground(defaults.getColor("ToolBar.foreground")); - toolBar.setFont(defaults.getFont("ToolBar.font")); + dockingBorderColor = UIManager.getColor("ToolBar.dockingForeground"); + dockingColor = UIManager.getColor("ToolBar.dockingBackground"); - dockingBorderColor = defaults.getColor("ToolBar.dockingForeground"); - dockingColor = defaults.getColor("ToolBar.dockingBackground"); - - floatingBorderColor = defaults.getColor("ToolBar.floatingForeground"); - floatingColor = defaults.getColor("ToolBar.floatingBackground"); + floatingBorderColor = UIManager.getColor("ToolBar.floatingForeground"); + floatingColor = UIManager.getColor("ToolBar.floatingBackground"); + setRolloverBorders(toolBar.isRollover()); } /** @@ -591,10 +610,8 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants /** * This method installs listeners for the JToolBar. - * - * @param toolbar The JToolBar to register listeners for. */ - protected void installListeners(JToolBar toolbar) + protected void installListeners() { dockingListener = createDockingListener(); toolBar.addMouseListener(dockingListener); @@ -694,7 +711,7 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants toolBar.setOpaque(true); installDefaults(); installComponents(); - installListeners(toolBar); + installListeners(); installKeyboardActions(); } } @@ -1000,6 +1017,7 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants */ public void mouseMoved(MouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -1030,13 +1048,15 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants } origin = new Point(0, 0); - SwingUtilities.convertPointToScreen(ssd, toolBar); + if (toolBar.isShowing()) + SwingUtilities.convertPointToScreen(ssd, toolBar); if (! (SwingUtilities.getAncestorOfClass(Window.class, toolBar) instanceof UIResource)) // Need to know who keeps the toolBar if it gets dragged back into it. origParent = toolBar.getParent(); - - SwingUtilities.convertPointToScreen(origin, toolBar); + + if (toolBar.isShowing()) + SwingUtilities.convertPointToScreen(origin, toolBar); isDragging = true; diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java index b7a08aa728e..5cec2e33365 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java @@ -1,5 +1,5 @@ /* BasicToolTipUI.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,20 +39,18 @@ exception statement from your version. */ package javax.swing.plaf.basic; import java.awt.Color; -import java.awt.Component; import java.awt.Dimension; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; import java.awt.Rectangle; +import java.awt.Toolkit; import javax.swing.JComponent; import javax.swing.JToolTip; +import javax.swing.LookAndFeel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; -import javax.swing.UIManager; -import javax.swing.border.Border; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ToolTipUI; @@ -61,58 +59,12 @@ import javax.swing.plaf.ToolTipUI; */ public class BasicToolTipUI extends ToolTipUI { - /** The default Border around the JToolTip. */ - private static Border defaultBorder = new Border() - { - // FIXME: This needs to go into Basic Look and Feel - // defaults. - /** - * This method returns the border insets. - * - * @param c The Component to find Border insets for. - * - * @return The Border insets. - */ - public Insets getBorderInsets(Component c) - { - return new Insets(4, 4, 4, 4); - } + /** The shared instance of BasicToolTipUI used for all ToolTips. */ + private static BasicToolTipUI shared; - /** - * This method returns whether the border is opaque. - * - * @return Whether the border is opaque. - */ - public boolean isBorderOpaque() - { - return false; - } - - /** - * This method paints the border. - * - * @param c The Component to paint this border around. - * @param g The Graphics object to paint with. - * @param x The x coordinate to start painting at. - * @param y The y coordinate to start painting at. - * @param w The width of the Component. - * @param h The height of the Component. - */ - public void paintBorder(Component c, Graphics g, int x, int y, int w, - int h) - { - Color saved = g.getColor(); - g.setColor(Color.BLACK); - - g.drawRect(0, 0, w - 1, h - 1); - - g.setColor(saved); - } - }; - - /** The shared instance of BasicToolTipUI used for all ToolTips. */ - private static BasicToolTipUI shared; + /** The tooltip's text */ + private String text; /** * Creates a new BasicToolTipUI object. @@ -124,7 +76,7 @@ public class BasicToolTipUI extends ToolTipUI /** * This method creates a new BasicToolTip UI for the given - * JComponent. + * JComponent. * * @param c The JComponent to create a UI for. * @@ -132,9 +84,9 @@ public class BasicToolTipUI extends ToolTipUI */ public static ComponentUI createUI(JComponent c) { - if (shared == null) - shared = new BasicToolTipUI(); - return shared; + if (shared == null) + shared = new BasicToolTipUI(); + return shared; } /** @@ -171,12 +123,16 @@ public class BasicToolTipUI extends ToolTipUI public Dimension getPreferredSize(JComponent c) { JToolTip tip = (JToolTip) c; + FontMetrics fm; + Toolkit g = tip.getToolkit(); + text = tip.getTipText(); + Rectangle vr = new Rectangle(); Rectangle ir = new Rectangle(); Rectangle tr = new Rectangle(); Insets insets = tip.getInsets(); - FontMetrics fm = tip.getToolkit().getFontMetrics(tip.getFont()); - SwingUtilities.layoutCompoundLabel(tip, fm, tip.getTipText(), null, + fm = g.getFontMetrics(tip.getFont()); + SwingUtilities.layoutCompoundLabel(tip, fm, text, null, SwingConstants.CENTER, SwingConstants.CENTER, SwingConstants.CENTER, @@ -192,11 +148,9 @@ public class BasicToolTipUI extends ToolTipUI */ protected void installDefaults(JComponent c) { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - c.setBackground(defaults.getColor("ToolTip.background")); - c.setForeground(defaults.getColor("ToolTip.foreground")); - c.setFont(defaults.getFont("ToolTip.font")); - c.setBorder(defaultBorder); + LookAndFeel.installColorsAndFont(c, "ToolTip.background", + "ToolTip.foreground", "ToolTip.font"); + LookAndFeel.installBorder(c, "ToolTip.border"); } /** @@ -206,6 +160,7 @@ public class BasicToolTipUI extends ToolTipUI */ protected void installListeners(JComponent c) { + // TODO: Implement this properly. } /** @@ -231,6 +186,7 @@ public class BasicToolTipUI extends ToolTipUI JToolTip tip = (JToolTip) c; String text = tip.getTipText(); + Toolkit t = tip.getToolkit(); if (text == null) return; @@ -238,19 +194,19 @@ public class BasicToolTipUI extends ToolTipUI vr = SwingUtilities.calculateInnerArea(tip, vr); Rectangle ir = new Rectangle(); Rectangle tr = new Rectangle(); - FontMetrics fm = tip.getToolkit().getFontMetrics(tip.getFont()); - SwingUtilities.layoutCompoundLabel(tip, fm, tip.getTipText(), null, + FontMetrics fm = t.getFontMetrics(tip.getFont()); + int ascent = fm.getAscent(); + SwingUtilities.layoutCompoundLabel(tip, fm, text, null, SwingConstants.CENTER, SwingConstants.CENTER, SwingConstants.CENTER, SwingConstants.CENTER, vr, ir, tr, 0); - Color saved = g.getColor(); g.setColor(Color.BLACK); - g.drawString(text, vr.x, vr.y + fm.getAscent()); + g.drawString(text, vr.x, vr.y + ascent); - g.setColor(saved); + g.setColor(saved); } /** @@ -273,6 +229,7 @@ public class BasicToolTipUI extends ToolTipUI */ protected void uninstallListeners(JComponent c) { + // TODO: Implement this properly. } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java index 6f714a39cb2..e967cd424fc 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java @@ -44,6 +44,7 @@ import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; +import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; @@ -62,6 +63,7 @@ import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.Enumeration; import java.util.Hashtable; import javax.swing.AbstractAction; @@ -76,6 +78,7 @@ import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.KeyStroke; +import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.UIDefaults; @@ -89,6 +92,7 @@ import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; +import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.InputMapUIResource; import javax.swing.plaf.TreeUI; @@ -96,7 +100,6 @@ import javax.swing.text.Caret; import javax.swing.tree.AbstractLayoutCache; import javax.swing.tree.DefaultTreeCellEditor; import javax.swing.tree.DefaultTreeCellRenderer; -import javax.swing.tree.ExpandVetoException; import javax.swing.tree.FixedHeightLayoutCache; import javax.swing.tree.TreeCellEditor; import javax.swing.tree.TreeCellRenderer; @@ -110,11 +113,11 @@ import javax.swing.tree.TreeSelectionModel; * the Basic look and feel. * * @see javax.swing.JTree - * @author Sascha Brawer (brawer@dandelis.ch) + * * @author Lillian Angel (langel@redhat.com) + * @author Sascha Brawer (brawer@dandelis.ch) */ -public class BasicTreeUI - extends TreeUI +public class BasicTreeUI extends TreeUI { /** Collapse Icon for the tree. */ protected transient Icon collapsedIcon; @@ -136,9 +139,6 @@ public class BasicTreeUI */ protected int totalChildIndent; - /** Minimum preferred size. */ - protected Dimension preferredMinsize; - /** Index of the row that was last selected. */ protected int lastSelectedRow; @@ -174,6 +174,9 @@ public class BasicTreeUI /** Size needed to completely display all the nodes. */ protected Dimension preferredSize; + + /** Minimum size needed to completely display all the nodes. */ + protected Dimension preferredMinSize; /** Is the preferredSize valid? */ protected boolean validCachedPreferredSize; @@ -223,38 +226,38 @@ public class BasicTreeUI /** Set to true if the editor has a different size than the renderer. */ protected boolean editorHasDifferentSize; - + /** The action listener for the editor's Timer. */ - private Timer editorTimer = new EditorUpdateTimer(); + Timer editorTimer = new EditorUpdateTimer(); /** The new value of the node after editing. */ - private Object newVal; + Object newVal; /** The action bound to KeyStrokes. */ - private TreeAction action; + TreeAction action; /** Boolean to keep track of editing. */ - private boolean isEditing; + boolean isEditing; + + /** The bounds of the current cell. */ + Rectangle bounds; + + /** The current path of the visible nodes in the tree. */ + TreePath currentVisiblePath; + + /** The gap between the icon and text. */ + int gap = 4; /** Listeners */ private PropertyChangeListener propertyChangeListener; - private FocusListener focusListener; - private TreeSelectionListener treeSelectionListener; - - private MouseInputListener mouseInputListener; - + private MouseListener mouseListener; private KeyListener keyListener; - private PropertyChangeListener selectionModelPropertyChangeListener; - private ComponentListener componentListener; - - private CellEditorListener cellEditorListener; - + CellEditorListener cellEditorListener; private TreeExpansionListener treeExpansionListener; - private TreeModelListener treeModelListener; /** @@ -262,6 +265,7 @@ public class BasicTreeUI */ public BasicTreeUI() { + validCachedPreferredSize = false; drawingCache = new Hashtable(); nodeDimensions = createNodeDimensions(); configureLayoutCache(); @@ -269,7 +273,7 @@ public class BasicTreeUI propertyChangeListener = createPropertyChangeListener(); focusListener = createFocusListener(); treeSelectionListener = createTreeSelectionListener(); - mouseInputListener = new MouseInputHandler(null, null, null); + mouseListener = createMouseListener(); keyListener = createKeyListener(); selectionModelPropertyChangeListener = createSelectionModelPropertyChangeListener(); componentListener = createComponentListener(); @@ -331,7 +335,7 @@ public class BasicTreeUI * * @return the indent value for the left child. */ - public int getLeftChildIndent(int newAmount) + public int getLeftChildIndent() { return leftChildIndent; } @@ -456,7 +460,6 @@ public class BasicTreeUI protected void setCellRenderer(TreeCellRenderer tcr) { currentCellRenderer = tcr; - tree.setCellRenderer(tcr); updateRenderer(); } @@ -625,14 +628,13 @@ public class BasicTreeUI { Object cell = path.getLastPathComponent(); - TreeModel mod = tree.getModel(); - if (mod != null) + if (treeModel != null) { - Object root = mod.getRoot(); + Object root = treeModel.getRoot(); + if (!tree.isRootVisible() && tree.isExpanded(new TreePath(root))) root = getNextNode(root); - - Point loc = getCellLocation(0, 0, tree, mod, cell, root); + Point loc = getCellLocation(0, 0, tree, treeModel, cell, root); return getCellBounds(loc.x, loc.y, cell); } } @@ -650,21 +652,11 @@ public class BasicTreeUI */ public TreePath getPathForRow(JTree tree, int row) { - TreeModel mod = tree.getModel(); - if (mod != null) + if (treeModel != null && currentVisiblePath != null) { - Object node = mod.getRoot(); - if (!tree.isRootVisible() - && tree.isExpanded(new TreePath(getPathToRoot(node, 0)))) - node = getNextNode(node); - - for (int i = 0; i < row; i++) - node = getNextVisibleNode(node); - - if (node == null) - return null; - - return new TreePath(getPathToRoot(node, 0)); + Object[] nodes = currentVisiblePath.getPath(); + if (row < nodes.length) + return new TreePath(getPathToRoot(nodes[row], 0)); } return null; } @@ -683,17 +675,20 @@ public class BasicTreeUI */ public int getRowForPath(JTree tree, TreePath path) { - int row = path.getPathCount(); - if (tree.isVisible(path)) - return row; - - path = path.getParentPath(); - while (row > 0 && !tree.isVisible(path)) + int row = 0; + Object dest = path.getLastPathComponent(); + int rowCount = getRowCount(tree); + if (currentVisiblePath != null) { - path = path.getParentPath(); - row--; + Object[] nodes = currentVisiblePath.getPath(); + while (row < rowCount) + { + if (dest.equals(nodes[row])) + return row; + row++; + } } - return row; + return -1; } /** @@ -705,22 +700,10 @@ public class BasicTreeUI */ public int getRowCount(JTree tree) { - TreeModel mod = tree.getModel(); - int count = 0; - if (mod != null) - { - Object node = mod.getRoot(); - if (!tree.isRootVisible() - && tree.isExpanded(new TreePath((getPathToRoot(node, 0))))) - node = getNextNode(node); - - while (node != null) - { - count++; - node = getNextVisibleNode(node); - } - } - return count; + updateCurrentVisiblePath(); + if (currentVisiblePath != null) + return currentVisiblePath.getPathCount(); + return 0; } /** @@ -739,9 +722,6 @@ public class BasicTreeUI */ public TreePath getClosestPathForLocation(JTree tree, int x, int y) { - // FIXME: what if root is hidden? should not depend on (0,0) - // should start counting rows from where root is. - int row = Math.round(y / getRowHeight()); TreePath path = getPathForRow(tree, row); @@ -828,7 +808,7 @@ public class BasicTreeUI */ protected void prepareForUIInstall() { - // FIXME: not implemented + // TODO: Implement this properly. } /** @@ -837,7 +817,7 @@ public class BasicTreeUI */ protected void completeUIInstall() { - // FIXME: not implemented + // TODO: Implement this properly. } /** @@ -846,7 +826,7 @@ public class BasicTreeUI */ protected void completeUIUninstall() { - // FIXME: not implemented + // TODO: Implement this properly. } /** @@ -854,7 +834,10 @@ public class BasicTreeUI */ protected void installComponents() { - // FIXME: not implemented + currentCellRenderer = createDefaultCellRenderer(); + rendererPane = createCellRendererPane(); + createdRenderer = true; + setCellRenderer(currentCellRenderer); } /** @@ -865,8 +848,7 @@ public class BasicTreeUI */ protected AbstractLayoutCache.NodeDimensions createNodeDimensions() { - // FIXME: not implemented - return null; + return new NodeDimensionsHandler(); } /** @@ -1035,7 +1017,7 @@ public class BasicTreeUI tree.removePropertyChangeListener(propertyChangeListener); tree.removeFocusListener(focusListener); tree.removeTreeSelectionListener(treeSelectionListener); - tree.removeMouseListener(mouseInputListener); + tree.removeMouseListener(mouseListener); tree.removeKeyListener(keyListener); tree.removePropertyChangeListener(selectionModelPropertyChangeListener); tree.removeComponentListener(componentListener); @@ -1044,9 +1026,8 @@ public class BasicTreeUI TreeCellEditor tce = tree.getCellEditor(); if (tce != null) tce.removeCellEditorListener(cellEditorListener); - TreeModel tm = tree.getModel(); - if (tm != null) - tm.removeTreeModelListener(treeModelListener); + if (treeModel != null) + treeModel.removeTreeModelListener(treeModelListener); } /** @@ -1054,6 +1035,7 @@ public class BasicTreeUI */ protected void uninstallKeyboardActions() { + // TODO: Implement this properly. } /** @@ -1061,7 +1043,10 @@ public class BasicTreeUI */ protected void uninstallComponents() { - // FIXME: not implemented + currentCellRenderer = null; + rendererPane = null; + createdRenderer = false; + setCellRenderer(currentCellRenderer); } /** @@ -1072,8 +1057,7 @@ public class BasicTreeUI */ protected int getVerticalLegBuffer() { - // FIXME: not implemented - return 0; + return getRowHeight() / 2; } /** @@ -1085,17 +1069,17 @@ public class BasicTreeUI */ protected int getHorizontalLegBuffer() { - // FIXME: not implemented - return 0; + return rightChildIndent / 2; } /** * Make all the nodes that are expanded in JTree expanded in LayoutCache. This - * invokes update ExpandedDescendants with the root path. + * invokes updateExpandedDescendants with the root path. */ protected void updateLayoutCacheExpandedNodes() { - // FIXME: not implemented + if (treeModel != null) + updateExpandedDescendants(new TreePath(treeModel.getRoot())); } /** @@ -1108,7 +1092,9 @@ public class BasicTreeUI */ protected void updateExpandedDescendants(TreePath path) { - // FIXME: not implemented + Enumeration expanded = tree.getExpandedDescendants(path); + while (expanded.hasMoreElements()) + treeState.setExpandedState(((TreePath) expanded.nextElement()), true); } /** @@ -1128,7 +1114,7 @@ public class BasicTreeUI */ protected void updateDepthOffset() { - // FIXME: not implemented + depthOffset += getVerticalLegBuffer(); } /** @@ -1148,7 +1134,15 @@ public class BasicTreeUI */ protected void updateRenderer() { - // FIXME: not implemented + if (tree != null) + { + if(tree.getCellRenderer() == null) + { + if(currentCellRenderer == null) + currentCellRenderer = createDefaultCellRenderer(); + tree.setCellRenderer(currentCellRenderer); + } + } } /** @@ -1166,19 +1160,36 @@ public class BasicTreeUI */ protected void updateSize() { - // FIXME: not implemented + preferredSize = null; + updateCachedPreferredSize(); + tree.treeDidChange(); } /** * Updates the <code>preferredSize</code> instance variable, which is - * returned from <code>getPreferredSize()</code>. For left to right - * orientations, the size is determined from the current AbstractLayoutCache. - * For RTL orientations, the preferred size becomes the width minus the - * minimum x position. + * returned from <code>getPreferredSize()</code>. */ protected void updateCachedPreferredSize() { - // FIXME: not implemented + int maxWidth = 0; + boolean isLeaf = false; + if (currentVisiblePath != null) + { + Object[] path = currentVisiblePath.getPath(); + for (int i = 0; i < path.length; i++) + { + TreePath curr = new TreePath(getPathToRoot(path[i], 0)); + Rectangle bounds = getPathBounds(tree, curr); + if (treeModel != null) + isLeaf = treeModel.isLeaf(path[i]); + if (!isLeaf && hasControlIcons()) + bounds.width += getCurrentControlIcon(curr).getIconWidth(); + maxWidth = Math.max(maxWidth, bounds.x + bounds.width); + } + preferredSize = new Dimension(maxWidth, (getRowHeight() * path.length)); + } + else preferredSize = new Dimension(0, 0); + validCachedPreferredSize = true; } /** @@ -1189,7 +1200,9 @@ public class BasicTreeUI */ protected void pathWasExpanded(TreePath path) { - // FIXME: not implemented + validCachedPreferredSize = false; + tree.revalidate(); + tree.repaint(); } /** @@ -1197,28 +1210,28 @@ public class BasicTreeUI */ protected void pathWasCollapsed(TreePath path) { - // FIXME: not implemented + validCachedPreferredSize = false; + tree.revalidate(); + tree.repaint(); } /** * Install all defaults for the tree. - * - * @param tree - * is the JTree to install defaults for */ - protected void installDefaults(JTree tree) + protected void installDefaults() { - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - - tree.setFont(defaults.getFont("Tree.font")); - tree.setForeground(defaults.getColor("Tree.foreground")); - tree.setBackground(defaults.getColor("Tree.background")); + LookAndFeel.installColorsAndFont(tree, "Tree.background", + "Tree.foreground", "Tree.font"); tree.setOpaque(true); - rightChildIndent = defaults.getInt("Tree.rightChildIndent"); - leftChildIndent = defaults.getInt("Tree.leftChildIndent"); - setRowHeight(defaults.getInt("Tree.rowHeight")); + rightChildIndent = UIManager.getInt("Tree.rightChildIndent"); + leftChildIndent = UIManager.getInt("Tree.leftChildIndent"); + setRowHeight(UIManager.getInt("Tree.rowHeight")); + tree.setRowHeight(UIManager.getInt("Tree.rowHeight")); tree.requestFocusInWindow(false); + tree.setScrollsOnExpand(UIManager.getBoolean("Tree.scrollsOnExpand")); + setExpandedIcon(UIManager.getIcon("Tree.expandedIcon")); + setCollapsedIcon(UIManager.getIcon("Tree.collapsedIcon")); } /** @@ -1229,7 +1242,7 @@ public class BasicTreeUI UIDefaults defaults = UIManager.getLookAndFeelDefaults(); InputMap focusInputMap = (InputMap) defaults.get("Tree.focusInputMap"); InputMapUIResource parentInputMap = new InputMapUIResource(); - ActionMap parentActionMap = new ActionMap(); + ActionMap parentActionMap = new ActionMapUIResource(); action = new TreeAction(); Object keys[] = focusInputMap.allKeys(); @@ -1308,7 +1321,7 @@ public class BasicTreeUI tree.addPropertyChangeListener(propertyChangeListener); tree.addFocusListener(focusListener); tree.addTreeSelectionListener(treeSelectionListener); - tree.addMouseListener(mouseInputListener); + tree.addMouseListener(mouseListener); tree.addKeyListener(keyListener); tree.addPropertyChangeListener(selectionModelPropertyChangeListener); tree.addComponentListener(componentListener); @@ -1325,37 +1338,36 @@ public class BasicTreeUI */ public void installUI(JComponent c) { - super.installUI(c); - installDefaults((JTree) c); tree = (JTree) c; + prepareForUIInstall(); + super.installUI(c); + installDefaults(); - currentCellRenderer = createDefaultCellRenderer(); - rendererPane = createCellRendererPane(); - createdRenderer = true; - + installComponents(); + installKeyboardActions(); + installListeners(); + setCellEditor(createDefaultCellEditor()); createdCellEditor = true; isEditing = false; - + TreeModel mod = tree.getModel(); setModel(mod); - tree.setRootVisible(true); if (mod != null) - tree.expandPath(new TreePath(mod.getRoot())); + { + TreePath path = new TreePath(mod.getRoot()); + if (!tree.isExpanded(path)) + toggleExpandState(path); + } treeSelectionModel = tree.getSelectionModel(); - installKeyboardActions(); - installListeners(); completeUIInstall(); } /** * Uninstall the defaults for the tree - * - * @param tree - * to uninstall defaults for */ - protected void uninstallDefaults(JTree tree) + protected void uninstallDefaults() { tree.setFont(null); tree.setForeground(null); @@ -1370,10 +1382,12 @@ public class BasicTreeUI */ public void uninstallUI(JComponent c) { - uninstallDefaults((JTree) c); + prepareForUIUninstall(); + uninstallDefaults(); uninstallKeyboardActions(); uninstallListeners(); tree = null; + uninstallComponents(); completeUIUninstall(); } @@ -1393,20 +1407,16 @@ public class BasicTreeUI public void paint(Graphics g, JComponent c) { JTree tree = (JTree) c; - - TreeModel mod = tree.getModel(); - - if (mod != null) + if (currentVisiblePath == null) + updateCurrentVisiblePath(); + + if (currentVisiblePath != null && treeModel != null) { - Object root = mod.getRoot(); - - if (!tree.isRootVisible()) - tree.expandPath(new TreePath(root)); - - paintRecursive(g, 0, 0, 0, 0, tree, mod, root); - + Object root = treeModel.getRoot(); + paintRecursive(g, 0, 0, 0, tree, treeModel, root); + if (hasControlIcons()) - paintControlIcons(g, 0, 0, 0, 0, tree, mod, root); + paintControlIcons(g, 0, 0, 0, tree, treeModel, root); } } @@ -1420,7 +1430,19 @@ public class BasicTreeUI */ protected void ensureRowsAreVisible(int beginRow, int endRow) { - // FIXME: not implemented + if (beginRow < endRow) + { + int temp = endRow; + endRow = beginRow; + beginRow = temp; + } + + for (int i = beginRow; i < endRow; i++) + { + TreePath path = getPathForRow(tree, i); + if (!tree.isVisible(path)) + tree.makeVisible(path); + } } /** @@ -1431,7 +1453,7 @@ public class BasicTreeUI */ public void setPreferredMinSize(Dimension newSize) { - // FIXME: not implemented + preferredMinSize = newSize; } /** @@ -1441,8 +1463,7 @@ public class BasicTreeUI */ public Dimension getPreferredMinSize() { - // FIXME: not implemented - return null; + return preferredMinSize; } /** @@ -1472,28 +1493,10 @@ public class BasicTreeUI */ public Dimension getPreferredSize(JComponent c, boolean checkConsistancy) { - // FIXME: checkConsistancy not implemented, c not used - TreeModel model = tree.getModel(); - int maxWidth = 0; - int count = 0; - if (model != null) - { - Object node = model.getRoot(); - if (node != null) - { - maxWidth = (int) (getCellBounds(0, 0, node).getWidth()); - while (node != null) - { - count++; - Object nextNode = getNextVisibleNode(node); - if (nextNode != null) - maxWidth = Math.max(maxWidth, - (int) (getCellBounds(0, 0, nextNode).getWidth())); - node = nextNode; - } - } - } - return new Dimension(maxWidth, (getRowHeight() * count)); + // FIXME: checkConsistancy not implemented, c not used + if(!validCachedPreferredSize) + updateCachedPreferredSize(); + return preferredSize; } /** @@ -1506,8 +1509,10 @@ public class BasicTreeUI */ public Dimension getMinimumSize(JComponent c) { - // FIXME: not implemented - return getPreferredSize(c); + Dimension min = getPreferredMinSize(); + if (min == null) + return new Dimension(); + return min; } /** @@ -1520,8 +1525,9 @@ public class BasicTreeUI */ public Dimension getMaximumSize(JComponent c) { - // FIXME: not implemented - return getPreferredSize(c); + if (c instanceof JTree) + return ((JTree) c).getPreferredSize(); + return new Dimension(); } /** @@ -1565,7 +1571,7 @@ public class BasicTreeUI } if (messageTree) - tree.getModel().valueForPathChanged(tree.getLeadSelectionPath(), newVal); + treeModel.valueForPathChanged(tree.getLeadSelectionPath(), newVal); } /** @@ -1584,7 +1590,7 @@ public class BasicTreeUI int y; if (event == null) { - Rectangle bounds = getPathBounds(tree, path); + bounds = getPathBounds(tree, path); x = bounds.x; y = bounds.y; } @@ -1600,6 +1606,7 @@ public class BasicTreeUI { editingPath = path; editingRow = tree.getRowForPath(editingPath); + Object val = editingPath.getLastPathComponent(); cellEditor.addCellEditorListener(cellEditorListener); stopEditingInCompleteEditing = false; @@ -1613,6 +1620,8 @@ public class BasicTreeUI editingComponent.getParent().validate(); tree.add(editingComponent.getParent()); editingComponent.getParent().validate(); + validCachedPreferredSize = false; + tree.revalidate(); ((JTextField) editingComponent).requestFocusInWindow(false); editorTimer.start(); return true; @@ -1634,7 +1643,8 @@ public class BasicTreeUI protected void checkForClickInExpandControl(TreePath path, int mouseX, int mouseY) { - // FIXME: not implemented + if (isLocationInExpandControl(path, mouseX, mouseY)) + toggleExpandState(path); } /** @@ -1655,8 +1665,18 @@ public class BasicTreeUI protected boolean isLocationInExpandControl(TreePath path, int mouseX, int mouseY) { - // FIXME: not implemented - return false; + boolean cntlClick = false; + int row = getRowForPath(tree, path); + + if (!isLeaf(row)) + { + bounds = getPathBounds(tree, path); + + if (hasControlIcons() && (mouseX < bounds.x) + && (mouseX > (bounds.x - getCurrentControlIcon(path).getIconWidth() - gap))) + cntlClick = true; + } + return cntlClick; } /** @@ -1672,7 +1692,7 @@ public class BasicTreeUI */ protected void handleExpandControlClick(TreePath path, int mouseX, int mouseY) { - // FIXME: not implemented + toggleExpandState(path); } /** @@ -1686,7 +1706,11 @@ public class BasicTreeUI */ protected void toggleExpandState(TreePath path) { - // FIXME: not implemented + if (tree.isExpanded(path)) + tree.collapsePath(path); + else + tree.expandPath(path); + updateCurrentVisiblePath(); } /** @@ -1700,8 +1724,8 @@ public class BasicTreeUI */ protected boolean isToggleSelectionEvent(MouseEvent event) { - // FIXME: not implemented - return false; + return (tree.getSelectionModel().getSelectionMode() == + TreeSelectionModel.SINGLE_TREE_SELECTION); } /** @@ -1715,8 +1739,8 @@ public class BasicTreeUI */ protected boolean isMultiSelectEvent(MouseEvent event) { - // FIXME: not implemented - return false; + return (tree.getSelectionModel().getSelectionMode() == + TreeSelectionModel.CONTIGUOUS_TREE_SELECTION); } /** @@ -1731,8 +1755,7 @@ public class BasicTreeUI */ protected boolean isToggleEvent(MouseEvent event) { - // FIXME: not implemented - return false; + return true; } /** @@ -1749,7 +1772,29 @@ public class BasicTreeUI */ protected void selectPathForEvent(TreePath path, MouseEvent event) { - // FIXME: not implemented + if (isToggleSelectionEvent(event)) + { + if (tree.isPathSelected(path)) + tree.removeSelectionPath(path); + else + { + tree.addSelectionPath(path); + tree.setAnchorSelectionPath(path); + } + } + else if (isMultiSelectEvent(event)) + { + TreePath anchor = tree.getAnchorSelectionPath(); + if (anchor != null) + { + int aRow = getRowForPath(tree, anchor); + tree.addSelectionInterval(aRow, getRowForPath(tree, path)); + } + else + tree.addSelectionPath(path); + } + else + tree.addSelectionPath(path); } /** @@ -1766,7 +1811,7 @@ public class BasicTreeUI return true; Object node = pathForRow.getLastPathComponent(); - return tree.getModel().isLeaf(node); + return treeModel.isLeaf(node); } /** @@ -1800,9 +1845,9 @@ public class BasicTreeUI (new TreeTraverseAction(0, "")).actionPerformed(e); else if (e.getActionCommand().equals("selectAll")) { - TreePath[] paths = new TreePath[tree.getRowCount()]; + TreePath[] paths = new TreePath[tree.getVisibleRowCount()]; - Object curr = getNextVisibleNode(tree.getModel().getRoot()); + Object curr = getNextVisibleNode(treeModel.getRoot()); int i = 0; while (curr != null && i < paths.length) { @@ -1822,13 +1867,8 @@ public class BasicTreeUI { Object last = lead.getLastPathComponent(); TreePath path = new TreePath(getPathToRoot(last, 0)); - if (!tree.getModel().isLeaf(last)) - { - if (tree.isExpanded(path)) - tree.collapsePath(path); - else - tree.expandPath(path); - } + if (!treeModel.isLeaf(last)) + toggleExpandState(path); } } else if (e.getActionCommand().equals("clearSelection")) @@ -1920,8 +1960,7 @@ public class BasicTreeUI /** * Updates the preferred size when scrolling, if necessary. */ - public class ComponentHandler - extends ComponentAdapter + public class ComponentHandler extends ComponentAdapter implements ActionListener { /** @@ -1937,6 +1976,7 @@ public class BasicTreeUI */ public ComponentHandler() { + // Nothing to do here. } /** @@ -1947,6 +1987,7 @@ public class BasicTreeUI */ public void componentMoved(ComponentEvent e) { + // TODO: What should be done here, if anything? } /** @@ -1955,6 +1996,7 @@ public class BasicTreeUI */ protected void startTimer() { + // TODO: Implement this properly. } /** @@ -1976,21 +2018,22 @@ public class BasicTreeUI */ public void actionPerformed(ActionEvent ae) { + // TODO: Implement this properly. } - }// ComponentHandler + } /** * Listener responsible for getting cell editing events and updating the tree * accordingly. */ - public class CellEditorHandler - implements CellEditorListener + public class CellEditorHandler implements CellEditorListener { /** * Constructor */ public CellEditorHandler() { + // Nothing to do here. } /** @@ -2023,6 +2066,9 @@ public class BasicTreeUI isEditing = false; tree.requestFocusInWindow(false); editorTimer.stop(); + validCachedPreferredSize = false; + tree.revalidate(); + tree.repaint(); } /** @@ -2051,6 +2097,8 @@ public class BasicTreeUI tree.requestFocusInWindow(false); editorTimer.stop(); isEditing = false; + validCachedPreferredSize = false; + tree.revalidate(); tree.repaint(); } }// CellEditorHandler @@ -2066,6 +2114,7 @@ public class BasicTreeUI */ public FocusHandler() { + // Nothing to do here. } /** @@ -2077,6 +2126,7 @@ public class BasicTreeUI */ public void focusGained(FocusEvent e) { + // TODO: Implement this properly. } /** @@ -2088,8 +2138,9 @@ public class BasicTreeUI */ public void focusLost(FocusEvent e) { + // TODO: Implement this properly. } - }// FocusHandler + } /** * This is used to get multiple key down events to appropriately genereate @@ -2109,6 +2160,7 @@ public class BasicTreeUI */ public KeyHandler() { + // Nothing to do here. } /** @@ -2122,6 +2174,7 @@ public class BasicTreeUI */ public void keyTyped(KeyEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2132,6 +2185,7 @@ public class BasicTreeUI */ public void keyPressed(KeyEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2142,22 +2196,22 @@ public class BasicTreeUI */ public void keyReleased(KeyEvent e) { + // TODO: What should be done here, if anything? } - }// KeyHandler + } /** * MouseListener is responsible for updating the selection based on mouse * events. */ - public class MouseHandler - extends MouseAdapter - implements MouseMotionListener + public class MouseHandler extends MouseAdapter implements MouseMotionListener { /** * Constructor */ public MouseHandler() { + // Nothing to do here. } /** @@ -2168,6 +2222,57 @@ public class BasicTreeUI */ public void mousePressed(MouseEvent e) { + Point click = e.getPoint(); + TreePath path = getClosestPathForLocation(tree, click.x, click.y); + + if (path != null) + { + bounds = getPathBounds(tree, path); + int row = getRowForPath(tree, path); + boolean cntlClick = isLocationInExpandControl(path, click.x, click.y); + + boolean isLeaf = isLeaf(row); + + TreeCellRenderer tcr = getCellRenderer(); + Icon icon; + if (isLeaf) + icon = UIManager.getIcon("Tree.leafIcon"); + else if (tree.isExpanded(path)) + icon = UIManager.getIcon("Tree.openIcon"); + else + icon = UIManager.getIcon("Tree.closedIcon"); + + if (tcr instanceof DefaultTreeCellRenderer) + { + Icon tmp = ((DefaultTreeCellRenderer) tcr).getIcon(); + if (tmp != null) + icon = tmp; + } + + // add gap*2 for the space before and after the text + if (icon != null) + bounds.width += icon.getIconWidth() + gap*2; + + boolean inBounds = bounds.contains(click.x, click.y); + if ((inBounds || cntlClick) && tree.isVisible(path)) + { + if (inBounds) + { + selectPath(tree, path); + if (e.getClickCount() == 2 && !isLeaf(row)) + toggleExpandState(path); + } + + if (cntlClick) + { + handleExpandControlClick(path, click.x, click.y); + if (cellEditor != null) + cellEditor.cancelCellEditing(); + } + else if (tree.isEditable()) + startEditing(path, e); + } + } } /** @@ -2181,6 +2286,7 @@ public class BasicTreeUI */ public void mouseDragged(MouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2192,6 +2298,7 @@ public class BasicTreeUI */ public void mouseMoved(MouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2202,16 +2309,16 @@ public class BasicTreeUI */ public void mouseReleased(MouseEvent e) { + // TODO: What should be done here, if anything? } - }// MouseHandler + } /** * MouseInputHandler handles passing all mouse events, including mouse motion * events, until the mouse is released to the destination it is constructed * with. */ - public class MouseInputHandler - implements MouseInputListener + public class MouseInputHandler implements MouseInputListener { /** Source that events are coming from */ protected Component source; @@ -2232,6 +2339,8 @@ public class BasicTreeUI public MouseInputHandler(Component source, Component destination, MouseEvent e) { + this.source = source; + this.destination = destination; } /** @@ -2243,6 +2352,7 @@ public class BasicTreeUI */ public void mouseClicked(MouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2253,42 +2363,7 @@ public class BasicTreeUI */ public void mousePressed(MouseEvent e) { - Point click = e.getPoint(); - int row = Math.round(click.y / getRowHeight()); - TreePath path = getClosestPathForLocation(tree, click.x, click.y); - - if (path != null) - { - boolean inBounds = false; - boolean cntlClick = false; - Rectangle bounds = getPathBounds(tree, path); - - bounds.x -= rightChildIndent - 4; - bounds.width += rightChildIndent + 4; - - if (bounds.contains(click.x, click.y)) - inBounds = true; - else if (hasControlIcons() - && (click.x < (bounds.x - rightChildIndent + 5) && - click.x > (bounds.x - rightChildIndent - 5))) - cntlClick = true; - - if ((inBounds || cntlClick) && tree.isVisible(path)) - { - selectPath(tree, path); - - if ((e.getClickCount() == 2 || cntlClick) && !isLeaf(row)) - { - if (tree.isExpanded(path)) - tree.collapsePath(path); - else - tree.expandPath(path); - } - - if (!cntlClick && tree.isEditable()) - startEditing(path, e); - } - } + // TODO: What should be done here, if anything? } /** @@ -2299,6 +2374,7 @@ public class BasicTreeUI */ public void mouseReleased(MouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2309,6 +2385,7 @@ public class BasicTreeUI */ public void mouseEntered(MouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2319,6 +2396,7 @@ public class BasicTreeUI */ public void mouseExited(MouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2332,6 +2410,7 @@ public class BasicTreeUI */ public void mouseDragged(MouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2343,6 +2422,7 @@ public class BasicTreeUI */ public void mouseMoved(MouseEvent e) { + // TODO: What should be done here, if anything? } /** @@ -2350,8 +2430,9 @@ public class BasicTreeUI */ protected void removeFromSource() { + // TODO: Implement this properly. } - }// MouseInputHandler + } /** * Class responsible for getting size of node, method is forwarded to @@ -2366,6 +2447,7 @@ public class BasicTreeUI */ public NodeDimensionsHandler() { + // Nothing to do here. } /** @@ -2413,6 +2495,7 @@ public class BasicTreeUI */ public PropertyChangeHandler() { + // Nothing to do here. } /** @@ -2424,8 +2507,15 @@ public class BasicTreeUI */ public void propertyChange(PropertyChangeEvent event) { + if ((event.getPropertyName()).equals("rootVisible")) + { + validCachedPreferredSize = false; + updateCurrentVisiblePath(); + tree.revalidate(); + tree.repaint(); + } } - }// PropertyChangeHandler + } /** * Listener on the TreeSelectionModel, resets the row selection if any of the @@ -2440,6 +2530,7 @@ public class BasicTreeUI */ public SelectionModelPropertyChangeHandler() { + // Nothing to do here. } /** @@ -2451,8 +2542,9 @@ public class BasicTreeUI */ public void propertyChange(PropertyChangeEvent event) { + // TODO: What should be done here, if anything? } - }// SelectionModelPropertyChangeHandler + } /** * ActionListener that invokes cancelEditing when action performed. @@ -2464,8 +2556,9 @@ public class BasicTreeUI /** * Constructor */ - public TreeCancelEditingAction() + public TreeCancelEditingAction(String name) { + // TODO: Implement this properly. } /** @@ -2476,6 +2569,7 @@ public class BasicTreeUI */ public void actionPerformed(ActionEvent e) { + // TODO: Implement this properly. } /** @@ -2485,9 +2579,10 @@ public class BasicTreeUI */ public boolean isEnabled() { + // TODO: Implement this properly. return false; } - }// TreeCancelEditingAction + } /** * Updates the TreeState in response to nodes expanding/collapsing. @@ -2501,6 +2596,7 @@ public class BasicTreeUI */ public TreeExpansionHandler() { + // Nothing to do here. } /** @@ -2511,6 +2607,9 @@ public class BasicTreeUI */ public void treeExpanded(TreeExpansionEvent event) { + validCachedPreferredSize = false; + updateCurrentVisiblePath(); + tree.revalidate(); tree.repaint(); } @@ -2522,6 +2621,9 @@ public class BasicTreeUI */ public void treeCollapsed(TreeExpansionEvent event) { + validCachedPreferredSize = false; + updateCurrentVisiblePath(); + tree.revalidate(); tree.repaint(); } }// TreeExpansionHandler @@ -2547,6 +2649,7 @@ public class BasicTreeUI */ public TreeHomeAction(int direction, String name) { + // TODO: Implement this properly } /** @@ -2557,6 +2660,7 @@ public class BasicTreeUI */ public void actionPerformed(ActionEvent e) { + // TODO: Implement this properly } /** @@ -2566,9 +2670,10 @@ public class BasicTreeUI */ public boolean isEnabled() { + // TODO: Implement this properly return false; } - }// TreeHomeAction + } /** * TreeIncrementAction is used to handle up/down actions. Selection is moved @@ -2591,6 +2696,7 @@ public class BasicTreeUI */ public TreeIncrementAction(int direction, String name) { + // TODO: Implement this properly } /** @@ -2606,11 +2712,11 @@ public class BasicTreeUI if (e.getActionCommand().equals("selectPreviousChangeLead")) { Object prev = getPreviousVisibleNode(last); - + if (prev != null) { TreePath newPath = new TreePath(getPathToRoot(prev, 0)); - selectPath(tree, new TreePath(getPathToRoot(prev, 0))); + selectPath(tree, newPath); tree.setLeadSelectionPath(newPath); } } @@ -2627,15 +2733,17 @@ public class BasicTreeUI else if (e.getActionCommand().equals("selectPrevious")) { Object prev = getPreviousVisibleNode(last); + if (prev != null) { TreePath newPath = new TreePath(getPathToRoot(prev, 0)); - selectPath(tree, new TreePath(getPathToRoot(prev, 0))); + selectPath(tree, newPath); } } else if (e.getActionCommand().equals("selectNext")) { Object next = getNextVisibleNode(last); + if (next != null) { TreePath newPath = new TreePath(getPathToRoot(next, 0)); @@ -2671,21 +2779,22 @@ public class BasicTreeUI */ public boolean isEnabled() { + // TODO: Implement this properly return false; } - }// TreeIncrementAction + } /** * Forwards all TreeModel events to the TreeState. */ - public class TreeModelHandler - implements TreeModelListener + public class TreeModelHandler implements TreeModelListener { /** * Constructor */ public TreeModelHandler() { + // Nothing to do here. } /** @@ -2703,6 +2812,9 @@ public class BasicTreeUI */ public void treeNodesChanged(TreeModelEvent e) { + validCachedPreferredSize = false; + updateCurrentVisiblePath(); + tree.revalidate(); tree.repaint(); } @@ -2716,6 +2828,9 @@ public class BasicTreeUI */ public void treeNodesInserted(TreeModelEvent e) { + validCachedPreferredSize = false; + updateCurrentVisiblePath(); + tree.revalidate(); tree.repaint(); } @@ -2732,6 +2847,9 @@ public class BasicTreeUI */ public void treeNodesRemoved(TreeModelEvent e) { + validCachedPreferredSize = false; + updateCurrentVisiblePath(); + tree.revalidate(); tree.repaint(); } @@ -2747,6 +2865,12 @@ public class BasicTreeUI */ public void treeStructureChanged(TreeModelEvent e) { + if (e.getPath().length == 1 + && !e.getPath()[0].equals(treeModel.getRoot())) + tree.expandPath(new TreePath(treeModel.getRoot())); + updateCurrentVisiblePath(); + validCachedPreferredSize = false; + tree.revalidate(); tree.repaint(); } }// TreeModelHandler @@ -2754,8 +2878,7 @@ public class BasicTreeUI /** * TreePageAction handles page up and page down events. */ - public class TreePageAction - extends AbstractAction + public class TreePageAction extends AbstractAction { /** Specifies the direction to adjust the selection by. */ protected int direction; @@ -2770,6 +2893,7 @@ public class BasicTreeUI */ public TreePageAction(int direction, String name) { + this.direction = direction; } /** @@ -2780,6 +2904,7 @@ public class BasicTreeUI */ public void actionPerformed(ActionEvent e) { + // TODO: Implement this properly. } /** @@ -2797,14 +2922,14 @@ public class BasicTreeUI * Listens for changes in the selection model and updates the display * accordingly. */ - public class TreeSelectionHandler - implements TreeSelectionListener + public class TreeSelectionHandler implements TreeSelectionListener { /** * Constructor */ public TreeSelectionHandler() { + // Nothing to do here. } /** @@ -2824,8 +2949,7 @@ public class BasicTreeUI /** * For the first selected row expandedness will be toggled. */ - public class TreeToggleAction - extends AbstractAction + public class TreeToggleAction extends AbstractAction { /** * Constructor @@ -2835,6 +2959,7 @@ public class BasicTreeUI */ public TreeToggleAction(String name) { + // Nothing to do here. } /** @@ -2845,6 +2970,7 @@ public class BasicTreeUI */ public void actionPerformed(ActionEvent e) { + // TODO: Implement this properly. } /** @@ -2862,8 +2988,7 @@ public class BasicTreeUI * TreeTraverseAction is the action used for left/right keys. Will toggle the * expandedness of a node, as well as potentially incrementing the selection. */ - public class TreeTraverseAction - extends AbstractAction + public class TreeTraverseAction extends AbstractAction { /** * Determines direction to traverse, 1 means expand, -1 means collapse. @@ -2880,6 +3005,7 @@ public class BasicTreeUI */ public TreeTraverseAction(int direction, String name) { + this.direction = direction; } /** @@ -2890,16 +3016,15 @@ public class BasicTreeUI */ public void actionPerformed(ActionEvent e) { - TreeModel mod = tree.getModel(); Object last = tree.getLeadSelectionPath().getLastPathComponent(); if (e.getActionCommand().equals("selectParent")) { TreePath path = new TreePath(getPathToRoot(last, 0)); - Object p = getParent(mod.getRoot(), last); + Object p = getParent(treeModel.getRoot(), last); - if (!mod.isLeaf(last) && tree.isExpanded(path)) - tree.collapsePath(path); + if (!treeModel.isLeaf(last)) + toggleExpandState(path); else if (p != null) selectPath(tree, new TreePath(getPathToRoot(p, 0))); } @@ -2907,8 +3032,8 @@ public class BasicTreeUI { TreePath path = new TreePath(getPathToRoot(last, 0)); - if (!mod.isLeaf(last) && tree.isCollapsed(path)) - tree.expandPath(path); + if (!treeModel.isLeaf(last)) + toggleExpandState(path); else { Object next = getNextVisibleNode(last); @@ -2926,9 +3051,10 @@ public class BasicTreeUI */ public boolean isEnabled() { + // TODO: Implement this properly return false; } - } // TreeTraverseAction + } /** * Returns the cell bounds for painting selected cells Package private for use @@ -2951,8 +3077,7 @@ public class BasicTreeUI FontMetrics fm = tree.getToolkit().getFontMetrics(f); if (s != null) - return new Rectangle(x, y, - SwingUtilities.computeStringWidth(fm, s) + 4, + return new Rectangle(x, y, SwingUtilities.computeStringWidth(fm, s), fm.getHeight()); } return new Rectangle(x, y, 0, 0); @@ -2982,80 +3107,19 @@ public class BasicTreeUI int rowHeight = getRowHeight(); if (startNode == null || startNode.equals(node)) { - if (!tree.isRootVisible() - && tree.isExpanded(new TreePath(mod.getRoot()))) - return new Point(x + ((getLevel(node)) * rightChildIndent), y); - - return new Point(x + ((getLevel(node) + 1) * rightChildIndent), y); - } - - if (!mod.isLeaf(startNode) - && tree.isExpanded(new TreePath(getPathToRoot(startNode, 0))) - && !mod.isLeaf(startNode) && mod.getChildCount(startNode) > 0) - { - Object child = mod.getChild(startNode, 0); - if (child != null) - return getCellLocation(x, y + rowHeight, tree, mod, node, child); + int level = getLevel(node); + if (level == 0) + return new Point(x, y); + if (!tree.isRootVisible() && + tree.isExpanded(new TreePath(mod.getRoot()))) + return new Point(x + ((level - 1) * rightChildIndent), y); + return new Point(x + (level * rightChildIndent), y); } - return getCellLocation(x, y + rowHeight, tree, mod, node, getNextVisibleNode(startNode)); } /** - * Paints a node in the tree Package private for use in inner classes. - * - * @param g - * the Graphics context in which to paint - * @param x - * the x location of the node - * @param y - * the y location of the node - * @param tree - * the tree to draw on - * @param node - * the object to draw - */ - void paintNode(Graphics g, int x, int y, JTree tree, Object node, - boolean isLeaf) - { - TreePath curr = new TreePath(getPathToRoot(node, 0)); - boolean selected = tree.isPathSelected(curr); - boolean expanded = false; - boolean hasIcons = false; - - if (tree.isVisible(curr)) - { - if (!isLeaf) - expanded = tree.isExpanded(curr); - - if (editingComponent != null && editingPath != null && isEditing(tree) - && node.equals(editingPath.getLastPathComponent())) - { - Rectangle bounds = getPathBounds(tree, editingPath); - rendererPane.paintComponent(g, editingComponent.getParent(), null, - new Rectangle(0, 0, bounds.width, - bounds.height)); - } - else - { - TreeCellRenderer dtcr = tree.getCellRenderer(); - if (dtcr == null) - dtcr = createDefaultCellRenderer(); - - int row = getRowForPath(tree, curr); - - Component c = dtcr.getTreeCellRendererComponent(tree, node, - selected, expanded, - isLeaf, row, false); - - rendererPane.paintComponent(g, c, c.getParent(), - getCellBounds(x, y, node)); - } - } - } - - /** * Recursively paints all elements of the tree Package private for use in * inner classes. * @@ -3065,8 +3129,6 @@ public class BasicTreeUI * of the current object * @param descent * is the number of elements drawn - * @param childNumber - * is the index of the current child in the tree * @param depth * is the depth of the current object in the tree * @param tree @@ -3077,71 +3139,79 @@ public class BasicTreeUI * is the current object to draw * @return int - current descent of the tree */ - int paintRecursive(Graphics g, int indentation, int descent, int childNumber, + int paintRecursive(Graphics g, int indentation, int descent, int depth, JTree tree, TreeModel mod, Object curr) { - Rectangle clip = g.getClipBounds(); + Rectangle clip = tree.getVisibleRect(); if (indentation > clip.x + clip.width + rightChildIndent || descent > clip.y + clip.height + getRowHeight()) return descent; + TreePath path = new TreePath(getPathToRoot(curr, 0)); int halfHeight = getRowHeight() / 2; int halfWidth = rightChildIndent / 2; int y0 = descent + halfHeight; int heightOfLine = descent + halfHeight; + int row = getRowForPath(tree, path); boolean isRootVisible = tree.isRootVisible(); - - if (mod.isLeaf(curr)) + boolean isExpanded = tree.isExpanded(path); + boolean isLeaf = mod.isLeaf(curr); + Rectangle bounds = getPathBounds(tree, path); + Object root = mod.getRoot(); + + if (isLeaf) { - paintNode(g, indentation + 4, descent, tree, curr, true); + paintRow(g, clip, null, bounds, path, row, true, false, true); descent += getRowHeight(); } else { if (depth > 0 || isRootVisible) { - paintNode(g, indentation + 4, descent, tree, curr, false); + paintRow(g, clip, null, bounds, path, row, isExpanded, false, false); descent += getRowHeight(); y0 += halfHeight; } - - int max = 0; - if (!mod.isLeaf(curr)) - max = mod.getChildCount(curr); - if (tree.isExpanded(new TreePath(getPathToRoot(curr, 0)))) + + if (isExpanded) { + int max = mod.getChildCount(curr); for (int i = 0; i < max; i++) { + Object child = mod.getChild(curr, i); + boolean childVis = tree.isVisible(new TreePath + (getPathToRoot(child, 0))); int indent = indentation + rightChildIndent; if (!isRootVisible && depth == 0) indent = 0; - else if ((!isRootVisible && !curr.equals(mod.getRoot())) - || isRootVisible) + else if (isRootVisible || + (!isRootVisible && !curr.equals(root)) && childVis) { g.setColor(getHashColor()); heightOfLine = descent + halfHeight; - g.drawLine(indentation + halfWidth, heightOfLine, - indentation + rightChildIndent, heightOfLine); + paintHorizontalLine(g, (JComponent) tree, heightOfLine, + indentation + halfWidth, indentation + rightChildIndent); } - descent = paintRecursive(g, indent, descent, i, depth + 1, - tree, mod, mod.getChild(curr, i)); + descent = paintRecursive(g, indent, descent, depth + 1, + tree, mod, child); } } } - if (tree.isExpanded(new TreePath(getPathToRoot(curr, 0)))) - if (y0 != heightOfLine && !mod.isLeaf(curr) - && mod.getChildCount(curr) > 0) + if (isExpanded) + if (y0 != heightOfLine + && (mod.getChildCount(curr) > 0 && + tree.isVisible(new TreePath(getPathToRoot(mod.getChild + (curr, 0), 0))))) { g.setColor(getHashColor()); - g.drawLine(indentation + halfWidth, y0, indentation + halfWidth, - heightOfLine); + paintVerticalLine(g, (JComponent) tree, indentation + halfWidth, y0, + heightOfLine); } - return descent; } - + /** * Recursively paints all the control icons on the tree. Package private for * use in inner classes. @@ -3152,76 +3222,99 @@ public class BasicTreeUI * of the current object * @param descent * is the number of elements drawn - * @param childNumber - * is the index of the current child in the tree * @param depth * is the depth of the current object in the tree * @param tree * is the tree to draw to * @param mod * is the TreeModel we are using to draw - * @param curr + * @param node * is the current object to draw - * @return int - current descent of the tree + * @return int current descent of the tree */ int paintControlIcons(Graphics g, int indentation, int descent, - int childNumber, int depth, JTree tree, TreeModel mod, + int depth, JTree tree, TreeModel mod, Object node) { - int h = descent; int rowHeight = getRowHeight(); - Icon ei = UIManager.getLookAndFeelDefaults().getIcon("Tree.expandedIcon"); - Icon ci = UIManager.getLookAndFeelDefaults().getIcon("Tree.collapsedIcon"); - Rectangle clip = g.getClipBounds(); + TreePath path = new TreePath(getPathToRoot(node, 0)); + Icon icon = getCurrentControlIcon(path); + + Rectangle clip = tree.getVisibleRect(); if (indentation > clip.x + clip.width + rightChildIndent || descent > clip.y + clip.height + getRowHeight()) return descent; - + if (mod.isLeaf(node)) descent += rowHeight; else - { + { + if (!node.equals(mod.getRoot()) && + (tree.isRootVisible() || getLevel(node) != 1)) + { + int width = icon.getIconWidth(); + int height = icon.getIconHeight() + 2; + int posX = indentation - rightChildIndent; + int posY = descent; + if (width > rightChildIndent) + posX -= gap; + else posX += width/2; + + if (height < rowHeight) + posY += height/2; + + icon.paintIcon(tree, g, posX, posY); + } + if (depth > 0 || tree.isRootVisible()) descent += rowHeight; - - int max = 0; - if (!mod.isLeaf(node)) - max = mod.getChildCount(node); - if (tree.isExpanded(new TreePath(getPathToRoot(node, 0)))) + + if (tree.isExpanded(path)) { - if (!node.equals(mod.getRoot())) - ei.paintIcon(tree, g, indentation - rightChildIndent - 3, h); - + int max = 0; + if (!mod.isLeaf(node)) + max = mod.getChildCount(node); + for (int i = 0; i < max; i++) { int indent = indentation + rightChildIndent; + Object child = mod.getChild(node, i); if (depth == 0 && !tree.isRootVisible()) - indent = -1; - - descent = paintControlIcons(g, indent, descent, i, depth + 1, - tree, mod, mod.getChild(node, i)); + indent = 1; + if (tree.isVisible(new TreePath(getPathToRoot(child, 0)))) + descent = paintControlIcons(g, indent, descent, depth + 1, + tree, mod, child); } } - else if (!node.equals(mod.getRoot())) - ci.paintIcon(tree, g, indentation - rightChildIndent - 3, - descent - getRowHeight()); } - + return descent; } /** - * Returns true if the LookAndFeel implements the control icons Package + * Returns true if the LookAndFeel implements the control icons. Package * private for use in inner classes. * - * @return true if control icons are visible + * @returns true if there are control icons */ boolean hasControlIcons() { - if (UIManager.getLookAndFeelDefaults().getIcon("Tree.expandedIcon") == null - || UIManager.getLookAndFeelDefaults().getIcon("Tree.collapsedIcon") == null) - return false; - return true; + if (expandedIcon != null || collapsedIcon != null) + return true; + return false; + } + + /** + * Returns control icon. It is null if the LookAndFeel does not implements the + * control icons. Package private for use in inner classes. + * + * @return control icon if it exists. + */ + Icon getCurrentControlIcon(TreePath path) + { + if (tree.isExpanded(path)) + return expandedIcon; + return collapsedIcon; } /** @@ -3235,8 +3328,10 @@ public class BasicTreeUI */ Object getParent(Object root, Object node) { - if (root == null || node == null) + if (root == null || node == null || + root.equals(node)) return null; + if (node instanceof TreeNode) return ((TreeNode) node).getParent(); return findNode(root, node); @@ -3253,113 +3348,69 @@ public class BasicTreeUI */ private Object findNode(Object root, Object node) { - TreeModel mod = tree.getModel(); - int size = 0; - if (!mod.isLeaf(root)) - size = mod.getChildCount(root); - for (int i = 0; i < size; i++) - { - if (mod.getIndexOfChild(root, node) != -1) - return root; - - Object n = findNode(mod.getChild(root, i), node); - if (n != null) - return n; - } - return null; - } - - /** - * Get next visible node in the tree. Package private for use in inner - * classes. - * - * @param the - * current node - * @return the next visible node in the JTree. Return null if there are no - * more. - */ - Object getNextVisibleNode(Object node) - { - Object next = null; - TreePath current = null; - - if (node != null) - next = getNextNode(node); - - if (next != null) + if (!treeModel.isLeaf(root) && !root.equals(node)) { - current = new TreePath(getPathToRoot(next, 0)); - if (tree.isVisible(current)) - return next; - - while (next != null && !tree.isVisible(current)) + int size = treeModel.getChildCount(root); + for (int j = 0; j < size; j++) { - next = getNextNode(next); + Object child = treeModel.getChild(root, j); + if (node.equals(child)) + return root; - if (next != null) - current = new TreePath(getPathToRoot(next, 0)); + Object n = findNode(child, node); + if (n != null) + return n; } } - return next; + return null; } - + /** * Get previous visible node in the tree. Package private for use in inner * classes. * - * @param the + * @param node - * current node * @return the next visible node in the JTree. Return null if there are no * more. */ Object getPreviousVisibleNode(Object node) { - Object prev = null; - TreePath current = null; - - if (node != null) - prev = getPreviousNode(node); - - if (prev != null) + updateCurrentVisiblePath(); + if (currentVisiblePath != null) { - current = new TreePath(getPathToRoot(prev, 0)); - if (tree.isVisible(current)) - return prev; - - while (prev != null && !tree.isVisible(current)) - { - prev = getPreviousNode(prev); - - if (prev != null) - current = new TreePath(getPathToRoot(prev, 0)); - } + Object[] nodes = currentVisiblePath.getPath(); + int i = 0; + while (i < nodes.length && !node.equals(nodes[i])) + i++; + // return the next node + if (i-1 >= 0) + return nodes[i-1]; } - return prev; + return null; } /** * Returns the next node in the tree Package private for use in inner classes. * - * @param the + * @param curr - * current node * @return the next node in the tree */ Object getNextNode(Object curr) { - TreeModel mod = tree.getModel(); - if (!mod.isLeaf(curr) && mod.getChildCount(curr) > 0) - return mod.getChild(curr, 0); + if (!treeModel.isLeaf(curr) && treeModel.getChildCount(curr) > 0) + return treeModel.getChild(curr, 0); Object node = curr; Object sibling = null; - do { sibling = getNextSibling(node); - node = getParent(mod.getRoot(), node); + node = getParent(treeModel.getRoot(), node); } while (sibling == null && node != null); - + return sibling; } @@ -3367,14 +3418,13 @@ public class BasicTreeUI * Returns the previous node in the tree Package private for use in inner * classes. * - * @param the + * @param node * current node * @return the previous node in the tree */ Object getPreviousNode(Object node) { - TreeModel mod = tree.getModel(); - Object parent = getParent(mod.getRoot(), node); + Object parent = getParent(treeModel.getRoot(), node); if (parent == null) return null; @@ -3384,13 +3434,13 @@ public class BasicTreeUI return parent; int size = 0; - if (!mod.isLeaf(sibling)) - size = mod.getChildCount(sibling); + if (!treeModel.isLeaf(sibling)) + size = treeModel.getChildCount(sibling); while (size > 0) { - sibling = mod.getChild(sibling, size - 1); - if (!mod.isLeaf(sibling)) - size = mod.getChildCount(sibling); + sibling = treeModel.getChild(sibling, size - 1); + if (!treeModel.isLeaf(sibling)) + size = treeModel.getChildCount(sibling); else size = 0; } @@ -3402,52 +3452,50 @@ public class BasicTreeUI * Returns the next sibling in the tree Package private for use in inner * classes. * - * @param the + * @param node - * current node * @return the next sibling in the tree */ Object getNextSibling(Object node) { - TreeModel mod = tree.getModel(); - Object parent = getParent(mod.getRoot(), node); + Object parent = getParent(treeModel.getRoot(), node); if (parent == null) return null; - int index = mod.getIndexOfChild(parent, node) + 1; + int index = treeModel.getIndexOfChild(parent, node) + 1; int size = 0; - if (!mod.isLeaf(parent)) - size = mod.getChildCount(parent); + if (!treeModel.isLeaf(parent)) + size = treeModel.getChildCount(parent); if (index == 0 || index >= size) return null; - return mod.getChild(parent, index); + return treeModel.getChild(parent, index); } - + /** * Returns the previous sibling in the tree Package private for use in inner * classes. * - * @param the + * @param node - * current node * @return the previous sibling in the tree */ Object getPreviousSibling(Object node) { - TreeModel mod = tree.getModel(); - Object parent = getParent(mod.getRoot(), node); + Object parent = getParent(treeModel.getRoot(), node); if (parent == null) return null; - int index = mod.getIndexOfChild(parent, node) - 1; + int index = treeModel.getIndexOfChild(parent, node) - 1; int size = 0; - if (!mod.isLeaf(parent)) - size = mod.getChildCount(parent); + if (!treeModel.isLeaf(parent)) + size = treeModel.getChildCount(parent); if (index < 0 || index >= size) return null; - return mod.getChild(parent, index); + return treeModel.getChild(parent, index); } /** @@ -3463,22 +3511,24 @@ public class BasicTreeUI { if (path != null) { - if (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION) + if (tree.getSelectionModel().getSelectionMode() == + TreeSelectionModel.SINGLE_TREE_SELECTION) { + tree.getSelectionModel().clearSelection(); tree.addSelectionPath(path); tree.setLeadSelectionPath(path); } - else if (tree.getSelectionModel().getSelectionMode() == TreeSelectionModel.CONTIGUOUS_TREE_SELECTION) + else if (tree.getSelectionModel().getSelectionMode() == + TreeSelectionModel.CONTIGUOUS_TREE_SELECTION) { // TODO } else { - tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); - - tree.getSelectionModel().clearSelection(); tree.addSelectionPath(path); tree.setLeadSelectionPath(path); + tree.getSelectionModel().setSelectionMode + (TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); } } } @@ -3495,7 +3545,6 @@ public class BasicTreeUI */ Object[] getPathToRoot(Object node, int depth) { - TreeModel mod = tree.getModel(); if (node == null) { if (depth == 0) @@ -3504,7 +3553,7 @@ public class BasicTreeUI return new Object[depth]; } - Object[] path = getPathToRoot(getParent(mod.getRoot(), node), depth + 1); + Object[] path = getPathToRoot(getParent(treeModel.getRoot(), node), depth + 1); path[path.length - depth - 1] = node; return path; } @@ -3512,22 +3561,23 @@ public class BasicTreeUI /** * Returns the level of the node in the tree. * - * @param the + * @param node - * current node * @return the number of the level */ int getLevel(Object node) { int count = -1; + Object current = node; do { - current = getParent(tree.getModel().getRoot(), current); + current = getParent(treeModel.getRoot(), current); count++; } while (current != null); - + return count; } @@ -3586,10 +3636,271 @@ public class BasicTreeUI * is the center position in y-direction FIXME what to do if x < * (icon.width / 2). Same with y */ - protected void drawCentered(JComponent c, Graphics g, Icon icon, int x, int y) + protected void drawCentered(Component c, Graphics g, Icon icon, int x, int y) { int beginPositionX = x - icon.getIconWidth() / 2; int beginPositionY = y - icon.getIconHeight() / 2; icon.paintIcon(c, g, beginPositionX, beginPositionY); } + + /** + * Draws a dashed horizontal line. + * + * @param g - the graphics configuration. + * @param y - the y location to start drawing at + * @param x1 - the x location to start drawing at + * @param x2 - the x location to finish drawing at + */ + protected void drawDashedHorizontalLine(Graphics g, int y, int x1, int x2) + { + for (int i = x1; i < x2; i += 2) + g.drawLine(i, y, i + 1, y); + } + + /** + * Draws a dashed vertical line. + * + * @param g - the graphics configuration. + * @param x - the x location to start drawing at + * @param y1 - the y location to start drawing at + * @param y2 - the y location to finish drawing at + */ + protected void drawDashedVerticalLine(Graphics g, int x, int y1, int y2) + { + for (int i = y1; i < y2; i += 2) + g.drawLine(x, i, x, i + 1); + } + + /** + * Paints the expand (toggle) part of a row. The receiver should NOT modify + * clipBounds, or insets. + * + * @param g - the graphics configuration + * @param clipBounds - + * @param insets - + * @param bounds - bounds of expand control + * @param path - path to draw control for + * @param row - row to draw control for + * @param isExpanded - is the row expanded + * @param hasBeenExpanded - has the row already been expanded + * @param isLeaf - is the path a leaf + */ + protected void paintExpandControl(Graphics g, Rectangle clipBounds, + Insets insets, Rectangle bounds, + TreePath path, int row, + boolean isExpanded, boolean hasBeenExpanded, + boolean isLeaf) + { + if (treeModel != null && hasControlIcons()) + paintControlIcons(g, 0, 0, 0, tree, treeModel, path.getLastPathComponent()); + } + + /** + * Paints the horizontal part of the leg. The receiver should NOT modify + * clipBounds, or insets. + * NOTE: parentRow can be -1 if the root is not visible. + * + * @param g - the graphics configuration + * @param clipBounds - + * @param insets - + * @param bounds - bounds of expand control + * @param path - path to draw control for + * @param row - row to draw control for + * @param isExpanded - is the row expanded + * @param hasBeenExpanded - has the row already been expanded + * @param isLeaf - is the path a leaf + */ + protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds, + Insets insets, Rectangle bounds, + TreePath path, int row, + boolean isExpanded, boolean hasBeenExpanded, + boolean isLeaf) + { + // FIXME: not implemented + } + + /** + * Paints the vertical part of the leg. The receiver should NOT modify + * clipBounds, insets. + * + * @param g - the graphics configuration. + * @param clipBounds - + * @param insets - + * @param path - the path to draw the vertical part for. + */ + protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, + Insets insets, TreePath path) + { + // FIXME: not implemented + } + + /** + * Paints the renderer part of a row. The receiver should NOT modify clipBounds, + * or insets. + * + * @param g - the graphics configuration + * @param clipBounds - + * @param insets - + * @param bounds - bounds of expand control + * @param path - path to draw control for + * @param row - row to draw control for + * @param isExpanded - is the row expanded + * @param hasBeenExpanded - has the row already been expanded + * @param isLeaf - is the path a leaf + */ + protected void paintRow(Graphics g, Rectangle clipBounds, + Insets insets, Rectangle bounds, + TreePath path, int row, + boolean isExpanded, boolean hasBeenExpanded, + boolean isLeaf) + { + boolean selected = tree.isPathSelected(path); + boolean hasIcons = false; + Object node = path.getLastPathComponent(); + + if (tree.isVisible(path)) + { + if (!validCachedPreferredSize) + updateCachedPreferredSize(); + bounds.x += gap; + bounds.width = preferredSize.width + bounds.x; + + if (editingComponent != null && editingPath != null && isEditing(tree) + && node.equals(editingPath.getLastPathComponent())) + { + rendererPane.paintComponent(g, editingComponent.getParent(), null, + bounds); + } + else + { + TreeCellRenderer dtcr = tree.getCellRenderer(); + if (dtcr == null) + dtcr = createDefaultCellRenderer(); + + Component c = dtcr.getTreeCellRendererComponent(tree, node, + selected, isExpanded, isLeaf, row, tree.hasFocus()); + rendererPane.paintComponent(g, c, c.getParent(), bounds); + } + } + } + + /** + * Prepares for the UI to uninstall. + */ + protected void prepareForUIUninstall() + { + // TODO: Implement this properly. + } + + /** + * Returns true if the expand (toggle) control should be drawn for the + * specified row. + * + * @param path - current path to check for. + * @param row - current row to check for. + * @param isExpanded - true if the path is expanded + * @param hasBeenExpanded - true if the path has been expanded already + * @param isLeaf - true if the row is a lead + */ + protected boolean shouldPaintExpandControl(TreePath path, int row, + boolean isExpanded, + boolean hasBeenExpanded, + boolean isLeaf) + { + Object node = path.getLastPathComponent(); + if (treeModel != null && (!isLeaf && !node.equals(treeModel.getRoot())) && + (tree.isRootVisible() || getLevel(node) != 1)) + return true; + return false; + } + + /** + * Updates the cached current TreePath of all visible + * nodes in the tree. + */ + void updateCurrentVisiblePath() + { + if (treeModel == null) + return; + + Object next = treeModel.getRoot(); + Rectangle bounds = getCellBounds(0, 0, next); + boolean rootVisible = isRootVisible(); + + // If root is not a valid size to be visible, or is + // not visible and the tree is expanded, then the next node acts + // as the root + if ((bounds.width == 0 && bounds.height == 0) || (!rootVisible + && tree.isExpanded(new TreePath(next)))) + next = getNextNode(next); + + Object root = next; + TreePath current = null; + while (next != null) + { + if (current == null) + current = new TreePath(next); + else + current = current.pathByAddingChild(next); + do + { + TreePath path = new TreePath(getPathToRoot(next, 0)); + if ((tree.isVisible(path) && tree.isExpanded(path)) + || treeModel.isLeaf(next)) + next = getNextNode(next); + else + { + Object pNext = next; + next = getNextSibling(pNext); + // if no next sibling, check parent's next sibling. + if (next == null) + { + Object parent = getParent(root, pNext); + while (next == null && parent != null) + { + next = getNextSibling(parent); + if (next == null) + parent = getParent(treeModel.getRoot(), next); + } + } + } + } + while (next != null && + !tree.isVisible(new TreePath(getPathToRoot(next, 0)))); + } + + currentVisiblePath = current; + if (currentVisiblePath != null) + tree.setVisibleRowCount(currentVisiblePath.getPathCount()); + else tree.setVisibleRowCount(0); + + if (tree.getSelectionModel() != null && tree.getSelectionCount() == 0 && + currentVisiblePath != null) + selectPath(tree, new TreePath(getPathToRoot(currentVisiblePath. + getPathComponent(0), 0))); + } + + /** + * Get next visible node in the currentVisiblePath. Package private for use in + * inner classes. + * + * @param node + * current node + * @return the next visible node in the JTree. Return null if there are no + * more. + */ + Object getNextVisibleNode(Object node) + { + if (currentVisiblePath != null) + { + Object[] nodes = currentVisiblePath.getPath(); + int i = 0; + while (i < nodes.length && !node.equals(nodes[i])) + i++; + // return the next node + if (i+1 < nodes.length) + return nodes[i+1]; + } + return null; + } } // BasicTreeUI diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicViewportUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicViewportUI.java index 0d461332a70..51b902d6443 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicViewportUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicViewportUI.java @@ -38,61 +38,22 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Image; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.image.ImageObserver; - import javax.swing.JComponent; -import javax.swing.JViewport; -import javax.swing.ViewportLayout; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; +import javax.swing.LookAndFeel; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ViewportUI; public class BasicViewportUI extends ViewportUI { - - ChangeListener changeListener; - Image backingStoreImage; - int backingStoreWidth = -1; - int backingStoreHeight = -1; - - class ChangeHandler implements ChangeListener + protected void installDefaults(JComponent c) { - public void stateChanged(ChangeEvent event) - { - JViewport v = (JViewport) event.getSource(); - v.repaint(); - } - } - - void installDefaults(JComponent c) - { c.setOpaque(true); + LookAndFeel.installColorsAndFont(c, "Viewport.background", + "Viewport.foreground", "Viewport.font"); } - - void uninstallDefaults(JComponent c) - { - } - - void installListeners(JComponent c) + protected void uninstallDefaults(JComponent c) { - ((JViewport)c).addChangeListener(changeListener); - } - - void uninstallListeners(JComponent c) - { - ((JViewport)c).removeChangeListener(changeListener); - } - - public BasicViewportUI() - { - changeListener = new ChangeHandler(); + // TODO: Implement this properly. } public static ComponentUI createUI(JComponent c) @@ -103,132 +64,12 @@ public class BasicViewportUI extends ViewportUI public void installUI(JComponent c) { super.installUI(c); - installListeners(c); + installDefaults(c); } public void uninstallUI(JComponent c) { - uninstallListeners(c); - } - - - public Dimension getPreferredSize(JComponent c) - { - // let the ViewportLayout decide - return null; - } - - public void paint(Graphics g, JComponent c) - { - JViewport port = (JViewport)c; - Component view = port.getView(); - - if (view == null) - return; - - Point pos = port.getViewPosition(); - Rectangle viewBounds = view.getBounds(); - Rectangle portBounds = port.getBounds(); - - if (viewBounds.width == 0 - || viewBounds.height == 0 - || portBounds.width == 0 - || portBounds.height == 0) - return; - - switch (port.getScrollMode()) - { - - case JViewport.BACKINGSTORE_SCROLL_MODE: - paintBackingStore(g, port, view, pos, viewBounds, portBounds); - break; - - case JViewport.BLIT_SCROLL_MODE: - // FIXME: implement separate blit mode - - case JViewport.SIMPLE_SCROLL_MODE: - default: - paintSimple(g, port, view, pos, viewBounds, portBounds); - break; - } - } - - private void paintSimple(Graphics g, - JViewport v, - Component view, - Point pos, - Rectangle viewBounds, - Rectangle portBounds) - { - Rectangle oldClip = g.getClipBounds(); - g.setClip(new Rectangle(0, 0, portBounds.width, portBounds.height)); - g.translate (-pos.x, -pos.y); - try - { - view.paint(g); - } - finally - { - g.translate (pos.x, pos.y); - g.setClip (oldClip); - } - } - - private void paintBackingStore(Graphics g, - JViewport v, - Component view, - Point pos, - Rectangle viewBounds, - Rectangle portBounds) - { - if (backingStoreImage == null - || backingStoreWidth != viewBounds.width - || backingStoreHeight != viewBounds.height) - { - backingStoreImage = v.createImage(viewBounds.width, viewBounds.height); - backingStoreWidth = viewBounds.width; - backingStoreHeight = viewBounds.height; - } - - Graphics g2 = backingStoreImage.getGraphics(); - - if (v.getBackground() != null) - { - // fill the backing store background - java.awt.Color save = g2.getColor(); - g2.setColor(v.getBackground()); - g2.fillRect (0, 0, backingStoreWidth, backingStoreHeight); - g2.setColor(save); - - // fill the viewport background - save = g.getColor(); - g.setColor(v.getBackground()); - g.fillRect (0, 0, portBounds.width, portBounds.height); - g.setColor(save); - - } - else - { - // clear the backing store background - g2.clearRect(0, 0, backingStoreWidth, backingStoreHeight); - - // clear the viewport background - g.clearRect(0, 0, portBounds.width, portBounds.height); - } - - g2.setClip(g.getClipBounds()); - g2.translate(-pos.x, -pos.y); - try - { - view.paint(g2); - } - finally - { - g2.translate(pos.x, pos.y); - } - g2 = null; - g.drawImage(backingStoreImage, - 0, 0, - (ImageObserver)null); + super.uninstallUI(c); + uninstallDefaults(c); } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java b/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java index f55510684c6..4fa3b364056 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java @@ -45,21 +45,29 @@ import java.awt.Insets; import javax.swing.AbstractButton; import javax.swing.ButtonModel; +import javax.swing.JButton; import javax.swing.JInternalFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JScrollPane; import javax.swing.JTextField; +import javax.swing.JToggleButton; +import javax.swing.JToolBar; +import javax.swing.SwingConstants; +import javax.swing.UIDefaults; +import javax.swing.UIManager; import javax.swing.border.AbstractBorder; import javax.swing.border.Border; import javax.swing.plaf.BorderUIResource; import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicBorders; +import javax.swing.text.JTextComponent; /** - * This factory class creates borders for the different Swing components - * UI. + * A factory class that creates borders for the different Swing components. * * @author Roman Kennke (roman@kennke.org) */ @@ -69,12 +77,24 @@ public class MetalBorders /** The shared instance for getButtonBorder(). */ private static Border buttonBorder; + /** The shared instance for getToggleButtonBorder(). */ + private static Border toggleButtonBorder; + + /** The shared instance for getDesktopIconBorder(). */ + private static Border desktopIconBorder; + /** The shared instance for getRolloverButtonBorder(). */ private static Border toolbarButtonBorder; /** The shared instance for getTextFieldBorder(). */ private static Border textFieldBorder; + /** The shared instance for getTextBorder(). */ + private static Border textBorder; + + /** The shared instance for getRolloverBorder(). */ + private static Border rolloverBorder; + /** * A MarginBorder that gets shared by multiple components. * Created on demand by the private helper function {@link @@ -83,20 +103,19 @@ public class MetalBorders private static BasicBorders.MarginBorder marginBorder; /** - * The border that is drawn around Swing buttons. + * A border used for {@link JButton} components. */ - public static class ButtonBorder - extends AbstractBorder - implements UIResource + public static class ButtonBorder extends AbstractBorder implements UIResource { /** The borders insets. */ protected static Insets borderInsets = new Insets(3, 3, 3, 3); /** - * Creates a new instance of ButtonBorder. + * Creates a new instance of <code>ButtonBorder</code>. */ public ButtonBorder() { + // Nothing to do here. } /** @@ -122,46 +141,55 @@ public class MetalBorders Color light = MetalLookAndFeel.getWhite(); Color middle = MetalLookAndFeel.getControl(); - // draw dark border - g.setColor(darkShadow); - g.drawRect(x, y, w - 2, h - 2); - - if (!bmodel.isPressed()) - { - // draw light border - g.setColor(light); - g.drawRect(x + 1, y + 1, w - 2, h - 2); + if (c.isEnabled()) + { + // draw dark border + g.setColor(darkShadow); + g.drawRect(x, y, w - 2, h - 2); - // draw crossing pixels of both borders - g.setColor(middle); - g.drawRect(x + 1, y + h - 2, 0, 0); - g.drawRect(x + w - 2, y + 1, 0, 0); - } - else - { - // draw light border - g.setColor(light); - g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); - g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); + if (!bmodel.isPressed()) + { + // draw light border + g.setColor(light); + g.drawRect(x + 1, y + 1, w - 2, h - 2); - // draw shadow border - g.setColor(middle); - g.drawLine(x + 1, y + 1, x + w - 2, y + 1); - g.drawLine(x + 1, y + 1, x + 1, y + h - 2); + // draw crossing pixels of both borders + g.setColor(middle); + g.drawRect(x + 1, y + h - 2, 0, 0); + g.drawRect(x + w - 2, y + 1, 0, 0); + } + else + { + // draw light border + g.setColor(light); + g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); + g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); - // draw crossing pixels of both borders - g.setColor(shadow); - g.drawRect(x + 1, y + h - 2, 0, 0); - g.drawRect(x + w - 2, y + 1, 0, 0); + // draw shadow border + g.setColor(middle); + g.drawLine(x + 1, y + 1, x + w - 2, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + h - 2); + + // draw crossing pixels of both borders + g.setColor(shadow); + g.drawRect(x + 1, y + h - 2, 0, 0); + g.drawRect(x + w - 2, y + 1, 0, 0); + } + } + else + { + // draw disabled border + g.setColor(MetalLookAndFeel.getInactiveControlTextColor()); + g.drawRect(x, y, w - 2, h - 2); } } /** - * Returns the insets of the ButtonBorder. + * Returns the insets of the <code>ButtonBorder</code>. * * @param c the component for which the border is used * - * @return the insets of the ButtonBorder + * @return The insets of the ButtonBorder */ public Insets getBorderInsets(Component c) { @@ -169,19 +197,20 @@ public class MetalBorders } /** - * Returns the insets of the ButtonBorder in the specified Insets object. + * Returns the insets of the <code>ButtonBorder</code> in the specified + * <code>newInsets</code> object. * * @param c the component for which the border is used - * @param newInsets the insets object where to put the values + * @param newInsets the insets object where to put the values (if + * <code>null</code>, a new instance is created). * - * @return the insets of the ButtonBorder + * @return The insets. */ public Insets getBorderInsets(Component c, Insets newInsets) { if (newInsets == null) newInsets = new Insets(0, 0, 0, 0); - AbstractButton b = (AbstractButton) c; newInsets.bottom = borderInsets.bottom; newInsets.left = borderInsets.left; newInsets.right = borderInsets.right; @@ -191,6 +220,71 @@ public class MetalBorders } /** + * A border used when painting {@link JInternalFrame} instances. + */ + static class DesktopIconBorder extends AbstractBorder + implements UIResource + { + /** + * Creates a new border instance. + */ + public DesktopIconBorder() + { + // Nothing to do here. + } + + /** + * Returns the border insets. + * + * @param c the component (ignored). + * + * @return The border insets. + */ + public Insets getBorderInsets(Component c) + { + return getBorderInsets(c, null); + } + + /** + * Returns the border insets. + * + * @param c the component (ignored). + * @return The border insets. + */ + public Insets getBorderInsets(Component c, Insets newInsets) + { + if (newInsets == null) + newInsets = new Insets(3, 3, 2, 3); + else + { + newInsets.top = 3; + newInsets.left = 3; + newInsets.bottom = 2; + newInsets.right = 3; + } + return newInsets; + } + + /** + * Paints the border for the specified component. + * + * @param c the component. + * @param g the graphics device. + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param w the width. + * @param h the height. + */ + public void paintBorder(Component c, Graphics g, int x, int y, int w, + int h) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawRect(x, y, w - 1, h - 1); + } + + } + + /** * A simple 3D border. */ public static class Flush3DBorder extends AbstractBorder @@ -201,6 +295,7 @@ public class MetalBorders */ public Flush3DBorder() { + // Nothing to do here. } /** @@ -262,6 +357,88 @@ public class MetalBorders } /** + * A border used for a {@link JInternalFrame} when it is being used as a + * palette. + * + * @since 1.3 + */ + public static class PaletteBorder extends AbstractBorder + implements UIResource + { + /** + * Creates a new <code>PaletteBorder</code>. + */ + public PaletteBorder() + { + // Nothing to do here. + } + + /** + * Returns the border insets. + * + * @param c the component (ignored). + * + * @return The border insets. + */ + public Insets getBorderInsets(Component c) + { + return getBorderInsets(c, null); + } + + /** + * Returns the border insets. + * + * @param c the component (ignored). + * @param newInsets the insets object that, if non-<code>null</code>, will + * be populated with the result from this method. + * + * @return The border insets. + */ + public Insets getBorderInsets(Component c, Insets newInsets) + { + if (newInsets == null) + newInsets = new Insets(1, 1, 1, 1); + else + { + newInsets.top = 1; + newInsets.left = 1; + newInsets.bottom = 1; + newInsets.right = 1; + } + return newInsets; + } + + /** + * Paints the border for the specified component. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param w the width. + * @param h the height. + */ + public void paintBorder(Component c, Graphics g, int x, int y, int w, + int h) + { + Color savedColor = g.getColor(); + + // draw the outline + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + g.drawRect(x, y, w - 1, h - 1); + + // put a dot in each corner + g.setColor(MetalLookAndFeel.getControl()); + g.fillRect(x, y, 1, 1); + g.fillRect(x + w - 1, y, 1, 1); + g.fillRect(x + w - 1, y + h - 1, 1, 1); + g.fillRect(x, y + h - 1, 1, 1); + g.setColor(savedColor); + } + + } + + /** * A border used for the {@link JTextField} component. */ public static class TextFieldBorder extends Flush3DBorder @@ -272,6 +449,7 @@ public class MetalBorders */ public TextFieldBorder() { + // Nothing to do here. } /** @@ -286,8 +464,17 @@ public class MetalBorders */ public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) - { - if (c.isEnabled()) + { + boolean enabledTextBorder; + if (c instanceof JTextComponent) + { + JTextComponent tc = (JTextComponent) c; + enabledTextBorder = tc.isEnabled() && tc.isEditable(); + } + else + enabledTextBorder = false; + + if (enabledTextBorder) super.paintBorder(c, g, x, y, w, h); else { @@ -301,7 +488,7 @@ public class MetalBorders } /** - * A border used when painting {@link JInternalFrame} instances. + * A border used for the {@link JInternalFrame} component. */ public static class InternalFrameBorder extends AbstractBorder implements UIResource @@ -311,6 +498,7 @@ public class MetalBorders */ public InternalFrameBorder() { + // Nothing to do here. } /** @@ -386,7 +574,10 @@ public class MetalBorders g.drawLine(x + w - 3, y + 14, x + w - 3, y + h - 15); // draw the line highlights - g.setColor(MetalLookAndFeel.getControl()); + if (f.isSelected()) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getControlShadow()); g.drawLine(x + 15, y + 3, x + w - 14, y + 3); g.drawLine(x + 15, y + h - 2, x + w - 14, y + h - 2); g.drawLine(x + 3, y + 15, x + 3, y + h - 14); @@ -396,24 +587,129 @@ public class MetalBorders } /** + * A border used for {@link JInternalFrame} components that are + * presented as dialogs (by the {@link JOptionPane} class). + */ + public static class OptionDialogBorder extends AbstractBorder + implements UIResource + { + + /** + * Creates a new border instance. + */ + public OptionDialogBorder() + { + // Nothing to do here. + } + + /** + * Returns the border insets. + * + * @param c the component (ignored). + * + * @return The border insets. + */ + public Insets getBorderInsets(Component c) + { + return getBorderInsets(c, null); + } + + /** + * Returns the border insets. + * + * @param c the component (ignored). + * @return The border insets. + */ + public Insets getBorderInsets(Component c, Insets newInsets) + { + if (newInsets == null) + newInsets = new Insets(3, 3, 3, 3); + else + { + newInsets.top = 3; + newInsets.left = 3; + newInsets.bottom = 3; + newInsets.right = 3; + } + return newInsets; + } + + /** + * Paints the border for the specified component. + * + * @param c the component. + * @param g the graphics device. + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param w the width. + * @param h the height. + */ + public void paintBorder(Component c, Graphics g, int x, int y, int w, + int h) + { + + JInternalFrame f = (JInternalFrame) c; + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + if (f.getContentPane() instanceof JOptionPane) + { + JOptionPane pane = (JOptionPane) f.getContentPane(); + int type = pane.getMessageType(); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + if (type == JOptionPane.QUESTION_MESSAGE) + { + Color bc = defaults.getColor( + "OptionPane.questionDialog.border.background"); + if (bc != null) + g.setColor(bc); + } + if (type == JOptionPane.WARNING_MESSAGE) + { + Color bc = defaults.getColor( + "OptionPane.warningDialog.border.background"); + if (bc != null) + g.setColor(bc); + } + else if (type == JOptionPane.ERROR_MESSAGE) + { + Color bc = defaults.getColor( + "OptionPane.errorDialog.border.background"); + if (bc != null) + g.setColor(bc); + } + } + + // fill the border background + g.fillRect(x, y, w, 3); + g.fillRect(x, y, 3, h); + g.fillRect(x + w - 3, y, 3, h); + g.fillRect(x, y + h - 3, w, 3); + + // draw a dot in each corner + g.setColor(MetalLookAndFeel.getControl()); + g.fillRect(x, y, 1, 1); + g.fillRect(x + w - 1, y, 1, 1); + g.fillRect(x + w - 1, y + h - 1, 1, 1); + g.fillRect(x, y + h - 1, 1, 1); + + } + + } + + /** * A border used for {@link JMenu} and {@link JMenuItem} components. */ - public static class MenuItemBorder - extends AbstractBorder - implements UIResource + public static class MenuItemBorder extends AbstractBorder + implements UIResource { /** The border insets. */ - protected static Insets borderInsets = new Insets(2, 2, 2, 2); - - // TODO: find where the real colors come from - private static Color borderColorDark = new Color(102, 102, 153); - private static Color borderColorLight = new Color(255, 255, 255); + protected static Insets borderInsets = new Insets(1, 1, 1, 1); /** * Creates a new border instance. */ public MenuItemBorder() { + // Nothing to do here. } /** @@ -430,15 +726,17 @@ public class MetalBorders public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { + Color dark = MetalLookAndFeel.getPrimaryControlDarkShadow(); + Color light = MetalLookAndFeel.getPrimaryControlHighlight(); if (c instanceof JMenu) { JMenu menu = (JMenu) c; if (menu.isSelected()) { - g.setColor(borderColorDark); + g.setColor(dark); g.drawLine(x, y, x, y + h); g.drawLine(x, y, x + w, y); g.drawLine(x + w - 2, y + 1, x + w - 2, y + h); - g.setColor(borderColorLight); + g.setColor(light); g.drawLine(x + w - 1, y + 1, x + w - 1, y + h); } } @@ -446,12 +744,18 @@ public class MetalBorders { JMenuItem item = (JMenuItem) c; if (item.isArmed()) - { - g.setColor(borderColorDark); - g.drawLine(x, y, x + w, y); - g.setColor(borderColorLight); - g.drawLine(x, y + h - 1, x + w, y + h - 1); - } + { + g.setColor(dark); + g.drawLine(x, y, x + w, y); + g.setColor(light); + g.drawLine(x, y + h - 1, x + w, y + h - 1); + } + else + { + // Normally we draw a light line on the left. + g.setColor(light); + g.drawLine(x, y, x, y + h); + } } } @@ -505,6 +809,7 @@ public class MetalBorders */ public MenuBarBorder() { + // Nothing to do here. } /** @@ -558,7 +863,7 @@ public class MetalBorders } /** - * A border for JScrollPanes. + * A border for {@link JScrollPane} components. */ public static class ScrollPaneBorder extends AbstractBorder @@ -572,6 +877,7 @@ public class MetalBorders */ public ScrollPaneBorder() { + // Nothing to do here. } /** @@ -634,6 +940,45 @@ public class MetalBorders } /** + * A button border that is only visible when the mouse pointer is within + * the button's bounds. + */ + public static class RolloverButtonBorder + extends MetalBorders.ButtonBorder + { + /** + * Creates a new border instance. + */ + public RolloverButtonBorder() + { + // Nothing to do here. + } + + /** + * Paints the border. + * + * @param c the component. + * @param g the graphics device. + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param w the width. + * @param h the height. + */ + public void paintBorder(Component c, Graphics g, int x, int y, int w, + int h) + { + boolean mouseIsOver = false; + if (c instanceof AbstractButton) + { + ButtonModel bmodel = ((AbstractButton) c).getModel(); + mouseIsOver = bmodel.isRollover(); + } + if (mouseIsOver) + super.paintBorder(c, g, x, y, w, h); + } + } + + /** * This border is used in Toolbar buttons as inner border. */ static class RolloverMarginBorder extends AbstractBorder @@ -646,6 +991,7 @@ public class MetalBorders */ public RolloverMarginBorder() { + // Nothing to do here. } /** @@ -693,13 +1039,14 @@ public class MetalBorders { /** The border's insets. */ - protected static Insets borderInsets = new Insets(2, 2, 1, 1); + protected static Insets borderInsets = new Insets(3, 1, 2, 1); /** * Constructs a new PopupMenuBorder. */ public PopupMenuBorder() { + // Nothing to do here. } /** @@ -763,13 +1110,258 @@ public class MetalBorders // draw highlighted inner border (only top and left) g.setColor(light); - g.drawLine(x + 1, y + 1, x + 1, y + h - 2); g.drawLine(x + 1, y + 1, x + w - 2, y + 1); } } /** + * A border used for the {@link JToggleButton} component. + * + * @since 1.3 + */ + public static class ToggleButtonBorder + extends ButtonBorder + { + /** + * Creates a new border instance. + */ + public ToggleButtonBorder() + { + // Nothing to do here. + } + + /** + * Paints the toggle button border. + * + * @param c the component for which we paint the border + * @param g the Graphics context to use + * @param x the X coordinate of the upper left corner of c + * @param y the Y coordinate of the upper left corner of c + * @param w the width of c + * @param h the height of c + */ + public void paintBorder(Component c, Graphics g, int x, int y, int w, + int h) + { + ButtonModel bmodel = null; + + if (c instanceof AbstractButton) + bmodel = ((AbstractButton) c).getModel(); + + Color darkShadow = MetalLookAndFeel.getControlDarkShadow(); + Color shadow = MetalLookAndFeel.getControlShadow(); + Color light = MetalLookAndFeel.getWhite(); + Color middle = MetalLookAndFeel.getControl(); + + if (c.isEnabled()) + { + // draw dark border + g.setColor(darkShadow); + g.drawRect(x, y, w - 2, h - 2); + + if (!bmodel.isArmed()) + { + // draw light border + g.setColor(light); + g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); + g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); + if (bmodel.isSelected()) + g.setColor(middle); + g.drawLine(x + 1, y + 1, x + w - 3, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + h - 3); + + // draw crossing pixels of both borders + g.setColor(shadow); + g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2); + g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1); + } + else + { + // draw light border + g.setColor(light); + g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); + g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); + + // draw shadow border + g.setColor(shadow); + g.drawLine(x + 1, y + 1, x + w - 2, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + h - 2); + + // draw crossing pixels of both borders + g.setColor(shadow); + g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2); + g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1); + + } + // draw corners + g.setColor(middle); + g.drawLine(x, y + h - 1, x, y + h - 1); + g.drawLine(x + w - 1, y, x + w - 1, y); + } + else + { + // draw disabled border + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawRect(x, y, w - 2, h - 2); + } + } + } + + /** + * A border used for the {@link JToolBar} component. + */ + public static class ToolBarBorder extends AbstractBorder + implements UIResource, SwingConstants + { + /** + * Creates a new border instance. + */ + public ToolBarBorder() + { + // Nothing to do here. + } + + /** + * Returns the border insets. + * + * @param c the component (ignored). + * + * @return The border insets. + */ + public Insets getBorderInsets(Component c) + { + return getBorderInsets(c, null); + } + + /** + * Returns the border insets. + * + * @param c the component (ignored). + * @return The border insets. + */ + public Insets getBorderInsets(Component c, Insets newInsets) + { + JToolBar tb = (JToolBar) c; + if (tb.getOrientation() == JToolBar.HORIZONTAL) + { + if (newInsets == null) + newInsets = new Insets(2, 16, 2, 2); + else + { + newInsets.top = 2; + newInsets.left = 16; + newInsets.bottom = 2; + newInsets.right = 2; + } + return newInsets; + } + else // assume JToolBar.VERTICAL + { + if (newInsets == null) + newInsets = new Insets(16, 2, 2, 2); + else + { + newInsets.top = 16; + newInsets.left = 2; + newInsets.bottom = 2; + newInsets.right = 2; + } + return newInsets; + } + + } + + /** + * Paints the border for the specified component. + * + * @param c the component. + * @param g the graphics device. + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param w the width. + * @param h the height. + */ + public void paintBorder(Component c, Graphics g, int x, int y, int w, + int h) + { + + JToolBar tb = (JToolBar) c; + if (tb.getOrientation() == JToolBar.HORIZONTAL) + { + MetalUtils.fillMetalPattern(tb, g, x + 2, y + 2, x + 11, y + h - 5, + MetalLookAndFeel.getControlHighlight(), + MetalLookAndFeel.getControlDarkShadow()); + } + else + { + MetalUtils.fillMetalPattern(tb, g, x + 2, y + 2, x + w - 5, y + 11, + MetalLookAndFeel.getControlHighlight(), + MetalLookAndFeel.getControlDarkShadow()); + } + } + + } + + /** + * A border for table header cells. + * + * @since 1.3 + */ + public static class TableHeaderBorder extends AbstractBorder + { + /** + * The insets of this border. + */ + // TODO: According to tests that I have done, this is really the border + // that should be returned by getBorderInsets(). However, the name + // is very distracting. Is there any deeper meaning in it? + protected Insets editorBorderInsets; + + /** + * Creates a new instance of <code>TableHeaderBorder</code>. + */ + public TableHeaderBorder() + { + editorBorderInsets = new Insets(1, 1, 1, 1); + } + + /** + * Return the insets of this border. + * + * @return the insets of this border + */ + public Insets getBorderInsets(Component c) + { + return editorBorderInsets; + } + + /** + * Paints the border. + * + * @param c the component for which to paint the border + * @param g the graphics context to use + * @param x the x cooridinate of the border rectangle + * @param y the y cooridinate of the border rectangle + * @param w the width of the border rectangle + * @param h the height of the border rectangle + */ + public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) + { + Color dark = MetalLookAndFeel.getControlDarkShadow(); + Color light = MetalLookAndFeel.getWhite(); + Color old = g.getColor(); + g.setColor(light); + g.drawLine(x, y, x + w - 2, y); + g.drawLine(x, y, x, y + h - 2); + g.setColor(dark); + g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); + g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); + g.setColor(old); + } + } + + /** * Returns a border for Swing buttons in the Metal Look & Feel. * * @return a border for Swing buttons in the Metal Look & Feel @@ -785,6 +1377,40 @@ public class MetalBorders } return buttonBorder; } + + /** + * Returns a border for use with {@link JToggleButton} components. + * + * @return A border. + * + * @since 1.3 + */ + public static Border getToggleButtonBorder() + { + if (toggleButtonBorder == null) + { + Border outer = new ToggleButtonBorder(); + Border inner = getMarginBorder(); + toggleButtonBorder = new BorderUIResource.CompoundBorderUIResource + (outer, inner); + } + return toggleButtonBorder; + } + + /** + * Returns a border instance that is used with a {@link JInternalFrame} when + * it is in the iconified state. + * + * @return A border. + * + * @since 1.3 + */ + public static Border getDesktopIconBorder() + { + if (desktopIconBorder == null) + desktopIconBorder = new DesktopIconBorder(); + return desktopIconBorder; + } /** * Returns a border for use by the {@link JTextField} component. @@ -796,11 +1422,36 @@ public class MetalBorders public static Border getTextFieldBorder() { if (textFieldBorder == null) - textFieldBorder = new TextFieldBorder(); + { + Border inner = getMarginBorder(); + Border outer = new TextFieldBorder(); + textFieldBorder = + new BorderUIResource.CompoundBorderUIResource(outer, inner); + } return textFieldBorder; } /** + * Returns the border that is used for text components (except text fields, + * which use {@link #getTextFieldBorder}. + * + * @return the border that is used for text components + * + * @since 1.3 + */ + public static Border getTextBorder() + { + if (textBorder == null) + { + Border inner = getMarginBorder(); + Border outer = new Flush3DBorder(); + textBorder = + new BorderUIResource.CompoundBorderUIResource(outer, inner); + } + return textBorder; + } + + /** * Returns a border for Toolbar buttons in the Metal Look & Feel. * * @return a border for Toolbar buttons in the Metal Look & Feel @@ -828,4 +1479,22 @@ public class MetalBorders marginBorder = new BasicBorders.MarginBorder(); return marginBorder; } + + /** + * Returns a shared instance of a compound border for rollover buttons. + * + * @return A shared border instance. + */ + static Border getRolloverBorder() + { + if (rolloverBorder == null) + { + Border outer = new MetalBorders.RolloverButtonBorder(); + Border inner = MetalBorders.getMarginBorder(); + rolloverBorder = new BorderUIResource.CompoundBorderUIResource(outer, + inner); + } + return rolloverBorder; + } + } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalButtonListener.java b/libjava/classpath/javax/swing/plaf/metal/MetalButtonListener.java new file mode 100644 index 00000000000..e6fb22e929f --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/metal/MetalButtonListener.java @@ -0,0 +1,86 @@ +/* MetalButtonListener.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.plaf.metal; + +import java.beans.PropertyChangeEvent; + +import javax.swing.AbstractButton; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicButtonListener; + +/** + * A listener for buttons under the {@link MetalLookAndFeel}. + * + * @see MetalButtonUI#createButtonListener + */ +class MetalButtonListener extends BasicButtonListener +{ + + /** + * Creates a new instance. + * + * @param button the button. + */ + public MetalButtonListener(AbstractButton button) + { + super(button); + } + + /** + * Handles property change events. + * + * @param e the event. + */ + public void propertyChange(PropertyChangeEvent e) + { + super.propertyChange(e); + if (e.getPropertyName().equals( + AbstractButton.ROLLOVER_ENABLED_CHANGED_PROPERTY)) + { + AbstractButton b = (AbstractButton) e.getSource(); + if (b.getBorder() instanceof UIResource) + { + if (Boolean.TRUE.equals(e.getNewValue())) + b.setBorder(MetalBorders.getRolloverBorder()); + else if (Boolean.FALSE.equals(e.getNewValue())) + b.setBorder(MetalBorders.getButtonBorder()); + } + } + } +} diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalButtonUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalButtonUI.java index 0dac5ec3953..02c39c1499e 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalButtonUI.java @@ -39,18 +39,23 @@ exception statement from your version. */ package javax.swing.plaf.metal; import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Rectangle; import javax.swing.AbstractButton; +import javax.swing.JButton; import javax.swing.JComponent; -import javax.swing.JToolBar; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicButtonListener; import javax.swing.plaf.basic.BasicButtonUI; /** - * The Metal Look & Feel implementation for - * {@link javax.swing.AbstractButton}s. + * A UI delegate for the {@link JButton} component. * * @author Roman Kennke (roman@kennke.org) */ @@ -58,27 +63,25 @@ public class MetalButtonUI extends BasicButtonUI { - /** The cached MetalButtonUI instance. */ - private static MetalButtonUI instance = null; - - /** The color for the focus border. */ + /** The color used to draw the focus rectangle around the text and/or icon. */ protected Color focusColor; - - /** The color that indicates a selected button. */ + + /** The background color for the button when it is pressed. */ protected Color selectColor; /** The color for disabled button labels. */ protected Color disabledTextColor; /** - * Creates a new instance of MetalButtonUI. + * Creates a new instance. */ public MetalButtonUI() { super(); - focusColor = getFocusColor(); - selectColor = getSelectColor(); - disabledTextColor = getDisabledTextColor(); + UIDefaults def = UIManager.getLookAndFeelDefaults(); + focusColor = def.getColor(getPropertyPrefix() + "focus"); + selectColor = def.getColor(getPropertyPrefix() + "select"); + disabledTextColor = def.getColor(getPropertyPrefix() + "disabledText"); } /** @@ -88,8 +91,7 @@ public class MetalButtonUI */ protected Color getFocusColor() { - UIDefaults def = UIManager.getLookAndFeelDefaults(); - return def.getColor(getPropertyPrefix() + ".focus"); + return focusColor; } /** @@ -99,8 +101,7 @@ public class MetalButtonUI */ protected Color getSelectColor() { - UIDefaults def = UIManager.getLookAndFeelDefaults(); - return def.getColor(getPropertyPrefix() + ".select"); + return selectColor; } /** @@ -110,36 +111,123 @@ public class MetalButtonUI */ protected Color getDisabledTextColor() { - UIDefaults def = UIManager.getLookAndFeelDefaults(); - return def.getColor(getPropertyPrefix() + ".disabledText"); + return disabledTextColor; } /** - * Returns an instance of MetalButtonUI. - * - * @param component a button for which a UI instance should be returned + * Returns a UI delegate for the specified component. + * + * @param c the component (should be a subclass of {@link AbstractButton}). + * + * @return A new instance of <code>MetalButtonUI</code>. */ - public static ComponentUI createUI(JComponent component) - { - if (instance == null) - instance = new MetalButtonUI(); - return instance; + public static ComponentUI createUI(JComponent c) { + return new MetalButtonUI(); } /** - * Install the Look & Feel defaults for Buttons. - * - * @param button the button for which to install the Look & Feel + * Installs the default settings for the specified button. + * + * @param button the button. + * + * @see #uninstallDefaults(AbstractButton) */ public void installDefaults(AbstractButton button) { super.installDefaults(button); + if (button.isRolloverEnabled()) + { + if (button.getBorder() instanceof UIResource) + button.setBorder(MetalBorders.getRolloverBorder()); + } + } + + /** + * Removes the defaults added by {@link #installDefaults(AbstractButton)}. + */ + public void uninstallDefaults(AbstractButton button) + { + super.uninstallDefaults(button); + if (button.getBorder() instanceof UIResource) + button.setBorder(null); + } - UIDefaults defaults = UIManager.getLookAndFeelDefaults(); - button.setFont(defaults.getFont("Button.font")); + /** + * Returns a button listener for the specified button. + * + * @param button the button. + * + * @return A button listener. + */ + protected BasicButtonListener createButtonListener(AbstractButton button) + { + return new MetalButtonListener(button); + } - if (button.getParent() instanceof JToolBar) - button.setBorder(MetalBorders.getToolbarButtonBorder()); + /** + * Paints the background of the button to indicate that it is in the "pressed" + * state. + * + * @param g the graphics context. + * @param b the button. + */ + protected void paintButtonPressed(Graphics g, AbstractButton b) + { + if (b.isContentAreaFilled()) + { + Rectangle area = b.getVisibleRect(); + g.setColor(selectColor); + g.fillRect(area.x, area.y, area.width, area.height); + } + } + + /** + * Paints the focus rectangle around the button text and/or icon. + * + * @param g the graphics context. + * @param b the button. + * @param viewRect the button bounds. + * @param textRect the text bounds. + * @param iconRect the icon bounds. + */ + protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect, + Rectangle textRect, Rectangle iconRect) { + if (b.isEnabled() && b.hasFocus() && b.isFocusPainted()) + { + Color savedColor = g.getColor(); + g.setColor(getFocusColor()); + Rectangle focusRect = iconRect.union(textRect); + g.drawRect(focusRect.x - 1, focusRect.y, + focusRect.width + 1, focusRect.height); + g.setColor(savedColor); + } + } + + /** + * Paints the button text. + * + * @param g the graphics context. + * @param c the button. + * @param textRect the text bounds. + * @param text the text to display. + */ + protected void paintText(Graphics g, JComponent c, Rectangle textRect, + String text) + { + AbstractButton b = (AbstractButton) c; + Font f = b.getFont(); + g.setFont(f); + FontMetrics fm = g.getFontMetrics(f); + + if (b.isEnabled()) + { + g.setColor(b.getForeground()); + g.drawString(text, textRect.x, textRect.y + fm.getAscent()); + } + else + { + g.setColor(getDisabledTextColor()); + g.drawString(text, textRect.x, textRect.y + fm.getAscent()); + } } - } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxIcon.java b/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxIcon.java index 3f3c9ce58b3..6b9f31b85b6 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxIcon.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxIcon.java @@ -38,10 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.metal; -import java.awt.Color; import java.awt.Component; import java.awt.Graphics; - import java.io.Serializable; import javax.swing.Icon; @@ -49,8 +47,7 @@ import javax.swing.JCheckBox; import javax.swing.plaf.UIResource; /** - * An {@link Icon} implementation for {@link JCheckBox}es in the - * Metal Look & Feel. + * An {@link Icon} used by the {@link MetalCheckBoxUI} class. * * @author Roman Kennke (roman@kennke.org) */ @@ -79,7 +76,10 @@ public class MetalCheckBoxIcon */ protected void drawCheck(Component c, Graphics g, int x, int y) { - g.setColor(Color.BLACK); + if (c.isEnabled()) + g.setColor(MetalLookAndFeel.getBlack()); + else + g.setColor(MetalLookAndFeel.getControlDisabled()); g.drawLine(3 + x, 5 + y, 3 + x, 9 + y); g.drawLine(4 + x, 5 + y, 4 + x, 9 + y); g.drawLine(5 + x, 7 + y, 9 + x, 3 + y); diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxUI.java index c46cb5f2fb1..b4f6f0a56cd 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalCheckBoxUI.java @@ -44,8 +44,7 @@ import javax.swing.UIDefaults; import javax.swing.plaf.ComponentUI; /** - * A UI delegate for the {@link JCheckBox} component under the - * {@link MetalLookAndFeel}. + * A UI delegate for the {@link JCheckBox} component. */ public class MetalCheckBoxUI extends MetalRadioButtonUI @@ -64,11 +63,11 @@ public class MetalCheckBoxUI } /** - * Returns an instance of MetalCheckBoxUI. + * Returns a shared instance of <code>MetalCheckBoxUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalCheckBoxUI + * @return A shared instance of <code>MetalCheckBoxUI</code>. */ public static ComponentUI createUI(JComponent component) { diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java new file mode 100644 index 00000000000..6993e18e9b9 --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java @@ -0,0 +1,241 @@ +/* MetalComboBoxButton.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.plaf.metal; + +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Rectangle; + +import javax.swing.CellRendererPane; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JList; +import javax.swing.SwingUtilities; + +/** + * A button used by the {@link MetalComboBoxUI} class. + */ +public class MetalComboBoxButton extends JButton { + + /** A reference to the JComboBox that the button belongs to. */ + protected JComboBox comboBox; + + /** A reference to the JList. */ + protected JList listBox; + + /** ??? */ + protected CellRendererPane rendererPane; + + /** The button icon. */ + protected Icon comboIcon; + + /** Display just the icon, or the icon plus the label. */ + protected boolean iconOnly; + + /** + * Creates a new button. + * + * @param cb the combo that the button is used for (<code>null</code> not + * permitted). + * @param i the icon displayed on the button. + * @param pane the rendering pane. + * @param list the list. + */ + public MetalComboBoxButton(JComboBox cb, Icon i, CellRendererPane pane, + JList list) + { + this(cb, i, cb.isEditable(), pane, list); + } + + /** + * Creates a new button. + * + * @param cb the combo that the button is used for (<code>null</code> not + * permitted). + * @param i the icon displayed on the button. + * @parma onlyIcon a flag that specifies whether the button displays only an + * icon, or text as well. + * @param pane the rendering pane. + * @param list the list. + */ + public MetalComboBoxButton(JComboBox cb, Icon i, boolean onlyIcon, + CellRendererPane pane, JList list) + { + super(); + if (cb == null) + throw new NullPointerException("Null 'cb' argument"); + comboBox = cb; + comboIcon = i; + iconOnly = onlyIcon; + listBox = list; + rendererPane = pane; + } + + /** + * Returns the combo box that the button is used with. + * + * @return The combo box. + */ + public final JComboBox getComboBox() + { + return comboBox; + } + + /** + * Sets the combo box that the button is used with. + * + * @param cb the combo box. + */ + public final void setComboBox(JComboBox cb) + { + comboBox = cb; + } + + /** + * Returns the icon displayed by the button. By default, this will be an + * instance of {@link MetalComboBoxIcon}. + * + * @return The icon displayed by the button. + */ + public final Icon getComboIcon() + { + return comboIcon; + } + + /** + * Sets the icon displayed by the button. + * + * @param i the icon. + */ + public final void setComboIcon(Icon i) + { + comboIcon = i; + } + + /** + * Returns a flag that controls whether the button displays an icon only, + * or text as well. + * + * @return A boolean. + */ + public final boolean isIconOnly() + { + return iconOnly; + } + + /** + * Sets the flag that controls whether the button displays an icon only, + * or text as well. + * + * @param isIconOnly the flag. + */ + public final void setIconOnly(boolean isIconOnly) + { + iconOnly = isIconOnly; + } + + /** + * Returns <code>false</code>, to indicate that this component is not part + * of the focus traversal group. + * + * @return <code>false</code> + */ + public boolean isFocusTraversable() + { + return false; + } + + /** + * Enables or disables the button. + * + * @param enabled the new status. + */ + public void setEnabled(boolean enabled) + { + super.setEnabled(enabled); + // TODO: figure out what this might need to be used for + // perhaps it has something to do with the button's icon and/or border? + } + + /** + * Paints the component. + * + * @param g the graphics device. + */ + public void paintComponent(Graphics g) + { + super.paintComponent(g); + if (iconOnly) + { + Rectangle bounds = getBounds(); + int x = (bounds.width - comboIcon.getIconWidth()) / 2; + int y = (bounds.height - comboIcon.getIconHeight()) / 2; + comboIcon.paintIcon(comboBox, g, x, y); + } + else + { + Object selected = comboBox.getModel().getSelectedItem(); + if (selected == null) + selected = ""; + Rectangle bounds = comboBox.getBounds(); + Rectangle innerArea = SwingUtilities.calculateInnerArea(this, null); + Insets insets = comboBox.getInsets(); + Rectangle renderArea = new Rectangle(innerArea.x, innerArea.y, + innerArea.width - comboIcon.getIconWidth() - 4, innerArea.height); + Component cellRenderer + = comboBox.getRenderer().getListCellRendererComponent(this.listBox, + selected, comboBox.getSelectedIndex(), false, false); + cellRenderer.setBackground(comboBox.getBackground()); + cellRenderer.setEnabled(comboBox.isEnabled()); + rendererPane.paintComponent(g, cellRenderer, this, renderArea); + if (comboBox.hasFocus()) + { + g.setColor(MetalLookAndFeel.getFocusColor()); + g.drawRect(innerArea.x, innerArea.y - 1, innerArea.width - 1, + innerArea.height); + } + int iconX = bounds.width - insets.right - comboIcon.getIconWidth() - 7; + int iconY = insets.top + + (bounds.height - comboIcon.getIconHeight()) / 2; + comboIcon.paintIcon(comboBox, g, iconX, iconY); + } + } +} diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxEditor.java b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxEditor.java new file mode 100644 index 00000000000..a531079cb1d --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxEditor.java @@ -0,0 +1,143 @@ +/* MetalComboBoxEditor.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.plaf.metal; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Insets; + +import javax.swing.JTextField; +import javax.swing.plaf.basic.BasicComboBoxEditor; +import javax.swing.plaf.metal.MetalLookAndFeel; +import javax.swing.plaf.metal.MetalBorders.Flush3DBorder; + +/** + * An editor used by the {@link MetalComboBoxUI} class. + */ +public class MetalComboBoxEditor extends BasicComboBoxEditor +{ + /** + * A border used for the {@link JTextField} component. + */ + static class MetalComboBoxEditorBorder extends Flush3DBorder + { + /** + * Creates a new border instance. + */ + public MetalComboBoxEditorBorder() + { + // Nothing to do here. + } + + /** + * Paints the border for the specified component. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param w the width. + * @param h the height. + */ + public void paintBorder(Component c, Graphics g, int x, int y, int w, + int h) + { + Color savedColor = g.getColor(); + if (c.isEnabled()) + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + else + g.setColor(MetalLookAndFeel.getControlShadow()); + g.drawLine(x, y, x + w - 1, y); + g.drawLine(x, y, x, y + h - 2); + g.drawLine(x + 2, y + h - 2, x + w - 1, y + h - 2); + g.setColor(MetalLookAndFeel.getControl()); + g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2); + g.setColor(MetalLookAndFeel.getWhite()); + g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); + g.setColor(savedColor); + } + + /** + * Measures the width of this border. + * + * @param c the component whose border is to be measured. + * + * @return an Insets object whose <code>left</code>, <code>right</code>, + * <code>top</code> and <code>bottom</code> fields indicate the + * width of the border at the respective edge, which is zero + * for the default implementation provided by AbstractButton. + * + * @see #getBorderInsets(java.awt.Component, java.awt.Insets) + */ + public Insets getBorderInsets(Component c) + { + return editorBorderInsets; + } + } + + /** + * A subclass of {@link MetalComboBoxEditor} that implements the + * {@link javax.swing.plaf.UIResource} interface. + */ + public static class UIResource extends MetalComboBoxEditor + implements javax.swing.plaf.UIResource + { + /** + * Creates a new instance. + */ + public UIResource() + { + // Nothing to do here. + } + } + + /** The editor's border insets. */ + protected static Insets editorBorderInsets = new Insets(4, 2, 4, 0); + + /** + * Creates a new editor. + */ + public MetalComboBoxEditor() + { + super(); + editor.setBorder(new MetalComboBoxEditorBorder()); + } + +} diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxIcon.java b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxIcon.java index ce4fa7d32ce..f21c5af6136 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxIcon.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxIcon.java @@ -1,4 +1,4 @@ -/* MetalComboBoxButton.java +/* MetalComboBoxIcon.java Copyright (C) 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -88,10 +88,11 @@ public class MetalComboBoxIcon implements Icon, Serializable { */ public void paintIcon(Component c, Graphics g, int x, int y) { - // TODO: work out whether/how the icon changes with different component - // states (and also different metal themes) Color savedColor = g.getColor(); - g.setColor(Color.black); + if (c.isEnabled()) + g.setColor(MetalLookAndFeel.getBlack()); + else + g.setColor(MetalLookAndFeel.getControlDisabled()); for (int i = 0; i < 5; i++) g.drawLine(x + i, y + i, x + 9 - i, y + i); g.setColor(savedColor); diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java index 28c279d8ed6..a43ee3cb04f 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxUI.java @@ -38,20 +38,134 @@ exception statement from your version. */ package javax.swing.plaf.metal; -import java.util.HashMap; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.LayoutManager; +import java.awt.Rectangle; +import java.awt.event.MouseEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import javax.swing.CellRendererPane; +import javax.swing.ComboBoxEditor; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicComboBoxUI; +import javax.swing.plaf.basic.BasicComboPopup; +import javax.swing.plaf.basic.ComboPopup; -public class MetalComboBoxUI - extends BasicComboBoxUI -{ - /** The UI instances for JComboBoxes. */ - private static HashMap instances = null; +/** + * A UI delegate for the {@link JComboBox} component. + */ +public class MetalComboBoxUI extends BasicComboBoxUI +{ + /** + * A layout manager that arranges the editor component (if active) and the + * button that make up the combo box. + */ + public class MetalComboBoxLayoutManager + extends BasicComboBoxUI.ComboBoxLayoutManager + { + /** + * Creates a new instance of the layout manager. + */ + public MetalComboBoxLayoutManager() + { + // Nothing to do here. + } + + /** + * Arranges the editor (if visible) and button that comprise the combo + * box. + * + * @param parent the parent. + */ + public void layoutContainer(Container parent) + { + JComboBox cb = (JComboBox) parent; + if (!cb.isEditable()) + { + Rectangle bounds = parent.getBounds(); + arrowButton.setBounds(0, 0, bounds.width, bounds.height); + } + else + superLayout(parent); + } + + /** + * Calls the <code>layoutContainer(Container)</code> method in the super + * class. + * + * @param parent the container. + */ + public void superLayout(Container parent) + { + super.layoutContainer(parent); + } + } + + /** + * A listener used to handle property changes in the {@link JComboBox} + * component, to ensure that the UI delegate accurately reflects the current + * state in the rendering onscreen. + */ + public class MetalPropertyChangeListener + extends BasicComboBoxUI.PropertyChangeHandler + { + /** + * Creates a new listener. + */ + public MetalPropertyChangeListener() + { + // Nothing to do here. + } + + /** + * Handles a property change event, updating the UI components as + * appropriate. + * + * @param e the event. + */ + public void propertyChange(PropertyChangeEvent e) + { + if (e.getPropertyName().equals("editable")) + editablePropertyChanged(e); + super.propertyChange(e); + } + } /** + * A popup menu for the combo-box. + * + * @see #createPopup() + * + * @deprecated 1.4 + */ + public class MetalComboPopup extends BasicComboPopup + { + /** + * Creates a new popup. + * + * @param cBox the combo box. + */ + public MetalComboPopup(JComboBox cBox) + { + super(cBox); + } + + public void delegateFocus(MouseEvent e) + { + super.delegateFocus(e); + } + } + + /** * Constructs a new instance of MetalComboBoxUI. */ public MetalComboBoxUI() @@ -68,19 +182,135 @@ public class MetalComboBoxUI */ public static ComponentUI createUI(JComponent component) { - if (instances == null) - instances = new HashMap(); - - Object o = instances.get(component); - MetalComboBoxUI instance; - if (o == null) + return new MetalComboBoxUI(); + } + + /** + * Creates an editor for the combo box. + * + * @return An editor. + */ + protected ComboBoxEditor createEditor() + { + return new MetalComboBoxEditor.UIResource(); + } + + /** + * Creates a popup for the combo box. + * + * @return A popup. + */ + protected ComboPopup createPopup() + { + return new MetalComboPopup(comboBox); + } + + /** + * Creates a new button for use in rendering the JComboBox. + * + * @return A button. + */ + protected JButton createArrowButton() + { + JButton button = new MetalComboBoxButton(comboBox, new MetalComboBoxIcon(), + new CellRendererPane(), listBox); + button.setMargin(new Insets(0, 1, 1, 3)); + return button; + } + + /** + * Creates a new property change listener. + * + * @return A new property change listener. + */ + public PropertyChangeListener createPropertyChangeListener() + { + return new MetalPropertyChangeListener(); + } + + public void paint(Graphics g, JComponent c) + { + // do nothing, the button and text field are painted elsewhere + } + + /** + * Updates the button and text field to reflect a change in the 'editable' + * property. + * + * @param e the event. + * + * @deprecated 1.4 + */ + protected void editablePropertyChanged(PropertyChangeEvent e) + { + if (arrowButton instanceof MetalComboBoxButton) + { + MetalComboBoxButton b = (MetalComboBoxButton) arrowButton; + b.setIconOnly(comboBox.isEditable()); + } + if (comboBox.isEditable()) { - instance = new MetalComboBoxUI(); - instances.put(component, instance); + arrowButton.setText(null); + if (editor != null) + editor.setVisible(true); } else - instance = (MetalComboBoxUI) o; - - return instance; + { + String text = ""; + Object selected = comboBox.getSelectedItem(); + if (selected != null) + text = selected.toString(); + arrowButton.setText(text); + if (editor != null) + editor.setVisible(true); + } + } + + /** + * Creates a new layout manager for the UI delegate. + * + * @return A new layout manager. + */ + protected LayoutManager createLayoutManager() + { + return new MetalComboBoxLayoutManager(); + } + + /** + * Not used in Classpath. + * + * @deprecated 1.4 + */ + protected void removeListeners() + { + // no longer used in JDK 1.4 + } + + /** + * Returns the minimum size for the combo. + * + * @param c the component + * + * @return The minimum size for the combo box. + */ + public Dimension getMinimumSize(JComponent c) + { + Dimension d = getDisplaySize(); + MetalComboBoxButton b = (MetalComboBoxButton) arrowButton; + Insets insets = b.getInsets(); + int insetsH = insets.top + insets.bottom; + int insetsW = insets.left + insets.right; + if (!comboBox.isEditable()) + { + Icon icon = b.getComboIcon(); + int iconWidth = icon.getIconWidth() + 6; + return new Dimension(d.width + insetsW + iconWidth, d.height + insetsH); + } + else + // FIXME: the following dimensions pass most of the Mauve tests, but + // I don't yet understand the logic behind this...it is probably wrong + return new Dimension(d.width + insetsW + (d.height + insetsH) - 4, + d.height + insetsH + 1); } + } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalDesktopIconUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalDesktopIconUI.java index 00870545bc6..ecbb76e6e7a 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalDesktopIconUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalDesktopIconUI.java @@ -39,9 +39,13 @@ exception statement from your version. */ package javax.swing.plaf.metal; import javax.swing.JComponent; +import javax.swing.JInternalFrame; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicDesktopIconUI; +/** + * A UI delegate for the {@link JInternalFrame.JDesktopIcon} component. + */ public class MetalDesktopIconUI extends BasicDesktopIconUI { @@ -51,7 +55,7 @@ public class MetalDesktopIconUI private static MetalDesktopIconUI instance = null; /** - * Constructs a new instance of MetalDesktopIconUI. + * Constructs a new instance of <code>MetalDesktopIconUI</code>. */ public MetalDesktopIconUI() { @@ -59,11 +63,11 @@ public class MetalDesktopIconUI } /** - * Returns an instance of MetalDesktopIconUI. + * Returns a shared instance of <code>MetalDesktopIconUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalDesktopIconUI + * @return A shared instance of <code>MetalDesktopIconUI</code>. */ public static ComponentUI createUI(JComponent component) { diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java new file mode 100644 index 00000000000..3a2e1c13508 --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java @@ -0,0 +1,430 @@ +/* MetalFileChooserUI.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.plaf.metal; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.AbstractListModel; +import javax.swing.ComboBoxModel; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.JList; +import javax.swing.UIManager; +import javax.swing.filechooser.FileFilter; +import javax.swing.filechooser.FileSystemView; +import javax.swing.filechooser.FileView; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicFileChooserUI; + + +/** + * A UI delegate for the {@link JFileChooser} component. This class is only + * partially implemented and is not usable yet. + */ +public class MetalFileChooserUI extends BasicFileChooserUI +{ + /** + * A combo box model containing the selected directory and all its parent + * directories. + */ + protected class DirectoryComboBoxModel extends AbstractListModel + implements ComboBoxModel + { + /** Storage for the items in the model. */ + private List items; + + /** The index of the selected item. */ + private int selectedIndex; + + /** + * Creates a new model. + */ + public DirectoryComboBoxModel() + { + items = new java.util.ArrayList(); + selectedIndex = -1; + } + + /** + * Returns the number of items in the model. + * + * @return The number of items in the model. + */ + public int getSize() + { + return items.size(); + } + + /** + * Returns the item at the specified index. + * + * @param index the item index. + * + * @return The item. + */ + public Object getElementAt(int index) + { + return items.get(index); + } + + /** + * Returns the depth of the item at the given <code>index</code>. + * + * @param index the item index. + * + * @return The depth. + */ + public int getDepth(int index) + { + return Math.max(index, 0); + } + + /** + * Returns the selected item, or <code>null</code> if no item is selected. + * + * @return The selected item, or <code>null</code>. + */ + public Object getSelectedItem() + { + if (selectedIndex >= 0) + return items.get(selectedIndex); + else + return null; + } + + /** + * Sets the selected item. This clears all the directories from the + * existing list, and repopulates it with the new selected directory + * and all its parent directories. + * + * @param selectedDirectory the selected directory. + */ + public void setSelectedItem(Object selectedDirectory) + { + items.clear(); + FileSystemView fsv = getFileChooser().getFileSystemView(); + File parent = (File) selectedDirectory; + while (parent != null) + { + items.add(0, parent); + parent = fsv.getParentDirectory(parent); + } + selectedIndex = items.indexOf(selectedDirectory); + fireContentsChanged(this, 0, items.size() - 1); + } + + } + + /** + * Handles changes to the selection in the directory combo box. + */ + protected class DirectoryComboBoxAction extends AbstractAction + { + /** + * Creates a new action. + */ + protected DirectoryComboBoxAction() + { + // Nothing to do here. + } + + /** + * Handles the action event. + * + * @param e the event. + */ + public void actionPerformed(ActionEvent e) + { + JFileChooser fc = getFileChooser(); + fc.setCurrentDirectory((File) directoryModel.getSelectedItem()); + } + } + + /** + * A renderer for the files and directories in the file chooser. + */ + protected class FileRenderer extends DefaultListCellRenderer + { + + /** + * Creates a new renderer. + */ + protected FileRenderer() + { + // Nothing to do here. + } + + /** + * Returns a component that can render the specified value. + * + * @param list the list. + * @param value the value (a {@link File}). + * @param index the index. + * @param isSelected is the item selected? + * @param cellHasFocus does the item have the focus? + * + * @return The renderer. + */ + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) + { + FileView v = getFileView(getFileChooser()); + File f = (File) value; + setText(v.getName(f)); + setIcon(v.getIcon(f)); + if (isSelected) + { + setBackground(list.getSelectionBackground()); + setForeground(list.getSelectionForeground()); + } + else + { + setBackground(list.getBackground()); + setForeground(list.getForeground()); + } + + setEnabled(list.isEnabled()); + setFont(list.getFont()); + + if (cellHasFocus) + setBorder(UIManager.getBorder("List.focusCellHighlightBorder")); + else + setBorder(noFocusBorder); + return this; + } + } + + /** + * A combo box model for the file selection filters. + */ + protected class FilterComboBoxModel + extends AbstractListModel + implements ComboBoxModel, PropertyChangeListener + { + + /** Storage for the filters in the model. */ + protected FileFilter[] filters; + + /** The index of the selected file filter. */ + private int selectedIndex; + + /** + * Creates a new model. + */ + protected FilterComboBoxModel() + { + filters = new FileFilter[1]; + filters[0] = getAcceptAllFileFilter(getFileChooser()); + selectedIndex = 0; + } + + /** + * Handles property changes. + * + * @param e the property change event. + */ + public void propertyChange(PropertyChangeEvent e) + { + if (e.getPropertyName().equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)) + { + selectedIndex = -1; + FileFilter selected = (FileFilter) e.getNewValue(); + for (int i = 0; i < filters.length; i++) + if (filters[i].equals(selected)) + selectedIndex = i; + fireContentsChanged(this, -1, -1); + } + else if (e.getPropertyName().equals( + JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY)) + { + // repopulate list + JFileChooser fc = getFileChooser(); + FileFilter[] choosableFilters = fc.getChoosableFileFilters(); + filters = choosableFilters; + fireContentsChanged(this, 0, filters.length); + } + } + + /** + * Sets the selected filter. + * + * @param filter the filter. + */ + public void setSelectedItem(Object filter) + { + // change the filter in the file chooser and let the property change + // event trigger the change to the selected item + getFileChooser().setFileFilter((FileFilter) filter); + } + + /** + * Returns the selected file filter. + * + * @return The selected file filter. + */ + public Object getSelectedItem() + { + if (selectedIndex >= 0) + return filters[selectedIndex]; + return null; + } + + /** + * Returns the number of items in the model. + * + * @return The number of items in the model. + */ + public int getSize() + { + return filters.length; + } + + /** + * Returns the item at the specified index. + * + * @param index the item index. + * + * @return The item at the specified index. + */ + public Object getElementAt(int index) + { + return filters[index]; + } + + } + + /** + * A renderer for the items in the file filter combo box. + */ + public class FilterComboBoxRenderer extends DefaultListCellRenderer + { + /** + * Creates a new renderer. + */ + public FilterComboBoxRenderer() + { + // Nothing to do here. + } + + /** + * Returns a component that can be used to paint the given value within + * the list. + * + * @param list the list. + * @param value the value (a {@link FileFilter}). + * @param index the item index. + * @param isSelected is the item selected? + * @param cellHasFocus does the list cell have focus? + * + * @return A component. + */ + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) + { + FileFilter filter = (FileFilter) value; + return super.getListCellRendererComponent(list, filter.getDescription(), + index, isSelected, cellHasFocus); + } + } + + /** The model for the directory combo box. */ + DirectoryComboBoxModel directoryModel; + + /** + * A factory method that returns a UI delegate for the specified + * component. + * + * @param c the component (which should be a {@link JFileChooser}). + */ + public static ComponentUI createUI(JComponent c) + { + JFileChooser chooser = (JFileChooser) c; + return new MetalFileChooserUI(chooser); + } + + /** + * Creates a new instance of this UI delegate. + * + * @param filechooser the file chooser component. + */ + public MetalFileChooserUI(JFileChooser filechooser) + { + super(filechooser); + } + + /** + * Creates and returns a new instance of {@link DirectoryComboBoxModel}. + * + * @return A new instance of {@link DirectoryComboBoxModel}. + */ + protected MetalFileChooserUI.DirectoryComboBoxModel + createDirectoryComboBoxModel(JFileChooser fc) + { + return new DirectoryComboBoxModel(); + } + + /** + * Creates and returns a new instance of {@link FilterComboBoxModel}. + * + * @return A new instance of {@link FilterComboBoxModel}. + */ + protected FilterComboBoxModel createFilterComboBoxModel() + { + return new FilterComboBoxModel(); + } + + /** + * Creates and returns a new instance of {@link FilterComboBoxRenderer}. + * + * @return A new instance of {@link FilterComboBoxRenderer}. + */ + protected MetalFileChooserUI.FilterComboBoxRenderer + createFilterComboBoxRenderer() + { + return new FilterComboBoxRenderer(); + } + +} diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java b/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java index d8c77432653..6f4feccfc37 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java @@ -43,9 +43,11 @@ import java.awt.Component; import java.awt.Graphics; import java.io.Serializable; +import javax.swing.AbstractButton; import javax.swing.Icon; import javax.swing.JCheckBox; import javax.swing.JCheckBoxMenuItem; +import javax.swing.JFileChooser; import javax.swing.JInternalFrame; import javax.swing.JRadioButton; import javax.swing.JRadioButtonMenuItem; @@ -75,6 +77,7 @@ public class MetalIconFactory implements Serializable */ public CheckBoxMenuItemIcon() { + // Nothing to do here. } /** @@ -137,6 +140,385 @@ public class MetalIconFactory implements Serializable } /** + * An icon used for the "detail view" button on a {@link JFileChooser} under + * the {@link MetalLookAndFeel}. + * + * @see MetalIconFactory#getFileChooserDetailViewIcon() + */ + private static class FileChooserDetailViewIcon implements Icon, Serializable + { + + /** + * Creates a new icon. + */ + public FileChooserDetailViewIcon() + { + // Nothing to do here. + } + + /** + * Returns the width of the icon, in pixels. + * + * @return The width of the icon. + */ + public int getIconWidth() + { + return 18; + } + + /** + * Returns the height of the icon, in pixels. + * + * @return The height of the icon. + */ + public int getIconHeight() + { + return 18; + } + + /** + * Paints the icon using colors from the {@link MetalLookAndFeel}. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate for the top-left of the icon. + * @param y the y-coordinate for the top-left of the icon. + */ + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color savedColor = g.getColor(); + g.setColor(MetalLookAndFeel.getBlack()); + + // file 1 outline + g.drawLine(x + 2, y + 2, x + 5, y + 2); + g.drawLine(x + 6, y + 3, x + 6, y + 7); + g.drawLine(x + 2, y + 7, x + 6, y + 7); + g.drawLine(x + 2, y + 2, x + 2, y + 7); + + // file 2 outline + g.drawLine(x + 2, y + 10, x + 5, y + 10); + g.drawLine(x + 6, y + 11, x + 6, y + 15); + g.drawLine(x + 2, y + 15, x + 6, y + 15); + g.drawLine(x + 2, y + 10, x + 2, y + 15); + + // detail lines + g.drawLine(x + 8, y + 5, x + 15, y + 5); + g.drawLine(x + 8, y + 13, x + 15, y + 13); + + // fill files + g.setColor(MetalLookAndFeel.getPrimaryControl()); + g.fillRect(x + 3, y + 3, 3, 4); + g.fillRect(x + 3, y + 11, 3, 4); + + // highlight files + g.setColor(MetalLookAndFeel.getPrimaryControlHighlight()); + g.drawLine(x + 4, y + 4, x + 4, y + 5); + g.drawLine(x + 4, y + 12, x + 4, y + 13); + + g.setColor(savedColor); + } + } + + /** + * An icon used for the "home folder" button on a {@link JFileChooser} under + * the {@link MetalLookAndFeel}. + * + * @see MetalIconFactory#getFileChooserHomeFolderIcon() + */ + private static class FileChooserHomeFolderIcon implements Icon, Serializable + { + + /** + * Creates a new icon. + */ + public FileChooserHomeFolderIcon() + { + // Nothing to do here. + } + + /** + * Returns the width of the icon, in pixels. + * + * @return The width of the icon. + */ + public int getIconWidth() + { + return 18; + } + + /** + * Returns the height of the icon, in pixels. + * + * @return The height of the icon. + */ + public int getIconHeight() + { + return 18; + } + + /** + * Paints the icon using colors from the {@link MetalLookAndFeel}. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate for the top-left of the icon. + * @param y the y-coordinate for the top-left of the icon. + */ + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color savedColor = g.getColor(); + g.setColor(MetalLookAndFeel.getBlack()); + + // roof + g.drawLine(x + 1, y + 8, x + 8, y + 1); + g.drawLine(x + 8, y + 1, x + 15, y + 8); + + // base of house + g.drawLine(x + 3, y + 6, x + 3, y + 15); + g.drawLine(x + 3, y + 15, x + 13, y + 15); + g.drawLine(x + 13, y + 6, x + 13, y + 15); + + // door frame + g.drawLine(x + 6, y + 9, x + 6, y + 15); + g.drawLine(x + 6, y + 9, x + 10, y + 9); + g.drawLine(x + 10, y + 9, x + 10, y + 15); + + // chimney + g.drawLine(x + 11, y + 2, x + 11, y + 4); + g.drawLine(x + 12, y + 2, x + 12, y + 5); + + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + + // roof paint + int xx = x + 8; + for (int i = 0; i < 4; i++) + g.drawLine(xx - i, y + 2 + i, xx + i, y + 2 + i); + g.fillRect(x + 4, y + 6, 9, 2); + + // door knob + g.drawLine(x + 9, y + 12, x + 9, y + 12); + + // house paint + g.setColor(MetalLookAndFeel.getPrimaryControl()); + g.drawLine(x + 4, y + 8, x + 12, y + 8); + g.fillRect(x + 4, y + 9, 2, 6); + g.fillRect(x + 11, y + 9, 2, 6); + + g.setColor(savedColor); + } + } + + /** + * An icon used for the "list view" button on a {@link JFileChooser} under + * the {@link MetalLookAndFeel}. + * + * @see MetalIconFactory#getFileChooserListViewIcon() + */ + private static class FileChooserListViewIcon implements Icon, Serializable + { + /** + * Creates a new icon. + */ + public FileChooserListViewIcon() + { + // Nothing to do here. + } + + /** + * Returns the width of the icon, in pixels. + * + * @return The width of the icon. + */ + public int getIconWidth() + { + return 18; + } + + /** + * Returns the height of the icon, in pixels. + * + * @return The height of the icon. + */ + public int getIconHeight() + { + return 18; + } + + /** + * Paints the icon using colors from the {@link MetalLookAndFeel}. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate for the top-left of the icon. + * @param y the y-coordinate for the top-left of the icon. + */ + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color savedColor = g.getColor(); + g.setColor(MetalLookAndFeel.getBlack()); + + // file 1 outline + g.drawLine(x + 2, y + 2, x + 5, y + 2); + g.drawLine(x + 6, y + 3, x + 6, y + 7); + g.drawLine(x + 2, y + 7, x + 6, y + 7); + g.drawLine(x + 2, y + 2, x + 2, y + 7); + + // file 2 outline + g.drawLine(x + 2, y + 10, x + 5, y + 10); + g.drawLine(x + 6, y + 11, x + 6, y + 15); + g.drawLine(x + 2, y + 15, x + 6, y + 15); + g.drawLine(x + 2, y + 10, x + 2, y + 15); + + // file 3 outline + g.drawLine(x + 10, y + 2, x + 13, y + 2); + g.drawLine(x + 14, y + 3, x + 14, y + 7); + g.drawLine(x + 10, y + 7, x + 14, y + 7); + g.drawLine(x + 10, y + 2, x + 10, y + 7); + + // file 4 outline + g.drawLine(x + 10, y + 10, x + 13, y + 10); + g.drawLine(x + 14, y + 11, x + 14, y + 15); + g.drawLine(x + 10, y + 15, x + 14, y + 15); + g.drawLine(x + 10, y + 10, x + 10, y + 15); + + g.drawLine(x + 8, y + 5, x + 8, y + 5); + g.drawLine(x + 8, y + 13, x + 8, y + 13); + g.drawLine(x + 16, y + 5, x + 16, y + 5); + g.drawLine(x + 16, y + 13, x + 16, y + 13); + + // fill files + g.setColor(MetalLookAndFeel.getPrimaryControl()); + g.fillRect(x + 3, y + 3, 3, 4); + g.fillRect(x + 3, y + 11, 3, 4); + g.fillRect(x + 11, y + 3, 3, 4); + g.fillRect(x + 11, y + 11, 3, 4); + + // highlight files + g.setColor(MetalLookAndFeel.getPrimaryControlHighlight()); + g.drawLine(x + 4, y + 4, x + 4, y + 5); + g.drawLine(x + 4, y + 12, x + 4, y + 13); + g.drawLine(x + 12, y + 4, x + 12, y + 5); + g.drawLine(x + 12, y + 12, x + 12, y + 13); + + g.setColor(savedColor); + } + } + + /** + * An icon used for the "new folder" button on a {@link JFileChooser} under + * the {@link MetalLookAndFeel}. + * + * @see MetalIconFactory#getFileChooserNewFolderIcon() + */ + private static class FileChooserNewFolderIcon implements Icon, Serializable + { + /** + * Creates a new icon. + */ + public FileChooserNewFolderIcon() + { + // Nothing to do here. + } + + /** + * Returns the width of the icon, in pixels. + * + * @return The width of the icon. + */ + public int getIconWidth() + { + return 18; + } + + /** + * Returns the height of the icon, in pixels. + * + * @return The height of the icon. + */ + public int getIconHeight() + { + return 18; + } + + /** + * Paints the icon using colors from the {@link MetalLookAndFeel}. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate for the top-left of the icon. + * @param y the y-coordinate for the top-left of the icon. + */ + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color savedColor = g.getColor(); + g.setColor(MetalLookAndFeel.getBlack()); + + g.drawLine(x + 2, y + 5, x + 9, y + 5); + g.drawLine(x + 10, y + 6, x + 15, y + 6); + g.drawLine(x + 15, y + 5, x + 15, y + 14); + g.drawLine(x + 2, y + 14, x + 15, y + 14); + g.drawLine(x + 1, y + 6, x + 1, y + 14); + + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + g.drawLine(x + 11, y + 3, x + 15, y + 3); + g.drawLine(x + 10, y + 4, x + 15, y + 4); + + g.setColor(MetalLookAndFeel.getPrimaryControl()); + g.fillRect(x + 3, y + 7, 7, 7); + g.fillRect(x + 10, y + 8, 5, 6); + g.drawLine(x + 10, y + 5, x + 14, y + 5); + + g.setColor(MetalLookAndFeel.getPrimaryControlHighlight()); + g.drawLine(x + 10, y + 7, x + 14, y + 7); + g.drawLine(x + 2, y + 6, x + 9, y + 6); + g.drawLine(x + 2, y + 6, x + 2, y + 13); + g.setColor(savedColor); + } + } + + /** + * An icon used for the "up folder" button on a {@link JFileChooser} under + * the {@link MetalLookAndFeel}. + * + * @see MetalIconFactory#getFileChooserNewFolderIcon() + */ + private static class FileChooserUpFolderIcon extends FileChooserNewFolderIcon + implements Icon, Serializable + { + /** + * Creates a new icon. + */ + public FileChooserUpFolderIcon() + { + // Nothing to do here. + } + + /** + * Paints the icon using colors from the {@link MetalLookAndFeel}. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate for the top-left of the icon. + * @param y the y-coordinate for the top-left of the icon. + */ + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color savedColor = g.getColor(); + + // draw the folder + super.paintIcon(c, g, x, y); + + // now draw the up arrow + g.setColor(MetalLookAndFeel.getBlack()); + g.drawLine(x + 8, y + 9, x + 8, y + 16); + int xx = x + 8; + for (int i = 0; i < 4; i++) + g.drawLine(xx - i, y + 9 + i, xx + i, y + 9 + i); + g.setColor(savedColor); + } + } + + /** * An icon representing a file (drawn as a piece of paper with the top-right * corner turned down). */ @@ -153,13 +535,15 @@ public class MetalIconFactory implements Serializable } /** - * Returns the height of the icon, in pixels. + * Returns the height of the icon, in pixels. The height returned is + * <code>16</code> plus the value returned by + * {@link #getAdditionalHeight()}. * * @return The height of the icon. */ public int getIconHeight() { - return 16; + return 16 + getAdditionalHeight(); } /** @@ -192,9 +576,11 @@ public class MetalIconFactory implements Serializable } /** - * Returns the additional height (???). + * Returns the additional height for the icon. The + * {@link #getIconHeight()} method adds this value to the icon height it + * returns. Subclasses can override this method to adjust the icon height. * - * @return The additional height. + * @return The additional height (<code>0</code> unless overridden). */ public int getAdditionalHeight() { @@ -228,13 +614,15 @@ public class MetalIconFactory implements Serializable } /** - * Returns the height of the icon, in pixels. + * Returns the height of the icon, in pixels. The height returned is + * <code>16</code> plus the value returned by + * {@link #getAdditionalHeight()}. * * @return The height of the icon. */ public int getIconHeight() { - return 16; + return 16 + getAdditionalHeight(); } /** @@ -265,9 +653,11 @@ public class MetalIconFactory implements Serializable } /** - * Returns the additional height (???). + * Returns the additional height for the icon. The + * {@link #getIconHeight()} method adds this value to the icon height it + * returns. Subclasses can override this method to adjust the icon height. * - * @return The additional height. + * @return The additional height (<code>0</code> unless overridden). */ public int getAdditionalHeight() { @@ -285,29 +675,77 @@ public class MetalIconFactory implements Serializable } } - + /** - * An {@link Icon} implementation for {@link JCheckBox}es in the - * Metal Look & Feel. - * - * @author Roman Kennke (roman@kennke.org) + * An icon used by the {@link MetalInternalFrameUI} class when the frame + * is displayed as a palette. + * + * @since 1.3 */ - static class RadioButtonIcon - implements Icon, UIResource, Serializable + public static class PaletteCloseIcon + implements Icon, Serializable, UIResource { /** - * Draws the check in the RadioButton. - * - * @param c the component to draw on - * @param g the Graphics context to draw with + * Returns the width of the icon, in pixels. + * + * @return The width of the icon. */ - protected void drawCheck(Component c, Graphics g) + public int getIconWidth() { - g.setColor(MetalLookAndFeel.getBlack()); - g.fillRect(4, 3, 4, 6); - g.drawLine(3, 4, 3, 7); - g.drawLine(8, 4, 8, 7); + return 7; } + + /** + * Returns the height of the icon, in pixels. + * + * @return The height of the icon. + */ + public int getIconHeight() + { + return 7; + } + + /** + * Paints the icon using colors from the {@link MetalLookAndFeel}. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate for the top-left of the icon. + * @param y the y-coordinate for the top-left of the icon. + */ + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color savedColor = g.getColor(); + AbstractButton button = (AbstractButton) c; + if (button.getModel().isPressed()) + g.setColor(MetalLookAndFeel.getBlack()); + else + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.fillRect(x + 2, y + 2, 3, 3); + g.drawLine(x + 1, y, x + 1, y + 2); + g.drawLine(x, y + 1, x + 2, y + 1); + g.drawLine(x + 5, y, x + 5, y + 2); + g.drawLine(x + 4, y + 1, x + 6, y + 1); + g.drawLine(x + 1, y + 4, x + 1, y + 6); + g.drawLine(x, y + 5, x + 2, y + 5); + g.drawLine(x + 5, y + 4, x + 5, y + 6); + g.drawLine(x + 4, y + 5, x + 6, y + 5); + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(x + 2, y + 6, x + 3, y + 5); + g.drawLine(x + 5, y + 3, x + 6, y + 2); + g.drawLine(x + 6, y + 6, x + 6, y + 6); + g.setColor(savedColor); + } + } + + /** + * An {@link Icon} implementation for {@link JCheckBox}es in the + * Metal Look & Feel. + * + * @author Roman Kennke (roman@kennke.org) + */ + static class RadioButtonIcon implements Icon, UIResource, Serializable + { /** * Returns the width of the icon in pixels. @@ -330,70 +768,104 @@ public class MetalIconFactory implements Serializable } /** - * Paints the icon. This first paints the border of the RadioButton and - * if the CheckBox is selected it calls {@link #drawCheck} to draw - * the check. + * Paints the icon, taking into account whether or not the component is + * enabled, selected and/or armed. * - * @param c the Component to draw on (gets casted to JCheckBox) + * @param c the Component to draw on (must be an instance of + * {@link JRadioButton}) * @param g the Graphics context to draw with * @param x the X position * @param y the Y position */ - public void paintIcon(Component c, Graphics g, int x, int y) - { - Color dark = MetalLookAndFeel.getControlDarkShadow(); - Color light = MetalLookAndFeel.getWhite(); - g.translate(x, y); - - // The light 'circle' - g.setColor(light); - g.drawLine(4, 1, 10, 1); - g.drawLine(2, 2, 3, 2); - g.drawLine(8, 2, 11, 2); - g.drawLine(2, 3, 2, 3); - g.drawLine(11, 2, 11, 9); - g.drawLine(1, 4, 1, 7); - g.drawLine(12, 4, 12, 7); - g.drawLine(2, 8, 2, 11); - g.drawLine(11, 8, 11, 9); - g.drawLine(10, 10, 10, 10); - g.drawLine(2, 11, 9, 11); - g.drawLine(4, 12, 7, 12); - - // The dark 'circle' - g.setColor(dark); - g.drawLine(4, 0, 7, 0); - g.drawLine(2, 1, 3, 1); - g.drawLine(8, 1, 9, 1); - g.drawLine(1, 2, 1, 3); - g.drawLine(10, 2, 10, 3); - g.drawLine(0, 4, 0, 7); - g.drawLine(11, 4, 11, 7); - g.drawLine(1, 8, 1, 9); - g.drawLine(10, 8, 10, 9); - g.drawLine(2, 10, 3, 10); - g.drawLine(8, 10, 9, 10); - g.drawLine(4, 11, 7, 11); - - JRadioButton rb = (JRadioButton) c; - if (rb.isSelected()) - drawCheck(c, g); - - g.translate(-x, -y); - } + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color savedColor = g.getColor(); + JRadioButton b = (JRadioButton) c; + + // draw outer circle + if (b.isEnabled()) + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + else + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(x + 2, y + 1, x + 3, y + 1); + g.drawLine(x + 4, y, x + 7, y); + g.drawLine(x + 8, y + 1, x + 9, y + 1); + g.drawLine(x + 10, y + 2, x + 10, y + 3); + g.drawLine(x + 11, y + 4, x + 11, y + 7); + g.drawLine(x + 10, y + 8, x + 10, y + 9); + g.drawLine(x + 8, y + 10, x + 9, y + 10); + g.drawLine(x + 4, y + 11, x + 7, y + 11); + g.drawLine(x + 2, y + 10, x + 3, y + 10); + g.drawLine(x + 1, y + 9, x + 1, y + 8); + g.drawLine(x, y + 7, x, y + 4); + g.drawLine(x + 1, y + 2, x + 1, y + 3); + + if (b.getModel().isArmed()) + { + g.setColor(MetalLookAndFeel.getControlShadow()); + g.drawLine(x + 4, y + 1, x + 7, y + 1); + g.drawLine(x + 4, y + 10, x + 7, y + 10); + g.drawLine(x + 1, y + 4, x + 1, y + 7); + g.drawLine(x + 10, y + 4, x + 10, y + 7); + g.fillRect(x + 2, y + 2, 8, 8); + } + else + { + // only draw inner highlight if not filled + if (b.isEnabled()) + { + g.setColor(MetalLookAndFeel.getWhite()); + + g.drawLine(x + 2, y + 8, x + 2, y + 9); + g.drawLine(x + 1, y + 4, x + 1, y + 7); + g.drawLine(x + 2, y + 2, x + 2, y + 3); + g.drawLine(x + 3, y + 2, x + 3, y + 2); + g.drawLine(x + 4, y + 1, x + 7, y + 1); + g.drawLine(x + 8, y + 2, x + 9, y + 2); + } + } + + // draw outer highlight + if (b.isEnabled()) + { + g.setColor(MetalLookAndFeel.getWhite()); + + // outer + g.drawLine(x + 10, y + 1, x + 10, y + 1); + g.drawLine(x + 11, y + 2, x + 11, y + 3); + g.drawLine(x + 12, y + 4, x + 12, y + 7); + g.drawLine(x + 11, y + 8, x + 11, y + 9); + g.drawLine(x + 10, y + 10, x + 10, y + 10); + g.drawLine(x + 8, y + 11, x + 9, y + 11); + g.drawLine(x + 4, y + 12, x + 7, y + 12); + g.drawLine(x + 2, y + 11, x + 3, y + 11); + } + + if (b.isSelected()) + { + if (b.isEnabled()) + g.setColor(MetalLookAndFeel.getBlack()); + else + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(x + 4, y + 3, x + 7, y + 3); + g.fillRect(x + 3, y + 4, 6, 4); + g.drawLine(x + 4, y + 8, x + 7, y + 8); + } + g.setColor(savedColor); + } } /** * An icon displayed for {@link JRadioButtonMenuItem} components. */ - private static class RadioButtonMenuItemIcon - implements Icon, Serializable + private static class RadioButtonMenuItemIcon implements Icon, Serializable { /** * Creates a new icon instance. */ public RadioButtonMenuItemIcon() - { + { + // Nothing to do here. } /** @@ -463,8 +935,7 @@ public class MetalIconFactory implements Serializable * The icon used to display the thumb control on a horizontally oriented * {@link JSlider} component. */ - private static class HorizontalSliderThumbIcon - implements Icon, Serializable + private static class HorizontalSliderThumbIcon implements Icon, Serializable { /** @@ -472,6 +943,7 @@ public class MetalIconFactory implements Serializable */ public HorizontalSliderThumbIcon() { + // Nothing to do here. } /** @@ -505,13 +977,19 @@ public class MetalIconFactory implements Serializable */ public void paintIcon(Component c, Graphics g, int x, int y) { + boolean enabled = false; boolean focus = false; - if (c != null) - focus = c.hasFocus(); - // TODO: pick up the colors from the look and feel + if (c != null) + { + enabled = c.isEnabled(); + focus = c.hasFocus(); + } // draw the outline - g.setColor(Color.black); + if (enabled) + g.setColor(MetalLookAndFeel.getBlack()); + else + g.setColor(MetalLookAndFeel.getControlDarkShadow()); g.drawLine(x + 1, y, x + 13, y); g.drawLine(x + 14, y + 1, x + 14, y + 7); g.drawLine(x + 14, y + 8, x + 7, y + 15); @@ -519,8 +997,11 @@ public class MetalIconFactory implements Serializable g.drawLine(x, y + 7, x, y + 1); // fill the icon - g.setColor(focus ? new Color(153, 153, 204) : new Color(204, 204, 204)); // medium - g.fillRect(x + 2, y + 2, 12, 7); + if (focus) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getControl()); + g.fillRect(x + 1, y + 2, 13, 7); g.drawLine(x + 2, y + 9, x + 12, y + 9); g.drawLine(x + 3, y + 10, x + 11, y + 10); g.drawLine(x + 4, y + 11, x + 10, y + 11); @@ -528,33 +1009,42 @@ public class MetalIconFactory implements Serializable g.drawLine(x + 6, y + 13, x + 8, y + 13); g.drawLine(x + 7, y + 14, x + 7, y + 14); - // draw highlights - g.setColor(focus ? new Color(204, 204, 255) : new Color(255, 255, 255)); // light - g.drawLine(x + 1, y + 1, x + 13, y + 1); - g.drawLine(x + 1, y + 2, x + 1, y + 8); - g.drawLine(x + 2, y + 2, x + 2, y + 2); - g.drawLine(x + 6, y + 2, x + 6, y + 2); - g.drawLine(x + 10, y + 2, x + 10, y + 2); - - g.drawLine(x + 4, y + 4, x + 4, y + 4); - g.drawLine(x + 8, y + 4, x + 8, y + 4); + // if the slider is enabled, draw dots and highlights + if (c.isEnabled()) + { + if (focus) + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + else + g.setColor(MetalLookAndFeel.getBlack()); + g.drawLine(x + 3, y + 3, x + 3, y + 3); + g.drawLine(x + 7, y + 3, x + 7, y + 3); + g.drawLine(x + 11, y + 3, x + 11, y + 3); - g.drawLine(x + 2, y + 6, x + 2, y + 6); - g.drawLine(x + 6, y + 6, x + 6, y + 6); - g.drawLine(x + 10, y + 6, x + 10, y + 6); + g.drawLine(x + 5, y + 5, x + 5, y + 5); + g.drawLine(x + 9, y + 5, x + 9, y + 5); - // draw dots - g.setColor(focus ? new Color(102, 102, 153) : Color.black); // dark - g.drawLine(x + 3, y + 3, x + 3, y + 3); - g.drawLine(x + 7, y + 3, x + 7, y + 3); - g.drawLine(x + 11, y + 3, x + 11, y + 3); + g.drawLine(x + 3, y + 7, x + 3, y + 7); + g.drawLine(x + 7, y + 7, x + 7, y + 7); + g.drawLine(x + 11, y + 7, x + 11, y + 7); - g.drawLine(x + 5, y + 5, x + 5, y + 5); - g.drawLine(x + 9, y + 5, x + 9, y + 5); + // draw highlights + if (focus) + g.setColor(MetalLookAndFeel.getPrimaryControl()); + else + g.setColor(MetalLookAndFeel.getWhite()); + g.drawLine(x + 1, y + 1, x + 13, y + 1); + g.drawLine(x + 1, y + 2, x + 1, y + 8); + g.drawLine(x + 2, y + 2, x + 2, y + 2); + g.drawLine(x + 6, y + 2, x + 6, y + 2); + g.drawLine(x + 10, y + 2, x + 10, y + 2); + + g.drawLine(x + 4, y + 4, x + 4, y + 4); + g.drawLine(x + 8, y + 4, x + 8, y + 4); - g.drawLine(x + 3, y + 7, x + 3, y + 7); - g.drawLine(x + 7, y + 7, x + 7, y + 7); - g.drawLine(x + 11, y + 7, x + 11, y + 7); + g.drawLine(x + 2, y + 6, x + 2, y + 6); + g.drawLine(x + 6, y + 6, x + 6, y + 6); + g.drawLine(x + 10, y + 6, x + 10, y + 6); + } } } @@ -563,7 +1053,7 @@ public class MetalIconFactory implements Serializable * An icon used for the 'close' button in the title frame of a * {@link JInternalFrame}. */ - private static class InternalFrameCloseIcon implements Icon, Serializable + private static class InternalFrameCloseIcon implements Icon, Serializable { /** The icon size in pixels. */ private int size; @@ -601,25 +1091,53 @@ public class MetalIconFactory implements Serializable /** * Paints the icon. * - * @param c the component. + * @param c the component (an {@link JInternalFrame} is expected). * @param g the graphics device. * @param x the x-coordinate. * @param y the y-coordinate. */ public void paintIcon(Component c, Graphics g, int x, int y) { - // draw the gray areas first - g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + Color savedColor = g.getColor(); + AbstractButton b = (AbstractButton) c; + + // fill the interior + if (b.getModel().isPressed()) + // FIXME: also need to take into account whether the internal frame is + // selected + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getPrimaryControl()); + g.fillRect(x + 2, y + 2, 10, 10); + + // draw the outline box and the cross + if (b.getModel().isPressed()) + g.setColor(MetalLookAndFeel.getBlack()); + else + { + // FIXME: also need to take into account whether the internal frame is + // selected + boolean selected = true; + if (selected) + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + else + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + } g.drawLine(x + 1, y + 1, x + 13, y + 1); g.drawLine(x + 1, y + 2, x + 1, y + 12); g.drawLine(x + 1, y + 13, x + 13, y + 13); g.drawLine(x + 13, y + 2, x + 13, y + 12); + g.drawLine(x + 2, y + 12, x + 2, y + 12); + g.drawLine(x + 12, y + 2, x + 12, y + 2); g.fillRect(x + 4, y + 4, 2, 2); - g.fillRect(x + 4, y + 9, 2, 2); - g.fillRect(x + 9, y + 4, 2, 2); - g.fillRect(x + 9, y + 9, 2, 2); - g.fillRect(x + 5, y + 5, 5, 5); + g.fillRect(x + 5, y + 5, 4, 4); + g.drawLine(x + 9, y + 4, x + 10, y + 4); + g.drawLine(x + 9, y + 4, x + 9, y + 5); + g.drawLine(x + 4, y + 9, x + 4, y + 10); + g.drawLine(x + 4, y + 9, x + 5, y + 9); + g.drawLine(x + 9, y + 8, x + 9, y + 10); + g.drawLine(x + 8, y + 9, x + 10, y + 9); g.setColor(MetalLookAndFeel.getBlack()); g.drawLine(x, y, x + 13, y); @@ -635,20 +1153,24 @@ public class MetalIconFactory implements Serializable g.drawLine(x + 1, y + 14, x + 14, y + 14); g.drawLine(x + 14, y + 1, x + 14, y + 14); - g.drawLine(x + 5, y + 10, x + 5, y + 10); - g.drawLine(x + 6, y + 9, x + 7, y + 9); - g.drawLine(x + 10, y + 5, x + 10, y + 5); - g.drawLine(x + 9, y + 6, x + 9, y + 7); - g.drawLine(x + 10, y + 10, x + 11, y + 10); - g.drawLine(x + 10, y + 11, x + 10, y + 11); + if (!b.getModel().isPressed()) + { + g.drawLine(x + 5, y + 10, x + 5, y + 10); + g.drawLine(x + 6, y + 9, x + 7, y + 9); + g.drawLine(x + 10, y + 5, x + 10, y + 5); + g.drawLine(x + 9, y + 6, x + 9, y + 7); + g.drawLine(x + 10, y + 10, x + 11, y + 10); + g.drawLine(x + 10, y + 11, x + 10, y + 11); + } + g.setColor(savedColor); } } /** * The icon displayed at the top-left corner of a {@link JInternalFrame}. */ - private static class InternalFrameDefaultMenuIcon - implements Icon, Serializable + private static class InternalFrameDefaultMenuIcon + implements Icon, Serializable { /** @@ -656,6 +1178,7 @@ public class MetalIconFactory implements Serializable */ public InternalFrameDefaultMenuIcon() { + // Nothing to do here. } /** @@ -718,8 +1241,8 @@ public class MetalIconFactory implements Serializable * maximise an internal frame, this icon will replace the 'maximise' icon to * provide a 'restore' option. */ - private static class InternalFrameAltMaximizeIcon - implements Icon, Serializable + private static class InternalFrameAltMaximizeIcon + implements Icon, Serializable { /** The icon size in pixels. */ private int size; @@ -764,14 +1287,23 @@ public class MetalIconFactory implements Serializable */ public void paintIcon(Component c, Graphics g, int x, int y) { - Color color = MetalLookAndFeel.getControlDarkShadow(); - if (c instanceof JInternalFrame) - { - JInternalFrame f = (JInternalFrame) c; - if (f.isSelected()) - color = MetalLookAndFeel.getPrimaryControlShadow(); - } - g.setColor(color); + Color savedColor = g.getColor(); + + AbstractButton b = (AbstractButton) c; + + // fill the small box interior + if (b.getModel().isPressed()) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getPrimaryControl()); + g.fillRect(x + 2, y + 6, 7, 7); + + + if (b.getModel().isPressed()) + g.setColor(MetalLookAndFeel.getBlack()); + else + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + g.drawLine(x + 12, y + 1, x + 13, y + 1); g.drawLine(x + 11, y + 2, x + 12, y + 2); g.drawLine(x + 10, y + 3, x + 11, y + 3); @@ -803,10 +1335,16 @@ public class MetalIconFactory implements Serializable g.drawLine(x + 13, y + 6, x + 13, y + 6); g.drawLine(x + 8, y + 7, x + 13, y + 7); g.drawLine(x + 6, y + 5, x + 6, y + 5); - g.drawLine(x + 2, y + 6, x + 6, y + 6); - g.drawLine(x + 2, y + 6, x + 2, y + 11); g.drawLine(x + 10, y + 8, x + 10, y + 13); g.drawLine(x + 1, y + 14, x + 10, y + 14); + + if (!b.getModel().isPressed()) + { + g.drawLine(x + 2, y + 6, x + 6, y + 6); + g.drawLine(x + 2, y + 6, x + 2, y + 11); + } + + g.setColor(savedColor); } } @@ -814,8 +1352,7 @@ public class MetalIconFactory implements Serializable * An icon used for the 'maximize' button in the title frame of a * {@link JInternalFrame}. */ - private static class InternalFrameMaximizeIcon - implements Icon, Serializable + private static class InternalFrameMaximizeIcon implements Icon, Serializable { /** @@ -823,6 +1360,7 @@ public class MetalIconFactory implements Serializable */ public InternalFrameMaximizeIcon() { + // Nothing to do here. } /** @@ -855,14 +1393,22 @@ public class MetalIconFactory implements Serializable */ public void paintIcon(Component c, Graphics g, int x, int y) { - Color color = MetalLookAndFeel.getControlDarkShadow(); - if (c instanceof JInternalFrame) - { - JInternalFrame f = (JInternalFrame) c; - if (f.isSelected()) - color = MetalLookAndFeel.getPrimaryControlShadow(); - } - g.setColor(color); + Color savedColor = g.getColor(); + + AbstractButton b = (AbstractButton) c; + + // fill the interior + if (b.getModel().isPressed()) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getPrimaryControl()); + g.fillRect(x + 2, y + 6, 7, 7); + + if (b.getModel().isPressed()) + g.setColor(MetalLookAndFeel.getBlack()); + else + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + g.drawLine(x + 9, y + 1, x + 10, y + 1); g.fillRect(x + 11, y + 1, 3, 3); g.fillRect(x + 12, y + 4, 2, 2); @@ -897,9 +1443,12 @@ public class MetalIconFactory implements Serializable // draw white g.setColor(MetalLookAndFeel.getWhite()); - g.drawLine(x + 2, y + 6, x + 5, y + 6); - g.drawLine(x + 2, y + 7, x + 2, y + 9); - g.drawLine(x + 4, y + 11, x + 7, y + 8); + if (!b.getModel().isPressed()) + { + g.drawLine(x + 2, y + 6, x + 5, y + 6); + g.drawLine(x + 2, y + 7, x + 2, y + 9); + g.drawLine(x + 4, y + 11, x + 7, y + 8); + } g.drawLine(x + 1, y + 14, x + 10, y + 14); g.drawLine(x + 10, y + 5, x + 10, y + 13); @@ -908,14 +1457,14 @@ public class MetalIconFactory implements Serializable g.drawLine(x + 11, y + 4, x + 11, y + 5); g.drawLine(x + 13, y + 6, x + 14, y + 6); g.drawLine(x + 14, y + 1, x + 14, y + 5); + g.setColor(savedColor); } } /** * An icon used in the title frame of a {@link JInternalFrame}. */ - private static class InternalFrameMinimizeIcon - implements Icon, Serializable + private static class InternalFrameMinimizeIcon implements Icon, Serializable { /** @@ -923,6 +1472,7 @@ public class MetalIconFactory implements Serializable */ public InternalFrameMinimizeIcon() { + // Nothing to do here. } /** @@ -955,14 +1505,17 @@ public class MetalIconFactory implements Serializable */ public void paintIcon(Component c, Graphics g, int x, int y) { - Color color = MetalLookAndFeel.getControlDarkShadow(); - if (c instanceof JInternalFrame) - { - JInternalFrame f = (JInternalFrame) c; - if (f.isSelected()) - color = MetalLookAndFeel.getPrimaryControlShadow(); - } - g.setColor(color); + Color savedColor = g.getColor(); + + AbstractButton b = (AbstractButton) c; + + if (b.getModel().isPressed()) + g.setColor(MetalLookAndFeel.getBlack()); + else + // FIXME: here the color depends on whether or not the internal frame + // is selected + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + g.drawLine(x + 12, y + 1, x + 13, y + 1); g.drawLine(x + 11, y + 2, x + 12, y + 2); g.drawLine(x + 10, y + 3, x + 11, y + 3); @@ -993,10 +1546,21 @@ public class MetalIconFactory implements Serializable g.drawLine(x + 11, y + 4, x + 13, y + 2); g.drawLine(x + 13, y + 6, x + 13, y + 6); g.drawLine(x + 8, y + 7, x + 13, y + 7); - g.drawLine(x + 2, y + 9, x + 4, y + 9); - g.drawLine(x + 2, y + 10, x + 2, y + 11); g.drawLine(x + 7, y + 9, x + 7, y + 13); g.drawLine(x + 1, y + 14, x + 7, y + 14); + + if (b.getModel().isPressed()) + { + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + g.fillRect(x + 2, y + 9, 3, 3); + } + else + { + g.drawLine(x + 2, y + 9, x + 4, y + 9); + g.drawLine(x + 2, y + 10, x + 2, y + 11); + } + + g.setColor(savedColor); } } @@ -1004,13 +1568,14 @@ public class MetalIconFactory implements Serializable * The icon used to display the thumb control on a horizontally oriented * {@link JSlider} component. */ - private static class VerticalSliderThumbIcon implements Icon, Serializable + private static class VerticalSliderThumbIcon implements Icon, Serializable { /** * Creates a new instance. */ public VerticalSliderThumbIcon() { + // Nothing to do here. } /** @@ -1045,13 +1610,19 @@ public class MetalIconFactory implements Serializable */ public void paintIcon(Component c, Graphics g, int x, int y) { + boolean enabled = false; boolean focus = false; - if (c != null) - focus = c.hasFocus(); - // TODO: pick up the colors from the look and feel + if (c != null) + { + enabled = c.isEnabled(); + focus = c.hasFocus(); + } // draw the outline - g.setColor(Color.black); + if (enabled) + g.setColor(MetalLookAndFeel.getBlack()); + else + g.setColor(MetalLookAndFeel.getControlDarkShadow()); g.drawLine(x + 1, y, x + 7, y); g.drawLine(x + 8, y, x + 15, y + 7); g.drawLine(x + 14, y + 8, x + 8, y + 14); @@ -1059,8 +1630,11 @@ public class MetalIconFactory implements Serializable g.drawLine(x, y + 13, x, y + 1); // fill the icon - g.setColor(focus ? new Color(153, 153, 204) : new Color(204, 204, 204)); // medium - g.fillRect(x + 2, y + 2, 7, 12); + if (focus) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getControl()); + g.fillRect(x + 2, y + 1, 7, 13); g.drawLine(x + 9, y + 2, x + 9, y + 12); g.drawLine(x + 10, y + 3, x + 10, y + 11); g.drawLine(x + 11, y + 4, x + 11, y + 10); @@ -1068,41 +1642,51 @@ public class MetalIconFactory implements Serializable g.drawLine(x + 13, y + 6, x + 13, y + 8); g.drawLine(x + 14, y + 7, x + 14, y + 7); - // draw highlights - g.setColor(focus ? new Color(204, 204, 255) : new Color(255, 255, 255)); // light - g.drawLine(x + 1, y + 1, x + 8, y + 1); - g.drawLine(x + 1, y + 2, x + 1, y + 13); - g.drawLine(x + 2, y + 2, x + 2, y + 2); - g.drawLine(x + 2, y + 6, x + 2, y + 6); - g.drawLine(x + 2, y + 10, x + 2, y + 10); + // if the slider is enabled, draw dots and highlights + if (enabled) + { + if (focus) + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + else + g.setColor(MetalLookAndFeel.getBlack()); + g.drawLine(x + 3, y + 3, x + 3, y + 3); + g.drawLine(x + 3, y + 7, x + 3, y + 7); + g.drawLine(x + 3, y + 11, x + 3, y + 11); - g.drawLine(x + 4, y + 4, x + 4, y + 4); - g.drawLine(x + 4, y + 8, x + 4, y + 8); + g.drawLine(x + 5, y + 5, x + 5, y + 5); + g.drawLine(x + 5, y + 9, x + 5, y + 9); - g.drawLine(x + 6, y + 2, x + 6, y + 2); - g.drawLine(x + 6, y + 6, x + 6, y + 6); - g.drawLine(x + 6, y + 10, x + 6, y + 10); + g.drawLine(x + 7, y + 3, x + 7, y + 3); + g.drawLine(x + 7, y + 7, x + 7, y + 7); + g.drawLine(x + 7, y + 11, x + 7, y + 11); - // draw dots - g.setColor(focus ? new Color(102, 102, 153) : Color.black); // dark - g.drawLine(x + 3, y + 3, x + 3, y + 3); - g.drawLine(x + 3, y + 7, x + 3, y + 7); - g.drawLine(x + 3, y + 11, x + 3, y + 11); + // draw highlights + if (focus) + g.setColor(MetalLookAndFeel.getPrimaryControl()); + else + g.setColor(MetalLookAndFeel.getWhite()); + g.drawLine(x + 1, y + 1, x + 8, y + 1); + g.drawLine(x + 1, y + 2, x + 1, y + 13); + g.drawLine(x + 2, y + 2, x + 2, y + 2); + g.drawLine(x + 2, y + 6, x + 2, y + 6); + g.drawLine(x + 2, y + 10, x + 2, y + 10); - g.drawLine(x + 5, y + 5, x + 5, y + 5); - g.drawLine(x + 5, y + 9, x + 5, y + 9); + g.drawLine(x + 4, y + 4, x + 4, y + 4); + g.drawLine(x + 4, y + 8, x + 4, y + 8); - g.drawLine(x + 7, y + 3, x + 7, y + 3); - g.drawLine(x + 7, y + 7, x + 7, y + 7); - g.drawLine(x + 7, y + 11, x + 7, y + 11); + g.drawLine(x + 6, y + 2, x + 6, y + 2); + g.drawLine(x + 6, y + 6, x + 6, y + 6); + g.drawLine(x + 6, y + 10, x + 6, y + 10); + + } } } - + /** * A tree control icon. This icon can be in one of two states: expanded and * collapsed. */ - public static class TreeControlIcon implements Icon, Serializable + public static class TreeControlIcon implements Icon, Serializable { /** ???. */ @@ -1235,19 +1819,21 @@ public class MetalIconFactory implements Serializable /** * A tree folder icon. */ - public static class TreeFolderIcon extends FolderIcon16 + public static class TreeFolderIcon extends FolderIcon16 { /** * Creates a new instance. */ public TreeFolderIcon() - { + { + // Nothing to do here. } /** - * Returns the additional height (???). + * Returns the additional height for this icon, in this case <code>2</code> + * pixels. * - * @return The additional height. + * @return <code>2</code>. */ public int getAdditionalHeight() { @@ -1268,19 +1854,21 @@ public class MetalIconFactory implements Serializable /** * A tree leaf icon. */ - public static class TreeLeafIcon extends FileIcon16 + public static class TreeLeafIcon extends FileIcon16 { /** * Creates a new instance. */ public TreeLeafIcon() { + // Nothing to do here. } /** - * Returns the additional height (???). + * Returns the additional height for this icon, in this case <code>4</code> + * pixels. * - * @return The additional height. + * @return <code>4</code>. */ public int getAdditionalHeight() { @@ -1297,16 +1885,310 @@ public class MetalIconFactory implements Serializable return 2; } } + + /** + * An icon representing a hard disk. + * + * @see MetalIconFactory#getTreeHardDriveIcon() + */ + private static class TreeHardDriveIcon implements Icon, Serializable + { + + /** + * Creates a new icon instance. + */ + public TreeHardDriveIcon() + { + // Nothing to do here. + } + + /** + * Returns the width of the icon, in pixels. + * + * @return <code>16</code>. + */ + public int getIconWidth() + { + return 16; + } + + /** + * Returns the height of the icon, in pixels. + * + * @return <code>16</code>. + */ + public int getIconHeight() + { + return 16; + } + + /** + * Paints the icon at the specified location, using colors from the + * current theme. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate for the top-left of the icon. + * @param y the y-coordinate for the top-left of the icon. + */ + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color saved = g.getColor(); + g.setColor(MetalLookAndFeel.getBlack()); + g.drawLine(x + 1, y + 4, x + 1, y + 5); + g.drawLine(x + 14, y + 4, x + 14, y + 5); + g.drawLine(x + 1, y + 7, x + 1, y + 8); + g.drawLine(x + 14, y + 7, x + 14, y + 8); + g.drawLine(x + 1, y + 10, x + 1, y + 11); + g.drawLine(x + 14, y + 10, x + 14, y + 11); + + g.drawLine(x + 2, y + 3, x + 3, y + 3); + g.drawLine(x + 12, y + 3, x + 13, y + 3); + g.drawLine(x + 2, y + 6, x + 3, y + 6); + g.drawLine(x + 12, y + 6, x + 13, y + 6); + g.drawLine(x + 2, y + 9, x + 3, y + 9); + g.drawLine(x + 12, y + 9, x + 13, y + 9); + g.drawLine(x + 2, y + 12, x + 3, y + 12); + g.drawLine(x + 12, y + 12, x + 13, y + 12); + + g.drawLine(x + 4, y + 2, x + 11, y + 2); + g.drawLine(x + 4, y + 7, x + 11, y + 7); + g.drawLine(x + 4, y + 10, x + 11, y + 10); + g.drawLine(x + 4, y + 13, x + 11, y + 13); + + g.setColor(MetalLookAndFeel.getWhite()); + g.fillRect(x + 4, y + 3, 2, 2); + g.drawLine(x + 6, y + 4, x + 6, y + 4); + g.drawLine(x + 7, y + 3, x + 9, y + 3); + g.drawLine(x + 8, y + 4, x + 8, y + 4); + g.drawLine(x + 11, y + 3, x + 11, y + 3); + g.fillRect(x + 2, y + 4, 2, 2); + g.fillRect(x + 2, y + 7, 2, 2); + g.fillRect(x + 2, y + 10, 2, 2); + g.drawLine(x + 4, y + 6, x + 4, y + 6); + g.drawLine(x + 4, y + 9, x + 4, y + 9); + g.drawLine(x + 4, y + 12, x + 4, y + 12); + + g.setColor(MetalLookAndFeel.getControlShadow()); + g.drawLine(x + 13, y + 4, x + 13, y + 4); + g.drawLine(x + 12, y + 5, x + 13, y + 5); + g.drawLine(x + 13, y + 7, x + 13, y + 7); + g.drawLine(x + 12, y + 8, x + 13, y + 8); + g.drawLine(x + 13, y + 10, x + 13, y + 10); + g.drawLine(x + 12, y + 11, x + 13, y + 11); + + g.drawLine(x + 10, y + 5, x + 10, y + 5); + g.drawLine(x + 7, y + 6, x + 7, y + 6); + g.drawLine(x + 9, y + 6, x + 9, y + 6); + g.drawLine(x + 11, y + 6, x + 11, y + 6); + + g.drawLine(x + 10, y + 8, x + 10, y + 8); + g.drawLine(x + 7, y + 9, x + 7, y + 9); + g.drawLine(x + 9, y + 9, x + 9, y + 9); + g.drawLine(x + 11, y + 9, x + 11, y + 9); + + g.drawLine(x + 10, y + 11, x + 10, y + 11); + g.drawLine(x + 7, y + 12, x + 7, y + 12); + g.drawLine(x + 9, y + 12, x + 9, y + 12); + g.drawLine(x + 11, y + 12, x + 11, y + 12); + + g.setColor(saved); + } + } + + /** + * An icon representing a floppy disk. + * + * @see MetalIconFactory#getTreeFloppyDriveIcon() + */ + private static class TreeFloppyDriveIcon implements Icon, Serializable + { + + /** + * Creates a new icon instance. + */ + public TreeFloppyDriveIcon() + { + // Nothing to do here. + } + + /** + * Returns the width of the icon, in pixels. + * + * @return <code>16</code>. + */ + public int getIconWidth() + { + return 16; + } + + /** + * Returns the height of the icon, in pixels. + * + * @return <code>16</code>. + */ + public int getIconHeight() + { + return 16; + } + + /** + * Paints the icon at the specified location, using colors from the + * current theme. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate for the top-left of the icon. + * @param y the y-coordinate for the top-left of the icon. + */ + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color saved = g.getColor(); + + g.setColor(MetalLookAndFeel.getBlack()); + g.drawLine(x + 1, y + 1, x + 13, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + 14); + g.drawLine(x + 1, y + 14, x + 14, y + 14); + g.drawLine(x + 14, y + 2, x + 14, y + 14); + + g.setColor(MetalLookAndFeel.getPrimaryControl()); + g.fillRect(x + 2, y + 2, 12, 12); + + g.setColor(MetalLookAndFeel.getControlShadow()); + g.fillRect(x + 5, y + 2, 6, 5); + g.drawLine(x + 4, y + 8, x + 11, y + 8); + g.drawLine(x + 3, y + 9, x + 3, y + 13); + g.drawLine(x + 12, y + 9, x + 12, y + 13); + + g.setColor(MetalLookAndFeel.getWhite()); + g.fillRect(x + 8, y + 3, 2, 3); + g.fillRect(x + 4, y + 9, 8, 5); + + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + g.drawLine(x + 5, y + 10, x + 9, y + 10); + g.drawLine(x + 5, y + 12, x + 8, y + 12); + + g.setColor(saved); + } + } + + /** + * An icon representing a computer. + * + * @see MetalIconFactory#getTreeComputerIcon() + */ + private static class TreeComputerIcon implements Icon, Serializable + { + + /** + * Creates a new icon instance. + */ + public TreeComputerIcon() + { + // Nothing to do here. + } + + /** + * Returns the width of the icon, in pixels. + * + * @return <code>16</code>. + */ + public int getIconWidth() + { + return 16; + } + + /** + * Returns the height of the icon, in pixels. + * + * @return <code>16</code>. + */ + public int getIconHeight() + { + return 16; + } + + /** + * Paints the icon at the specified location, using colors from the + * current theme. + * + * @param c the component (ignored). + * @param g the graphics device. + * @param x the x-coordinate for the top-left of the icon. + * @param y the y-coordinate for the top-left of the icon. + */ + public void paintIcon(Component c, Graphics g, int x, int y) + { + Color saved = g.getColor(); + + g.setColor(MetalLookAndFeel.getBlack()); + g.drawLine(x + 3, y + 1, x + 12, y + 1); + g.drawLine(x + 2, y + 2, x + 2, y + 8); + g.drawLine(x + 13, y + 2, x + 13, y + 8); + g.drawLine(x + 3, y + 9, x + 3, y + 9); + g.drawLine(x + 12, y + 9, x + 12, y + 9); + g.drawRect(x + 1, y + 10, 13, 4); + g.drawLine(x + 5, y + 3, x + 10, y + 3); + g.drawLine(x + 5, y + 8, x + 10, y + 8); + g.drawLine(x + 4, y + 4, x + 4, y + 7); + g.drawLine(x + 11, y + 4, x + 11, y + 7); + + g.setColor(MetalLookAndFeel.getPrimaryControl()); + g.fillRect(x + 5, y + 4, 6, 4); + + g.setColor(MetalLookAndFeel.getControlShadow()); + g.drawLine(x + 6, y + 12, x + 8, y + 12); + g.drawLine(x + 10, y + 12, x + 12, y + 12); + g.setColor(saved); + } + } + /** The icon returned by {@link #getCheckBoxIcon()}. */ + private static Icon checkBoxIcon; + + /** The icon returned by {@link #getCheckBoxMenuItemIcon()}. */ + private static Icon checkBoxMenuItemIcon; + + /** The icon returned by {@link #getFileChooserDetailViewIcon()}. */ + private static Icon fileChooserDetailViewIcon; + + /** The icon returned by {@link #getFileChooserHomeFolderIcon()}. */ + private static Icon fileChooserHomeFolderIcon; + + /** The icon returned by {@link #getFileChooserListViewIcon()}. */ + private static Icon fileChooserListViewIcon; + + /** The icon returned by {@link #getFileChooserNewFolderIcon()}. */ + private static Icon fileChooserNewFolderIcon; + + /** The icon returned by {@link #getFileChooserUpFolderIcon()}. */ + private static Icon fileChooserUpFolderIcon; + /** The cached RadioButtonIcon instance. */ private static RadioButtonIcon radioButtonIcon; + /** The icon returned by {@link #getRadioButtonMenuItemIcon()}. */ + private static Icon radioButtonMenuItemIcon; + + /** The icon returned by {@link #getInternalFrameDefaultMenuIcon()}. */ + private static Icon internalFrameDefaultMenuIcon; + + /** The icon returned by {@link #getTreeComputerIcon()}. */ + private static Icon treeComputerIcon; + + /** The icon instance returned by {@link #getTreeFloppyDriveIcon()}. */ + private static Icon treeFloppyDriveIcon; + + /** The icon instance returned by {@link #getTreeHardDriveIcon()}. */ + private static Icon treeHardDriveIcon; + /** * Creates a new instance. All the methods are static, so creating an * instance isn't necessary. */ public MetalIconFactory() - { + { + // Nothing to do here. } /** @@ -1318,7 +2200,9 @@ public class MetalIconFactory implements Serializable */ public static Icon getCheckBoxIcon() { - return new MetalCheckBoxIcon(); + if (checkBoxIcon == null) + checkBoxIcon = new MetalCheckBoxIcon(); + return checkBoxIcon; } /** @@ -1329,7 +2213,69 @@ public class MetalIconFactory implements Serializable */ public static Icon getCheckBoxMenuItemIcon() { - return new CheckBoxMenuItemIcon(); + if (checkBoxMenuItemIcon == null) + checkBoxMenuItemIcon = new CheckBoxMenuItemIcon(); + return checkBoxMenuItemIcon; + } + + /** + * Returns an icon for use by the {@link JFileChooser} component. + * + * @return An icon. + */ + public static Icon getFileChooserDetailViewIcon() + { + if (fileChooserDetailViewIcon == null) + fileChooserDetailViewIcon = new FileChooserDetailViewIcon(); + return fileChooserDetailViewIcon; + } + + /** + * Returns an icon for use by the {@link JFileChooser} component. + * + * @return An icon. + */ + public static Icon getFileChooserHomeFolderIcon() + { + if (fileChooserHomeFolderIcon == null) + fileChooserHomeFolderIcon = new FileChooserHomeFolderIcon(); + return fileChooserHomeFolderIcon; + } + + /** + * Returns an icon for use by the {@link JFileChooser} component. + * + * @return An icon. + */ + public static Icon getFileChooserListViewIcon() + { + if (fileChooserListViewIcon == null) + fileChooserListViewIcon = new FileChooserListViewIcon(); + return fileChooserListViewIcon; + } + + /** + * Returns an icon for use by the {@link JFileChooser} component. + * + * @return An icon. + */ + public static Icon getFileChooserNewFolderIcon() + { + if (fileChooserNewFolderIcon == null) + fileChooserNewFolderIcon = new FileChooserNewFolderIcon(); + return fileChooserNewFolderIcon; + } + + /** + * Returns an icon for use by the {@link JFileChooser} component. + * + * @return An icon. + */ + public static Icon getFileChooserUpFolderIcon() + { + if (fileChooserUpFolderIcon == null) + fileChooserUpFolderIcon = new FileChooserUpFolderIcon(); + return fileChooserUpFolderIcon; } /** @@ -1351,7 +2297,9 @@ public class MetalIconFactory implements Serializable */ public static Icon getRadioButtonMenuItemIcon() { - return new RadioButtonMenuItemIcon(); + if (radioButtonMenuItemIcon == null) + radioButtonMenuItemIcon = new RadioButtonMenuItemIcon(); + return radioButtonMenuItemIcon; } /** @@ -1386,7 +2334,9 @@ public class MetalIconFactory implements Serializable */ public static Icon getInternalFrameDefaultMenuIcon() { - return new InternalFrameDefaultMenuIcon(); + if (internalFrameDefaultMenuIcon == null) + internalFrameDefaultMenuIcon = new InternalFrameDefaultMenuIcon(); + return internalFrameDefaultMenuIcon; } /** @@ -1475,4 +2425,40 @@ public class MetalIconFactory implements Serializable return new TreeControlIcon(isCollapsed); } + /** + * Returns a <code>16x16</code> icon representing a computer. + * + * @return The icon. + */ + public static Icon getTreeComputerIcon() + { + if (treeComputerIcon == null) + treeComputerIcon = new TreeComputerIcon(); + return treeComputerIcon; + } + + /** + * Returns a <code>16x16</code> icon representing a floppy disk. + * + * @return The icon. + */ + public static Icon getTreeFloppyDriveIcon() + { + if (treeFloppyDriveIcon == null) + treeFloppyDriveIcon = new TreeFloppyDriveIcon(); + return treeFloppyDriveIcon; + } + + /** + * Returns a <code>16x16</code> icon representing a hard disk. + * + * @return The icon. + */ + public static Icon getTreeHardDriveIcon() + { + if (treeHardDriveIcon == null) + treeHardDriveIcon = new TreeHardDriveIcon(); + return treeHardDriveIcon; + } + } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameTitlePane.java b/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameTitlePane.java index 20526eba8cd..33eb3491ad0 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameTitlePane.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameTitlePane.java @@ -38,30 +38,208 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Color; +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.awt.Rectangle; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import javax.swing.Icon; import javax.swing.JInternalFrame; +import javax.swing.JLabel; import javax.swing.JMenu; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.UIDefaults; +import javax.swing.UIManager; import javax.swing.plaf.basic.BasicInternalFrameTitlePane; + /** - * The title pane for a {@link JInternalFrame}. + * The title pane for a {@link JInternalFrame} (see + * {@link MetalInternalFrameUI#createNorthPane(JInternalFrame)}). This can + * be displayed in two styles: one for regular internal frames, and the other + * for "palette" style internal frames. */ public class MetalInternalFrameTitlePane extends BasicInternalFrameTitlePane { + /** + * A property change handler that listens for changes to the + * <code>JInternalFrame.isPalette</code> property and updates the title + * pane as appropriate. + */ + class MetalInternalFrameTitlePanePropertyChangeHandler + extends PropertyChangeHandler + { + /** + * Creates a new handler. + */ + public MetalInternalFrameTitlePanePropertyChangeHandler() + { + super(); + } + + /** + * Handles <code>JInternalFrame.isPalette</code> property changes, with all + * other property changes being passed to the superclass. + * + * @param e the event. + */ + public void propertyChange(PropertyChangeEvent e) + { + String propName = e.getPropertyName(); + if (propName.equals("JInternalFrame.isPalette")) + { + if (e.getNewValue().equals(Boolean.TRUE)) + setPalette(true); + else + setPalette(false); + } + else + super.propertyChange(e); + } + } + + /** + * A layout manager for the title pane. + * + * @see #createLayout() + */ + private class MetalTitlePaneLayout implements LayoutManager + { + /** + * Creates a new <code>TitlePaneLayout</code> object. + */ + public MetalTitlePaneLayout() + { + // Do nothing. + } + + /** + * Adds a Component to the Container. + * + * @param name The name to reference the added Component by. + * @param c The Component to add. + */ + public void addLayoutComponent(String name, Component c) + { + // Do nothing. + } + + /** + * This method is called to lay out the children of the Title Pane. + * + * @param c The Container to lay out. + */ + public void layoutContainer(Container c) + { + + Dimension size = c.getSize(); + Insets insets = c.getInsets(); + int width = size.width - insets.left - insets.right; + int height = size.height - insets.top - insets.bottom; + + + int loc = width - insets.right - 1; + int top = insets.top + 2; + int buttonHeight = height - 4; + if (closeButton.isVisible()) + { + int buttonWidth = closeIcon.getIconWidth(); + loc -= buttonWidth + 2; + closeButton.setBounds(loc, top, buttonWidth, buttonHeight); + loc -= 6; + } + + if (maxButton.isVisible()) + { + int buttonWidth = maxIcon.getIconWidth(); + loc -= buttonWidth + 4; + maxButton.setBounds(loc, top, buttonWidth, buttonHeight); + } + + if (iconButton.isVisible()) + { + int buttonWidth = minIcon.getIconWidth(); + loc -= buttonWidth + 4; + iconButton.setBounds(loc, top, buttonWidth, buttonHeight); + loc -= 2; + } + + Dimension titlePreferredSize = title.getPreferredSize(); + title.setBounds(insets.left + 5, insets.top, + Math.min(titlePreferredSize.width, loc - insets.left - 10), + height); + + } + + /** + * This method returns the minimum size of the given Container given the + * children that it has. + * + * @param c The Container to get a minimum size for. + * + * @return The minimum size of the Container. + */ + public Dimension minimumLayoutSize(Container c) + { + return preferredLayoutSize(c); + } + + /** + * Returns the preferred size of the given Container taking + * into account the children that it has. + * + * @param c The Container to lay out. + * + * @return The preferred size of the Container. + */ + public Dimension preferredLayoutSize(Container c) + { + if (isPalette) + return new Dimension(paletteTitleHeight, paletteTitleHeight); + else + return new Dimension(22, 22); + } + + /** + * Removes a Component from the Container. + * + * @param c The Component to remove. + */ + public void removeLayoutComponent(Component c) + { + // Nothing to do here. + } + } + + /** A flag indicating whether the title pane uses the palette style. */ protected boolean isPalette; - protected Icon paletteCloseIcon = MetalIconFactory.getInternalFrameCloseIcon(16); + /** + * The icon used for the close button - this is fetched from the look and + * feel defaults using the key <code>InternalFrame.paletteCloseIcon</code>. + */ + protected Icon paletteCloseIcon; + + /** + * The height of the title pane when <code>isPalette</code> is + * <code>true</code>. This value is fetched from the look and feel defaults + * using the key <code>InternalFrame.paletteTitleHeight</code>. + */ + protected int paletteTitleHeight; + + /** The label used to display the title for the internal frame. */ + JLabel title; - protected int paletteTitleHeight = 12; - /** - * Creates a new title pane. + * Creates a new title pane for the specified frame. * * @param f the internal frame. */ @@ -81,6 +259,15 @@ public class MetalInternalFrameTitlePane extends BasicInternalFrameTitlePane selectedTitleColor = MetalLookAndFeel.getWindowTitleBackground(); notSelectedTextColor = MetalLookAndFeel.getInactiveControlTextColor(); notSelectedTitleColor = MetalLookAndFeel.getWindowTitleInactiveBackground(); + + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + paletteTitleHeight = defaults.getInt("InternalFrame.paletteTitleHeight"); + paletteCloseIcon = defaults.getIcon("InternalFrame.paletteCloseIcon"); + minIcon = MetalIconFactory.getInternalFrameAltMaximizeIcon(16); + + title = new JLabel(frame.getTitle(), + MetalIconFactory.getInternalFrameDefaultMenuIcon(), + SwingConstants.LEFT); } /** @@ -93,6 +280,9 @@ public class MetalInternalFrameTitlePane extends BasicInternalFrameTitlePane selectedTitleColor = null; notSelectedTextColor = null; notSelectedTitleColor = null; + paletteCloseIcon = null; + minIcon = null; + title = null; } /** @@ -128,44 +318,123 @@ public class MetalInternalFrameTitlePane extends BasicInternalFrameTitlePane } /** - * Creates a layout manager for the components in the title pane. + * Adds the sub components of the title pane. + */ + protected void addSubComponents() + { + // FIXME: this method is probably overridden to only add the required + // buttons + add(title); + add(closeButton); + add(iconButton); + add(maxButton); + } + + /** + * Creates a new instance of {@link MetalTitlePaneLayout}. * - * @return A layout manager. - */ + * @return A new instance of {@link MetalTitlePaneLayout}. + */ protected LayoutManager createLayout() { - return new TitlePaneLayout() - { - public Dimension preferredLayoutSize(Container c) - { - return new Dimension(24, 24); - } - }; + return new MetalTitlePaneLayout(); } + /** + * Draws the title pane in the palette style. + * + * @param g the graphics device. + * + * @see #paintComponent(Graphics) + */ public void paintPalette(Graphics g) { - // FIXME: needs implementing - // most likely this is equivalent to paintComponent(g) when the isPalette - // flag is true + Color savedColor = g.getColor(); + Rectangle b = SwingUtilities.getLocalBounds(this); + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + g.fillRect(b.x, b.y, b.width, b.height); + MetalUtils.fillMetalPattern(this, g, b.x + 4, b.y + 2, b.width + - paletteCloseIcon.getIconWidth() - 13, b.height - 5, + MetalLookAndFeel.getPrimaryControlHighlight(), + MetalLookAndFeel.getBlack()); + + // draw a line separating the title pane from the frame content + Dimension d = getSize(); + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + g.drawLine(0, d.height - 1, d.width - 1, d.height - 1); + + g.setColor(savedColor); } - + + /** + * Paints a representation of the current state of the internal frame. + * + * @param g the graphics device. + */ public void paintComponent(Graphics g) { - // probably need to check the isPalette flag here, if true pass over to - // paintPalette(Graphics) - super.paintComponent(g); - Dimension d = getSize(); - if (frame.isSelected()) - g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + Color savedColor = g.getColor(); + if (isPalette) + paintPalette(g); else - g.setColor(MetalLookAndFeel.getControlDarkShadow()); - g.drawLine(0, d.height - 1, d.width - 1, d.height - 1); + { + paintTitleBackground(g); + paintChildren(g); + Dimension d = getSize(); + if (frame.isSelected()) + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + else + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + + // put a dot in each of the top corners + g.drawLine(0, 0, 0, 0); + g.drawLine(d.width - 1, 0, d.width - 1, 0); + + g.drawLine(0, d.height - 1, d.width - 1, d.height - 1); + + // draw the metal pattern + Rectangle b = title.getBounds(); + int startX = b.x + b.width + 5; + int endX = startX; + if (iconButton.isVisible()) + endX = Math.max(iconButton.getX(), endX); + else if (maxButton.isVisible()) + endX = Math.max(maxButton.getX(), endX); + else if (closeButton.isVisible()) + endX = Math.max(closeButton.getX(), endX); + endX -= 7; + if (endX > startX) + MetalUtils.fillMetalPattern(this, g, startX, 3, endX - startX, getHeight() - 6, Color.white, Color.gray); + } + g.setColor(savedColor); } + /** + * Sets the flag that controls whether the title pane is drawn in the + * palette style or the regular style. + * + * @param b the new value of the flag. + */ public void setPalette(boolean b) { - isPalette = b; + isPalette = b; + title.setVisible(!isPalette); + iconButton.setVisible(!isPalette && frame.isIconifiable()); + maxButton.setVisible(!isPalette && frame.isMaximizable()); + if (isPalette) + closeButton.setIcon(paletteCloseIcon); + else + closeButton.setIcon(closeIcon); + } + + /** + * Creates and returns a property change handler for the title pane. + * + * @return The property change handler. + */ + protected PropertyChangeListener createPropertyChangeListener() + { + return new MetalInternalFrameTitlePanePropertyChangeHandler(); } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameUI.java index 7c7cb92e549..6be573f4bac 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalInternalFrameUI.java @@ -38,24 +38,31 @@ exception statement from your version. */ package javax.swing.plaf.metal; -import java.util.HashMap; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import javax.swing.JComponent; import javax.swing.JInternalFrame; -import javax.swing.border.EmptyBorder; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicInternalFrameUI; - +/** + * A UI delegate for the {@link JInternalFrame} component. + */ public class MetalInternalFrameUI extends BasicInternalFrameUI { - - /** The instances of MetalInternalFrameUI*/ - private static HashMap instances; + /** + * The key (<code>JInternalFrame.isPalette</code>) for the client property + * that controls whether the internal frame is displayed using the palette + * style. + */ + protected static String IS_PALETTE = "JInternalFrame.isPalette"; /** - * Constructs a new instance of MetalInternalFrameUI. + * Constructs a new instance of <code>MetalInternalFrameUI</code>. + * + * @param frame the frame. */ public MetalInternalFrameUI(JInternalFrame frame) { @@ -63,37 +70,96 @@ public class MetalInternalFrameUI } /** - * Returns an instance of MetalInternalFrameUI. + * Returns an instance of <code>MetalInternalFrameUI</code>. * - * @param component the component for which we return an UI instance + * @param component the internal frame. * - * @return an instance of MetalInternalFrameUI + * @return an instance of <code>MetalInternalFrameUI</code>. */ public static ComponentUI createUI(JComponent component) { - if (instances == null) - instances = new HashMap(); - - - Object o = instances.get(component); - MetalInternalFrameUI instance; - if (o == null) - { - instance = new MetalInternalFrameUI((JInternalFrame) component); - instances.put(component, instance); - } - else - instance = (MetalInternalFrameUI) o; - - return instance; + return new MetalInternalFrameUI((JInternalFrame) component); } + /** + * Sets the fields and properties for the component. + * + * @param c the component. + */ + public void installUI(JComponent c) + { + super.installUI(c); + JInternalFrame f = (JInternalFrame) c; + boolean isPalette = false; + Boolean p = (Boolean) f.getClientProperty(IS_PALETTE); + if (p != null) + isPalette = p.booleanValue(); + setPalette(isPalette); + } + + /** + * Creates and returns the component that will be used for the north pane + * of the {@link JInternalFrame}. + * + * @param w the internal frame. + * + * @return A new instance of {@link MetalInternalFrameTitlePane}. + */ protected JComponent createNorthPane(JInternalFrame w) { titlePane = new MetalInternalFrameTitlePane(w); - titlePane.setBorder(new EmptyBorder(2, 2, 2, 2)); return titlePane; } - + /** + * Sets the state of the {@link JInternalFrame} to reflect whether or not + * it is using the palette style. When a frame is displayed as a palette, + * it uses a different border and the title pane is drawn differently. + * + * @param isPalette use the palette style? + */ + public void setPalette(boolean isPalette) + { + MetalInternalFrameTitlePane title = (MetalInternalFrameTitlePane) northPane; + title.setPalette(isPalette); + if (isPalette) + frame.setBorder(new MetalBorders.PaletteBorder()); + else + frame.setBorder(new MetalBorders.InternalFrameBorder()); + } + + /** A listener that is used to handle IS_PALETTE property changes. */ + private PropertyChangeListener paletteListener; + + /** + * Adds the required listeners. + */ + protected void installListeners() + { + super.installListeners(); + paletteListener = new PropertyChangeListener() + { + public void propertyChange(PropertyChangeEvent e) + { + if (e.getPropertyName().equals(IS_PALETTE)) + { + if (Boolean.TRUE.equals(e.getNewValue())) + setPalette(true); + else + setPalette(false); + } + } + }; + frame.addPropertyChangeListener(paletteListener); + } + + /** + * Removes the listeners used. + */ + protected void uninstallListeners() + { + super.uninstallListeners(); + frame.removePropertyChangeListener(IS_PALETTE, paletteListener); + paletteListener = null; + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalLabelUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalLabelUI.java index fe8a9e4e4ea..e4eaa71724d 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalLabelUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalLabelUI.java @@ -50,17 +50,17 @@ import javax.swing.plaf.basic.BasicGraphicsUtils; import javax.swing.plaf.basic.BasicLabelUI; /** - * A UI delegate used for {@link JLabel}s in the {@link MetalLookAndFeel}. + * A UI delegate for the {@link JLabel} component. */ public class MetalLabelUI extends BasicLabelUI { - /** The shared UI instance for JLabels. */ + /** The shared instance of the UI delegate. */ protected static MetalLabelUI metalLabelUI; /** - * Constructs a new instance of MetalLabelUI. + * Constructs a new instance of <code>MetalLabelUI</code>. */ public MetalLabelUI() { @@ -68,11 +68,11 @@ public class MetalLabelUI } /** - * Returns an instance of MetalLabelUI. + * Returns a shared instance of <code>MetalLabelUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalLabelUI + * @return A shared instance of <code>MetalLabelUI</code>. */ public static ComponentUI createUI(JComponent component) { diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java b/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java index d9cf7c6220c..ec8014b17b3 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java @@ -42,11 +42,14 @@ import java.awt.Color; import java.awt.Font; import java.awt.Insets; +import javax.swing.LookAndFeel; import javax.swing.UIDefaults; +import javax.swing.UIManager; import javax.swing.plaf.BorderUIResource; import javax.swing.plaf.ColorUIResource; import javax.swing.plaf.FontUIResource; import javax.swing.plaf.InsetsUIResource; +import javax.swing.plaf.BorderUIResource.LineBorderUIResource; import javax.swing.plaf.basic.BasicLookAndFeel; @@ -69,7 +72,8 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public MetalLookAndFeel() { - createDefaultTheme(); + if (theme == null) + createDefaultTheme(); } /** @@ -601,12 +605,21 @@ public class MetalLookAndFeel extends BasicLookAndFeel } /** - * Sets the current theme for the look and feel. + * Sets the current theme for the look and feel. Note that the theme must be + * set <em>before</em> the look and feel is installed. To change the theme + * for an already running application that is using the + * {@link MetalLookAndFeel}, first set the theme with this method, then + * create a new instance of {@link MetalLookAndFeel} and install it in the + * usual way (see {@link UIManager#setLookAndFeel(LookAndFeel)}). * - * @param theme the theme. + * @param theme the theme (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>theme</code> is <code>null</code>. */ public static void setCurrentTheme(MetalTheme theme) { + if (theme == null) + throw new NullPointerException("Null 'theme' not permitted."); MetalLookAndFeel.theme = theme; } @@ -759,62 +772,206 @@ public class MetalLookAndFeel extends BasicLookAndFeel { super.initComponentDefaults(defaults); Object[] myDefaults = new Object[] { - "Button.background", new ColorUIResource(getControl()), + "Button.background", getControl(), "Button.border", MetalBorders.getButtonBorder(), - "Button.darkShadow", new ColorUIResource(getControlDarkShadow()), - "Button.disabledText", new ColorUIResource(getControlDisabled()), - "Button.focus", new ColorUIResource(getFocusColor()), + "Button.darkShadow", getControlDarkShadow(), + "Button.disabledText", getInactiveControlTextColor(), + "Button.focus", getFocusColor(), "Button.font", getControlTextFont(), - "Button.foreground", new ColorUIResource(getSystemTextColor()), - "Button.highlight", new ColorUIResource(getControlHighlight()), - "Button.light", new ColorUIResource(getControlHighlight()), - "Button.margin", new Insets(2, 14, 2, 14), - "Button.select", new ColorUIResource(getPrimaryControlShadow()), - "Button.shadow", new ColorUIResource(getPrimaryControlShadow()), - "CheckBox.background", new ColorUIResource(getControl()), + "Button.foreground", getControlTextColor(), + "Button.highlight", getControlHighlight(), + "Button.light", getControlHighlight(), + "Button.margin", new InsetsUIResource(2, 14, 2, 14), + "Button.select", getControlShadow(), + "Button.shadow", getControlShadow(), + + "CheckBox.background", getControl(), "CheckBox.border", MetalBorders.getButtonBorder(), + "CheckBox.disabledText", getInactiveControlTextColor(), + "CheckBox.focus", getFocusColor(), + "CheckBox.font", new FontUIResource("Dialog", Font.BOLD, 12), + "CheckBox.foreground", getControlTextColor(), "CheckBox.icon", new UIDefaults.ProxyLazyValue ("javax.swing.plaf.metal.MetalCheckBoxIcon"), "CheckBox.checkIcon", new UIDefaults.ProxyLazyValue ("javax.swing.plaf.metal.MetalCheckBoxIcon"), - "CheckBoxMenuItem.background", new ColorUIResource(getControl()), + "Checkbox.select", getControlShadow(), + + "CheckBoxMenuItem.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 10), + "CheckBoxMenuItem.acceleratorForeground", getAcceleratorForeground(), + "CheckBoxMenuItem.acceleratorSelectionForeground", getAcceleratorSelectedForeground(), + "CheckBoxMenuItem.background", getMenuBackground(), + "CheckBoxMenuItem.borderPainted", new Boolean(true), + "CheckBoxMenuItem.commandSound", "sounds/MenuItemCommand.wav", "CheckBoxMenuItem.checkIcon", MetalIconFactory.getCheckBoxMenuItemIcon(), - "ToolBar.background", new ColorUIResource(getControl()), - "Panel.background", new ColorUIResource(getControl()), - "Slider.background", new ColorUIResource(getControl()), - "OptionPane.background", new ColorUIResource(getControl()), - "ProgressBar.background", new ColorUIResource(getControl()), - "ScrollPane.border", new MetalBorders.ScrollPaneBorder(), - "TabbedPane.background", new ColorUIResource(getControl()), + "CheckBoxMenuItem.disabledForeground", getMenuDisabledForeground(), + "CheckBoxMenuItem.font", new FontUIResource("Dialog", Font.BOLD, 12), + "CheckBoxMenuItem.foreground", getMenuForeground(), + "CheckBoxMenuItem.selectionBackground", getMenuSelectedBackground(), + "CheckBoxMenuItem.selectionForeground", getMenuSelectedForeground(), + + "ColorChooser.background", getControl(), + "ColorChooser.foreground", getControlTextColor(), + "ColorChooser.rgbBlueMnemonic", new Integer(0), + "ColorChooser.rgbGreenMnemonic", new Integer(0), + "ColorChooser.rgbRedMnemonic", new Integer(0), + "ColorChooser.swatchesDefaultRecentColor", getControl(), + + "ComboBox.background", getControl(), + "ComboBox.buttonBackground", getControl(), + "ComboBox.buttonDarkShadow", getControlDarkShadow(), + "ComboBox.buttonHighlight", getControlHighlight(), + "ComboBox.buttonShadow", getControlShadow(), + "ComboBox.disabledBackground", getControl(), + "ComboBox.disabledForeground", getInactiveSystemTextColor(), + "ComboBox.font", new FontUIResource("Dialog", Font.BOLD, 12), + "ComboBox.foreground", getControlTextColor(), + "ComboBox.selectionBackground", getPrimaryControlShadow(), + "ComboBox.selectionForeground", getControlTextColor(), + + "Desktop.background", getDesktopColor(), + + "DesktopIcon.background", getControl(), + "DesktopIcon.foreground", getControlTextColor(), + "DesktopIcon.width", new Integer(160), + "DesktopIcon.border", MetalBorders.getDesktopIconBorder(), + + "EditorPane.background", getWindowBackground(), + "EditorPane.caretForeground", getUserTextColor(), + "EditorPane.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "EditorPane.foreground", getUserTextColor(), + "EditorPane.inactiveForeground", getInactiveSystemTextColor(), + "EditorPane.selectionBackground", getTextHighlightColor(), + "EditorPane.selectionForeground", getHighlightedTextColor(), + + "FormattedTextField.background", getWindowBackground(), + "FormattedTextField.border", + new BorderUIResource(MetalBorders.getTextFieldBorder()), + "FormattedTextField.caretForeground", getUserTextColor(), + "FormattedTextField.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "FormattedTextField.foreground", getUserTextColor(), + "FormattedTextField.inactiveBackground", getControl(), + "FormattedTextField.inactiveForeground", getInactiveSystemTextColor(), + "FormattedTextField.selectionBackground", getTextHighlightColor(), + "FormattedTextField.selectionForeground", getHighlightedTextColor(), + + "FileView.computerIcon", MetalIconFactory.getTreeComputerIcon(), + "FileView.directoryIcon", MetalIconFactory.getTreeFolderIcon(), + "FileView.fileIcon", MetalIconFactory.getTreeLeafIcon(), + "FileView.floppyDriveIcon", MetalIconFactory.getTreeFloppyDriveIcon(), + "FileView.hardDriveIcon", MetalIconFactory.getTreeHardDriveIcon(), + + "InternalFrame.activeTitleBackground", getWindowTitleBackground(), + "InternalFrame.activeTitleForeground", getWindowTitleForeground(), "InternalFrame.border", new MetalBorders.InternalFrameBorder(), + "InternalFrame.borderColor", getControl(), + "InternalFrame.borderDarkShadow", getControlDarkShadow(), + "InternalFrame.borderHighlight", getControlHighlight(), + "InternalFrame.borderLight", getControlHighlight(), + "InternalFrame.borderShadow", getControlShadow(), "InternalFrame.icon", MetalIconFactory.getInternalFrameDefaultMenuIcon(), "InternalFrame.closeIcon", MetalIconFactory.getInternalFrameCloseIcon(16), + "InternalFrame.inactiveTitleBackground", getWindowTitleInactiveBackground(), + "InternalFrame.inactiveTitleForeground", getWindowTitleInactiveForeground(), "InternalFrame.maximizeIcon", MetalIconFactory.getInternalFrameMaximizeIcon(16), "InternalFrame.iconifyIcon", MetalIconFactory.getInternalFrameMinimizeIcon(16), - "Label.background", new ColorUIResource(getControl()), + "InternalFrame.paletteBorder", new MetalBorders.PaletteBorder(), + "InternalFrame.paletteCloseIcon", new MetalIconFactory.PaletteCloseIcon(), + "InternalFrame.paletteTitleHeight", new Integer(11), + + "Label.background", getControl(), + "Label.disabledForeground", getInactiveSystemTextColor(), + "Label.disabledShadow", getControlShadow(), "Label.font", getControlTextFont(), - "Label.disabledForeground", new ColorUIResource(getInactiveControlTextColor()), - "Label.foreground", new ColorUIResource(getControlTextColor()), - "Menu.background", new ColorUIResource(getControl()), + "Label.foreground", getSystemTextColor(), + + "List.font", getControlTextFont(), + "List.background", getWindowBackground(), + "List.foreground", getUserTextColor(), + "List.selectionBackground", getTextHighlightColor(), + "List.selectionForeground", getHighlightedTextColor(), + "List.focusCellHighlightBorder", + new LineBorderUIResource(MetalLookAndFeel.getFocusColor()), + + "Menu.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 10), + "Menu.acceleratorForeground", getAcceleratorForeground(), + "Menu.acceleratorSelectionForeground", getAcceleratorSelectedForeground(), + "Menu.background", getMenuBackground(), "Menu.border", new MetalBorders.MenuItemBorder(), "Menu.borderPainted", Boolean.TRUE, + "Menu.disabledForeground", getMenuDisabledForeground(), "Menu.font", getControlTextFont(), + "Menu.foreground", getMenuForeground(), "Menu.selectionBackground", getMenuSelectedBackground(), "Menu.selectionForeground", getMenuSelectedForeground(), - "MenuBar.background", new ColorUIResource(getControl()), + + "MenuBar.background", getMenuBackground(), "MenuBar.border", new MetalBorders.MenuBarBorder(), "MenuBar.font", getControlTextFont(), - "MenuItem.background", new ColorUIResource(getControl()), + "MenuBar.foreground", getMenuForeground(), + "MenuBar.highlight", getControlHighlight(), + "MenuBar.shadow", getControlShadow(), + + "MenuItem.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 10), + "MenuItem.acceleratorForeground", getAcceleratorForeground(), + "MenuItem.acceleratorSelectionForeground", getAcceleratorSelectedForeground(), + "MenuItem.background", getMenuBackground(), "MenuItem.border", new MetalBorders.MenuItemBorder(), + "MenuItem.disabledForeground", getMenuDisabledForeground(), "MenuItem.font", getControlTextFont(), + "MenuItem.foreground", getMenuForeground(), "MenuItem.selectionBackground", getMenuSelectedBackground(), "MenuItem.selectionForeground", getMenuSelectedForeground(), - "Panel.background", new ColorUIResource(getControl()), + + "OptionPane.background", getControl(), + "OptionPane.errorDialog.border.background", new ColorUIResource(153, 51, 51), + "OptionPane.errorDialog.titlePane.background", new ColorUIResource(255, 153, 153), + "OptionPane.errorDialog.titlePane.foreground", new ColorUIResource(51, 0, 0), + "OptionPane.errorDialog.titlePane.shadow", new ColorUIResource(204, 102, 102), + "OptionPane.foreground", getControlTextColor(), + "OptionPane.messageForeground", getControlTextColor(), + "OptionPane.questionDialog.border.background", new ColorUIResource(51, 102, 51), + "OptionPane.questionDialog.titlePane.background", new ColorUIResource(153, 204, 153), + "OptionPane.questionDialog.titlePane.foreground", new ColorUIResource(0, 51, 0), + "OptionPane.questionDialog.titlePane.shadow", new ColorUIResource(102, 153, 102), + "OptionPane.warningDialog.border.background", new ColorUIResource(153, 102, 51), + "OptionPane.warningDialog.titlePane.background", new ColorUIResource(255, 204, 153), + "OptionPane.warningDialog.titlePane.foreground", new ColorUIResource(102, 51, 0), + "OptionPane.warningDialog.titlePane.shadow", new ColorUIResource(204, 153, 102), + + "Panel.background", getControl(), + "Panel.foreground", getUserTextColor(), + + "PasswordField.background", getWindowBackground(), + "PasswordField.border", + new BorderUIResource(MetalBorders.getTextFieldBorder()), + "PasswordField.caretForeground", getUserTextColor(), + "PasswordField.foreground", getUserTextColor(), + "PasswordField.inactiveBackground", getControl(), + "PasswordField.inactiveForeground", getInactiveSystemTextColor(), + "PasswordField.selectionBackground", getTextHighlightColor(), + "PasswordField.selectionForeground", getHighlightedTextColor(), + + "PopupMenu.background", getMenuBackground(), + "PopupMenu.border", new MetalBorders.PopupMenuBorder(), + "PopupMenu.font", new FontUIResource("Dialog", Font.BOLD, 12), + "PopupMenu.foreground", getMenuForeground(), + + "ProgressBar.background", getControl(), + "ProgressBar.border", new BorderUIResource.LineBorderUIResource(getControlDarkShadow(), 1), + "ProgressBar.font", new FontUIResource("Dialog", Font.BOLD, 12), + "ProgressBar.foreground", getPrimaryControlShadow(), + "ProgressBar.selectionBackground", getPrimaryControlDarkShadow(), + "ProgressBar.selectionForeground", getControl(), + + "RadioButton.background", getControl(), + "RadioButton.darkShadow", getControlDarkShadow(), + "RadioButton.disabledText", getInactiveControlTextColor(), "RadioButton.icon", new UIDefaults.LazyValue() { @@ -823,80 +980,199 @@ public class MetalLookAndFeel extends BasicLookAndFeel return MetalIconFactory.getRadioButtonIcon(); } }, - + "RadioButton.focus", MetalLookAndFeel.getFocusColor(), + "RadioButton.font", MetalLookAndFeel.getControlTextFont(), + "RadioButton.foreground", getControlTextColor(), + "RadioButton.highlight", getControlHighlight(), + "RadioButton.light", getControlHighlight(), + "RadioButton.select", getControlShadow(), + "RadioButton.shadow", getControlShadow(), + + "RadioButtonMenuItem.acceleratorFont", new Font("Dialog", Font.PLAIN, 10), + "RadioButtonMenuItem.acceleratorForeground", getAcceleratorForeground(), + "RadioButtonMenuItem.acceleratorSelectionForeground", getAcceleratorSelectedForeground(), + "RadioButtonMenuItem.background", getMenuBackground(), "RadioButtonMenuItem.border", new MetalBorders.MenuItemBorder(), "RadioButtonMenuItem.borderPainted", Boolean.TRUE, "RadioButtonMenuItem.checkIcon", MetalIconFactory.getRadioButtonMenuItemIcon(), + "RadioButtonMenuItem.disabledForeground", getMenuDisabledForeground(), "RadioButtonMenuItem.font", MetalLookAndFeel.getControlTextFont(), + "RadioButtonMenuItem.foreground", getMenuForeground(), "RadioButtonMenuItem.margin", new InsetsUIResource(2, 2, 2, 2), "RadioButtonMenuItem.selectionBackground", MetalLookAndFeel.getMenuSelectedBackground(), "RadioButtonMenuItem.selectionForeground", MetalLookAndFeel.getMenuSelectedForeground(), - "ScrollBar.background", new ColorUIResource(getControl()), - "ScrollBar.shadow", new ColorUIResource(getControlShadow()), - "ScrollBar.thumb", new ColorUIResource(getPrimaryControlShadow()), - "ScrollBar.thumbDarkShadow", - new ColorUIResource(getPrimaryControlDarkShadow()), - "ScrollBar.thumbHighlight", - new ColorUIResource(getPrimaryControl()), + "ScrollBar.background", getControl(), + "ScrollBar.darkShadow", getControlDarkShadow(), + "ScrollBar.foreground", getControl(), + "ScrollBar.highlight", getControlHighlight(), + "ScrollBar.shadow", getControlShadow(), + "ScrollBar.thumb", getPrimaryControlShadow(), + "ScrollBar.thumbDarkShadow", getControlDarkShadow(), + "ScrollBar.thumbHighlight", getPrimaryControl(), + "ScrollBar.thumbShadow", getPrimaryControlDarkShadow(), + "ScrollBar.track", getControl(), + "ScrollBar.trackHighlight", getControlDarkShadow(), + "ScrollBar.width", new Integer(17), + + "ScrollPane.background", getControl(), + "ScrollPane.border", new MetalBorders.ScrollPaneBorder(), + "ScrollPane.foreground", getControlTextColor(), - "SplitPane.darkShadow", - new ColorUIResource(getControlDarkShadow()), - "SplitPane.highlight", - new ColorUIResource(getControlHighlight()), + "Separator.background", getSeparatorBackground(), + "Separator.foreground", getSeparatorForeground(), + "Separator.highlight", getControlHighlight(), + "Separator.shadow", getControlShadow(), + "Slider.background", getControl(), + "Slider.focus", getFocusColor(), "Slider.focusInsets", new InsetsUIResource(0, 0, 0, 0), + "Slider.foreground", getPrimaryControlShadow(), + "Slider.highlight", getControlHighlight(), "Slider.horizontalThumbIcon", MetalIconFactory.getHorizontalSliderThumbIcon(), + "Slider.majorTickLength", new Integer(6), + "Slider.shadow", getControlShadow(), + "Slider.trackWidth", new Integer(7), "Slider.verticalThumbIcon", MetalIconFactory.getVerticalSliderThumbIcon(), - "Slider.trackWidth", new Integer(7), - "Slider.majorTickLength", new Integer(6), - + + "Spinner.background", getControl(), + "Spinner.font", new FontUIResource("Dialog", Font.BOLD, 12), + "Spinner.foreground", getControl(), + + "SplitPane.background", getControl(), + "SplitPane.darkShadow", getControlDarkShadow(), + "SplitPane.dividerFocusColor", getPrimaryControl(), + "SplitPane.highlight", getControlHighlight(), + "SplitPane.shadow", getControlShadow(), + + "SplitPaneDivider.draggingColor", Color.DARK_GRAY, + + "TabbedPane.background", getControlShadow(), + "TabbedPane.darkShadow", getControlDarkShadow(), + "TabbedPane.focus", getPrimaryControlDarkShadow(), "TabbedPane.font", new FontUIResource("Dialog", Font.BOLD, 12), - "TabbedPane.tabInsets", new InsetsUIResource(0, 9, 1, 9), + "TabbedPane.foreground", getControlTextColor(), + "TabbedPane.highlight", getControlHighlight(), + "TabbedPane.light", getControl(), + "TabbedPane.selected", getControl(), + "TabbedPane.selectHighlight", getControlHighlight(), "TabbedPane.selectedTabPadInsets", new InsetsUIResource(2, 2, 2, 1), + "TabbedPane.shadow", getControlShadow(), + "TabbedPane.tabAreaBackground", getControl(), "TabbedPane.tabAreaInsets", new InsetsUIResource(4, 2, 0, 6), - - "ToggleButton.background", new ColorUIResource(getControl()), - "ToggleButton.border", MetalBorders.getButtonBorder(), - "ToggleButton.darkShadow", new ColorUIResource(getControlDarkShadow()), - "ToggleButton.disabledText", new ColorUIResource(getControlDisabled()), - "ToggleButton.focus", new ColorUIResource(getFocusColor()), + "TabbedPane.tabInsets", new InsetsUIResource(0, 9, 1, 9), + + "Table.background", getWindowBackground(), + "Table.focusCellBackground", getWindowBackground(), + "Table.focusCellForeground", getControlTextColor(), + "Table.foreground", getControlTextColor(), + "Table.focusCellHighlightBorder", + new BorderUIResource.LineBorderUIResource(getControlShadow()), + "Table.focusCellBackground", getWindowBackground(), + "Table.gridColor", getControlDarkShadow(), + "Table.selectionBackground", new ColorUIResource(204, 204, 255), + "Table.selectionForeground", new ColorUIResource(0, 0, 0), + + "TableHeader.background", getControl(), + "TableHeader.cellBorder", new MetalBorders.TableHeaderBorder(), + "TableHeader.foreground", getControlTextColor(), + + "TextArea.background", getWindowBackground(), + "TextArea.caretForeground", getUserTextColor(), + "TextArea.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "TextArea.foreground", getUserTextColor(), + "TextArea.inactiveForeground", getInactiveSystemTextColor(), + "TextArea.selectionBackground", getTextHighlightColor(), + "TextArea.selectionForeground", getHighlightedTextColor(), + + "TextField.background", getWindowBackground(), + "TextField.border", + new BorderUIResource(MetalBorders.getTextFieldBorder()), + "TextField.caretForeground", getUserTextColor(), + "TextField.darkShadow", getControlDarkShadow(), + "TextField.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "TextField.foreground", getUserTextColor(), + "TextField.highlight", getControlHighlight(), + "TextField.inactiveBackground", getControl(), + "TextField.inactiveForeground", getInactiveSystemTextColor(), + "TextField.light", getControlHighlight(), + "TextField.selectionBackground", getTextHighlightColor(), + "TextField.selectionForeground", getHighlightedTextColor(), + "TextField.shadow", getControlShadow(), + + "TextPane.background", getWindowBackground(), + "TextPane.caretForeground", getUserTextColor(), + "TextPane.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "TextPane.foreground", getUserTextColor(), + "TextPane.inactiveForeground", getInactiveSystemTextColor(), + "TextPane.selectionBackground", getTextHighlightColor(), + "TextPane.selectionForeground", getHighlightedTextColor(), + + "TitledBorder.font", new FontUIResource("Dialog", Font.BOLD, 12), + "TitledBorder.titleColor", getSystemTextColor(), + + "ToggleButton.background", getControl(), + "ToggleButton.border", MetalBorders.getToggleButtonBorder(), + "ToggleButton.darkShadow", getControlDarkShadow(), + "ToggleButton.disabledText", getInactiveControlTextColor(), + "ToggleButton.focus", getFocusColor(), "ToggleButton.font", getControlTextFont(), - "ToggleButton.foreground", new ColorUIResource(getSystemTextColor()), - "ToggleButton.highlight", new ColorUIResource(getControlHighlight()), - "ToggleButton.light", new ColorUIResource(getControlHighlight()), - "ToggleButton.margin", new Insets(2, 14, 2, 14), - "ToggleButton.select", new ColorUIResource(getPrimaryControlShadow()), - "ToggleButton.shadow", new ColorUIResource(getPrimaryControlShadow()), - - "Tree.openIcon", MetalIconFactory.getTreeFolderIcon(), + "ToggleButton.foreground", getControlTextColor(), + "ToggleButton.highlight", getControlHighlight(), + "ToggleButton.light", getControlHighlight(), + "ToggleButton.margin", new InsetsUIResource(2, 14, 2, 14), + "ToggleButton.select", getControlShadow(), + "ToggleButton.shadow", getControlShadow(), + + "ToolBar.background", getMenuBackground(), + "ToolBar.darkShadow", getControlDarkShadow(), + "ToolBar.dockingBackground", getMenuBackground(), + "ToolBar.dockingForeground", getPrimaryControlDarkShadow(), + "ToolBar.floatingBackground", getMenuBackground(), + "ToolBar.floatingForeground", getPrimaryControl(), + "ToolBar.font", new FontUIResource("Dialog", Font.BOLD, 12), + "ToolBar.foreground", getMenuForeground(), + "ToolBar.highlight", getControlHighlight(), + "ToolBar.light", getControlHighlight(), + "ToolBar.shadow", getControlShadow(), + "ToolBar.border", new MetalBorders.ToolBarBorder(), + + "ToolTip.background", getPrimaryControl(), + "ToolTip.backgroundInactive", getControl(), + "ToolTip.border", new BorderUIResource.LineBorderUIResource(getPrimaryControlDarkShadow(), 1), + "ToolTip.borderInactive", new BorderUIResource.LineBorderUIResource(getControlDarkShadow(), 1), + "ToolTip.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "ToolTip.foreground", getPrimaryControlInfo(), + "ToolTip.foregroundInactive", getControlDarkShadow(), + + "Tree.background", getWindowBackground(), "Tree.closedIcon", MetalIconFactory.getTreeFolderIcon(), - "Tree.leafIcon", MetalIconFactory.getTreeLeafIcon(), "Tree.collapsedIcon", MetalIconFactory.getTreeControlIcon(true), "Tree.expandedIcon", MetalIconFactory.getTreeControlIcon(false), - "Tree.font", new FontUIResource(new Font("Helvetica", Font.PLAIN, 12)), - "Tree.background", new ColorUIResource(Color.white), - "Tree.foreground", new ColorUIResource(new Color(204, 204, 255)), - "Tree.hash", new ColorUIResource(new Color(204, 204, 255)), + "Tree.font", new FontUIResource("Dialog", Font.PLAIN, 12), + "Tree.foreground", getUserTextColor(), + "Tree.hash", getPrimaryControl(), + "Tree.leafIcon", MetalIconFactory.getTreeLeafIcon(), "Tree.leftChildIndent", new Integer(7), + "Tree.line", getPrimaryControl(), + "Tree.openIcon", MetalIconFactory.getTreeFolderIcon(), "Tree.rightChildIndent", new Integer(13), "Tree.rowHeight", new Integer(20), "Tree.scrollsOnExpand", Boolean.TRUE, - "Tree.selectionBackground", new ColorUIResource(new Color(204, 204, 255)), - "Tree.nonSelectionBackground", new ColorUIResource(Color.white), - "Tree.selectionBorderColor", new ColorUIResource(new Color(102, 102, 153)), + "Tree.selectionBackground", getTextHighlightColor(), "Tree.selectionBorder", new BorderUIResource.LineBorderUIResource(new Color(102, 102, 153)), - "Tree.nonSelectionBorder", new BorderUIResource.LineBorderUIResource(Color.white), - "Tree.selectionForeground", new ColorUIResource(Color.black), - "Tree.textBackground", new ColorUIResource(new Color(204, 204, 255)), - "Tree.textForeground", new ColorUIResource(Color.black), - "Tree.selectionForeground", new ColorUIResource(Color.black), - "PopupMenu.border", new MetalBorders.PopupMenuBorder() + "Tree.selectionBorderColor", getFocusColor(), + "Tree.selectionForeground", getHighlightedTextColor(), + "Tree.textBackground", getWindowBackground(), + "Tree.textForeground", getUserTextColor(), + + "Viewport.background", getControl(), + "Viewport.foreground", getUserTextColor() }; defaults.putDefaults(myDefaults); } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalPopupMenuSeparatorUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalPopupMenuSeparatorUI.java index ec9bf2b5586..44a2d3bcd6a 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalPopupMenuSeparatorUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalPopupMenuSeparatorUI.java @@ -39,8 +39,12 @@ exception statement from your version. */ package javax.swing.plaf.metal; import javax.swing.JComponent; +import javax.swing.JPopupMenu; import javax.swing.plaf.ComponentUI; +/** + * A UI delegate for the {@link JPopupMenu.Separator} component. + */ public class MetalPopupMenuSeparatorUI extends MetalSeparatorUI { @@ -50,7 +54,7 @@ public class MetalPopupMenuSeparatorUI private static MetalPopupMenuSeparatorUI instance = null; /** - * Constructs a new instance of MetalPopupMenuSeparatorUI. + * Constructs a new instance of <code>MetalPopupMenuSeparatorUI</code>. */ public MetalPopupMenuSeparatorUI() { @@ -58,11 +62,11 @@ public class MetalPopupMenuSeparatorUI } /** - * Returns an instance of MetalPopupMenuSeparatorUI. + * Returns a shared instance of <code>MetalPopupMenuSeparatorUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalPopupMenuSeparatorUI + * @return A shared instance of <code>MetalPopupMenuSeparatorUI</code>. */ public static ComponentUI createUI(JComponent component) { diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalProgressBarUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalProgressBarUI.java index 96d1988fd3d..0f28818b96c 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalProgressBarUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalProgressBarUI.java @@ -38,20 +38,22 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Insets; + import javax.swing.JComponent; +import javax.swing.JProgressBar; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicProgressBarUI; -public class MetalProgressBarUI - extends BasicProgressBarUI -{ - - // FIXME: maybe replace by a Map of instances when this becomes stateful - /** The shared UI instance for MetalProgressBarUIs */ - private static MetalProgressBarUI instance = null; - +/** + * A UI delegate for the {@link JProgressBar} component. + */ +public class MetalProgressBarUI extends BasicProgressBarUI +{ /** - * Constructs a new instance of MetalProgressBarUI. + * Constructs a new instance of <code>MetalProgressBarUI</code>. */ public MetalProgressBarUI() { @@ -59,16 +61,87 @@ public class MetalProgressBarUI } /** - * Returns an instance of MetalProgressBarUI. + * Returns a new instance of <code>MetalProgressBarUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalProgressBarUI + * @return A new instance of <code>MetalProgressBarUI</code>. */ public static ComponentUI createUI(JComponent component) { - if (instance == null) - instance = new MetalProgressBarUI(); - return instance; + return new MetalProgressBarUI(); + } + + /** + * Performs the painting for determinate progress bars. This calls the + * superclass behaviour and then adds some highlighting to the upper and left + * edge of the progress bar. + * + * @param g the graphics context + * @param c not used here + */ + public void paintDeterminate(Graphics g, JComponent c) + { + super.paintDeterminate(g, c); + Color saved = g.getColor(); + Insets i = progressBar.getInsets(); + int w = progressBar.getWidth(); + int h = progressBar.getHeight(); + int orientation = progressBar.getOrientation(); + + Color shadow = MetalLookAndFeel.getControlShadow(); + g.setColor(shadow); + + g.drawLine(i.left, i.top, w - i.right, i.top); + g.drawLine(i.left, i.top, i.left, h - i.bottom); + int full = getAmountFull(i, w, h); + if (full > 0) + { + Color darkShadow = MetalLookAndFeel.getPrimaryControlDarkShadow(); + g.setColor(darkShadow); + if (orientation == JProgressBar.HORIZONTAL) + { + g.drawLine(i.left, i.top, i.left, h - i.bottom); + g.drawLine(i.left, i.top, i.left + full - 1, i.top); + } + else + { + if (full >= (h - i.top - i.bottom)) + g.drawLine(i.left, i.top, w - i.right, i.top); + g.drawLine(i.left, h - i.bottom, i.left, h - i.bottom - full); + } + } + g.setColor(saved); + } + + /** + * Performs the painting for indeterminate progress bars. This calls the + * superclass behaviour and then adds some highlighting to the upper and left + * edge of the progress bar. + * + * @param g the graphics context + * @param c not used here + */ + public void paintIndeterminate(Graphics g, JComponent c) + { + super.paintIndeterminate(g, c); + Color saved = g.getColor(); + Insets i = progressBar.getInsets(); + int w = progressBar.getWidth(); + int h = progressBar.getHeight(); + Color shadow = MetalLookAndFeel.getControlShadow(); + g.setColor(shadow); + g.drawLine(i.left, i.top, w - i.right, i.top); + g.drawLine(i.left, i.top, i.left, h - i.bottom); + + boxRect = getBox(boxRect); + Color darkShadow = MetalLookAndFeel.getPrimaryControlDarkShadow(); + g.setColor(darkShadow); + int orientation = progressBar.getOrientation(); + if (orientation == JProgressBar.HORIZONTAL) + g.drawLine(boxRect.x, i.top, boxRect.x + boxRect.width - 1, i.top); + else + g.drawLine(i.left, boxRect.y, i.left, boxRect.y + boxRect.height - 1); + g.setColor(saved); } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java index a668f914e43..f37626e630f 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalRadioButtonUI.java @@ -38,20 +38,38 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; + +import javax.swing.AbstractButton; import javax.swing.JComponent; +import javax.swing.JRadioButton; +import javax.swing.UIDefaults; +import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicRadioButtonUI; + +/** + * A UI delegate for the {@link JRadioButton} component. + */ public class MetalRadioButtonUI extends BasicRadioButtonUI { - // FIXME: maybe replace by a Map of instances when this becomes stateful - /** The shared UI instance for JRadioButtons. */ - private static MetalRadioButtonUI instance = null; - + /** Used to draw the focus rectangle. */ + protected Color focusColor; + + /** Used to fill the icon when the button is pressed. */ + protected Color selectColor; + + /** Used to draw disabled text. */ + protected Color disabledTextColor; + /** - * Constructs a new instance of MetalRadioButtonUI. + * Constructs a new instance of <code>MetalRadioButtonUI</code>. */ public MetalRadioButtonUI() { @@ -59,16 +77,108 @@ public class MetalRadioButtonUI } /** - * Returns an instance of MetalRadioButtonUI. + * Returns a new instance of <code>MetalRadioButtonUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalRadioButtonUI + * @return A new instance of <code>MetalRadioButtonUI</code>. */ public static ComponentUI createUI(JComponent component) { - if (instance == null) - instance = new MetalRadioButtonUI(); - return instance; + return new MetalRadioButtonUI(); + } + + /** + * Sets the default values for the specified button. + * + * @param b the button. + */ + public void installDefaults(AbstractButton b) + { + super.installDefaults(b); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + disabledTextColor = defaults.getColor("RadioButton.disabledText"); + focusColor = defaults.getColor("RadioButton.focus"); + selectColor = defaults.getColor("RadioButton.select"); + } + + /** + * Clears any defaults set in the installDefaults() method. + * + * @param b the {@link JRadioButton}. + */ + protected void uninstallDefaults(AbstractButton b) + { + super.uninstallDefaults(b); + disabledTextColor = null; + focusColor = null; + selectColor = null; + } + + /** + * Returns the color used to fill the {@link JRadioButton}'s icon when the + * button is pressed. The default color is obtained from the + * {@link UIDefaults} via an entry with the key + * <code>RadioButton.select</code>. + * + * @return The select color. + */ + protected Color getSelectColor() + { + return selectColor; + } + + /** + * Returns the color for the {@link JRadioButton}'s text when the button is + * disabled. The default color is obtained from the {@link UIDefaults} via + * an entry with the key <code>RadioButton.disabledText</code>. + * + * @return The disabled text color. + */ + protected Color getDisabledTextColor() + { + return disabledTextColor; + } + + /** + * Returns the color used to draw the focus rectangle when the + * {@link JRadioButton} has the focus. The default color is obtained from + * the {@link UIDefaults} via an entry with the key + * <code>RadioButton.focus</code>. + * + * @return The color used to draw the focus rectangle. + * + * @see #paintFocus(Graphics, Rectangle, Dimension) + */ + protected Color getFocusColor() + { + return focusColor; + } + + /** + * Paints the {@link JRadioButton}. + * + * @param g the graphics device. + * @param c the component (an instance of {@link JRadioButton}). + */ + public void paint(Graphics g, JComponent c) + { + super.paint(g, c); + // FIXME: disabled text isn't being drawn correctly, it's possible that + // it could be done here... + } + + /** + * Paints the focus rectangle for the {@link JRadioButton}. + * + * @param g the graphics device. + * @param t the bounding rectangle for the text. + * @param d ??? + */ + protected void paintFocus(Graphics g, Rectangle t, Dimension d) + { + g.setColor(focusColor); + g.drawRect(t.x - 1, t.y + 2, t.width + 2, t.height - 4); } + } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java index 4196a4e477c..faed80382d0 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java @@ -39,9 +39,16 @@ exception statement from your version. */ package javax.swing.plaf.metal; import javax.swing.JComponent; +import javax.swing.JRootPane; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicRootPaneUI; +/** + * A UI delegate for the {@link JRootPane} component. This class is not fully + * implemented. + * + * @since 1.4 + */ public class MetalRootPaneUI extends BasicRootPaneUI { @@ -51,7 +58,7 @@ public class MetalRootPaneUI private static MetalRootPaneUI instance = null; /** - * Constructs a new instance of MetalRootPaneUI. + * Constructs a shared instance of <code>MetalRootPaneUI</code>. */ public MetalRootPaneUI() { @@ -59,11 +66,11 @@ public class MetalRootPaneUI } /** - * Returns an instance of MetalRootPaneUI. + * Returns a shared instance of <code>MetalRootPaneUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalRootPaneUI + * @return A shared instance of <code>MetalRootPaneUI</code>. */ public static ComponentUI createUI(JComponent component) { diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java index 526dfb50ae2..9602ade9947 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java @@ -38,30 +38,106 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Rectangle; -import java.util.HashMap; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import javax.swing.JButton; import javax.swing.JComponent; +import javax.swing.JScrollBar; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicScrollBarUI; -public class MetalScrollBarUI - extends BasicScrollBarUI +/** + * A UI delegate for the {@link JScrollBar} component. + */ +public class MetalScrollBarUI extends BasicScrollBarUI { + + /** + * A property change handler for the UI delegate that monitors for + * changes to the "JScrollBar.isFreeStanding" property, and updates + * the buttons and track rendering as appropriate. + */ + class MetalScrollBarPropertyChangeHandler + extends BasicScrollBarUI.PropertyChangeHandler + { + /** + * Creates a new handler. + * + * @see #createPropertyChangeListener() + */ + public MetalScrollBarPropertyChangeHandler() + { + // Nothing to do here. + } + + /** + * Handles a property change event. If the event name is + * <code>JSlider.isFreeStanding</code>, this method updates the + * delegate, otherwise the event is passed up to the super class. + * + * @param e the property change event. + */ + public void propertyChange(PropertyChangeEvent e) + { + if (e.getPropertyName().equals(FREE_STANDING_PROP)) + { + Boolean prop = (Boolean) e.getNewValue(); + isFreeStanding = (prop == null ? true : prop.booleanValue()); + if (increaseButton != null) + increaseButton.setFreeStanding(isFreeStanding); + if (decreaseButton != null) + decreaseButton.setFreeStanding(isFreeStanding); + } + else + super.propertyChange(e); + } + } + + /** The name for the 'free standing' property. */ + public static final String FREE_STANDING_PROP = "JScrollBar.isFreeStanding"; - /** The minimum thumb size */ - private static final Dimension MIN_THUMB_SIZE = new Dimension(18, 18); - - // FIXME: maybe replace by a Map of instances when this becomes stateful - /** The shared UI instance for JScrollBars. */ - private static HashMap instances = null; + /** The minimum thumb size for a scroll bar that is not free standing. */ + private static final Dimension MIN_THUMB_SIZE = new Dimension(15, 15); + /** The minimum thumb size for a scroll bar that is free standing. */ + private static final Dimension MIN_THUMB_SIZE_FREE_STANDING + = new Dimension(17, 17); + + /** The button that increases the value in the scroll bar. */ + protected MetalScrollButton increaseButton; + + /** The button that decreases the value in the scroll bar. */ + protected MetalScrollButton decreaseButton; + + /** + * The scroll bar width. + */ + protected int scrollBarWidth; + + /** + * A flag that indicates whether the scroll bar is "free standing", which + * means it has complete borders and can be used anywhere in the UI. A + * scroll bar which is not free standing has borders missing from one + * side, and relies on being part of another container with its own borders + * to look right visually. */ + protected boolean isFreeStanding = true; + + /** + * The color for the scroll bar shadow (this is read from the UIDefaults in + * the installDefaults() method). + */ + Color scrollBarShadowColor; + /** - * Constructs a new instance of MetalScrollBarUI. + * Constructs a new instance of <code>MetalScrollBarUI</code>, with no + * specific initialisation. */ public MetalScrollBarUI() { @@ -69,28 +145,192 @@ public class MetalScrollBarUI } /** - * Returns an instance of MetalScrollBarUI. + * Returns a new instance of <code>MetalScrollBarUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalScrollBarUI + * @return An instance of MetalScrollBarUI */ public static ComponentUI createUI(JComponent component) { - if (instances == null) - instances = new HashMap(); + return new MetalScrollBarUI(); + } + + /** + * Installs the defaults. + */ + protected void installDefaults() + { + // need to initialise isFreeStanding before calling the super class, + // so that the value is set when createIncreaseButton() and + // createDecreaseButton() are called (unless there is somewhere earlier + // that we can do this). + Boolean prop = (Boolean) scrollbar.getClientProperty(FREE_STANDING_PROP); + isFreeStanding = (prop == null ? true : prop.booleanValue()); + scrollBarShadowColor = UIManager.getColor("ScrollBar.shadow"); + super.installDefaults(); + } + + /** + * Creates a property change listener for the delegate to use. This + * overrides the method to provide a custom listener for the + * {@link MetalLookAndFeel} that can handle the + * <code>JScrollBar.isFreeStanding</code> property. + * + * @return A property change listener. + */ + protected PropertyChangeListener createPropertyChangeListener() + { + return new MetalScrollBarPropertyChangeHandler(); + } + + /** + * Creates a new button to use as the control at the lower end of the + * {@link JScrollBar}. + * + * @param orientation the orientation of the button ({@link #NORTH}, + * {@link #SOUTH}, {@link #EAST} or {@link #WEST}). + * + * @return The button. + */ + protected JButton createDecreaseButton(int orientation) + { + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + scrollBarWidth = defaults.getInt("ScrollBar.width"); + decreaseButton = new MetalScrollButton(orientation, scrollBarWidth, + isFreeStanding); + return decreaseButton; + } - Object o = instances.get(component); - MetalScrollBarUI instance; - if (o == null) + /** + * Creates a new button to use as the control at the upper end of the + * {@link JScrollBar}. + * + * @param orientation the orientation of the button ({@link #NORTH}, + * {@link #SOUTH}, {@link #EAST} or {@link #WEST}). + * + * @return The button. + */ + protected JButton createIncreaseButton(int orientation) + { + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + scrollBarWidth = defaults.getInt("ScrollBar.width"); + increaseButton = new MetalScrollButton(orientation, scrollBarWidth, + isFreeStanding); + return increaseButton; + } + + /** + * Paints the track for the scrollbar. + * + * @param g the graphics device. + * @param c the component. + * @param trackBounds the track bounds. + */ + protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) + { + g.setColor(MetalLookAndFeel.getControl()); + g.fillRect(trackBounds.x, trackBounds.y, trackBounds.width, + trackBounds.height); + if (scrollbar.getOrientation() == HORIZONTAL) + paintTrackHorizontal(g, c, trackBounds.x, trackBounds.y, + trackBounds.width, trackBounds.height); + else + paintTrackVertical(g, c, trackBounds.x, trackBounds.y, + trackBounds.width, trackBounds.height); + + } + + /** + * Paints the track for a horizontal scrollbar. + * + * @param g the graphics device. + * @param c the component. + * @param x the x-coordinate for the track bounds. + * @param y the y-coordinate for the track bounds. + * @param w the width for the track bounds. + * @param h the height for the track bounds. + */ + private void paintTrackHorizontal(Graphics g, JComponent c, + int x, int y, int w, int h) + { + if (c.isEnabled()) { - instance = new MetalScrollBarUI(); - instances.put(component, instance); + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(x, y, x, y + h - 1); + g.drawLine(x, y, x + w - 1, y); + g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); + + g.setColor(scrollBarShadowColor); + g.drawLine(x + 1, y + 1, x + 1, y + h - 1); + g.drawLine(x + 1, y + 1, x + w - 2, y + 1); + + if (isFreeStanding) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(x, y + h - 2, x + w - 1, y + h - 2); + g.setColor(scrollBarShadowColor); + g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); + } } else - instance = (MetalScrollBarUI) o; - - return instance; + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + if (isFreeStanding) + g.drawRect(x, y, w - 1, h - 1); + else + { + g.drawLine(x, y, x + w - 1, y); + g.drawLine(x, y, x, y + h - 1); + g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); + } + } + } + + /** + * Paints the track for a vertical scrollbar. + * + * @param g the graphics device. + * @param c the component. + * @param x the x-coordinate for the track bounds. + * @param y the y-coordinate for the track bounds. + * @param w the width for the track bounds. + * @param h the height for the track bounds. + */ + private void paintTrackVertical(Graphics g, JComponent c, + int x, int y, int w, int h) + { + if (c.isEnabled()) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(x, y, x, y + h - 1); + g.drawLine(x, y, x + w - 1, y); + g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); + + g.setColor(scrollBarShadowColor); + g.drawLine(x + 1, y + 1, x + w - 1, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + h - 2); + + if (isFreeStanding) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(x + w - 2, y, x + w - 2, y + h - 1); + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(x + w - 1, y, x + w - 1, y + h - 1); + } + } + else + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + if (isFreeStanding) + g.drawRect(x, y, w - 1, h - 1); + else + { + g.drawLine(x, y, x + w - 1, y); + g.drawLine(x, y, x, y + h - 1); + g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); + } + } } /** @@ -102,45 +342,139 @@ public class MetalScrollBarUI */ protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { - // first we fill the background - g.setColor(thumbColor); - g.fillRect(thumbBounds.x, thumbBounds.y, thumbBounds.width, - thumbBounds.height); + // a disabled scrollbar has no thumb in the metal look and feel + if (!c.isEnabled()) + return; + if (scrollbar.getOrientation() == HORIZONTAL) + paintThumbHorizontal(g, c, thumbBounds); + else + paintThumbVertical(g, c, thumbBounds); - // draw the outer dark line - g.setColor(thumbDarkShadowColor); - g.drawRect(thumbBounds.x, thumbBounds.y, thumbBounds.width - 1, - thumbBounds.height - 1); + // draw the pattern + MetalUtils.fillMetalPattern(c, g, thumbBounds.x + 3, thumbBounds.y + 3, + thumbBounds.width - 6, thumbBounds.height - 6, + thumbHighlightColor, thumbLightShadowColor); + } - // draw the inner light line + /** + * Paints the thumb for a horizontal scroll bar. + * + * @param g the graphics device. + * @param c the scroll bar component. + * @param thumbBounds the thumb bounds. + */ + private void paintThumbHorizontal(Graphics g, JComponent c, + Rectangle thumbBounds) + { + int x = thumbBounds.x; + int y = thumbBounds.y; + int w = thumbBounds.width; + int h = thumbBounds.height; + + // first we fill the background + g.setColor(thumbColor); + if (isFreeStanding) + g.fillRect(x, y, w, h - 1); + else + g.fillRect(x, y, w, h); + + // then draw the dark box + g.setColor(thumbLightShadowColor); + if (isFreeStanding) + g.drawRect(x, y, w - 1, h - 2); + else + { + g.drawLine(x, y, x + w - 1, y); + g.drawLine(x, y, x, y + h - 1); + g.drawLine(x + w - 1, y, x + w - 1, y + h -1); + } + + // then the highlight g.setColor(thumbHighlightColor); - g.drawLine(thumbBounds.x + 1, thumbBounds.y + 1, - thumbBounds.x + thumbBounds.width - 2, - thumbBounds.y + 1); - g.drawLine(thumbBounds.x + 1, thumbBounds.y + 1, - thumbBounds.x + 1, - thumbBounds.y + thumbBounds.height - 2); - + if (isFreeStanding) + { + g.drawLine(x + 1, y + 1, x + w - 3, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + h - 3); + } + else + { + g.drawLine(x + 1, y + 1, x + w - 3, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + h - 1); + } + // draw the shadow line UIDefaults def = UIManager.getLookAndFeelDefaults(); g.setColor(def.getColor("ScrollBar.shadow")); - g.drawLine(thumbBounds.x + 1, thumbBounds.y + thumbBounds.height, - thumbBounds.x + thumbBounds.width, - thumbBounds.y + thumbBounds.height); + g.drawLine(x + w, y + 1, x + w, y + h - 1); - // draw the pattern - MetalUtils.fillMetalPattern(g, thumbBounds.x + 3, thumbBounds.y + 3, - thumbBounds.width - 6, thumbBounds.height - 6, - thumbHighlightColor, thumbDarkShadowColor); } - + /** - * This method returns the minimum thumb size. + * Paints the thumb for a vertical scroll bar. + * + * @param g the graphics device. + * @param c the scroll bar component. + * @param thumbBounds the thumb bounds. + */ + private void paintThumbVertical(Graphics g, JComponent c, + Rectangle thumbBounds) + { + int x = thumbBounds.x; + int y = thumbBounds.y; + int w = thumbBounds.width; + int h = thumbBounds.height; + + // first we fill the background + g.setColor(thumbColor); + if (isFreeStanding) + g.fillRect(x, y, w - 1, h); + else + g.fillRect(x, y, w, h); + + // then draw the dark box + g.setColor(thumbLightShadowColor); + if (isFreeStanding) + g.drawRect(x, y, w - 2, h - 1); + else + { + g.drawLine(x, y, x + w - 1, y); + g.drawLine(x, y, x, y + h - 1); + g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); + } + + // then the highlight + g.setColor(thumbHighlightColor); + if (isFreeStanding) + { + g.drawLine(x + 1, y + 1, x + w - 3, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + h - 3); + } + else + { + g.drawLine(x + 1, y + 1, x + w - 1, y + 1); + g.drawLine(x + 1, y + 1, x + 1, y + h - 3); + } + + // draw the shadow line + UIDefaults def = UIManager.getLookAndFeelDefaults(); + g.setColor(def.getColor("ScrollBar.shadow")); + g.drawLine(x + 1, y + h, x + w - 2, y + h); + } + + /** + * Returns the minimum thumb size. For a free standing scroll bar the + * minimum size is <code>17 x 17</code> pixels, whereas for a non free + * standing scroll bar the minimum size is <code>15 x 15</code> pixels. * * @return The minimum thumb size. */ protected Dimension getMinimumThumbSize() { - return MIN_THUMB_SIZE; + if (isFreeStanding) + return MIN_THUMB_SIZE_FREE_STANDING; + else + return MIN_THUMB_SIZE; } + } + diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java b/libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java new file mode 100644 index 00000000000..84f9cfe494e --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java @@ -0,0 +1,483 @@ +/* MetalScrollButton.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.plaf.metal; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; + +import javax.swing.SwingUtilities; +import javax.swing.plaf.basic.BasicArrowButton; + +/** + * A button used by the {@link MetalScrollBarUI}. The button appearance + * varies according to the button direction, whether or not it is part of a + * "free standing" scroll bar, and the current state of the button. + */ +public class MetalScrollButton extends BasicArrowButton +{ + + /** + * The maximum size for buttons. + * @see #getMaximumSize() + */ + private static Dimension maximumSize; + + /** The width of the button. */ + private int buttonWidth; + + /** + * A flag that indicates whether the button is part of a free standing + * scroll bar. This affects how the border is drawn. + */ + private boolean freeStanding; + + /** + * Creates a new button. + * + * @param direction the direction (this should be one of {@link #NORTH}, + * {@link #SOUTH}, {@link #EAST} and {@link #WEST}, but + * this is not enforced). + * @param width the button width. + * @param freeStanding a flag indicating whether the scroll button is free + * standing or not. + */ + public MetalScrollButton(int direction, int width, boolean freeStanding) + { + super(direction); + buttonWidth = width; + this.freeStanding = freeStanding; + } + + /** + * Returns the button width. + * + * @return The button width. + */ + public int getButtonWidth() + { + return buttonWidth; + } + + /** + * Sets the free standing flag. This controls how the button border is + * drawn. + * + * @param freeStanding the new value of the flag. + */ + public void setFreeStanding(boolean freeStanding) + { + this.freeStanding = freeStanding; + } + + /** + * Paints the button. + * + * @param g the graphics device. + */ + public void paint(Graphics g) + { + Rectangle bounds = SwingUtilities.getLocalBounds(this); + + // fill the background + if (getModel().isPressed()) + g.setColor(MetalLookAndFeel.getControlShadow()); + else + g.setColor(MetalLookAndFeel.getControl()); + g.fillRect(0, 0, bounds.width, bounds.height); + + paintArrow(g, bounds.width, bounds.height); + + // paint a border manually - I tried using a real (custom) Border + // but couldn't get it to stay set for the button, something was + // overwriting it... + if (freeStanding) + { + if (direction == WEST) + paintWestBorderFreeStanding(g, bounds.width, bounds.height); + else if (direction == EAST) + paintEastBorderFreeStanding(g, bounds.width, bounds.height); + else if (direction == SOUTH) + paintSouthBorderFreeStanding(g, bounds.width, bounds.height); + else // asume NORTH + paintNorthBorderFreeStanding(g, bounds.width, bounds.height); + } + else + { + if (direction == WEST) + paintWestBorder(g, bounds.width, bounds.height); + else if (direction == EAST) + paintEastBorder(g, bounds.width, bounds.height); + else if (direction == SOUTH) + paintSouthBorder(g, bounds.width, bounds.height); + else // asume NORTH + paintNorthBorder(g, bounds.width, bounds.height); + } + } + + private void paintArrow(Graphics g, int w, int h) + { + if (isEnabled()) + g.setColor(MetalLookAndFeel.getBlack()); + else + g.setColor(MetalLookAndFeel.getControlDisabled()); + + if (direction == SOUTH) + { + int x = w / 2; + int y = h / 2 + 2; + for (int i = 1; i < 5; i++) + g.drawLine(x - i, y - i, x + i - 1, y - i); + } + else if (direction == EAST) + { + int x = w / 2 + 2; + int y = h / 2; + for (int i = 1; i < 5; i++) + g.drawLine(x - i, y - i, x - i, y + i - 1); + } + else if (direction == WEST) + { + int x = w / 2 - 3; + int y = h / 2; + for (int i = 1; i < 5; i++) + g.drawLine(x + i, y - i, x + i, y + i - 1); + } + else // assume NORTH + { + int x = w / 2; + int y = h / 2 - 3; + for (int i = 1; i < 5; i++) + g.drawLine(x - i, y + i, x + i - 1, y + i); + } + } + /** + * Paints the border for a button with a {@link #NORTH} direction that + * belongs to a free standing scroll bar. + * + * @param g the graphics device. + * @param w the button width. + * @param h the button height. + */ + private void paintNorthBorderFreeStanding(Graphics g, int w, int h) + { + if (isEnabled()) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(0, 0, w - 2, 0); + g.drawLine(0, 0, 0, h - 1); + g.drawLine(2, h - 1, w - 2, h - 1); + g.drawLine(w - 2, 2, w - 2, h - 1); + + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(1, 1, 1, h - 2); + g.drawLine(1, 1, w - 3, 1); + g.drawLine(w - 1, 1, w - 1, h - 1); + + g.setColor(MetalLookAndFeel.getControl()); + g.drawLine(1, h - 1, 1, h - 1); + g.drawLine(w - 2, 1, w - 2, 1); + } + else + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(0, 0, w - 1, 0); + g.drawLine(w - 1, 0, w - 1, h - 1); + g.drawLine(0, 0, 0, h - 1); + } + } + + /** + * Paints the border for a button with a {@link #SOUTH} direction that + * belongs to a free standing scroll bar. + * + * @param g the graphics device. + * @param w the button width. + * @param h the button height. + */ + private void paintSouthBorderFreeStanding(Graphics g, int w, int h) + { + if (isEnabled()) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(0, 0, w - 2, 0); + g.drawLine(0, 0, 0, h - 1); + g.drawLine(2, h - 1, w - 2, h - 1); + g.drawLine(w - 2, 2, w - 2, h - 1); + + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(1, 1, 1, h - 1); + g.drawLine(1, 1, w - 1, 1); + g.drawLine(w - 1, 1, w - 1, h - 1); + + g.setColor(MetalLookAndFeel.getControl()); + g.drawLine(1, h - 1, 1, h - 1); + g.drawLine(w - 1, 1, w - 1, 1); + } + else + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(0, h - 1, w - 1, h - 1); + g.drawLine(w - 1, 0, w - 1, h - 1); + g.drawLine(0, 0, 0, h - 1); + } + } + + /** + * Paints the border for a button with an {@link #EAST} direction that + * belongs to a free standing scroll bar. + * + * @param g the graphics device. + * @param w the button width. + * @param h the button height. + */ + private void paintEastBorderFreeStanding(Graphics g, int w, int h) + { + if (isEnabled()) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(0, 0, w - 2, 0); + g.drawLine(w - 2, 0, w - 2, h - 2); + g.drawLine(0, h - 2, w - 2, h - 2); + + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(0, 1, w - 1, 1); + g.drawLine(w - 1, 1, w - 1, h - 1); + g.drawLine(0, h - 1, w - 1, h - 1); + + g.setColor(MetalLookAndFeel.getControl()); + g.drawLine(w - 2, 1, w - 2, 1); + } + else + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(0, 0, w - 1, 0); + g.drawLine(w - 1, 0, w - 1, h - 1); + g.drawLine(0, h - 1, w - 1, h - 1); + } + } + + /** + * Paints the border for a button with a {@link #WEST} direction that + * belongs to a free standing scroll bar. + * + * @param g the graphics device. + * @param w the button width. + * @param h the button height. + */ + private void paintWestBorderFreeStanding(Graphics g, int w, int h) + { + if (isEnabled()) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(0, 0, w - 1, 0); + g.drawLine(0, 0, 0, h - 2); + g.drawLine(0, h - 2, w - 1, h - 2); + + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(1, 1, w - 1, 1); + g.drawLine(1, 1, 1, h - 1); + g.drawLine(1, h - 1, w - 1, h - 1); + + g.setColor(MetalLookAndFeel.getControl()); + g.drawLine(1, h - 2, 1, h - 2); + } + else + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(0, 0, w - 1, 0); + g.drawLine(0, 0, 0, h - 1); + g.drawLine(0, h - 1, w - 1, h - 1); + } + } + + /** + * Paints the border for a button with a {@link #NORTH} direction that + * belongs to a scroll bar that is not free standing. + * + * @param g the graphics device. + * @param w the button width. + * @param h the button height. + */ + private void paintNorthBorder(Graphics g, int w, int h) + { + if (isEnabled()) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(0, 0, 0, h - 1); + + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(1, 0, 1, h - 1); + g.drawLine(1, 0, w - 1, 0); + } + else + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(0, 0, 0, h - 1); + } + } + + /** + * Paints the border for a button with a {@link #SOUTH} direction that + * belongs to a scroll bar that is not free standing. + * + * @param g the graphics device. + * @param w the button width. + * @param h the button height. + */ + private void paintSouthBorder(Graphics g, int w, int h) + { + if (isEnabled()) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(0, 0, 0, h - 1); + g.drawLine(0, h - 1, w - 1, h - 1); + + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(1, 0, 1, h - 1); + g.drawLine(1, 0, w - 1, 0); + + g.setColor(MetalLookAndFeel.getControl()); + g.drawLine(1, h - 1, 1, h - 1); + } + else + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(0, 0, 0, h - 1); + } + } + + /** + * Paints the border for a button with an {@link #EAST} direction that + * belongs to a scroll bar that is not free standing. + * + * @param g the graphics device. + * @param w the button width. + * @param h the button height. + */ + private void paintEastBorder(Graphics g, int w, int h) + { + if (isEnabled()) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(0, 0, w - 1, 0); + g.drawLine(w - 1, 2, w - 1, h - 1); + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(0, 1, w - 2, 1); + g.drawLine(0, 1, 0, h - 1); + } + else + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(0, 0, w - 1, 0); + } + } + + /** + * Paints the border for a button with a {@link #WEST} direction that + * belongs to a scroll bar that is not free standing. + * + * @param g the graphics device. + * @param w the button width. + * @param h the button height. + */ + private void paintWestBorder(Graphics g, int w, int h) + { + Rectangle bounds = SwingUtilities.getLocalBounds(this); + if (isEnabled()) + { + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + g.drawLine(0, 0, bounds.width - 1, 0); + g.setColor(MetalLookAndFeel.getControlHighlight()); + g.drawLine(0, 1, bounds.width - 1, 1); + g.drawLine(0, 1, 0, bounds.height - 1); + } + else + { + g.setColor(MetalLookAndFeel.getControlDisabled()); + g.drawLine(0, 0, bounds.width - 1, 0); + } + } + + /** + * Returns the preferred size for the button, which varies depending on + * the direction of the button and whether or not it is free standing. + * + * @return The preferred size. + */ + public Dimension getPreferredSize() + { + int adj = 1; + if (!freeStanding) + adj = 2; + + if (direction == EAST) + return new Dimension(buttonWidth - adj, buttonWidth); + else if (direction == WEST) + return new Dimension(buttonWidth - 2, buttonWidth); + else if (direction == SOUTH) + return new Dimension(buttonWidth, buttonWidth - adj); + else // assume NORTH + return new Dimension(buttonWidth, buttonWidth - 2); + } + + /** + * Returns the minimum size for the button. + * + * @return The minimum size for the button. + */ + public Dimension getMinimumSize() + { + return getPreferredSize(); + } + + /** + * Returns the maximum size for the button. + * + * @return <code>Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)</code>. + */ + public Dimension getMaximumSize() + { + if (maximumSize == null) + maximumSize = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + return maximumSize; + } + +} diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java index 3e1198b398d..d5bf175f92d 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalScrollPaneUI.java @@ -39,19 +39,18 @@ exception statement from your version. */ package javax.swing.plaf.metal; import javax.swing.JComponent; +import javax.swing.JScrollPane; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicScrollPaneUI; +/** + * A UI delegate for the {@link JScrollPane} component. + */ public class MetalScrollPaneUI extends BasicScrollPaneUI { - - // FIXME: maybe replace by a Map of instances when this becomes stateful - /** The shared UI instance for JScrollPanes. */ - private static MetalScrollPaneUI instance = null; - /** - * Constructs a new instance of MetalScrollPaneUI. + * Constructs a new instance of <code>MetalScrollPaneUI</code>. */ public MetalScrollPaneUI() { @@ -59,16 +58,14 @@ public class MetalScrollPaneUI } /** - * Returns an instance of MetalScrollPaneUI. + * Returns a shared instance of <code>MetalScrollPaneUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalScrollPaneUI + * @return A shared instance of <code>MetalScrollPaneUI</code>. */ public static ComponentUI createUI(JComponent component) { - if (instance == null) - instance = new MetalScrollPaneUI(); - return instance; + return new MetalScrollPaneUI(); } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalSeparatorUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalSeparatorUI.java index 6e78ccb7071..1d48e9be2b0 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalSeparatorUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalSeparatorUI.java @@ -38,10 +38,20 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Rectangle; + import javax.swing.JComponent; +import javax.swing.JSeparator; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicSeparatorUI; +/** + * A UI delegate for the {@link JSeparator} component. + */ public class MetalSeparatorUI extends BasicSeparatorUI { @@ -51,7 +61,7 @@ public class MetalSeparatorUI private static MetalSeparatorUI instance = null; /** - * Constructs a new instance of MetalSeparatorUI. + * Constructs a new instance of <code>MetalSeparatorUI</code>. */ public MetalSeparatorUI() { @@ -59,11 +69,11 @@ public class MetalSeparatorUI } /** - * Returns an instance of MetalSeparatorUI. + * Returns a shared instance of <code>MetalSeparatorUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalSeparatorUI + * @return A shared instance of <code>MetalSeparatorUI</code>. */ public static ComponentUI createUI(JComponent component) { @@ -71,4 +81,51 @@ public class MetalSeparatorUI instance = new MetalSeparatorUI(); return instance; } + + /** + * The separator is made of two lines. The top line will be + * the Metal theme color separatorForeground (or left line if it's vertical). + * The bottom or right line will be the Metal theme color + * separatorBackground. + * The two lines will + * be centered inside the bounds box. If the separator is horizontal, + * then it will be vertically centered, or if it's vertical, it will + * be horizontally centered. + * + * @param g The Graphics object to paint with + * @param c The JComponent to paint. + */ + public void paint(Graphics g, JComponent c) + { + Rectangle r = new Rectangle(); + SwingUtilities.calculateInnerArea(c, r); + Color saved = g.getColor(); + Color c1 = UIManager.getColor("Separator.foreground"); + Color c2 = UIManager.getColor("Separator.background"); + JSeparator s; + if (c instanceof JSeparator) + s = (JSeparator) c; + else + return; + + if (s.getOrientation() == JSeparator.HORIZONTAL) + { + int midAB = r.height / 2; + g.setColor(c1); + g.drawLine(r.x, r.y + midAB - 1, r.x + r.width, r.y + midAB - 1); + + g.setColor(c2); + g.fillRect(r.x, r.y + midAB, r.x + r.width, r.y + midAB); + } + else + { + int midAD = r.height / 2 + r.y; + g.setColor(c1); + g.drawLine(r.x, r.y, r.x, r.y + r.height); + + g.setColor(c2); + g.fillRect(r.x + midAD, r.y + r.height, r.x + midAD, r.y + r.height); + } + g.setColor(saved); + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalSliderUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalSliderUI.java index 4b52c4b0041..08fb99d216c 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalSliderUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalSliderUI.java @@ -42,8 +42,8 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Rectangle; +import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.util.HashMap; import javax.swing.Icon; import javax.swing.JComponent; @@ -56,16 +56,59 @@ import javax.swing.plaf.basic.BasicSliderUI; /** * A UI delegate for the {@link JSlider} component. */ -public class MetalSliderUI - extends BasicSliderUI +public class MetalSliderUI extends BasicSliderUI { - // TODO: find a use for this + /** + * A property change handler that updates the rendered component in response + * to specific property change events. This custom handler is used to + * intercept the "JSlider.isFilled" property, which is only recognised by + * the {@link MetalLookAndFeel}. + */ + protected class MetalPropertyListener + extends BasicSliderUI.PropertyChangeHandler + { + /** + * Creates a new listener. + */ + protected MetalPropertyListener() + { + // Nothing to do here. + } + + /** + * Handles property change events. Events with the name "JSlider.isFilled" + * are handled here, and other events are passed to the superclass. + * + * @param e the property change event. + */ + public void propertyChange(PropertyChangeEvent e) + { + if (e.getPropertyName().equals(SLIDER_FILL)) + { + Boolean b = (Boolean) e.getNewValue(); + if (b == null) + filledSlider = false; + else + filledSlider = b.booleanValue(); + } + else + super.propertyChange(e); + } + } + + /** The thumb color (unused, because an icon is used to draw the thumb). */ protected static Color thumbColor; - // TODO: find a use for this + /** + * The highlight color used for drawing the track rect when the slider is + * enabled. + */ protected static Color highlightColor; - // TODO: find a use for this + /** + * The shadow color used for drawing the track rect when the slider is + * enabled. + */ protected static Color darkShadowColor; /** The track width. */ @@ -85,17 +128,14 @@ public class MetalSliderUI /** The gap between the track and the tick marks. */ protected final int TICK_BUFFER = 4; + /** A key to look up the filledSlider setting in the {@link UIManager}. */ + protected final String SLIDER_FILL = "JSlider.isFilled"; + /** * A flag that controls whether or not the track is filled up to the value * of the slider. */ protected boolean filledSlider; - - /** A key to look up the filledSlider setting in the {@link UIManager}. */ - protected final String SLIDER_FILL = "JSlider.isFilled"; - - /** The UI instances for MetalSliderUIs */ - private static HashMap instances; /** * Constructs a new instance. @@ -104,33 +144,27 @@ public class MetalSliderUI { super(null); filledSlider = UIManager.getBoolean(SLIDER_FILL); + darkShadowColor = MetalLookAndFeel.getControlDarkShadow(); + highlightColor = MetalLookAndFeel.getControlHighlight(); } /** - * Returns an instance of MetalSliderUI. + * Returns a new instance of <code>MetalSliderUI</code>. * - * @param component the component for which we return an UI instance + * @param component the component (ignored). * - * @return an instance of MetalSliderUI + * @return A new instance of <code>MetalSliderUI</code>. */ public static ComponentUI createUI(JComponent component) { - if (instances == null) - instances = new HashMap(); - - Object o = instances.get(component); - MetalSliderUI instance; - if (o == null) - { - instance = new MetalSliderUI(); - instances.put(component, instance); - } - else - instance = (MetalSliderUI) o; - - return instance; + return new MetalSliderUI(); } + /** + * Installs the default for this UI delegate in the supplied component. + * + * @param c the component. + */ public void installUI(JComponent c) { super.installUI(c); @@ -140,6 +174,18 @@ public class MetalSliderUI } /** + * Creates a property change listener for the slider. + * + * @param slider the slider. + * + * @return A new instance of {@link MetalPropertyListener}. + */ + protected PropertyChangeListener createPropertyChangeListener(JSlider slider) + { + return new MetalPropertyListener(); + } + + /** * Paints the thumb icon for the slider. * * @param g the graphics device. @@ -153,46 +199,79 @@ public class MetalSliderUI } /** - * Creates a property change listener for the slider. - * - * @param slider the slider. - */ - protected PropertyChangeListener createPropertyChangeListener(JSlider slider) - { - // TODO: try to figure out why it might be necessary to override this - // method as is done in Sun's implementation - return super.createPropertyChangeListener(slider); - } - - /** * Paints the track along which the thumb control moves. * * @param g the graphics device. */ public void paintTrack(Graphics g) { + Color shadowColor = MetalLookAndFeel.getControlShadow(); if (slider.getOrientation() == JSlider.HORIZONTAL) - { - if (filledSlider) { - // TODO: fill the track + int trackX = trackRect.x; + int trackY = trackRect.y + (trackRect.height - getTrackWidth()) / 2; + int trackW = trackRect.width - 1; + int trackH = getTrackWidth(); + + // draw border + if (slider.isEnabled()) + BasicGraphicsUtils.drawEtchedRect(g, trackX, trackY, trackW, trackH, + darkShadowColor, shadowColor, darkShadowColor, highlightColor); + else + { + g.setColor(MetalLookAndFeel.getControlShadow()); + g.drawRect(trackX, trackY, trackW - 2, trackH - 2); + } + + // fill track (if required) + if (filledSlider) + { + int xPos = xPositionForValue(slider.getValue()); + int x = (slider.getInverted() ? xPos : trackRect.x); + int w = (slider.getInverted() ? trackX + trackW - xPos + : xPos - trackRect.x); + g.setColor(MetalLookAndFeel.getControlShadow()); + g.fillRect(x + 1, trackY + 1, w - 3, getTrackWidth() - 3); + if (slider.isEnabled()) + { + g.setColor(MetalLookAndFeel.getControl()); + g.drawLine(x + 1, trackY + 1, x + w - 3, trackY + 1); + g.drawLine(x + 1, trackY + 1, x + 1, + trackY + getTrackWidth() - 3); + } + } } - BasicGraphicsUtils.drawEtchedRect(g, trackRect.x, trackRect.y - + (trackRect.height - getTrackWidth()) / 2, trackRect.width - 1, - getTrackWidth(), Color.darkGray, Color.gray, Color.darkGray, - Color.white); - } else - { - if (filledSlider) { - // TODO: fill the track + int trackX = trackRect.x + (trackRect.width - getTrackWidth()) / 2; + int trackY = trackRect.y; + int trackW = getTrackWidth(); + int trackH = trackRect.height - 1; + if (slider.isEnabled()) + BasicGraphicsUtils.drawEtchedRect(g, trackX, trackY, trackW, trackH, + darkShadowColor, shadowColor, darkShadowColor, highlightColor); + else + { + g.setColor(MetalLookAndFeel.getControlShadow()); + g.drawRect(trackX, trackY, trackW - 2, trackH - 2); + } + + if (filledSlider) + { + int yPos = yPositionForValue(slider.getValue()); + int y = (slider.getInverted() ? trackY : yPos); + int h = (slider.getInverted() ? yPos - trackY + : trackY + trackH - yPos); + g.setColor(MetalLookAndFeel.getControlShadow()); + g.fillRect(trackX + 1, y + 1, getTrackWidth() - 3, h - 3); + if (slider.isEnabled()) + { + g.setColor(MetalLookAndFeel.getControl()); + g.drawLine(trackX + 1, y + 1, trackX + trackW - 3, y + 1); + g.drawLine(trackX + 1, y + 1, trackX + 1, y + h - 3); + } + } } - BasicGraphicsUtils.drawEtchedRect(g, trackRect.x + (trackRect.width - - getTrackWidth()) / 2, trackRect.y, getTrackWidth(), - trackRect.height - 1, Color.darkGray, Color.gray, Color.darkGray, - Color.white); - } } /** @@ -262,12 +341,13 @@ public class MetalSliderUI */ protected int getThumbOverhang() { - // TODO: figure out what this is used for + // FIXME: for what might this method be used? return 0; } protected void scrollDueToClickInTrack(int dir) { + // FIXME: for what might this method be overridden? super.scrollDueToClickInTrack(dir); } @@ -283,8 +363,10 @@ public class MetalSliderUI { // Note the incoming 'g' has a translation in place to get us to the // start of the tick rect already... - // TODO: get color from UIManager... - g.setColor(new Color(153, 153, 204)); + if (slider.isEnabled()) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getControlDisabled()); g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength / 2); } @@ -300,8 +382,10 @@ public class MetalSliderUI { // Note the incoming 'g' has a translation in place to get us to the // start of the tick rect already... - // TODO: get color from UIManager... - g.setColor(new Color(153, 153, 204)); + if (slider.isEnabled()) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getControlDisabled()); g.drawLine(x, TICK_BUFFER, x, TICK_BUFFER + tickLength); } @@ -317,8 +401,10 @@ public class MetalSliderUI { // Note the incoming 'g' has a translation in place to get us to the // start of the tick rect already... - // TODO: get color from UIManager... - g.setColor(new Color(153, 153, 204)); + if (slider.isEnabled()) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getControlDisabled()); g.drawLine(TICK_BUFFER - 1, y, TICK_BUFFER - 1 + tickLength / 2, y); } @@ -334,8 +420,10 @@ public class MetalSliderUI { // Note the incoming 'g' has a translation in place to get us to the // start of the tick rect already... - // TODO: get color from UIManager... - g.setColor(new Color(153, 153, 204)); + if (slider.isEnabled()) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getControlDisabled()); g.drawLine(TICK_BUFFER - 1, y, TICK_BUFFER - 1 + tickLength, y); } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java index 60e9c055952..016e09557d6 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java @@ -78,7 +78,7 @@ class MetalSplitPaneDivider extends BasicSplitPaneDivider { //super.paint(g); Dimension s = getSize(); - MetalUtils.fillMetalPattern(g, 2, 2, s.width - 4, s.height - 4, + MetalUtils.fillMetalPattern(splitPane, g, 2, 2, s.width - 4, s.height - 4, light, dark); } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneUI.java index b7ea8984b43..b39fb23366e 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneUI.java @@ -39,24 +39,22 @@ exception statement from your version. */ package javax.swing.plaf.metal; import java.awt.Color; -import java.util.HashMap; import javax.swing.JComponent; +import javax.swing.JSplitPane; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.basic.BasicSplitPaneUI; import javax.swing.plaf.basic.BasicSplitPaneDivider; +import javax.swing.plaf.basic.BasicSplitPaneUI; -public class MetalSplitPaneUI - extends BasicSplitPaneUI +/** + * A UI delegate for the {@link JSplitPane} component. + */ +public class MetalSplitPaneUI extends BasicSplitPaneUI { - - /** The UI instances for MetalSplitPaneUIs */ - private static HashMap instances; - /** - * Constructs a new instance of MetalSplitPaneUI. + * Constructs a new instance of <code>MetalSplitPaneUI</code>. */ public MetalSplitPaneUI() { @@ -64,28 +62,15 @@ public class MetalSplitPaneUI } /** - * Returns an instance of MetalSplitPaneUI. + * Returns a new instance of <code>MetalSplitPaneUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalSplitPaneUI + * @return A new instance of <code>MetalSplitPaneUI</code>. */ public static ComponentUI createUI(JComponent component) { - if (instances == null) - instances = new HashMap(); - - Object o = instances.get(component); - MetalSplitPaneUI instance; - if (o == null) - { - instance = new MetalSplitPaneUI(); - instances.put(component, instance); - } - else - instance = (MetalSplitPaneUI) o; - - return instance; + return new MetalSplitPaneUI(); } /** diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java index 1b5fe144f6c..b1e02c7a6b0 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java @@ -40,7 +40,6 @@ package javax.swing.plaf.metal; import java.awt.Graphics; import java.awt.LayoutManager; -import java.util.HashMap; import javax.swing.JComponent; import javax.swing.JTabbedPane; @@ -48,11 +47,9 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicTabbedPaneUI; /** - * A UI delegate used for the {@link JTabbedPane} component in the - * {@link MetalLookAndFeel}. + * A UI delegate for the {@link JTabbedPane} component. */ -public class MetalTabbedPaneUI - extends BasicTabbedPaneUI +public class MetalTabbedPaneUI extends BasicTabbedPaneUI { /** @@ -65,13 +62,14 @@ public class MetalTabbedPaneUI * public for compatibility. */ public class TabbedPaneLayout - extends BasicTabbedPaneUI.TabbedPaneLayout + extends BasicTabbedPaneUI.TabbedPaneLayout { /** * Creates a new instance of the layout manager. */ public TabbedPaneLayout() { + // Nothing to do here. } /** @@ -102,9 +100,6 @@ public class MetalTabbedPaneUI } } - /** The shared UI instance for JTabbedPanes. */ - private static HashMap instances = null; - /** * Constructs a new instance of MetalTabbedPaneUI. */ @@ -122,20 +117,7 @@ public class MetalTabbedPaneUI */ public static ComponentUI createUI(JComponent component) { - if (instances == null) - instances = new HashMap(); - - Object o = instances.get(component); - MetalTabbedPaneUI instance; - if (o == null) - { - instance = new MetalTabbedPaneUI(); - instances.put(component, instance); - } - else - instance = (MetalTabbedPaneUI) o; - - return instance; + return new MetalTabbedPaneUI(); } /** @@ -145,7 +127,7 @@ public class MetalTabbedPaneUI */ protected LayoutManager createLayoutManager() { - return new TabbedPaneLayout(); + return super.createLayoutManager(); } /** diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java index d6e50e12239..6984daeccbe 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalTextFieldUI.java @@ -38,19 +38,16 @@ exception statement from your version. */ package javax.swing.plaf.metal; -import java.util.HashMap; - import javax.swing.JComponent; +import javax.swing.JTextField; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicTextFieldUI; -public class MetalTextFieldUI - extends BasicTextFieldUI +/** + * A UI delegate for the {@link JTextField} component. + */ +public class MetalTextFieldUI extends BasicTextFieldUI { - - /** The UI instances for MetalTextFieldUIs */ - private static HashMap instances = null; - /** * Constructs a new instance of MetalTextFieldUI. */ @@ -60,27 +57,14 @@ public class MetalTextFieldUI } /** - * Returns an instance of MetalTextFieldUI. + * Returns a new instance of <code>MetalTextFieldUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalTextFieldUI + * @return A new instance of <code>MetalTextFieldUI</code>. */ public static ComponentUI createUI(JComponent component) { - if (instances == null) - instances = new HashMap(); - - Object o = instances.get(component); - MetalTextFieldUI instance; - if (o == null) - { - instance = new MetalTextFieldUI(); - instances.put(component, instance); - } - else - instance = (MetalTextFieldUI) o; - - return instance; + return new MetalTextFieldUI(); } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalToggleButtonUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalToggleButtonUI.java index be6d0c39ec8..46a19bdbe9e 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalToggleButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalToggleButtonUI.java @@ -39,13 +39,24 @@ exception statement from your version. */ package javax.swing.plaf.metal; import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Rectangle; +import javax.swing.AbstractButton; import javax.swing.JComponent; +import javax.swing.JToggleButton; +import javax.swing.SwingUtilities; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicButtonUI; import javax.swing.plaf.basic.BasicToggleButtonUI; +/** + * A UI delegate for the {@link JToggleButton} component. + */ public class MetalToggleButtonUI extends BasicToggleButtonUI { @@ -59,21 +70,26 @@ public class MetalToggleButtonUI /** The color for disabled button labels. */ protected Color disabledTextColor; - /** The shared UI instance for MetalToggleButtonUIs */ - private static MetalToggleButtonUI instance = null; + /** + * Returns a new instance of <code>MetalToggleButtonUI</code>. + * + * @param component the component for which we return an UI instance + * + * @return A new instance of <code>MetalToggleButtonUI</code>. + */ + public static ComponentUI createUI(JComponent component) + { + return new MetalToggleButtonUI(); + } /** - * Constructs a new instance of MetalToggleButtonUI. + * Constructs a new instance of <code>MetalToggleButtonUI</code>. */ public MetalToggleButtonUI() { super(); - focusColor = getFocusColor(); - selectColor = getSelectColor(); - disabledTextColor = getDisabledTextColor(); } - /** * Returns the color for the focus border. * @@ -81,8 +97,7 @@ public class MetalToggleButtonUI */ protected Color getFocusColor() { - UIDefaults def = UIManager.getLookAndFeelDefaults(); - return def.getColor(getPropertyPrefix() + ".focus"); + return focusColor; } /** @@ -92,32 +107,98 @@ public class MetalToggleButtonUI */ protected Color getSelectColor() { - UIDefaults def = UIManager.getLookAndFeelDefaults(); - return def.getColor(getPropertyPrefix() + ".select"); + return selectColor; } /** - * Returns the color for the text label of disabled buttons. + * Returns the color for the text label of disabled buttons. The value + * is initialised in the {@link #installDefaults(AbstractButton)} method + * by reading the <code>ToggleButton.disabledText</code> item from the UI + * defaults. * - * @return the color for the text label of disabled buttons + * @return The color for the text label of disabled buttons. */ protected Color getDisabledTextColor() { - UIDefaults def = UIManager.getLookAndFeelDefaults(); - return def.getColor(getPropertyPrefix() + ".disabledText"); + return disabledTextColor; } /** - * Returns an instance of MetalToggleButtonUI. - * - * @param component the component for which we return an UI instance - * - * @return an instance of MetalToggleButtonUI + * Updates the button with the defaults for this look and feel. + * + * @param b the button. */ - public static ComponentUI createUI(JComponent component) + public void installDefaults(AbstractButton b) + { + super.installDefaults(b); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + focusColor = defaults.getColor(getPropertyPrefix() + "focus"); + selectColor = defaults.getColor(getPropertyPrefix() + "select"); + disabledTextColor = defaults.getColor(getPropertyPrefix() + "disabledText"); + } + + /** + * Paints the button background when it is pressed/selected. + * + * @param g the graphics device. + * @param b the button. + */ + protected void paintButtonPressed(Graphics g, AbstractButton b) + { + if (b.isContentAreaFilled() && b.isOpaque()) + { + Color saved = g.getColor(); + Rectangle bounds = SwingUtilities.getLocalBounds(b); + g.setColor(selectColor); + g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); + g.setColor(saved); + } + } + + /** + * Paints the text for the button. + * + * @param g the graphics device. + * @param c the component. + * @param textRect the bounds for the text. + * @param text the text. + * + * @deprecated 1.4 Use {@link BasicButtonUI#paintText(java.awt.Graphics, + * javax.swing.AbstractButton, java.awt.Rectangle, java.lang.String)}. + */ + protected void paintText(Graphics g, JComponent c, Rectangle textRect, + String text) + { + Font savedFont = g.getFont(); + Color savedColor = g.getColor(); + g.setFont(c.getFont()); + if (c.isEnabled()) + g.setColor(c.getForeground()); + else + g.setColor(disabledTextColor); + FontMetrics fm = g.getFontMetrics(c.getFont()); + int ascent = fm.getAscent(); + g.drawString(text, textRect.x, textRect.y + ascent); + g.setFont(savedFont); + g.setColor(savedColor); + } + + /** + * Draws the focus highlight around the text and icon. + * + * @param g the graphics device. + * @param b the button. + */ + protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect, + Rectangle textRect, Rectangle iconRect) { - if (instance == null) - instance = new MetalToggleButtonUI(); - return instance; + if (!b.hasFocus()) + return; + Color saved = g.getColor(); + g.setColor(focusColor); + Rectangle fr = iconRect.union(textRect); + g.drawRect(fr.x - 1, fr.y - 1, fr.width + 1, fr.height + 1); + g.setColor(saved); } + } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java index 39af0011ae6..c5ca91399ab 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalToolBarUI.java @@ -38,20 +38,73 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.event.ContainerListener; +import java.beans.PropertyChangeListener; + import javax.swing.JComponent; +import javax.swing.JToolBar; +import javax.swing.border.Border; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicToolBarUI; -public class MetalToolBarUI - extends BasicToolBarUI +/** + * A UI delegate for the {@link JToolBar} component. + */ +public class MetalToolBarUI extends BasicToolBarUI { + + /** + * A listener (no longer used) that responds when components are added to or + * removed from the {@link JToolBar}. The required behaviour is now + * handled in the super class. + * + * @see MetalToolBarUI#createContainerListener() + */ + protected class MetalContainerListener + extends BasicToolBarUI.ToolBarContListener + { + /** + * Creates a new instance. + */ + protected MetalContainerListener() + { + // Nothing to do here. + } + } - // FIXME: maybe replace by a Map of instances when this becomes stateful - /** The shared UI instance for MetalToolBarUIs */ - private static MetalToolBarUI instance = null; + /** + * A listener (no longer used) that responds to property change events in a + * {@link JToolBar} component. The required behaviour is now handled in the + * super class. + * + * @see MetalToolBarUI#createRolloverListener() + */ + protected class MetalRolloverListener + extends BasicToolBarUI.PropertyListener + { + /** + * Creates a new instance. + */ + protected MetalRolloverListener() + { + // Nothing to do here. + } + } + + /** + * The container listener (an implementation specific field, according to the + * spec, and not used in GNU Classpath). + */ + protected ContainerListener contListener; + + /** + * The rollover listener (an implementation specific field, according to the + * spec, and not used in GNU Classpath). + */ + protected PropertyChangeListener rolloverListener; /** - * Constructs a new instance of MetalToolBarUI. + * Creates a new instance of this UI delegate. */ public MetalToolBarUI() { @@ -59,16 +112,51 @@ public class MetalToolBarUI } /** - * Returns an instance of MetalToolBarUI. + * Returns a new instance of <code>MetalToolBarUI</code>. * - * @param component the component for which we return an UI instance + * @param component the component for which we return an UI instance * - * @return an instance of MetalToolBarUI + * @return A new instance of <code>MetalToolBarUI</code>. */ public static ComponentUI createUI(JComponent component) { - if (instance == null) - instance = new MetalToolBarUI(); - return instance; + return new MetalToolBarUI(); + } + + /** + * Returns <code>null</code> as permitted by recent versions of the API + * specification. Originally it seems this method returned a new instance of + * {@link MetalRolloverListener}, but this is now redundant. + * + * @return <code>null</code>. + */ + protected PropertyChangeListener createRolloverListener() + { + return null; + } + + /** + * Returns <code>null</code> as permitted by recent versions of the API + * specification. Originally it seems this method returned a new instance of + * {@link MetalContainerListener}, but this is now redundant. + * + * @return <code>null</code>. + */ + protected ContainerListener createContainerListener() + { + return null; + } + + /** + * Returns a border with no rollover effect for buttons in the tool bar. + * + * @return A border. + * + * @see MetalBorders#getToolbarButtonBorder() + */ + protected Border createNonRolloverBorder() + { + return MetalBorders.getToolbarButtonBorder(); } + } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalToolTipUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalToolTipUI.java index c88b6534ab7..5085d170ac2 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalToolTipUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalToolTipUI.java @@ -38,32 +38,92 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractButton; import javax.swing.JComponent; +import javax.swing.JMenuItem; +import javax.swing.JToolTip; +import javax.swing.KeyStroke; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.border.Border; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicToolTipUI; +/** + * A UI delegate for the {@link JToolTip} component. + */ public class MetalToolTipUI extends BasicToolTipUI { + /** + * The amount of space between the tool tip text and the accelerator + * description (if visible). + */ + public static final int padSpaceBetweenStrings = 12; - // FIXME: maybe replace by a Map of instances when this becomes stateful - /** The shared UI instance for MetalToolTipUIs */ + /** The shared UI instance. */ private static MetalToolTipUI instance = null; - + + /** A flag controlling the visibility of the accelerator (if there is one). */ + private boolean isAcceleratorHidden; + + /** A string representing the accelerator key for the component. */ + private String acceleratorString; + + /** + * The delimiter for the accelerator string. + */ + private String acceleratorDelimiter; + + /** The font for the accelerator string. */ + private Font acceleratorFont; + + /** The color for the accelerator string. */ + private Color acceleratorForeground; + + /** The active border. */ + private Border activeBorder; + + /** The inactive border. */ + private Border inactiveBorder; + /** - * Constructs a new instance of MetalToolTipUI. + * Constructs a new instance of <code>MetalToolTipUI</code>. */ public MetalToolTipUI() { super(); + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + activeBorder = defaults.getBorder("ToolTip.border"); + inactiveBorder = defaults.getBorder("ToolTip.borderInactive"); + isAcceleratorHidden = defaults.getBoolean("ToolTip.hideAccelerator"); + acceleratorFont = defaults.getFont("MenuItem.acceleratorFont"); + acceleratorForeground = defaults.getColor("MenuItem.acceleratorForeground"); + acceleratorDelimiter = defaults.getString("MenuItem.acceleratorDelimiter"); } /** - * Returns an instance of MetalToolTipUI. + * Returns a shared instance of the <code>MetalToolTipUI</code> class. + * Although this UI delegate does maintain state information, there is never + * more than one tool tip visible, so it is OK to use a shared instance. * - * @param component the component for which we return an UI instance + * @param component the component (a {@link JToolTip}). * - * @return an instance of MetalToolTipUI + * @return A shared instance of the <code>MetalToolTipUI</code> class. */ public static ComponentUI createUI(JComponent component) { @@ -71,4 +131,202 @@ public class MetalToolTipUI instance = new MetalToolTipUI(); return instance; } + + /** + * Returns a string representing the accelerator key (if there is one) for + * the component that the tool tip belongs to. + * + * @return A string representing the accelerator key. + */ + public String getAcceleratorString() + { + return acceleratorString; + } + + /** + * Installs the UI for the specified component (a {@link JToolTip}). + * + * @param c the {@link JToolTip} component. + */ + public void installUI(JComponent c) + { + super.installUI(c); + Border existingBorder = c.getBorder(); + if (existingBorder == null || existingBorder instanceof UIResource) + { + if (c.isEnabled()) + c.setBorder(activeBorder); + else + c.setBorder(inactiveBorder); + } + } + + /** + * Clears the defaults set in {@link #installUI(JComponent)}. + * + * @param c the component. + */ + public void uninstallUI(JComponent c) + { + super.uninstallUI(c); + if (c.getBorder() instanceof UIResource) + c.setBorder(null); + } + + /** + * Returns <code>true</code> if the accelerator string is hidden, and + * <code>false</code> otherwise. This setting is controlled by the + * <code>ToolTip.hideAccelerator</code> entry in the UI defaults table. + * + * @return A boolean. + */ + protected boolean isAcceleratorHidden() + { + return isAcceleratorHidden; + } + + /** + * Returns the preferred size for the {@link JToolTip} component. + * + * @param c the component (a {@link JToolTip}). + * + * @return The preferred size. + */ + public Dimension getPreferredSize(JComponent c) + { + if (isAcceleratorHidden()) + return super.getPreferredSize(c); + else + { + Insets insets = c.getInsets(); + JToolTip tt = (JToolTip) c; + String tipText = tt.getTipText(); + if (tipText != null) + { + FontMetrics fm = c.getFontMetrics(c.getFont()); + int prefH = fm.getHeight() + insets.top + insets.bottom; + int prefW = fm.stringWidth(tipText) + insets.left + insets.right; + + // this seems to be the first opportunity we have to get the + // accelerator string from the component (if it has one) + acceleratorString = fetchAcceleratorString(c); + if (acceleratorString != null) + { + prefW += padSpaceBetweenStrings; + fm = c.getFontMetrics(acceleratorFont); + prefW += fm.stringWidth(acceleratorString); + } + return new Dimension(prefW, prefH); + } + else return new Dimension(0, 0); + } + } + + /** + * Paints the tool tip. + * + * @param g the graphics context. + * @param c the {@link JToolTip} component. + */ + public void paint(Graphics g, JComponent c) + { + JToolTip tip = (JToolTip) c; + + String text = tip.getTipText(); + Toolkit t = tip.getToolkit(); + if (text == null) + return; + + Rectangle vr = new Rectangle(); + vr = SwingUtilities.calculateInnerArea(tip, vr); + Rectangle ir = new Rectangle(); + Rectangle tr = new Rectangle(); + FontMetrics fm = t.getFontMetrics(tip.getFont()); + int ascent = fm.getAscent(); + SwingUtilities.layoutCompoundLabel(tip, fm, text, null, + SwingConstants.CENTER, SwingConstants.LEFT, + SwingConstants.CENTER, SwingConstants.CENTER, vr, ir, tr, 0); + Color saved = g.getColor(); + g.setColor(Color.BLACK); + + g.drawString(text, vr.x, vr.y + ascent); + + // paint accelerator + if (acceleratorString != null) + { + g.setFont(acceleratorFont); + g.setColor(acceleratorForeground); + fm = t.getFontMetrics(acceleratorFont); + int width = fm.stringWidth(acceleratorString); + g.drawString(acceleratorString, vr.x + vr.width - width - padSpaceBetweenStrings/2, + vr.y + vr.height - fm.getDescent()); + } + + g.setColor(saved); + } + + /** + * Returns a string representing the accelerator for the component, or + * <code>null</code> if the component has no accelerator. + * + * @param c the component. + * + * @return A string representing the accelerator (possibly + * <code>null</code>). + */ + private String fetchAcceleratorString(JComponent c) + { + String result = null; + if (c instanceof JToolTip) + { + JToolTip toolTip = (JToolTip) c; + JComponent component = toolTip.getComponent(); + KeyStroke ks = null; + int mne = 0; + if (component instanceof JMenuItem) + { + JMenuItem item = (JMenuItem) component; + ks = item.getAccelerator(); + if (ks == null) + mne = item.getMnemonic(); + } + else if (component instanceof AbstractButton) + { + AbstractButton button = (AbstractButton) component; + mne = button.getMnemonic(); + } + if (mne > 0) + ks = KeyStroke.getKeyStroke(Character.toUpperCase((char) mne), + InputEvent.ALT_MASK, false); + if (ks != null) + result = acceleratorToString(ks); + } + return result; + } + + /** + * Returns a string representing an accelerator. + * + * @param accelerator the accelerator (<code>null</code> not permitted). + * + * @return A string representing an accelerator. + */ + private String acceleratorToString(KeyStroke accelerator) + { + // convert keystroke into string format + String modifiersText = ""; + int modifiers = accelerator.getModifiers(); + char keyChar = accelerator.getKeyChar(); + int keyCode = accelerator.getKeyCode(); + + if (modifiers != 0) + modifiersText = KeyEvent.getKeyModifiersText(modifiers) + + acceleratorDelimiter; + + if (keyCode == KeyEvent.VK_UNDEFINED) + return modifiersText + keyChar; + else + return modifiersText + KeyEvent.getKeyText(keyCode); + } + } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalTreeUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalTreeUI.java index 8d16f7463fe..0ffa0d17470 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalTreeUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalTreeUI.java @@ -38,21 +38,50 @@ exception statement from your version. */ package javax.swing.plaf.metal; -import java.util.HashMap; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.event.ComponentListener; +import java.awt.event.FocusListener; +import java.awt.event.KeyListener; +import java.awt.event.MouseListener; +import java.beans.PropertyChangeListener; +import java.util.Hashtable; import javax.swing.JComponent; +import javax.swing.JTree; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.tree.TreeCellEditor; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; +import javax.swing.event.CellEditorListener; +import javax.swing.event.TreeExpansionListener; +import javax.swing.event.TreeModelListener; +import javax.swing.event.TreeSelectionListener; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicTreeUI; -public class MetalTreeUI - extends BasicTreeUI +/** + * A UI delegate for the {@link JTree} component. + */ +public class MetalTreeUI extends BasicTreeUI { - /** The UI instances for MetalTreeUIs */ - private static HashMap instances = null; - + /** Listeners */ + private PropertyChangeListener propertyChangeListener; + private FocusListener focusListener; + private TreeSelectionListener treeSelectionListener; + private MouseListener mouseListener; + private KeyListener keyListener; + private PropertyChangeListener selectionModelPropertyChangeListener; + private ComponentListener componentListener; + private CellEditorListener cellEditorListener; + private TreeExpansionListener treeExpansionListener; + private TreeModelListener treeModelListener; + /** - * Constructs a new instance of MetalTreeUI. + * Constructs a new instance of <code>MetalTreeUI</code>. */ public MetalTreeUI() { @@ -60,27 +89,238 @@ public class MetalTreeUI } /** - * Returns an instance of MetalTreeUI. + * Returns a new instance of <code>MetalTreeUI</code>. * * @param component the component for which we return an UI instance * - * @return an instance of MetalTreeUI + * @return A new instance of <code>MetalTreeUI</code>. */ public static ComponentUI createUI(JComponent component) { - if (instances == null) - instances = new HashMap(); + return new MetalTreeUI(); + } + + /** + * The horizontal element of legs between nodes starts at the right of the + * left-hand side of the child node by default. This method makes the + * leg end before that. + */ + protected int getHorizontalLegBuffer() + { + return super.getHorizontalLegBuffer(); + } + + /** + * Configures the specified component appropriate for the look and feel. + * This method is invoked when the ComponentUI instance is being installed + * as the UI delegate on the specified component. This method should completely + * configure the component for the look and feel, including the following: + * 1. Install any default property values for color, fonts, borders, icons, + * opacity, etc. on the component. Whenever possible, property values + * initialized by the client program should not be overridden. + * 2. Install a LayoutManager on the component if necessary. + * 3. Create/add any required sub-components to the component. + * 4. Create/install event listeners on the component. + * 5. Create/install a PropertyChangeListener on the component in order + * to detect and respond to component property changes appropriately. + * 6. Install keyboard UI (mnemonics, traversal, etc.) on the component. + * 7. Initialize any appropriate instance data. + */ + public void installUI(JComponent c) + { + tree = (JTree) c; + configureLayoutCache(); + + UIDefaults defaults = UIManager.getLookAndFeelDefaults(); + tree.setFont(defaults.getFont("Tree.font")); + tree.setForeground(defaults.getColor("Tree.foreground")); + tree.setBackground(defaults.getColor("Tree.background")); + tree.setOpaque(true); + tree.setScrollsOnExpand(defaults.getBoolean("Tree.scrollsOnExpand")); + rightChildIndent = defaults.getInt("Tree.rightChildIndent"); + leftChildIndent = defaults.getInt("Tree.leftChildIndent"); + setRowHeight(defaults.getInt("Tree.rowHeight")); + tree.setRowHeight(defaults.getInt("Tree.rowHeight")); + tree.requestFocusInWindow(false); + + setExpandedIcon(defaults.getIcon("Tree.expandedIcon")); + setCollapsedIcon(defaults.getIcon("Tree.collapsedIcon")); + + currentCellRenderer = createDefaultCellRenderer(); + rendererPane = createCellRendererPane(); + createdRenderer = true; + setCellEditor(createDefaultCellEditor()); + createdCellEditor = true; + TreeModel mod = tree.getModel(); + setModel(mod); + + treeSelectionModel = tree.getSelectionModel(); + drawingCache = new Hashtable(); + nodeDimensions = createNodeDimensions(); + + propertyChangeListener = createPropertyChangeListener(); + focusListener = createFocusListener(); + treeSelectionListener = createTreeSelectionListener(); + mouseListener = createMouseListener(); + keyListener = createKeyListener(); + selectionModelPropertyChangeListener = createSelectionModelPropertyChangeListener(); + componentListener = createComponentListener(); + cellEditorListener = createCellEditorListener(); + treeExpansionListener = createTreeExpansionListener(); + treeModelListener = createTreeModelListener(); - Object o = instances.get(component); - MetalTreeUI instance; - if (o == null) + editingRow = -1; + lastSelectedRow = -1; + + installKeyboardActions(); + + tree.addPropertyChangeListener(propertyChangeListener); + tree.addFocusListener(focusListener); + tree.addTreeSelectionListener(treeSelectionListener); + tree.addMouseListener(mouseListener); + tree.addKeyListener(keyListener); + tree.addPropertyChangeListener(selectionModelPropertyChangeListener); + tree.addComponentListener(componentListener); + tree.addTreeExpansionListener(treeExpansionListener); + if (treeModel != null) + treeModel.addTreeModelListener(treeModelListener); + + if (mod != null) { - instance = new MetalTreeUI(); - instances.put(component, instance); + TreePath path = new TreePath(mod.getRoot()); + if (!tree.isExpanded(path)) + toggleExpandState(path); } - else - instance = (MetalTreeUI) o; + + completeUIInstall(); + } + + /** + * Reverses configuration which was done on the specified component during + * installUI. This method is invoked when this UIComponent instance is being + * removed as the UI delegate for the specified component. This method should + * undo the configuration performed in installUI, being careful to leave the + * JComponent instance in a clean state (no extraneous listeners, + * look-and-feel-specific property objects, etc.). This should include + * the following: + * 1. Remove any UI-set borders from the component. + * 2. Remove any UI-set layout managers on the component. + * 3. Remove any UI-added sub-components from the component. + * 4. Remove any UI-added event/property listeners from the component. + * 5. Remove any UI-installed keyboard UI from the component. + * 6. Nullify any allocated instance data objects to allow for GC. + */ + public void uninstallUI(JComponent c) + { + tree.setFont(null); + tree.setForeground(null); + tree.setBackground(null); + + uninstallKeyboardActions(); + + tree.removePropertyChangeListener(propertyChangeListener); + tree.removeFocusListener(focusListener); + tree.removeTreeSelectionListener(treeSelectionListener); + tree.removeMouseListener(mouseListener); + tree.removeKeyListener(keyListener); + tree.removePropertyChangeListener(selectionModelPropertyChangeListener); + tree.removeComponentListener(componentListener); + tree.removeTreeExpansionListener(treeExpansionListener); + + TreeCellEditor tce = tree.getCellEditor(); + if (tce != null) + tce.removeCellEditorListener(cellEditorListener); + TreeModel tm = tree.getModel(); + if (tm != null) + tm.removeTreeModelListener(treeModelListener); + + tree = null; + uninstallComponents(); + completeUIUninstall(); + } + + /** + * This function converts between the string passed into the client + * property and the internal representation (currently an int). + * + * @param lineStyleFlag - String representation + */ + protected void decodeLineStyle(Object lineStyleFlag) + { + // FIXME: not implemented + } - return instance; + /** + * Checks if the location is in expand control. + * + * @param row - current row + * @param rowLevel - current level + * @param mouseX - current x location of the mouse click + * @param mouseY - current y location of the mouse click + */ + protected boolean isLocationInExpandControl(int row, int rowLevel, + int mouseX, int mouseY) + { + return super.isLocationInExpandControl(tree.getPathForRow(row), + mouseX, mouseY); + } + + /** + * Paints the specified component appropriate for the look and feel. + * This method is invoked from the ComponentUI.update method when the + * specified component is being painted. Subclasses should override this + * method and use the specified Graphics object to render the content of + * the component. + * + * @param g - the current graphics configuration. + * @param c - the current component to draw + */ + public void paint(Graphics g, JComponent c) + { + // Calls BasicTreeUI's paint since it takes care of painting all + // types of icons. + super.paint(g, c); + } + + /** + * Paints the horizontal separators. + * + * @param g - the current graphics configuration. + * @param c - the current component to draw + */ + protected void paintHorizontalSeparators(Graphics g, JComponent c) + { + // FIXME: not implemented + } + + + /** + * Paints the vertical part of the leg. The receiver should NOT modify + * clipBounds, insets. + * + * @param g - the current graphics configuration. + * @param clipBounds - + * @param insets - + * @param path - the current path + */ + protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, + Insets insets, TreePath path) + { + super.paintVerticalPartOfLeg(g, clipBounds, insets, path); + } + + /** + * Paints the horizontal part of the leg. The receiver should NOT \ + * modify clipBounds, or insets. + * NOTE: parentRow can be -1 if the root is not visible. + */ + protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds, + Insets insets, Rectangle bounds, + TreePath path, int row, + boolean isExpanded, boolean hasBeenExpanded, + boolean isLeaf) + { + super.paintHorizontalPartOfLeg(g, clipBounds, insets, bounds, path, row, + isExpanded, hasBeenExpanded, isLeaf); } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java b/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java index a342ee02bd3..c0b4e657676 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java @@ -1,4 +1,4 @@ -/* Metaltils.java +/* MetalUtils.java Copyright (C) 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,7 +38,12 @@ exception statement from your version. */ package javax.swing.plaf.metal; import java.awt.Color; +import java.awt.Component; import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.TexturePaint; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; /** * Some utility and helper methods for the Metal Look & Feel. @@ -49,6 +54,21 @@ class MetalUtils { /** + * The typical metal pattern for use with Graphics2D. + */ + static BufferedImage pattern2D; + + /** + * The light color to draw the pattern. + */ + static Color lightColor; + + /** + * The dark color to draw to draw the pattern. + */ + static Color darkColor; + + /** * Fills a rectangle with the typical Metal pattern. * * @param g the <code>Graphics</code> context to use @@ -57,31 +77,78 @@ class MetalUtils * @param y the Y coordinate of the upper left corner of the rectangle to * fill * @param w the width of the rectangle to fill - * @param w the height of the rectangle to fill + * @param h the height of the rectangle to fill * @param light the light color to use * @param dark the dark color to use */ - static void fillMetalPattern(Graphics g, int x, int y, int w, int h, + static void fillMetalPattern(Component c, Graphics g, int x, int y, int w, int h, Color light, Color dark) { - int xOff = 0; - for (int mY = y; mY < (y + h); mY++) + if (g instanceof Graphics2D) + fillMetalPattern2D((Graphics2D) g, x, y, w, h, light, dark); + else { - // set color alternating with every line - if ((mY % 2) == 0) - g.setColor(light); - else - g.setColor(dark); - - for (int mX = x + (xOff); mX < (x + w); mX += 4) + int xOff = 0; + for (int mY = y; mY < (y + h); mY++) { - g.drawLine(mX, mY, mX, mY); + // set color alternating with every line + if (((mY - y) % 2) == 0) + g.setColor(light); + else + g.setColor(dark); + + for (int mX = x + (xOff); mX < (x + w); mX += 4) + { + g.drawLine(mX, mY, mX, mY); + } + + // increase x offset + xOff++; + if (xOff > 3) + xOff = 0; } + } + } + + /** + * Fills a rectangle with the typical Metal pattern using Java2D. + * + * @param g2d the <code>Graphics2D</code> context to use + * @param x the X coordinate of the upper left corner of the rectangle to + * fill + * @param y the Y coordinate of the upper left corner of the rectangle to + * fill + * @param w the width of the rectangle to fill + * @param h the height of the rectangle to fill + */ + static void fillMetalPattern2D(Graphics2D g2d, int x, int y, int w, int h, + Color light, Color dark) + { + if (pattern2D == null || !darkColor.equals(dark) || !lightColor.equals(light)) + initializePattern(light, dark); + + // Prepare the texture. + TexturePaint texture = + new TexturePaint(pattern2D, new Rectangle2D.Double(0., 0., 4., 4.)); + g2d.setPaint(texture); + g2d.fillRect(x, y, w, h); + } - // increase x offset - xOff++; - if (xOff > 3) - xOff = 0; - } + /** + * Initializes the pattern image. + */ + static void initializePattern(Color light, Color dark) + { + pattern2D = new BufferedImage(4, 4, BufferedImage.TYPE_INT_ARGB); + lightColor = light; + darkColor = dark; + Graphics g = pattern2D.getGraphics(); + g.setColor(light); + g.fillRect(0, 0, 1, 1); + g.fillRect(2, 2, 1, 1); + g.setColor(dark); + g.fillRect(1, 1, 1, 1); + g.fillRect(3, 3, 1, 1); + g.dispose(); } } diff --git a/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java b/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java new file mode 100644 index 00000000000..85a8cb1ff86 --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/metal/OceanTheme.java @@ -0,0 +1,209 @@ +/* DefaultMetalTheme.java -- A modern theme for the Metal L&F + 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.plaf.metal; + +import javax.swing.UIDefaults; +import javax.swing.plaf.ColorUIResource; + +/** + * A modern theme for the Metal Look & Feel. + * @since 1.5 + * + * @author Roman Kennke (roman@kennke.org) + */ +public class OceanTheme extends DefaultMetalTheme +{ + /** + * The OceanTheme value for black. + */ + static final ColorUIResource BLACK = new ColorUIResource(51, 51, 51); + + /** + * The OceanTheme value for primary1. + */ + static final ColorUIResource PRIMARY1 = new ColorUIResource(99, 130, 191); + + /** + * The OceanTheme value for primary1. + */ + static final ColorUIResource PRIMARY2 = new ColorUIResource(163, 184, 204); + + /** + * The OceanTheme value for primary1. + */ + static final ColorUIResource PRIMARY3 = new ColorUIResource(184, 207, 229); + + /** + * The OceanTheme value for secondary1. + */ + static final ColorUIResource SECONDARY1 = new ColorUIResource(122, 138, 153); + + /** + * The OceanTheme value for secondary2. + */ + static final ColorUIResource SECONDARY2 = new ColorUIResource(184, 207, 229); + + /** + * The OceanTheme value for secondary3. + */ + static final ColorUIResource SECONDARY3 = new ColorUIResource(238, 238, 238); + + /** + * The OceanTheme value for inactive control text. + */ + static final ColorUIResource INACTIVE_CONTROL_TEXT = + new ColorUIResource(153, 153, 153); + + /** + * Returns the name of this theme, "Ocean" + */ + public String getName() + { + return "Ocean"; + } + + /** + * Returns the color for control text, which is the + * value of the theme's black value. + */ + public ColorUIResource getControlTextColor() + { + return getBlack(); + } + + /** + * Returns the desktop color, which is the theme's white color. + */ + public ColorUIResource getDesktopColor() + { + return getWhite(); + } + + /** + * Returns the color for inactive control text, which is the + * RGB value (153, 153, 153). + */ + public ColorUIResource getInactiveControlTextColor() + { + return INACTIVE_CONTROL_TEXT; + } + + /** + * Returns the OceanTheme's color for disabled menu foreground, + * + */ + public ColorUIResource getMenuDisabledForeground() + { + return INACTIVE_CONTROL_TEXT; + } + + + /** + * Returns the OceanTheme's color for black, the RGB value + * (51, 51, 51). + * + * @return Returns the OceanTheme's value for black + */ + protected ColorUIResource getBlack() + { + return BLACK; + } + + /** + * Return the OceanTheme's value for primary 1, the RGB value + * (99, 130, 191). + */ + protected ColorUIResource getPrimary1() + { + return PRIMARY1; + } + + /** + * Return the OceanTheme's value for primary 2, the RGB value + * (163, 184, 204). + */ + protected ColorUIResource getPrimary2() + { + return PRIMARY2; + } + + /** + * Return the OceanTheme's value for primary 1, the RGB value + * (184, 207, 229). + */ + protected ColorUIResource getPrimary3() + { + return PRIMARY3; + } + + /** + * Return the OceanTheme's value for secondary 1, the RGB value + * (122, 138, 153). + */ + protected ColorUIResource getSecondary1() + { + return SECONDARY1; + } + + /** + * Return the OceanTheme's value for secondary 2, the RGB value + * (184, 207, 229). + */ + protected ColorUIResource getSecondary2() + { + return SECONDARY2; + } + /** + * Return the OceanTheme's value for secondary 3, the RGB value + * (238, 238, 238). + */ + protected ColorUIResource getSecondary3() + { + return SECONDARY3; + } + + /** + * Adds customized entries to the UIDefaults table. + * + * @param defaults the UI defaults table + */ + public void addCustomEntriesToTable(UIDefaults defaults) + { + defaults.put("Button.rollover", Boolean.TRUE); + } +} diff --git a/libjava/classpath/javax/swing/plaf/metal/package.html b/libjava/classpath/javax/swing/plaf/metal/package.html index 2ea787bb5e2..8675493b68c 100644 --- a/libjava/classpath/javax/swing/plaf/metal/package.html +++ b/libjava/classpath/javax/swing/plaf/metal/package.html @@ -40,7 +40,16 @@ exception statement from your version. --> <head><title>GNU Classpath - javax.swing.plaf.metal</title></head> <body> -<p>Provides a cross-platform look and feel known as "Metal".</p> - +<p>Provides a cross-platform look and feel known as "Metal". To install this +look and feel, add the following code (or something similar) +near the start of your application:</p> +<pre>try + { + UIManager.setLookAndFeel(new MetalLookAndFeel()); + } +catch (UnsupportedLookAndFeelException e) + { + e.printStackTrace(); + }</pre> </body> </html> diff --git a/libjava/classpath/javax/swing/plaf/multi/MultiLookAndFeel.java b/libjava/classpath/javax/swing/plaf/multi/MultiLookAndFeel.java index a70a8ff690e..2bd358dd01e 100644 --- a/libjava/classpath/javax/swing/plaf/multi/MultiLookAndFeel.java +++ b/libjava/classpath/javax/swing/plaf/multi/MultiLookAndFeel.java @@ -56,6 +56,7 @@ public class MultiLookAndFeel extends LookAndFeel { */ public MultiLookAndFeel() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java b/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java index 349f4baad12..a187d74a686 100644 --- a/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java +++ b/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java @@ -43,8 +43,10 @@ import java.awt.Component; import java.awt.Rectangle; import java.io.Serializable; +import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JTable; +import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import javax.swing.JTextField; @@ -64,10 +66,21 @@ public class DefaultTableCellRenderer extends JLabel { public UIResource() { + super(); } } /** + * Stores the color set by setForeground(). + */ + Color foreground; + + /** + * Stores the color set by setBackground(). + */ + Color background; + + /** * Creates a default table cell renderer with an empty border. */ public DefaultTableCellRenderer() @@ -83,6 +96,7 @@ public class DefaultTableCellRenderer extends JLabel public void setForeground(Color c) { super.setForeground(c); + foreground = c; } /** @@ -93,6 +107,7 @@ public class DefaultTableCellRenderer extends JLabel public void setBackground(Color c) { super.setBackground(c); + background = c; } /** @@ -104,6 +119,8 @@ public class DefaultTableCellRenderer extends JLabel public void updateUI() { super.updateUI(); + background = null; + foreground = null; } /** @@ -137,17 +154,41 @@ public class DefaultTableCellRenderer extends JLabel if (isSelected) { - setBackground(table.getSelectionBackground()); - setForeground(table.getSelectionForeground()); + super.setBackground(table.getSelectionBackground()); + super.setForeground(table.getSelectionForeground()); } else { - setBackground(table.getBackground()); - setForeground(table.getForeground()); + if (background != null) + super.setBackground(background); + else + super.setBackground(table.getBackground()); + if (foreground != null) + super.setForeground(foreground); + else + super.setForeground(table.getForeground()); } + if (hasFocus) + { + setBorder(UIManager.getBorder("Table.focusCellHighlightBorder")); + if (table.isCellEditable(row, column)) + { + super.setBackground(UIManager.getColor("Table.focusCellBackground")); + super.setForeground(UIManager.getColor("Table.focusCellForeground")); + } + } + else + setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); + setEnabled(table.isEnabled()); setFont(table.getFont()); + + // If the current background is equal to the table's background, then we + // can avoid filling the background by setting the renderer opaque. + Color back = getBackground(); + setOpaque(back != null && back.equals(table.getBackground())); + return this; } diff --git a/libjava/classpath/javax/swing/table/JTableHeader.java b/libjava/classpath/javax/swing/table/JTableHeader.java index 45586da2009..163509a45c2 100644 --- a/libjava/classpath/javax/swing/table/JTableHeader.java +++ b/libjava/classpath/javax/swing/table/JTableHeader.java @@ -61,9 +61,14 @@ import javax.accessibility.AccessibleValue; import javax.swing.JComponent; import javax.swing.JTable; import javax.swing.UIManager; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.TableColumnModelEvent; +import javax.swing.event.TableColumnModelListener; import javax.swing.plaf.TableHeaderUI; public class JTableHeader extends JComponent + implements TableColumnModelListener, Accessible { protected class AccessibleJTableHeader extends AccessibleJComponent { @@ -305,11 +310,6 @@ public class JTableHeader extends JComponent private static final long serialVersionUID = 5144633983372967710L; /** - * The accessibleContext property. - */ - AccessibleContext accessibleContext; - - /** * The columnModel property. */ protected TableColumnModel columnModel; @@ -373,17 +373,8 @@ public class JTableHeader extends JComponent */ public JTableHeader(TableColumnModel cm) { - accessibleContext = new AccessibleJTableHeader(); columnModel = cm == null ? createDefaultColumnModel() : cm; - draggedColumn = null; - draggedDistance = 0; - opaque = true; - reorderingAllowed = true; - resizingAllowed = true; - resizingColumn = null; - table = null; - updateTableInRealTime = true; - cellRenderer = createDefaultRenderer(); + initializeLocalVars(); updateUI(); } @@ -504,7 +495,9 @@ public class JTableHeader extends JComponent */ public void setColumnModel(TableColumnModel c) { + columnModel.removeColumnModelListener(this); columnModel = c; + columnModel.addColumnModelListener(this); } /** @@ -619,7 +612,7 @@ public class JTableHeader extends JComponent public Rectangle getHeaderRect(int column) { - Rectangle r = getTable().getCellRect(-1, column, true); + Rectangle r = getTable().getCellRect(-1, column, false); r.height = getHeight(); return r; } @@ -665,4 +658,88 @@ public class JTableHeader extends JComponent return -1; } + + /** + * Receives notification when a column is added to the column model. + * + * @param event the table column model event + */ + public void columnAdded(TableColumnModelEvent event) + { + // TODO: What else to do here (if anything)? + resizeAndRepaint(); + } + + /** + * Receives notification when a column margin changes in the column model. + * + * @param event the table column model event + */ + public void columnMarginChanged(ChangeEvent event) + { + // TODO: What else to do here (if anything)? + resizeAndRepaint(); + } + + /** + * Receives notification when a column is moved within the column model. + * + * @param event the table column model event + */ + public void columnMoved(TableColumnModelEvent event) + { + // TODO: What else to do here (if anything)? + resizeAndRepaint(); + } + + /** + * Receives notification when a column is removed from the column model. + * + * @param event the table column model event + */ + public void columnRemoved(TableColumnModelEvent event) + { + // TODO: What else to do here (if anything)? + resizeAndRepaint(); + } + + /** + * Receives notification when the column selection has changed. + * + * @param event the table column model event + */ + public void columnSelectionChanged(ListSelectionEvent event) + { + // TODO: What else to do here (if anything)? + resizeAndRepaint(); + } + + /** + * Validates the layout of this table header and repaints it. This is + * equivalent to <code>revalidate()</code> followed by + * <code>repaint()</code>. + */ + public void resizeAndRepaint() + { + revalidate(); + repaint(); + } + + /** + * Initializes the fields and properties of this class with default values. + * This is called by the constructors. + */ + protected void initializeLocalVars() + { + accessibleContext = new AccessibleJTableHeader(); + draggedColumn = null; + draggedDistance = 0; + opaque = true; + reorderingAllowed = true; + resizingAllowed = true; + resizingColumn = null; + table = null; + updateTableInRealTime = true; + cellRenderer = createDefaultRenderer(); + } } diff --git a/libjava/classpath/javax/swing/table/TableColumn.java b/libjava/classpath/javax/swing/table/TableColumn.java index 9c36bb05ab0..9f06c5b7fcc 100644 --- a/libjava/classpath/javax/swing/table/TableColumn.java +++ b/libjava/classpath/javax/swing/table/TableColumn.java @@ -402,7 +402,11 @@ public class TableColumn if (width == oldWidth) return; - firePropertyChange(COLUMN_WIDTH_PROPERTY, oldWidth, width); + // We do have a constant field COLUMN_WIDTH_PROPERTY, + // however, tests show that the actual fired property name is 'width' + // and even Sun's API docs say that this constant field is obsolete and + // not used. + firePropertyChange("width", oldWidth, width); } /** @@ -422,12 +426,16 @@ public class TableColumn */ public void setPreferredWidth(int preferredWidth) { + int oldPrefWidth = this.preferredWidth; + if (preferredWidth < minWidth) this.preferredWidth = minWidth; else if (preferredWidth > maxWidth) this.preferredWidth = maxWidth; else this.preferredWidth = preferredWidth; + + firePropertyChange("preferredWidth", oldPrefWidth, this.preferredWidth); } /** diff --git a/libjava/classpath/javax/swing/table/TableColumnModel.java b/libjava/classpath/javax/swing/table/TableColumnModel.java index 76a145604db..b006f9ad4bb 100644 --- a/libjava/classpath/javax/swing/table/TableColumnModel.java +++ b/libjava/classpath/javax/swing/table/TableColumnModel.java @@ -50,6 +50,7 @@ import javax.swing.event.TableColumnModelListener; * * @author Andrew Selkirk */ +// FIXME: The API documentation in this class is incomplete. public interface TableColumnModel { /** @@ -107,7 +108,7 @@ public interface TableColumnModel * @throws IllegalArgumentException if <code>identifier</code> is * <code>null</code> or there is no column with that identifier. */ - int getColumnIndex(Object columnIdentifier); + int getColumnIndex(Object identifier); /** * Returns the <code>TableColumn</code> at the specified index. @@ -169,7 +170,6 @@ public interface TableColumnModel /** * getSelectionModel - * @param column TableColumn */ ListSelectionModel getSelectionModel(); diff --git a/libjava/classpath/javax/swing/text/AbstractDocument.java b/libjava/classpath/javax/swing/text/AbstractDocument.java index 3c9a4d497a5..baf8608b888 100644 --- a/libjava/classpath/javax/swing/text/AbstractDocument.java +++ b/libjava/classpath/javax/swing/text/AbstractDocument.java @@ -65,11 +65,10 @@ import javax.swing.undo.UndoableEdit; * @author original author unknown * @author Roman Kennke (roman@kennke.org) */ -public abstract class AbstractDocument - implements Document, Serializable +public abstract class AbstractDocument implements Document, Serializable { - /** The serial version UID for this class as of JDK1.4. */ - private static final long serialVersionUID = -116069779446114664L; + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 6842927725919637215L; /** * Standard error message to indicate a bad location. @@ -128,7 +127,28 @@ public abstract class AbstractDocument * Manages event listeners for this <code>Document</code>. */ protected EventListenerList listenerList = new EventListenerList(); + + /** + * Stores the current writer thread. Used for locking. + */ + private Thread currentWriter = null; + + /** + * The number of readers. Used for locking. + */ + private int numReaders = 0; + + /** + * Tells if there are one or more writers waiting. + */ + private int numWritersWaiting = 0; + + /** + * A condition variable that readers and writers wait on. + */ + Object documentCV = new Object(); + /** * Creates a new <code>AbstractDocument</code> with the specified * {@link Content} model. @@ -332,7 +352,7 @@ public abstract class AbstractDocument * @see GapContent * @see StringContent */ - protected Content getContent() + protected final Content getContent() { return content; } @@ -348,8 +368,7 @@ public abstract class AbstractDocument */ protected Thread getCurrentWriter() { - // FIXME: Implement locking! - return null; + return currentWriter; } /** @@ -516,13 +535,18 @@ public abstract class AbstractDocument // Just return when no text to insert was given. if (text == null || text.length() == 0) return; - DefaultDocumentEvent event = new DefaultDocumentEvent(offset, text.length(), DocumentEvent.EventType.INSERT); - content.insertString(offset, text); + + writeLock(); + UndoableEdit undo = content.insertString(offset, text); insertUpdate(event, attributes); + writeUnlock(); + fireInsertUpdate(event); + if (undo != null) + fireUndoableEditUpdate(new UndoableEditEvent(this, undo)); } /** @@ -566,10 +590,28 @@ public abstract class AbstractDocument } /** - * Blocks until a read lock can be obtained. + * Blocks until a read lock can be obtained. Must block if there is + * currently a writer modifying the <code>Document</code>. */ public void readLock() { + if (currentWriter != null && currentWriter.equals(Thread.currentThread())) + return; + synchronized (documentCV) + { + while (currentWriter != null || numWritersWaiting > 0) + { + try + { + documentCV.wait(); + } + catch (InterruptedException ie) + { + throw new Error("interrupted trying to get a readLock"); + } + } + numReaders++; + } } /** @@ -578,6 +620,40 @@ public abstract class AbstractDocument */ public void readUnlock() { + // Note we could have a problem here if readUnlock was called without a + // prior call to readLock but the specs simply warn users to ensure that + // balance by using a finally block: + // readLock() + // try + // { + // doSomethingHere + // } + // finally + // { + // readUnlock(); + // } + + // All that the JDK seems to check for is that you don't call unlock + // more times than you've previously called lock, but it doesn't make + // sure that the threads calling unlock were the same ones that called lock + + // FIXME: the reference implementation throws a + // javax.swing.text.StateInvariantError here + if (numReaders == 0) + throw new IllegalStateException("document lock failure"); + + synchronized (documentCV) + { + // If currentWriter is not null, the application code probably had a + // writeLock and then tried to obtain a readLock, in which case + // numReaders wasn't incremented + if (currentWriter == null) + { + numReaders --; + if (numReaders == 0 && numWritersWaiting != 0) + documentCV.notify(); + } + } } /** @@ -595,10 +671,42 @@ public abstract class AbstractDocument DefaultDocumentEvent event = new DefaultDocumentEvent(offset, length, DocumentEvent.EventType.REMOVE); + + // Here we set up the parameters for an ElementChange, if one + // needs to be added to the DocumentEvent later + Element root = getDefaultRootElement(); + int start = root.getElementIndex(offset); + int end = root.getElementIndex(offset + length); + + Element[] removed = new Element[end - start + 1]; + for (int i = start; i <= end; i++) + removed[i - start] = root.getElement(i); + removeUpdate(event); - content.remove(offset, length); + + Element[] added = new Element[1]; + added[0] = root.getElement(start); + boolean shouldFire = content.getString(offset, length).length() != 0; + + writeLock(); + UndoableEdit temp = content.remove(offset, length); + writeUnlock(); + postRemoveUpdate(event); - fireRemoveUpdate(event); + + GapContent.UndoRemove changes = null; + if (content instanceof GapContent) + changes = (GapContent.UndoRemove) temp; + + if (changes != null && !(start == end)) + { + // We need to add an ElementChange to our DocumentEvent + ElementEdit edit = new ElementEdit (root, start, removed, added); + event.addEdit(edit); + } + + if (shouldFire) + fireRemoveUpdate(event); } /** @@ -713,7 +821,15 @@ public abstract class AbstractDocument */ public void render(Runnable runnable) { - // FIXME: Implement me! + readLock(); + try + { + runnable.run(); + } + finally + { + readUnlock(); + } } /** @@ -725,6 +841,7 @@ public abstract class AbstractDocument */ public void setAsynchronousLoadPriority(int p) { + // TODO: Implement this properly. } /** @@ -739,11 +856,30 @@ public abstract class AbstractDocument } /** - * Blocks until a write lock can be obtained. + * Blocks until a write lock can be obtained. Must wait if there are + * readers currently reading or another thread is currently writing. */ protected void writeLock() { - // FIXME: Implement me. + if (currentWriter!= null && currentWriter.equals(Thread.currentThread())) + return; + synchronized (documentCV) + { + numWritersWaiting++; + while (numReaders > 0) + { + try + { + documentCV.wait(); + } + catch (InterruptedException ie) + { + throw new Error("interruped while trying to obtain write lock"); + } + } + numWritersWaiting --; + currentWriter = Thread.currentThread(); + } } /** @@ -752,7 +888,14 @@ public abstract class AbstractDocument */ protected void writeUnlock() { - // FIXME: Implement me. + synchronized (documentCV) + { + if (Thread.currentThread().equals(currentWriter)) + { + currentWriter = null; + documentCV.notifyAll(); + } + } } /** @@ -970,8 +1113,8 @@ public abstract class AbstractDocument public abstract class AbstractElement implements Element, MutableAttributeSet, TreeNode, Serializable { - /** The serial version UID for AbstractElement. */ - private static final long serialVersionUID = 1265312733007397733L; + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 1712240033321461704L; /** The number of characters that this Element spans. */ int count; @@ -1231,6 +1374,9 @@ public abstract class AbstractDocument /** * Returns the resolve parent of this element. + * This is taken from the AttributeSet, but if this is null, + * this method instead returns the Element's parent's + * AttributeSet * * @return the resolve parent of this element * @@ -1238,7 +1384,9 @@ public abstract class AbstractDocument */ public AttributeSet getResolveParent() { - return attributes.getResolveParent(); + if (attributes.getResolveParent() != null) + return attributes.getResolveParent(); + return element_parent.getAttributes(); } /** @@ -1355,49 +1503,6 @@ public abstract class AbstractDocument public abstract int getStartOffset(); /** - * Prints diagnostic information to the specified stream. - * - * @param stream the stream to dump to - * @param indent the indentation level - * @param element the element to be dumped - */ - private void dumpElement(PrintStream stream, String indent, - Element element) - { - // FIXME: Should the method be removed? - System.out.println(indent + "<" + element.getName() +">"); - - if (element.isLeaf()) - { - int start = element.getStartOffset(); - int end = element.getEndOffset(); - String text = ""; - try - { - text = getContent().getString(start, end - start); - } - catch (BadLocationException e) - { - AssertionError error = - new AssertionError("BadLocationException should not be " - + "thrown here. start = " + start - + ", end = " + end); - error.initCause(e); - throw error; - } - System.out.println(indent + " [" - + start + "," - + end + "][" - + text + "]"); - } - else - { - for (int i = 0; i < element.getElementCount(); ++i) - dumpElement(stream, indent + " ", element.getElement(i)); - } - } - - /** * Prints diagnostic output to the specified stream. * * @param stream the stream to write to @@ -1405,10 +1510,66 @@ public abstract class AbstractDocument */ public void dump(PrintStream stream, int indent) { - String indentStr = ""; + StringBuffer b = new StringBuffer(); for (int i = 0; i < indent; ++i) - indentStr += " "; - dumpElement(stream, indentStr, this); + b.append(' '); + b.append('<'); + b.append(getName()); + // Dump attributes if there are any. + if (getAttributeCount() > 0) + { + b.append('\n'); + Enumeration attNames = getAttributeNames(); + while (attNames.hasMoreElements()) + { + for (int i = 0; i < indent + 2; ++i) + b.append(' '); + Object attName = attNames.nextElement(); + b.append(attName); + b.append('='); + Object attribute = getAttribute(attName); + b.append(attribute); + b.append('\n'); + } + } + b.append(">\n"); + + // Dump element content for leaf elements. + if (isLeaf()) + { + for (int i = 0; i < indent + 2; ++i) + b.append(' '); + int start = getStartOffset(); + int end = getEndOffset(); + b.append('['); + b.append(start); + b.append(','); + b.append(end); + b.append("]["); + try + { + b.append(getDocument().getText(start, end - start)); + } + catch (BadLocationException ex) + { + AssertionError err = new AssertionError("BadLocationException " + + "must not be thrown " + + "here."); + err.initCause(ex); + throw err; + } + b.append("]\n"); + } + stream.print(b.toString()); + + // Dump child elements if any. + int count = getElementCount(); + for (int i = 0; i < count; ++i) + { + Element el = getElement(i); + if (el instanceof AbstractElement) + ((AbstractElement) el).dump(stream, indent + 2); + } } } @@ -1418,8 +1579,8 @@ public abstract class AbstractDocument */ public class BranchElement extends AbstractElement { - /** The serial version UID for BranchElement. */ - private static final long serialVersionUID = -8595176318868717313L; + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = -6037216547466333183L; /** The child elements of this BranchElement. */ private Element[] children = new Element[0]; @@ -1503,19 +1664,30 @@ public abstract class AbstractDocument */ public int getElementIndex(int offset) { - // If we have no children, return -1. - if (getElementCount() == 0) - return - 1; - + // If offset is less than the start offset of our first child, + // return 0 + if (offset < getStartOffset()) + return 0; + // XXX: There is surely a better algorithm // as beginning from first element each time. - for (int index = 0; index < children.length; ++index) + for (int index = 0; index < children.length - 1; ++index) { Element elem = children[index]; if ((elem.getStartOffset() <= offset) && (offset < elem.getEndOffset())) return index; + // If the next element's start offset is greater than offset + // then we have to return the closest Element, since no Elements + // will contain the offset + if (children[index + 1].getStartOffset() > offset) + { + if ((offset - elem.getEndOffset()) > (children[index + 1].getStartOffset() - offset)) + return index + 1; + else + return index; + } } // If offset is greater than the index of the last element, return @@ -1642,8 +1814,8 @@ public abstract class AbstractDocument public class DefaultDocumentEvent extends CompoundEdit implements DocumentEvent { - /** The serial version UID of DefaultDocumentEvent. */ - private static final long serialVersionUID = -7406103236022413522L; + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 5230037221564563284L; /** The starting offset of the change. */ private int offset; @@ -1748,7 +1920,7 @@ public abstract class AbstractDocument return (DocumentEvent.ElementChange) changes.get(elem); } } - + /** * An implementation of {@link DocumentEvent.ElementChange} to be added * to {@link DefaultDocumentEvent}s. @@ -1843,8 +2015,8 @@ public abstract class AbstractDocument */ public class LeafElement extends AbstractElement { - /** The serial version UID of LeafElement. */ - private static final long serialVersionUID = 5115368706941283802L; + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = -8906306331347768017L; /** Manages the start offset of this element. */ Position startPos; diff --git a/libjava/classpath/javax/swing/text/AttributeSet.java b/libjava/classpath/javax/swing/text/AttributeSet.java index 2f1f1890bae..01d148c067b 100644 --- a/libjava/classpath/javax/swing/text/AttributeSet.java +++ b/libjava/classpath/javax/swing/text/AttributeSet.java @@ -58,6 +58,7 @@ public interface AttributeSet */ static interface CharacterAttribute { + // This interface is a marker interface and has no methods. } /** @@ -65,6 +66,7 @@ public interface AttributeSet */ static interface ColorAttribute { + // This interface is a marker interface and has no methods. } /** @@ -72,6 +74,7 @@ public interface AttributeSet */ static interface FontAttribute { + // This interface is a marker interface and has no methods. } /** @@ -79,6 +82,7 @@ public interface AttributeSet */ static interface ParagraphAttribute { + // This interface is a marker interface and has no methods. } /** @@ -99,7 +103,7 @@ public interface AttributeSet * <code>false</code> otherwise. * * @param name the name of the requested attribute - * @param the value of the requested attribute + * @param value the value of the requested attribute * * @return <code>true</code> if this <code>AttributeSet</code> contains * an attribute with the specified <code>name</code> and diff --git a/libjava/classpath/javax/swing/text/BoxView.java b/libjava/classpath/javax/swing/text/BoxView.java index 0f8ba1ce15e..f201045dbdb 100644 --- a/libjava/classpath/javax/swing/text/BoxView.java +++ b/libjava/classpath/javax/swing/text/BoxView.java @@ -155,8 +155,9 @@ public class BoxView * automatically when any of the child view changes its preferences * via {@link #preferenceChanged(View, boolean, boolean)}. * - * The layout will be updated the next time when {@link #setSize()} is - * called, typically from within the {@link #paint()} method. + * The layout will be updated the next time when + * {@link #setSize(float, float)} is called, typically from within the + * {@link #paint(Graphics, Shape)} method. * * Valid values for the axis are {@link View#X_AXIS} and * {@link View#Y_AXIS}. @@ -216,12 +217,11 @@ public class BoxView * @param alloc the allocated region for the child to paint into * @param index the index of the child to be painted * - * @see {@link #childAllocation} + * @see #childAllocation(int, Rectangle) */ protected void paintChild(Graphics g, Rectangle alloc, int index) { View child = getView(index); - childAllocation(index, alloc); child.paint(g, alloc); } @@ -301,18 +301,15 @@ public class BoxView setSize(bounds.width, bounds.height); Rectangle inside = getInsideAllocation(a); - Rectangle copy = new Rectangle(inside); int count = getViewCount(); for (int i = 0; i < count; ++i) { - // TODO: Figure out if the parameter to paintChild is meant to - // be the child allocation or the allocation of this BoxView. - // I assume the second option here. - // We pass this method a copy of the inside rectangle here because - // it modifies the actual values. copy.setBounds(inside); - paintChild(g, copy, i); + childAllocation(i, copy); + if (!copy.isEmpty() + && g.hitClip(copy.x, copy.y, copy.width, copy.height)) + paintChild(g, copy, i); } } @@ -362,6 +359,24 @@ public class BoxView } /** + * Calculates the layout of the children of this <code>BoxView</code> along + * the specified axis. + * + * @param span the target span + * @param axis the axis that is examined + * @param offsets an empty array, filled with the offsets of the children + * @param spans an empty array, filled with the spans of the children + */ + protected void baselineLayout(int span, int axis, int[] offsets, + int[] spans) + { + if (axis == myAxis) + layoutMajorAxis(span, axis, offsets, spans); + else + layoutMinorAxis(span, axis, offsets, spans); + } + + /** * Calculates the size requirements of this <code>BoxView</code> along * its major axis, that is the axis specified in the constructor. * @@ -375,27 +390,8 @@ public class BoxView protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements sr) { - if (sr == null) - sr = new SizeRequirements(); - else - { - sr.maximum = 0; - sr.minimum = 0; - sr.preferred = 0; - sr.alignment = 0.5F; - } - - int count = getViewCount(); - - // Sum up the sizes of the children along the specified axis. - for (int i = 0; i < count; ++i) - { - View child = getView(i); - sr.minimum += child.getMinimumSpan(axis); - sr.preferred += child.getPreferredSpan(axis); - sr.maximum += child.getMaximumSpan(axis); - } - return sr; + SizeRequirements[] childReqs = getChildRequirements(axis); + return SizeRequirements.getTiledSizeRequirements(childReqs); } /** @@ -413,48 +409,8 @@ public class BoxView protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements sr) { - if (sr == null) - sr = new SizeRequirements(); - else - { - sr.maximum = 0; - sr.minimum = 0; - sr.preferred = 0; - sr.alignment = 0.5F; - } - - int count = getViewCount(); - - int aboveBaseline = 0; - int belowBaseline = 0; - int aboveBaselineMin = 0; - int belowBaselineMin = 0; - int aboveBaselineMax = 0; - int belowBaselineMax = 0; - - for (int i = 0; i < count; ++i) - { - View child = getView(i); - float align = child.getAlignment(axis); - int pref = (int) child.getPreferredSpan(axis); - int min = (int) child.getMinimumSpan(axis); - int max = (int) child.getMaximumSpan(axis); - aboveBaseline += (int) (align * pref); - belowBaseline += (int) ((1.F - align) * pref); - aboveBaselineMin += (int) (align * min); - belowBaselineMin += (int) ((1.F - align) * min); - aboveBaselineMax += (int) (align * max); - belowBaselineMax += (int) ((1.F - align) * max); - } - sr.minimum = aboveBaselineMin + belowBaselineMin; - sr.maximum = aboveBaselineMax + belowBaselineMax; - sr.preferred = aboveBaseline + belowBaseline; - if (aboveBaseline == 0) - sr.alignment = 1.0F; - else - sr.alignment = (float) (sr.preferred / aboveBaseline); - - return sr; + SizeRequirements[] childReqs = getChildRequirements(axis); + return SizeRequirements.getAlignedSizeRequirements(childReqs); } /** @@ -569,19 +525,8 @@ public class BoxView */ protected void layout(int width, int height) { - this.width = width; - this.height = height; - - if (myAxis == X_AXIS) - { - layoutMajorAxis(width, X_AXIS, offsetsX, spansX); - layoutMinorAxis(height, Y_AXIS, offsetsY, spansY); - } - else - { - layoutMajorAxis(height, Y_AXIS, offsetsY, spansY); - layoutMinorAxis(width, X_AXIS, offsetsX, spansX); - } + baselineLayout(width, X_AXIS, offsetsX, spansX); + baselineLayout(height, Y_AXIS, offsetsY, spansY); } /** @@ -591,28 +536,16 @@ public class BoxView * to layout the children * @param axis the axis along which the layout is performed * @param offsets the array that holds the offsets of the children on exit - * @param offsets the array that holds the spans of the children on exit + * @param spans the array that holds the spans of the children on exit */ protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { - // Allocate SizeRequirements for each child view. - int count = getViewCount(); - SizeRequirements[] childReqs = new SizeRequirements[count]; - for (int i = 0; i < count; ++i) - { - View view = getView(i); - childReqs[i] = new SizeRequirements((int) view.getMinimumSpan(axis), - (int) view.getPreferredSpan(axis), - (int) view.getMaximumSpan(axis), - view.getAlignment(axis)); - } - + SizeRequirements[] childReqs = getChildRequirements(axis); // Calculate the spans and offsets using the SizeRequirements uility // methods. SizeRequirements.calculateTiledPositions(targetSpan, null, childReqs, offsets, spans); - validateLayout(axis); } @@ -623,26 +556,21 @@ public class BoxView * to layout the children * @param axis the axis along which the layout is performed * @param offsets the array that holds the offsets of the children on exit - * @param offsets the array that holds the spans of the children on exit + * @param spans the array that holds the spans of the children on exit */ protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { - // Allocate SizeRequirements for each child view. - int count = getViewCount(); - SizeRequirements[] childReqs = new SizeRequirements[count]; - for (int i = 0; i < count; ++i) - { - View view = getView(i); - childReqs[i] = new SizeRequirements((int) view.getMinimumSpan(axis), - (int) view.getPreferredSpan(axis), - (int) view.getMaximumSpan(axis), - view.getAlignment(axis)); - } - + SizeRequirements[] childReqs = getChildRequirements(axis); // Calculate the spans and offsets using the SizeRequirements uility // methods. - SizeRequirements.calculateAlignedPositions(targetSpan, null, childReqs, + // TODO: This might be an opportunity for performance optimization. Here + // we could use a cached instance of SizeRequirements instead of passing + // null to baselineRequirements. However, this would involve rewriting + // the baselineRequirements() method to not use the SizeRequirements + // utility method, since they cannot reuse a cached instance. + SizeRequirements total = baselineRequirements(axis, null); + SizeRequirements.calculateAlignedPositions(targetSpan, total, childReqs, offsets, spans); validateLayout(axis); } @@ -692,6 +620,9 @@ public class BoxView layoutChanged(X_AXIS); if (this.height != (int) height) layoutChanged(Y_AXIS); + + this.width = (int) width; + this.height = (int) height; Rectangle outside = new Rectangle(0, 0, this.width, this.height); Rectangle inside = getInsideAllocation(outside); @@ -711,4 +642,99 @@ public class BoxView if (axis == Y_AXIS) yLayoutValid = true; } + + /** + * Returns the size requirements of this view's children for the major + * axis. + * + * @return the size requirements of this view's children for the major + * axis + */ + SizeRequirements[] getChildRequirements(int axis) + { + // Allocate SizeRequirements for each child view. + int count = getViewCount(); + SizeRequirements[] childReqs = new SizeRequirements[count]; + for (int i = 0; i < count; ++i) + { + View view = getView(i); + childReqs[i] = new SizeRequirements((int) view.getMinimumSpan(axis), + (int) view.getPreferredSpan(axis), + (int) view.getMaximumSpan(axis), + view.getAlignment(axis)); + } + return childReqs; + } + + /** + * Returns the span for the child view with the given index for the specified + * axis. + * + * @param axis the axis to examine, either <code>X_AXIS</code> or + * <code>Y_AXIS</code> + * @param childIndex the index of the child for for which to return the span + * + * @return the span for the child view with the given index for the specified + * axis + */ + protected int getSpan(int axis, int childIndex) + { + if (axis == X_AXIS) + return spansX[childIndex]; + else + return spansY[childIndex]; + } + + /** + * Returns the offset for the child view with the given index for the + * specified axis. + * + * @param axis the axis to examine, either <code>X_AXIS</code> or + * <code>Y_AXIS</code> + * @param childIndex the index of the child for for which to return the span + * + * @return the offset for the child view with the given index for the + * specified axis + */ + protected int getOffset(int axis, int childIndex) + { + if (axis == X_AXIS) + return offsetsX[childIndex]; + else + return offsetsY[childIndex]; + } + + /** + * Returns the alignment for this box view for the specified axis. The + * axis that is tiled (the major axis) will be requested to be aligned + * centered (0.5F). The minor axis alignment depends on the child view's + * total alignment. + * + * @param axis the axis which is examined + * + * @return the alignment for this box view for the specified axis + */ + public float getAlignment(int axis) + { + if (axis == myAxis) + return 0.5F; + else + return baselineRequirements(axis, null).alignment; + } + + /** + * Called by a child View when its preferred span has changed. + * + * @param width indicates that the preferred width of the child changed. + * @param height indicates that the preferred height of the child changed. + * @param child the child View. + */ + public void preferenceChanged (View child, boolean width, boolean height) + { + if (width) + xLayoutValid = false; + if (height) + yLayoutValid = false; + super.preferenceChanged(child, width, height); + } } diff --git a/libjava/classpath/javax/swing/text/ComponentView.java b/libjava/classpath/javax/swing/text/ComponentView.java index f6feda21513..16112c8f4de 100644 --- a/libjava/classpath/javax/swing/text/ComponentView.java +++ b/libjava/classpath/javax/swing/text/ComponentView.java @@ -1,5 +1,5 @@ /* ComponentView.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -41,65 +41,125 @@ import java.awt.Component; import java.awt.Graphics; import java.awt.Shape; +import javax.swing.SwingConstants; + +/** + * A {@link View} implementation that is able to render arbitrary + * {@link Component}s. This uses the attribute + * {@link StyleConstants#ComponentAttribute} to determine the + * <code>Component</code> that should be rendered. This <code>Component</code> + * becomes a direct child of the <code>JTextComponent</code> that contains + * this <code>ComponentView</code>, so this view must not be shared between + * multiple <code>JTextComponent</code>s. + * + * @author original author unknown + * @author Roman Kennke (roman@kennke.org) + */ +// FIXME: This class is a complete stub and needs to be implemented properly. public class ComponentView extends View { - public ComponentView(Element elem) - { - super(elem); - } - - protected Component createComponent() - { - return null; - } - - public float getAlignment(int axis) - { - return 0; - } - - public final Component getComponent() - { - return null; - } - - public float getMaximumSpan(int axis) - { - return 0; - } - - public float getMinimumSpan(int axis) - { - return 0; - } - - public float getPreferredSpan(int axis) - { - return 0; - } - - public Shape modelToView(int pos, Shape a, Position.Bias b) - throws BadLocationException - { - return null; - } - - public void paint(Graphics g, Shape a) - { - } + /** + * Creates a new instance of <code>ComponentView</code> for the specified + * <code>Element</code>. + * + * @param elem the element that this <code>View</code> is rendering + */ + public ComponentView(Element elem) + { + super(elem); + } + + /** + * Creates the <code>Component</code> that this <code>View</code> is + * rendering. The <code>Component</code> is determined using + * the {@link StyleConstants#ComponentAttribute} of the associated + * <code>Element</code>. + * + * @return the component that is rendered + */ + protected Component createComponent() + { + return StyleConstants.getComponent(getElement().getAttributes()); + } + + /** + * Returns the alignment of this <code>View</code> along the specified axis. + * + * @param axis either {@link View#X_AXIS} or {@link View#Y_AXIS} + * + * @return the alignment of this <code>View</code> along the specified axis + */ + public float getAlignment(int axis) + { + return 0; + } + + /** + * Returns the <code>Component</code> that is rendered by this + * <code>ComponentView</code>. + * + * @return the <code>Component</code> that is rendered by this + * <code>ComponentView</code> + */ + public final Component getComponent() + { + return null; + } + + /** + * Returns the maximum span of this <code>View</code> along the specified + * axis. + * + * This will return {@link Component#getMaximumSize()} for the specified + * axis. + * + * @return the maximum span of this <code>View</code> along the specified + * axis + */ + public float getMaximumSpan(int axis) + { + return 0; + } + + public float getMinimumSpan(int axis) + { + // TODO: Implement this properly. + return 0; + } + + public float getPreferredSpan(int axis) + { + // TODO: Implement this properly. + return 0; + } + + public Shape modelToView(int pos, Shape a, Position.Bias b) + throws BadLocationException + { + // TODO: Implement this properly. + return null; + } - public void setParent(View p) - { - } + public void paint(Graphics g, Shape a) + { + // TODO: Implement this properly. + } + + public void setParent(View p) + { + // TODO: Implement this properly. + } - public void setSize(float width, float height) - { - } + public void setSize(float width, float height) + { + // TODO: Implement this properly. + } - public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) - { - return 0; - } + public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) + { + // TODO: Implement this properly. + return 0; + } /** * Maps coordinates from the <code>View</code>'s space into a position @@ -118,4 +178,34 @@ public class ComponentView extends View // FIXME: Implement this properly. return 0; } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset in + * the document model + */ + public int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException + { + // TODO: Implement this properly. + throw new AssertionError("Not implemented yet."); + } } diff --git a/libjava/classpath/javax/swing/text/CompositeView.java b/libjava/classpath/javax/swing/text/CompositeView.java index 6776c95727a..bc626a40696 100644 --- a/libjava/classpath/javax/swing/text/CompositeView.java +++ b/libjava/classpath/javax/swing/text/CompositeView.java @@ -62,7 +62,7 @@ public abstract class CompositeView /** * The allocation of this <code>View</code> minus its insets. This is * initialized in {@link #getInsideAllocation} and reused and modified in - * {@link childAllocation}. + * {@link #childAllocation(int, Rectangle)}. */ Rectangle insideAllocation; @@ -221,20 +221,17 @@ public abstract class CompositeView if (childIndex != -1) { View child = getView(childIndex); - Shape result = child.modelToView(pos, a, bias); + Rectangle r = a.getBounds(); + childAllocation(childIndex, r); + Shape result = child.modelToView(pos, r, bias); if (result == null) throw new AssertionError("" + child.getClass().getName() + ".modelToView() must not return null"); return result; } else - { - // FIXME: Handle the case when we have no child view for the given - // position. - throw new AssertionError("No child views found where child views are " - + "expected. pos = " + pos + ", bias = " - + bias); - } + throw new BadLocationException("No child view for the specified location", + pos); } /** @@ -314,6 +311,7 @@ public abstract class CompositeView */ public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a, int direction, Position.Bias[] biasRet) + throws BadLocationException { int retVal = -1; switch (direction) @@ -433,10 +431,16 @@ public abstract class CompositeView */ protected int getViewIndexAtPosition(int pos) { - // We have one child view allocated for each child element in - // loadChildren(), so this should work. - Element el = getElement(); - int index = el.getElementIndex(pos); + int index = -1; + for (int i = 0; i < children.length; i++) + { + if (children[i].getStartOffset() <= pos + && children[i].getEndOffset() > pos) + { + index = i; + break; + } + } return index; } @@ -473,8 +477,8 @@ public abstract class CompositeView insideAllocation = inside; } } - inside.x = alloc.x - insets.left; - inside.y = alloc.y - insets.top; + inside.x = alloc.x + insets.left; + inside.y = alloc.y + insets.top; inside.width = alloc.width - insets.left - insets.right; inside.height = alloc.height - insets.top - insets.bottom; return inside; @@ -594,6 +598,7 @@ public abstract class CompositeView protected int getNextNorthSouthVisualPositionFrom(int pos, Position.Bias b, Shape a, int direction, Position.Bias[] biasRet) + throws BadLocationException { // FIXME: Implement this correctly. return pos; @@ -627,6 +632,7 @@ public abstract class CompositeView protected int getNextEastWestVisualPositionFrom(int pos, Position.Bias b, Shape a, int direction, Position.Bias[] biasRet) + throws BadLocationException { // FIXME: Implement this correctly. return pos; @@ -649,4 +655,34 @@ public abstract class CompositeView { return false; } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset in + * the document model + */ + public int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException + { + // TODO: Implement this properly. + throw new AssertionError("Not implemented yet."); + } } diff --git a/libjava/classpath/javax/swing/text/DefaultCaret.java b/libjava/classpath/javax/swing/text/DefaultCaret.java index 33c3ae3bf28..66e2f4723cf 100644 --- a/libjava/classpath/javax/swing/text/DefaultCaret.java +++ b/libjava/classpath/javax/swing/text/DefaultCaret.java @@ -40,15 +40,24 @@ package javax.swing.text; import java.awt.Graphics; import java.awt.Point; import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.util.EventListener; +import javax.swing.JComponent; +import javax.swing.SwingUtilities; +import javax.swing.Timer; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import javax.swing.event.EventListenerList; /** @@ -60,12 +69,164 @@ import javax.swing.event.EventListenerList; public class DefaultCaret extends Rectangle implements Caret, FocusListener, MouseListener, MouseMotionListener { + + /** + * Controls the blinking of the caret. + * + * @author Roman Kennke (kennke@aicas.com) + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ + private class BlinkTimerListener implements ActionListener + { + /** + * Forces the next event to be ignored. The next event should be ignored + * if we force the caret to appear. We do not know how long will it take + * to fire the comming event; this may be near immediately. Better to leave + * the caret visible one iteration longer. + */ + boolean ignoreNextEvent; + + /** + * Receives notification when the blink timer fires and updates the visible + * state of the caret. + * + * @param event the action event + */ + public void actionPerformed(ActionEvent event) + { + if (ignoreNextEvent) + ignoreNextEvent = false; + else + { + visible = !visible; + repaint(); + } + } + } + + /** + * Listens for changes in the text component's document and updates the + * caret accordingly. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class DocumentHandler implements DocumentListener + { + /** + * Receives notification that some text attributes have changed. No action + * is taken here. + * + * @param event the document event + */ + public void changedUpdate(DocumentEvent event) + { + // Nothing to do here. + } + + /** + * Receives notification that some text has been inserted from the text + * component. The caret is moved forward accordingly. + * + * @param event the document event + */ + public void insertUpdate(DocumentEvent event) + { + if (policy == ALWAYS_UPDATE || + (SwingUtilities.isEventDispatchThread() && + policy == UPDATE_WHEN_ON_EDT)) + { + int dot = getDot(); + setDot(dot + event.getLength()); + } + } + + /** + * Receives notification that some text has been removed into the text + * component. The caret is moved backwards accordingly. + * + * @param event the document event + */ + public void removeUpdate(DocumentEvent event) + { + if (policy == ALWAYS_UPDATE || + (SwingUtilities.isEventDispatchThread() && + policy == UPDATE_WHEN_ON_EDT)) + { + int dot = getDot(); + setDot(dot - event.getLength()); + } + else if (policy == NEVER_UPDATE) + { + int docLength = event.getDocument().getLength(); + if (getDot() > docLength) + setDot(docLength); + } + } + } + + /** + * Listens for property changes on the text document. This is used to add and + * remove our document listener, if the document of the text component has + * changed. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class PropertyChangeHandler implements PropertyChangeListener + { + + /** + * Receives notification when a property has changed on the text component. + * This adds/removes our document listener from the text component's + * document when the document changes. + * + * @param e the property change event + */ + public void propertyChange(PropertyChangeEvent e) + { + if (e.getPropertyName().equals("document")) + { + Document oldDoc = (Document) e.getOldValue(); + oldDoc.removeDocumentListener(documentListener); + Document newDoc = (Document) e.getNewValue(); + newDoc.addDocumentListener(documentListener); + } + } + + } + + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 4325555698756477346L; + /** - * The serial version UID for DefaultCaret. + * Indicates the Caret position should always be updated after Document + * changes even if the updates are not performed on the Event Dispatching + * thread. + * + * @since 1.5 */ - private static final long serialVersionUID = 228155774675466193L; + public static final int ALWAYS_UPDATE = 2; /** + * Indicates the Caret position should not be changed unless the Document + * length becomes less than the Caret position, in which case the Caret + * is moved to the end of the Document. + * + * @since 1.5 + */ + public static final int NEVER_UPDATE = 1; + + /** + * Indicates the Caret position should be updated only if Document changes + * are made on the Event Dispatcher thread. + * + * @since 1.5 + */ + public static final int UPDATE_WHEN_ON_EDT = 0; + + /** Keeps track of the current update policy **/ + int policy = UPDATE_WHEN_ON_EDT; + + /** * The <code>ChangeEvent</code> that is fired by {@link #fireStateChanged()}. */ protected ChangeEvent changeEvent = new ChangeEvent(this); @@ -76,6 +237,16 @@ public class DefaultCaret extends Rectangle protected EventListenerList listenerList = new EventListenerList(); /** + * Our document listener. + */ + DocumentListener documentListener; + + /** + * Our property listener. + */ + PropertyChangeListener propertyChangeListener; + + /** * The text component in which this caret is installed. */ private JTextComponent textComponent; @@ -106,15 +277,65 @@ public class DefaultCaret extends Rectangle private Point magicCaretPosition = null; /** - * Indicates if this <code>Caret</code> is currently visible or not. + * Indicates if this <code>Caret</code> is currently visible or not. This is + * package private to avoid an accessor method. */ - private boolean visible = true; + boolean visible = false; /** * The current highlight entry. */ private Object highlightEntry; + private Timer blinkTimer; + + private BlinkTimerListener blinkListener; + + /** + * Creates a new <code>DefaultCaret</code> instance. + */ + public DefaultCaret() + { + // Nothing to do here. + } + + /** + * Sets the Caret update policy. + * + * @param policy the new policy. Valid values are: + * ALWAYS_UPDATE: always update the Caret position, even when Document + * updates don't occur on the Event Dispatcher thread. + * NEVER_UPDATE: don't update the Caret position unless the Document + * length becomes less than the Caret position (then update the + * Caret to the end of the Document). + * UPDATE_WHEN_ON_EDT: update the Caret position when the + * Document updates occur on the Event Dispatcher thread. This is the + * default. + * + * @since 1.5 + * @throws IllegalArgumentException if policy is not one of the above. + */ + public void setUpdatePolicy (int policy) + { + if (policy != ALWAYS_UPDATE && policy != NEVER_UPDATE + && policy != UPDATE_WHEN_ON_EDT) + throw new + IllegalArgumentException + ("policy must be ALWAYS_UPDATE, NEVER__UPDATE, or UPDATE_WHEN_ON_EDT"); + this.policy = policy; + } + + /** + * Gets the caret update policy. + * + * @return the caret update policy. + * @since 1.5 + */ + public int getUpdatePolicy () + { + return policy; + } + /** * Moves the caret position when the mouse is dragged over the text * component, modifying the selection accordingly. @@ -123,7 +344,7 @@ public class DefaultCaret extends Rectangle */ public void mouseDragged(MouseEvent event) { - // FIXME: Implement this properly. + moveCaret(event); } /** @@ -153,7 +374,7 @@ public class DefaultCaret extends Rectangle */ public void mouseClicked(MouseEvent event) { - // FIXME: Implement this properly. + // TODO: Implement double- and triple-click behaviour here. } /** @@ -175,6 +396,7 @@ public class DefaultCaret extends Rectangle */ public void mouseExited(MouseEvent event) { + // Nothing to do here. } /** @@ -187,7 +409,7 @@ public class DefaultCaret extends Rectangle */ public void mousePressed(MouseEvent event) { - // FIXME: Implement this properly. + positionCaret(event); } /** @@ -208,6 +430,7 @@ public class DefaultCaret extends Rectangle */ public void focusGained(FocusEvent event) { + setVisible(true); } /** @@ -217,6 +440,8 @@ public class DefaultCaret extends Rectangle */ public void focusLost(FocusEvent event) { + if (event.isTemporary() == false) + setVisible(false); } /** @@ -227,7 +452,8 @@ public class DefaultCaret extends Rectangle */ protected void moveCaret(MouseEvent event) { - // FIXME: Implement this properly. + int newDot = getComponent().viewToModel(event.getPoint()); + moveDot(newDot); } /** @@ -238,7 +464,8 @@ public class DefaultCaret extends Rectangle */ protected void positionCaret(MouseEvent event) { - // FIXME: Implement this properly. + int newDot = getComponent().viewToModel(event.getPoint()); + setDot(newDot); } /** @@ -253,7 +480,16 @@ public class DefaultCaret extends Rectangle textComponent.removeFocusListener(this); textComponent.removeMouseListener(this); textComponent.removeMouseMotionListener(this); + textComponent.getDocument().removeDocumentListener(documentListener); + documentListener = null; + textComponent.removePropertyChangeListener(propertyChangeListener); + propertyChangeListener = null; textComponent = null; + + // Deinstall blink timer if present. + if (blinkTimer != null) + blinkTimer.stop(); + blinkTimer = null; } /** @@ -269,6 +505,11 @@ public class DefaultCaret extends Rectangle textComponent.addFocusListener(this); textComponent.addMouseListener(this); textComponent.addMouseMotionListener(this); + propertyChangeListener = new PropertyChangeHandler(); + textComponent.addPropertyChangeListener(propertyChangeListener); + documentListener = new DocumentHandler(); + textComponent.getDocument().addDocumentListener(documentListener); + repaint(); } @@ -376,10 +617,7 @@ public class DefaultCaret extends Rectangle */ protected final void repaint() { - // FIXME: Is this good? This possibly causes alot of the component - // hierarchy to be repainted on every caret blink. - if (textComponent != null) - textComponent.repaint(); + getComponent().repaint(x, y, width, height); } /** @@ -390,7 +628,8 @@ public class DefaultCaret extends Rectangle */ public void paint(Graphics g) { - if (textComponent == null) + JTextComponent comp = getComponent(); + if (comp == null) return; int dot = getDot(); @@ -398,25 +637,33 @@ public class DefaultCaret extends Rectangle try { - rect = textComponent.modelToView(dot); + rect = textComponent.modelToView(dot); } catch (BadLocationException e) { - // This should never happen as dot should be always valid. - return; + assert false : "Unexpected bad caret location: " + dot; + return; } if (rect == null) return; - - // First we need to delete the old caret. - // FIXME: Implement deleting of old caret. - + + // Check if paint has possibly been called directly, without a previous + // call to damage(). In this case we need to do some cleanup first. + if ((x != rect.x) || (y != rect.y)) + { + repaint(); // Erase previous location of caret. + x = rect.x; + y = rect.y; + width = 1; + height = rect.height; + } + // Now draw the caret on the new position if visible. if (visible) { - g.setColor(textComponent.getCaretColor()); - g.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height); + g.setColor(textComponent.getCaretColor()); + g.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height); } } @@ -507,6 +754,8 @@ public class DefaultCaret extends Rectangle */ public void setBlinkRate(int rate) { + if (blinkTimer != null) + blinkTimer.setDelay(rate); blinkRate = rate; } @@ -534,7 +783,8 @@ public class DefaultCaret extends Rectangle { this.dot = dot; handleHighlight(); - repaint(); + adjustVisibility(this); + appear(); } /** @@ -551,8 +801,45 @@ public class DefaultCaret extends Rectangle this.dot = dot; this.mark = dot; handleHighlight(); - repaint(); + adjustVisibility(this); + appear(); } + + /** + * Show the caret (may be hidden due blinking) and adjust the timer not to + * hide it (possibly immediately). + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ + void appear() + { + // All machinery is only required if the carret is blinking. + if (blinkListener != null) + { + blinkListener.ignoreNextEvent = true; + + // If the caret is visible, erase the current position by repainting + // over. + if (visible) + repaint(); + + // Draw the caret in the new position. + visible = true; + + Rectangle area = null; + try + { + area = getComponent().modelToView(getDot()); + } + catch (BadLocationException ex) + { + assert false : "Unexpected bad caret location: " + getDot(); + } + if (area != null) + damage(area); + } + repaint(); + } /** * Returns <code>true</code> if this <code>Caret</code> is currently visible, @@ -574,8 +861,33 @@ public class DefaultCaret extends Rectangle */ public void setVisible(boolean v) { - visible = v; - repaint(); + if (v != visible) + { + visible = v; + if (visible) + if (textComponent.isEnabled() && textComponent.isEditable()) + { + if (blinkTimer == null) + initBlinkTimer(); + blinkTimer.start(); + } + else + { + if (blinkTimer != null) + blinkTimer.stop(); + } + Rectangle area = null; + try + { + area = getComponent().modelToView(getDot()); + } + catch (BadLocationException ex) + { + assert false: "Unexpected bad caret location: " + getDot(); + } + if (area != null) + damage(area); + } } /** @@ -589,4 +901,47 @@ public class DefaultCaret extends Rectangle { return DefaultHighlighter.DefaultPainter; } + + /** + * Updates the carets rectangle properties to the specified rectangle and + * repaints the caret. + * + * @param r the rectangle to set as the caret rectangle + */ + protected void damage(Rectangle r) + { + if (r == null) + return; + x = r.x; + y = r.y; + width = 1; + // height is normally set in paint and we leave it untouched. However, we + // must set a valid value here, since otherwise the painting mechanism + // sets a zero clip and never calls paint. + if (height <= 0) + height = getComponent().getHeight(); + repaint(); + } + + /** + * Adjusts the text component so that the caret is visible. This default + * implementation simply calls + * {@link JComponent#scrollRectToVisible(Rectangle)} on the text component. + * Subclasses may wish to change this. + */ + protected void adjustVisibility(Rectangle rect) + { + getComponent().scrollRectToVisible(rect); + } + + /** + * Initializes the blink timer. + */ + private void initBlinkTimer() + { + // Setup the blink timer. + blinkListener = new BlinkTimerListener(); + blinkTimer = new Timer(getBlinkRate(), blinkListener); + blinkTimer.setRepeats(true); + } } diff --git a/libjava/classpath/javax/swing/text/DefaultEditorKit.java b/libjava/classpath/javax/swing/text/DefaultEditorKit.java index a14f3ff4fe0..3b3fc1f7238 100644 --- a/libjava/classpath/javax/swing/text/DefaultEditorKit.java +++ b/libjava/classpath/javax/swing/text/DefaultEditorKit.java @@ -66,8 +66,7 @@ public class DefaultEditorKit extends EditorKit * * @see Toolkit#beep() */ - public static class BeepAction - extends TextAction + public static class BeepAction extends TextAction { /** * Creates a new <code>BeepAction</code>. @@ -95,8 +94,7 @@ public class DefaultEditorKit extends EditorKit * @see CutAction * @see PasteAction */ - public static class CopyAction - extends TextAction + public static class CopyAction extends TextAction { /** @@ -128,8 +126,7 @@ public class DefaultEditorKit extends EditorKit * @see CopyAction * @see PasteAction */ - public static class CutAction - extends TextAction + public static class CutAction extends TextAction { /** @@ -159,8 +156,7 @@ public class DefaultEditorKit extends EditorKit * @see CopyAction * @see CutAction */ - public static class PasteAction - extends TextAction + public static class PasteAction extends TextAction { /** @@ -226,9 +222,6 @@ public class DefaultEditorKit extends EditorKit { t.getDocument().insertString(t.getCaret().getDot(), event.getActionCommand(), null); - t.getCaret().setDot(Math.min(t.getCaret().getDot() + 1, - t.getDocument().getEndPosition() - .getOffset())); } catch (BadLocationException be) { @@ -243,8 +236,7 @@ public class DefaultEditorKit extends EditorKit * of the text component. This is typically triggered by hitting * ENTER on the keyboard. */ - public static class InsertBreakAction - extends TextAction + public static class InsertBreakAction extends TextAction { /** @@ -273,8 +265,7 @@ public class DefaultEditorKit extends EditorKit */ // FIXME: Figure out what this Action is supposed to do. Obviously text // that is entered by the user is inserted through DefaultKeyTypedAction. - public static class InsertContentAction - extends TextAction + public static class InsertContentAction extends TextAction { /** @@ -292,14 +283,15 @@ public class DefaultEditorKit extends EditorKit */ public void actionPerformed(ActionEvent event) { + // FIXME: Figure out what this Action is supposed to do. Obviously text + // that is entered by the user is inserted through DefaultKeyTypedAction. } } /** * Inserts a TAB character into the text editor. */ - public static class InsertTabAction - extends TextAction + public static class InsertTabAction extends TextAction { /** @@ -699,6 +691,7 @@ public class DefaultEditorKit extends EditorKit */ public DefaultEditorKit() { + // Nothing to do here. } /** @@ -954,15 +947,23 @@ public class DefaultEditorKit extends EditorKit * @param offset the beginning offset from where to write * @param len the length of the fragment to write * - * @throws BadLocationException if <code>offset</code> or - * <code>offset + len</code>is an invalid location inside - * <code>document</code> + * @throws BadLocationException if <code>offset</code> is an + * invalid location inside <code>document</code>. * @throws IOException if something goes wrong while writing to * <code>out</code> */ public void write(Writer out, Document document, int offset, int len) - throws BadLocationException, IOException + throws BadLocationException, IOException { - // TODO: Implement this properly. + // Throw a BLE if offset is invalid + if (offset < 0 || offset > document.getLength()) + throw new BadLocationException("Tried to write to invalid location", + offset); + + // If they gave an overly large len, just adjust it + if (offset + len > document.getLength()) + len = document.getLength() - offset; + + out.write(document.getText(offset, len)); } } diff --git a/libjava/classpath/javax/swing/text/DefaultFormatter.java b/libjava/classpath/javax/swing/text/DefaultFormatter.java index c97d90703c7..f9e0f10e654 100644 --- a/libjava/classpath/javax/swing/text/DefaultFormatter.java +++ b/libjava/classpath/javax/swing/text/DefaultFormatter.java @@ -57,8 +57,7 @@ import javax.swing.JFormattedTextField; * * @author Roman Kennke (roman@kennke.org) */ -public class DefaultFormatter - extends JFormattedTextField.AbstractFormatter +public class DefaultFormatter extends JFormattedTextField.AbstractFormatter implements Cloneable, Serializable { @@ -156,9 +155,6 @@ public class DefaultFormatter * Checks if the value in the input field is valid. If the * property allowsInvalid is set to <code>false</code>, then * the string in the input field is not allowed to be entered. - * - * @param doc the document of the input field - * @param value the current (old) value of the input field */ private void checkValidInput() { @@ -179,15 +175,18 @@ public class DefaultFormatter catch (ParseException pe) { // if that happens, something serious must be wrong - throw new AssertionError("values must be parseable"); + AssertionError ae; + ae = new AssertionError("values must be parseable"); + ae.initCause(pe); + throw ae; } } } } } - /** The serialVersoinUID. */ - private static final long serialVersionUID = -7369196326612908900L; + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = -355018354457785329L; /** * Indicates if the value should be committed after every diff --git a/libjava/classpath/javax/swing/text/DefaultHighlighter.java b/libjava/classpath/javax/swing/text/DefaultHighlighter.java index c8d874caa51..40ea4f80aab 100644 --- a/libjava/classpath/javax/swing/text/DefaultHighlighter.java +++ b/libjava/classpath/javax/swing/text/DefaultHighlighter.java @@ -168,6 +168,7 @@ public class DefaultHighlighter extends LayeredHighlighter public DefaultHighlighter() { + // Nothing to do here. } public boolean getDrawsLayeredHighlights() @@ -238,6 +239,7 @@ public class DefaultHighlighter extends LayeredHighlighter Shape viewBounds, JTextComponent editor, View view) { + // TODO: Implement this properly. } public void paint(Graphics g) diff --git a/libjava/classpath/javax/swing/text/DefaultStyledDocument.java b/libjava/classpath/javax/swing/text/DefaultStyledDocument.java index 3545e52c453..eb56bb0f8e5 100644 --- a/libjava/classpath/javax/swing/text/DefaultStyledDocument.java +++ b/libjava/classpath/javax/swing/text/DefaultStyledDocument.java @@ -1,5 +1,5 @@ /* DefaultStyledDocument.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -41,8 +41,14 @@ package javax.swing.text; import java.awt.Color; import java.awt.Font; import java.io.Serializable; +import java.util.Enumeration; +import java.util.Vector; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.UndoableEdit; /** * The default implementation of {@link StyledDocument}. @@ -60,12 +66,359 @@ public class DefaultStyledDocument extends AbstractDocument implements StyledDocument { /** + * An {@link UndoableEdit} that can undo attribute changes to an element. + * + * @author Roman Kennke (kennke@aicas.com) + */ + public static class AttributeUndoableEdit + extends AbstractUndoableEdit + { + /** + * A copy of the old attributes. + */ + protected AttributeSet copy; + + /** + * The new attributes. + */ + protected AttributeSet newAttributes; + + /** + * If the new attributes replaced the old attributes or if they only were + * added to them. + */ + protected boolean isReplacing; + + /** + * The element that has changed. + */ + protected Element element; + + /** + * Creates a new <code>AttributeUndoableEdit</code>. + * + * @param el the element that changes attributes + * @param newAtts the new attributes + * @param replacing if the new attributes replace the old or only append to + * them + */ + public AttributeUndoableEdit(Element el, AttributeSet newAtts, + boolean replacing) + { + element = el; + newAttributes = newAtts; + isReplacing = replacing; + copy = el.getAttributes().copyAttributes(); + } + + /** + * Undos the attribute change. The <code>copy</code> field is set as + * attributes on <code>element</code>. + */ + public void undo() + { + super.undo(); + AttributeSet atts = element.getAttributes(); + if (atts instanceof MutableAttributeSet) + { + MutableAttributeSet mutable = (MutableAttributeSet) atts; + mutable.removeAttributes(atts); + mutable.addAttributes(copy); + } + } + + /** + * Redos an attribute change. This adds <code>newAttributes</code> to the + * <code>element</code>'s attribute set, possibly clearing all attributes + * if <code>isReplacing</code> is true. + */ + public void redo() + { + super.undo(); + AttributeSet atts = element.getAttributes(); + if (atts instanceof MutableAttributeSet) + { + MutableAttributeSet mutable = (MutableAttributeSet) atts; + if (isReplacing) + mutable.removeAttributes(atts); + mutable.addAttributes(newAttributes); + } + } + } + + /** + * Carries specification information for new {@link Element}s that should + * be created in {@link ElementBuffer}. This allows the parsing process + * to be decoupled from the <code>Element</code> creation process. + */ + public static class ElementSpec + { + /** + * This indicates a start tag. This is a possible value for + * {@link #getType}. + */ + public static final short StartTagType = 1; + + /** + * This indicates an end tag. This is a possible value for + * {@link #getType}. + */ + public static final short EndTagType = 2; + + /** + * This indicates a content element. This is a possible value for + * {@link #getType}. + */ + public static final short ContentType = 3; + + /** + * This indicates that the data associated with this spec should be joined + * with what precedes it. This is a possible value for + * {@link #getDirection}. + */ + public static final short JoinPreviousDirection = 4; + + /** + * This indicates that the data associated with this spec should be joined + * with what follows it. This is a possible value for + * {@link #getDirection}. + */ + public static final short JoinNextDirection = 5; + + /** + * This indicates that the data associated with this spec should be used + * to create a new element. This is a possible value for + * {@link #getDirection}. + */ + public static final short OriginateDirection = 6; + + /** + * This indicates that the data associated with this spec should be joined + * to the fractured element. This is a possible value for + * {@link #getDirection}. + */ + public static final short JoinFractureDirection = 7; + + /** + * The type of the tag. + */ + short type; + + /** + * The direction of the tag. + */ + short direction; + + /** + * The offset of the content. + */ + int offset; + + /** + * The length of the content. + */ + int length; + + /** + * The actual content. + */ + char[] content; + + /** + * The attributes for the tag. + */ + AttributeSet attributes; + + /** + * Creates a new <code>ElementSpec</code> with no content, length or + * offset. This is most useful for start and end tags. + * + * @param a the attributes for the element to be created + * @param type the type of the tag + */ + public ElementSpec(AttributeSet a, short type) + { + this(a, type, 0); + } + + /** + * Creates a new <code>ElementSpec</code> that specifies the length but + * not the offset of an element. Such <code>ElementSpec</code>s are + * processed sequentially from a known starting point. + * + * @param a the attributes for the element to be created + * @param type the type of the tag + * @param len the length of the element + */ + public ElementSpec(AttributeSet a, short type, int len) + { + this(a, type, null, 0, len); + } + + /** + * Creates a new <code>ElementSpec</code> with document content. + * + * @param a the attributes for the element to be created + * @param type the type of the tag + * @param txt the actual content + * @param offs the offset into the <code>txt</code> array + * @param len the length of the element + */ + public ElementSpec(AttributeSet a, short type, char[] txt, int offs, + int len) + { + attributes = a; + this.type = type; + offset = offs; + length = len; + content = txt; + direction = OriginateDirection; + } + + /** + * Sets the type of the element. + * + * @param type the type of the element to be set + */ + public void setType(short type) + { + this.type = type; + } + + /** + * Returns the type of the element. + * + * @return the type of the element + */ + public short getType() + { + return type; + } + + /** + * Sets the direction of the element. + * + * @param dir the direction of the element to be set + */ + public void setDirection(short dir) + { + direction = dir; + } + + /** + * Returns the direction of the element. + * + * @return the direction of the element + */ + public short getDirection() + { + return direction; + } + + /** + * Returns the attributes of the element. + * + * @return the attributes of the element + */ + public AttributeSet getAttributes() + { + return attributes; + } + + /** + * Returns the actual content of the element. + * + * @return the actual content of the element + */ + public char[] getArray() + { + return content; + } + + /** + * Returns the offset of the content. + * + * @return the offset of the content + */ + public int getOffset() + { + return offset; + } + + /** + * Returns the length of the content. + * + * @return the length of the content + */ + public int getLength() + { + return length; + } + + /** + * Returns a String representation of this <code>ElementSpec</code> + * describing the type, direction and length of this + * <code>ElementSpec</code>. + * + * @return a String representation of this <code>ElementSpec</code> + */ + public String toString() + { + StringBuilder b = new StringBuilder(); + b.append('<'); + switch (type) + { + case StartTagType: + b.append("StartTag"); + break; + case EndTagType: + b.append("EndTag"); + break; + case ContentType: + b.append("Content"); + break; + default: + b.append("??"); + break; + } + + b.append(':'); + + switch (direction) + { + case JoinPreviousDirection: + b.append("JoinPrevious"); + break; + case JoinNextDirection: + b.append("JoinNext"); + break; + case OriginateDirection: + b.append("Originate"); + break; + case JoinFractureDirection: + b.append("Fracture"); + break; + default: + b.append("??"); + break; + } + + b.append(':'); + b.append(length); + + return b.toString(); + } + } + + /** * Performs all <em>structural</code> changes to the <code>Element</code> * hierarchy. */ - public class ElementBuffer - implements Serializable + public class ElementBuffer implements Serializable { + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 1688745877691146623L; + /** The root element of the hierarchy. */ private Element root; @@ -76,6 +429,19 @@ public class DefaultStyledDocument extends AbstractDocument private int length; /** + * Holds fractured elements during insertion of end and start tags. + * Inserting an end tag may lead to fracturing of the current paragraph + * element. The elements that have been cut off may be added to the + * next paragraph that is created in the next start tag. + */ + Element[] fracture; + + /** + * The ElementChange that describes the latest changes. + */ + DefaultDocumentEvent documentEvent; + + /** * Creates a new <code>ElementBuffer</code> for the specified * <code>root</code> element. * @@ -114,6 +480,7 @@ public class DefaultStyledDocument extends AbstractDocument { this.offset = offset; this.length = length; + documentEvent = ev; changeUpdate(); } @@ -142,38 +509,352 @@ public class DefaultStyledDocument extends AbstractDocument void split(Element el, int offset) { if (el instanceof AbstractElement) - { - AbstractElement ael = (AbstractElement) el; - int startOffset = ael.getStartOffset(); - int endOffset = ael.getEndOffset(); - int len = endOffset - startOffset; - if (startOffset != offset && endOffset != offset) - { - Element paragraph = ael.getParentElement(); - if (paragraph instanceof BranchElement) - { - BranchElement par = (BranchElement) paragraph; - Element child1 = createLeafElement(par, ael, startOffset, - offset); - Element child2 = createLeafElement(par, ael, offset, - endOffset); - int index = par.getElementIndex(startOffset); - par.replace(index, 1, new Element[]{ child1, child2 }); - } + { + AbstractElement ael = (AbstractElement) el; + int startOffset = ael.getStartOffset(); + int endOffset = ael.getEndOffset(); + int len = endOffset - startOffset; + if (startOffset != offset && endOffset != offset) + { + Element paragraph = ael.getParentElement(); + if (paragraph instanceof BranchElement) + { + BranchElement par = (BranchElement) paragraph; + Element child1 = createLeafElement(par, ael, startOffset, + offset); + Element child2 = createLeafElement(par, ael, offset, + endOffset); + int index = par.getElementIndex(startOffset); + Element[] add = new Element[]{ child1, child2 }; + par.replace(index, 1, add); + documentEvent.addEdit(new ElementEdit(par, index, + new Element[]{ el }, + add)); + } else throw new AssertionError("paragraph elements are expected to " + "be instances of " + "javax.swing.text.AbstractDocument.BranchElement"); - } - } + } + } else - throw new AssertionError("content elements are expected to be " - + "instances of " + throw new AssertionError("content elements are expected to be " + + "instances of " + "javax.swing.text.AbstractDocument.AbstractElement"); } + + /** + * Inserts new <code>Element</code> in the document at the specified + * position. + * + * Most of the work is done by {@link #insertUpdate}, after some fields + * have been prepared for it. + * + * @param offset the location in the document at which the content is + * inserted + * @param length the length of the inserted content + * @param data the element specifications for the content to be inserted + * @param ev the document event that is updated to reflect the structural + * changes + */ + public void insert(int offset, int length, ElementSpec[] data, + DefaultDocumentEvent ev) + { + this.offset = offset; + this.length = length; + documentEvent = ev; + insertUpdate(data); + } + + /** + * Performs the actual structural change for {@link #insert}. This + * creates a bunch of {@link Element}s as specified by <code>data</code> + * and inserts it into the document as specified in the arguments to + * {@link #insert}. + * + * @param data the element specifications for the elements to be inserte + */ + protected void insertUpdate(ElementSpec[] data) + { + for (int i = 0; i < data.length; i++) + { + switch (data[i].getType()) + { + case ElementSpec.StartTagType: + insertStartTag(data[i]); + break; + case ElementSpec.EndTagType: + insertEndTag(data[i]); + break; + default: + insertContentTag(data[i]); + break; + } + } + } + + /** + * Insert a new paragraph after the paragraph at the current position. + * + * @param tag the element spec that describes the element to be inserted + */ + void insertStartTag(ElementSpec tag) + { + BranchElement root = (BranchElement) getDefaultRootElement(); + int index = root.getElementIndex(offset); + if (index == -1) + index = 0; + + BranchElement newParagraph = + (BranchElement) createBranchElement(root, tag.getAttributes()); + newParagraph.setResolveParent(getStyle(StyleContext.DEFAULT_STYLE)); + + // Add new paragraph into document structure. + Element[] added = new Element[]{newParagraph}; + root.replace(index + 1, 0, added); + ElementEdit edit = new ElementEdit(root, index + 1, new Element[0], + added); + documentEvent.addEdit(edit); + + // Maybe add fractured elements. + if (tag.getDirection() == ElementSpec.JoinFractureDirection) + { + Element[] newFracture = new Element[fracture.length]; + for (int i = 0; i < fracture.length; i++) + { + Element oldLeaf = fracture[i]; + Element newLeaf = createLeafElement(newParagraph, + oldLeaf.getAttributes(), + oldLeaf.getStartOffset(), + oldLeaf.getEndOffset()); + newFracture[i] = newLeaf; + } + newParagraph.replace(0, 0, newFracture); + edit = new ElementEdit(newParagraph, 0, new Element[0], + fracture); + documentEvent.addEdit(edit); + fracture = new Element[0]; + } + } + + /** + * Inserts an end tag into the document structure. This cuts of the + * current paragraph element, possibly fracturing it's child elements. + * The fractured elements are saved so that they can be joined later + * with a new paragraph element. + */ + void insertEndTag(ElementSpec tag) + { + BranchElement root = (BranchElement) getDefaultRootElement(); + int parIndex = root.getElementIndex(offset); + BranchElement paragraph = (BranchElement) root.getElement(parIndex); + + int index = paragraph.getElementIndex(offset); + LeafElement content = (LeafElement) paragraph.getElement(index); + // We might have to split the element at offset. + split(content, offset); + index = paragraph.getElementIndex(offset); + + int count = paragraph.getElementCount(); + // Store fractured elements. + fracture = new Element[count - index]; + for (int i = index; i < count; ++i) + fracture[i - index] = paragraph.getElement(i); + + // Delete fractured elements. + paragraph.replace(index, count - index, new Element[0]); + + // Add this action to the document event. + ElementEdit edit = new ElementEdit(paragraph, index, fracture, + new Element[0]); + documentEvent.addEdit(edit); + } + + /** + * Inserts a content element into the document structure. + * + * @param tag the element spec + */ + void insertContentTag(ElementSpec tag) + { + int len = tag.getLength(); + int dir = tag.getDirection(); + if (dir == ElementSpec.JoinPreviousDirection) + { + Element prev = getCharacterElement(offset); + BranchElement prevParent = (BranchElement) prev.getParentElement(); + Element join = createLeafElement(prevParent, tag.getAttributes(), + prev.getStartOffset(), + Math.max(prev.getEndOffset(), + offset + len)); + int ind = prevParent.getElementIndex(offset); + if (ind == -1) + ind = 0; + Element[] add = new Element[]{join}; + prevParent.replace(ind, 1, add); + + // Add this action to the document event. + ElementEdit edit = new ElementEdit(prevParent, ind, + new Element[]{prev}, add); + documentEvent.addEdit(edit); + } + else if (dir == ElementSpec.JoinNextDirection) + { + Element next = getCharacterElement(offset + len); + BranchElement nextParent = (BranchElement) next.getParentElement(); + Element join = createLeafElement(nextParent, tag.getAttributes(), + offset, + next.getEndOffset()); + int ind = nextParent.getElementIndex(offset + len); + if (ind == -1) + ind = 0; + Element[] add = new Element[]{join}; + nextParent.replace(ind, 1, add); + + // Add this action to the document event. + ElementEdit edit = new ElementEdit(nextParent, ind, + new Element[]{next}, add); + documentEvent.addEdit(edit); + } + else + { + BranchElement par = (BranchElement) getParagraphElement(offset); + + int ind = par.getElementIndex(offset); + + // Make room for the element. + // Cut previous element. + Element prev = par.getElement(ind); + if (prev != null && prev.getStartOffset() < offset) + { + Element cutPrev = createLeafElement(par, prev.getAttributes(), + prev.getStartOffset(), + offset); + Element[] remove = new Element[]{prev}; + Element[] add = new Element[]{cutPrev}; + if (prev.getEndOffset() > offset + len) + { + Element rem = createLeafElement(par, prev.getAttributes(), + offset + len, + prev.getEndOffset()); + add = new Element[]{cutPrev, rem}; + } + + par.replace(ind, 1, add); + documentEvent.addEdit(new ElementEdit(par, ind, remove, add)); + ind++; + } + // ind now points to the next element. + + // Cut next element if necessary. + Element next = par.getElement(ind); + if (next != null && next.getStartOffset() < offset + len) + { + Element cutNext = createLeafElement(par, next.getAttributes(), + offset + len, + next.getEndOffset()); + Element[] remove = new Element[]{next}; + Element[] add = new Element[]{cutNext}; + par.replace(ind, 1, add); + documentEvent.addEdit(new ElementEdit(par, ind, remove, + add)); + } + + // Insert new element. + Element newEl = createLeafElement(par, tag.getAttributes(), + offset, offset + len); + Element[] added = new Element[]{newEl}; + par.replace(ind, 0, added); + // Add this action to the document event. + ElementEdit edit = new ElementEdit(par, ind, new Element[0], + added); + documentEvent.addEdit(edit); + } + offset += len; + } + + /** + * Creates a copy of the element <code>clonee</code> that has the parent + * <code>parent</code>. + * @param parent the parent of the newly created Element + * @param clonee the Element to clone + * @return the cloned Element + */ + public Element clone (Element parent, Element clonee) + { + // If the Element we want to clone is a leaf, then simply copy it + if (clonee.isLeaf()) + return createLeafElement(parent, clonee.getAttributes(), + clonee.getStartOffset(), clonee.getEndOffset()); + + // Otherwise create a new BranchElement with the desired parent and + // the clonee's attributes + BranchElement result = (BranchElement) createBranchElement(parent, clonee.getAttributes()); + + // And clone all the of clonee's children + Element[] children = new Element[clonee.getElementCount()]; + for (int i = 0; i < children.length; i++) + children[i] = clone(result, clonee.getElement(i)); + + // Make the cloned children the children of the BranchElement + result.replace(0, 0, children); + return result; + } } /** + * An element type for sections. This is a simple BranchElement with + * a unique name. + */ + protected class SectionElement extends BranchElement + { + /** + * Creates a new SectionElement. + */ + public SectionElement() + { + super(null, null); + } + + /** + * Returns the name of the element. This method always returns + * "section". + * + * @return the name of the element + */ + public String getName() + { + return "section"; + } + } + + /** + * Receives notification when any of the document's style changes and calls + * {@link DefaultStyledDocument#styleChanged(Style)}. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class StyleChangeListener + implements ChangeListener + { + + /** + * Receives notification when any of the document's style changes and calls + * {@link DefaultStyledDocument#styleChanged(Style)}. + * + * @param event the change event + */ + public void stateChanged(ChangeEvent event) + { + Style style = (Style) event.getSource(); + styleChanged(style); + } + } + + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 940485415728614849L; + + /** * The default size to use for new content buffers. */ public static final int BUFFER_SIZE_DEFAULT = 4096; @@ -185,6 +866,11 @@ public class DefaultStyledDocument extends AbstractDocument protected DefaultStyledDocument.ElementBuffer buffer; /** + * Listens for changes on this document's styles and notifies styleChanged(). + */ + private StyleChangeListener styleChangeListener; + + /** * Creates a new <code>DefaultStyledDocument</code>. */ public DefaultStyledDocument() @@ -237,7 +923,14 @@ public class DefaultStyledDocument extends AbstractDocument public Style addStyle(String nm, Style parent) { StyleContext context = (StyleContext) getAttributeContext(); - return context.addStyle(nm, parent); + Style newStyle = context.addStyle(nm, parent); + + // Register change listener. + if (styleChangeListener == null) + styleChangeListener = new StyleChangeListener(); + newStyle.addChangeListener(styleChangeListener); + + return newStyle; } /** @@ -250,9 +943,11 @@ public class DefaultStyledDocument extends AbstractDocument Element[] tmp; // FIXME: Create a SecionElement here instead of a BranchElement. // Use createBranchElement() and createLeafElement instead. - BranchElement section = new BranchElement(null, null); - - BranchElement paragraph = new BranchElement(section, null); + SectionElement section = new SectionElement(); + + BranchElement paragraph = + (BranchElement) createBranchElement(section, null); + paragraph.setResolveParent(getStyle(StyleContext.DEFAULT_STYLE)); tmp = new Element[1]; tmp[0] = paragraph; section.replace(0, 0, tmp); @@ -261,7 +956,7 @@ public class DefaultStyledDocument extends AbstractDocument tmp = new Element[1]; tmp[0] = leaf; paragraph.replace(0, 0, tmp); - + return section; } @@ -279,10 +974,10 @@ public class DefaultStyledDocument extends AbstractDocument { Element element = getDefaultRootElement(); - while (! element.isLeaf()) + while (!element.isLeaf()) { - int index = element.getElementIndex(position); - element = element.getElement(index); + int index = element.getElementIndex(position); + element = element.getElement(index); } return element; @@ -353,6 +1048,10 @@ public class DefaultStyledDocument extends AbstractDocument /** * Returns the paragraph element for the specified position. + * If the position is outside the bounds of the document's root element, + * then the closest element is returned. That is the last paragraph if + * <code>position >= endIndex</code> or the first paragraph if + * <code>position < startIndex</code>. * * @param position the position for which to query the paragraph element * @@ -360,8 +1059,18 @@ public class DefaultStyledDocument extends AbstractDocument */ public Element getParagraphElement(int position) { - Element element = getCharacterElement(position); - return element.getParentElement(); + BranchElement root = (BranchElement) getDefaultRootElement(); + int start = root.getStartOffset(); + int end = root.getEndOffset(); + if (position >= end) + position = end - 1; + else if (position < start) + position = start; + + Element par = root.positionToElement(position); + + assert par != null : "The paragraph element must not be null"; + return par; } /** @@ -416,35 +1125,37 @@ public class DefaultStyledDocument extends AbstractDocument int paragraphCount = root.getElementCount(); for (int pindex = 0; pindex < paragraphCount; pindex++) { - Element paragraph = root.getElement(pindex); - // Skip paragraphs that lie outside the interval. - if ((paragraph.getStartOffset() > offset + length) - || (paragraph.getEndOffset() < offset)) - continue; - - // Visit content elements within this paragraph - int contentCount = paragraph.getElementCount(); - for (int cindex = 0; cindex < contentCount; cindex++) - { - Element content = paragraph.getElement(cindex); - // Skip content that lies outside the interval. - if ((content.getStartOffset() > offset + length) - || (content.getEndOffset() < offset)) - continue; - - if (content instanceof AbstractElement) - { - AbstractElement el = (AbstractElement) content; - if (replace) - el.removeAttributes(el); - el.addAttributes(attributes); - } - else - throw new AssertionError("content elements are expected to be" - + "instances of " + Element paragraph = root.getElement(pindex); + // Skip paragraphs that lie outside the interval. + if ((paragraph.getStartOffset() > offset + length) + || (paragraph.getEndOffset() < offset)) + continue; + + // Visit content elements within this paragraph + int contentCount = paragraph.getElementCount(); + for (int cindex = 0; cindex < contentCount; cindex++) + { + Element content = paragraph.getElement(cindex); + // Skip content that lies outside the interval. + if ((content.getStartOffset() > offset + length) + || (content.getEndOffset() < offset)) + continue; + + if (content instanceof AbstractElement) + { + AbstractElement el = (AbstractElement) content; + if (replace) + el.removeAttributes(el); + el.addAttributes(attributes); + } + else + throw new AssertionError("content elements are expected to be" + + "instances of " + "javax.swing.text.AbstractDocument.AbstractElement"); - } + } } + + fireChangedUpdate(ev); } /** @@ -476,10 +1187,199 @@ public class DefaultStyledDocument extends AbstractDocument * selection are overridden, otherwise they are merged */ public void setParagraphAttributes(int offset, int length, - AttributeSet attributes, - boolean replace) + AttributeSet attributes, + boolean replace) + { + int index = offset; + while (index < offset + length) + { + AbstractElement par = (AbstractElement) getParagraphElement(index); + AttributeContext ctx = getAttributeContext(); + if (replace) + par.removeAttributes(par); + par.addAttributes(attributes); + index = par.getElementCount(); + } + } + + /** + * Called in response to content insert actions. This is used to + * update the element structure. + * + * @param ev the <code>DocumentEvent</code> describing the change + * @param attr the attributes for the change + */ + protected void insertUpdate(DefaultDocumentEvent ev, AttributeSet attr) + { + super.insertUpdate(ev, attr); + int offset = ev.getOffset(); + int length = ev.getLength(); + int endOffset = offset + length; + Segment txt = new Segment(); + try + { + getText(offset, length, txt); + } + catch (BadLocationException ex) + { + AssertionError ae = new AssertionError("Unexpected bad location"); + ae.initCause(ex); + throw ae; + } + + int len = 0; + Vector specs = new Vector(); + + Element prev = getCharacterElement(offset); + Element next = getCharacterElement(endOffset); + + for (int i = offset; i < endOffset; ++i) + { + len++; + if (txt.array[i] == '\n') + { + ElementSpec spec = new ElementSpec(attr, ElementSpec.ContentType, + len); + + // If we are at the last index, then check if we could probably be + // joined with the next element. + if (i == endOffset - 1) + { + if (next.getAttributes().isEqual(attr)) + spec.setDirection(ElementSpec.JoinNextDirection); + } + // If we are at the first new element, then check if it could be + // joined with the previous element. + else if (specs.size() == 0) + { + if (prev.getAttributes().isEqual(attr)) + spec.setDirection(ElementSpec.JoinPreviousDirection); + } + + specs.add(spec); + + // Add ElementSpecs for the newline. + ElementSpec endTag = new ElementSpec(null, ElementSpec.EndTagType); + specs.add(endTag); + ElementSpec startTag = new ElementSpec(null, + ElementSpec.StartTagType); + startTag.setDirection(ElementSpec.JoinFractureDirection); + specs.add(startTag); + + len = 0; + offset += len; + } + } + + // Create last element if last character hasn't been a newline. + if (len > 0) + { + ElementSpec spec = new ElementSpec(attr, ElementSpec.ContentType, len); + // If we are at the first new element, then check if it could be + // joined with the previous element. + if (specs.size() == 0) + { + if (prev.getAttributes().isEqual(attr)) + spec.setDirection(ElementSpec.JoinPreviousDirection); + } + // Check if we could probably be joined with the next element. + else if (next.getAttributes().isEqual(attr)) + spec.setDirection(ElementSpec.JoinNextDirection); + + specs.add(spec); + } + + ElementSpec[] elSpecs = + (ElementSpec[]) specs.toArray(new ElementSpec[specs.size()]); + + buffer.insert(offset, length, elSpecs, ev); + } + + /** + * Returns an enumeration of all style names. + * + * @return an enumeration of all style names + */ + public Enumeration getStyleNames() + { + StyleContext context = (StyleContext) getAttributeContext(); + return context.getStyleNames(); + } + + /** + * Called when any of this document's styles changes. + * + * @param style the style that changed + */ + protected void styleChanged(Style style) { - // FIXME: Implement me. - throw new Error("not implemented"); + // Nothing to do here. This is intended to be overridden by subclasses. + } + + /** + * Inserts a bulk of structured content at once. + * + * @param offset the offset at which the content should be inserted + * @param data the actual content spec to be inserted + */ + protected void insert(int offset, ElementSpec[] data) + throws BadLocationException + { + writeLock(); + // First we insert the content. + int index = offset; + for (int i = 0; i < data.length; i++) + { + ElementSpec spec = data[i]; + if (spec.getArray() != null && spec.getLength() > 0) + { + String insertString = new String(spec.getArray(), spec.getOffset(), + spec.getLength()); + content.insertString(index, insertString); + } + index += spec.getLength(); + } + // Update the view structure. + DefaultDocumentEvent ev = new DefaultDocumentEvent(offset, index - offset, + DocumentEvent.EventType.INSERT); + for (int i = 0; i < data.length; i++) + { + ElementSpec spec = data[i]; + AttributeSet atts = spec.getAttributes(); + if (atts != null) + insertUpdate(ev, atts); + } + + // Finally we must update the document structure and fire the insert update + // event. + buffer.insert(offset, index - offset, data, ev); + fireInsertUpdate(ev); + writeUnlock(); + } + + /** + * Initializes the <code>DefaultStyledDocument</code> with the specified + * data. + * + * @param data the specification of the content with which the document is + * initialized + */ + protected void create(ElementSpec[] data) + { + try + { + // Clear content. + content.remove(0, content.length()); + // Clear buffer and root element. + buffer = new ElementBuffer(createDefaultRoot()); + // Insert the data. + insert(0, data); + } + catch (BadLocationException ex) + { + AssertionError err = new AssertionError("Unexpected bad location"); + err.initCause(ex); + throw err; + } } } diff --git a/libjava/classpath/javax/swing/text/DefaultTextUI.java b/libjava/classpath/javax/swing/text/DefaultTextUI.java new file mode 100644 index 00000000000..e7ff01845e6 --- /dev/null +++ b/libjava/classpath/javax/swing/text/DefaultTextUI.java @@ -0,0 +1,61 @@ +/* DefaultTextUI.java -- Deprecated base UI for text components + 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.text; + +import javax.swing.plaf.basic.BasicTextUI; + +/** + * This class is deprecated and should not be used anymore. The base UI for + * all text components is now {@link BasicTextUI}. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public abstract class DefaultTextUI extends BasicTextUI +{ + /** + * This class is deprecated and should not be used anymore. The base UI for + * all text components is now {@link BasicTextUI}. + * + * @deprecated use {@link BasicTextUI} instead + */ + public DefaultTextUI() + { + // Nothing to do here. + } +} diff --git a/libjava/classpath/javax/swing/text/EditorKit.java b/libjava/classpath/javax/swing/text/EditorKit.java index bd51a866f68..8719aee595f 100644 --- a/libjava/classpath/javax/swing/text/EditorKit.java +++ b/libjava/classpath/javax/swing/text/EditorKit.java @@ -48,24 +48,24 @@ import java.io.Writer; import javax.swing.Action; import javax.swing.JEditorPane; -public abstract class EditorKit - implements Cloneable, Serializable +public abstract class EditorKit implements Cloneable, Serializable { private static final long serialVersionUID = -5044124649345887822L; public EditorKit() { + // Nothing to do here. } public Object clone() { try { - return super.clone(); + return super.clone(); } catch (CloneNotSupportedException e) { - return null; + return null; } } @@ -74,10 +74,12 @@ public abstract class EditorKit */ public void deinstall(JEditorPane c) { + // This default implementation does nothing. } public void install(JEditorPane c) { + // This default implementation does nothing. } public abstract Caret createCaret(); diff --git a/libjava/classpath/javax/swing/text/FieldView.java b/libjava/classpath/javax/swing/text/FieldView.java index e2e04d7c495..2496418444e 100644 --- a/libjava/classpath/javax/swing/text/FieldView.java +++ b/libjava/classpath/javax/swing/text/FieldView.java @@ -118,22 +118,24 @@ public class FieldView extends PlainView FontMetrics fm = getFontMetrics(); if (axis == Y_AXIS) - return fm.getHeight(); + return super.getPreferredSpan(axis); String text; Element elem = getElement(); try { - text = elem.getDocument().getText(elem.getStartOffset(), - elem.getEndOffset()); + text = elem.getDocument().getText(elem.getStartOffset(), + elem.getEndOffset()); } catch (BadLocationException e) { - // This should never happen. - text = ""; + // Should never happen + AssertionError ae = new AssertionError(); + ae.initCause(e); + throw ae; } - + return fm.stringWidth(text); } @@ -159,18 +161,21 @@ public class FieldView extends PlainView { Shape newAlloc = adjustAllocation(shape); super.insertUpdate(ev, newAlloc, vf); + getContainer().repaint(); } public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) { Shape newAlloc = adjustAllocation(shape); super.removeUpdate(ev, newAlloc, vf); + getContainer().repaint(); } public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) { Shape newAlloc = adjustAllocation(shape); super.removeUpdate(ev, newAlloc, vf); + getContainer().repaint(); } public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) diff --git a/libjava/classpath/javax/swing/text/FlowView.java b/libjava/classpath/javax/swing/text/FlowView.java index a6ef89efb78..fd6785b6f18 100644 --- a/libjava/classpath/javax/swing/text/FlowView.java +++ b/libjava/classpath/javax/swing/text/FlowView.java @@ -42,8 +42,10 @@ import java.awt.Container; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; +import java.util.Iterator; import java.util.Vector; +import javax.swing.SwingConstants; import javax.swing.event.DocumentEvent; /** @@ -71,6 +73,7 @@ public abstract class FlowView extends BoxView */ public FlowStrategy() { + // Nothing to do here. } /** @@ -137,7 +140,7 @@ public abstract class FlowView extends BoxView * Performs the layout for the whole view. By default this rebuilds * all the physical views from the logical views of the managed FlowView. * - * This is called by {@link FlowLayout#layout} to update the layout of + * This is called by {@link FlowView#layout} to update the layout of * the view. * * @param fv the flow view for which we perform the layout @@ -183,11 +186,17 @@ public abstract class FlowView extends BoxView { View child = createView(fv, offset, spanLeft, rowIndex); if (child == null) - break; + { + offset = -1; + break; + } int span = (int) child.getPreferredSpan(flowAxis); if (span > spanLeft) - break; + { + offset = -1; + break; + } row.append(child); spanLeft -= span; @@ -204,7 +213,7 @@ public abstract class FlowView extends BoxView * not fit in the available span and also cannot be broken down). * * @param fv the flow view - * @param startOffset the start offset for the view to be created + * @param offset the start offset for the view to be created * @param spanLeft the available span * @param rowIndex the index of the row * @@ -218,13 +227,15 @@ public abstract class FlowView extends BoxView View logicalView = getLogicalView(fv); int viewIndex = logicalView.getViewIndex(offset, Position.Bias.Forward); + if (viewIndex == -1) + return null; + View child = logicalView.getView(viewIndex); int flowAxis = fv.getFlowAxis(); int span = (int) child.getPreferredSpan(flowAxis); if (span <= spanLeft) return child; - else if (child.getBreakWeight(flowAxis, offset, spanLeft) > BadBreakWeight) // FIXME: What to do with the pos parameter here? @@ -326,7 +337,19 @@ public abstract class FlowView extends BoxView */ public int getViewIndex(int pos, Position.Bias b) { - return getElement().getElementIndex(pos); + int index = -1; + int i = 0; + for (Iterator it = children.iterator(); it.hasNext(); i++) + { + View child = (View) it.next(); + if (child.getStartOffset() >= pos + && child.getEndOffset() < pos) + { + index = i; + break; + } + } + return index; } /** @@ -373,6 +396,37 @@ public abstract class FlowView extends BoxView throw new AssertionError("This method must not be called in " + "LogicalView."); } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset in + * the document model + */ + public int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException + { + assert false : "getNextVisualPositionFrom() must not be called in " + + "LogicalView"; + return 0; + } } /** @@ -478,7 +532,7 @@ public abstract class FlowView extends BoxView * The real children are created at layout time and each represent one * row. * - * This method is called by {@link #setParent} in order to initialize + * This method is called by {@link View#setParent} in order to initialize * the view. * * @param vf the view factory to use for creating the child views @@ -502,7 +556,7 @@ public abstract class FlowView extends BoxView /** * Performs the layout of this view. If the span along the flow axis changed, - * this first calls {@link FlowStrategy.layout} in order to rebuild the + * this first calls {@link FlowStrategy#layout} in order to rebuild the * rows of this view. Then the superclass's behaviour is called to arrange * the rows within the box. * diff --git a/libjava/classpath/javax/swing/text/GapContent.java b/libjava/classpath/javax/swing/text/GapContent.java index 1dd46c4b0f4..4c65de0f5f4 100644 --- a/libjava/classpath/javax/swing/text/GapContent.java +++ b/libjava/classpath/javax/swing/text/GapContent.java @@ -39,10 +39,15 @@ exception statement from your version. */ package javax.swing.text; import java.io.Serializable; +import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; +import java.util.Iterator; import java.util.ListIterator; +import java.util.Vector; +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; import javax.swing.undo.UndoableEdit; /** @@ -59,7 +64,6 @@ import javax.swing.undo.UndoableEdit; public class GapContent implements AbstractDocument.Content, Serializable { - /** * A {@link Position} implementation for <code>GapContent</code>. */ @@ -114,6 +118,11 @@ public class GapContent */ public int getOffset() { + // Check precondition. + assert mark <= gapStart || mark >= gapEnd : "mark: " + mark + + ", gapStart: " + gapStart + + ", gapEnd: " + gapEnd; + if (mark <= gapStart) return mark; else @@ -121,13 +130,91 @@ public class GapContent } } - private static final long serialVersionUID = 8374645204155842629L; + class UndoInsertString extends AbstractUndoableEdit + { + public int where, length; + String text; + public UndoInsertString(int start, int len) + { + where = start; + length = len; + } + + public void undo () throws CannotUndoException + { + super.undo(); + try + { + text = getString(where, length); + remove(where, length); + } + catch (BadLocationException ble) + { + throw new CannotUndoException(); + } + } + + public void redo () throws CannotUndoException + { + super.redo(); + try + { + insertString(where, text); + } + catch (BadLocationException ble) + { + throw new CannotRedoException(); + } + } + + } + + class UndoRemove extends AbstractUndoableEdit + { + public int where; + String text; + public UndoRemove(int start, String removedText) + { + where = start; + text = removedText; + } + + public void undo () throws CannotUndoException + { + super.undo(); + try + { + insertString(where, text); + } + catch (BadLocationException ble) + { + throw new CannotUndoException(); + } + } + + public void redo () throws CannotUndoException + { + super.redo(); + try + { + remove(where, text.length()); + } + catch (BadLocationException ble) + { + throw new CannotRedoException(); + } + } + + } + + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = -6226052713477823730L; /** * This is the default buffer size and the amount of bytes that a buffer is * extended if it is full. */ - static final int DEFAULT_BUFSIZE = 64; + static final int DEFAULT_BUFSIZE = 10; /** * The text buffer. @@ -148,7 +235,7 @@ public class GapContent * The positions generated by this GapContent. They are kept in an ordered * fashion, so they can be looked up easily. */ - LinkedList positions; + ArrayList positions; /** * Creates a new GapContent object. @@ -166,10 +253,10 @@ public class GapContent public GapContent(int size) { buffer = (char[]) allocateArray(size); - gapStart = 0; - gapEnd = size - 1; - buffer[size - 1] = '\n'; - positions = new LinkedList(); + gapStart = 1; + gapEnd = size; + buffer[0] = '\n'; + positions = new ArrayList(); } /** @@ -211,8 +298,7 @@ public class GapContent * @param where the position where the string is inserted * @param str the string that is to be inserted * - * @return an UndoableEdit object (currently not supported, so - * <code>null</code> is returned) + * @return an UndoableEdit object * * @throws BadLocationException if <code>where</code> is not a valid * location in the buffer @@ -228,9 +314,9 @@ public class GapContent throw new BadLocationException("the where argument cannot be greater" + " than the content length", where); - replace(where, 0, str.toCharArray(), str.length()); + replace(where, 0, str.toCharArray(), strLen); - return null; + return new UndoInsertString(where, strLen); } /** @@ -239,8 +325,7 @@ public class GapContent * @param where the position where the content is to be removed * @param nitems number of characters to be removed * - * @return an UndoableEdit object (currently not supported, so - * <code>null</code> is returned) + * @return an UndoableEdit object * * @throws BadLocationException if <code>where</code> is not a valid * location in the buffer @@ -257,9 +342,10 @@ public class GapContent throw new BadLocationException("where + nitems cannot be greater" + " than the content length", where + nitems); + String removedText = getString(where, nitems); replace(where, nitems, null, 0); - return null; + return new UndoRemove(where, removedText); } /** @@ -372,7 +458,6 @@ public class GapContent if (index < 0) index = -(index + 1); positions.add(index, pos); - return pos; } @@ -386,7 +471,14 @@ public class GapContent */ protected void shiftEnd(int newSize) { - int delta = (gapEnd - gapStart) - newSize; + assert newSize > (gapEnd - gapStart) : "The new gap size must be greater " + + "than the old gap size"; + + int delta = newSize - gapEnd + gapStart; + // Update the marks after the gapEnd. + adjustPositionsInRange(gapEnd, buffer.length - gapEnd, delta); + + // Copy the data around. char[] newBuf = (char[]) allocateArray(length() + newSize); System.arraycopy(buffer, 0, newBuf, 0, gapStart); System.arraycopy(buffer, gapEnd, newBuf, gapStart + newSize, buffer.length @@ -394,18 +486,6 @@ public class GapContent gapEnd = gapStart + newSize; buffer = newBuf; - // Update the marks after the gapEnd. - int index = Collections.binarySearch(positions, new GapContentPosition( - gapEnd)); - if (index < 0) - { - index = -(index + 1); - } - for (ListIterator i = positions.listIterator(index); i.hasNext();) - { - GapContentPosition p = (GapContentPosition) i.next(); - p.mark += delta; - } } /** @@ -415,32 +495,15 @@ public class GapContent */ protected void shiftGap(int newGapStart) { - int newGapEnd = newGapStart + (gapEnd - gapStart); - - // Update the positions between newGapEnd and (old) gapEnd. The marks - // must be shifted by (gapEnd - newGapEnd). - int index1 = Collections.binarySearch(positions, - new GapContentPosition(gapEnd)); - int index2 = Collections.binarySearch(positions, - new GapContentPosition(newGapEnd)); - if (index1 > 0 && index2 > 0) - { - int i1 = Math.min(index1, index2); - int i2 = Math.max(index1, index2); - for (ListIterator i = positions.listIterator(i1); i.hasNext();) - { - if (i.nextIndex() > i2) - break; - - GapContentPosition p = (GapContentPosition) i.next(); - p.mark += gapEnd - newGapEnd; - } - } - if (newGapStart == gapStart) return; - else if (newGapStart < gapStart) + + int newGapEnd = newGapStart + gapEnd - gapStart; + if (newGapStart < gapStart) { + // Update the positions between newGapStart and (old) gapStart. The marks + // must be shifted by (gapEnd - gapStart). + adjustPositionsInRange(newGapStart, gapStart - newGapStart, gapEnd - gapStart); System.arraycopy(buffer, newGapStart, buffer, newGapEnd, gapStart - newGapStart); gapStart = newGapStart; @@ -448,11 +511,54 @@ public class GapContent } else { + // Update the positions between newGapEnd and (old) gapEnd. The marks + // must be shifted by (gapEnd - gapStart). + adjustPositionsInRange(gapEnd, newGapEnd - gapEnd, -(gapEnd - gapStart)); System.arraycopy(buffer, gapEnd, buffer, gapStart, newGapStart - gapStart); gapStart = newGapStart; gapEnd = newGapEnd; } + if (gapStart == 0) + resetMarksAtZero(); + } + + /** + * Shifts the gap start downwards. This does not affect the content of the + * buffer. This only updates the gap start and all the marks that are between + * the old gap start and the new gap start. They all are squeezed to the start + * of the gap, because their location has been removed. + * + * @param newGapStart the new gap start + */ + protected void shiftGapStartDown(int newGapStart) + { + if (newGapStart == gapStart) + return; + + assert newGapStart < gapStart : "The new gap start must be less than the " + + "old gap start."; + setPositionsInRange(newGapStart, gapStart - newGapStart, gapStart); + gapStart = newGapStart; + } + + /** + * Shifts the gap end upwards. This does not affect the content of the + * buffer. This only updates the gap end and all the marks that are between + * the old gap end and the new end start. They all are squeezed to the end + * of the gap, because their location has been removed. + * + * @param newGapEnd the new gap start + */ + protected void shiftGapEndUp(int newGapEnd) + { + if (newGapEnd == gapEnd) + return; + + assert newGapEnd > gapEnd : "The new gap end must be greater than the " + + "old gap end."; + setPositionsInRange(gapEnd, newGapEnd - gapEnd, newGapEnd + 1); + gapEnd = newGapEnd; } /** @@ -476,13 +582,15 @@ public class GapContent protected void replace(int position, int rmSize, Object addItems, int addSize) { + if (gapStart != position) + shiftGap(position); // Remove content - shiftGap(position); - gapEnd += rmSize; + if (rmSize > 0) + shiftGapEndUp(gapEnd + rmSize); // If gap is too small, enlarge the gap. - if ((gapEnd - gapStart) < addSize) - shiftEnd(addSize); + if ((gapEnd - gapStart) <= addSize) + shiftEnd((addSize - gapEnd + gapStart + 1) * 2 + gapEnd + DEFAULT_BUFSIZE); // Add new items to the buffer. if (addItems != null) @@ -491,4 +599,164 @@ public class GapContent gapStart += addSize; } } + + /** + * Returns the start index of the gap within the buffer array. + * + * @return the start index of the gap within the buffer array + */ + protected final int getGapStart() + { + return gapStart; + } + + /** + * Returns the end index of the gap within the buffer array. + * + * @return the end index of the gap within the buffer array + */ + protected final int getGapEnd() + { + return gapEnd; + } + + /** + * Returns all <code>Position</code>s that are in the range specified by + * <code>offset</code> and </code>length</code> within the buffer array. + * + * @param v the vector to use; if <code>null</code>, a new Vector is allocated + * @param offset the start offset of the range to search + * @param length the length of the range to search + * + * @return the positions within the specified range + */ + protected Vector getPositionsInRange(Vector v, int offset, int length) + { + Vector res = v; + if (res == null) + res = new Vector(); + else + res.clear(); + + int endOffset = offset + length; + + int index1 = Collections.binarySearch(positions, + new GapContentPosition(offset)); + if (index1 < 0) + index1 = -(index1 + 1); + for (ListIterator i = positions.listIterator(index1); i.hasNext();) + { + GapContentPosition p = (GapContentPosition) i.next(); + if (p.mark > endOffset) + break; + if (p.mark >= offset && p.mark <= endOffset) + res.add(p); + } + return res; + } + + /** + * Sets the mark of all <code>Position</code>s that are in the range + * specified by <code>offset</code> and </code>length</code> within + * the buffer array to <code>value</code> + * + * @param offset the start offset of the range to search + * @param length the length of the range to search + * @param value the new value for each mark + */ + void setPositionsInRange(int offset, int length, int value) + { + int endOffset = offset + length; + + int index1 = Collections.binarySearch(positions, + new GapContentPosition(offset)); + if (index1 < 0) + index1 = -(index1 + 1); + for (ListIterator i = positions.listIterator(index1); i.hasNext();) + { + GapContentPosition p = (GapContentPosition) i.next(); + if (p.mark > endOffset) + break; + + if (p.mark >= offset && p.mark <= endOffset) + p.mark = value; + } + } + + /** + * Adjusts the mark of all <code>Position</code>s that are in the range + * specified by <code>offset</code> and </code>length</code> within + * the buffer array by <code>increment</code> + * + * @param offset the start offset of the range to search + * @param length the length of the range to search + * @param incr the increment + */ + void adjustPositionsInRange(int offset, int length, int incr) + { + int endOffset = offset + length; + + int index1 = Collections.binarySearch(positions, + new GapContentPosition(offset)); + if (index1 < 0) + index1 = -(index1 + 1); + for (ListIterator i = positions.listIterator(index1); i.hasNext();) + { + GapContentPosition p = (GapContentPosition) i.next(); + if (p.mark > endOffset) + break; + + if (p.mark >= offset && p.mark <= endOffset) + p.mark += incr; + } + } + + /** + * Resets all <code>Position</code> that have an offset of <code>0</code>, + * to also have an array index of <code>0</code>. This might be necessary + * after a call to <code>shiftGap(0)</code>, since then the marks at offset + * <code>0</code> get shifted to <code>gapEnd</code>. + */ + protected void resetMarksAtZero() + { + if (gapStart != 0) + return; + + setPositionsInRange(gapEnd, 0, 0); + } + + /** + * Outputs debugging info to System.err. It prints out the buffer array, + * the gapStart is marked by a < sign, the gapEnd is marked by a > + * sign and each position is marked by a # sign. + */ + private void dump() + { + System.err.println("GapContent debug information"); + System.err.println("buffer length: " + buffer.length); + System.err.println("gap start: " + gapStart); + System.err.println("gap end: " + gapEnd); + for (int i = 0; i < buffer.length; i++) + { + if (i == gapStart) + System.err.print('<'); + if (i == gapEnd) + System.err.print('>'); + + if (!Character.isISOControl(buffer[i])) + System.err.print(buffer[i]); + else + System.err.print('.'); + } + System.err.println(); + } + + private void dumpPositions() + { + for (Iterator i = positions.iterator(); i.hasNext();) + { + GapContentPosition pos = (GapContentPosition) i.next(); + System.err.println("position at: " + pos.mark); + } + } } diff --git a/libjava/classpath/javax/swing/text/GlyphView.java b/libjava/classpath/javax/swing/text/GlyphView.java index f9e60972d84..eb1fadd4f2d 100644 --- a/libjava/classpath/javax/swing/text/GlyphView.java +++ b/libjava/classpath/javax/swing/text/GlyphView.java @@ -44,6 +44,12 @@ import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; +import java.awt.Toolkit; +import java.text.BreakIterator; + +import javax.swing.SwingConstants; +import javax.swing.event.DocumentEvent; +import javax.swing.text.Position.Bias; /** * Renders a run of styled text. This {@link View} subclass paints the @@ -52,9 +58,7 @@ import java.awt.Shape; * * @author Roman Kennke (roman@kennke.org) */ -public class GlyphView - extends View - implements TabableView, Cloneable +public class GlyphView extends View implements TabableView, Cloneable { /** @@ -68,15 +72,47 @@ public class GlyphView */ public GlyphPainter() { + // Nothing to do here. } /** + * Returns the ascent of the font that is used by this glyph painter. + * + * @param v the glyph view + * + * @return the ascent of the font that is used by this glyph painter + */ + public abstract float getAscent(GlyphView v); + + /** + * Returns the descent of the font that is used by this glyph painter. + * + * @param v the glyph view + * + * @return the descent of the font that is used by this glyph painter + */ + public abstract float getDescent(GlyphView v); + + /** * Returns the full height of the rendered text. * * @return the full height of the rendered text */ public abstract float getHeight(GlyphView view); - + + /** + * Determines the model offset, so that the text between <code>p0</code> + * and this offset fits within the span starting at <code>x</code> with + * the length of <code>len</code>. + * + * @param v the glyph view + * @param p0 the starting offset in the model + * @param x the start location in the view + * @param len the length of the span in the view + */ + public abstract int getBoundedPosition(GlyphView v, int p0, float x, + float len); + /** * Paints the glyphs. * @@ -97,8 +133,8 @@ public class GlyphView * @param view the glyph view * @param pos the position of the character in the model * @param a the area that is occupied by the view - * @param bias either {@link Position.Bias.Forward} or - * {@link Position.Bias.Backward} depending on the preferred + * @param b either {@link Position.Bias#Forward} or + * {@link Position.Bias#Backward} depending on the preferred * direction bias. If <code>null</code> this defaults to * <code>Position.Bias.Forward</code> * @@ -114,6 +150,20 @@ public class GlyphView throws BadLocationException; /** + * Maps a visual position into a document location. + * + * @param v the glyph view + * @param x the X coordinate of the visual position + * @param y the Y coordinate of the visual position + * @param a the allocated region + * @param biasRet filled with the bias of the model location on method exit + * + * @return the model location that represents the specified view location + */ + public abstract int viewToModel(GlyphView v, float x, float y, Shape a, + Position.Bias[] biasRet); + + /** * Determine the span of the glyphs from location <code>p0</code> to * location <code>p1</code>. If <code>te</code> is not <code>null</code>, * then TABs are expanded using this <code>TabExpander</code>. @@ -122,7 +172,7 @@ public class GlyphView * * @param view the glyph view * @param p0 the starting location in the document model - * @param p0 the end location in the document model + * @param p1 the end location in the document model * @param te the tab expander to use * @param x the location at which the view is located * @@ -132,6 +182,69 @@ public class GlyphView public abstract float getSpan(GlyphView view, int p0, int p1, TabExpander te, float x); + + /** + * Returns the model location that should be used to place a caret when + * moving the caret through the document. + * + * @param v the glyph view + * @param pos the current model location + * @param b the bias for <code>p</code> + * @param a the allocated region for the glyph view + * @param direction the direction from the current position; Must be one of + * {@link SwingConstants#EAST}, {@link SwingConstants#WEST}, + * {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH} + * @param biasRet filled with the bias of the resulting location when method + * returns + * + * @return the location within the document that should be used to place the + * caret when moving the caret around the document + * + * @throws BadLocationException if <code>pos</code> is an invalid model + * location + * @throws IllegalArgumentException if <code>d</code> is invalid + */ + public int getNextVisualPositionFrom(GlyphView v, int pos, Position.Bias b, + Shape a, int direction, + Position.Bias[] biasRet) + throws BadLocationException + + { + int result = pos; + switch (direction) + { + case SwingConstants.EAST: + result = pos + 1; + break; + case SwingConstants.WEST: + result = pos - 1; + break; + case SwingConstants.NORTH: + case SwingConstants.SOUTH: + default: + // This should be handled in enclosing view, since the glyph view + // does not layout vertically. + break; + } + return result; + } + + /** + * Returns a painter that can be used to render the specified glyph view. + * If this glyph painter is stateful, then it should return a new instance. + * However, if this painter is stateless it should return itself. The + * default behaviour is to return itself. + * + * @param v the glyph view for which to create a painter + * @param p0 the start offset of the rendered area + * @param p1 the end offset of the rendered area + * + * @return a painter that can be used to render the specified glyph view + */ + public GlyphPainter getPainter(GlyphView v, int p0, int p1) + { + return this; + } } /** @@ -147,7 +260,7 @@ public class GlyphView public float getHeight(GlyphView view) { Font font = view.getFont(); - FontMetrics metrics = view.getContainer().getFontMetrics(font); + FontMetrics metrics = Toolkit.getDefaultToolkit().getFontMetrics(font); float height = metrics.getHeight(); return height; } @@ -173,11 +286,40 @@ public class GlyphView if (parent instanceof TabExpander) tabEx = (TabExpander) parent; - // FIXME: Set character attributes like font-family, font-size, colors. - Color foreground = view.getForeground(); - g.setColor(foreground); - Utilities.drawTabbedText(txt, bounds.x, bounds.y, g, tabEx, - txt.offset); + // Fill the background of the text run. + Color background = view.getBackground(); + g.setColor(background); + int width = Utilities.getTabbedTextWidth(txt, g.getFontMetrics(), + bounds.x, tabEx, txt.offset); + g.fillRect(bounds.x, bounds.y, width, height); + + // Draw the actual text. + g.setColor(view.getForeground()); + g.setFont(view.getFont()); + if (view.isSuperscript()) + // TODO: Adjust font for superscripting. + Utilities.drawTabbedText(txt, bounds.x, bounds.y - height / 2, g, tabEx, + txt.offset); + else if (view.isSubscript()) + // TODO: Adjust font for subscripting. + Utilities.drawTabbedText(txt, bounds.x, bounds.y + height / 2, g, tabEx, + txt.offset); + else + Utilities.drawTabbedText(txt, bounds.x, bounds.y, g, tabEx, + txt.offset); + + if (view.isStikeThrough()) + { + int strikeHeight = (int) (getAscent(view) / 2); + g.drawLine(bounds.x, bounds.y + strikeHeight, bounds.height + width, + bounds.y + strikeHeight); + } + if (view.isUnderline()) + { + int lineHeight = (int) getAscent(view); + g.drawLine(bounds.x, bounds.y + lineHeight, bounds.height + width, + bounds.y + lineHeight); + } } /** @@ -188,8 +330,8 @@ public class GlyphView * @param view the glyph view * @param pos the position of the character in the model * @param a the area that is occupied by the view - * @param bias either {@link Position.Bias.Forward} or - * {@link Position.Bias.Backward} depending on the preferred + * @param b either {@link Position.Bias#Forward} or + * {@link Position.Bias#Backward} depending on the preferred * direction bias. If <code>null</code> this defaults to * <code>Position.Bias.Forward</code> * @@ -225,7 +367,7 @@ public class GlyphView * * @param view the glyph view * @param p0 the starting location in the document model - * @param p0 the end location in the document model + * @param p1 the end location in the document model * @param te the tab expander to use * @param x the location at which the view is located * @@ -237,11 +379,90 @@ public class GlyphView { Element el = view.getElement(); Font font = view.getFont(); - FontMetrics fm = view.getContainer().getFontMetrics(font); + FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(font); Segment txt = view.getText(p0, p1); int span = Utilities.getTabbedTextWidth(txt, fm, (int) x, te, p0); return span; } + + /** + * Returns the ascent of the text run that is rendered by this + * <code>GlyphPainter</code>. + * + * @param v the glyph view + * + * @return the ascent of the text run that is rendered by this + * <code>GlyphPainter</code> + * + * @see FontMetrics#getAscent() + */ + public float getAscent(GlyphView v) + { + Font font = v.getFont(); + FontMetrics fm = v.getContainer().getFontMetrics(font); + return fm.getAscent(); + } + + /** + * Returns the descent of the text run that is rendered by this + * <code>GlyphPainter</code>. + * + * @param v the glyph view + * + * @return the descent of the text run that is rendered by this + * <code>GlyphPainter</code> + * + * @see FontMetrics#getDescent() + */ + public float getDescent(GlyphView v) + { + Font font = v.getFont(); + FontMetrics fm = v.getContainer().getFontMetrics(font); + return fm.getDescent(); + } + + /** + * Determines the model offset, so that the text between <code>p0</code> + * and this offset fits within the span starting at <code>x</code> with + * the length of <code>len</code>. + * + * @param v the glyph view + * @param p0 the starting offset in the model + * @param x the start location in the view + * @param len the length of the span in the view + */ + public int getBoundedPosition(GlyphView v, int p0, float x, float len) + { + TabExpander te = v.getTabExpander(); + Segment txt = v.getText(p0, v.getEndOffset()); + Font font = v.getFont(); + FontMetrics fm = v.getContainer().getFontMetrics(font); + int pos = Utilities.getTabbedTextOffset(txt, fm, (int) x, + (int) (x + len), te, p0, false); + return pos; + } + + /** + * Maps a visual position into a document location. + * + * @param v the glyph view + * @param x the X coordinate of the visual position + * @param y the Y coordinate of the visual position + * @param a the allocated region + * @param biasRet filled with the bias of the model location on method exit + * + * @return the model location that represents the specified view location + */ + public int viewToModel(GlyphView v, float x, float y, Shape a, + Bias[] biasRet) + { + Rectangle b = a.getBounds(); + assert b.contains(x, y) : "The coordinates are expected to be within the " + + "view's bounds: x=" + x + ", y=" + y + + "a=" + a; + int pos = getBoundedPosition(v, v.getStartOffset(), b.x, x - b.x); + return pos; + } } /** @@ -250,6 +471,16 @@ public class GlyphView GlyphPainter glyphPainter; /** + * The start offset within the document for this view. + */ + int startOffset; + + /** + * The end offset within the document for this view. + */ + int endOffset; + + /** * Creates a new <code>GlyphView</code> for the given <code>Element</code>. * * @param element the element that is rendered by this GlyphView @@ -257,6 +488,8 @@ public class GlyphView public GlyphView(Element element) { super(element); + startOffset = element.getStartOffset(); + endOffset = element.getEndOffset(); } /** @@ -319,16 +552,21 @@ public class GlyphView */ public float getPreferredSpan(int axis) { - Element el = getElement(); + float span = 0; checkPainter(); GlyphPainter painter = getGlyphPainter(); - TabExpander tabEx = null; - View parent = getParent(); - if (parent instanceof TabExpander) - tabEx = (TabExpander) parent; - // FIXME: Figure out how to determine the x parameter. - float span = painter.getSpan(this, el.getStartOffset(), el.getEndOffset(), - tabEx, 0.F); + if (axis == X_AXIS) + { + Element el = getElement(); + TabExpander tabEx = null; + View parent = getParent(); + if (parent instanceof TabExpander) + tabEx = (TabExpander) parent; + span = painter.getSpan(this, getStartOffset(), getEndOffset(), + tabEx, 0.F); + } + else + span = painter.getHeight(this); return span; } @@ -372,8 +610,9 @@ public class GlyphView */ public int viewToModel(float x, float y, Shape a, Position.Bias[] b) { - // FIXME: not implemented - return 0; + checkPainter(); + GlyphPainter painter = getGlyphPainter(); + return painter.viewToModel(this, x, y, a, b); } /** @@ -383,12 +622,11 @@ public class GlyphView */ public TabExpander getTabExpander() { - // TODO: Figure out if this is correct. TabExpander te = null; View parent = getParent(); - if (parent instanceof ParagraphView) - te = (ParagraphView) parent; + if (parent instanceof TabExpander) + te = (TabExpander) parent; return te; } @@ -428,23 +666,26 @@ public class GlyphView } catch (BadLocationException ex) { - throw new AssertionError("BadLocationException must not be thrown " - + "here"); + AssertionError ae; + ae = new AssertionError("BadLocationException must not be thrown " + + "here"); + ae.initCause(ex); + throw ae; } FontMetrics fm = null; // Fetch font metrics somewhere. return Utilities.getTabbedTextWidth(seg, fm, 0, null, p0); } /** - * Returns the starting offset in the document model of the portion + * Returns the start offset in the document model of the portion * of text that this view is responsible for. * - * @return the starting offset in the document model of the portion + * @return the start offset in the document model of the portion * of text that this view is responsible for */ - public int getBeginIndex() + public int getStartOffset() { - return getElement().getStartOffset(); + return startOffset; } /** @@ -454,9 +695,9 @@ public class GlyphView * @return the end offset in the document model of the portion * of text that this view is responsible for */ - public int getEndIndex() + public int getEndOffset() { - return getElement().getEndOffset(); + return endOffset; } /** @@ -476,8 +717,11 @@ public class GlyphView } catch (BadLocationException ex) { - throw new AssertionError("BadLocationException should not be " - + "thrown here. p0 = " + p0 + ", p1 = " + p1); + AssertionError ae; + ae = new AssertionError("BadLocationException should not be " + + "thrown here. p0 = " + p0 + ", p1 = " + p1); + ae.initCause(ex); + throw ae; } return txt; @@ -518,4 +762,332 @@ public class GlyphView AttributeSet atts = el.getAttributes(); return StyleConstants.getForeground(atts); } + + /** + * Returns the background color which should be used to paint the text. + * This is fetched from the associated element's text attributes using + * {@link StyleConstants#getBackground}. + * + * @return the background color which should be used to paint the text + */ + public Color getBackground() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + return StyleConstants.getBackground(atts); + } + + /** + * Determines whether the text should be rendered strike-through or not. This + * is determined using the method + * {@link StyleConstants#isStrikeThrough(AttributeSet)} on the element of + * this view. + * + * @return whether the text should be rendered strike-through or not + */ + public boolean isStikeThrough() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + return StyleConstants.isStrikeThrough(atts); + } + + /** + * Determines whether the text should be rendered as subscript or not. This + * is determined using the method + * {@link StyleConstants#isSubscript(AttributeSet)} on the element of + * this view. + * + * @return whether the text should be rendered as subscript or not + */ + public boolean isSubscript() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + return StyleConstants.isSubscript(atts); + } + + /** + * Determines whether the text should be rendered as superscript or not. This + * is determined using the method + * {@link StyleConstants#isSuperscript(AttributeSet)} on the element of + * this view. + * + * @return whether the text should be rendered as superscript or not + */ + public boolean isSuperscript() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + return StyleConstants.isSuperscript(atts); + } + + /** + * Determines whether the text should be rendered as underlined or not. This + * is determined using the method + * {@link StyleConstants#isUnderline(AttributeSet)} on the element of + * this view. + * + * @return whether the text should be rendered as underlined or not + */ + public boolean isUnderline() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + return StyleConstants.isUnderline(atts); + } + + /** + * Creates and returns a shallow clone of this GlyphView. This is used by + * the {@link #createFragment} and {@link #breakView} methods. + * + * @return a shallow clone of this GlyphView + */ + protected final Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException ex) + { + AssertionError err = new AssertionError("CloneNotSupportedException " + + "must not be thrown here"); + err.initCause(ex); + throw err; + } + } + + /** + * Tries to break the view near the specified view span <code>len</code>. + * The glyph view can only be broken in the X direction. For Y direction it + * returns itself. + * + * @param axis the axis for breaking, may be {@link View#X_AXIS} or + * {@link View#Y_AXIS} + * @param p0 the model location where the fragment should start + * @param pos the view position along the axis where the fragment starts + * @param len the desired length of the fragment view + * + * @return the fragment view, or <code>this</code> if breaking was not + * possible + */ + public View breakView(int axis, int p0, float pos, float len) + { + if (axis == Y_AXIS) + return this; + + checkPainter(); + GlyphPainter painter = getGlyphPainter(); + int breakLocation = painter.getBoundedPosition(this, p0, pos, len); + // Try to find a suitable line break. + BreakIterator lineBreaker = BreakIterator.getLineInstance(); + Segment txt = new Segment(); + try + { + getDocument().getText(getStartOffset(), getEndOffset(), txt); + } + catch (BadLocationException ex) + { + AssertionError err = new AssertionError("BadLocationException must not " + + "be thrown here."); + err.initCause(ex); + throw err; + } + lineBreaker.setText(txt); + int goodBreakLocation = lineBreaker.previous(); + if (goodBreakLocation != BreakIterator.DONE) + breakLocation = goodBreakLocation; + + View brokenView = createFragment(p0, breakLocation); + return brokenView; + } + + /** + * Determines how well the specified view location is suitable for inserting + * a line break. If <code>axis</code> is <code>View.Y_AXIS</code>, then + * this method forwards to the superclass, if <code>axis</code> is + * <code>View.X_AXIS</code> then this method returns + * {@link View#ExcellentBreakWeight} if there is a suitable break location + * (usually whitespace) within the specified view span, or + * {@link View#GoodBreakWeight} if not. + * + * @param axis the axis along which the break weight is requested + * @param pos the starting view location + * @param len the length of the span at which the view should be broken + * + * @return the break weight + */ + public int getBreakWeight(int axis, float pos, float len) + { + int weight; + if (axis == Y_AXIS) + weight = super.getBreakWeight(axis, pos, len); + else + { + // Determine the model locations at pos and pos + len. + int spanX = (int) getPreferredSpan(X_AXIS); + int spanY = (int) getPreferredSpan(Y_AXIS); + Rectangle dummyAlloc = new Rectangle(0, 0, spanX, spanY); + Position.Bias[] biasRet = new Position.Bias[1]; + int offset1 = viewToModel(pos, spanY / 2, dummyAlloc, biasRet); + int offset2 = viewToModel(pos, spanY / 2, dummyAlloc, biasRet); + Segment txt = getText(offset1, offset2); + BreakIterator lineBreaker = BreakIterator.getLineInstance(); + lineBreaker.setText(txt); + int breakLoc = lineBreaker.previous(); + if (breakLoc == offset1) + weight = View.BadBreakWeight; + else if(breakLoc == BreakIterator.DONE) + weight = View.GoodBreakWeight; + else + weight = View.ExcellentBreakWeight; + } + return weight; + } + + /** + * Receives notification that some text attributes have changed within the + * text fragment that this view is responsible for. This calls + * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for + * both width and height. + * + * @param e the document event describing the change; not used here + * @param a the view allocation on screen; not used here + * @param vf the view factory; not used here + */ + public void changedUpdate(DocumentEvent e, Shape a, ViewFactory vf) + { + getParent().preferenceChanged(this, true, true); + } + + /** + * Receives notification that some text has been inserted within the + * text fragment that this view is responsible for. This calls + * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for + * width. + * + * @param e the document event describing the change; not used here + * @param a the view allocation on screen; not used here + * @param vf the view factory; not used here + */ + public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf) + { + getParent().preferenceChanged(this, true, false); + } + + /** + * Receives notification that some text has been removed within the + * text fragment that this view is responsible for. This calls + * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for + * width. + * + * @param e the document event describing the change; not used here + * @param a the view allocation on screen; not used here + * @param vf the view factory; not used here + */ + public void removeUpdate(DocumentEvent e, Shape a, ViewFactory vf) + { + getParent().preferenceChanged(this, true, false); + } + + /** + * Creates a fragment view of this view that starts at <code>p0</code> and + * ends at <code>p1</code>. + * + * @param p0 the start location for the fragment view + * @param p1 the end location for the fragment view + * + * @return the fragment view + */ + public View createFragment(int p0, int p1) + { + GlyphView fragment = (GlyphView) clone(); + fragment.startOffset = p0; + fragment.endOffset = p1; + return fragment; + } + + /** + * Returns the alignment of this view along the specified axis. For the Y + * axis this is <code>(height - descent) / height</code> for the used font, + * so that it is aligned along the baseline. + * For the X axis the superclass is called. + */ + public float getAlignment(int axis) + { + float align; + if (axis == Y_AXIS) + { + checkPainter(); + GlyphPainter painter = getGlyphPainter(); + float height = painter.getHeight(this); + float descent = painter.getDescent(this); + align = (height - descent) / height; + } + else + align = super.getAlignment(axis); + + return align; + } + + /** + * Returns the model location that should be used to place a caret when + * moving the caret through the document. + * + * @param pos the current model location + * @param bias the bias for <code>p</code> + * @param a the allocated region for the glyph view + * @param direction the direction from the current position; Must be one of + * {@link SwingConstants#EAST}, {@link SwingConstants#WEST}, + * {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH} + * @param biasRet filled with the bias of the resulting location when method + * returns + * + * @return the location within the document that should be used to place the + * caret when moving the caret around the document + * + * @throws BadLocationException if <code>pos</code> is an invalid model + * location + * @throws IllegalArgumentException if <code>d</code> is invalid + */ + public int getNextVisualPositionFrom(int pos, Position.Bias bias, Shape a, + int direction, Position.Bias[] biasRet) + throws BadLocationException + { + checkPainter(); + GlyphPainter painter = getGlyphPainter(); + return painter.getNextVisualPositionFrom(this, pos, bias, a, direction, + biasRet); + } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset in + * the document model + */ + public int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException + { + // TODO: Implement this properly. + throw new AssertionError("Not implemented yet."); + } } diff --git a/libjava/classpath/javax/swing/text/IconView.java b/libjava/classpath/javax/swing/text/IconView.java index c7e22b6c3eb..6dd0f7ad34a 100644 --- a/libjava/classpath/javax/swing/text/IconView.java +++ b/libjava/classpath/javax/swing/text/IconView.java @@ -41,6 +41,8 @@ package javax.swing.text; import java.awt.Graphics; import java.awt.Shape; +import javax.swing.SwingConstants; + // TODO: Implement this class. public class IconView extends View @@ -125,4 +127,34 @@ public class IconView // FIXME: not implemented return 0; } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset in + * the document model + */ + public int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException + { + // TODO: Implement this properly. + throw new AssertionError("Not implemented yet."); + } } diff --git a/libjava/classpath/javax/swing/text/InternationalFormatter.java b/libjava/classpath/javax/swing/text/InternationalFormatter.java index cedaf59feeb..86300a70d8e 100644 --- a/libjava/classpath/javax/swing/text/InternationalFormatter.java +++ b/libjava/classpath/javax/swing/text/InternationalFormatter.java @@ -57,9 +57,8 @@ import javax.swing.JFormattedTextField; public class InternationalFormatter extends DefaultFormatter { - - /** The serialVersoinUID. */ - private static final long serialVersionUID = 6941977820906408656L; + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 2436068675711756856L; /** The format that handles value to string conversion. */ Format format; diff --git a/libjava/classpath/javax/swing/text/JTextComponent.java b/libjava/classpath/javax/swing/text/JTextComponent.java index b3fad79124c..83966bbdf47 100644 --- a/libjava/classpath/javax/swing/text/JTextComponent.java +++ b/libjava/classpath/javax/swing/text/JTextComponent.java @@ -50,9 +50,9 @@ import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.InputMethodListener; import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; import java.io.IOException; import java.io.Reader; import java.io.Writer; @@ -72,7 +72,6 @@ import javax.swing.JViewport; import javax.swing.KeyStroke; import javax.swing.Scrollable; import javax.swing.SwingConstants; -import javax.swing.Timer; import javax.swing.TransferHandler; import javax.swing.UIManager; import javax.swing.event.CaretEvent; @@ -89,6 +88,7 @@ public abstract class JTextComponent extends JComponent /** * AccessibleJTextComponent */ + // FIXME: This inner class is a complete stub and needs to be implemented. public class AccessibleJTextComponent extends AccessibleJComponent implements AccessibleText, CaretListener, DocumentListener { @@ -99,6 +99,7 @@ public abstract class JTextComponent extends JComponent */ public AccessibleJTextComponent() { + // Nothing to do here. } /** @@ -301,50 +302,6 @@ public abstract class JTextComponent extends JComponent } /** - * The timer that lets the caret blink. - */ - private class CaretBlinkTimer - extends Timer - implements ActionListener - { - /** - * Creates a new CaretBlinkTimer object with a default delay of 1 second. - */ - public CaretBlinkTimer() - { - super(1000, null); - addActionListener(this); - } - - /** - * Lets the caret blink. - */ - public void actionPerformed(ActionEvent ev) - { - Caret c = caret; - if (c != null) - c.setVisible(!c.isVisible()); - } - - /** - * Updates the blink delay according to the current caret. - */ - public void update() - { - stop(); - Caret c = caret; - if (c != null) - { - setDelay(c.getBlinkRate()); - if (editable) - start(); - else - c.setVisible(false); - } - } - } - - /** * According to <a * href="http://java.sun.com/products/jfc/tsc/special_report/kestrel/keybindings.html">this * report</a>, a pair of private classes wraps a {@link @@ -604,8 +561,7 @@ public abstract class JTextComponent extends JComponent } } - class DefaultTransferHandler - extends TransferHandler + class DefaultTransferHandler extends TransferHandler { public boolean canImport(JComponent component, DataFlavor[] flavors) { @@ -631,23 +587,23 @@ public abstract class JTextComponent extends JComponent int end = textComponent.getSelectionEnd(); if (start == end) - return; + return; try - { - // Copy text to clipboard. - String data = textComponent.getDocument().getText(start, end); - StringSelection selection = new StringSelection(data); - clipboard.setContents(selection, null); - - // Delete selected text on cut action. - if (action == MOVE) - doc.remove(start, end - start); - } + { + // Copy text to clipboard. + String data = textComponent.getDocument().getText(start, end); + StringSelection selection = new StringSelection(data); + clipboard.setContents(selection, null); + + // Delete selected text on cut action. + if (action == MOVE) + doc.remove(start, end - start); + } catch (BadLocationException e) - { - // Ignore this and do nothing. - } + { + // Ignore this and do nothing. + } } public int getSourceActions() @@ -661,30 +617,30 @@ public abstract class JTextComponent extends JComponent DataFlavor[] flavors = transferable.getTransferDataFlavors(); if (flavors == null) - return false; + return false; for (int i = 0; i < flavors.length; ++i) - if (flavors[i].equals(DataFlavor.stringFlavor)) - flavor = flavors[i]; + if (flavors[i].equals(DataFlavor.stringFlavor)) + flavor = flavors[i]; if (flavor == null) - return false; + return false; try - { - JTextComponent textComponent = (JTextComponent) component; - String data = (String) transferable.getTransferData(flavor); - textComponent.replaceSelection(data); - return true; - } + { + JTextComponent textComponent = (JTextComponent) component; + String data = (String) transferable.getTransferData(flavor); + textComponent.replaceSelection(data); + return true; + } catch (IOException e) - { - // Ignored. - } + { + // Ignored. + } catch (UnsupportedFlavorException e) - { - // Ignored. - } + { + // Ignored. + } return false; } @@ -701,8 +657,6 @@ public abstract class JTextComponent extends JComponent private char focusAccelerator = '\0'; private NavigationFilter navigationFilter; - private CaretBlinkTimer caretBlinkTimer; - /** * Get a Keymap from the global keymap table, by name. * @@ -960,8 +914,6 @@ public abstract class JTextComponent extends JComponent creatingKeymap = true; } - caretBlinkTimer = new CaretBlinkTimer(); - setFocusable(true); setEditable(true); enableEvents(AWTEvent.KEY_EVENT_MASK); @@ -1021,12 +973,17 @@ public abstract class JTextComponent extends JComponent { try { - doc.remove(0, doc.getLength()); - doc.insertString(0, text, null); + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).replace(0, doc.getLength(), text, null); + else + { + doc.remove(0, doc.getLength()); + doc.insertString(0, text, null); + } } catch (BadLocationException e) { - // This can never happen. + // This can never happen. } } @@ -1044,12 +1001,12 @@ public abstract class JTextComponent extends JComponent try { - return doc.getText(0, doc.getLength()); + return doc.getText(0, doc.getLength()); } catch (BadLocationException e) { - // This should never happen. - return ""; + // This should never happen. + return ""; } } @@ -1080,12 +1037,12 @@ public abstract class JTextComponent extends JComponent { try { - return doc.getText(getSelectionStart(), getSelectionEnd()); + return doc.getText(getSelectionStart(), getSelectionEnd()); } catch (BadLocationException e) { - // This should never happen. - return null; + // This should never happen. + return null; } } @@ -1105,7 +1062,8 @@ public abstract class JTextComponent extends JComponent */ protected String paramString() { - return "JTextComponent"; + // TODO: Do something useful here. + return super.paramString(); } /** @@ -1194,14 +1152,6 @@ public abstract class JTextComponent extends JComponent if (editable == newValue) return; - if (newValue == true) - caretBlinkTimer.start(); - else - { - caretBlinkTimer.stop(); - caret.setVisible(false); - } - boolean oldValue = editable; editable = newValue; firePropertyChange("editable", oldValue, newValue); @@ -1230,8 +1180,6 @@ public abstract class JTextComponent extends JComponent Caret oldCaret = caret; caret = newCaret; - caretBlinkTimer.update(); - if (caret != null) caret.install(this); @@ -1399,7 +1347,7 @@ public abstract class JTextComponent extends JComponent start = Math.max(start, 0); start = Math.min(start, length); - end = Math.max(end, 0); + end = Math.max(end, start); end = Math.min(end, length); setCaretPosition(start); @@ -1422,28 +1370,28 @@ public abstract class JTextComponent extends JComponent // If content is empty delete selection. if (content == null) { - caret.setDot(dot); - return; + caret.setDot(dot); + return; } try { - int start = getSelectionStart(); - int end = getSelectionEnd(); - - // Remove selected text. - if (dot != mark) - doc.remove(start, end - start); - - // Insert new text. - doc.insertString(start, content, null); - - // Set dot to new position. - setCaretPosition(start + content.length()); + int start = getSelectionStart(); + int end = getSelectionEnd(); + + // Remove selected text. + if (dot != mark) + doc.remove(start, end - start); + + // Insert new text. + doc.insertString(start, content, null); + + // Set dot to new position. + setCaretPosition(start + content.length()); } catch (BadLocationException e) { - // This should never happen. + // This should never happen. } } @@ -1577,15 +1525,15 @@ public abstract class JTextComponent extends JComponent // Install default TransferHandler if none set. if (getTransferHandler() == null) { - if (defaultTransferHandler == null) - defaultTransferHandler = new DefaultTransferHandler(); - - setTransferHandler(defaultTransferHandler); + if (defaultTransferHandler == null) + defaultTransferHandler = new DefaultTransferHandler(); + + setTransferHandler(defaultTransferHandler); } // Perform action. ActionEvent event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, - action.getValue(Action.NAME).toString()); + action.getValue(Action.NAME).toString()); action.actionPerformed(event); } @@ -1669,5 +1617,20 @@ public abstract class JTextComponent extends JComponent throws IOException { output.write(getText()); - } + } + + /** + * Returns the tooltip text for this text component for the given mouse + * event. This forwards the call to + * {@link TextUI#getToolTipText(JTextComponent, Point)}. + * + * @param ev the mouse event + * + * @return the tooltip text for this text component for the given mouse + * event + */ + public String getToolTipText(MouseEvent ev) + { + return getUI().getToolTipText(this, ev.getPoint()); + } } diff --git a/libjava/classpath/javax/swing/text/LabelView.java b/libjava/classpath/javax/swing/text/LabelView.java index a10391613cd..4890735b925 100644 --- a/libjava/classpath/javax/swing/text/LabelView.java +++ b/libjava/classpath/javax/swing/text/LabelView.java @@ -38,10 +38,57 @@ exception statement from your version. */ package javax.swing.text; -// TODO: Implement this class. -public class LabelView - extends GlyphView +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Shape; + +import javax.swing.event.DocumentEvent; + +/** + * A {@link GlyphView} that caches the textattributes for most effective + * rendering. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class LabelView extends GlyphView { + + /** + * The background color. + */ + Color background; + + /** + * The foreground color. + */ + Color foreground; + + /** + * The background color. + */ + Font font; + + /** + * The strikethrough flag. + */ + boolean strikeThrough; + + /** + * The underline flag. + */ + boolean underline; + + /** + * The subscript flag. + */ + boolean subscript; + + /** + * The superscript flag. + */ + boolean superscript; + /** * Creates a new <code>GlyphView</code> for the given <code>Element</code>. * @@ -50,5 +97,194 @@ public class LabelView public LabelView(Element element) { super(element); + setPropertiesFromAttributes(); + } + + /** + * Loads the properties of this label view from the element's text + * attributes. This method is called from the constructor and the + * {@link #changedUpdate} method + */ + protected void setPropertiesFromAttributes() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + background = StyleConstants.getBackground(atts); + foreground = StyleConstants.getForeground(atts); + strikeThrough = StyleConstants.isStrikeThrough(atts); + subscript = StyleConstants.isSubscript(atts); + superscript = StyleConstants.isSuperscript(atts); + underline = StyleConstants.isUnderline(atts); + + // Determine the font. + String family = StyleConstants.getFontFamily(atts); + int size = StyleConstants.getFontSize(atts); + int style = Font.PLAIN; + if (StyleConstants.isBold(atts)) + style |= Font.BOLD; + if (StyleConstants.isItalic(atts)) + style |= Font.ITALIC; + font = new Font(family, style, size); + } + + /** + * Receives notification when text attributes change in the chunk of + * text that this view is responsible for. This simply calls + * {@link #setPropertiesFromAttributes()}. + * + * @param e the document event + * @param a the allocation of this view + * @param vf the view factory to use for creating new views + */ + public void changedUpdate(DocumentEvent e, Shape a, ViewFactory vf) + { + setPropertiesFromAttributes(); + } + + /** + * Returns the background color for the glyphs. + * + * @return the background color for the glyphs + */ + public Color getBackground() + { + return background; + } + + /** + * Sets the background color for the glyphs. A value of <code>null</code> + * means the background of the parent view should shine through. + * + * @param bg the background to set or <code>null</code> + * + * @since 1.5 + */ + protected void setBackground(Color bg) + { + background = bg; + } + + /** + * Returns the foreground color for the glyphs. + * + * @return the foreground color for the glyphs + */ + public Color getForeground() + { + return foreground; + } + + /** + * Returns the font for the glyphs. + * + * @return the font for the glyphs + */ + public Font getFont() + { + return font; + } + + /** + * Returns the font metrics of the current font. + * + * @return the font metrics of the current font + * + * @deprecated this is not used anymore + */ + protected FontMetrics getFontMetrics() + { + return getContainer().getGraphics().getFontMetrics(font); + } + + /** + * Returns <code>true</code> if the glyphs are rendered underlined, + * <code>false</code> otherwise. + * + * @return <code>true</code> if the glyphs are rendered underlined, + * <code>false</code> otherwise + */ + public boolean isUnderline() + { + return underline; + } + + /** + * Sets the underline flag. + * + * @param flag <code>true</code> if the glyphs are rendered underlined, + * <code>false</code> otherwise + */ + protected void setUnderline(boolean flag) + { + underline = flag; + } + + /** + * Returns <code>true</code> if the glyphs are rendered as subscript, + * <code>false</code> otherwise. + * + * @return <code>true</code> if the glyphs are rendered as subscript, + * <code>false</code> otherwise + */ + public boolean isSubscript() + { + return subscript; + } + + /** + * Sets the subscript flag. + * + * @param flag <code>true</code> if the glyphs are rendered as subscript, + * <code>false</code> otherwise + */ + protected void setSubscript(boolean flag) + { + subscript = flag; + } + + /** + * Returns <code>true</code> if the glyphs are rendered as superscript, + * <code>false</code> otherwise. + * + * @return <code>true</code> if the glyphs are rendered as superscript, + * <code>false</code> otherwise + */ + public boolean isSuperscript() + { + return superscript; + } + + /** + * Sets the superscript flag. + * + * @param flag <code>true</code> if the glyphs are rendered as superscript, + * <code>false</code> otherwise + */ + protected void setSuperscript(boolean flag) + { + superscript = flag; + } + + /** + * Returns <code>true</code> if the glyphs are rendered strike-through, + * <code>false</code> otherwise. + * + * @return <code>true</code> if the glyphs are rendered strike-through, + * <code>false</code> otherwise + */ + public boolean isStrikeThrough() + { + return strikeThrough; + } + + /** + * Sets the strike-through flag. + * + * @param flag <code>true</code> if the glyphs are rendered strike-through, + * <code>false</code> otherwise + */ + protected void setStrikeThrough(boolean flag) + { + strikeThrough = flag; } } diff --git a/libjava/classpath/javax/swing/text/LayoutQueue.java b/libjava/classpath/javax/swing/text/LayoutQueue.java index 83433b6eef5..b0c84b972b2 100644 --- a/libjava/classpath/javax/swing/text/LayoutQueue.java +++ b/libjava/classpath/javax/swing/text/LayoutQueue.java @@ -57,6 +57,7 @@ public class LayoutQueue */ public LayoutQueue() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/text/ParagraphView.java b/libjava/classpath/javax/swing/text/ParagraphView.java index 6c6006a2a0f..6fb121f949e 100644 --- a/libjava/classpath/javax/swing/text/ParagraphView.java +++ b/libjava/classpath/javax/swing/text/ParagraphView.java @@ -59,6 +59,11 @@ public class ParagraphView extends FlowView implements TabExpander { super(el, X_AXIS); } + public float getAlignment(int axis) + { + // FIXME: This is very likely not 100% correct. Work this out. + return 0.0F; + } } /** @@ -86,4 +91,29 @@ public class ParagraphView extends FlowView implements TabExpander { return new Row(getElement()); } + + /** + * Returns the alignment for this paragraph view for the specified axis. + * For the X_AXIS the paragraph view will be aligned at it's left edge + * (0.0F). For the Y_AXIS the paragraph view will be aligned at the + * center of it's first row. + * + * @param axis the axis which is examined + * + * @return the alignment for this paragraph view for the specified axis + */ + public float getAlignment(int axis) + { + if (axis == X_AXIS) + return 0.0F; + else if (getViewCount() > 0) + { + + float prefHeight = getPreferredSpan(Y_AXIS); + float firstRowHeight = getView(0).getPreferredSpan(Y_AXIS); + return (firstRowHeight / 2.F) / prefHeight; + } + else + return 0.0F; + } } diff --git a/libjava/classpath/javax/swing/text/PlainDocument.java b/libjava/classpath/javax/swing/text/PlainDocument.java index 71070e92da7..9e600c4c908 100644 --- a/libjava/classpath/javax/swing/text/PlainDocument.java +++ b/libjava/classpath/javax/swing/text/PlainDocument.java @@ -132,8 +132,8 @@ public class PlainDocument extends AbstractDocument // collapse elements if the removal spans more than 1 line Element newEl = createLeafElement(rootElement, SimpleAttributeSet.EMPTY, - start, end - len); - rootElement.replace(i1, i2 - i1, new Element[]{ newEl }); + start, end); + rootElement.replace(i1, i2 - i1 + 1, new Element[]{ newEl }); } } @@ -147,4 +147,28 @@ public class PlainDocument extends AbstractDocument Element root = getDefaultRootElement(); return root.getElement(root.getElementIndex(pos)); } + + /** + * Inserts a string into the document. If the document property + * '<code>filterNewLines</code>' is set to <code>Boolean.TRUE</code>, then + * all newlines in the inserted string are replaced by space characters, + * otherwise the superclasses behaviour is executed. + * + * Inserting content causes a write lock to be acquired during this method + * call. + * + * @param offs the offset at which to insert the string + * @param str the string to be inserted + * @param atts the text attributes of the string to be inserted + * + * @throws BadLocationException + */ + public void insertString(int offs, String str, AttributeSet atts) + throws BadLocationException + { + String string = str; + if (Boolean.TRUE.equals(getProperty("filterNewlines"))) + string = str.replaceAll("\n", " "); + super.insertString(offs, string, atts); + } } diff --git a/libjava/classpath/javax/swing/text/PlainView.java b/libjava/classpath/javax/swing/text/PlainView.java index 91d7547e77c..9f5ee8ad3c8 100644 --- a/libjava/classpath/javax/swing/text/PlainView.java +++ b/libjava/classpath/javax/swing/text/PlainView.java @@ -46,15 +46,35 @@ import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; -public class PlainView extends View - implements TabExpander +import javax.swing.SwingConstants; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentEvent.ElementChange; + +public class PlainView extends View implements TabExpander { Color selectedColor; Color unselectedColor; + + /** + * The color that is used to draw disabled text fields. + */ + Color disabledColor; + Font font; + /** The length of the longest line in the Document **/ + float maxLineLength = -1; + + /** The longest line in the Document **/ + Element longestLine = null; + protected FontMetrics metrics; + /** + * The instance returned by {@link #getLineBuffer()}. + */ + private transient Segment lineBuffer; + public PlainView(Element elem) { super(elem); @@ -104,7 +124,7 @@ public class PlainView extends View // Get the rectangle for position. Element line = getElement().getElement(lineIndex); int lineStart = line.getStartOffset(); - Segment segment = new Segment(); + Segment segment = getLineBuffer(); document.getText(lineStart, position - lineStart, segment); int xoffset = Utilities.getTabbedTextWidth(segment, metrics, rect.x, this, lineStart); @@ -129,7 +149,9 @@ public class PlainView extends View } catch (BadLocationException e) { - // This should never happen. + AssertionError ae = new AssertionError("Unexpected bad location"); + ae.initCause(e); + throw ae; } } @@ -137,7 +159,7 @@ public class PlainView extends View throws BadLocationException { g.setColor(selectedColor); - Segment segment = new Segment(); + Segment segment = getLineBuffer(); getDocument().getText(p0, p1 - p0, segment); return Utilities.drawTabbedText(segment, x, y, g, this, 0); } @@ -145,8 +167,13 @@ public class PlainView extends View protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1) throws BadLocationException { - g.setColor(unselectedColor); - Segment segment = new Segment(); + JTextComponent textComponent = (JTextComponent) getContainer(); + if (textComponent.isEnabled()) + g.setColor(unselectedColor); + else + g.setColor(disabledColor); + + Segment segment = getLineBuffer(); getDocument().getText(p0, p1 - p0, segment); return Utilities.drawTabbedText(segment, x, y, g, this, segment.offset); } @@ -161,7 +188,8 @@ public class PlainView extends View g.setFont(textComponent.getFont()); selectedColor = textComponent.getSelectedTextColor(); unselectedColor = textComponent.getForeground(); - + disabledColor = textComponent.getDisabledTextColor(); + Rectangle rect = s.getBounds(); // FIXME: Text may be scrolled. @@ -176,9 +204,19 @@ public class PlainView extends View } } + /** + * Returns the tab size of a tab. Checks the Document's + * properties for PlainDocument.tabSizeAttribute and returns it if it is + * defined, otherwise returns 8. + * + * @return the tab size. + */ protected int getTabSize() { - return 8; + Object tabSize = getDocument().getProperty(PlainDocument.tabSizeAttribute); + if (tabSize == null) + return 8; + return ((Integer)tabSize).intValue(); } /** @@ -191,10 +229,54 @@ public class PlainView extends View */ public float nextTabStop(float x, int tabStop) { - float tabSizePixels = getTabSize() + metrics.charWidth('m'); + float tabSizePixels = getTabSize() * metrics.charWidth('m'); return (float) (Math.floor(x / tabSizePixels) + 1) * tabSizePixels; } + /** + * Returns the length of the longest line, used for getting the span + * @return the length of the longest line + */ + float determineMaxLineLength() + { + // if the longest line is cached, return the cached value + if (maxLineLength != -1) + return maxLineLength; + + // otherwise we have to go through all the lines and find it + Element el = getElement(); + Segment seg = getLineBuffer(); + float span = 0; + for (int i = 0; i < el.getElementCount(); i++) + { + Element child = el.getElement(i); + int start = child.getStartOffset(); + int end = child.getEndOffset(); + try + { + el.getDocument().getText(start, end - start, seg); + } + catch (BadLocationException ex) + { + AssertionError ae = new AssertionError("Unexpected bad location"); + ae.initCause(ex); + throw ae; + } + + if (seg == null || seg.array == null || seg.count == 0) + continue; + + int width = metrics.charsWidth(seg.array, seg.offset, seg.count); + if (width > span) + { + longestLine = child; + span = width; + } + } + maxLineLength = span; + return maxLineLength; + } + public float getPreferredSpan(int axis) { if (axis != X_AXIS && axis != Y_AXIS) @@ -205,36 +287,16 @@ public class PlainView extends View float span = 0; Element el = getElement(); - Document doc = el.getDocument(); - Segment seg = new Segment(); switch (axis) { case X_AXIS: - // calculate the maximum of the line's widths - for (int i = 0; i < el.getElementCount(); i++) - { - Element child = el.getElement(i); - int start = child.getStartOffset(); - int end = child.getEndOffset(); - try { - doc.getText(start, start + end, seg); - } - catch (BadLocationException ex) - { - // throw new ClasspathAssertionError - // ("no BadLocationException should be thrown here"); - } - int width = metrics.charsWidth(seg.array, seg.offset, seg.count); - span = Math.max(span, width); - } - break; + span = determineMaxLineLength(); case Y_AXIS: default: span = metrics.getHeight() * el.getElementCount(); break; } - return span; } @@ -252,8 +314,251 @@ public class PlainView extends View */ public int viewToModel(float x, float y, Shape a, Position.Bias[] b) { - // FIXME: not implemented - return 0; + Rectangle rec = a.getBounds(); + Document doc = getDocument(); + Element root = doc.getDefaultRootElement(); + + // PlainView doesn't support line-wrapping so we can find out which + // Element was clicked on just by the y-position + int lineClicked = (int) (y - rec.y) / metrics.getHeight(); + if (lineClicked >= root.getElementCount()) + return getEndOffset() - 1; + + Element line = root.getElement(lineClicked); + Segment s = getLineBuffer(); + int start = line.getStartOffset(); + // We don't want the \n at the end of the line. + int end = line.getEndOffset() - 1; + try + { + doc.getText(start, end - start, s); + } + catch (BadLocationException ble) + { + AssertionError ae = new AssertionError("Unexpected bad location"); + ae.initCause(ble); + throw ae; + } + + int pos = Utilities.getTabbedTextOffset(s, metrics, rec.x, (int)x, this, start); + return Math.max (0, pos); + } + + /** + * Since insertUpdate and removeUpdate each deal with children + * Elements being both added and removed, they both have to perform + * the same checks. So they both simply call this method. + * @param changes the DocumentEvent for the changes to the Document. + * @param a the allocation of the View. + * @param f the ViewFactory to use for rebuilding. + */ + protected void updateDamage(DocumentEvent changes, Shape a, ViewFactory f) + { + Element el = getElement(); + ElementChange ec = changes.getChange(el); + + // If ec is null then no lines were added or removed, just + // repaint the changed line + if (ec == null) + { + int line = getElement().getElementIndex(changes.getOffset()); + damageLineRange(line, line, a, getContainer()); + return; + } + + Element[] removed = ec.getChildrenRemoved(); + Element[] newElements = ec.getChildrenAdded(); + + // If no Elements were added or removed, we just want to repaint + // the area containing the line that was modified + if (removed == null && newElements == null) + { + int line = getElement().getElementIndex(changes.getOffset()); + damageLineRange(line, line, a, getContainer()); + return; + } + + // Check to see if we removed the longest line, if so we have to + // search through all lines and find the longest one again + if (removed != null) + { + for (int i = 0; i < removed.length; i++) + if (removed[i].equals(longestLine)) + { + // reset maxLineLength and search through all lines for longest one + maxLineLength = -1; + determineMaxLineLength(); + ((JTextComponent)getContainer()).repaint(); + return; + } + } + + // If we've reached here, that means we haven't removed the longest line + if (newElements == null) + { + // No lines were added, just repaint the container and exit + ((JTextComponent)getContainer()).repaint(); + return; + } + + // Make sure we have the metrics + updateMetrics(); + + // If we've reached here, that means we haven't removed the longest line + // and we have added at least one line, so we have to check if added lines + // are longer than the previous longest line + Segment seg = getLineBuffer(); + float longestNewLength = 0; + Element longestNewLine = null; + + // Loop through the added lines to check their length + for (int i = 0; i < newElements.length; i++) + { + Element child = newElements[i]; + int start = child.getStartOffset(); + int end = child.getEndOffset(); + try + { + el.getDocument().getText(start, end - start, seg); + } + catch (BadLocationException ex) + { + AssertionError ae = new AssertionError("Unexpected bad location"); + ae.initCause(ex); + throw ae; + } + + if (seg == null || seg.array == null || seg.count == 0) + continue; + + int width = metrics.charsWidth(seg.array, seg.offset, seg.count); + if (width > longestNewLength) + { + longestNewLine = child; + longestNewLength = width; + } + } + + // Check if the longest of the new lines is longer than our previous + // longest line, and if so update our values + if (longestNewLength > maxLineLength) + { + maxLineLength = longestNewLength; + longestLine = longestNewLine; + } + // Repaint the container + ((JTextComponent)getContainer()).repaint(); + } + + /** + * This method is called when something is inserted into the Document + * that this View is displaying. + * + * @param changes the DocumentEvent for the changes. + * @param a the allocation of the View + * @param f the ViewFactory used to rebuild + */ + public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f) + { + updateDamage(changes, a, f); + } + + /** + * This method is called when something is removed from the Document + * that this View is displaying. + * + * @param changes the DocumentEvent for the changes. + * @param a the allocation of the View + * @param f the ViewFactory used to rebuild + */ + public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f) + { + updateDamage(changes, a, f); + } + + /** + * This method is called when attributes were changed in the + * Document in a location that this view is responsible for. + */ + public void changedUpdate (DocumentEvent changes, Shape a, ViewFactory f) + { + updateDamage(changes, a, f); + } + + /** + * Repaint the given line range. This is called from insertUpdate, + * changedUpdate, and removeUpdate when no new lines were added + * and no lines were removed, to repaint the line that was + * modified. + * + * @param line0 the start of the range + * @param line1 the end of the range + * @param a the rendering region of the host + * @param host the Component that uses this View (used to call repaint + * on that Component) + * + * @since 1.4 + */ + protected void damageLineRange (int line0, int line1, Shape a, Component host) + { + if (a == null) + return; + + Rectangle rec0 = lineToRect(a, line0); + Rectangle rec1 = lineToRect(a, line1); + + if (rec0 == null || rec1 == null) + // something went wrong, repaint the entire host to be safe + host.repaint(); + else + { + Rectangle repaintRec = rec0.union(rec1); + host.repaint(); + } + } + + /** + * Provides a {@link Segment} object, that can be used to fetch text from + * the document. + * + * @returna {@link Segment} object, that can be used to fetch text from + * the document + */ + protected Segment getLineBuffer() + { + if (lineBuffer == null) + lineBuffer = new Segment(); + return lineBuffer; + } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset in + * the document model + */ + public int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException + { + // TODO: Implement this properly. + throw new AssertionError("Not implemented yet."); } } diff --git a/libjava/classpath/javax/swing/text/Segment.java b/libjava/classpath/javax/swing/text/Segment.java index 92d850016d9..84e0e700f2e 100644 --- a/libjava/classpath/javax/swing/text/Segment.java +++ b/libjava/classpath/javax/swing/text/Segment.java @@ -39,8 +39,7 @@ package javax.swing.text; import java.text.CharacterIterator; -public class Segment - implements Cloneable, CharacterIterator +public class Segment implements Cloneable, CharacterIterator { private boolean partialReturn; private int current; @@ -51,6 +50,7 @@ public class Segment public Segment() { + // Nothing to do here. } public Segment(char[] array, int offset, int count) diff --git a/libjava/classpath/javax/swing/text/SimpleAttributeSet.java b/libjava/classpath/javax/swing/text/SimpleAttributeSet.java index 3ef5db61d43..0c9f607b196 100644 --- a/libjava/classpath/javax/swing/text/SimpleAttributeSet.java +++ b/libjava/classpath/javax/swing/text/SimpleAttributeSet.java @@ -45,6 +45,9 @@ import java.util.Hashtable; public class SimpleAttributeSet implements MutableAttributeSet, Serializable, Cloneable { + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 8267656273837665219L; + public static final AttributeSet EMPTY = new SimpleAttributeSet(); Hashtable tab; @@ -84,12 +87,34 @@ public class SimpleAttributeSet return s; } + /** + * Returns true if the given name and value represent an attribute + * found either in this AttributeSet or in its resolve parent hierarchy. + * @param name the key for the attribute + * @param value the value for the attribute + * @return true if the attribute is found here or in this set's resolve + * parent hierarchy + */ public boolean containsAttribute(Object name, Object value) { + return (tab.containsKey(name) && tab.get(name).equals(value)) || + (getResolveParent() != null && getResolveParent(). + containsAttribute(name, value)); + } + + /** + * Returns true if the given name and value are found in this AttributeSet. + * Does not check the resolve parent. + * @param name the key for the attribute + * @param value the value for the attribute + * @return true if the attribute is found in this AttributeSet + */ + boolean containsAttributeLocally(Object name, Object value) + { return tab.containsKey(name) && tab.get(name).equals(value); } - + public boolean containsAttributes(AttributeSet attributes) { Enumeration e = attributes.getAttributeNames(); @@ -110,9 +135,9 @@ public class SimpleAttributeSet public boolean equals(Object obj) { - return (obj != null) - && (obj instanceof SimpleAttributeSet) - && ((SimpleAttributeSet)obj).tab.equals(this.tab); + return + (obj instanceof AttributeSet) + && this.isEqual((AttributeSet) obj); } public Object getAttribute(Object name) @@ -157,10 +182,16 @@ public class SimpleAttributeSet { return tab.isEmpty(); } - + + /** + * Returns true if the given set has the same number of attributes + * as this set and <code>containsAttributes(attr)</code> returns + * true. + */ public boolean isEqual(AttributeSet attr) { - return this.equals(attr); + return getAttributeCount() == attr.getAttributeCount() + && this.containsAttributes(attr); } public void removeAttribute(Object name) @@ -168,9 +199,21 @@ public class SimpleAttributeSet tab.remove(name); } + /** + * Removes attributes from this set if they are found in the + * given set. Only attributes whose key AND value are removed. + * Removes attributes only from this set, not from the resolving parent. + */ public void removeAttributes(AttributeSet attributes) { - removeAttributes(attributes.getAttributeNames()); + Enumeration e = attributes.getAttributeNames(); + while (e.hasMoreElements()) + { + Object name = e.nextElement(); + Object val = attributes.getAttribute(name); + if (containsAttributeLocally(name, val)) + removeAttribute(name); + } } public void removeAttributes(Enumeration names) diff --git a/libjava/classpath/javax/swing/text/StringContent.java b/libjava/classpath/javax/swing/text/StringContent.java index bedf480d4ec..7db377a1c9b 100644 --- a/libjava/classpath/javax/swing/text/StringContent.java +++ b/libjava/classpath/javax/swing/text/StringContent.java @@ -56,6 +56,9 @@ import javax.swing.undo.UndoableEdit; */ public final class StringContent implements AbstractDocument.Content, Serializable { + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 4755994433709540381L; + // This is package-private to avoid an accessor method. char[] content; diff --git a/libjava/classpath/javax/swing/text/StyleConstants.java b/libjava/classpath/javax/swing/text/StyleConstants.java index 3f973f22631..598eaf621bc 100644 --- a/libjava/classpath/javax/swing/text/StyleConstants.java +++ b/libjava/classpath/javax/swing/text/StyleConstants.java @@ -54,11 +54,13 @@ public class StyleConstants public static final Object BidiLevel = CharacterConstants.BidiLevel; public static final Object Bold = CharacterConstants.Bold; public static final Object ComponentAttribute = CharacterConstants.ComponentAttribute; - public static final Object FontFamily = CharacterConstants.Family; + public static final Object Family = CharacterConstants.Family; + public static final Object FontFamily = CharacterConstants.Family; public static final Object FontSize = CharacterConstants.Size; public static final Object Foreground = CharacterConstants.Foreground; public static final Object IconAttribute = CharacterConstants.IconAttribute; public static final Object Italic = CharacterConstants.Italic; + public static final Object Size = CharacterConstants.Size; public static final Object StrikeThrough = CharacterConstants.StrikeThrough; public static final Object Subscript = CharacterConstants.Subscript; public static final Object Superscript = CharacterConstants.Superscript; @@ -109,7 +111,7 @@ public class StyleConstants if (a.isDefined(Background)) return (Color) a.getAttribute(Background); else - return Color.BLACK; + return Color.WHITE; } public static int getBidiLevel(AttributeSet a) diff --git a/libjava/classpath/javax/swing/text/StyleContext.java b/libjava/classpath/javax/swing/text/StyleContext.java index ae11622ffc6..6c4e299455f 100644 --- a/libjava/classpath/javax/swing/text/StyleContext.java +++ b/libjava/classpath/javax/swing/text/StyleContext.java @@ -57,9 +57,15 @@ import javax.swing.event.EventListenerList; public class StyleContext implements Serializable, AbstractDocument.AttributeContext { + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 8042858831190784241L; + public class NamedStyle implements Serializable, Style { + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = -6690628971806226374L; + protected ChangeEvent changeEvent; protected EventListenerList listenerList; @@ -288,7 +294,7 @@ public class StyleContext public boolean equals(Object obj) { return - (obj instanceof SmallAttributeSet) + (obj instanceof AttributeSet) && this.isEqual((AttributeSet)obj); } @@ -300,9 +306,14 @@ public class StyleContext return attrs[i+1]; } - Object p = getResolveParent(); - if (p != null && p instanceof AttributeSet) - return (((AttributeSet)p).getAttribute(key)); + // Check the resolve parent, unless we're looking for the + // ResolveAttribute, which would cause an infinite loop + if (!(key.equals(ResolveAttribute))) + { + Object p = getResolveParent(); + if (p != null && p instanceof AttributeSet) + return (((AttributeSet)p).getAttribute(key)); + } return null; } diff --git a/libjava/classpath/javax/swing/text/StyledDocument.java b/libjava/classpath/javax/swing/text/StyledDocument.java index ea277540f23..168e1b116f3 100644 --- a/libjava/classpath/javax/swing/text/StyledDocument.java +++ b/libjava/classpath/javax/swing/text/StyledDocument.java @@ -45,101 +45,96 @@ import java.awt.Font; * @author Andrew Selkirk * @version 1.0 */ -public interface StyledDocument extends Document { - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * addStyle - * @param nm TODO - * @param rent TODO - * @returns Style - */ - Style addStyle(String nm, Style parent); - - /** - * removeStyle - * @param nm TODO - */ - void removeStyle(String nm); - - /** - * getStyle - * @param nm TODO - * @returns Style - */ - Style getStyle(String nm); - - /** - * setCharacterAttributes - * @param offset TODO - * @param length TODO - * @param set TODO - * @param replace TODO - */ - void setCharacterAttributes(int offset, int length, - AttributeSet set, boolean replace); - - /** - * setParagraphAttributes - * @param offset TODO - * @param length TODO - * @param set TODO - * @param replace TODO - */ - void setParagraphAttributes(int offset, int length, - AttributeSet set, boolean replace); - - /** - * getLogicalStyle - * @param position TODO - * @returns Style - */ - Style getLogicalStyle(int position); - - /** - * setLogicalStyle - * @param position TODO - * @param style TODO - */ - void setLogicalStyle(int position, Style style); - - /** - * getParagraphElement - * @param position TODO - * @returns Element - */ - Element getParagraphElement(int position); - - /** - * getCharacterElement - * @param position TODO - * @returns Element - */ - Element getCharacterElement(int position); - - /** - * getForeground - * @param set TODO - * @returns Color - */ - Color getForeground(AttributeSet set); - - /** - * getBackground - * @param set TODO - * @returns Color - */ - Color getBackground(AttributeSet set); - - /** - * getFont - * @param set TODO - * @returns Font - */ - Font getFont(AttributeSet set); - - -} // StyledDocument +public interface StyledDocument extends Document +{ + /** + * addStyle + * @param nm TODO + * @param parent TODO + * @returns Style + */ + Style addStyle(String nm, Style parent); + + /** + * removeStyle + * @param nm TODO + */ + void removeStyle(String nm); + + /** + * getStyle + * @param nm TODO + * @returns Style + */ + Style getStyle(String nm); + + /** + * setCharacterAttributes + * @param offset TODO + * @param length TODO + * @param set TODO + * @param replace TODO + */ + void setCharacterAttributes(int offset, int length, AttributeSet set, + boolean replace); + + /** + * setParagraphAttributes + * @param offset TODO + * @param length TODO + * @param set TODO + * @param replace TODO + */ + void setParagraphAttributes(int offset, int length, AttributeSet set, + boolean replace); + + /** + * getLogicalStyle + * @param position TODO + * @returns Style + */ + Style getLogicalStyle(int position); + + /** + * setLogicalStyle + * @param position TODO + * @param style TODO + */ + void setLogicalStyle(int position, Style style); + + /** + * getParagraphElement + * @param position TODO + * @returns Element + */ + Element getParagraphElement(int position); + + /** + * getCharacterElement + * @param position TODO + * @returns Element + */ + Element getCharacterElement(int position); + + /** + * getForeground + * @param set TODO + * @returns Color + */ + Color getForeground(AttributeSet set); + + /** + * getBackground + * @param set TODO + * @returns Color + */ + Color getBackground(AttributeSet set); + + /** + * getFont + * @param set TODO + * @returns Font + */ + Font getFont(AttributeSet set); + +} diff --git a/libjava/classpath/javax/swing/text/StyledEditorKit.java b/libjava/classpath/javax/swing/text/StyledEditorKit.java index 89c4cf18ee4..e71f992b534 100644 --- a/libjava/classpath/javax/swing/text/StyledEditorKit.java +++ b/libjava/classpath/javax/swing/text/StyledEditorKit.java @@ -40,13 +40,9 @@ package javax.swing.text; import java.awt.Color; import java.awt.event.ActionEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.Serializable; import javax.swing.Action; import javax.swing.JEditorPane; -import javax.swing.JTextPane; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; @@ -460,11 +456,11 @@ public class StyledEditorKit extends DefaultEditorKit * <code>StyledEditorKit</code>, namely the following types of Elements: * * <ul> - * <li>{@link AbstractDocument.ContentElementName}</li> - * <li>{@link AbstractDocument.ParagraphElementName}</li> - * <li>{@link AbstractDocument.SectionElementName}</li> - * <li>{@link StyleContext.ComponentElementName}</li> - * <li>{@link StyleContext.IconElementName}</li> + * <li>{@link AbstractDocument#ContentElementName}</li> + * <li>{@link AbstractDocument#ParagraphElementName}</li> + * <li>{@link AbstractDocument#SectionElementName}</li> + * <li>{@link StyleConstants#ComponentElementName}</li> + * <li>{@link StyleConstants#IconElementName}</li> * </ul> */ static class StyledViewFactory @@ -667,11 +663,11 @@ public class StyledEditorKit extends DefaultEditorKit * namely the following types of <code>Element</code>s: * * <ul> - * <li>{@link AbstractDocument.ContentElementName}</li> - * <li>{@link AbstractDocument.ParagraphElementName}</li> - * <li>{@link AbstractDocument.SectionElementName}</li> - * <li>{@link StyleContext.ComponentElementName}</li> - * <li>{@link StyleContext.IconElementName}</li> + * <li>{@link AbstractDocument#ContentElementName}</li> + * <li>{@link AbstractDocument#ParagraphElementName}</li> + * <li>{@link AbstractDocument#SectionElementName}</li> + * <li>{@link StyleConstants#ComponentElementName}</li> + * <li>{@link StyleConstants#IconElementName}</li> * </ul> * * @return a {@link ViewFactory} that is able to create {@link View}s diff --git a/libjava/classpath/javax/swing/text/TabSet.java b/libjava/classpath/javax/swing/text/TabSet.java index 146f545aac7..ecad9444ea5 100644 --- a/libjava/classpath/javax/swing/text/TabSet.java +++ b/libjava/classpath/javax/swing/text/TabSet.java @@ -41,6 +41,9 @@ import java.io.Serializable; public class TabSet implements Serializable { + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 2367703481999080593L; + TabStop[] tabs; public TabSet(TabStop[] t) diff --git a/libjava/classpath/javax/swing/text/TabStop.java b/libjava/classpath/javax/swing/text/TabStop.java index 032da8bca46..56f862fdae4 100644 --- a/libjava/classpath/javax/swing/text/TabStop.java +++ b/libjava/classpath/javax/swing/text/TabStop.java @@ -41,6 +41,9 @@ import java.io.Serializable; public class TabStop implements Serializable { + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = -5381995917363605058L; + public static final int ALIGN_LEFT = 0; public static final int ALIGN_RIGHT = 1; public static final int ALIGN_CENTER = 2; diff --git a/libjava/classpath/javax/swing/text/Utilities.java b/libjava/classpath/javax/swing/text/Utilities.java index d40408ddc3f..7830b2fca04 100644 --- a/libjava/classpath/javax/swing/text/Utilities.java +++ b/libjava/classpath/javax/swing/text/Utilities.java @@ -40,6 +40,11 @@ package javax.swing.text; import java.awt.FontMetrics; import java.awt.Graphics; +import java.awt.Point; +import java.awt.Rectangle; +import java.text.BreakIterator; + +import javax.swing.SwingConstants; /** * A set of utilities to deal with text. This is used by several other classes @@ -195,4 +200,409 @@ public class Utilities return maxWidth; } + + /** + * Provides a facility to map screen coordinates into a model location. For a + * given text fragment and start location within this fragment, this method + * determines the model location so that the resulting fragment fits best + * into the span <code>[x0, x]</code>. + * + * The parameter <code>round</code> controls which model location is returned + * if the view coordinates are on a character: If <code>round</code> is + * <code>true</code>, then the result is rounded up to the next character, so + * that the resulting fragment is the smallest fragment that is larger than + * the specified span. If <code>round</code> is <code>false</code>, then the + * resulting fragment is the largest fragment that is smaller than the + * specified span. + * + * @param s the text segment + * @param fm the font metrics to use + * @param x0 the starting screen location + * @param x the target screen location at which the requested fragment should + * end + * @param te the tab expander to use; if this is <code>null</code>, TABs are + * expanded to one space character + * @param p0 the starting model location + * @param round if <code>true</code> round up to the next location, otherwise + * round down to the current location + * + * @return the model location, so that the resulting fragment fits within the + * specified span + */ + public static final int getTabbedTextOffset(Segment s, FontMetrics fm, int x0, + int x, TabExpander te, int p0, + boolean round) + { + // At the end of the for loop, this holds the requested model location + int pos; + int currentX = x0; + + for (pos = p0; pos < s.count; pos++) + { + char nextChar = s.array[s.offset+pos]; + if (nextChar == 0) + { + if (! round) + pos--; + break; + } + if (nextChar != '\t') + currentX += fm.charWidth(nextChar); + else + { + if (te == null) + currentX += fm.charWidth(' '); + else + currentX = (int) te.nextTabStop(currentX, pos); + } + if (currentX > x) + { + if (! round) + pos--; + break; + } + } + return pos; + } + + /** + * Provides a facility to map screen coordinates into a model location. For a + * given text fragment and start location within this fragment, this method + * determines the model location so that the resulting fragment fits best + * into the span <code>[x0, x]</code>. + * + * This method rounds up to the next location, so that the resulting fragment + * will be the smallest fragment of the text, that is greater than the + * specified span. + * + * @param s the text segment + * @param fm the font metrics to use + * @param x0 the starting screen location + * @param x the target screen location at which the requested fragment should + * end + * @param te the tab expander to use; if this is <code>null</code>, TABs are + * expanded to one space character + * @param p0 the starting model location + * + * @return the model location, so that the resulting fragment fits within the + * specified span + */ + public static final int getTabbedTextOffset(Segment s, FontMetrics fm, int x0, + int x, TabExpander te, int p0) + { + return getTabbedTextOffset(s, fm, x0, x, te, p0, true); + } + + /** + * Finds the start of the next word for the given offset. + * + * @param c + * the text component + * @param offs + * the offset in the document + * @return the location in the model of the start of the next word. + * @throws BadLocationException + * if the offset is invalid. + */ + public static final int getNextWord(JTextComponent c, int offs) + throws BadLocationException + { + if (offs < 0 || offs > (c.getText().length() - 1)) + throw new BadLocationException("invalid offset specified", offs); + String text = c.getText(); + BreakIterator wb = BreakIterator.getWordInstance(); + wb.setText(text); + int last = wb.following(offs); + int current = wb.next(); + while (current != BreakIterator.DONE) + { + for (int i = last; i < current; i++) + { + // FIXME: Should use isLetter(int) and text.codePointAt(int) + // instead, but isLetter(int) isn't implemented yet + if (Character.isLetter(text.charAt(i))) + return last; + } + last = current; + current = wb.next(); + } + return BreakIterator.DONE; + } + + /** + * Finds the start of the previous word for the given offset. + * + * @param c + * the text component + * @param offs + * the offset in the document + * @return the location in the model of the start of the previous word. + * @throws BadLocationException + * if the offset is invalid. + */ + public static final int getPreviousWord(JTextComponent c, int offs) + throws BadLocationException + { + if (offs < 0 || offs > (c.getText().length() - 1)) + throw new BadLocationException("invalid offset specified", offs); + String text = c.getText(); + BreakIterator wb = BreakIterator.getWordInstance(); + wb.setText(text); + int last = wb.preceding(offs); + int current = wb.previous(); + + while (current != BreakIterator.DONE) + { + for (int i = last; i < offs; i++) + { + // FIXME: Should use isLetter(int) and text.codePointAt(int) + // instead, but isLetter(int) isn't implemented yet + if (Character.isLetter(text.charAt(i))) + return last; + } + last = current; + current = wb.previous(); + } + return 0; + } + + /** + * Finds the start of a word for the given location. + * @param c the text component + * @param offs the offset location + * @return the location of the word beginning + * @throws BadLocationException if the offset location is invalid + */ + public static final int getWordStart(JTextComponent c, int offs) + throws BadLocationException + { + if (offs < 0 || offs >= c.getText().length()) + throw new BadLocationException("invalid offset specified", offs); + + String text = c.getText(); + BreakIterator wb = BreakIterator.getWordInstance(); + wb.setText(text); + if (wb.isBoundary(offs)) + return offs; + return wb.preceding(offs); + } + + /** + * Finds the end of a word for the given location. + * @param c the text component + * @param offs the offset location + * @return the location of the word end + * @throws BadLocationException if the offset location is invalid + */ + public static final int getWordEnd(JTextComponent c, int offs) + throws BadLocationException + { + if (offs < 0 || offs >= c.getText().length()) + throw new BadLocationException("invalid offset specified", offs); + + String text = c.getText(); + BreakIterator wb = BreakIterator.getWordInstance(); + wb.setText(text); + return wb.following(offs); + } + + /** + * Get the model position of the end of the row that contains the + * specified model position. Return null if the given JTextComponent + * does not have a size. + * @param c the JTextComponent + * @param offs the model position + * @return the model position of the end of the row containing the given + * offset + * @throws BadLocationException if the offset is invalid + */ + public static final int getRowEnd(JTextComponent c, int offs) + throws BadLocationException + { + String text = c.getText(); + if (text == null) + return -1; + + // Do a binary search for the smallest position X > offs + // such that that character at positino X is not on the same + // line as the character at position offs + int high = offs + ((text.length() - 1 - offs) / 2); + int low = offs; + int oldHigh = text.length() + 1; + while (true) + { + if (c.modelToView(high).y != c.modelToView(offs).y) + { + oldHigh = high; + high = low + ((high + 1 - low) / 2); + if (oldHigh == high) + return high - 1; + } + else + { + low = high; + high += ((oldHigh - high) / 2); + if (low == high) + return low; + } + } + } + + /** + * Get the model position of the start of the row that contains the specified + * model position. Return null if the given JTextComponent does not have a + * size. + * + * @param c the JTextComponent + * @param offs the model position + * @return the model position of the start of the row containing the given + * offset + * @throws BadLocationException if the offset is invalid + */ + public static final int getRowStart(JTextComponent c, int offs) + throws BadLocationException + { + String text = c.getText(); + if (text == null) + return -1; + + // Do a binary search for the greatest position X < offs + // such that the character at position X is not on the same + // row as the character at position offs + int high = offs; + int low = 0; + int oldLow = 0; + while (true) + { + if (c.modelToView(low).y != c.modelToView(offs).y) + { + oldLow = low; + low = high - ((high + 1 - low) / 2); + if (oldLow == low) + return low + 1; + } + else + { + high = low; + low -= ((low - oldLow) / 2); + if (low == high) + return low; + } + } + } + + /** + * Determine where to break the text in the given Segment, attempting to find + * a word boundary. + * @param s the Segment that holds the text + * @param metrics the font metrics used for calculating the break point + * @param x0 starting view location representing the start of the text + * @param x the target view location + * @param e the TabExpander used for expanding tabs (if this is null tabs + * are expanded to 1 space) + * @param startOffset the offset in the Document of the start of the text + * @return the offset at which we should break the text + */ + public static final int getBreakLocation(Segment s, FontMetrics metrics, + int x0, int x, TabExpander e, + int startOffset) + { + int mark = Utilities.getTabbedTextOffset(s, metrics, x0, x, e, startOffset); + BreakIterator breaker = BreakIterator.getWordInstance(); + breaker.setText(s.toString()); + + // If mark is equal to the end of the string, just use that position + if (mark == s.count) + return mark; + + // Try to find a word boundary previous to the mark at which we + // can break the text + int preceding = breaker.preceding(mark + 1); + + if (preceding != 0) + return preceding; + else + // If preceding is 0 we couldn't find a suitable word-boundary so + // just break it on the character boundary + return mark; + } + + /** + * Returns the paragraph element in the text component <code>c</code> at + * the specified location <code>offset</code>. + * + * @param c the text component + * @param offset the offset of the paragraph element to return + * + * @return the paragraph element at <code>offset</code> + */ + public static final Element getParagraphElement(JTextComponent c, int offset) + { + Document doc = c.getDocument(); + Element par = null; + if (doc instanceof StyledDocument) + { + StyledDocument styledDoc = (StyledDocument) doc; + par = styledDoc.getParagraphElement(offset); + } + else + { + Element root = c.getDocument().getDefaultRootElement(); + int parIndex = root.getElementIndex(offset); + par = root.getElement(parIndex); + } + return par; + } + + /** + * Returns the document position that is closest above to the specified x + * coordinate in the row containing <code>offset</code>. + * + * @param c the text component + * @param offset the offset + * @param x the x coordinate + * + * @return the document position that is closest above to the specified x + * coordinate in the row containing <code>offset</code> + * + * @throws BadLocationException if <code>offset</code> is not a valid offset + */ + public static final int getPositionAbove(JTextComponent c, int offset, int x) + throws BadLocationException + { + View rootView = c.getUI().getRootView(c); + Rectangle r = c.modelToView(offset); + int offs = c.viewToModel(new Point(x, r.y)); + int pos = rootView.getNextVisualPositionFrom(c, offs, + Position.Bias.Forward, + SwingConstants.NORTH, + new Position.Bias[1]); + return pos; + } + + /** + * Returns the document position that is closest below to the specified x + * coordinate in the row containing <code>offset</code>. + * + * @param c the text component + * @param offset the offset + * @param x the x coordinate + * + * @return the document position that is closest above to the specified x + * coordinate in the row containing <code>offset</code> + * + * @throws BadLocationException if <code>offset</code> is not a valid offset + */ + public static final int getPositionBelow(JTextComponent c, int offset, int x) + throws BadLocationException + { + View rootView = c.getUI().getRootView(c); + Rectangle r = c.modelToView(offset); + int offs = c.viewToModel(new Point(x, r.y)); + int pos = rootView.getNextVisualPositionFrom(c, offs, + Position.Bias.Forward, + SwingConstants.SOUTH, + new Position.Bias[1]); + return pos; + } } diff --git a/libjava/classpath/javax/swing/text/View.java b/libjava/classpath/javax/swing/text/View.java index 24efba9a1bc..daab347d731 100644 --- a/libjava/classpath/javax/swing/text/View.java +++ b/libjava/classpath/javax/swing/text/View.java @@ -43,7 +43,6 @@ import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; -import javax.swing.JComponent; import javax.swing.SwingConstants; import javax.swing.event.DocumentEvent; @@ -87,9 +86,9 @@ public abstract class View implements SwingConstants { View parent = getParent(); if (parent == null) - throw new AssertionError("The parent of a View must not be null."); - - return parent.getContainer(); + return null; + else + return parent.getContainer(); } public Document getDocument() @@ -508,6 +507,30 @@ public abstract class View implements SwingConstants } /** + * Maps a position in the document into the coordinate space of the View. + * The output rectangle usually reflects the font height but has a width + * of zero. + * + * This method is deprecated and calls + * {@link #modelToView(int, Position.Bias, int, Position.Bias, Shape)} with + * a bias of {@link Position.Bias#Forward}. + * + * @param pos the position of the character in the model + * @param a the area that is occupied by the view + * + * @return a rectangle that gives the location of the document position + * inside the view coordinate space + * + * @throws BadLocationException if <code>pos</code> is invalid + * + * @deprecated Use {@link #modelToView(int, Shape, Position.Bias)} instead. + */ + public Shape modelToView(int pos, Shape a) throws BadLocationException + { + return modelToView(pos, a, Position.Bias.Forward); + } + + /** * Maps coordinates from the <code>View</code>'s space into a position * in the document model. * @@ -521,6 +544,25 @@ public abstract class View implements SwingConstants */ public abstract int viewToModel(float x, float y, Shape a, Position.Bias[] b); + /** + * Maps coordinates from the <code>View</code>'s space into a position + * in the document model. This method is deprecated and only there for + * compatibility. + * + * @param x the x coordinate in the view space + * @param y the y coordinate in the view space + * @param a the allocation of this <code>View</code> + * + * @return the position in the document that corresponds to the screen + * coordinates <code>x, y</code> + * + * @deprecated Use {@link #viewToModel(float, float, Shape, Position.Bias[])} + * instead. + */ + public int viewToModel(float x, float y, Shape a) + { + return viewToModel(x, y, a, new Position.Bias[0]); + } /** * Dumps the complete View hierarchy. This method can be used for debugging @@ -552,4 +594,30 @@ public abstract class View implements SwingConstants for (int i = 0; i < count; ++i) getView(i).dump(indent + 1); } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset in + * the document model + */ + public abstract int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException; } diff --git a/libjava/classpath/javax/swing/text/WrappedPlainView.java b/libjava/classpath/javax/swing/text/WrappedPlainView.java new file mode 100644 index 00000000000..b90519046ae --- /dev/null +++ b/libjava/classpath/javax/swing/text/WrappedPlainView.java @@ -0,0 +1,700 @@ +/* WrappedPlainView.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.text; + +import java.awt.Color; +import java.awt.Container; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Shape; + +import javax.swing.SwingConstants; +import javax.swing.event.DocumentEvent; +import javax.swing.text.Position.Bias; + +/** + * @author abalkiss + * + */ +public class WrappedPlainView extends BoxView implements TabExpander +{ + /** The color for selected text **/ + Color selectedColor; + + /** The color for unselected text **/ + Color unselectedColor; + + /** The color for disabled components **/ + Color disabledColor; + + /** Stores the font metrics **/ + protected FontMetrics metrics; + + /** Whether or not to wrap on word boundaries **/ + boolean wordWrap; + + /** A ViewFactory that creates WrappedLines **/ + ViewFactory viewFactory = new WrappedLineCreator(); + + /** The start of the selected text **/ + int selectionStart; + + /** The end of the selected text **/ + int selectionEnd; + + /** + * The instance returned by {@link #getLineBuffer()}. + */ + private transient Segment lineBuffer; + + public WrappedPlainView (Element elem) + { + this (elem, false); + } + + public WrappedPlainView (Element elem, boolean wordWrap) + { + super (elem, Y_AXIS); + this.wordWrap = wordWrap; + } + + /** + * Provides access to the Segment used for retrievals from the Document. + * @return the Segment. + */ + protected final Segment getLineBuffer() + { + if (lineBuffer == null) + lineBuffer = new Segment(); + return lineBuffer; + } + + /** + * Returns the next tab stop position after a given reference position. + * + * This implementation ignores the <code>tabStop</code> argument. + * + * @param x the current x position in pixels + * @param tabStop the position within the text stream that the tab occured at + */ + public float nextTabStop(float x, int tabStop) + { + JTextComponent host = (JTextComponent)getContainer(); + float tabSizePixels = getTabSize() + * host.getFontMetrics(host.getFont()).charWidth('m'); + return (float) (Math.floor(x / tabSizePixels) + 1) * tabSizePixels; + } + + /** + * Returns the tab size for the Document based on + * PlainDocument.tabSizeAttribute, defaulting to 8 if this property is + * not defined + * + * @return the tab size. + */ + protected int getTabSize() + { + Object tabSize = getDocument().getProperty(PlainDocument.tabSizeAttribute); + if (tabSize == null) + return 8; + return ((Integer)tabSize).intValue(); + } + + /** + * Draws a line of text, suppressing white space at the end and expanding + * tabs. Calls drawSelectedText and drawUnselectedText. + * @param p0 starting document position to use + * @param p1 ending document position to use + * @param g graphics context + * @param x starting x position + * @param y starting y position + */ + protected void drawLine(int p0, int p1, Graphics g, int x, int y) + { + try + { + // We have to draw both selected and unselected text. There are + // several cases: + // - entire range is unselected + // - entire range is selected + // - start of range is selected, end of range is unselected + // - start of range is unselected, end of range is selected + // - middle of range is selected, start and end of range is unselected + + // entire range unselected: + if ((selectionStart == selectionEnd) || + (p0 > selectionEnd || p1 < selectionStart)) + drawUnselectedText(g, x, y, p0, p1); + + // entire range selected + else if (p0 >= selectionStart && p1 <= selectionEnd) + drawSelectedText(g, x, y, p0, p1); + + // start of range selected, end of range unselected + else if (p0 >= selectionStart) + { + x = drawSelectedText(g, x, y, p0, selectionEnd); + drawUnselectedText(g, x, y, selectionEnd, p1); + } + + // start of range unselected, end of range selected + else if (selectionStart > p0 && selectionEnd > p1) + { + x = drawUnselectedText(g, x, y, p0, selectionStart); + drawSelectedText(g, x, y, selectionStart, p1); + } + + // middle of range selected + else if (selectionStart > p0) + { + x = drawUnselectedText(g, x, y, p0, selectionStart); + x = drawSelectedText(g, x, y, selectionStart, selectionEnd); + drawUnselectedText(g, x, y, selectionEnd, p1); + } + } + catch (BadLocationException ble) + { + // shouldn't happen + } + } + + /** + * Renders the range of text as selected text. Just paints the text + * in the color specified by the host component. Assumes the highlighter + * will render the selected background. + * @param g the graphics context + * @param x the starting X coordinate + * @param y the starting Y coordinate + * @param p0 the starting model location + * @param p1 the ending model location + * @return the X coordinate of the end of the text + * @throws BadLocationException if the given range is invalid + */ + protected int drawSelectedText(Graphics g, int x, int y, int p0, int p1) + throws BadLocationException + { + g.setColor(selectedColor); + Segment segment = getLineBuffer(); + getDocument().getText(p0, p1 - p0, segment); + return Utilities.drawTabbedText(segment, x, y, g, this, p0); + } + + /** + * Renders the range of text as normal unhighlighted text. + * @param g the graphics context + * @param x the starting X coordinate + * @param y the starting Y coordinate + * @param p0 the starting model location + * @param p1 the end model location + * @return the X location of the end off the range + * @throws BadLocationException if the range given is invalid + */ + protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1) + throws BadLocationException + { + JTextComponent textComponent = (JTextComponent) getContainer(); + if (textComponent.isEnabled()) + g.setColor(unselectedColor); + else + g.setColor(disabledColor); + + Segment segment = getLineBuffer(); + getDocument().getText(p0, p1 - p0, segment); + return Utilities.drawTabbedText(segment, x, y, g, this, p0); + } + + /** + * Loads the children to initiate the view. Called by setParent. + * Creates a WrappedLine for each child Element. + */ + protected void loadChildren (ViewFactory f) + { + Element root = getElement(); + int numChildren = root.getElementCount(); + if (numChildren == 0) + return; + + View[] children = new View[numChildren]; + for (int i = 0; i < numChildren; i++) + children[i] = new WrappedLine(root.getElement(i)); + replace(0, 0, children); + } + + /** + * Calculates the break position for the text between model positions + * p0 and p1. Will break on word boundaries or character boundaries + * depending on the break argument given in construction of this + * WrappedPlainView. Used by the nested WrappedLine class to determine + * when to start the next logical line. + * @param p0 the start model position + * @param p1 the end model position + * @return the model position at which to break the text + */ + protected int calculateBreakPosition(int p0, int p1) + { + Container c = getContainer(); + Rectangle alloc = c.isValid() ? c.getBounds() + : new Rectangle(c.getPreferredSize()); + updateMetrics(); + try + { + getDocument().getText(p0, p1 - p0, getLineBuffer()); + } + catch (BadLocationException ble) + { + // this shouldn't happen + } + // FIXME: Should we account for the insets of the container? + if (wordWrap) + return p0 + + Utilities.getBreakLocation(lineBuffer, metrics, alloc.x, + alloc.x + alloc.width, this, 0); + else + { + return p0 + + Utilities.getTabbedTextOffset(lineBuffer, metrics, alloc.x, + alloc.x + alloc.width, this, 0); + } + } + + void updateMetrics() + { + Container component = getContainer(); + metrics = component.getFontMetrics(component.getFont()); + } + + /** + * Determines the preferred span along the given axis. Implemented to + * cache the font metrics and then call the super classes method. + */ + public float getPreferredSpan (int axis) + { + updateMetrics(); + return super.getPreferredSpan(axis); + } + + /** + * Determines the minimum span along the given axis. Implemented to + * cache the font metrics and then call the super classes method. + */ + public float getMinimumSpan (int axis) + { + updateMetrics(); + return super.getMinimumSpan(axis); + } + + /** + * Determines the maximum span along the given axis. Implemented to + * cache the font metrics and then call the super classes method. + */ + public float getMaximumSpan (int axis) + { + updateMetrics(); + return super.getMaximumSpan(axis); + } + + /** + * Called when something was inserted. Overridden so that + * the view factory creates WrappedLine views. + */ + public void insertUpdate (DocumentEvent e, Shape a, ViewFactory f) + { + super.insertUpdate(e, a, viewFactory); + // FIXME: could improve performance by repainting only the necessary area + getContainer().repaint(); + } + + /** + * Called when something is removed. Overridden so that + * the view factory creates WrappedLine views. + */ + public void removeUpdate (DocumentEvent e, Shape a, ViewFactory f) + { + super.removeUpdate(e, a, viewFactory); + // FIXME: could improve performance by repainting only the necessary area + getContainer().repaint(); + } + + /** + * Called when the portion of the Document that this View is responsible + * for changes. Overridden so that the view factory creates + * WrappedLine views. + */ + public void changedUpdate (DocumentEvent e, Shape a, ViewFactory f) + { + super.changedUpdate(e, a, viewFactory); + // FIXME: could improve performance by repainting only the necessary area + getContainer().repaint(); + } + + class WrappedLineCreator implements ViewFactory + { + // Creates a new WrappedLine + public View create(Element elem) + { + return new WrappedLine(elem); + } + } + + /** + * Renders the <code>Element</code> that is associated with this + * <code>View</code>. Caches the metrics and then calls + * super.paint to paint all the child views. + * + * @param g the <code>Graphics</code> context to render to + * @param a the allocated region for the <code>Element</code> + */ + public void paint(Graphics g, Shape a) + { + JTextComponent comp = (JTextComponent)getContainer(); + selectionStart = comp.getSelectionStart(); + selectionEnd = comp.getSelectionEnd(); + updateMetrics(); + super.paint(g, a); + } + + /** + * Sets the size of the View. Implemented to update the metrics + * and then call super method. + */ + public void setSize (float width, float height) + { + updateMetrics(); + if (width != getWidth()) + preferenceChanged(null, true, true); + super.setSize(width, height); + } + + class WrappedLine extends View + { + /** Used to cache the number of lines for this View **/ + int numLines; + + public WrappedLine(Element elem) + { + super(elem); + determineNumLines(); + } + + /** + * Renders this (possibly wrapped) line using the given Graphics object + * and on the given rendering surface. + */ + public void paint(Graphics g, Shape s) + { + // Ensure metrics are up-to-date. + updateMetrics(); + JTextComponent textComponent = (JTextComponent) getContainer(); + + g.setFont(textComponent.getFont()); + selectedColor = textComponent.getSelectedTextColor(); + unselectedColor = textComponent.getForeground(); + disabledColor = textComponent.getDisabledTextColor(); + + // FIXME: this is a hack, for some reason textComponent.getSelectedColor + // was returning black, which is not visible against a black background + selectedColor = Color.WHITE; + + Rectangle rect = s.getBounds(); + int lineHeight = metrics.getHeight(); + + int end = getEndOffset(); + int currStart = getStartOffset(); + int currEnd; + while (currStart < end) + { + currEnd = calculateBreakPosition(currStart, end); + drawLine(currStart, currEnd, g, rect.x, rect.y); + rect.y += lineHeight; + if (currEnd == currStart) + currStart ++; + else + currStart = currEnd; + } + } + + /** + * Determines the number of logical lines that the Element + * needs to be displayed + * @return the number of lines needed to display the Element + */ + int determineNumLines() + { + numLines = 0; + int end = getEndOffset(); + if (end == 0) + return 0; + + int breakPoint; + for (int i = getStartOffset(); i < end;) + { + numLines ++; + // careful: check that there's no off-by-one problem here + // depending on which position calculateBreakPosition returns + breakPoint = calculateBreakPosition(i, end); + if (breakPoint == i) + i ++; + else + i = breakPoint; + } + return numLines; + } + + /** + * Determines the preferred span for this view along the given axis. + * + * @param axis the axis (either X_AXIS or Y_AXIS) + * + * @return the preferred span along the given axis. + * @throws IllegalArgumentException if axis is not X_AXIS or Y_AXIS + */ + public float getPreferredSpan(int axis) + { + if (axis == X_AXIS) + return getWidth(); + else if (axis == Y_AXIS) + return numLines * metrics.getHeight(); + + throw new IllegalArgumentException("Invalid axis for getPreferredSpan: " + + axis); + } + + /** + * Provides a mapping from model space to view space. + * + * @param pos the position in the model + * @param a the region into which the view is rendered + * @param b the position bias (forward or backward) + * + * @return a box in view space that represents the given position + * in model space + * @throws BadLocationException if the given model position is invalid + */ + public Shape modelToView(int pos, Shape a, Bias b) + throws BadLocationException + { + Segment s = getLineBuffer(); + int lineHeight = metrics.getHeight(); + Rectangle rect = a.getBounds(); + + // Return a rectangle with width 1 and height equal to the height + // of the text + rect.height = lineHeight; + rect.width = 1; + + int currLineStart = getStartOffset(); + int end = getEndOffset(); + + if (pos < currLineStart || pos >= end) + throw new BadLocationException("invalid offset", pos); + + while (true) + { + int currLineEnd = calculateBreakPosition(currLineStart, end); + // If pos is between currLineStart and currLineEnd then just find + // the width of the text from currLineStart to pos and add that + // to rect.x + if (pos >= currLineStart && pos < currLineEnd || pos == end - 1) + { + try + { + getDocument().getText(currLineStart, pos - currLineStart, s); + } + catch (BadLocationException ble) + { + // Shouldn't happen + } + rect.x += Utilities.getTabbedTextWidth(s, metrics, rect.x, + WrappedPlainView.this, + currLineStart); + return rect; + } + // Increment rect.y so we're checking the next logical line + rect.y += lineHeight; + + // Increment currLineStart to the model position of the start + // of the next logical line + if (currLineEnd == currLineStart) + currLineStart = end; + else + currLineStart = currLineEnd; + } + + } + + /** + * Provides a mapping from view space to model space. + * + * @param x the x coordinate in view space + * @param y the y coordinate in view space + * @param a the region into which the view is rendered + * @param b the position bias (forward or backward) + * + * @return the location in the model that best represents the + * given point in view space + */ + public int viewToModel(float x, float y, Shape a, Bias[] b) + { + Segment s = getLineBuffer(); + Rectangle rect = a.getBounds(); + int currLineStart = getStartOffset(); + int end = getEndOffset(); + int lineHeight = metrics.getHeight(); + if (y < rect.y) + return currLineStart; + if (y > rect.y + rect.height) + return end - 1; + + while (true) + { + int currLineEnd = calculateBreakPosition(currLineStart, end); + // If we're at the right y-position that means we're on the right + // logical line and we should look for the character + if (y >= rect.y && y < rect.y + lineHeight) + { + // Check if the x position is to the left or right of the text + if (x < rect.x) + return currLineStart; + if (x > rect.x + rect.width) + return currLineEnd - 1; + + try + { + getDocument().getText(currLineStart, end - currLineStart, s); + } + catch (BadLocationException ble) + { + // Shouldn't happen + } + int mark = Utilities.getTabbedTextOffset(s, metrics, rect.x, + (int) x, + WrappedPlainView.this, + currLineStart); + return currLineStart + mark; + } + // Increment rect.y so we're checking the next logical line + rect.y += lineHeight; + + // Increment currLineStart to the model position of the start + // of the next logical line + if (currLineEnd == currLineStart) + currLineStart = end; + else + currLineStart = currLineEnd; + } + } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset + * in the document model + */ + public int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException + { + // TODO: Implement this properly. + throw new AssertionError("Not implemented yet."); + } + + /** + * This method is called from insertUpdate and removeUpdate. + * If the number of lines in the document has changed, just repaint + * the whole thing (note, could improve performance by not repainting + * anything above the changes). If the number of lines hasn't changed, + * just repaint the given Rectangle. + * @param a the Rectangle to repaint if the number of lines hasn't changed + */ + void updateDamage (Rectangle a) + { + int newNumLines = determineNumLines(); + if (numLines != newNumLines) + { + numLines = newNumLines; + getContainer().repaint(); + } + else + getContainer().repaint(a.x, a.y, a.width, a.height); + } + + /** + * This method is called when something is inserted into the Document + * that this View is displaying. + * + * @param changes the DocumentEvent for the changes. + * @param a the allocation of the View + * @param f the ViewFactory used to rebuild + */ + public void insertUpdate (DocumentEvent changes, Shape a, ViewFactory f) + { + updateDamage((Rectangle)a); + } + + /** + * This method is called when something is removed from the Document + * that this View is displaying. + * + * @param changes the DocumentEvent for the changes. + * @param a the allocation of the View + * @param f the ViewFactory used to rebuild + */ + public void removeUpdate (DocumentEvent changes, Shape a, ViewFactory f) + { + updateDamage((Rectangle)a); + } + } +} diff --git a/libjava/classpath/javax/swing/text/html/CSS.java b/libjava/classpath/javax/swing/text/html/CSS.java new file mode 100644 index 00000000000..029ad26f8a9 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/CSS.java @@ -0,0 +1,461 @@ +/* CSS.java -- Provides CSS attributes + 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.text.html; + +import java.util.HashMap; + +/** + * Provides CSS attributes to be used by the HTML view classes. The constants + * defined here are used as keys for text attributes for use in + * {@link javax.swing.text.AttributeSet}s of {@link javax.swing.text.Element}s. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class CSS +{ + /** + * Returns an array of all CSS attributes. + * + * @return All available CSS.Attribute objects. + */ + public static CSS.Attribute[] getAllAttributeKeys() + { + Object[] src = Attribute.attributeMap.values().toArray(); + CSS.Attribute[] dst = new CSS.Attribute[ src.length ]; + System.arraycopy(src, 0, dst, 0, src.length); + return dst; + } + + /** + * Returns an a given CSS attribute. + * + * @param name - The name of the attribute. + * @return The CSS attribute with the given name, or <code>null</code> if + * no attribute with that name exists. + */ + public static CSS.Attribute getAttribute(String name) + { + return (CSS.Attribute)Attribute.attributeMap.get( name ); + } + + public static final class Attribute + { + /** + * The CSS attribute 'background'. + */ + public static final Attribute BACKGROUND = + new Attribute("background", false, null); + + /** + * The CSS attribute 'background-attachment'. + */ + public static final Attribute BACKGROUND_ATTACHMENT = + new Attribute("background-attachment", false, "scroll"); + + /** + * The CSS attribute 'background-color'. + */ + public static final Attribute BACKGROUND_COLOR = + new Attribute("background-color", false, "transparent"); + + /** + * The CSS attribute 'background-image'. + */ + public static final Attribute BACKGROUND_IMAGE = + new Attribute("background-image", false, "none"); + + /** + * The CSS attribute 'background-position'. + */ + public static final Attribute BACKGROUND_POSITION = + new Attribute("background-position", false, null); + + /** + * The CSS attribute 'background-repeat'. + */ + public static final Attribute BACKGROUND_REPEAT = + new Attribute("background-repeat", false, "repeat"); + + /** + * The CSS attribute 'border'. + */ + public static final Attribute BORDER = new Attribute("border", false, null); + + /** + * The CSS attribute 'border-bottom'. + */ + public static final Attribute BORDER_BOTTOM = + new Attribute("border-bottom", false, null); + + /** + * The CSS attribute 'border-bottom-width'. + */ + public static final Attribute BORDER_BOTTOM_WIDTH = + new Attribute("border-bottom-width", false, "medium"); + + /** + * The CSS attribute 'border-color'. + */ + public static final Attribute BORDER_COLOR = + new Attribute("border-color", false, "black"); + + /** + * The CSS attribute 'border-left'. + */ + public static final Attribute BORDER_LEFT = + new Attribute("border-left", false, null); + + /** + * The CSS attribute 'border-left-width'. + */ + public static final Attribute BORDER_LEFT_WIDTH = + new Attribute("border-left-width", false, "medium"); + + /** + * The CSS attribute 'border-right'. + */ + public static final Attribute BORDER_RIGHT = + new Attribute("border-right", false, null); + + /** + * The CSS attribute 'border-right-width'. + */ + public static final Attribute BORDER_RIGHT_WIDTH = + new Attribute("border-right-width", false, "medium"); + + /** + * The CSS attribute 'border-style'. + */ + public static final Attribute BORDER_STYLE = + new Attribute("border-style", false, "none"); + + /** + * The CSS attribute 'border-top'. + */ + public static final Attribute BORDER_TOP = + new Attribute("border-top", false, null); + + /** + * The CSS attribute 'border-top-width'. + */ + public static final Attribute BORDER_TOP_WIDTH = + new Attribute("border-top-width", false, "medium"); + + /** + * The CSS attribute 'border-width'. + */ + public static final Attribute BORDER_WIDTH = + new Attribute("border-width", false, "medium"); + + /** + * The CSS attribute 'clear'. + */ + public static final Attribute CLEAR = new Attribute("clear", false, "none"); + + /** + * The CSS attribute 'color'. + */ + public static final Attribute COLOR = new Attribute("color", true, "black"); + + /** + * The CSS attribute 'display'. + */ + public static final Attribute DISPLAY = + new Attribute("display", false, "block"); + + /** + * The CSS attribute 'float'. + */ + public static final Attribute FLOAT = new Attribute("float", false, "none"); + + /** + * The CSS attribute 'font'. + */ + public static final Attribute FONT = new Attribute("font", true, null); + + /** + * The CSS attribute 'font-family'. + */ + public static final Attribute FONT_FAMILY = + new Attribute("font-family", true, null); + + /** + * The CSS attribute 'font-size'. + */ + public static final Attribute FONT_SIZE = + new Attribute("font-size", true, "medium"); + + /** + * The CSS attribute 'font-style'. + */ + public static final Attribute FONT_STYLE = + new Attribute("font-style", true, "normal"); + + /** + * The CSS attribute 'font-variant'. + */ + public static final Attribute FONT_VARIANT = + new Attribute("font-variant", true, "normal"); + + /** + * The CSS attribute 'font-weight'. + */ + public static final Attribute FONT_WEIGHT = + new Attribute("font-weight", true, "normal"); + + /** + * The CSS attribute 'height'. + */ + public static final Attribute HEIGHT = + new Attribute("height", false, "auto"); + + /** + * The CSS attribute 'letter-spacing'. + */ + public static final Attribute LETTER_SPACING = + new Attribute("letter-spacing", true, "normal"); + + /** + * The CSS attribute 'line-height'. + */ + public static final Attribute LINE_HEIGHT = + new Attribute("line-height", true, "normal"); + + /** + * The CSS attribute 'list-style'. + */ + public static final Attribute LIST_STYLE = + new Attribute("list-style", true, null); + + /** + * The CSS attribute 'list-style-image'. + */ + public static final Attribute LIST_STYLE_IMAGE = + new Attribute("list-style-image", true, "none"); + + /** + * The CSS attribute 'list-style-position'. + */ + public static final Attribute LIST_STYLE_POSITION = + new Attribute("list-style-position", true, "outside"); + + /** + * The CSS attribute 'list-style-type'. + */ + public static final Attribute LIST_STYLE_TYPE = + new Attribute("list-style-type", true, "disc"); + + /** + * The CSS attribute 'margin'. + */ + public static final Attribute MARGIN = new Attribute("margin", false, null); + + /** + * The CSS attribute 'margin-bottom'. + */ + public static final Attribute MARGIN_BOTTOM = + new Attribute("margin-bottom", false, "0"); + + /** + * The CSS attribute 'margin-left'. + */ + public static final Attribute MARGIN_LEFT = + new Attribute("margin-left", false, "0"); + + /** + * The CSS attribute 'margin-right'. + */ + public static final Attribute MARGIN_RIGHT = + new Attribute("margin-right", false, "0"); + + /** + * The CSS attribute 'margin-top'. + */ + public static final Attribute MARGIN_TOP = + new Attribute("margin-top", false, "0"); + + /** + * The CSS attribute 'padding'. + */ + public static final Attribute PADDING = + new Attribute("padding", false, null); + + /** + * The CSS attribute 'padding-bottom'. + */ + public static final Attribute PADDING_BOTTOM = + new Attribute("padding-bottom", false, "0"); + + /** + * The CSS attribute 'padding-left'. + */ + public static final Attribute PADDING_LEFT = + new Attribute("padding-left", false, "0"); + + /** + * The CSS attribute 'padding-right'. + */ + public static final Attribute PADDING_RIGHT = + new Attribute("padding-right", false, "0"); + + /** + * The CSS attribute 'padding-top'. + */ + public static final Attribute PADDING_TOP = + new Attribute("padding-top", false, "0"); + + /** + * The CSS attribute 'text-align'. + */ + public static final Attribute TEXT_ALIGN = + new Attribute("text-align", true, null); + + /** + * The CSS attribute 'text-decoration'. + */ + public static final Attribute TEXT_DECORATION = + new Attribute("text-decoration", true, "none"); + + /** + * The CSS attribute 'text-indent'. + */ + public static final Attribute TEXT_INDENT = + new Attribute("text-indent", true, "0"); + + /** + * The CSS attribute 'text-transform'. + */ + public static final Attribute TEXT_TRANSFORM = + new Attribute("text-transform", true, "none"); + + /** + * The CSS attribute 'vertical-align'. + */ + public static final Attribute VERTICAL_ALIGN = + new Attribute("vertical-align", false, "baseline"); + + /** + * The CSS attribute 'white-space'. + */ + public static final Attribute WHITE_SPACE = + new Attribute("white-space", true, "normal"); + + /** + * The CSS attribute 'width'. + */ + public static final Attribute WIDTH = + new Attribute("width", false, "auto"); + + /** + * The CSS attribute 'word-spacing'. + */ + public static final Attribute WORD_SPACING = + new Attribute("word-spacing", true, "normal"); + + /** + * The attribute string. + */ + String attStr; + + /** + * Indicates if this attribute should be inherited from it's parent or + * not. + */ + boolean isInherited; + + /** + * A default value for this attribute if one exists, otherwise null. + */ + String defaultValue; + + /** + * A HashMap of all attributes. + */ + static HashMap attributeMap; + + /** + * Creates a new Attribute instance with the specified values. + * + * @param attr the attribute string + * @param inherited if the attribute should be inherited or not + * @param def a default value; may be <code>null</code> + */ + Attribute(String attr, boolean inherited, String def) + { + attStr = attr; + isInherited = inherited; + defaultValue = def; + if( attributeMap == null) + attributeMap = new HashMap(); + attributeMap.put( attr, this ); + } + + /** + * Returns the string representation of this attribute as specified + * in the CSS specification. + */ + public String toString() + { + return attStr; + } + + /** + * Returns <code>true</code> if the attribute should be inherited from + * the parent, <code>false</code> otherwise. + * + * @return <code>true</code> if the attribute should be inherited from + * the parent, <code>false</code> otherwise + */ + public boolean isInherited() + { + return isInherited; + } + + /** + * Returns the default value of this attribute if one exists, + * <code>null</code> otherwise. + * + * @return the default value of this attribute if one exists, + * <code>null</code> otherwise + */ + public String getDefaultValue() + { + return defaultValue; + } + } +} diff --git a/libjava/classpath/javax/swing/text/html/HTML.java b/libjava/classpath/javax/swing/text/html/HTML.java index 3c03a63a471..0b758d2b873 100644 --- a/libjava/classpath/javax/swing/text/html/HTML.java +++ b/libjava/classpath/javax/swing/text/html/HTML.java @@ -945,22 +945,22 @@ public class HTML * This tag is not included into the array, returned by getAllTags(). * toString() returns 'comment'. HTML reader synthesizes this tag. */ - public static final Tag COMMENT = new Tag("comment", SYNTETIC); + public static final Tag COMMENT = new Tag("comment", SYNTHETIC); /** * All text content is labeled with this tag. * This tag is not included into the array, returned by getAllTags(). * toString() returns 'content'. HTML reader synthesizes this tag. */ - public static final Tag CONTENT = new Tag("content", SYNTETIC); + public static final Tag CONTENT = new Tag("content", SYNTHETIC); /** * All text content must be in a paragraph element. * If a paragraph didn't exist when content was encountered, * a paragraph is manufactured. - * toString() returns 'implied'. HTML reader synthesizes this tag. + * toString() returns 'p-implied'. HTML reader synthesizes this tag. */ - public static final Tag IMPLIED = new Tag("implied", SYNTETIC); + public static final Tag IMPLIED = new Tag("p-implied", SYNTHETIC); final String name; final int flags; @@ -1144,7 +1144,7 @@ public class HTML */ boolean isSyntetic() { - return (flags & SYNTETIC) != 0; + return (flags & SYNTHETIC) != 0; } private static void unexpected(Exception ex) @@ -1185,7 +1185,7 @@ public class HTML static final int BREAKS = 1; static final int BLOCK = 2; static final int PREFORMATTED = 4; - static final int SYNTETIC = 8; + static final int SYNTHETIC = 8; private static Map tagMap; private static Map attrMap; @@ -1196,6 +1196,7 @@ public class HTML */ public HTML() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/text/html/HTMLDocument.java b/libjava/classpath/javax/swing/text/html/HTMLDocument.java index a95e496ec4d..d048a04e614 100644 --- a/libjava/classpath/javax/swing/text/html/HTMLDocument.java +++ b/libjava/classpath/javax/swing/text/html/HTMLDocument.java @@ -38,7 +38,14 @@ exception statement from your version. */ package javax.swing.text.html; +import java.net.URL; + +import javax.swing.text.AbstractDocument; +import javax.swing.text.AttributeSet; import javax.swing.text.DefaultStyledDocument; +import javax.swing.text.Element; +import javax.swing.text.ElementIterator; +import javax.swing.text.html.HTML.Tag; /** * TODO: This class is not yet completetely implemented. @@ -47,7 +54,215 @@ import javax.swing.text.DefaultStyledDocument; */ public class HTMLDocument extends DefaultStyledDocument { + /** A key for document properies. The value for the key is + * a Vector of Strings of comments not found in the body. + */ + public static final String AdditionalComments = "AdditionalComments"; + URL baseURL = null; + boolean preservesUnknownTags = true; + + /** + * Returns the location against which to resolve relative URLs. + * This is the document's URL if the document was loaded from a URL. + * If a <code>base</code> tag is found, it will be used. + * @return the base URL + */ + public URL getBase() + { + return baseURL; + } + + /** + * Sets the location against which to resolve relative URLs. + * @param u the new base URL + */ + public void setBase(URL u) + { + baseURL = u; + //TODO: also set the base of the StyleSheet + } + + /** + * Returns whether or not the parser preserves unknown HTML tags. + * @return true if the parser preserves unknown tags + */ + public boolean getPreservesUnknownTags() + { + return preservesUnknownTags; + } + + /** + * Sets the behaviour of the parser when it encounters unknown HTML tags. + * @param preservesTags true if the parser should preserve unknown tags. + */ + public void setPreservesUnknownTags(boolean preservesTags) + { + preservesUnknownTags = preservesTags; + } + + /** + * An iterator to iterate through LeafElements in the document. + */ + class LeafIterator extends Iterator + { + HTML.Tag tag; + HTMLDocument doc; + ElementIterator it; + + public LeafIterator (HTML.Tag t, HTMLDocument d) + { + doc = d; + tag = t; + it = new ElementIterator(doc); + } + + /** + * Return the attributes for the tag associated with this iteartor + * @return the AttributeSet + */ + public AttributeSet getAttributes() + { + if (it.current() != null) + return it.current().getAttributes(); + return null; + } + + /** + * Get the end of the range for the current occurrence of the tag + * being defined and having the same attributes. + * @return the end of the range + */ + public int getEndOffset() + { + if (it.current() != null) + return it.current().getEndOffset(); + return -1; + } + + /** + * Get the start of the range for the current occurrence of the tag + * being defined and having the same attributes. + * @return the start of the range (-1 if it can't be found). + */ + + public int getStartOffset() + { + if (it.current() != null) + return it.current().getStartOffset(); + return -1; + } + + /** + * Advance the iterator to the next LeafElement . + */ + public void next() + { + it.next(); + while (it.current()!= null && !it.current().isLeaf()) + it.next(); + } + + /** + * Indicates whether or not the iterator currently represents an occurrence + * of the tag. + * @return true if the iterator currently represents an occurrence of the + * tag. + */ + public boolean isValid() + { + return it.current() != null; + } + + /** + * Type of tag for this iterator. + */ + public Tag getTag() + { + return tag; + } + + } + public void processHTMLFrameHyperlinkEvent(HTMLFrameHyperlinkEvent event) { + // TODO: Implement this properly. + } + + /** + * Gets an iterator for the given HTML.Tag. + * @param t the requested HTML.Tag + * @return the Iterator + */ + public HTMLDocument.Iterator getIterator (HTML.Tag t) + { + return new HTMLDocument.LeafIterator(t, this); + } + + /** + * An iterator over a particular type of tag. + */ + public abstract static class Iterator + { + /** + * Return the attribute set for this tag. + * @return the <code>AttributeSet</code> (null if none found). + */ + public abstract AttributeSet getAttributes(); + + /** + * Get the end of the range for the current occurrence of the tag + * being defined and having the same attributes. + * @return the end of the range + */ + public abstract int getEndOffset(); + + /** + * Get the start of the range for the current occurrence of the tag + * being defined and having the same attributes. + * @return the start of the range (-1 if it can't be found). + */ + public abstract int getStartOffset(); + + /** + * Move the iterator forward. + */ + public abstract void next(); + + /** + * Indicates whether or not the iterator currently represents an occurrence + * of the tag. + * @return true if the iterator currently represents an occurrence of the + * tag. + */ + public abstract boolean isValid(); + + /** + * Type of tag this iterator represents. + * @return the tag. + */ + public abstract HTML.Tag getTag(); + } + + public class BlockElement extends AbstractDocument.BranchElement + { + public BlockElement (Element parent, AttributeSet a) + { + super (parent, a); + } + + /** + * Gets the resolving parent. Since HTML attributes are not + * inherited at the model level, this returns null. + */ + public AttributeSet getResolveParent() + { + return null; + } + + public String getName() + { + //FIXME: this is supposed to do something different from the super class + return super.getName(); + } } } diff --git a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java index c0182fe6ac9..5189c777539 100644 --- a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java +++ b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java @@ -43,8 +43,10 @@ import java.io.Reader; import java.io.Serializable; import javax.swing.text.BadLocationException; +import javax.swing.text.Document; import javax.swing.text.MutableAttributeSet; import javax.swing.text.StyledEditorKit; +import javax.swing.text.html.parser.ParserDelegator; /** * This class is NOT implemented. This file currently holds only @@ -96,9 +98,9 @@ public class HTMLEditorKit /** * The parser calls this method after it finishes parsing the document. */ - public void flush() - throws BadLocationException + public void flush() throws BadLocationException { + // TODO: What to do here, if anything? } /** @@ -108,6 +110,7 @@ public class HTMLEditorKit */ public void handleComment(char[] comment, int position) { + // TODO: What to do here, if anything? } /** @@ -118,6 +121,7 @@ public class HTMLEditorKit */ public void handleEndOfLineString(String end_of_line) { + // TODO: What to do here, if anything? } /** @@ -129,6 +133,7 @@ public class HTMLEditorKit */ public void handleEndTag(HTML.Tag tag, int position) { + // TODO: What to do here, if anything? } /** @@ -139,6 +144,7 @@ public class HTMLEditorKit */ public void handleError(String message, int position) { + // TODO: What to do here, if anything? } /** @@ -149,9 +155,9 @@ public class HTMLEditorKit * @param position The tag position in the text being parsed. */ public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet attributes, - int position - ) + int position) { + // TODO: What to do here, if anything? } /** @@ -165,6 +171,7 @@ public class HTMLEditorKit int position ) { + // TODO: What to do here, if anything? } /** @@ -174,6 +181,7 @@ public class HTMLEditorKit */ public void handleText(char[] text, int position) { + // TODO: What to do here, if anything? } } @@ -247,4 +255,26 @@ public class HTMLEditorKit * The "ident paragraph right" action. */ public static final String PARA_INDENT_RIGHT = "html-para-indent-right"; + + /** + * Create a text storage model for this type of editor. + * + * @return the model + */ + public Document createDefaultDocument() + { + HTMLDocument document = new HTMLDocument(); + return document; + } + + /** + * Get the parser that this editor kit uses for reading HTML streams. This + * method can be overridden to use the alternative parser. + * + * @return the HTML parser (by default, {@link ParserDelegator}). + */ + protected Parser getParser() + { + return new ParserDelegator(); + } }
\ No newline at end of file diff --git a/libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java b/libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java index dc0ab10a8f7..e146965d778 100644 --- a/libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java +++ b/libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java @@ -41,7 +41,6 @@ package javax.swing.text.html; import java.net.URL; import javax.swing.event.HyperlinkEvent; -import javax.swing.event.HyperlinkEvent.EventType; import javax.swing.text.Element; /** @@ -50,8 +49,7 @@ import javax.swing.text.Element; * * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) */ -public class HTMLFrameHyperlinkEvent - extends HyperlinkEvent +public class HTMLFrameHyperlinkEvent extends HyperlinkEvent { private final String target_frame; diff --git a/libjava/classpath/javax/swing/text/html/parser/ContentModel.java b/libjava/classpath/javax/swing/text/html/parser/ContentModel.java index deb7b1602bb..70e9c2acbff 100644 --- a/libjava/classpath/javax/swing/text/html/parser/ContentModel.java +++ b/libjava/classpath/javax/swing/text/html/parser/ContentModel.java @@ -95,9 +95,12 @@ public final class ContentModel */ public int type; - /** Create a content model initializing all fields to default values. */ + /** + * Create a content model initializing all fields to default values. + */ public ContentModel() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/text/html/parser/DTD.java b/libjava/classpath/javax/swing/text/html/parser/DTD.java index f17ca011ea0..16bc5b0d6af 100644 --- a/libjava/classpath/javax/swing/text/html/parser/DTD.java +++ b/libjava/classpath/javax/swing/text/html/parser/DTD.java @@ -81,8 +81,9 @@ public class DTD { /** * The version of the persistent data format. + * @specnote This was made <code>final</code> in 1.5. */ - public static int FILE_VERSION = 1; + public static final int FILE_VERSION = 1; /** * The table of existing available DTDs. @@ -590,8 +591,7 @@ public class DTD * @param name the name of the entity * @param type the type of the entity, a bitwise combination * of GENERAL, PARAMETER, SYSTEM and PUBLIC. - * @throws an error if the parameter is both GENERAL and PARAMETER - * of both PUBLIC and SYSTEM. + * * @return the created entity */ private Entity newEntity(String name, int type) diff --git a/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java b/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java index 164297f1882..062606d17ba 100644 --- a/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java +++ b/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java @@ -168,6 +168,7 @@ public class DocumentParser * specific packages, write your own DTD or obtain the working instance * of parser in other way, for example, by calling * {@link javax.swing.text.html.HTMLEditorKit#getParser()}. + * * @param a_dtd a DTD to use. */ public DocumentParser(DTD a_dtd) @@ -212,6 +213,7 @@ public class DocumentParser */ protected void handleComment(char[] comment) { + // This default implementation does nothing. } /** @@ -224,6 +226,7 @@ public class DocumentParser protected void handleEmptyTag(TagElement tag) throws javax.swing.text.ChangedCharSetException { + // This default implementation does nothing. } /** @@ -234,11 +237,13 @@ public class DocumentParser */ protected void handleEndTag(TagElement tag) { + // This default implementation does nothing. } /* Handle error that has occured in the given line. */ protected void handleError(int line, String message) { + // This default implementation does nothing. } /** @@ -249,6 +254,7 @@ public class DocumentParser */ protected void handleStartTag(TagElement tag) { + // This default implementation does nothing. } /** @@ -257,5 +263,6 @@ public class DocumentParser */ protected void handleText(char[] text) { + // This default implementation does nothing. } } diff --git a/libjava/classpath/javax/swing/text/html/parser/Element.java b/libjava/classpath/javax/swing/text/html/parser/Element.java index 098983c6923..c07c07f5426 100644 --- a/libjava/classpath/javax/swing/text/html/parser/Element.java +++ b/libjava/classpath/javax/swing/text/html/parser/Element.java @@ -148,10 +148,10 @@ public final class Element /** * The default constructor must have package level access in this * class. Use DTD.defineElement(..) to create an element when required. - * @todo MAKE THIS PACKAGE in the final version. Now the Parser needs it! */ Element() { + // Nothing to do here. } /** diff --git a/libjava/classpath/javax/swing/text/html/parser/Parser.java b/libjava/classpath/javax/swing/text/html/parser/Parser.java index 7ff6853da82..a88e9ce1953 100644 --- a/libjava/classpath/javax/swing/text/html/parser/Parser.java +++ b/libjava/classpath/javax/swing/text/html/parser/Parser.java @@ -256,6 +256,7 @@ public class Parser */ protected void endTag(boolean omitted) { + // This default implementation does nothing. } /** @@ -310,6 +311,7 @@ public class Parser */ protected void handleComment(char[] comment) { + // This default implementation does nothing. } /** @@ -333,6 +335,7 @@ public class Parser protected void handleEmptyTag(TagElement tag) throws ChangedCharSetException { + // This default implementation does nothing. } /** @@ -343,11 +346,13 @@ public class Parser */ protected void handleEndTag(TagElement tag) { + // This default implementation does nothing. } /* Handle error that has occured in the given line. */ protected void handleError(int line, String message) { + // This default implementation does nothing. } /** @@ -358,6 +363,7 @@ public class Parser */ protected void handleStartTag(TagElement tag) { + // This default implementation does nothing. } /** @@ -376,6 +382,7 @@ public class Parser */ protected void handleText(char[] text) { + // This default implementation does nothing. } /** @@ -387,6 +394,7 @@ public class Parser */ protected void handleTitle(char[] title) { + // This default implementation does nothing. } /** @@ -420,6 +428,7 @@ public class Parser */ protected void markFirstTime(Element element) { + // This default implementation does nothing. } /** @@ -432,5 +441,6 @@ public class Parser protected void startTag(TagElement tag) throws ChangedCharSetException { + // This default implementation does nothing. } } diff --git a/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java b/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java index e709e2a0449..d9747729317 100644 --- a/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java +++ b/libjava/classpath/javax/swing/tree/DefaultMutableTreeNode.java @@ -45,7 +45,6 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; -import java.util.Collections; import java.util.Enumeration; import java.util.LinkedList; import java.util.NoSuchElementException; diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java b/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java index 7a44e738338..2891a778ee9 100644 --- a/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java +++ b/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java @@ -47,7 +47,6 @@ import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; import java.awt.Rectangle; -import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; @@ -56,12 +55,8 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.EventObject; -import javax.swing.CellRendererPane; import javax.swing.DefaultCellEditor; import javax.swing.Icon; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JComponent; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.SwingUtilities; @@ -354,9 +349,9 @@ public class DefaultTreeCellEditor /** * Configures the editing component whenever it is null. * - * @param tree- the tree to configure to component for. - * @param renderer- the renderer used to set up the nodes - * @param editor- the editor used + * @param tree the tree to configure to component for. + * @param renderer the renderer used to set up the nodes + * @param editor the editor used */ private void configureEditingComponent(JTree tree, DefaultTreeCellRenderer renderer, @@ -513,6 +508,8 @@ public class DefaultTreeCellEditor // Cell may not be currently editable, but may need to start timer. if (shouldStartEditingTimer(event)) startEditingTimer(); + else if (timer.isRunning()) + timer.stop(); return false; } @@ -605,7 +602,7 @@ public class DefaultTreeCellEditor /** * Messaged when the timer fires, this will start the editing session. * - * @param @param e - the event that characterizes the action. + * @param e the event that characterizes the action. */ public void actionPerformed(ActionEvent e) { diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java b/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java index 4a353b30176..d1cb9c0e8b7 100644 --- a/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java +++ b/libjava/classpath/javax/swing/tree/DefaultTreeCellRenderer.java @@ -419,8 +419,7 @@ public class DefaultTreeCellRenderer super.setBackground(getBackgroundSelectionColor()); setForeground(getTextSelectionColor()); - if (tree.getLeadSelectionPath() == null || - (tree.getLeadSelectionPath().getLastPathComponent()).equals(val)) + if (hasFocus) setBorderSelectionColor(UIManager.getLookAndFeelDefaults(). getColor("Tree.selectionBorderColor")); else diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeModel.java b/libjava/classpath/javax/swing/tree/DefaultTreeModel.java index 5b5e0391478..5cf80986061 100644 --- a/libjava/classpath/javax/swing/tree/DefaultTreeModel.java +++ b/libjava/classpath/javax/swing/tree/DefaultTreeModel.java @@ -299,6 +299,7 @@ public class DefaultTreeModel public void insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent, int index) { + newChild.setParent(parent); parent.insert(newChild, index); int[] childIndices = new int[1]; childIndices[0] = index; diff --git a/libjava/classpath/javax/swing/tree/TreeCellRenderer.java b/libjava/classpath/javax/swing/tree/TreeCellRenderer.java index ebbe3fa9133..a1808c9ee91 100644 --- a/libjava/classpath/javax/swing/tree/TreeCellRenderer.java +++ b/libjava/classpath/javax/swing/tree/TreeCellRenderer.java @@ -46,22 +46,24 @@ import javax.swing.JTree; * TreeCellRenderer public interface * @author Andrew Selkirk */ -public interface TreeCellRenderer { +public interface TreeCellRenderer +{ - /** - * getTreeCellRendererComponent - * @param tree TODO - * @param value TODO - * @param selected TODO - * @param expanded TODO - * @param leaf TODO - * @param row TODO - * @param us TODO - * @returns TODO - */ - Component getTreeCellRendererComponent(JTree tree, - Object value, boolean selected, boolean expanded, - boolean leaf, int row, boolean hasFocus); + /** + * getTreeCellRendererComponent + * @param tree TODO + * @param value TODO + * @param selected TODO + * @param expanded TODO + * @param leaf TODO + * @param row TODO + * @param hasFocus TODO + * @returns TODO + */ + Component getTreeCellRendererComponent(JTree tree, Object value, + boolean selected, boolean expanded, + boolean leaf, int row, + boolean hasFocus); -} // TreeCellRenderer +} diff --git a/libjava/classpath/javax/swing/tree/TreeModel.java b/libjava/classpath/javax/swing/tree/TreeModel.java index 759aaac588c..ec1884efdb7 100644 --- a/libjava/classpath/javax/swing/tree/TreeModel.java +++ b/libjava/classpath/javax/swing/tree/TreeModel.java @@ -44,66 +44,62 @@ import javax.swing.event.TreeModelListener; * TreeModel public interface * @author Andrew Selkirk */ -public interface TreeModel { - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * getRoot - * @returns Object - */ - Object getRoot(); - - /** - * getChild - * @param parent TODO - * @param index TODO - * @returns Object - */ - Object getChild(Object parent, int index); - - /** - * getChildCount - * @param parent TODO - * @returns int - */ - int getChildCount(Object parent); - - /** - * isLeaf - * @param node TODO - * @returns boolean - */ - boolean isLeaf(Object node); - - /** - * valueForPathChanged - * @param path TODO - * @param newvalue TODO - */ - void valueForPathChanged(TreePath path, Object newvalue); - - /** - * getIndexOfChild - * @param parent TODO - * @param ild TODO - * @returns int - */ - int getIndexOfChild(Object parent, Object child); - - /** - * addTreeModelListener - * @param listener TODO - */ - void addTreeModelListener(TreeModelListener listener); - - /** - * removeTreeModelListener - * @param listener TODO - */ - void removeTreeModelListener(TreeModelListener listener); - - -} // TreeModel +public interface TreeModel +{ + /** + * getRoot + * @returns Object + */ + Object getRoot(); + + /** + * getChild + * @param parent TODO + * @param index TODO + * @returns Object + */ + Object getChild(Object parent, int index); + + /** + * getChildCount + * @param parent TODO + * @returns int + */ + int getChildCount(Object parent); + + /** + * isLeaf + * @param node TODO + * @returns boolean + */ + boolean isLeaf(Object node); + + /** + * valueForPathChanged + * @param path TODO + * @param newvalue TODO + */ + void valueForPathChanged(TreePath path, Object newvalue); + + /** + * getIndexOfChild + * @param parent TODO + * @param child TODO + * @returns int + */ + int getIndexOfChild(Object parent, Object child); + + /** + * addTreeModelListener + * @param listener TODO + */ + void addTreeModelListener(TreeModelListener listener); + + /** + * removeTreeModelListener + * @param listener TODO + */ + void removeTreeModelListener(TreeModelListener listener); + + +} diff --git a/libjava/classpath/javax/swing/undo/CannotRedoException.java b/libjava/classpath/javax/swing/undo/CannotRedoException.java index 7d70a38c2c8..5f22648350a 100644 --- a/libjava/classpath/javax/swing/undo/CannotRedoException.java +++ b/libjava/classpath/javax/swing/undo/CannotRedoException.java @@ -44,13 +44,13 @@ package javax.swing.undo; * @author Andrew Selkirk (aselkirk@sympatico.ca) * @author Sascha Brawer (brawer@dandelis.ch) */ -public class CannotRedoException - extends RuntimeException +public class CannotRedoException extends RuntimeException { /** * Constructs a new instance of a <code>CannotRedoException</code>. */ public CannotRedoException() { + super(); } } diff --git a/libjava/classpath/javax/swing/undo/CannotUndoException.java b/libjava/classpath/javax/swing/undo/CannotUndoException.java index 9fc0ec3bd3a..8d08cda6853 100644 --- a/libjava/classpath/javax/swing/undo/CannotUndoException.java +++ b/libjava/classpath/javax/swing/undo/CannotUndoException.java @@ -53,5 +53,6 @@ public class CannotUndoException */ public CannotUndoException() { + super(); } } |