diff options
author | Mark Wielaard <mark@gcc.gnu.org> | 2006-08-14 23:12:35 +0000 |
---|---|---|
committer | Mark Wielaard <mark@gcc.gnu.org> | 2006-08-14 23:12:35 +0000 |
commit | ac1ed908de999523efc36f38e69bca1aadfe0808 (patch) | |
tree | 97037d2c09c8384d80531f67ec36a01205df6bdb /libjava/classpath/javax/swing | |
parent | abab460491408e05ea93fb85e1975296a87df504 (diff) | |
download | gcc-ac1ed908de999523efc36f38e69bca1aadfe0808.tar.gz |
Imported GNU Classpath 0.92
2006-08-14 Mark Wielaard <mark@klomp.org>
Imported GNU Classpath 0.92
* HACKING: Add more importing hints. Update automake version
requirement.
* configure.ac (gconf-peer): New enable AC argument.
Add --disable-gconf-peer and --enable-default-preferences-peer
to classpath configure when gconf is disabled.
* scripts/makemake.tcl: Set gnu/java/util/prefs/gconf and
gnu/java/awt/dnd/peer/gtk to bc. Classify
gnu/java/security/Configuration.java as generated source file.
* gnu/java/lang/management/VMGarbageCollectorMXBeanImpl.java,
gnu/java/lang/management/VMMemoryPoolMXBeanImpl.java,
gnu/java/lang/management/VMClassLoadingMXBeanImpl.java,
gnu/java/lang/management/VMRuntimeMXBeanImpl.java,
gnu/java/lang/management/VMMemoryManagerMXBeanImpl.java,
gnu/java/lang/management/VMThreadMXBeanImpl.java,
gnu/java/lang/management/VMMemoryMXBeanImpl.java,
gnu/java/lang/management/VMCompilationMXBeanImpl.java: New VM stub
classes.
* java/lang/management/VMManagementFactory.java: Likewise.
* java/net/VMURLConnection.java: Likewise.
* gnu/java/nio/VMChannel.java: Likewise.
* java/lang/Thread.java (getState): Add stub implementation.
* java/lang/Class.java (isEnum): Likewise.
* java/lang/Class.h (isEnum): Likewise.
* gnu/awt/xlib/XToolkit.java (getClasspathTextLayoutPeer): Removed.
* javax/naming/spi/NamingManager.java: New override for StackWalker
functionality.
* configure, sources.am, Makefile.in, gcj/Makefile.in,
include/Makefile.in, testsuite/Makefile.in: Regenerated.
From-SVN: r116139
Diffstat (limited to 'libjava/classpath/javax/swing')
111 files changed, 8154 insertions, 3613 deletions
diff --git a/libjava/classpath/javax/swing/AbstractButton.java b/libjava/classpath/javax/swing/AbstractButton.java index 9b2b526f30b..63f827a1ae0 100644 --- a/libjava/classpath/javax/swing/AbstractButton.java +++ b/libjava/classpath/javax/swing/AbstractButton.java @@ -199,7 +199,7 @@ public abstract class AbstractButton extends JComponent Icon pressed_icon; /** The icon displayed when the button is disabled. */ - Icon disabeldIcon; + Icon disabledIcon; /** The icon displayed when the button is selected. */ Icon selectedIcon; @@ -923,7 +923,7 @@ public abstract class AbstractButton extends JComponent // constructor). // This way the behavior of the JDK is matched. if(text != null) - this.text = text; + setText(text); if (icon != null) default_icon = icon; @@ -1297,9 +1297,11 @@ public abstract class AbstractButton extends JComponent * alignment is a numeric constant from {@link SwingConstants}. It must * be one of: <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>, * <code>LEADING</code> or <code>TRAILING</code>. The default is - * <code>RIGHT</code>. + * <code>CENTER</code>. * * @return The current horizontal alignment + * + * @see #setHorizontalAlignment(int) */ public int getHorizontalAlignment() { @@ -1311,17 +1313,21 @@ public abstract class AbstractButton extends JComponent * alignment is a numeric constant from {@link SwingConstants}. It must * be one of: <code>RIGHT</code>, <code>LEFT</code>, <code>CENTER</code>, * <code>LEADING</code> or <code>TRAILING</code>. The default is - * <code>RIGHT</code>. + * <code>CENTER</code>. * * @param a The new horizontal alignment * @throws IllegalArgumentException If alignment is not one of the legal * constants. + * + * @see #getHorizontalAlignment() */ public void setHorizontalAlignment(int a) { if (horizontalAlignment == a) return; - + if (a != LEFT && a != CENTER && a != RIGHT && a != LEADING + && a != TRAILING) + throw new IllegalArgumentException("Invalid alignment."); int old = horizontalAlignment; horizontalAlignment = a; firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY, old, a); @@ -1358,6 +1364,9 @@ public abstract class AbstractButton extends JComponent { if (horizontalTextPosition == t) return; + if (t != LEFT && t != CENTER && t != RIGHT && t != LEADING + && t != TRAILING) + throw new IllegalArgumentException("Invalid alignment."); int old = horizontalTextPosition; horizontalTextPosition = t; @@ -1373,6 +1382,8 @@ public abstract class AbstractButton extends JComponent * <code>BOTTOM</code>. The default is <code>CENTER</code>. * * @return The current vertical alignment + * + * @see #setVerticalAlignment(int) */ public int getVerticalAlignment() { @@ -1388,12 +1399,16 @@ public abstract class AbstractButton extends JComponent * @param a The new vertical alignment * @throws IllegalArgumentException If alignment is not one of the legal * constants. + * + * @see #getVerticalAlignment() */ public void setVerticalAlignment(int a) { if (verticalAlignment == a) return; - + if (a != TOP && a != CENTER && a != BOTTOM) + throw new IllegalArgumentException("Invalid alignment."); + int old = verticalAlignment; verticalAlignment = a; firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, old, a); @@ -1430,6 +1445,8 @@ public abstract class AbstractButton extends JComponent { if (verticalTextPosition == t) return; + if (t != TOP && t != CENTER && t != BOTTOM) + throw new IllegalArgumentException("Invalid alignment."); int old = verticalTextPosition; verticalTextPosition = t; @@ -1708,14 +1725,14 @@ public abstract class AbstractButton extends JComponent */ public Icon getDisabledIcon() { - if (disabeldIcon == null && default_icon instanceof ImageIcon) + if (disabledIcon == null && default_icon instanceof ImageIcon) { Image iconImage = ((ImageIcon) default_icon).getImage(); Image grayImage = GrayFilter.createDisabledImage(iconImage); - disabeldIcon = new ImageIcon(grayImage); + disabledIcon = new ImageIcon(grayImage); } - return disabeldIcon; + return disabledIcon; } /** @@ -1729,7 +1746,11 @@ public abstract class AbstractButton extends JComponent */ public void setDisabledIcon(Icon d) { - disabeldIcon = d; + if (disabledIcon == d) + return; + Icon old = disabledIcon; + disabledIcon = d; + firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, old, d); revalidate(); repaint(); } @@ -2092,7 +2113,8 @@ public abstract class AbstractButton extends JComponent } /** - * Set the button's rollover icon. The look and feel class should + * Set the button's rollover icon and sets the <code>rolloverEnabled</code> + * property to <code>true</code>. The look and feel class should * paint this icon when the "rolloverEnabled" property of the button is * <code>true</code> and the mouse rolls over the button. * @@ -2106,6 +2128,7 @@ public abstract class AbstractButton extends JComponent Icon old = rolloverIcon; rolloverIcon = r; firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, old, rolloverIcon); + setRolloverEnabled(true); revalidate(); repaint(); } @@ -2124,12 +2147,13 @@ public abstract class AbstractButton extends JComponent } /** - * Set the button's rollover selected icon. The look and feel class - * should paint this icon when the "rolloverEnabled" property of the button - * is <code>true</code>, the "selected" property of the button's model is - * <code>true</code>, and the mouse rolls over the button. + * Set the button's rollover selected icon and sets the + * <code>rolloverEnabled</code> property to <code>true</code>. The look and + * feel class should paint this icon when the "rolloverEnabled" property of + * the button is <code>true</code>, the "selected" property of the button's + * model is <code>true</code>, and the mouse rolls over the button. * - * @param r The new rollover selected icon + * @param r The new rollover selected icon. */ public void setRolloverSelectedIcon(Icon r) { @@ -2139,6 +2163,7 @@ public abstract class AbstractButton extends JComponent Icon old = rolloverSelectedIcon; rolloverSelectedIcon = r; firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, old, r); + setRolloverEnabled(true); revalidate(); repaint(); } diff --git a/libjava/classpath/javax/swing/AbstractCellEditor.java b/libjava/classpath/javax/swing/AbstractCellEditor.java index df0d3db12b5..e993df5cfc7 100644 --- a/libjava/classpath/javax/swing/AbstractCellEditor.java +++ b/libjava/classpath/javax/swing/AbstractCellEditor.java @@ -134,9 +134,9 @@ public abstract class AbstractCellEditor * * @param listener the CellEditorListener to add */ - public void addCellEditorListener (CellEditorListener listener) + public void addCellEditorListener(CellEditorListener listener) { - listenerList.add (CellEditorListener.class, listener); + listenerList.add(CellEditorListener.class, listener); } /** @@ -145,9 +145,9 @@ public abstract class AbstractCellEditor * * @param listener the CellEditorListener to remove */ - public void removeCellEditorListener (CellEditorListener listener) + public void removeCellEditorListener(CellEditorListener listener) { - listenerList.remove (CellEditorListener.class, listener); + listenerList.remove(CellEditorListener.class, listener); } /** @@ -161,8 +161,8 @@ public abstract class AbstractCellEditor */ public CellEditorListener[] getCellEditorListeners() { - return (CellEditorListener[]) listenerList.getListeners - (CellEditorListener.class); + return (CellEditorListener[]) listenerList.getListeners( + CellEditorListener.class); } /** diff --git a/libjava/classpath/javax/swing/AbstractSpinnerModel.java b/libjava/classpath/javax/swing/AbstractSpinnerModel.java index 1a82f0a359d..089e2048da2 100644 --- a/libjava/classpath/javax/swing/AbstractSpinnerModel.java +++ b/libjava/classpath/javax/swing/AbstractSpinnerModel.java @@ -113,7 +113,7 @@ public abstract class AbstractSpinnerModel implements SpinnerModel { ChangeListener[] listeners = getChangeListeners(); - for(int i = 0; i < listeners.length; ++i) + for (int i = 0; i < listeners.length; ++i) listeners[i].stateChanged(changeEvent); } } diff --git a/libjava/classpath/javax/swing/Box.java b/libjava/classpath/javax/swing/Box.java index 57519f6fcbd..0f984a98465 100644 --- a/libjava/classpath/javax/swing/Box.java +++ b/libjava/classpath/javax/swing/Box.java @@ -196,9 +196,8 @@ public class Box extends JComponent implements Accessible */ public static Component createGlue() { - Filler glue = new Filler(new Dimension(0,0), new Dimension(0,0), - new Dimension(Short.MAX_VALUE,Short.MAX_VALUE) - ); + Filler glue = new Filler(new Dimension(0, 0), new Dimension(0, 0), + new Dimension(Short.MAX_VALUE, Short.MAX_VALUE)); return glue; } @@ -216,9 +215,8 @@ public class Box extends JComponent implements Accessible */ public static Component createHorizontalGlue() { - Filler glue = new Filler(new Dimension(0,0), new Dimension(0,0), - new Dimension(Short.MAX_VALUE, 0) - ); + Filler glue = new Filler(new Dimension(0, 0), new Dimension(0, 0), + new Dimension(Short.MAX_VALUE, 0)); return glue; } diff --git a/libjava/classpath/javax/swing/BoxLayout.java b/libjava/classpath/javax/swing/BoxLayout.java index 408dea934f8..a7f9dd61a6e 100644 --- a/libjava/classpath/javax/swing/BoxLayout.java +++ b/libjava/classpath/javax/swing/BoxLayout.java @@ -426,7 +426,7 @@ public class BoxLayout implements LayoutManager2, Serializable Insets in = container.getInsets(); int width = container.getWidth() - in.left - in.right; - int height = container.getHeight() - in.top -in.bottom; + int height = container.getHeight() - in.top - in.bottom; if (isHorizontalIn(container)) { diff --git a/libjava/classpath/javax/swing/ButtonGroup.java b/libjava/classpath/javax/swing/ButtonGroup.java index 2f8d19831cb..efa36b5f641 100644 --- a/libjava/classpath/javax/swing/ButtonGroup.java +++ b/libjava/classpath/javax/swing/ButtonGroup.java @@ -1,5 +1,5 @@ /* ButtonGroup.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -65,10 +65,9 @@ import java.util.Vector; */ public class ButtonGroup implements Serializable { - /** DOCUMENT ME! */ private static final long serialVersionUID = 4259076101881721375L; - /** The buttons added to this button group. */ + /** Stores references to the buttons added to this button group. */ protected Vector buttons = new Vector(); /** The currently selected button model. */ @@ -83,12 +82,20 @@ public class ButtonGroup implements Serializable } /** - * Adds a button to this group. + * Adds a button to this group. If the button is in the selected state, then: + * <ul> + * <li>if the group has no current selection, the new button becomes the + * selected button for the group;</li> + * <li>if the group already has a selected button, the new button is set to + * "not selected".</li> + * </ul> * - * @param b the button to add + * @param b the button to add (<code>null</code> is ignored). */ public void add(AbstractButton b) { + if (b == null) + return; b.getModel().setGroup(this); if (b.isSelected()) { @@ -96,17 +103,24 @@ public class ButtonGroup implements Serializable sel = b.getModel(); else b.setSelected(false); - } buttons.addElement(b); + } + buttons.addElement(b); } /** - * Removed a given button from this group. + * Removes the specified button from this group. If the button is the + * selected button, the current selection is set to <code>null</code>. + * The group for the removed button's model is set to <code>null</code>. * - * @param b the button to remove + * @param b the button to remove (<code>null</code> is ignored). */ public void remove(AbstractButton b) { + if (b == null) + return; b.getModel().setGroup(null); + if (b.getModel() == sel) + sel = null; buttons.removeElement(b); } @@ -132,19 +146,20 @@ public class ButtonGroup implements Serializable } /** - * DOCUMENT ME! + * Returns the button that has the specified model, or <code>null</code> if + * there is no such button in the group. * - * @param m DOCUMENT ME! + * @param m the button model. * - * @return DOCUMENT ME! + * @return The button that has the specified model, or <code>null</code>. */ - AbstractButton FindButton(ButtonModel m) + AbstractButton findButton(ButtonModel m) { for (int i = 0; i < buttons.size(); i++) { - AbstractButton a = (AbstractButton) buttons.get(i); - if (a.getModel() == m) - return a; + AbstractButton a = (AbstractButton) buttons.get(i); + if (a.getModel() == m) + return a; } return null; } @@ -168,7 +183,7 @@ public class ButtonGroup implements Serializable if (old != null) old.setSelected(false); - AbstractButton button = FindButton(old); + AbstractButton button = findButton(old); if (button != null) button.repaint(); } @@ -180,10 +195,10 @@ public class ButtonGroup implements Serializable * Checks if the given <code>ButtonModel</code> is selected in this button * group. * - * @param m DOCUMENT ME! + * @param m the button model (<code>null</code> permitted). * - * @return true of given <code>ButtonModel</code> is selected, false - * otherwise + * @return <code>true</code> if <code>m</code> is the selected button model + * in this group, and <code>false</code> otherwise. */ public boolean isSelected(ButtonModel m) { diff --git a/libjava/classpath/javax/swing/DefaultBoundedRangeModel.java b/libjava/classpath/javax/swing/DefaultBoundedRangeModel.java index 10de4b94837..efca148f449 100644 --- a/libjava/classpath/javax/swing/DefaultBoundedRangeModel.java +++ b/libjava/classpath/javax/swing/DefaultBoundedRangeModel.java @@ -1,6 +1,6 @@ /* DefaultBoundedRangeModel.java -- Default implementation of BoundedRangeModel. - Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,6 +39,9 @@ exception statement from your version. */ package javax.swing; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.EventListener; @@ -440,4 +443,33 @@ public class DefaultBoundedRangeModel { return (ChangeListener[]) getListeners(ChangeListener.class); } + + /** + * Provides serialization support. + * + * @param stream the output stream (<code>null</code> not permitted). + * + * @throws IOException if there is an I/O error. + */ + private void writeObject(ObjectOutputStream stream) + throws IOException + { + stream.defaultWriteObject(); + } + + /** + * Provides serialization support. + * + * @param stream the input stream (<code>null</code> not permitted). + * + * @throws IOException if there is an I/O error. + * @throws ClassNotFoundException if there is a classpath problem. + */ + private void readObject(ObjectInputStream stream) + throws ClassNotFoundException, IOException + { + stream.defaultReadObject(); + listenerList = new EventListenerList(); + } + } diff --git a/libjava/classpath/javax/swing/DefaultButtonModel.java b/libjava/classpath/javax/swing/DefaultButtonModel.java index 2be18cc8aac..020c904a4e9 100644 --- a/libjava/classpath/javax/swing/DefaultButtonModel.java +++ b/libjava/classpath/javax/swing/DefaultButtonModel.java @@ -466,14 +466,14 @@ public class DefaultButtonModel implements ButtonModel, Serializable if (s) { fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, - null, ItemEvent.SELECTED)); + this, ItemEvent.SELECTED)); if (group != null) group.setSelected(this, true); } else { fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, - null, ItemEvent.DESELECTED)); + this, ItemEvent.DESELECTED)); if (group != null) group.setSelected(this, false); } diff --git a/libjava/classpath/javax/swing/DefaultComboBoxModel.java b/libjava/classpath/javax/swing/DefaultComboBoxModel.java index ab80b61f1f9..ef785f34dec 100644 --- a/libjava/classpath/javax/swing/DefaultComboBoxModel.java +++ b/libjava/classpath/javax/swing/DefaultComboBoxModel.java @@ -146,9 +146,9 @@ public class DefaultComboBoxModel extends AbstractListModel if (selected == index) // choose a new selected item { if (selected > 0) - selectedItem = getElementAt(selected - 1); + setSelectedItem(getElementAt(selected - 1)); else - selectedItem = getElementAt(selected + 1); + setSelectedItem(getElementAt(selected + 1)); } list.removeElementAt(index); fireIntervalRemoved(this, index, index); diff --git a/libjava/classpath/javax/swing/DefaultListModel.java b/libjava/classpath/javax/swing/DefaultListModel.java index fdbbb562c90..2d02874a7e4 100644 --- a/libjava/classpath/javax/swing/DefaultListModel.java +++ b/libjava/classpath/javax/swing/DefaultListModel.java @@ -184,7 +184,7 @@ public class DefaultListModel extends AbstractListModel /** * Inserts an element at a particular index in the list. Each element at - * index <code>i >= index</code> is shifted to position <code>i+1</code>. + * index <code>i >= index</code> is shifted to position <code>i + 1</code>. * If <code>index</code> is equal to <code>size()</code>, this is * equivalent to appending an element to the array. Any * <code>index</code> greater than <code>size()</code> is illegal. @@ -420,7 +420,7 @@ public class DefaultListModel extends AbstractListModel /** * Inserts an element at a particular index in the list. Each element at - * index <code>i >= index</code> is shifted to position <code>i+1</code>. + * index <code>i >= index</code> is shifted to position <code>i + 1</code>. * If <code>index</code> is equal to <code>size()</code>, this is * equivalent to appending an element to the array. Any * <code>index</code> greater than <code>size()</code> is illegal. diff --git a/libjava/classpath/javax/swing/DefaultListSelectionModel.java b/libjava/classpath/javax/swing/DefaultListSelectionModel.java index 998aee45279..482ce2cc224 100644 --- a/libjava/classpath/javax/swing/DefaultListSelectionModel.java +++ b/libjava/classpath/javax/swing/DefaultListSelectionModel.java @@ -150,9 +150,14 @@ public class DefaultListSelectionModel implements Cloneable, boolean setLeadCalledFromAdd = false; /** - * Gets the value of the {@link #selectionMode} property. - * - * @return The current value of the property + * Returns the selection mode, which is one of {@link #SINGLE_SELECTION}, + * {@link #SINGLE_INTERVAL_SELECTION} and + * {@link #MULTIPLE_INTERVAL_SELECTION}. The default value is + * {@link #MULTIPLE_INTERVAL_SELECTION}. + * + * @return The selection mode. + * + * @see #setSelectionMode(int) */ public int getSelectionMode() { @@ -187,13 +192,19 @@ public class DefaultListSelectionModel implements Cloneable, /** * Sets the value of the {@link #anchorSelectionIndex} property. * - * @param anchorIndex The new property value + * @param index The new property value * * @see #getAnchorSelectionIndex */ - public void setAnchorSelectionIndex(int anchorIndex) + public void setAnchorSelectionIndex(int index) { - anchorSelectionIndex = anchorIndex; + if (anchorSelectionIndex != index) + { + int old = anchorSelectionIndex; + anchorSelectionIndex = index; + if (leadAnchorNotificationEnabled) + fireValueChanged(index, old); + } } /** @@ -459,12 +470,14 @@ public class DefaultListSelectionModel implements Cloneable, if (index0 == -1 || index1 == -1) return; + if (selectionMode == SINGLE_SELECTION) + setSelectionInterval(index0, index1); + else + { int lo = Math.min(index0, index1); int hi = Math.max(index0, index1); oldSel = sel.clone(); - if (selectionMode == SINGLE_SELECTION) - setSelectionInterval(index0, index1); // COMPAT: Like Sun (but not like IBM), we allow calls to // addSelectionInterval when selectionMode is @@ -503,6 +516,7 @@ public class DefaultListSelectionModel implements Cloneable, sel.set(lo, hi+1); fireDifference(sel, (BitSet) oldSel); } + } } @@ -588,27 +602,82 @@ public class DefaultListSelectionModel implements Cloneable, * the current selection mode is <code>SINGLE_SELECTION</code> only the * index <code>index2</code> is selected. * - * @param index0 The low end of the new selection - * @param index1 The high end of the new selection + * @param anchor the anchor selection index. + * @param lead the lead selection index. */ - public void setSelectionInterval(int index0, int index1) + public void setSelectionInterval(int anchor, int lead) { - if (index0 == -1 || index1 == -1) + if (anchor == -1 || lead == -1) return; - - BitSet oldSel = (BitSet) sel.clone(); - sel.clear(); if (selectionMode == SINGLE_SELECTION) - index0 = index1; - - int lo = Math.min(index0, index1); - int hi = Math.max(index0, index1); - sel.set(lo, hi+1); - // update the anchorSelectionIndex and leadSelectionIndex variables - setAnchorSelectionIndex(index0); - leadSelectionIndex=index1; + { + int lo = lead; + int hi = lead; + int selected = sel.nextSetBit(0); + if (selected == lead) + return; // the selection is not changing + if (selected >= 0) + { + lo = Math.min(lo, selected); + hi = Math.max(hi, selected); + } + if (anchorSelectionIndex >= 0) + { + lo = Math.min(lo, anchorSelectionIndex); + hi = Math.max(hi, anchorSelectionIndex); + } + sel.clear(); + sel.set(lead); + leadSelectionIndex = lead; + anchorSelectionIndex = lead; + fireValueChanged(lo, hi); + } + else if (selectionMode == SINGLE_INTERVAL_SELECTION) + { + // determine the current interval + int first = sel.nextSetBit(0); + int last = first; + if (first >= 0) + last += (sel.cardinality() - 1); + + // update the selection + int lo = Math.min(anchor, lead); + int hi = Math.max(anchor, lead); + if (lo == first && hi == last) + return; // selected interval is not being changed + sel.clear(); + sel.set(lo, hi + 1); + + // include the old selection in the event range + if (first >= 0) + lo = Math.min(lo, first); + if (last >= 0) + hi = Math.max(hi, last); + if (anchorSelectionIndex >= 0) + { + lo = Math.min(lo, anchorSelectionIndex); + hi = Math.max(hi, anchorSelectionIndex); + } + anchorSelectionIndex = anchor; + leadSelectionIndex = lead; + fireValueChanged(lo, hi); + } + else + { + BitSet oldSel = (BitSet) sel.clone(); + sel.clear(); + if (selectionMode == SINGLE_SELECTION) + anchor = lead; + + int lo = Math.min(anchor, lead); + int hi = Math.max(anchor, lead); + sel.set(lo, hi+1); + // update the anchorSelectionIndex and leadSelectionIndex variables + setAnchorSelectionIndex(anchor); + leadSelectionIndex = lead; - fireDifference(sel, oldSel); + fireDifference(sel, oldSel); + } } /** diff --git a/libjava/classpath/javax/swing/GrayFilter.java b/libjava/classpath/javax/swing/GrayFilter.java index b920b088a17..ef0d14718d6 100644 --- a/libjava/classpath/javax/swing/GrayFilter.java +++ b/libjava/classpath/javax/swing/GrayFilter.java @@ -76,9 +76,8 @@ public class GrayFilter extends RGBImageFilter */ public static Image createDisabledImage(Image src) { - return (Toolkit.getDefaultToolkit(). - createImage(new FilteredImageSource(src.getSource(), - new GrayFilter(true, 0)))); + return (Toolkit.getDefaultToolkit().createImage(new FilteredImageSource( + src.getSource(), new GrayFilter(true, 0)))); } /** diff --git a/libjava/classpath/javax/swing/InputMap.java b/libjava/classpath/javax/swing/InputMap.java index 28fccd9b9cd..19a75f4e985 100644 --- a/libjava/classpath/javax/swing/InputMap.java +++ b/libjava/classpath/javax/swing/InputMap.java @@ -1,5 +1,5 @@ /* InputMap.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,9 +37,6 @@ exception statement from your version. */ package javax.swing; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Arrays; import java.util.HashMap; @@ -47,14 +44,13 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; - /** * Maps {@link KeyStroke}s to arbitrary objects, usually Strings. This * is used in combination with {@link ActionMap}s. * * If a component receives an input event, this is looked up in * the component's <code>InputMap</code>. The result is an object which - * serves as a key to the components <code>ActionMap</code>. Finally + * serves as a key to the component's <code>ActionMap</code>. Finally * the <code>Action</code> that is stored is executed. * * @author Andrew Selkirk @@ -68,33 +64,37 @@ public class InputMap private static final long serialVersionUID = -5429059542008604257L; /** - * inputMap + * Storage for the KeyStroke --> Object mappings. */ - private Map inputMap = new HashMap(); + private Map inputMap; /** - * parent + * An optional parent map. */ private InputMap parent; /** - * Creates a new <code>InputMap</code> instance. + * Creates a new <code>InputMap</code> instance. This default instance + * contains no mappings and has no parent. */ public InputMap() { - // TODO + // nothing to do } /** - * Returns the binding for keystroke. + * Returns the binding for the specified keystroke, if there is one. * - * @param keystroke the key of the enty + * @param keystroke the key of the entry (<code>null</code> is ignored). * - * @return the binding associated with keystroke may be null + * @return The binding associated with the specified keystroke (or + * <code>null</code>). */ public Object get(KeyStroke keystroke) { - Object result = inputMap.get(keystroke); + Object result = null; + if (inputMap != null) + result = inputMap.get(keystroke); if (result == null && parent != null) result = parent.get(keystroke); @@ -102,14 +102,20 @@ public class InputMap } /** - * Puts a new entry into the <code>InputMap</code>. - * If actionMapKey is null an existing entry will be removed. + * Puts a new entry into the <code>InputMap</code>. If + * <code>actionMapKey</code> is <code>null</code> any existing entry will be + * removed. * - * @param keystroke the keystroke for the entry - * @param actionMapKey the action. + * @param keystroke the keystroke for the entry (<code>null</code> is + * ignored). + * @param actionMapKey the action (<code>null</code> permitted). */ public void put(KeyStroke keystroke, Object actionMapKey) { + if (keystroke == null) + return; + if (inputMap == null) + inputMap = new HashMap(); if (actionMapKey == null) inputMap.remove(keystroke); else @@ -117,19 +123,25 @@ public class InputMap } /** - * Remove an entry from the <code>InputMap</code>. + * Removes an entry from this <code>InputMap</code>. Note that this will + * not remove any entry from the parent map, if there is one. * - * @param keystroke the key of the entry to remove + * @param keystroke the key of the entry to remove (<code>null</code> is + * ignored). */ public void remove(KeyStroke keystroke) { - inputMap.remove(keystroke); + if (inputMap != null) + inputMap.remove(keystroke); } /** - * Returns the parent of this <code>InputMap</code>. + * Returns the parent of this <code>InputMap</code>. The default value + * is <code>null</code>. * - * @return the parent, may be null. + * @return The parent map (possibly <code>null</code>). + * + * @see #setParent(InputMap) */ public InputMap getParent() { @@ -137,9 +149,13 @@ public class InputMap } /** - * Sets a parent for this <code>InputMap</code>. + * Sets a parent for this <code>InputMap</code>. If a parent is specified, + * the {@link #get(KeyStroke)} method will look in the parent if it cannot + * find an entry in this map. * - * @param parentMap the new parent + * @param parentMap the new parent (<code>null</code> permitted). + * + * @see #getParent() */ public void setParent(InputMap parentMap) { @@ -147,31 +163,44 @@ public class InputMap } /** - * Returns the number of entries in this <code>InputMap</code>. + * Returns the number of entries in this <code>InputMap</code>. This count + * does not include any entries from the parent map, if there is one. * - * @return the number of entries + * @return The number of entries. */ public int size() { - return inputMap.size(); + int result = 0; + if (inputMap != null) + result = inputMap.size(); + return result; } /** - * Clears the <code>InputMap</code>. + * Clears the entries from this <code>InputMap</code>. The parent map, if + * there is one, is not cleared. */ public void clear() { - inputMap.clear(); + if (inputMap != null) + inputMap.clear(); } /** - * Returns all keys of entries in this <code>InputMap</code>. + * Returns all keys of entries in this <code>InputMap</code>. This does not + * include keys defined in the parent, if there is one (use the + * {@link #allKeys()} method for that case). + * <br><br> + * Following the behaviour of the reference implementation, this method will + * return <code>null</code> when no entries have been added to the map, + * and a zero length array if entries have been added but subsequently + * removed (or cleared) from the map. * - * @return an array of keys + * @return An array of keys (may be <code>null</code> or have zero length). */ public KeyStroke[] keys() { - if (size() != 0) + if (inputMap != null) { KeyStroke[] array = new KeyStroke[size()]; return (KeyStroke[]) inputMap.keySet().toArray(array); @@ -180,10 +209,10 @@ public class InputMap } /** - * Returns all keys of entries in this <code>InputMap</code> - * and all its parents. + * Returns all keys of entries in this <code>InputMap</code> and all its + * parents. * - * @return an array of keys + * @return An array of keys (may be <code>null</code> or have zero length). */ public KeyStroke[] allKeys() { @@ -195,36 +224,12 @@ public class InputMap if (parentKeys != null) set.addAll(Arrays.asList(parentKeys)); } - set.addAll(inputMap.keySet()); + if (inputMap != null) + set.addAll(inputMap.keySet()); if (set.size() == 0) return null; KeyStroke[] array = new KeyStroke[set.size()]; return (KeyStroke[]) set.toArray(array); } - /** - * writeObject - * - * @param stream the stream to write to - * - * @exception IOException If an error occurs - */ - private void writeObject(ObjectOutputStream stream) throws IOException - { - // TODO - } - - /** - * readObject - * - * @param stream the stream to read from - * - * @exception ClassNotFoundException If the serialized class cannot be found - * @exception IOException If an error occurs - */ - private void readObject(ObjectInputStream stream) - throws ClassNotFoundException, IOException - { - // TODO - } } diff --git a/libjava/classpath/javax/swing/JCheckBoxMenuItem.java b/libjava/classpath/javax/swing/JCheckBoxMenuItem.java index 3222d189f37..51634c93f0b 100644 --- a/libjava/classpath/javax/swing/JCheckBoxMenuItem.java +++ b/libjava/classpath/javax/swing/JCheckBoxMenuItem.java @@ -1,39 +1,39 @@ /* JCheckBoxMenuItem.java -- - Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc. - -This file is part of GNU Classpath. - -GNU Classpath is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU Classpath is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU Classpath; see the file COPYING. If not, write to the -Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301 USA. - -Linking this library statically or dynamically with other modules is -making a combined work based on this library. Thus, the terms and -conditions of the GNU General Public License cover the whole -combination. - -As a special exception, the copyright holders of this library give you -permission to link this library with independent modules to produce an -executable, regardless of the license terms of these independent -modules, and to copy and distribute the resulting executable under -terms of your choice, provided that you also meet, for each linked -independent module, the terms and conditions of the license of that -module. An independent module is a module which is not derived from -or based on this library. If you modify this library, you may extend -this exception to your version of the library, but you are not -obligated to do so. If you do not wish to do so, delete this -exception statement from your version. */ + Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ package javax.swing; @@ -43,20 +43,20 @@ import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleRole; /** - * A menu item that displays a checkbox. Its behaviour is very similar - * to {@link JCheckBox}. Just like the <code>JCheckBox</code>, user can check + * 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 AbstractButton#setSelected} and {@link #setState} can be use used - * for the same purpose. - * <code>JCheckBoxMenuItem</code> uses + * {@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. - * + * * @author original author unknown */ -public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, - Accessible +public class JCheckBoxMenuItem + extends JMenuItem + implements SwingConstants, Accessible { - private static final long serialVersionUID = -6676402307973384715L; + private static final long serialVersionUID = - 6676402307973384715L; /** name for the UI delegate for this menuItem. */ private static final String uiClassID = "CheckBoxMenuItemUI"; @@ -65,8 +65,8 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, private boolean state; /** - * This array contains text of this menu item if this menu item is in - * checked state and null it is not. + * This array contains text of this menu item if this menu item is in checked + * state and null it is not. */ private Object[] selectedObjects = new Object[1]; @@ -80,7 +80,7 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, /** * Creates a new JCheckBoxMenuItem with given icon - * + * * @param icon Icon for this menu item */ public JCheckBoxMenuItem(Icon icon) @@ -90,7 +90,7 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, /** * Creates a new JCheckBoxMenuItem with given label - * + * * @param text Label for this menu item */ public JCheckBoxMenuItem(String text) @@ -100,7 +100,7 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, /** * Creates a new JCheckBoxMenuItem using given action - * + * * @param action Action for this menu item. */ public JCheckBoxMenuItem(Action action) @@ -111,7 +111,7 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, /** * Creates a new JCheckBoxMenuItem object with given label and icon - * + * * @param text Label for this menu item * @param icon Icon for this menu item */ @@ -121,12 +121,12 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, } /** - * Creates a new JCheckBoxMenuItem object using specified label and - * marked as checked if given 'state' is true. - * + * Creates a new JCheckBoxMenuItem object using specified label and marked as + * checked if given 'state' is true. + * * @param text Label for this menu item - * @param state <code>true</code> if this item should be in checked state and - * <code>false</code> otherwise + * @param state <code>true</code> if this item should be in checked state + * and <code>false</code> otherwise */ public JCheckBoxMenuItem(String text, boolean state) { @@ -134,26 +134,28 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, } /** - * Creates a new JCheckBoxMenuItem object with given label, icon, - * and marked as checked if given 'state' is true. - * + * Creates a new JCheckBoxMenuItem object with given label, icon, and marked + * as checked if given 'state' is true. + * * @param text Label for this menu item * @param icon icon for this menu item - * @param state <code>true</code> if this item should be in checked state and - * false otherwise + * @param state <code>true</code> if this item should be in checked state + * and false otherwise */ public JCheckBoxMenuItem(String text, Icon icon, boolean state) { super(text, icon); setModel(new JToggleButton.ToggleButtonModel()); this.state = state; - this.setVisible(true); + if (state == true) + this.setSelected(true); + setFocusable(false); } /** * This method returns a name to identify which look and feel class will be * the UI delegate for the menuItem. - * + * * @return The Look and Feel classID. "JCheckBoxMenuItemUI" */ public String getUIClassID() @@ -163,9 +165,9 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, /** * Returns checked state for this check box menu item. - * - * @return Returns true if this menu item is in checked state - * and false otherwise. + * + * @return Returns true if this menu item is in checked state and false + * otherwise. */ public boolean getState() { @@ -173,10 +175,9 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, } /** - * Sets state for this check box menu item. If - * given 'state' is true, then mark menu item as checked, - * and uncheck this menu item otherwise. - * + * Sets state for this check box menu item. If given 'state' is true, then + * mark menu item as checked, and uncheck this menu item otherwise. + * * @param state new state for this menu item */ public synchronized void setState(boolean state) @@ -185,11 +186,11 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, } /** - * This method returns array containing label of this - * menu item if it is selected and null otherwise. - * - * @return Array containing label of this - * menu item if this menu item is selected or null otherwise. + * This method returns array containing label of this menu item if it is + * selected and null otherwise. + * + * @return Array containing label of this menu item if this menu item is + * selected or null otherwise. */ public Object[] getSelectedObjects() { @@ -202,27 +203,26 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, } /** - * This method overrides JComponent.requestFocus with an empty - * implementation, since JCheckBoxMenuItems should not - * receive focus in general. - */ + * This method overrides JComponent.requestFocus with an empty implementation, + * since JCheckBoxMenuItems should not receive focus in general. + */ public void requestFocus() { - // Should do nothing here + // Should do nothing here } /** - * Returns a string describing the attributes for the - * <code>JCheckBoxMenuItem</code> component, for use in debugging. The - * return value is guaranteed to be non-<code>null</code>, but the format + * Returns a string describing the attributes for the + * <code>JCheckBoxMenuItem</code> component, for use in debugging. The + * return value is guaranteed to be non-<code>null</code>, but the format * of the string may vary between implementations. - * - * @return A string describing the attributes of the - * <code>JCheckBoxMenuItem</code>. + * + * @return A string describing the attributes of the + * <code>JCheckBoxMenuItem</code>. */ protected String paramString() { - // calling super seems to be sufficient to match the reference + // calling super seems to be sufficient to match the reference // implementation here... return super.paramString(); } @@ -230,9 +230,9 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, /** * Returns the object that provides accessibility features for this * <code>JCheckBoxMenuItem</code> component. - * - * @return The accessible context (an instance of - * {@link AccessibleJCheckBoxMenuItem}). + * + * @return The accessible context (an instance of + * {@link AccessibleJCheckBoxMenuItem}). */ public AccessibleContext getAccessibleContext() { @@ -243,12 +243,13 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, } /** - * Provides the accessibility features for the <code>JCheckBoxMenuItem</code> + * Provides the accessibility features for the <code>JCheckBoxMenuItem</code> * component. * * @see JCheckBoxMenuItem#getAccessibleContext() */ - protected class AccessibleJCheckBoxMenuItem extends AccessibleJMenuItem + protected class AccessibleJCheckBoxMenuItem + extends AccessibleJMenuItem { private static final long serialVersionUID = 1079958073579370777L; @@ -261,9 +262,9 @@ public class JCheckBoxMenuItem extends JMenuItem implements SwingConstants, } /** - * Returns the accessible role for the <code>JCheckBoxMenuItem</code> + * Returns the accessible role for the <code>JCheckBoxMenuItem</code> * component. - * + * * @return {@link AccessibleRole#CHECK_BOX}. */ public AccessibleRole getAccessibleRole() diff --git a/libjava/classpath/javax/swing/JComboBox.java b/libjava/classpath/javax/swing/JComboBox.java index efb04592b50..c75a94bdc36 100644 --- a/libjava/classpath/javax/swing/JComboBox.java +++ b/libjava/classpath/javax/swing/JComboBox.java @@ -471,6 +471,7 @@ public class JComboBox extends JComponent implements ItemSelectable, public void setSelectedItem(Object item) { dataModel.setSelectedItem(item); + fireActionEvent(); } /** @@ -1028,7 +1029,8 @@ public class JComboBox extends JComponent implements ItemSelectable, } /** - * This method hides combo box's popup whenever TAB key is pressed. + * This method is fired whenever a key is pressed with the combo box + * in focus * * @param e The KeyEvent indicating which key was pressed. */ @@ -1036,15 +1038,6 @@ public class JComboBox extends JComponent implements ItemSelectable, { 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); } @@ -1066,7 +1059,7 @@ public class JComboBox extends JComponent implements ItemSelectable, */ public KeySelectionManager getKeySelectionManager() { - return null; + return keySelectionManager; } /** @@ -1098,7 +1091,7 @@ public class JComboBox extends JComponent implements ItemSelectable, */ protected KeySelectionManager createDefaultKeySelectionManager() { - return null; + return new DefaultKeySelectionManager(); } /** @@ -1471,4 +1464,34 @@ public class JComboBox extends JComponent implements ItemSelectable, // Nothing to do here. } } + + private class DefaultKeySelectionManager + implements KeySelectionManager + { + + public int selectionForKey(char aKey, ComboBoxModel aModel) + { + int selectedIndex = getSelectedIndex(); + + // Start at currently selected item and iterate to end of list + for (int i = selectedIndex + 1; i < aModel.getSize(); i++) + { + String nextItem = aModel.getElementAt(i).toString(); + + if (nextItem.charAt(0) == aKey) + return i; + } + + // Wrap to start of list if no match yet + for (int i = 0; i <= selectedIndex; i++) + { + String nextItem = aModel.getElementAt(i).toString(); + + if (nextItem.charAt(0) == aKey) + return i; + } + + return - 1; + } + } } diff --git a/libjava/classpath/javax/swing/JComponent.java b/libjava/classpath/javax/swing/JComponent.java index 66c562d110b..fa83502946d 100644 --- a/libjava/classpath/javax/swing/JComponent.java +++ b/libjava/classpath/javax/swing/JComponent.java @@ -48,12 +48,10 @@ import java.awt.EventQueue; import java.awt.FocusTraversalPolicy; import java.awt.Font; import java.awt.Graphics; -import java.awt.Graphics2D; 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; @@ -69,8 +67,8 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyVetoException; import java.beans.VetoableChangeListener; +import java.beans.VetoableChangeSupport; import java.io.Serializable; -import java.util.ArrayList; import java.util.EventListener; import java.util.Hashtable; import java.util.Locale; @@ -571,6 +569,21 @@ public abstract class JComponent extends Container implements Serializable */ String toolTipText; + /** + * The popup menu for the component. + * + * @see #getComponentPopupMenu() + * @see #setComponentPopupMenu(JPopupMenu) + */ + JPopupMenu componentPopupMenu; + + /** + * A flag that controls whether the {@link #getComponentPopupMenu()} method + * looks to the component's parent when the <code>componentPopupMenu</code> + * field is <code>null</code>. + */ + boolean inheritsPopupMenu; + /** * <p>Whether to double buffer this component when painting. This flag * should generally be <code>true</code>, to ensure good painting @@ -668,7 +681,13 @@ public abstract class JComponent extends Container implements Serializable * Indicates whether the current paint call is already double buffered or * not. */ - static boolean isPaintingDoubleBuffered = false; + static boolean paintingDoubleBuffered = false; + + /** + * Indicates whether we are calling paintDoubleBuffered() from + * paintImmadiately (RepaintManager) or from paint() (AWT refresh). + */ + static private boolean isRepainting = false; /** * Listeners for events other than {@link PropertyChangeEvent} are @@ -677,6 +696,11 @@ public abstract class JComponent extends Container implements Serializable */ protected EventListenerList listenerList = new EventListenerList(); + /** + * Handles VetoableChangeEvents. + */ + private VetoableChangeSupport vetoableChangeSupport; + /** * Storage for "client properties", which are key/value pairs associated * with this component by a "client", such as a user application or a @@ -690,7 +714,7 @@ public abstract class JComponent extends Container implements Serializable private ComponentInputMap inputMap_whenInFocusedWindow; private ActionMap actionMap; /** @since 1.3 */ - private boolean verifyInputWhenFocusTarget; + private boolean verifyInputWhenFocusTarget = true; private InputVerifier inputVerifier; private TransferHandler transferHandler; @@ -784,7 +808,7 @@ public abstract class JComponent extends Container implements Serializable { super(); setDropTarget(new DropTarget()); - defaultLocale = Locale.getDefault(); + setLocale(getDefaultLocale()); debugGraphicsOptions = DebugGraphics.NONE_OPTION; setRequestFocusEnabled(true); } @@ -868,7 +892,8 @@ public abstract class JComponent extends Container implements Serializable */ public void removeVetoableChangeListener(VetoableChangeListener listener) { - listenerList.remove(VetoableChangeListener.class, listener); + if (vetoableChangeSupport != null) + vetoableChangeSupport.removeVetoableChangeListener(listener); } /** @@ -884,23 +909,6 @@ public abstract class JComponent extends Container implements Serializable } /** - * Register a <code>PropertyChangeListener</code> for a specific, named - * property. To listen to all property changes, regardless of name, use - * {@link #addPropertyChangeListener(PropertyChangeListener)} instead. - * - * @param propertyName The property name to listen to - * @param listener The listener to register - * - * @see #removePropertyChangeListener(String, PropertyChangeListener) - * @see #changeSupport - */ - public void addPropertyChangeListener(String propertyName, - PropertyChangeListener listener) - { - listenerList.add(PropertyChangeListener.class, listener); - } - - /** * Register a <code>VetoableChangeListener</code>. * * @param listener The listener to register @@ -910,7 +918,10 @@ public abstract class JComponent extends Container implements Serializable */ public void addVetoableChangeListener(VetoableChangeListener listener) { - listenerList.add(VetoableChangeListener.class, listener); + // Lazily instantiate this, it's rarely needed. + if (vetoableChangeSupport == null) + vetoableChangeSupport = new VetoableChangeSupport(this); + vetoableChangeSupport.addVetoableChangeListener(listener); } /** @@ -936,6 +947,8 @@ public abstract class JComponent extends Container implements Serializable { if (listenerType == PropertyChangeListener.class) return getPropertyChangeListeners(); + else if (listenerType == VetoableChangeListener.class) + return getVetoableChangeListeners(); else return listenerList.getListeners(listenerType); } @@ -954,84 +967,89 @@ public abstract class JComponent extends Container implements Serializable /** * Return all registered <code>VetoableChangeListener</code> objects. * - * @return The set of <code>VetoableChangeListener</code> objects in {@link - * #listenerList} + * @return An array of the <code>VetoableChangeListener</code> objects + * registered with this component (possibly empty but never + * <code>null</code>). + * + * @since 1.4 */ public VetoableChangeListener[] getVetoableChangeListeners() - { - return (VetoableChangeListener[]) getListeners(VetoableChangeListener.class); + { + return vetoableChangeSupport == null ? new VetoableChangeListener[0] + : vetoableChangeSupport.getVetoableChangeListeners(); } /** - * A variant of {@link #firePropertyChange(String,Object,Object)} - * for properties with <code>boolean</code> values. + * Call {@link VetoableChangeListener#vetoableChange} on all listeners + * registered to listen to a given property. Any method which changes + * the specified property of this component should call this method. + * + * @param propertyName The property which changed + * @param oldValue The old value of the property + * @param newValue The new value of the property + * + * @throws PropertyVetoException if the change was vetoed by a listener * - * @specnote It seems that in JDK1.5 all property related methods have been - * moved to java.awt.Component, except this and 2 others. We call - * super here. I guess this will also be removed in one of the next - * releases. + * @see #addVetoableChangeListener + * @see #removeVetoableChangeListener */ - public void firePropertyChange(String propertyName, boolean oldValue, - boolean newValue) + protected void fireVetoableChange(String propertyName, Object oldValue, + Object newValue) + throws PropertyVetoException { - super.firePropertyChange(propertyName, oldValue, newValue); + if (vetoableChangeSupport != null) + vetoableChangeSupport.fireVetoableChange(propertyName, oldValue, newValue); } + /** - * A variant of {@link #firePropertyChange(String,Object,Object)} - * for properties with <code>char</code> values. + * Fires a property change for a primitive integer property. + * + * @param property the name of the property + * @param oldValue the old value of the property + * @param newValue the new value of the property * - * @specnote It seems that in JDK1.5 all property related methods have been - * moved to java.awt.Component, except this and 2 others. We call - * super here. I guess this will also be removed in one of the next - * releases. + * @specnote This method is implemented in + * {@link Component#firePropertyChange(String, int, int)}. It is + * only here because it is specified to be public, whereas the + * Component method is protected. */ - public void firePropertyChange(String propertyName, char oldValue, - char newValue) + public void firePropertyChange(String property, int oldValue, int newValue) { - super.firePropertyChange(propertyName, oldValue, newValue); + super.firePropertyChange(property, oldValue, newValue); } - + /** - * A variant of {@link #firePropertyChange(String,Object,Object)} - * for properties with <code>int</code> values. + * Fires a property change for a primitive boolean property. + * + * @param property the name of the property + * @param oldValue the old value of the property + * @param newValue the new value of the property * - * @specnote It seems that in JDK1.5 all property related methods have been - * moved to java.awt.Component, except this and 2 others. We call - * super here. I guess this will also be removed in one of the next - * releases. + * @specnote This method is implemented in + * {@link Component#firePropertyChange(String, boolean, boolean)}. + * It is only here because it is specified to be public, whereas + * the Component method is protected. */ - public void firePropertyChange(String propertyName, int oldValue, - int newValue) + public void firePropertyChange(String property, boolean oldValue, + boolean newValue) { - super.firePropertyChange(propertyName, oldValue, newValue); + super.firePropertyChange(property, oldValue, newValue); } /** - * Call {@link VetoableChangeListener#vetoableChange} on all listeners - * registered to listen to a given property. Any method which changes - * the specified property of this component should call this method. - * - * @param propertyName The property which changed - * @param oldValue The old value of the property - * @param newValue The new value of the property - * - * @throws PropertyVetoException if the change was vetoed by a listener + * Fires a property change for a primitive character property. * - * @see #addVetoableChangeListener - * @see #removeVetoableChangeListener + * @param property the name of the property + * @param oldValue the old value of the property + * @param newValue the new value of the property */ - protected void fireVetoableChange(String propertyName, Object oldValue, - Object newValue) - throws PropertyVetoException + public void firePropertyChange(String property, char oldValue, + char newValue) { - VetoableChangeListener[] listeners = getVetoableChangeListeners(); - - PropertyChangeEvent evt = - new PropertyChangeEvent(this, propertyName, oldValue, newValue); - - for (int i = 0; i < listeners.length; i++) - listeners[i].vetoableChange(evt); + // FIXME - This method is already public in awt Component, but + // is included here to work around a compilation bug in gcj 4.1. + super.firePropertyChange(property, oldValue, newValue); } /** @@ -1402,11 +1420,32 @@ public abstract class JComponent extends Container implements Serializable * Return the set of {@link KeyStroke} objects which are registered * to initiate actions on this component. * - * @return An array of the registered keystrokes + * @return An array of the registered keystrokes (possibly empty but never + * <code>null</code>). */ public KeyStroke[] getRegisteredKeyStrokes() { - return null; + KeyStroke[] ks0; + KeyStroke[] ks1; + KeyStroke[] ks2; + if (inputMap_whenFocused != null) + ks0 = inputMap_whenFocused.keys(); + else + ks0 = new KeyStroke[0]; + if (inputMap_whenAncestorOfFocused != null) + ks1 = inputMap_whenAncestorOfFocused.keys(); + else + ks1 = new KeyStroke[0]; + if (inputMap_whenInFocusedWindow != null) + ks2 = inputMap_whenInFocusedWindow.keys(); + else + ks2 = new KeyStroke[0]; + int count = ks0.length + ks1.length + ks2.length; + KeyStroke[] result = new KeyStroke[count]; + System.arraycopy(ks0, 0, result, 0, ks0.length); + System.arraycopy(ks1, 0, result, ks0.length, ks1.length); + System.arraycopy(ks2, 0, result, ks0.length + ks1.length, ks2.length); + return result; } /** @@ -1524,8 +1563,90 @@ public abstract class JComponent extends Container implements Serializable { return getToolTipText(); } + + /** + * Returns the flag that controls whether or not the component inherits its + * parent's popup menu when no popup menu is specified for this component. + * + * @return A boolean. + * + * @since 1.5 + * + * @see #setInheritsPopupMenu(boolean) + */ + public boolean getInheritsPopupMenu() + { + return inheritsPopupMenu; + } + + /** + * Sets the flag that controls whether or not the component inherits its + * parent's popup menu when no popup menu is specified for this component. + * This is a bound property with the property name 'inheritsPopupMenu'. + * + * @param inherit the new flag value. + * + * @since 1.5 + * + * @see #getInheritsPopupMenu() + */ + public void setInheritsPopupMenu(boolean inherit) + { + if (inheritsPopupMenu != inherit) + { + inheritsPopupMenu = inherit; + this.firePropertyChange("inheritsPopupMenu", ! inherit, inherit); + } + } + + /** + * Returns the popup menu for this component. If the popup menu is + * <code>null</code> AND the {@link #getInheritsPopupMenu()} method returns + * <code>true</code>, this method will return the parent's popup menu (if it + * has one). + * + * @return The popup menu (possibly <code>null</code>. + * + * @since 1.5 + * + * @see #setComponentPopupMenu(JPopupMenu) + * @see #getInheritsPopupMenu() + */ + public JPopupMenu getComponentPopupMenu() + { + if (componentPopupMenu == null && getInheritsPopupMenu()) + { + Container parent = getParent(); + if (parent instanceof JComponent) + return ((JComponent) parent).getComponentPopupMenu(); + else + return null; + } + else + return componentPopupMenu; + } /** + * Sets the popup menu for this component (this is a bound property with + * the property name 'componentPopupMenu'). + * + * @param popup the popup menu (<code>null</code> permitted). + * + * @since 1.5 + * + * @see #getComponentPopupMenu() + */ + public void setComponentPopupMenu(JPopupMenu popup) + { + if (componentPopupMenu != popup) + { + JPopupMenu old = componentPopupMenu; + componentPopupMenu = popup; + firePropertyChange("componentPopupMenu", old, popup); + } + } + + /** * Return the top level ancestral container (usually a {@link * java.awt.Window} or {@link java.applet.Applet}) which this component is * contained within, or <code>null</code> if no ancestors exist. @@ -1725,7 +1846,7 @@ public abstract class JComponent extends Container implements Serializable // 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() + if (!paintingDoubleBuffered && isDoubleBuffered() && rm.isDoubleBufferingEnabled()) { Rectangle clip = g.getClipBounds(); @@ -1816,235 +1937,77 @@ public abstract class JComponent extends Container implements Serializable { if (getComponentCount() > 0) { - if (isOptimizedDrawingEnabled()) - paintChildrenOptimized(g); - else - paintChildrenWithOverlap(g); - } - } - - /** - * Paints the children of this JComponent in the case when the component - * is not marked as optimizedDrawingEnabled, that means the container cannot - * guarantee that it's children are tiled. For this case we must - * perform a more complex optimization to determine the minimal rectangle - * to be painted for each child component. - * - * @param g the graphics context to use - */ - private void paintChildrenWithOverlap(Graphics 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 rectangles that need to be painted for each child component. - // We push on this list arrays that have the Rectangles to be painted as - // the first elements and the component to be painted as the last one. - // Later we go through that list in reverse order and paint the rectangles. - ArrayList paintRegions = new ArrayList(children.length); - ArrayList paintRectangles = new ArrayList(); - ArrayList newPaintRects = new ArrayList(); - paintRectangles.add(g.getClipBounds()); - ArrayList componentRectangles = new ArrayList(); - - // Go through children from top to bottom and find out their paint - // rectangles. - for (int index = 0; paintRectangles.size() > 0 && - index < children.length; index++) - { - Component comp = children[index]; - if (! comp.isVisible()) - continue; - - Rectangle compBounds = comp.getBounds(); - boolean isOpaque = comp instanceof JComponent - && ((JComponent) comp).isOpaque(); - - // Add all the current paint rectangles that intersect with the - // component to the component's paint rectangle array. - for (int i = paintRectangles.size() - 1; i >= 0; i--) + // Need to lock the tree to avoid problems with AWT and concurrency. + synchronized (getTreeLock()) { - Rectangle r = (Rectangle) paintRectangles.get(i); - if (r.intersects(compBounds)) + for (int i = getComponentCount() - 1; i >= 0; i--) { - Rectangle compRect = r.intersection(compBounds); - componentRectangles.add(compRect); - // If the component is opaque, split up each paint rect and - // add paintRect - compBounds to the newPaintRects array. - if (isOpaque) + Component child = getComponent(i); + if (child != null && child.isLightweight() + && child.isVisible()) { - int x, y, w, h; - Rectangle rect = new Rectangle(); - - // The north retangle. - x = Math.max(compBounds.x, r.x); - y = r.y; - w = Math.min(compBounds.width, r.width + r.x - x); - h = compBounds.y - r.y; - rect.setBounds(x, y, w, h); - if (! rect.isEmpty()) - { - newPaintRects.add(rect); - rect = new Rectangle(); - } - - // The south rectangle. - x = Math.max(compBounds.x, r.x); - y = compBounds.y + compBounds.height; - w = Math.min(compBounds.width, r.width + r.x - x); - h = r.height - (compBounds.y - r.y) - compBounds.height; - rect.setBounds(x, y, w, h); - if (! rect.isEmpty()) - { - newPaintRects.add(rect); - rect = new Rectangle(); - } - - // The west rectangle. - x = r.x; - y = r.y; - w = compBounds.x - r.x; - h = r.height; - rect.setBounds(x, y, w, h); - if (! rect.isEmpty()) + int cx = child.getX(); + int cy = child.getY(); + int cw = child.getWidth(); + int ch = child.getHeight(); + if (g.hitClip(cx, cy, cw, ch)) { - newPaintRects.add(rect); - rect = new Rectangle(); - } - - // The east rectangle. - x = compBounds.x + compBounds.width; - y = r.y; - w = r.width - (compBounds.x - r.x) - compBounds.width; - h = r.height; - rect.setBounds(x, y, w, h); - if (! rect.isEmpty()) - { - newPaintRects.add(rect); + if ((! isOptimizedDrawingEnabled()) && i > 0) + { + // Check if the child is completely obscured. + Rectangle clip = g.getClipBounds(); // A copy. + SwingUtilities.computeIntersection(cx, cy, cw, ch, + clip); + if (isCompletelyObscured(i, clip)) + continue; // Continues the for-loop. + } + Graphics cg = g.create(cx, cy, cw, ch); + cg.setColor(child.getForeground()); + cg.setFont(child.getFont()); + try + { + child.paint(cg); + } + finally + { + cg.dispose(); + } } } - else - { - // Not opaque, need to reuse the current paint rectangles - // for the next component. - newPaintRects.add(r); - } - - } - else - { - newPaintRects.add(r); } } - - // Replace the paintRectangles with the new split up - // paintRectangles. - paintRectangles.clear(); - paintRectangles.addAll(newPaintRects); - newPaintRects.clear(); - - // Store paint rectangles if there are any for the current component. - int compRectsSize = componentRectangles.size(); - if (compRectsSize > 0) - { - componentRectangles.add(comp); - paintRegions.add(componentRectangles); - componentRectangles = new ArrayList(); - } } - - // paintingTile becomes true just before we start painting the component's - // children. - paintingTile = true; - - // We must go through the painting regions backwards, because the - // topmost components have been added first, followed by the components - // below. - int prEndIndex = paintRegions.size() - 1; - for (int i = prEndIndex; i >= 0; i--) - { - // paintingTile must be set to false before we begin to start painting - // the last tile. - if (i == 0) - paintingTile = false; - - ArrayList paintingRects = (ArrayList) paintRegions.get(i); - // The last element is always the component. - Component c = (Component) paintingRects.get(paintingRects.size() - 1); - int endIndex = paintingRects.size() - 2; - for (int j = 0; j <= endIndex; j++) - { - Rectangle cBounds = c.getBounds(); - Rectangle bounds = (Rectangle) paintingRects.get(j); - Rectangle oldClip = g.getClipBounds(); - if (oldClip == null) - oldClip = bounds; - - boolean translated = false; - try - { - g.setClip(bounds); - g.translate(cBounds.x, cBounds.y); - translated = true; - c.paint(g); - } - finally - { - if (translated) - g.translate(-cBounds.x, -cBounds.y); - g.setClip(oldClip); - } - } - } - g.setClip(originalClip); } /** - * Paints the children of this container when it is marked as - * optimizedDrawingEnabled. In this case the container can guarantee that - * it's children are tiled, which allows for a much more efficient - * algorithm to determine the minimum rectangles to be painted for - * each child. + * Determines if a region of a child component is completely obscured by one + * of its siblings. + * + * @param index the index of the child component + * @param rect the region to check * - * @param g the graphics context to use + * @return <code>true</code> if the region is completely obscured by a + * sibling, <code>false</code> otherwise */ - private void paintChildrenOptimized(Graphics g) + private boolean isCompletelyObscured(int index, Rectangle rect) { - Shape originalClip = g.getClip(); - Rectangle inner = SwingUtilities.calculateInnerArea(this, rectCache); - g.clipRect(inner.x, inner.y, inner.width, inner.height); - Component[] children = getComponents(); - - // paintingTile becomes true just before we start painting the component's - // children. - paintingTile = true; - for (int i = children.length - 1; i >= 0; i--) //children.length; i++) + boolean obscured = false; + for (int i = index - 1; i >= 0 && obscured == false; i--) { - // paintingTile must be set to false before we begin to start painting - // the last tile. - if (i == children.length - 1) - 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; - Graphics g2 = g.create(bounds.x, bounds.y, bounds.width, - bounds.height); - children[i].paint(g2); - g2.dispose(); + Component sib = getComponent(i); + if (sib.isVisible()) + { + Rectangle sibRect = sib.getBounds(rectCache); + if (sib.isOpaque() && rect.x >= sibRect.x + && (rect.x + rect.width) <= (sibRect.x + sibRect.width) + && rect.y >= sibRect.y + && (rect.y + rect.height) <= (sibRect.y + sibRect.height)) + { + obscured = true; + } + } } - g.setClip(originalClip); + return obscured; } /** @@ -2064,12 +2027,15 @@ public abstract class JComponent extends Container implements Serializable { if (ui != null) { - Graphics g2 = g; - if (!(g instanceof Graphics2D)) - g2 = g.create(); - ui.update(g2, this); - if (!(g instanceof Graphics2D)) - g2.dispose(); + Graphics g2 = g.create(); + try + { + ui.update(g2, this); + } + finally + { + g2.dispose(); + } } } @@ -2112,16 +2078,13 @@ public abstract class JComponent extends Container implements Serializable 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) + if (root == null|| !root.isShowing()) return; - if (root == null || !root.isShowing()) - return; - - Rectangle rootClip = SwingUtilities.convertRectangle(this, r, root); + SwingUtilities.convertRectangleToAncestor(this, r, root); if (root instanceof JComponent) - ((JComponent) root).paintImmediately2(rootClip); + ((JComponent) root).paintImmediately2(r); else - root.repaint(rootClip.x, rootClip.y, rootClip.width, rootClip.height); + root.repaint(r.x, r.y, r.width, r.height); } /** @@ -2131,40 +2094,35 @@ public abstract class JComponent extends Container implements Serializable */ void paintImmediately2(Rectangle r) { + isRepainting = true; RepaintManager rm = RepaintManager.currentManager(this); - if (rm.isDoubleBufferingEnabled() && isDoubleBuffered()) + if (rm.isDoubleBufferingEnabled() && isPaintingDoubleBuffered()) paintDoubleBuffered(r); else paintSimple(r); + isRepainting = false; } /** - * Gets the root of the component given. If a parent of the - * component is an instance of Applet, then the applet is - * returned. The applet is considered the root for painting - * and adding/removing components. Otherwise, the root Window - * is returned if it exists. - * - * @param comp - The component to get the root for. - * @return the parent root. An applet if it is a parent, - * or the root window. If neither exist, null is returned. + * Returns true if we must paint double buffered, that is, when this + * component or any of it's ancestors are double buffered. + * + * @return true if we must paint double buffered, that is, when this + * component or any of it's ancestors are double buffered */ - private Component getRoot(Component comp) + private boolean isPaintingDoubleBuffered() { - Applet app = null; - - while (comp != null) - { - if (app == null && comp instanceof Window) - return comp; - else if (comp instanceof Applet) - app = (Applet) comp; - comp = comp.getParent(); - } - - return app; + boolean doubleBuffered = isDoubleBuffered(); + Component parent = getParent(); + while (! doubleBuffered && parent != null) + { + doubleBuffered = parent instanceof JComponent + && ((JComponent) parent).isDoubleBuffered(); + parent = parent.getParent(); + } + return doubleBuffered; } - + /** * Performs double buffered repainting. */ @@ -2173,7 +2131,7 @@ public abstract class JComponent extends Container implements Serializable RepaintManager rm = RepaintManager.currentManager(this); // Paint on the offscreen buffer. - Component root = getRoot(this); + Component root = SwingUtilities.getRoot(this); Image buffer = rm.getVolatileOffscreenBuffer(this, root.getWidth(), root.getHeight()); @@ -2183,26 +2141,30 @@ public abstract class JComponent extends Container implements Serializable buffer = rm.getOffscreenBuffer(this, root.getWidth(), root.getHeight()); //Rectangle targetClip = SwingUtilities.convertRectangle(this, r, root); - Point translation = SwingUtilities.convertPoint(this, 0, 0, root); Graphics g2 = buffer.getGraphics(); clipAndTranslateGraphics(root, this, g2); g2.clipRect(r.x, r.y, r.width, r.height); g2 = getComponentGraphics(g2); - isPaintingDoubleBuffered = true; + paintingDoubleBuffered = true; try { - paint(g2); + if (isRepainting) // Called from paintImmediately, go through paint(). + paint(g2); + else // Called from paint() (AWT refresh), don't call it again. + { + paintComponent(g2); + paintBorder(g2); + paintChildren(g2); + } } finally { - isPaintingDoubleBuffered = false; + paintingDoubleBuffered = false; g2.dispose(); } // Paint the buffer contents on screen. - rm.commitBuffer(root, new Rectangle(translation.x + r.x, - translation.y + r.y, r.width, - r.height)); + rm.commitBuffer(this, r); } /** @@ -2217,11 +2179,16 @@ public abstract class JComponent extends Container implements Serializable private void clipAndTranslateGraphics(Component root, Component target, Graphics g) { - Component parent = target.getParent(); - if (parent != root) - clipAndTranslateGraphics(root, parent, g); - - g.translate(target.getX(), target.getY()); + Component parent = target; + int deltaX = 0; + int deltaY = 0; + while (parent != root) + { + deltaX += parent.getX(); + deltaY += parent.getY(); + parent = parent.getParent(); + } + g.translate(deltaX, deltaY); g.clipRect(0, 0, target.getWidth(), target.getHeight()); } @@ -2271,7 +2238,13 @@ public abstract class JComponent extends Container implements Serializable /** * A variant of {@link * #registerKeyboardAction(ActionListener,String,KeyStroke,int)} which - * provides <code>null</code> for the command name. + * provides <code>null</code> for the command name. + * + * @param act the action listener to notify when the keystroke occurs. + * @param stroke the key stroke. + * @param cond the condition (one of {@link #WHEN_FOCUSED}, + * {@link #WHEN_IN_FOCUSED_WINDOW} and + * {@link #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}). */ public void registerKeyboardAction(ActionListener act, KeyStroke stroke, @@ -2348,9 +2321,22 @@ public abstract class JComponent extends Container implements Serializable KeyStroke stroke, int cond) { - getInputMap(cond).put(stroke, new ActionListenerProxy(act, cmd)); + ActionListenerProxy proxy = new ActionListenerProxy(act, cmd); + getInputMap(cond).put(stroke, proxy); + getActionMap().put(proxy, proxy); } + /** + * Sets the input map for the given condition. + * + * @param condition the condition (one of {@link #WHEN_FOCUSED}, + * {@link #WHEN_IN_FOCUSED_WINDOW} and + * {@link #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}). + * @param map the map. + * + * @throws IllegalArgumentException if <code>condition</code> is not one of + * the specified values. + */ public final void setInputMap(int condition, InputMap map) { enableEvents(AWTEvent.KEY_EVENT_MASK); @@ -2486,13 +2472,17 @@ public abstract class JComponent extends Container implements Serializable */ public ActionListener getActionForKeyStroke(KeyStroke ks) { - Object cmd = getInputMap().get(ks); - if (cmd != null) + Object key = getInputMap(JComponent.WHEN_FOCUSED).get(ks); + if (key == null) + key = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).get(ks); + if (key == null) + key = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).get(ks); + if (key != null) { - if (cmd instanceof ActionListenerProxy) - return (ActionListenerProxy) cmd; - else if (cmd instanceof String) - return getActionMap().get(cmd); + if (key instanceof ActionListenerProxy) + return ((ActionListenerProxy) key).target; + else + return getActionMap().get(key); } return null; } @@ -2591,10 +2581,11 @@ public abstract class JComponent extends Container implements Serializable if (isEnabled()) { Action act = null; + Object cmd = null; InputMap map = getInputMap(condition); if (map != null) { - Object cmd = map.get(ks); + cmd = map.get(ks); if (cmd != null) { if (cmd instanceof ActionListenerProxy) @@ -2604,7 +2595,23 @@ public abstract class JComponent extends Container implements Serializable } } if (act != null && act.isEnabled()) - return SwingUtilities.notifyAction(act, ks, e, this, e.getModifiers()); + { + // Need to synchronize here so we don't get in trouble with + // our __command__ hack. + synchronized (act) + { + // We add the command as value to the action, so that + // the action can later determine the command with which it + // was called. This is undocumented, but shouldn't affect + // compatibility. It allows us to use only one Action instance + // to do the work for all components of one type, instead of + // having loads of small Actions. This effectivly saves startup + // time of Swing. + act.putValue("__command__", cmd); + return SwingUtilities.notifyAction(act, ks, e, this, + e.getModifiers()); + } + } } return false; } @@ -2706,6 +2713,11 @@ public abstract class JComponent extends Container implements Serializable */ public void revalidate() { + // As long as we don't have a parent we don't need to do any layout, since + // this is done anyway as soon as we get connected to a parent. + if (getParent() == null) + return; + if (! EventQueue.isDispatchThread()) SwingUtilities.invokeLater(new Runnable() { @@ -3089,11 +3101,29 @@ public abstract class JComponent extends Container implements Serializable // Nothing to do here. } + /** + * Returns the locale used as the default for all new components. The + * default value is {@link Locale#getDefault()} (that is, the platform + * default locale). + * + * @return The locale (never <code>null</code>). + * + * @see #setDefaultLocale(Locale) + */ public static Locale getDefaultLocale() { + if (defaultLocale == null) + defaultLocale = Locale.getDefault(); return defaultLocale; } + /** + * Sets the locale to be used as the default for all new components. If this + * is set to <code>null</code>, the {@link #getDefaultLocale()} method will + * return the platform default locale. + * + * @param l the locale (<code>null</code> permitted). + */ public static void setDefaultLocale(Locale l) { defaultLocale = l; @@ -3531,12 +3561,13 @@ public abstract class JComponent extends Container implements Serializable } } // Dispatch event to all children. - Component[] children = getComponents(); - for (int i = 0; i < children.length; i++) + int numChildren = getComponentCount(); + for (int i = 0; i < numChildren; i++) { - if (!(children[i] instanceof JComponent)) + Component child = getComponent(i); + if (! (child instanceof JComponent)) continue; - JComponent jc = (JComponent) children[i]; + JComponent jc = (JComponent) child; jc.fireAncestorEvent(ancestor, id); } } @@ -3619,10 +3650,10 @@ public abstract class JComponent extends Container implements Serializable ! SwingUtilities.isRectangleContainingRectangle(parentRect, target); if (! haveOverlap) { - Component[] children = newParent.getComponents(); - for (int i = 0; children[i] != parent && !haveOverlap; i++) + Component child; + for (int i = 0; (child = newParent.getComponent(i)) != parent && !haveOverlap; i++) { - Rectangle childRect = children[i].getBounds(); + Rectangle childRect = child.getBounds(); haveOverlap = target.intersects(childRect); } } @@ -3654,7 +3685,7 @@ public abstract class JComponent extends Container implements Serializable { if ((found instanceof JComponent) && ((JComponent) found).isOpaque()) break; - else if (!(found instanceof JComponent)) + else if (!(found instanceof JComponent) && !found.isLightweight()) break; Container p = found.getParent(); if (p == null) diff --git a/libjava/classpath/javax/swing/JFileChooser.java b/libjava/classpath/javax/swing/JFileChooser.java index 64f9bd0f4e6..a508b8fcb20 100644 --- a/libjava/classpath/javax/swing/JFileChooser.java +++ b/libjava/classpath/javax/swing/JFileChooser.java @@ -43,6 +43,8 @@ import java.awt.GraphicsEnvironment; import java.awt.HeadlessException; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowAdapter; import java.beans.PropertyChangeEvent; import java.io.File; import java.util.ArrayList; @@ -351,7 +353,7 @@ public class JFileChooser extends JComponent implements Accessible * The file selection mode. * @see #setFileSelectionMode(int) */ - private int fileSelectionMode = FILES_AND_DIRECTORIES; + private int fileSelectionMode = FILES_ONLY; /** * The file view. @@ -744,10 +746,16 @@ public class JFileChooser extends JComponent implements Accessible JDialog dialog = new JDialog(toUse); setSelectedFile(null); dialog.getContentPane().add(this); + dialog.addWindowListener( new WindowAdapter() + { + public void windowClosing(WindowEvent e) + { + cancelSelection(); + } + }); dialog.setModal(true); dialog.invalidate(); dialog.repaint(); - return dialog; } diff --git a/libjava/classpath/javax/swing/JInternalFrame.java b/libjava/classpath/javax/swing/JInternalFrame.java index 79dcc7326be..ff3ae1faa66 100644 --- a/libjava/classpath/javax/swing/JInternalFrame.java +++ b/libjava/classpath/javax/swing/JInternalFrame.java @@ -38,9 +38,11 @@ exception statement from your version. */ package javax.swing; +import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Graphics; +import java.awt.IllegalComponentStateException; import java.awt.KeyboardFocusManager; import java.awt.LayoutManager; import java.awt.Rectangle; @@ -490,12 +492,6 @@ public class JInternalFrame extends JComponent implements Accessible, /** Whether the JInternalFrame has become visible for the very first time. */ private transient boolean isFirstTimeVisible = true; - /** - * Whether the JInternalFrame is in the transition from being a maximized - * frame back to a regular sized frame. - */ - private transient boolean maxTransition = false; - /** DOCUMENT ME! */ private transient boolean wasIcon = false; @@ -581,11 +577,12 @@ public class JInternalFrame extends JComponent implements Accessible, this.closable = closable; this.maximizable = maximizable; this.iconable = iconifiable; - storedBounds = new Rectangle(); + isMaximum = false; setRootPane(createRootPane()); // JInternalFrames are invisible and opaque by default. setVisible(false); setOpaque(true); + desktopIcon = new JDesktopIcon(this); updateUI(); setRootPaneCheckingEnabled(true); // Done the init stage, now adds go to content pane. } @@ -643,24 +640,25 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void dispose() { - setVisible(false); - JDesktopPane pane = getDesktopPane(); - if (pane != null) - pane.setSelectedFrame(null); - else + if (isVisible()) + setVisible(false); + if (isSelected()) { - try - { - setSelected(false); - } - catch (PropertyVetoException e) - { - // Do nothing if they don't want to be unselected. - } + try + { + setSelected(false); + } + catch (PropertyVetoException e) + { + // Do nothing if they don't want to be unselected. + } + } + if (! isClosed) + { + firePropertyChange(IS_CLOSED_PROPERTY, Boolean.FALSE, Boolean.TRUE); + isClosed = true; } - isClosed = true; fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_CLOSED); - removeNotify(); } /** @@ -799,8 +797,6 @@ public class JInternalFrame extends JComponent implements Accessible, */ public JDesktopIcon getDesktopIcon() { - if (desktopIcon == null) - desktopIcon = new JDesktopIcon(this); return desktopIcon; } @@ -904,18 +900,7 @@ public class JInternalFrame extends JComponent implements Accessible, */ public int getLayer() { - JDesktopPane pane = getDesktopPane(); - if (pane != null) - // The cast here forces the call to the instance method getLayer() - // instead of the static method (this would lead to infinite - // recursion). - return pane.getLayer((Component) this); - - Integer layer = (Integer) getClientProperty(JLayeredPane.LAYER_PROPERTY); - if (layer != null) - return layer.intValue(); - - return JLayeredPane.DEFAULT_LAYER.intValue(); + return JLayeredPane.getLayer(this); } /** @@ -970,7 +955,7 @@ public class JInternalFrame extends JComponent implements Accessible, */ public Rectangle getNormalBounds() { - if (! isMaximum() && ! maxTransition) + if (storedBounds == null) return getBounds(); else return storedBounds; @@ -1035,20 +1020,8 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void hide() { - JDesktopPane pane = getDesktopPane(); - if (pane != null) - pane.setSelectedFrame(null); - else - { - try - { - setSelected(false); - } - catch (PropertyVetoException e) - { - // Do nothing. - } - } + if (isIcon()) + getDesktopIcon().hide(); super.hide(); } @@ -1162,8 +1135,9 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void moveToBack() { - if (getParent() instanceof JLayeredPane) - ((JLayeredPane) getParent()).moveToBack(this); + Container p = getParent(); + if (p instanceof JLayeredPane) + ((JLayeredPane) p).moveToBack(this); } /** @@ -1172,8 +1146,9 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void moveToFront() { - if (getParent() instanceof JLayeredPane) - ((JLayeredPane) getParent()).moveToFront(this); + Container p = getParent(); + if (p != null && p instanceof JLayeredPane) + ((JLayeredPane) p).moveToFront(this); } /** @@ -1196,6 +1171,7 @@ public class JInternalFrame extends JComponent implements Accessible, // Do nothing if they don't want to be restored first. } setSize(getPreferredSize()); + validate(); } /** @@ -1311,7 +1287,6 @@ public class JInternalFrame extends JComponent implements Accessible, dispose(); firePropertyChange(IS_CLOSED_PROPERTY, false, true); - fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_CLOSED); } } @@ -1468,7 +1443,9 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void setJMenuBar(JMenuBar b) { + JMenuBar old = getJMenuBar(); getRootPane().setJMenuBar(b); + firePropertyChange(MENU_BAR_PROPERTY, old, b); } /** @@ -1493,11 +1470,17 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void setLayer(Integer layer) { - JDesktopPane p = getDesktopPane(); - if (p != null) + Container p = getParent(); + if (p instanceof JLayeredPane) { - int pos = p.getPosition(this); - p.setLayer(this, layer.intValue(), pos); + JLayeredPane lp = (JLayeredPane) p; + lp.setLayer(this, layer.intValue(), lp.getPosition(this)); + } + else + { + JLayeredPane.putLayer(this, layer.intValue()); + if (p != null) + p.repaint(getX(), getY(), getWidth(), getHeight()); } } @@ -1508,6 +1491,9 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void setLayeredPane(JLayeredPane layered) { + if (layered == null) + throw new IllegalComponentStateException("LayeredPane must not be null"); + if (layered != getLayeredPane()) { JLayeredPane old = getLayeredPane(); @@ -1561,15 +1547,11 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void setMaximum(boolean b) throws PropertyVetoException { - if (b != isMaximum()) + if (b != isMaximum) { - fireVetoableChange(IS_MAXIMUM_PROPERTY, b, isMaximum); + fireVetoableChange(IS_MAXIMUM_PROPERTY, isMaximum, b); isMaximum = b; - if (b) - setNormalBounds(getBounds()); - maxTransition = ! b; firePropertyChange(IS_MAXIMUM_PROPERTY, ! isMaximum, isMaximum); - maxTransition = false; } } @@ -1593,7 +1575,7 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void setNormalBounds(Rectangle r) { - storedBounds.setBounds(r.x, r.y, r.width, r.height); + storedBounds = r; } /** @@ -1621,8 +1603,23 @@ public class JInternalFrame extends JComponent implements Accessible, if (rootPane != null) remove(rootPane); + JRootPane old = rootPane; rootPane = root; - add(root); + + if (rootPane != null) + { + boolean checkingEnabled = isRootPaneCheckingEnabled(); + try + { + setRootPaneCheckingEnabled(false); + add(rootPane, BorderLayout.CENTER); + } + finally + { + setRootPaneCheckingEnabled(checkingEnabled); + } + } + firePropertyChange(ROOT_PANE_PROPERTY, old, rootPane); } /** @@ -1652,27 +1649,26 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void setSelected(boolean selected) throws PropertyVetoException { - if (selected != isSelected()) + if (selected != isSelected + && (! selected || (isIcon ? desktopIcon.isShowing() : isShowing()))) { - fireVetoableChange(IS_SELECTED_PROPERTY, selected, isSelected); - - if (! selected) - defaultFocus = getMostRecentFocusOwner(); + fireVetoableChange(IS_SELECTED_PROPERTY, isSelected, selected); - isSelected = selected; + if (! selected) + defaultFocus = getMostRecentFocusOwner(); - if (selected) - restoreSubcomponentFocus(); + isSelected = selected; + firePropertyChange(IS_SELECTED_PROPERTY, ! isSelected, isSelected); - if (isShowing()) - repaint(); + if (isSelected) + fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_ACTIVATED); + else + fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_DEACTIVATED); - firePropertyChange(IS_SELECTED_PROPERTY, ! isSelected, isSelected); + if (selected) + restoreSubcomponentFocus(); - if (isSelected) - fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_ACTIVATED); - else - fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_DEACTIVATED); + repaint(); } } @@ -1687,14 +1683,9 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void setTitle(String title) { - if (title == null && this.title == null) - return; - if (title == null || this.title == null || ! this.title.equals(title)) - { - String old = this.title; - this.title = title; - firePropertyChange(TITLE_PROPERTY, old, this.title); - } + String old = this.title; + this.title = title; + firePropertyChange(TITLE_PROPERTY, old, this.title); } /** @@ -1707,12 +1698,21 @@ public class JInternalFrame extends JComponent implements Accessible, { if (! isVisible()) { + if (isFirstTimeVisible) + { + isFirstTimeVisible = false; + fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_OPENED); + } + + getDesktopIcon().setVisible(true); + + toFront(); super.show(); - JDesktopPane pane = getDesktopPane(); - if (pane != null) - pane.setSelectedFrame(this); - else + if (isIcon()) + return; + + if (! isSelected()) { try { @@ -1723,11 +1723,6 @@ public class JInternalFrame extends JComponent implements Accessible, // Do nothing. if they don't want to be selected. } } - if (isFirstTimeVisible) - { - isFirstTimeVisible = false; - fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_OPENED); - } } } diff --git a/libjava/classpath/javax/swing/JLabel.java b/libjava/classpath/javax/swing/JLabel.java index a5fe3ba1af0..fcf0fd7cb13 100644 --- a/libjava/classpath/javax/swing/JLabel.java +++ b/libjava/classpath/javax/swing/JLabel.java @@ -46,6 +46,7 @@ import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.KeyEvent; +import java.beans.PropertyChangeEvent; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; @@ -57,7 +58,7 @@ import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; /** - * A swing widget that displays a text message and/or an icon. + * A component that displays a static text message and/or an icon. */ public class JLabel extends JComponent implements Accessible, SwingConstants { @@ -359,7 +360,7 @@ public class JLabel extends JComponent implements Accessible, SwingConstants /** The label's mnemnonic key. */ private transient int displayedMnemonic = KeyEvent.VK_UNDEFINED; - /** The index of the menemonic character in the text. */ + /** The index of the mnemonic character in the text. */ private transient int displayedMnemonicIndex = -1; /** The gap between the icon and the text. */ @@ -435,11 +436,12 @@ public class JLabel extends JComponent implements Accessible, SwingConstants this.icon = icon; this.horizontalAlignment = horizontalAlignment; setAlignmentX(0.0F); + setInheritsPopupMenu(true); updateUI(); } /** - * This method returns the label's UI delegate. + * Returns the label's UI delegate. * * @return The label's UI delegate. */ @@ -449,9 +451,9 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method sets the label's UI delegate. + * Sets the label's UI delegate. * - * @param ui The label's UI delegate. + * @param ui The label's UI delegate (<code>null</code> not permitted). */ public void setUI(LabelUI ui) { @@ -459,8 +461,8 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method resets the label's UI delegate to the default UI for the - * current look and feel. + * Resets the label's UI delegate to the default UI for the current look and + * feel. */ public void updateUI() { @@ -468,10 +470,10 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method returns a name to identify which look and feel class will be + * Returns a name to identify which look and feel class will be * the UI delegate for this label. * - * @return The UIClass identifier. "LabelUI" + * @return <code>"LabelUI"</code> */ public String getUIClassID() { @@ -518,9 +520,11 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method returns the label text. + * Returns the text displayed by the label. * - * @return The label text. + * @return The label text (possibly <code>null</code>). + * + * @see #setText(String) */ public String getText() { @@ -528,31 +532,42 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method changes the "text" property. The given text will be painted - * in the label. + * Sets the text for the label and sends a {@link PropertyChangeEvent} (with + * the name 'text') to all registered listeners. This method will also + * update the <code>displayedMnemonicIndex</code>, if necessary. * - * @param newText The label's text. + * @param newText The text (<code>null</code> permitted). + * + * @see #getText() + * @see #getDisplayedMnemonicIndex() */ public void setText(String newText) { - if (text != newText) - { - String oldText = text; - text = newText; - firePropertyChange("text", oldText, newText); + if (text == null && newText == null) + return; + if (text != null && text.equals(newText)) + return; - if (text != null && text.length() <= displayedMnemonicIndex) - setDisplayedMnemonicIndex(text.length() - 1); - revalidate(); - repaint(); - } + String oldText = text; + text = newText; + firePropertyChange("text", oldText, newText); + + if (text != null) + setDisplayedMnemonicIndex(text.toUpperCase().indexOf(displayedMnemonic)); + else + setDisplayedMnemonicIndex(-1); + revalidate(); + repaint(); } /** - * This method returns the active icon. The active icon is painted when the - * label is enabled. + * Returns the active icon. The active icon is painted when the label is + * enabled. * * @return The active icon. + * + * @see #setIcon(Icon) + * @see #getDisabledIcon() */ public Icon getIcon() { @@ -560,81 +575,93 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method changes the "icon" property. This icon (the active icon) will - * be the one displayed when the label is enabled. + * Sets the icon for the label (this is a bound property with the name + * 'icon'). This icon will be displayed when the label is enabled. * - * @param newIcon The active icon. + * @param newIcon The icon (<code>null</code> permitted). + * + * @see #getIcon() + * @see #setDisabledIcon(Icon) */ public void setIcon(Icon newIcon) { if (icon != newIcon) { - Icon oldIcon = icon; - icon = newIcon; - firePropertyChange("icon", oldIcon, newIcon); - repaint(); + Icon oldIcon = icon; + icon = newIcon; + firePropertyChange("icon", oldIcon, newIcon); + repaint(); } } /** - * This method returns the disabled icon. The disabled icon is painted when - * the label is disabled. If the disabled icon is null and the active icon - * is an ImageIcon, this method returns a grayed version of the icon. The - * grayed version of the icon becomes the disabledIcon. + * Returns the disabled icon. The disabled icon is painted when the label is + * disabled. If the disabled icon is <code>null</code> and the active icon + * is an {@link ImageIcon}, this method returns a grayed version of the icon. + * The grayed version of the icon becomes the <code>disabledIcon</code>. * * @return The disabled icon. + * + * @see #setDisabledIcon(Icon) */ public Icon getDisabledIcon() { if (disabledIcon == null && icon instanceof ImageIcon) - disabledIcon = new ImageIcon(GrayFilter.createDisabledImage(((ImageIcon) icon) - .getImage())); + disabledIcon = new ImageIcon( + GrayFilter.createDisabledImage(((ImageIcon) icon).getImage())); return disabledIcon; } /** - * This method changes the "disabledIcon" property. This icon (the disabled - * icon) will be the one displayed when the label is disabled. + * Sets the icon displayed when the label is disabled (this is a bound + * property with the name 'disabledIcon'). * - * @param newIcon The disabled icon. + * @param newIcon The disabled icon (<code>null</code> permitted). + * + * @see #getDisabledIcon() */ public void setDisabledIcon(Icon newIcon) { if (disabledIcon != newIcon) { - Icon oldIcon = disabledIcon; - disabledIcon = newIcon; - firePropertyChange("disabledIcon", oldIcon, newIcon); + Icon oldIcon = disabledIcon; + disabledIcon = newIcon; + firePropertyChange("disabledIcon", oldIcon, newIcon); } } /** - * This method sets the keycode that will be the label's mnemonic. If the - * label is used as a label for another component, the label will give - * focus to that component when the mnemonic is activated. + * Sets the keycode that will be the label's mnemonic (this is a bound + * property with the name 'displayedMnemonic'). If the label is used as a + * label for another component, the label will give focus to that component + * when the mnemonic is activated. * * @param mnemonic The keycode to use for the mnemonic. + * + * @see #getDisplayedMnemonic() */ public void setDisplayedMnemonic(int mnemonic) { if (displayedMnemonic != mnemonic) { - firePropertyChange("displayedMnemonic", - displayedMnemonic, mnemonic); - displayedMnemonic = mnemonic; - - if (text != null) - setDisplayedMnemonicIndex(text.toUpperCase().indexOf(mnemonic)); + int old = displayedMnemonic; + displayedMnemonic = mnemonic; + firePropertyChange("displayedMnemonic", old, displayedMnemonic); + if (text != null) + setDisplayedMnemonicIndex(text.toUpperCase().indexOf(mnemonic)); } } /** - * This method sets the character that will be the mnemonic used. If the + * Sets the character that will be the label's mnemonic. If the * label is used as a label for another component, the label will give - * focus to that component when the mnemonic is activated. + * focus to that component when the mnemonic is activated via the keyboard. * - * @param mnemonic The character to use for the mnemonic. + * @param mnemonic The character to use for the mnemonic (this will be + * converted to the equivalent upper case character). + * + * @see #getDisplayedMnemonic() */ public void setDisplayedMnemonic(char mnemonic) { @@ -642,51 +669,63 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method returns the keycode that is used for the label's mnemonic. + * Returns the keycode that is used for the label's mnemonic. * * @return The keycode that is used for the label's mnemonic. + * + * @see #setDisplayedMnemonic(int) */ public int getDisplayedMnemonic() { - return (int) displayedMnemonic; + return displayedMnemonic; } /** - * This method sets which character in the text will be the underlined - * character. If the given index is -1, then this indicates that there is - * no mnemonic. If the index is less than -1 or if the index is equal to - * the length, this method will throw an IllegalArgumentException. + * Sets the index of the character in the text that will be underlined to + * indicate that it is the mnemonic character for the label. You only need + * to call this method if you wish to override the automatically calculated + * character index. For instance, for a label "Find Next" with the mnemonic + * character 'n', you might wish to underline the second occurrence of 'n' + * rather than the first (which is the default). + * <br><br> + * Note that this method does not validate the character at the specified + * index to ensure that it matches the key code returned by + * {@link #getDisplayedMnemonic()}. * * @param newIndex The index of the character to underline. * - * @throws IllegalArgumentException If index less than -1 or index equals - * length. + * @throws IllegalArgumentException If index less than -1 or index is greater + * than or equal to the label length. + * + * @see #getDisplayedMnemonicIndex() + * @since 1.4 */ public void setDisplayedMnemonicIndex(int newIndex) throws IllegalArgumentException { - if (newIndex < -1 || (text != null && newIndex >= text.length())) + int maxValid = -1; + if (text != null) + maxValid = text.length() - 1; + if (newIndex < -1 || newIndex > maxValid) throw new IllegalArgumentException(); - if (newIndex == -1 - || text == null - || text.charAt(newIndex) != displayedMnemonic) - newIndex = -1; - if (newIndex != displayedMnemonicIndex) { - int oldIndex = displayedMnemonicIndex; - displayedMnemonicIndex = newIndex; - firePropertyChange("displayedMnemonicIndex", - oldIndex, newIndex); + int oldIndex = displayedMnemonicIndex; + displayedMnemonicIndex = newIndex; + firePropertyChange("displayedMnemonicIndex", oldIndex, newIndex); } } /** - * This method returns which character in the text will be the underlined - * character. + * Returns the index of the character in the label's text that will be + * underlined (to indicate that it is the mnemonic character), or -1 if no + * character is to be underlined. * * @return The index of the character that will be underlined. + * + * @see #setDisplayedMnemonicIndex(int) + * @since 1.4 */ public int getDisplayedMnemonicIndex() { @@ -694,14 +733,16 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method ensures that the key is valid as a horizontal alignment. - * Valid keys are: LEFT, CENTER, RIGHT, LEADING, TRAILING + * Checks the specified key to ensure that it is valid as a horizontal + * alignment, throwing an {@link IllegalArgumentException} if the key is + * invalid. Valid keys are {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, + * {@link #LEADING} and {@link #TRAILING}. * * @param key The key to check. * @param message The message of the exception to be thrown if the key is * invalid. * - * @return The key if it's valid. + * @return The key if it is valid. * * @throws IllegalArgumentException If the key is invalid. */ @@ -715,14 +756,15 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method ensures that the key is valid as a vertical alignment. Valid - * keys are: TOP, CENTER, and BOTTOM. + * Checks the specified key to ensure that it is valid as a vertical + * alignment, throwing an {@link IllegalArgumentException} if the key is + * invalid. Valid keys are {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}. * * @param key The key to check. * @param message The message of the exception to be thrown if the key is * invalid. * - * @return The key if it's valid. + * @return The key if it is valid. * * @throws IllegalArgumentException If the key is invalid. */ @@ -735,9 +777,11 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method returns the gap between the icon and the text. + * Returns the gap between the icon and the text. * * @return The gap between the icon and the text. + * + * @see #setIconTextGap(int) */ public int getIconTextGap() { @@ -745,10 +789,12 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method changes the "iconTextGap" property. The iconTextGap - * determines how much space there is between the icon and the text. + * Sets the gap between the icon and the text, in the case that both are + * visible (this is a bound property with the name 'iconTextGap'). * - * @param newGap The gap between the icon and the text. + * @param newGap The gap (in pixels). + * + * @see #getIconTextGap() */ public void setIconTextGap(int newGap) { @@ -760,9 +806,13 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method returns the vertical alignment of the label. + * Returns the vertical alignment of the label (one of + * {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}). The default value + * depends on the installed look and feel, but is usually {@link #CENTER}. * - * @return The vertical alignment of the label. + * @return The vertical alignment. + * + * @see #setVerticalAlignment(int) */ public int getVerticalAlignment() { @@ -770,12 +820,18 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method changes the "verticalAlignment" property of the label. The - * vertical alignment determines how where the label will be placed - * vertically. If the alignment is not valid, it will default to the - * center. + * Sets the vertical alignment for the label (this is a bound property with + * the name 'verticalAlignment'). The vertical alignment determines where + * the label (icon and text) will be placed vertically within the component + * bounds. Valid alignment codes are {@link #TOP}, {@link #CENTER} and + * {@link #BOTTOM}. * * @param alignment The vertical alignment of the label. + * + * @throws IllegalArgumentException if <code>alignment</code> is not one of + * the specified values. + * + * @see #getVerticalAlignment() */ public void setVerticalAlignment(int alignment) { @@ -788,9 +844,14 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method returns the horziontal alignment of the label. + * Returns the horizontal alignment of the label (one of {@link #LEFT}, + * {@link #CENTER}, {@link #RIGHT}, {@link #LEADING} and {@link #TRAILING}). + * The default value depends on the installed look and feel, but is usually + * {@link #LEFT}. * - * @return The horizontal alignment of the label. + * @return The horizontal alignment. + * + * @see #setHorizontalAlignment(int) */ public int getHorizontalAlignment() { @@ -798,10 +859,18 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method changes the "horizontalAlignment" property. The horizontal - * alignment determines where the label will be placed horizontally. + * Sets the horizontal alignment for the label (this is a bound property with + * the name 'horizontalAlignment'). The horizontal alignment determines where + * the label (icon and text) will be placed horizontally within the + * component bounds. Valid alignment codes are {@link #LEFT}, + * {@link #CENTER}, {@link #RIGHT}, {@link #LEADING} and {@link #TRAILING}. * * @param alignment The horizontal alignment of the label. + * + * @throws IllegalArgumentException if <code>alignment</code> is not one of + * the specified values. + * + * @see #getHorizontalAlignment() */ public void setHorizontalAlignment(int alignment) { @@ -815,9 +884,12 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method returns the vertical text position of the label. - * - * @return The vertical text position of the label. + * Returns the vertical position of the label's text relative to the icon. + * This will be one of {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}. + * + * @return The vertical position of the label's text relative to the icon. + * + * @see #setVerticalTextPosition(int) */ public int getVerticalTextPosition() { @@ -825,28 +897,35 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method changes the "verticalTextPosition" property of the label. The - * vertical text position determines where the text will be placed - * vertically relative to the icon. + * Sets the vertical position of the label's text relative to the icon (this + * is a bound property with the name 'verticalTextPosition'). Valid + * positions are {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}. * * @param textPosition The vertical text position. + * + * @throws IllegalArgumentException if <code>textPosition</code> is not one + * of the specified values. */ public void setVerticalTextPosition(int textPosition) { if (textPosition != verticalTextPosition) { - int oldPos = verticalTextPosition; - verticalTextPosition = checkVerticalKey(textPosition, - "verticalTextPosition"); - firePropertyChange("verticalTextPosition", oldPos, - verticalTextPosition); + int oldPos = verticalTextPosition; + verticalTextPosition = checkVerticalKey(textPosition, + "verticalTextPosition"); + firePropertyChange("verticalTextPosition", oldPos, + verticalTextPosition); } } /** - * This method returns the horizontal text position of the label. - * - * @return The horizontal text position. + * Returns the horizontal position of the label's text relative to the icon. + * This will be one of {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, + * {@link #LEADING} and {@link #TRAILING}. + * + * @return The horizontal position of the label's text relative to the icon. + * + * @see #setHorizontalTextPosition(int) */ public int getHorizontalTextPosition() { @@ -854,28 +933,31 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method changes the "horizontalTextPosition" property of the label. - * The horizontal text position determines where the text will be placed - * horizontally relative to the icon. + * Sets the horizontal position of the label's text relative to the icon (this + * is a bound property with the name 'horizontalTextPosition'). Valid + * positions are {@link #LEFT}, {@link #CENTER}, {@link #RIGHT}, + * {@link #LEADING} and {@link #TRAILING}. * * @param textPosition The horizontal text position. + * + * @throws IllegalArgumentException if <code>textPosition</code> is not one + * of the specified values. */ public void setHorizontalTextPosition(int textPosition) { if (textPosition != horizontalTextPosition) { - int oldPos = horizontalTextPosition; - horizontalTextPosition = checkHorizontalKey(textPosition, - "horizontalTextPosition"); - firePropertyChange("horizontalTextPosition", oldPos, - horizontalTextPosition); + int oldPos = horizontalTextPosition; + horizontalTextPosition = checkHorizontalKey(textPosition, + "horizontalTextPosition"); + firePropertyChange("horizontalTextPosition", oldPos, + horizontalTextPosition); } } /** - * This method simply returns false if the current icon image (current icon - * will depend on whether the label is enabled) is not equal to the passed - * in image. + * Returns false if the current icon image (current icon will depend on + * whether the label is enabled) is not equal to the passed in image. * * @param img The image to check. * @param infoflags The bitwise inclusive OR of ABORT, ALLBITS, ERROR, @@ -900,11 +982,11 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method returns the component that the label gives focus to when the - * mnemonic is activated. + * Returns the component that this <code>JLabel</code> is providing the label + * for. This component will typically receive the focus when the label's + * mnemonic key is activated via the keyboard. * - * @return The component that gets focus when the label's mnemonic is - * activated. + * @return The component (possibly <code>null</code>). */ public Component getLabelFor() { @@ -912,12 +994,14 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method changes the "labelFor" property. The component that the label - * is acting as a label for will request focus when the label's mnemonic - * is activated. + * Sets the component that this <code>JLabel</code> is providing the label + * for (this is a bound property with the name 'labelFor'). This component + * will typically receive the focus when the label's mnemonic key is + * activated via the keyboard. * - * @param c The component that gets focus when the label's mnemonic is - * activated. + * @param c the component (<code>null</code> permitted). + * + * @see #getLabelFor() */ public void setLabelFor(Component c) { @@ -946,10 +1030,9 @@ public class JLabel extends JComponent implements Accessible, SwingConstants } /** - * This method overrides setFont so that we can call for a repaint after the - * font is changed. + * Sets the font for the label (this a bound property with the name 'font'). * - * @param f The font for this label. + * @param f The font (<code>null</code> permitted). */ public void setFont(Font f) { diff --git a/libjava/classpath/javax/swing/JLayeredPane.java b/libjava/classpath/javax/swing/JLayeredPane.java index 11650e721d0..ca913e97fed 100644 --- a/libjava/classpath/javax/swing/JLayeredPane.java +++ b/libjava/classpath/javax/swing/JLayeredPane.java @@ -43,6 +43,7 @@ import java.awt.Component; import java.awt.Container; import java.awt.Graphics; import java.awt.Rectangle; +import java.util.ArrayList; import java.util.Hashtable; import javax.accessibility.Accessible; @@ -326,13 +327,13 @@ public class JLayeredPane extends JComponent implements Accessible { int pos = -1; int index = getIndexOf(c); - Component[] components = getComponents(); - int layer = getLayer(c); if (index >= 0) { - for (int i = index; i >= 0; --i) + pos = 0; + int layer = getLayer(c); + for (int i = index - 1; i >= 0; --i) { - if (layer == getLayer(components[i])) + if (layer == getLayer(getComponent(i))) pos++; else break; @@ -353,9 +354,7 @@ public class JLayeredPane extends JComponent implements Accessible */ public void setPosition(Component c, int position) { - int layer = getLayer(c); - int index = insertIndexForLayer(layer, position); - setComponentZOrder(c, index); + setLayer(c, getLayer(c), position); } /** @@ -478,34 +477,85 @@ public class JLayeredPane extends JComponent implements Accessible */ protected int insertIndexForLayer(int layer, int position) { - // position < 0 means insert at greatest position within layer. - if (position < 0) - position = Integer.MAX_VALUE; + return insertIndexForLayer(null, layer, position); + } - Component[] components = getComponents(); - int index = 0; + /** + * Similar to {@link #insertIndexForLayer(int, int)}, only that it takes a + * component parameter, which should be ignored in the search. This is + * necessary to support {@link #setLayer(Component, int, int)} which uses + * Container.setComponentZOrder(), which doesn't remove the component. + * + * @param comp the component to ignore + * @param layer the layer + * @param position the position + * + * @return the insertion index + */ + private int insertIndexForLayer(Component comp, int layer, int position) + { + // Create the component list to search through. + ArrayList l = new ArrayList(); + int count = getComponentCount(); + for (int i = 0; i < count; i++) + { + Component c = getComponent(i); + if (c != comp) + l.add(c); + } - // Try to find the start index of the specified layer. - int p = -1; - for (int i = 0; i < components.length; i++) + count = l.size(); + int layerStart = -1; + int layerEnd = -1; + for (int i = 0; i < count; i++) { - int l = getLayer(components[i]); - if (l > layer) - index++; - // If we are in the layer we look for, try to find the position. - else if (l == layer) + int layerOfComponent = getLayer((Component) l.get(i)); + if (layerStart == -1 && layerOfComponent == layer) + layerStart = i; + if (layerOfComponent < layer) { - p++; - if (p < position) - index++; + // We are beyond the layer that we are looking for. Update the + // layerStart and layerEnd and exit the loop. + if (i == 0) + { + layerStart = 0; + layerEnd = 0; + } else - break; + layerEnd = i; + break; } - // No need to look further if the layer at i is smaller than layer. + } + + // No layer found. The requested layer is lower than any existing layer, + // put the component at the end. + int insertIndex; + if (layerStart == -1 && layerEnd == -1) + { + insertIndex = count; + } + else + { + // Corner cases. + if (layerStart != -1 && layerEnd == -1) + layerEnd = count; + if (layerStart == -1 && layerEnd != -1) + layerStart = layerEnd; + + // Adding to the bottom of a layer returns the end index + // in the layer. + if (position == -1) + insertIndex = layerEnd; else - break; + { + // Insert into a layer. + if (position > -1 && layerStart + position <= layerEnd) + insertIndex = layerStart + position; + else + insertIndex = layerEnd; + } } - return index; + return insertIndex; } /** @@ -559,17 +609,29 @@ public class JLayeredPane extends JComponent implements Accessible public void setLayer(Component c, int layer, int position) { Integer layerObj = getObjectForLayer(layer); - if (c instanceof JComponent) + + // Nothing to do if neither the layer nor the position is + // changed. + if (layer != getLayer(c) || position != getPosition(c)) { - JComponent jc = (JComponent) c; - jc.putClientProperty(LAYER_PROPERTY, layerObj); - } - else - componentToLayer.put (c, layerObj); + // Store the layer either in the JComponent or in the hashtable + if (c instanceof JComponent) + { + JComponent jc = (JComponent) c; + jc.putClientProperty(LAYER_PROPERTY, layerObj); + } + else + componentToLayer.put (c, layerObj); - // Set position only of component is already added to this layered pane. - if (getIndexOf(c) != -1) - setPosition(c, position); + // Update the component in the Z order of the Container. + Container parent = c.getParent(); + if (parent == this) + { + int index = insertIndexForLayer(c, layer, position); + setComponentZOrder(c, index); + } + } + repaint(c.getX(), c.getY(), c.getWidth(), c.getHeight()); } /** @@ -592,14 +654,17 @@ public class JLayeredPane extends JComponent implements Accessible { int layer; if (layerConstraint != null && layerConstraint instanceof Integer) - layer = ((Integer) layerConstraint).intValue(); + { + layer = ((Integer) layerConstraint).intValue(); + setLayer(comp, layer); + } else - layer = getLayer(comp); + layer = getLayer(comp); int newIdx = insertIndexForLayer(layer, index); - setLayer(comp, layer); super.addImpl(comp, layerConstraint, newIdx); - repaint(comp.getX(), comp.getY(), comp.getWidth(), comp.getHeight()); + comp.validate(); + comp.repaint(); } /** diff --git a/libjava/classpath/javax/swing/JList.java b/libjava/classpath/javax/swing/JList.java index eab6be3c30c..6a98770eeda 100644 --- a/libjava/classpath/javax/swing/JList.java +++ b/libjava/classpath/javax/swing/JList.java @@ -944,17 +944,6 @@ public class JList extends JComponent implements Accessible, Scrollable */ ListSelectionModel selectionModel; - - /** - * This property indicates that the list's selection is currently - * "adjusting" -- perhaps due to a user actively dragging the mouse over - * multiple list elements -- and is therefore likely to change again in - * the near future. A {@link ListSelectionListener} might choose to delay - * updating its view of the list's selection until this property is - * false, meaning that the adjustment has completed. - */ - boolean valueIsAdjusting; - /** * This property indicates a <em>preference</em> for the number of rows * displayed in the list, and will scale the @@ -1085,8 +1074,7 @@ public class JList extends JComponent implements Accessible, Scrollable fixedCellWidth = -1; layoutOrientation = VERTICAL; opaque = true; - valueIsAdjusting = false; - visibleRowCount = 7; + visibleRowCount = 8; cellRenderer = new DefaultListCellRenderer(); listListener = new ListListener(); @@ -1196,11 +1184,13 @@ public class JList extends JComponent implements Accessible, Scrollable } /** - * Gets the value of the {@link #visibleRowCount} property. + * Gets the value of the {@link #visibleRowCount} property. The default + * value is 8. * * @return the current value of the property. + * + * @see #setVisibleRowCount(int) */ - public int getVisibleRowCount() { return visibleRowCount; @@ -1210,12 +1200,19 @@ public class JList extends JComponent implements Accessible, Scrollable * Sets the value of the {@link #visibleRowCount} property. * * @param vc The new property value + * + * @see #getVisibleRowCount() */ public void setVisibleRowCount(int vc) { - visibleRowCount = vc; - revalidate(); - repaint(); + if (visibleRowCount != vc) + { + int oldValue = visibleRowCount; + visibleRowCount = Math.max(vc, 0); + firePropertyChange("visibleRowCount", oldValue, vc); + revalidate(); + repaint(); + } } /** @@ -2184,23 +2181,25 @@ public class JList extends JComponent implements Accessible, Scrollable } /** - * Returns the value of the <code>valueIsAdjusting</code> property. + * Returns the <code>valueIsAdjusting</code> flag from the list's selection + * model. * * @return the value */ public boolean getValueIsAdjusting() { - return valueIsAdjusting; + return selectionModel.getValueIsAdjusting(); } /** - * Sets the <code>valueIsAdjusting</code> property. + * Sets the <code>valueIsAdjusting</code> flag in the list's selection + * model. * * @param isAdjusting the new value */ public void setValueIsAdjusting(boolean isAdjusting) { - valueIsAdjusting = isAdjusting; + selectionModel.setValueIsAdjusting(isAdjusting); } /** @@ -2228,11 +2227,13 @@ public class JList extends JComponent implements Accessible, Scrollable } /** - * Returns the layout orientation. + * Returns the layout orientation, which will be one of {@link #VERTICAL}, + * {@link #VERTICAL_WRAP} and {@link #HORIZONTAL_WRAP}. The default value + * is {@link #VERTICAL}. * - * @return the orientation, one of <code>JList.VERTICAL</code>, - * <code>JList.VERTICAL_WRAP</code> and <code>JList.HORIZONTAL_WRAP</code> + * @return the orientation. * + * @see #setLayoutOrientation(int) * @since 1.4 */ public int getLayoutOrientation() @@ -2241,15 +2242,21 @@ public class JList extends JComponent implements Accessible, Scrollable } /** - * Sets the layout orientation. + * Sets the layout orientation (this is a bound property with the name + * 'layoutOrientation'). Valid orientations are {@link #VERTICAL}, + * {@link #VERTICAL_WRAP} and {@link #HORIZONTAL_WRAP}. * - * @param orientation the orientation to set, one of <code>JList.VERTICAL</code>, - * <code>JList.VERTICAL_WRAP</code> and <code>JList.HORIZONTAL_WRAP</code> + * @param orientation the orientation. * + * @throws IllegalArgumentException if <code>orientation</code> is not one + * of the specified values. * @since 1.4 + * @see #getLayoutOrientation() */ public void setLayoutOrientation(int orientation) { + if (orientation < JList.VERTICAL || orientation > JList.HORIZONTAL_WRAP) + throw new IllegalArgumentException(); if (layoutOrientation == orientation) return; @@ -2282,14 +2289,16 @@ public class JList extends JComponent implements Accessible, Scrollable } /** - * Returns the next list element (beginning from <code>startIndex</code> - * that starts with <code>prefix</code>. Searching is done in the direction - * specified by <code>bias</code>. + * Returns the index of the next list element (beginning at + * <code>startIndex</code> and moving in the specified direction through the + * list, looping around if necessary) that starts with <code>prefix</code> + * (ignoring case). * * @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 direction the search direction, either {@link Position.Bias#Forward} + * or {@link Position.Bias#Backward} (<code>null</code> is interpreted + * as {@link Position.Bias#Backward}. * * @return the index of the found element or -1 if no such element has * been found @@ -2299,7 +2308,8 @@ public class JList extends JComponent implements Accessible, Scrollable * * @since 1.4 */ - public int getNextMatch(String prefix, int startIndex, Position.Bias bias) + public int getNextMatch(String prefix, int startIndex, + Position.Bias direction) { if (prefix == null) throw new IllegalArgumentException("The argument 'prefix' must not be" @@ -2309,37 +2319,33 @@ public class JList extends JComponent implements Accessible, Scrollable + " be less than zero."); int size = model.getSize(); - if (startIndex > model.getSize()) + if (startIndex >= model.getSize()) throw new IllegalArgumentException("The argument 'startIndex' must not" + " be greater than the number of" + " elements in the ListModel."); - int index = -1; - if (bias == Position.Bias.Forward) - { - for (int i = startIndex; i < size; i++) - { - String item = model.getElementAt(i).toString(); - if (item.startsWith(prefix)) - { - index = i; - break; - } - } - } - else + int result = -1; + int current = startIndex; + int delta = -1; + int itemCount = model.getSize(); + boolean finished = false; + prefix = prefix.toUpperCase(); + + if (direction == Position.Bias.Forward) + delta = 1; + while (!finished) { - for (int i = startIndex; i >= 0; i--) - { - String item = model.getElementAt(i).toString(); - if (item.startsWith(prefix)) - { - index = i; - break; - } - } + String itemStr = model.getElementAt(current).toString().toUpperCase(); + if (itemStr.startsWith(prefix)) + return current; + current = (current + delta); + if (current == -1) + current += itemCount; + else + current = current % itemCount; + finished = current == startIndex; } - return index; + return result; } /** diff --git a/libjava/classpath/javax/swing/JMenu.java b/libjava/classpath/javax/swing/JMenu.java index 02cb20eab57..0840509f906 100644 --- a/libjava/classpath/javax/swing/JMenu.java +++ b/libjava/classpath/javax/swing/JMenu.java @@ -40,6 +40,7 @@ package javax.swing; import java.awt.Component; import java.awt.Point; +import java.awt.PopupMenu; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; @@ -74,7 +75,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement private static final long serialVersionUID = 4227225638931828014L; /** A Popup menu associated with this menu, which pops up when menu is selected */ - private JPopupMenu popupMenu = new JPopupMenu(); + private JPopupMenu popupMenu = null; /** Whenever menu is selected or deselected the MenuEvent is fired to menu's registered listeners. */ @@ -98,6 +99,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement { super(); setOpaque(false); + setDelay(200); } /** @@ -108,8 +110,10 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement public JMenu(String text) { super(text); + popupMenu = new JPopupMenu(); popupMenu.setInvoker(this); setOpaque(false); + setDelay(200); } /** @@ -122,8 +126,10 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement { super(action); createActionChangeListener(this); + popupMenu = new JPopupMenu(); popupMenu.setInvoker(this); setOpaque(false); + setDelay(200); } /** @@ -137,6 +143,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement { // FIXME: tearoff not implemented this(text); + setDelay(200); } /** @@ -148,7 +155,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public JMenuItem add(JMenuItem item) { - return popupMenu.add(item); + return getPopupMenu().add(item); } /** @@ -160,7 +167,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public Component add(Component component) { - popupMenu.insert(component, -1); + getPopupMenu().insert(component, -1); return component; } @@ -174,7 +181,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public Component add(Component component, int index) { - return popupMenu.add(component, index); + return getPopupMenu().add(component, index); } /** @@ -186,7 +193,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public JMenuItem add(String text) { - return popupMenu.add(text); + return getPopupMenu().add(text); } /** @@ -198,7 +205,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public JMenuItem add(Action action) { - return popupMenu.add(action); + return getPopupMenu().add(action); } /** @@ -209,7 +216,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public void remove(JMenuItem item) { - popupMenu.remove(item); + getPopupMenu().remove(item); } /** @@ -219,7 +226,11 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public void remove(int index) { - popupMenu.remove(index); + if (index < 0 || (index > 0 && getMenuComponentCount() == 0)) + throw new IllegalArgumentException(); + + if (getMenuComponentCount() > 0) + popupMenu.remove(index); } /** @@ -229,8 +240,9 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public void remove(Component component) { - int index = popupMenu.getComponentIndex(component); - popupMenu.remove(index); + int index = getPopupMenu().getComponentIndex(component); + if (index >= 0) + getPopupMenu().remove(index); } /** @@ -238,7 +250,8 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public void removeAll() { - popupMenu.removeAll(); + if (popupMenu != null) + popupMenu.removeAll(); } /** @@ -267,7 +280,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement if (index < 0) throw new IllegalArgumentException("index less than zero"); - popupMenu.insert(item, index); + getPopupMenu().insert(item, index); return item; } @@ -381,7 +394,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement super.setSelected(false); super.setArmed(false); fireMenuDeselected(); - popupMenu.setVisible(false); + getPopupMenu().setVisible(false); } } @@ -404,7 +417,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public boolean isPopupMenuVisible() { - return popupMenu.isVisible(); + return getPopupMenu().isVisible(); } /** @@ -415,7 +428,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement public void setPopupMenuVisible(boolean popup) { if (getModel().isEnabled()) - popupMenu.setVisible(popup); + getPopupMenu().setVisible(popup); } /** @@ -530,6 +543,9 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement if (index < 0) throw new IllegalArgumentException("index less than 0"); + if (getItemCount() == 0) + return null; + Component c = popupMenu.getComponentAtIndex(index); if (c instanceof JMenuItem) @@ -558,7 +574,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement public boolean isTearOff() { // NOT YET IMPLEMENTED - return false; + throw new Error("The method isTearOff() has not yet been implemented."); } /** @@ -568,7 +584,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public int getMenuComponentCount() { - return popupMenu.getComponentCount(); + return getPopupMenu().getComponentCount(); } /** @@ -581,6 +597,9 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public Component getMenuComponent(int index) { + if (getPopupMenu() == null || getMenuComponentCount() == 0) + return null; + return (Component) popupMenu.getComponentAtIndex(index); } @@ -591,7 +610,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public Component[] getMenuComponents() { - return popupMenu.getComponents(); + return getPopupMenu().getComponents(); } /** @@ -626,6 +645,11 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public JPopupMenu getPopupMenu() { + if (popupMenu == null) + { + popupMenu = new JPopupMenu(); + popupMenu.setInvoker(this); + } return popupMenu; } diff --git a/libjava/classpath/javax/swing/JMenuItem.java b/libjava/classpath/javax/swing/JMenuItem.java index 90d54b1d30a..f7f93bf00d7 100644 --- a/libjava/classpath/javax/swing/JMenuItem.java +++ b/libjava/classpath/javax/swing/JMenuItem.java @@ -184,7 +184,7 @@ public class JMenuItem extends AbstractButton implements Accessible, out statement below for now. */ //borderPainted = false; focusPainted = false; - horizontalAlignment = JButton.LEFT; + horizontalAlignment = JButton.LEADING; horizontalTextPosition = JButton.TRAILING; } diff --git a/libjava/classpath/javax/swing/JOptionPane.java b/libjava/classpath/javax/swing/JOptionPane.java index f9490553834..43caecd1a48 100644 --- a/libjava/classpath/javax/swing/JOptionPane.java +++ b/libjava/classpath/javax/swing/JOptionPane.java @@ -48,6 +48,8 @@ import java.awt.MenuComponent; import java.awt.Toolkit; import java.awt.event.MouseAdapter; import java.awt.event.MouseMotionAdapter; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; @@ -378,11 +380,49 @@ public class JOptionPane extends JComponent implements Accessible dialog.setResizable(false); dialog.pack(); dialog.setLocationRelativeTo(parentComponent); - + + addPropertyChangeListener(new ValuePropertyHandler(dialog)); return dialog; } /** + * Handles changes of the value property. Whenever this property changes, + * the JOptionPane dialog should be closed. + */ + private static class ValuePropertyHandler + implements PropertyChangeListener + { + /** + * The dialog to close. + */ + JDialog dialog; + + /** + * Creates a new instance. + * + * @param d the dialog to be closed + */ + ValuePropertyHandler(JDialog d) + { + dialog = d; + } + + /** + * Receives notification when any of the properties change. + */ + public void propertyChange(PropertyChangeEvent p) + { + String prop = p.getPropertyName(); + Object val = p.getNewValue(); + if (prop.equals(VALUE_PROPERTY) && val != null + && val != UNINITIALIZED_VALUE) + { + dialog.setVisible(false); + } + } + } + + /** * This method creates a new JInternalFrame that is in the JLayeredPane * which contains the parentComponent given. If no suitable JLayeredPane * can be found from the parentComponent given, a RuntimeException will be diff --git a/libjava/classpath/javax/swing/JPopupMenu.java b/libjava/classpath/javax/swing/JPopupMenu.java index c7890ea0e1e..d46015afdf3 100644 --- a/libjava/classpath/javax/swing/JPopupMenu.java +++ b/libjava/classpath/javax/swing/JPopupMenu.java @@ -120,7 +120,7 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement private boolean lightWeightPopupEnabled; /** SelectionModel that keeps track of menu selection. */ - private SingleSelectionModel selectionModel; + protected SingleSelectionModel selectionModel; /* Popup that is used to display JPopupMenu */ private transient Popup popup; diff --git a/libjava/classpath/javax/swing/JRadioButtonMenuItem.java b/libjava/classpath/javax/swing/JRadioButtonMenuItem.java index 0d7c1d10533..13ef189a514 100644 --- a/libjava/classpath/javax/swing/JRadioButtonMenuItem.java +++ b/libjava/classpath/javax/swing/JRadioButtonMenuItem.java @@ -144,6 +144,7 @@ public class JRadioButtonMenuItem extends JMenuItem implements Accessible super(text, icon); setModel(new JToggleButton.ToggleButtonModel()); model.setSelected(selected); + setFocusable(false); } /** diff --git a/libjava/classpath/javax/swing/JSplitPane.java b/libjava/classpath/javax/swing/JSplitPane.java index 2747686a31a..5b77f5176ae 100644 --- a/libjava/classpath/javax/swing/JSplitPane.java +++ b/libjava/classpath/javax/swing/JSplitPane.java @@ -708,7 +708,8 @@ public class JSplitPane extends JComponent implements Accessible throw new IllegalArgumentException ("proportion has to be between 0 and 1."); - int max = (orientation == HORIZONTAL_SPLIT) ? getWidth() : getHeight(); + int max = ((orientation == HORIZONTAL_SPLIT) ? getWidth() : getHeight()) + - getDividerSize(); setDividerLocation((int) (proportionalLocation * max)); } diff --git a/libjava/classpath/javax/swing/JTabbedPane.java b/libjava/classpath/javax/swing/JTabbedPane.java index ee6af857ee3..5c8d0474852 100644 --- a/libjava/classpath/javax/swing/JTabbedPane.java +++ b/libjava/classpath/javax/swing/JTabbedPane.java @@ -990,6 +990,8 @@ public class JTabbedPane extends JComponent implements Serializable, checkIndex(index, -1, tabs.size()); if (index != getSelectedIndex()) { + // Hiding and showing the involved components + // is done by the JTabbedPane's UI. model.setSelectedIndex(index); } } @@ -1247,7 +1249,32 @@ public class JTabbedPane extends JComponent implements Serializable, */ public void remove(Component component) { - super.remove(component); + // Since components implementing UIResource + // are not added as regular tabs by the add() + // methods we have to take special care when + // removing these object. Especially + // Container.remove(Component) cannot be used + // because it will call JTabbedPane.remove(int) + // later which is overridden and can only + // handle tab components. + // This implementation can even cope with a + // situation that someone called insertTab() + // with a component that implements UIResource. + int index = indexOfComponent(component); + + // If the component is not a tab component + // find out its Container-given index + // and call that class' implementation + // directly. + if (index == -1) + { + Component[] cs = getComponents(); + for (int i = 0; i< cs.length; i++) + if (cs[i] == component) + super.remove(i); + } + else + removeTabAt(index); } /** @@ -1257,7 +1284,6 @@ public class JTabbedPane extends JComponent implements Serializable, */ public void remove(int index) { - super.remove(index); removeTabAt(index); } diff --git a/libjava/classpath/javax/swing/JTable.java b/libjava/classpath/javax/swing/JTable.java index 855530881f5..5c7bff5d019 100644 --- a/libjava/classpath/javax/swing/JTable.java +++ b/libjava/classpath/javax/swing/JTable.java @@ -2922,56 +2922,189 @@ public class JTable { // update the column model from the table model if the structure has // changed and the flag autoCreateColumnsFromModel is set - if ((event == null || (event.getFirstRow() == TableModelEvent.HEADER_ROW)) - && autoCreateColumnsFromModel) + if (event == null || (event.getFirstRow() == TableModelEvent.HEADER_ROW)) + handleCompleteChange(event); + else if (event.getType() == TableModelEvent.INSERT) + handleInsert(event); + else if (event.getType() == TableModelEvent.DELETE) + handleDelete(event); + else + handleUpdate(event); + } + + /** + * Handles a request for complete relayout. This is the case when + * event.getFirstRow() == TableModelEvent.HEADER_ROW. + * + * @param ev the table model event + */ + private void handleCompleteChange(TableModelEvent ev) + { + clearSelection(); + checkSelection(); + rowHeights = null; + if (getAutoCreateColumnsFromModel()) + createDefaultColumnsFromModel(); + else + resizeAndRepaint(); + } + + /** + * Handles table model insertions. + * + * @param ev the table model event + */ + private void handleInsert(TableModelEvent ev) + { + // Sync selection model with data model. + int first = ev.getFirstRow(); + if (first < 0) + first = 0; + int last = ev.getLastRow(); + if (last < 0) + last = getRowCount() - 1; + selectionModel.insertIndexInterval(first, last - first + 1, true); + checkSelection(); + + // For variable height rows we must update the SizeSequence thing. + if (rowHeights != null) { - rowHeights = null; - if (getAutoCreateColumnsFromModel()) - createDefaultColumnsFromModel(); - resizeAndRepaint(); - return; + rowHeights.insertEntries(first, last - first + 1, rowHeight); + // TODO: We repaint the whole thing when the rows have variable + // heights. We might want to handle this better though. + repaint(); + } + else + { + // Repaint the dirty region and revalidate. + int rowHeight = getRowHeight(); + Rectangle dirty = new Rectangle(0, first * rowHeight, + getColumnModel().getTotalColumnWidth(), + (getRowCount() - first) * rowHeight); + repaint(dirty); + } + revalidate(); + } + + /** + * Handles table model deletions. + * + * @param ev the table model event + */ + private void handleDelete(TableModelEvent ev) + { + // Sync selection model with data model. + int first = ev.getFirstRow(); + if (first < 0) + first = 0; + int last = ev.getLastRow(); + if (last < 0) + last = getRowCount() - 1; + + selectionModel.removeIndexInterval(first, last); + + checkSelection(); + + if (dataModel.getRowCount() == 0) + clearSelection(); + + // For variable height rows we must update the SizeSequence thing. + if (rowHeights != null) + { + rowHeights.removeEntries(first, last - first + 1); + // TODO: We repaint the whole thing when the rows have variable + // heights. We might want to handle this better though. + repaint(); + } + else + { + // Repaint the dirty region and revalidate. + int rowHeight = getRowHeight(); + int oldRowCount = getRowCount() + last - first + 1; + Rectangle dirty = new Rectangle(0, first * rowHeight, + getColumnModel().getTotalColumnWidth(), + (oldRowCount - first) * rowHeight); + repaint(dirty); } + revalidate(); + } - // 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 == null || event.getType() == TableModelEvent.INSERT) + /** + * Handles table model updates without structural changes. + * + * @param ev the table model event + */ + private void handleUpdate(TableModelEvent ev) + { + if (rowHeights == null) { - // Sync selection model with data model. - if (event != null) + // Some cells have been changed without changing the structure. + // Figure out the dirty rectangle and repaint. + int firstRow = ev.getFirstRow(); + int lastRow = ev.getLastRow(); + int col = ev.getColumn(); + Rectangle dirty; + if (col == TableModelEvent.ALL_COLUMNS) { - int first = event.getFirstRow(); - if (first < 0) - first = 0; - int last = event.getLastRow(); - if (last < 0) - last = getRowCount() - 1; - selectionModel.insertIndexInterval(first, last - first + 1, true); - if (rowHeights != null) - rowHeights.insertEntries(first, last - first + 1, rowHeight); + // All columns changed. + dirty = new Rectangle(0, firstRow * getRowHeight(), + getColumnModel().getTotalColumnWidth(), 0); } - revalidate(); + else + { + // Only one cell or column of cells changed. + // We need to convert to view column first. + int column = convertColumnIndexToModel(col); + dirty = getCellRect(firstRow, column, false); + } + + // Now adjust the height of the dirty region. + dirty.height = (lastRow + 1) * getRowHeight(); + // .. and repaint. + repaint(dirty); } - if (event == null || event.getType() == TableModelEvent.DELETE) + else { - // Sync selection model with data model. - if (event != null) + // TODO: We repaint the whole thing when the rows have variable + // heights. We might want to handle this better though. + repaint(); + } + } + + /** + * Helper method for adjusting the lead and anchor indices when the + * table structure changed. This sets the lead and anchor to -1 if there's + * no more rows, or set them to 0 when they were at -1 and there are actually + * some rows now. + */ + private void checkSelection() + { + TableModel m = getModel(); + ListSelectionModel sm = selectionModel; + if (m != null) + { + int lead = sm.getLeadSelectionIndex(); + int c = m.getRowCount(); + if (c == 0 && lead != -1) + { + // No rows in the model, reset lead and anchor to -1. + sm.setValueIsAdjusting(true); + sm.setAnchorSelectionIndex(-1); + sm.setLeadSelectionIndex(-1); + sm.setValueIsAdjusting(false); + } + else if (c != 0 && lead == -1) { - int first = event.getFirstRow(); - if (first < 0) - first = 0; - int last = event.getLastRow(); - if (last < 0) - last = getRowCount() - 1; - selectionModel.removeIndexInterval(first, last); - if (rowHeights != null) - rowHeights.removeEntries(first, last - first + 1); + // We have rows, but no lead/anchor. Set them to 0. We + // do a little trick here so that the actual selection is not + // touched. + if (sm.isSelectedIndex(0)) + sm.addSelectionInterval(0, 0); + else + sm.removeSelectionInterval(0, 0); } - if (dataModel.getRowCount() == 0) - clearSelection(); - revalidate(); + // Nothing to do in the other cases. } - repaint(); } /** @@ -3468,6 +3601,8 @@ public class JTable * Get the value of the {@link #rowSelectionAllowed} property. * * @return The current value of the property + * + * @see #setRowSelectionAllowed(boolean) */ public boolean getRowSelectionAllowed() { @@ -3621,6 +3756,8 @@ public class JTable * Get the value of the <code>columnSelectionAllowed</code> property. * * @return The current value of the columnSelectionAllowed property + * + * @see #setColumnSelectionAllowed(boolean) */ public boolean getColumnSelectionAllowed() { @@ -3874,11 +4011,17 @@ public class JTable * Set the value of the {@link #rowSelectionAllowed} property. * * @param r The new value of the rowSelectionAllowed property + * + * @see #getRowSelectionAllowed() */ public void setRowSelectionAllowed(boolean r) { - rowSelectionAllowed = r; - repaint(); + if (rowSelectionAllowed != r) + { + rowSelectionAllowed = r; + firePropertyChange("rowSelectionAllowed", !r, r); + repaint(); + } } /** @@ -3988,11 +4131,17 @@ public class JTable * Set the value of the <code>columnSelectionAllowed</code> property. * * @param c The new value of the property + * + * @see #getColumnSelectionAllowed() */ public void setColumnSelectionAllowed(boolean c) { - getColumnModel().setColumnSelectionAllowed(c); - repaint(); + if (columnModel.getColumnSelectionAllowed() != c) + { + columnModel.setColumnSelectionAllowed(c); + firePropertyChange("columnSelectionAllowed", !c, c); + repaint(); + } } /** @@ -4014,6 +4163,7 @@ public class JTable if (s != null) s.addListSelectionListener(this); selectionModel = s; + checkSelection(); } /** diff --git a/libjava/classpath/javax/swing/JTree.java b/libjava/classpath/javax/swing/JTree.java index f7583ad69e2..fa898c5a940 100644 --- a/libjava/classpath/javax/swing/JTree.java +++ b/libjava/classpath/javax/swing/JTree.java @@ -1392,9 +1392,9 @@ public class JTree extends JComponent implements Scrollable, Accessible public static final String EXPANDS_SELECTED_PATHS_PROPERTY = "expandsSelectedPaths"; - private static final Object EXPANDED = new Object(); + private static final Object EXPANDED = Boolean.TRUE; - private static final Object COLLAPSED = new Object(); + private static final Object COLLAPSED = Boolean.FALSE; private boolean dragEnabled; @@ -1515,6 +1515,9 @@ public class JTree extends JComponent implements Scrollable, Accessible // The root node appears expanded by default. nodeStates = new Hashtable(); + // The cell renderer gets set by the UI. + cellRenderer = null; + // Install the UI before installing the model. This way we avoid double // initialization of lots of UI and model stuff inside the UI and related // classes. The necessary UI updates are performed via property change diff --git a/libjava/classpath/javax/swing/LookAndFeel.java b/libjava/classpath/javax/swing/LookAndFeel.java index 7f59f1b5f93..be543439636 100644 --- a/libjava/classpath/javax/swing/LookAndFeel.java +++ b/libjava/classpath/javax/swing/LookAndFeel.java @@ -242,15 +242,15 @@ public abstract class LookAndFeel { if (keys == null) return; - for (int i = 0; i < keys.length - 1; i+= 2) + for (int i = 0; i < keys.length - 1; i += 2) { Object key = keys[i]; KeyStroke keyStroke; if (key instanceof KeyStroke) - keyStroke = (KeyStroke)key; + keyStroke = (KeyStroke) key; else - keyStroke = KeyStroke.getKeyStroke((String)key); - retMap.put(keyStroke, keys[i+1]); + keyStroke = KeyStroke.getKeyStroke((String) key); + retMap.put(keyStroke, keys[i + 1]); } } @@ -328,15 +328,15 @@ public abstract class LookAndFeel { JTextComponent.KeyBinding[] retBindings = new JTextComponent.KeyBinding[keyBindingList.length / 2]; - for (int i = 0; i < keyBindingList.length - 1; i+= 2) + for (int i = 0; i < keyBindingList.length - 1; i += 2) { KeyStroke stroke; if (keyBindingList[i] instanceof KeyStroke) - stroke = (KeyStroke)keyBindingList[i]; + stroke = (KeyStroke) keyBindingList[i]; else - stroke = KeyStroke.getKeyStroke((String)keyBindingList[i]); - retBindings[i/2] = new JTextComponent.KeyBinding(stroke, - (String) keyBindingList[i+1]); + stroke = KeyStroke.getKeyStroke((String) keyBindingList[i]); + retBindings[i / 2] = new JTextComponent.KeyBinding(stroke, + (String) keyBindingList[i + 1]); } return retBindings; } diff --git a/libjava/classpath/javax/swing/ProgressMonitor.java b/libjava/classpath/javax/swing/ProgressMonitor.java index 28d22e8a63e..b4c3d222349 100644 --- a/libjava/classpath/javax/swing/ProgressMonitor.java +++ b/libjava/classpath/javax/swing/ProgressMonitor.java @@ -142,12 +142,12 @@ public class ProgressMonitor */ public void close() { - if ( progressDialog != null ) + if (progressDialog != null) { progressDialog.setVisible(false); } - if ( timer != null ) + if (timer != null) { timer.stop(); timer = null; @@ -173,7 +173,7 @@ public class ProgressMonitor // Initializes and starts a timer with a task // which measures the duration and displays // a progress dialog if neccessary. - if ( timer == null && progressDialog == null ) + if (timer == null && progressDialog == null) { timer = new Timer(25, null); timer.addActionListener(new TimerListener()); @@ -182,7 +182,7 @@ public class ProgressMonitor // Cancels timer and hides progress dialog if the // maximum value is reached. - if ( progressBar != null && this.progress >= progressBar.getMaximum() ) + 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 @@ -326,7 +326,7 @@ public class ProgressMonitor */ public void setNote(String note) { - if ( noteLabel != null ) + if (noteLabel != null) { noteLabel.setText(note); } @@ -401,18 +401,18 @@ public class ProgressMonitor { long now = System.currentTimeMillis(); - if ( first ) + if (first) { - if (( now - timestamp ) > millisToDecideToPopup ) + if ((now - timestamp) > millisToDecideToPopup) { first = false; - long expected = ( progress - min == 0 ) ? - ( now - timestamp ) * ( max - min ) : - ( now - timestamp ) * ( max - min ) / ( progress - min ); + long expected = (progress - min == 0) ? + (now - timestamp) * (max - min) : + (now - timestamp) * (max - min) / (progress - min); - if ( expected > millisToPopup ) + if (expected > millisToPopup) { createDialog(); } @@ -424,14 +424,14 @@ public class ProgressMonitor return; } } - else if ( progressDialog != null ) + 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 ) + if ((delta * progressBar.getWidth() / (max - min)) > 0) { // At least one pixel would change. progressBar.setValue(progress); diff --git a/libjava/classpath/javax/swing/ProgressMonitorInputStream.java b/libjava/classpath/javax/swing/ProgressMonitorInputStream.java index fec5c2ee03b..90e721247b1 100644 --- a/libjava/classpath/javax/swing/ProgressMonitorInputStream.java +++ b/libjava/classpath/javax/swing/ProgressMonitorInputStream.java @@ -240,7 +240,7 @@ public class ProgressMonitorInputStream extends FilterInputStream private void checkMonitorCanceled() throws InterruptedIOException { - if ( monitor.isCanceled() ) + if (monitor.isCanceled()) { throw new InterruptedIOException("ProgressMonitor was canceled"); } diff --git a/libjava/classpath/javax/swing/RepaintManager.java b/libjava/classpath/javax/swing/RepaintManager.java index f95934123ea..80f0a3481cd 100644 --- a/libjava/classpath/javax/swing/RepaintManager.java +++ b/libjava/classpath/javax/swing/RepaintManager.java @@ -38,7 +38,6 @@ exception statement from your version. */ package javax.swing; -import java.applet.Applet; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; @@ -314,26 +313,45 @@ public class RepaintManager */ public void addInvalidComponent(JComponent component) { - Component ancestor = component; - - while (ancestor != null - && (! (ancestor instanceof JComponent) - || ! ((JComponent) ancestor).isValidateRoot() )) - ancestor = ancestor.getParent(); + Component validateRoot = null; + Component c = component; + while (c != null) + { + // Special cases we don't bother validating are when the invalidated + // component (or any of it's ancestors) is inside a CellRendererPane + // or if it doesn't have a peer yet (== not displayable). + if (c instanceof CellRendererPane || ! c.isDisplayable()) + return; + if (c instanceof JComponent && ((JComponent) c).isValidateRoot()) + { + validateRoot = c; + break; + } - if (ancestor != null - && ancestor instanceof JComponent - && ((JComponent) ancestor).isValidateRoot()) - component = (JComponent) ancestor; + c = c.getParent(); + } - if (invalidComponents.contains(component)) + // If we didn't find a validate root, then we don't validate. + if (validateRoot == null) return; - synchronized (invalidComponents) + // Make sure the validate root and all of it's ancestors are visible. + c = validateRoot; + while (c != null) { - invalidComponents.add(component); + if (! c.isVisible() || ! c.isDisplayable()) + return; + c = c.getParent(); } + if (invalidComponents.contains(validateRoot)) + return; + + //synchronized (invalidComponents) + // { + invalidComponents.add(validateRoot); + // } + if (! repaintWorker.isLive()) { repaintWorker.setLive(true); @@ -379,7 +397,7 @@ public class RepaintManager { if (w <= 0 || h <= 0 || !component.isShowing()) return; - + Component parent = component.getParent(); component.computeVisibleRect(rectCache); @@ -444,8 +462,7 @@ public class RepaintManager */ public void markCompletelyDirty(JComponent component) { - Rectangle r = component.getBounds(); - addDirtyRegion(component, 0, 0, r.width, r.height); + addDirtyRegion(component, 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE); } /** @@ -483,13 +500,11 @@ public class RepaintManager */ public boolean isCompletelyDirty(JComponent component) { - boolean retVal = false; - if (dirtyComponents.containsKey(component)) - { - Rectangle dirtyRegion = (Rectangle) dirtyComponents.get(component); - retVal = dirtyRegion.equals(SwingUtilities.getLocalBounds(component)); - } - return retVal; + boolean dirty = false; + Rectangle r = getDirtyRegion(component); + if(r.width == Integer.MAX_VALUE && r.height == Integer.MAX_VALUE) + dirty = true; + return dirty; } /** @@ -618,7 +633,7 @@ public class RepaintManager public Image getOffscreenBuffer(Component component, int proposedWidth, int proposedHeight) { - Component root = getRoot(component); + Component root = SwingUtilities.getWindowAncestor(component); Image buffer = (Image) offscreenBuffers.get(root); if (buffer == null || buffer.getWidth(null) < proposedWidth @@ -628,38 +643,12 @@ public class RepaintManager width = Math.min(doubleBufferMaximumSize.width, width); int height = Math.max(proposedHeight, root.getHeight()); height = Math.min(doubleBufferMaximumSize.height, height); - buffer = component.createImage(width, height); + buffer = root.createImage(width, height); offscreenBuffers.put(root, buffer); } return buffer; } - - /** - * Gets the root of the component given. If a parent of the - * component is an instance of Applet, then the applet is - * returned. The applet is considered the root for painting. - * Otherwise, the root Window is returned if it exists. - * - * @param comp - The component to get the root for. - * @return the parent root. An applet if it is a parent, - * or the root window. If neither exist, null is returned. - */ - private Component getRoot(Component comp) - { - Applet app = null; - - while (comp != null) - { - if (app == null && comp instanceof Window) - return comp; - else if (comp instanceof Applet) - app = (Applet) comp; - comp = comp.getParent(); - } - - return app; - } - + /** * Blits the back buffer of the specified root component to the screen. If * the RepaintManager is currently working on a paint request, the commit @@ -667,67 +656,98 @@ public class RepaintManager * done (by {@link #commitRemainingBuffers}). This is package private because * it must get called by JComponent. * - * @param root the component, either a Window or an Applet instance - * @param area the area to paint on screen + * @param comp the component to be painted + * @param area the area to paint on screen, in comp coordinates */ - void commitBuffer(Component root, Rectangle area) + void commitBuffer(Component comp, Rectangle area) { + // Determine the component that we finally paint the buffer upon. + // We need to paint on the nearest heavyweight component, so that Swing + // hierarchies inside (non-window) heavyweights get painted correctly. + // Otherwise we would end up blitting the backbuffer behind the heavyweight + // which is wrong. + Component root = getHeavyweightParent(comp); + // FIXME: Optimize this. + Rectangle rootRect = SwingUtilities.convertRectangle(comp, area, root); + // We synchronize on dirtyComponents here because that is what // paintDirtyRegions also synchronizes on while painting. synchronized (dirtyComponents) { // If the RepaintManager is not currently painting, then directly // blit the requested buffer on the screen. - if (! repaintUnderway) + if (true || ! repaintUnderway) { - Graphics g = root.getGraphics(); - Image buffer = (Image) offscreenBuffers.get(root); - Rectangle clip = g.getClipBounds(); - if (clip != null) - area = SwingUtilities.computeIntersection(clip.x, clip.y, - clip.width, clip.height, - area); - int dx1 = area.x; - int dy1 = area.y; - int dx2 = area.x + area.width; - int dy2 = area.y + area.height; - // Make sure we have a sane clip at this point. - g.clipRect(area.x, area.y, area.width, area.height); - - // Make sure the coordinates are inside the buffer, everything else - // might lead to problems. - // TODO: This code should not really be necessary, however, in fact - // we have two issues here: - // 1. We shouldn't get repaint requests in areas outside the buffer - // region in the first place. This still happens for example - // when a component is inside a JViewport, and the component has - // a size that would reach beyond the window size. - // 2. Graphics.drawImage() should not behave strange when trying - // to draw regions outside the image. - int bufferWidth = buffer.getWidth(root); - int bufferHeight = buffer.getHeight(root); - dx1 = Math.min(bufferWidth, dx1); - dy1 = Math.min(bufferHeight, dy1); - dx2 = Math.min(bufferWidth, dx2); - dy2 = Math.min(bufferHeight, dy2); - g.drawImage(buffer, 0, 0, root); - g.dispose(); + blitBuffer(root, rootRect); } + // Otherwise queue this request up, until all the RepaintManager work // is done. else { if (commitRequests.containsKey(root)) - SwingUtilities.computeUnion(area.x, area.y, area.width, - area.height, + SwingUtilities.computeUnion(rootRect.x, rootRect.y, + rootRect.width, rootRect.height, (Rectangle) commitRequests.get(root)); else - commitRequests.put(root, area); + commitRequests.put(root, rootRect); } } } /** + * Copies the buffer to the screen. Note that the root component here is + * not necessarily the component with which the offscreen buffer is + * associated. The offscreen buffers are always allocated for the toplevel + * windows. However, painted is performed on lower-level heavyweight + * components too, if they contain Swing components. + * + * @param root the heavyweight component to blit upon + * @param rootRect the rectangle in the root component's coordinate space + */ + private void blitBuffer(Component root, Rectangle rootRect) + { + if (! root.isShowing()) + return; + + // Find the Window from which we use the backbuffer. + Component bufferRoot = root; + Rectangle bufferRect = rootRect.getBounds(); + if (!(bufferRoot instanceof Window)) + { + bufferRoot = SwingUtilities.getWindowAncestor(bufferRoot); + SwingUtilities.convertRectangleToAncestor(root, rootRect, bufferRoot); + } + + Graphics g = root.getGraphics(); + Image buffer = (Image) offscreenBuffers.get(bufferRoot); + + // Make sure we have a sane clip at this point. + g.clipRect(rootRect.x, rootRect.y, rootRect.width, rootRect.height); + g.drawImage(buffer, rootRect.x - bufferRect.x, rootRect.y - bufferRect.y, + root); + g.dispose(); + + } + + /** + * Finds and returns the nearest heavyweight parent for the specified + * component. If the component isn't contained inside a heavyweight parent, + * this returns null. + * + * @param comp the component + * + * @return the nearest heavyweight parent for the specified component or + * null if the component has no heavyweight ancestor + */ + private Component getHeavyweightParent(Component comp) + { + while (comp != null && comp.isLightweight()) + comp = comp.getParent(); + return comp; + } + + /** * Commits the queued up back buffers to screen all at once. */ private void commitRemainingBuffers() @@ -743,7 +763,7 @@ public class RepaintManager Map.Entry entry = (Map.Entry) i.next(); Component root = (Component) entry.getKey(); Rectangle area = (Rectangle) entry.getValue(); - commitBuffer(root, area); + blitBuffer(root, area); i.remove(); } } @@ -767,7 +787,7 @@ public class RepaintManager public Image getVolatileOffscreenBuffer(Component comp, int proposedWidth, int proposedHeight) { - Component root = getRoot(comp); + Component root = SwingUtilities.getWindowAncestor(comp); Image buffer = (Image) offscreenBuffers.get(root); if (buffer == null || buffer.getWidth(null) < proposedWidth diff --git a/libjava/classpath/javax/swing/ScrollPaneLayout.java b/libjava/classpath/javax/swing/ScrollPaneLayout.java index 31846fa557d..8ce8fd86f7a 100644 --- a/libjava/classpath/javax/swing/ScrollPaneLayout.java +++ b/libjava/classpath/javax/swing/ScrollPaneLayout.java @@ -82,7 +82,8 @@ public class ScrollPaneLayout // Nothing to do here. } - public void syncWithScrollPane(JScrollPane scrollPane) { + public void syncWithScrollPane(JScrollPane scrollPane) + { viewport = scrollPane.getViewport(); rowHead = scrollPane.getRowHeader(); colHead = scrollPane.getColumnHeader(); @@ -145,7 +146,8 @@ public class ScrollPaneLayout throw new IllegalArgumentException(); } - public void removeLayoutComponent(Component component) { + public void removeLayoutComponent(Component component) + { if (component == viewport) viewport = null; else if (component == vsb) @@ -448,7 +450,8 @@ public class ScrollPaneLayout * @deprecated As of Swing 1.1 replaced by * {@link javax.swing.JScrollPane#getViewportBorderBounds}. */ - public Rectangle getViewportBorderBounds(JScrollPane scrollPane) { + public Rectangle getViewportBorderBounds(JScrollPane scrollPane) + { return null; } diff --git a/libjava/classpath/javax/swing/SizeSequence.java b/libjava/classpath/javax/swing/SizeSequence.java index 26099a15461..a5f34710c76 100644 --- a/libjava/classpath/javax/swing/SizeSequence.java +++ b/libjava/classpath/javax/swing/SizeSequence.java @@ -37,6 +37,8 @@ exception statement from your version. */ package javax.swing; +import java.util.Arrays; + /** * A sequence of values that represent the dimensions (widths or heights) of * some collection of items (for example, the widths of the columns in a table). @@ -80,8 +82,8 @@ public class SizeSequence */ public SizeSequence(int numEntries, int value) { - sizes = new int[0]; - insertEntries(0, numEntries, value); + sizes = new int[numEntries]; + Arrays.fill(sizes, value); } /** diff --git a/libjava/classpath/javax/swing/SpringLayout.java b/libjava/classpath/javax/swing/SpringLayout.java index d87050639fe..2595b196703 100644 --- a/libjava/classpath/javax/swing/SpringLayout.java +++ b/libjava/classpath/javax/swing/SpringLayout.java @@ -505,7 +505,7 @@ public class SpringLayout implements LayoutManager2 } } - private static abstract class DeferredDimension extends Spring + private abstract static class DeferredDimension extends Spring { private int value; diff --git a/libjava/classpath/javax/swing/SwingUtilities.java b/libjava/classpath/javax/swing/SwingUtilities.java index 5d02d9bb396..ccd37d03a55 100644 --- a/libjava/classpath/javax/swing/SwingUtilities.java +++ b/libjava/classpath/javax/swing/SwingUtilities.java @@ -1045,8 +1045,7 @@ public class SwingUtilities */ public static boolean isLeftMouseButton(MouseEvent event) { - return ((event.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) - == InputEvent.BUTTON1_DOWN_MASK); + return ((event.getModifiers() & InputEvent.BUTTON1_MASK) != 0); } /** @@ -1599,4 +1598,27 @@ public class SwingUtilities throw new IllegalArgumentException("Unrecognised code: " + code); } } + + /** + * Converts a rectangle in the coordinate system of a child component into + * a rectangle of one of it's Ancestors. The result is stored in the input + * rectangle. + * + * @param comp the child component + * @param r the rectangle to convert + * @param ancestor the ancestor component + */ + static void convertRectangleToAncestor(Component comp, Rectangle r, + Component ancestor) + { + if (comp == ancestor) + return; + + r.x += comp.getX(); + r.y += comp.getY(); + + Component parent = comp.getParent(); + if (parent != null && parent != ancestor) + convertRectangleToAncestor(parent, r, ancestor); + } } diff --git a/libjava/classpath/javax/swing/Timer.java b/libjava/classpath/javax/swing/Timer.java index 231b71d73bb..acd22624947 100644 --- a/libjava/classpath/javax/swing/Timer.java +++ b/libjava/classpath/javax/swing/Timer.java @@ -1,5 +1,5 @@ /* Timer.java -- - Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -264,9 +264,13 @@ public class Timer * firing the first event. * * @param d The time gap between the subsequent events, in milliseconds + * + * @throws IllegalArgumentException if <code>d</code> is less than zero. */ public void setDelay(int d) { + if (d < 0) + throw new IllegalArgumentException("Invalid delay: " + d); delay = d; } @@ -287,9 +291,13 @@ public class Timer * subsequent events. * * @param i the initial delay, in milliseconds + * + * @throws IllegalArgumentException if <code>i</code> is less than zero. */ public void setInitialDelay(int i) { + if (i < 0) + throw new IllegalArgumentException("Invalid initial delay: " + i); initialDelay = i; } diff --git a/libjava/classpath/javax/swing/ToolTipManager.java b/libjava/classpath/javax/swing/ToolTipManager.java index c7de4db8330..963ccf88117 100644 --- a/libjava/classpath/javax/swing/ToolTipManager.java +++ b/libjava/classpath/javax/swing/ToolTipManager.java @@ -1,5 +1,5 @@ /* ToolTipManager.java -- - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -267,10 +267,12 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener } /** - * This method sets the initial delay before the ToolTip is shown when the + * Sets the initial delay before the ToolTip is shown when the * mouse enters a Component. * * @param delay The initial delay before the ToolTip is shown. + * + * @throws IllegalArgumentException if <code>delay</code> is less than zero. */ public void setInitialDelay(int delay) { @@ -289,9 +291,11 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener } /** - * This method sets the time the ToolTip will be shown before being hidden. + * Sets the time the ToolTip will be shown before being hidden. * - * @param delay The time the ToolTip will be shown before being hidden. + * @param delay the delay (in milliseconds) before tool tips are hidden. + * + * @throws IllegalArgumentException if <code>delay</code> is less than zero. */ public void setDismissDelay(int delay) { @@ -310,10 +314,12 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener } /** - * This method sets the amount of delay where if the mouse re-enters a + * Sets the amount of delay where if the mouse re-enters a * Component, the tooltip will be shown immediately. * - * @param delay The reshow delay. + * @param delay The reshow delay (in milliseconds). + * + * @throws IllegalArgumentException if <code>delay</code> is less than zero. */ public void setReshowDelay(int delay) { diff --git a/libjava/classpath/javax/swing/UIManager.java b/libjava/classpath/javax/swing/UIManager.java index e6f80116321..77be44afcbb 100644 --- a/libjava/classpath/javax/swing/UIManager.java +++ b/libjava/classpath/javax/swing/UIManager.java @@ -45,6 +45,7 @@ import java.awt.Insets; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.Serializable; +import java.util.Enumeration; import java.util.Locale; import javax.swing.border.Border; @@ -117,6 +118,87 @@ public class UIManager implements Serializable } } + /** + * A UIDefaults subclass that multiplexes between itself and a 'fallback' + * UIDefaults instance. This is used to protect the L&F UIDefaults from beeing + * overwritten by applications. + */ + private static class MultiplexUIDefaults + extends UIDefaults + { + private class MultiplexEnumeration + implements Enumeration + { + Enumeration[] enums; + int i; + MultiplexEnumeration(Enumeration e1, Enumeration e2) + { + enums = new Enumeration[]{ e1, e2 }; + i = 0; + } + + public boolean hasMoreElements() + { + return enums[i].hasMoreElements() || i < enums.length - 1; + } + + public Object nextElement() + { + Object val = enums[i].nextElement(); + if (! enums[i].hasMoreElements() && i < enums.length - 1) + i++; + return val; + } + + } + + UIDefaults fallback; + + MultiplexUIDefaults(UIDefaults d) + { + fallback = d; + } + + public Object get(Object key) + { + Object val = super.get(key); + if (val == null) + val = fallback.get(key); + return val; + } + + public Object get(Object key, Locale l) + { + Object val = super.get(key, l); + if (val == null) + val = fallback.get(key, l); + return val; + } + + public Object remove(Object key) + { + Object val = super.remove(key); + if (val == null) + val = fallback.remove(key); + return val; + } + + public int size() + { + return super.size() + fallback.size(); + } + + public Enumeration keys() + { + return new MultiplexEnumeration(super.keys(), fallback.keys()); + } + + public Enumeration elements() + { + return new MultiplexEnumeration(super.elements(), fallback.elements()); + } + } + private static final long serialVersionUID = -5547433830339189365L; /** The installed look and feel(s). */ @@ -131,12 +213,9 @@ public class UIManager implements Serializable /** The current look and feel. */ static LookAndFeel currentLookAndFeel; - static UIDefaults currentUIDefaults; + static MultiplexUIDefaults currentUIDefaults; - /** - * UIDefaults set by the user. - */ - static UIDefaults userUIDefaults; + static UIDefaults lookAndFeelDefaults; /** Property change listener mechanism. */ static PropertyChangeSupport listeners @@ -149,9 +228,7 @@ public class UIManager implements Serializable { if (defaultlaf != null) { - Class lafClass = Class.forName(defaultlaf); - LookAndFeel laf = (LookAndFeel) lafClass.newInstance(); - setLookAndFeel(laf); + setLookAndFeel(defaultlaf); } else { @@ -162,6 +239,7 @@ public class UIManager implements Serializable { System.err.println("cannot initialize Look and Feel: " + defaultlaf); System.err.println("error: " + ex.toString()); + ex.printStackTrace(); System.err.println("falling back to Metal Look and Feel"); try { @@ -312,12 +390,7 @@ public class UIManager implements Serializable */ public static Object get(Object key) { - Object val = null; - if (userUIDefaults != null) - val = userUIDefaults.get(key); - if (val == null) - val = getLookAndFeelDefaults().get(key); - return val; + return getDefaults().get(key); } /** @@ -330,12 +403,7 @@ public class UIManager implements Serializable */ public static Object get(Object key, Locale locale) { - Object val = null; - if (userUIDefaults != null) - val = userUIDefaults.get(key, locale); - if (val == null) - val = getLookAndFeelDefaults().get(key, locale); - return val; + return getDefaults().get(key, locale); } /** @@ -414,6 +482,8 @@ public class UIManager implements Serializable */ public static UIDefaults getDefaults() { + if (currentUIDefaults == null) + currentUIDefaults = new MultiplexUIDefaults(null); return currentUIDefaults; } @@ -546,7 +616,7 @@ public class UIManager implements Serializable */ public static UIDefaults getLookAndFeelDefaults() { - return currentUIDefaults; + return lookAndFeelDefaults; } /** @@ -587,13 +657,7 @@ public class UIManager implements Serializable */ public static ComponentUI getUI(JComponent target) { - ComponentUI ui = null; - if (userUIDefaults != null - && userUIDefaults.get(target.getUIClassID()) != null) - ui = userUIDefaults.getUI(target); - if (ui == null) - ui = currentUIDefaults.getUI(target); - return ui; + return getDefaults().getUI(target); } /** @@ -625,11 +689,7 @@ public class UIManager implements Serializable */ public static Object put(Object key, Object value) { - Object old = get(key); - if (userUIDefaults == null) - userUIDefaults = new UIDefaults(); - userUIDefaults.put(key, value); - return old; + return getDefaults().put(key, value); } /** @@ -654,7 +714,8 @@ public class UIManager implements Serializable throws UnsupportedLookAndFeelException { if (newLookAndFeel != null && ! newLookAndFeel.isSupportedLookAndFeel()) - throw new UnsupportedLookAndFeelException(newLookAndFeel.getName()); + throw new UnsupportedLookAndFeelException(newLookAndFeel.getName() + + " not supported on this platform"); LookAndFeel oldLookAndFeel = currentLookAndFeel; if (oldLookAndFeel != null) oldLookAndFeel.uninitialize(); @@ -664,7 +725,12 @@ public class UIManager implements Serializable if (newLookAndFeel != null) { newLookAndFeel.initialize(); - currentUIDefaults = newLookAndFeel.getDefaults(); + lookAndFeelDefaults = newLookAndFeel.getDefaults(); + if (currentUIDefaults == null) + currentUIDefaults = + new MultiplexUIDefaults(lookAndFeelDefaults); + else + currentUIDefaults.fallback = lookAndFeelDefaults; } else { @@ -689,7 +755,8 @@ public class UIManager implements Serializable throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException { - Class c = Class.forName(className); + Class c = Class.forName(className, true, + Thread.currentThread().getContextClassLoader()); LookAndFeel a = (LookAndFeel) c.newInstance(); // throws class-cast-exception setLookAndFeel(a); } diff --git a/libjava/classpath/javax/swing/border/TitledBorder.java b/libjava/classpath/javax/swing/border/TitledBorder.java index 56146e01d1b..38ccd720f16 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, 2005 Free Software Foundation, Inc. + Copyright (C) 2003, 2004, 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -961,7 +961,8 @@ public class TitledBorder extends AbstractBorder public void setTitlePosition(int titlePosition) { if ((titlePosition < DEFAULT_POSITION) || (titlePosition > BELOW_BOTTOM)) - throw new IllegalArgumentException(); + throw new IllegalArgumentException(titlePosition + + " is not a valid title position."); // Swing borders are not JavaBeans, thus no need to fire an event. this.titlePosition = titlePosition; @@ -982,7 +983,8 @@ public class TitledBorder extends AbstractBorder { if ((titleJustification < DEFAULT_JUSTIFICATION) || (titleJustification > TRAILING)) - throw new IllegalArgumentException(); + throw new IllegalArgumentException(titleJustification + + " is not a valid title justification."); // Swing borders are not JavaBeans, thus no need to fire an event. this.titleJustification = titleJustification; diff --git a/libjava/classpath/javax/swing/event/EventListenerList.java b/libjava/classpath/javax/swing/event/EventListenerList.java index 6a2f34ebb3d..bde8b3c7e4f 100644 --- a/libjava/classpath/javax/swing/event/EventListenerList.java +++ b/libjava/classpath/javax/swing/event/EventListenerList.java @@ -108,7 +108,7 @@ public class EventListenerList * An array with all currently registered listeners. The array has * twice as many elements as there are listeners. For an even * integer <code>i</code>, <code>listenerList[i]</code> indicates - * the registered class, and <code>listenerList[i+1]</code> is the + * the registered class, and <code>listenerList[i + 1]</code> is the * listener. */ protected transient Object[] listenerList = NO_LISTENERS; diff --git a/libjava/classpath/javax/swing/event/ListDataEvent.java b/libjava/classpath/javax/swing/event/ListDataEvent.java index 2a6e6dbe9f0..897fc128f20 100644 --- a/libjava/classpath/javax/swing/event/ListDataEvent.java +++ b/libjava/classpath/javax/swing/event/ListDataEvent.java @@ -1,5 +1,5 @@ /* ListDataEvent.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -41,6 +41,9 @@ package javax.swing.event; import java.util.EventObject; /** + * An event that contains information about a modification to the content of + * a list. + * * @author Andrew Selkirk * @author Ronald Veldema */ @@ -48,32 +51,46 @@ public class ListDataEvent extends EventObject { private static final long serialVersionUID = 2510353260071004774L; + /** An event type indicating that the list content has been modified. */ public static final int CONTENTS_CHANGED = 0; + + /** An event type indicating that an interval has been added to the list. */ public static final int INTERVAL_ADDED = 1; + + /** + * An event type indicating that an interval has been removed from the + * list. + */ public static final int INTERVAL_REMOVED = 2; - private int type = 0; - private int index0 = 0; - private int index1 = 0; + private int type; + private int index0; + private int index1; /** * Creates a <code>ListDataEvent</code> object. * - * @param source The source of the event. - * @param type The type of the event - * @param index0 Bottom of range - * @param index1 Top of range + * @param source the source of the event (<code>null</code> not permitted). + * @param type the type of the event (should be one of + * {@link #CONTENTS_CHANGED}, {@link #INTERVAL_ADDED} or + * {@link #INTERVAL_REMOVED}, although this is not enforced). + * @param index0 the index for one end of the modified range of list + * elements. + * @param index1 the index for the other end of the modified range of list + * elements. */ public ListDataEvent(Object source, int type, int index0, int index1) { super(source); this.type = type; - this.index0 = index0; - this.index1 = index1; + this.index0 = Math.min(index0, index1); + this.index1 = Math.max(index0, index1); } /** - * Returns the bottom index. + * Returns the index of the first item in the range of modified list items. + * + * @return The index of the first item in the range of modified list items. */ public int getIndex0() { @@ -81,7 +98,9 @@ public class ListDataEvent extends EventObject } /** - * Returns the top index. + * Returns the index of the last item in the range of modified list items. + * + * @return The index of the last item in the range of modified list items. */ public int getIndex1() { @@ -89,10 +108,25 @@ public class ListDataEvent extends EventObject } /** - * Returns the type of this event. + * Returns a code representing the type of this event, which is usually one + * of {@link #CONTENTS_CHANGED}, {@link #INTERVAL_ADDED} or + * {@link #INTERVAL_REMOVED}. + * + * @return The event type. */ public int getType() { return type; } + + /** + * Returns a string representing the state of this event. + * + * @return A string. + */ + public String toString() + { + return getClass().getName() + "[type=" + type + ",index0=" + index0 + + ",index1=" + index1 + "]"; + } } diff --git a/libjava/classpath/javax/swing/event/MenuEvent.java b/libjava/classpath/javax/swing/event/MenuEvent.java index 35bb5b97b43..8ba32929e2d 100644 --- a/libjava/classpath/javax/swing/event/MenuEvent.java +++ b/libjava/classpath/javax/swing/event/MenuEvent.java @@ -1,5 +1,5 @@ /* MenuEvent.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,7 +37,6 @@ exception statement from your version. */ package javax.swing.event; -// Imports import java.util.EventObject; /** @@ -45,15 +44,16 @@ import java.util.EventObject; * @author Andrew Selkirk * @author Ronald Veldema */ -public class MenuEvent extends EventObject { - - /** - * Constructor MenuEvent - * @param source Source object - */ - public MenuEvent(Object source) { - super(source); - } // MenuEvent() - - -} // MenuEvent +public class MenuEvent extends EventObject +{ + + /** + * Constructor MenuEvent + * @param source Source object + */ + public MenuEvent(Object source) + { + super(source); + } + +} diff --git a/libjava/classpath/javax/swing/event/TreeExpansionListener.java b/libjava/classpath/javax/swing/event/TreeExpansionListener.java index 08507a0ffe9..45a5ef93c6d 100644 --- a/libjava/classpath/javax/swing/event/TreeExpansionListener.java +++ b/libjava/classpath/javax/swing/event/TreeExpansionListener.java @@ -37,26 +37,26 @@ exception statement from your version. */ package javax.swing.event; -// Imports import java.util.EventListener; /** * TreeExpansionListener public interface * @author Andrew Selkirk */ -public interface TreeExpansionListener extends EventListener { +public interface TreeExpansionListener extends EventListener +{ - /** - * Tree collapsed - * @param event Tree Expansion Event - */ - void treeCollapsed(TreeExpansionEvent event); + /** + * Tree collapsed + * @param event Tree Expansion Event + */ + void treeCollapsed(TreeExpansionEvent event); - /** - * Tree expanded - * @param event Tree Expansion Event - */ - void treeExpanded(TreeExpansionEvent event); + /** + * Tree expanded + * @param event Tree Expansion Event + */ + void treeExpanded(TreeExpansionEvent event); -} // TreeExpansionListener +} diff --git a/libjava/classpath/javax/swing/filechooser/FileSystemView.java b/libjava/classpath/javax/swing/filechooser/FileSystemView.java index f51b745c892..84b80dd402c 100644 --- a/libjava/classpath/javax/swing/filechooser/FileSystemView.java +++ b/libjava/classpath/javax/swing/filechooser/FileSystemView.java @@ -76,7 +76,10 @@ public abstract class FileSystemView */ public File createFileObject(String path) { - return new File(path); + File f = new File(path); + if (isFileSystemRoot(f)) + f = this.createFileSystemRoot(f); + return f; } /** @@ -223,16 +226,24 @@ public abstract class FileSystemView /** * Returns the name of a file as it would be displayed by the underlying - * system. This implementation returns <code>null</code>, subclasses must - * override. + * system. * * @param f the file. * - * @return <code>null</code>. + * @return the name of a file as it would be displayed by the underlying + * system + * + * @specnote The specification suggests that the information here is + * fetched from a ShellFolder class. This seems to be a non public + * private file handling class. We simply return File.getName() + * here and leave special handling to subclasses. */ public String getSystemDisplayName(File f) { - return null; + String name = null; + if (f != null) + name = f.getName(); + return name; } /** diff --git a/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java b/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java index 96dfd2e1b1a..f8d71e1df33 100644 --- a/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java +++ b/libjava/classpath/javax/swing/filechooser/UnixFileSystemView.java @@ -106,17 +106,34 @@ class UnixFileSystemView extends FileSystemView /** * Returns the name of a file as it would be displayed by the underlying - * system. This method is NOT YET IMPLEMENTED. + * system. * * @param f the file. * - * @return <code>null</code>. + * @return the name of a file as it would be displayed by the underlying + * system */ public String getSystemDisplayName(File f) - throws NotImplementedException { - // FIXME: Implement; - return null; + String name = null; + if (f != null) + { + if (isRoot(f)) + name = f.getAbsolutePath(); + else + { + try + { + String path = f.getCanonicalPath(); + name = path.substring(path.lastIndexOf(File.separator) + 1); + } + catch (IOException e) + { + name = f.getName(); + } + } + } + return name; } /** diff --git a/libjava/classpath/javax/swing/plaf/IconUIResource.java b/libjava/classpath/javax/swing/plaf/IconUIResource.java index 659c8e7bab7..8ac9e0875ae 100644 --- a/libjava/classpath/javax/swing/plaf/IconUIResource.java +++ b/libjava/classpath/javax/swing/plaf/IconUIResource.java @@ -1,5 +1,5 @@ /* IconUIResource.java -- - Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2004, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -63,7 +63,8 @@ public class IconUIResource implements Icon, UIResource, Serializable /** - * The icon that is wrapped by this <code>IconUIResource</code>. + * The icon that is wrapped by this <code>IconUIResource</code> (never + * <code>null</code>). */ private Icon delegate; @@ -73,10 +74,12 @@ public class IconUIResource implements Icon, UIResource, Serializable * icon. All messages are forwarded to the delegate icon. * * @param delegate the icon that is wrapped by this - * <code>IconUIResource</code>. + * <code>IconUIResource</code> (<code>null</code> not permitted). */ public IconUIResource(Icon delegate) { + if (delegate == null) + throw new IllegalArgumentException("Null 'delegate' argument."); this.delegate = delegate; } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java b/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java index f796d9a730a..781269b2adf 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java @@ -1,5 +1,5 @@ /* BasicArrowButton.java -- - Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -42,7 +42,6 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Polygon; -import java.awt.Rectangle; import javax.swing.ButtonModel; import javax.swing.JButton; @@ -86,7 +85,9 @@ public class BasicArrowButton extends JButton implements SwingConstants transient Color highlight = Color.WHITE; /** - * Creates a new <code>BasicArrowButton</code> object. + * Creates a new <code>BasicArrowButton</code> object with an arrow pointing + * in the specified direction. If the <code>direction</code> is not one of + * the specified constants, no arrow is drawn. * * @param direction The direction the arrow points in (one of: * {@link #NORTH}, {@link #SOUTH}, {@link #EAST} and {@link #WEST}). @@ -95,6 +96,7 @@ public class BasicArrowButton extends JButton implements SwingConstants { super(); setDirection(direction); + setFocusable(false); } /** @@ -116,8 +118,7 @@ public class BasicArrowButton extends JButton implements SwingConstants this.shadow = shadow; this.darkShadow = darkShadow; this.highlight = highlight; - // Mark the button as not closing the popup, we handle this ourselves. - putClientProperty(BasicLookAndFeel.DONT_CANCEL_POPUP, Boolean.TRUE); + setFocusable(false); } /** @@ -162,28 +163,22 @@ public class BasicArrowButton extends JButton implements SwingConstants public void paint(Graphics g) { super.paint(g); - Rectangle bounds = getBounds(); - int size = bounds.height / 4; - int x = bounds.x + (bounds.width - size) / 2; - int y = (bounds.height - size) / 4; + + int height = getHeight(); + int size = height / 4; + + int x = (getWidth() - size) / 2; + int y = (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); /** * Returns the preferred size of the arrow button. @@ -192,7 +187,10 @@ public class BasicArrowButton extends JButton implements SwingConstants */ public Dimension getPreferredSize() { - return PREFERRED_SIZE; + // since Dimension is NOT immutable, we must return a new instance + // every time (if we return a cached value, the caller might modify it) + // - tests show that the reference implementation does the same. + return new Dimension(16, 16); } /** @@ -202,17 +200,23 @@ public class BasicArrowButton extends JButton implements SwingConstants */ public Dimension getMinimumSize() { - return MINIMUM_SIZE; + // since Dimension is NOT immutable, we must return a new instance + // every time (if we return a cached value, the caller might modify it) + // - tests show that the reference implementation does the same. + return new Dimension(5, 5); } /** * Returns the maximum size of the arrow button. * - * @return The maximum size. + * @return The maximum size (always Integer.MAX_VALUE x Integer.MAX_VALUE). */ public Dimension getMaximumSize() { - return MAXIMUM_SIZE; + // since Dimension is NOT immutable, we must return a new instance + // every time (if we return a cached value, the caller might modify it) + // - tests show that the reference implementation does the same. + return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java b/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java index 89e99a29a31..84895821518 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java @@ -38,13 +38,17 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.SystemProperties; + import java.awt.event.ActionEvent; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; -import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -66,7 +70,23 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener, public void propertyChange(PropertyChangeEvent e) { - // TODO: What should be done here, if anything? + // Store the TextLayout for this in a client property for speed-up + // painting of the label. + String property = e.getPropertyName(); + if ((property.equals(AbstractButton.TEXT_CHANGED_PROPERTY) + || property.equals("font")) + && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D") + == null) + { + AbstractButton b = (AbstractButton) e.getSource(); + String text = b.getText(); + if (text == null) + text = ""; + FontRenderContext frc = new FontRenderContext(new AffineTransform(), + false, false); + TextLayout layout = new TextLayout(text, b.getFont(), frc); + b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, layout); + } } protected void checkOpacity(AbstractButton b) @@ -160,11 +180,14 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener, { AbstractButton button = (AbstractButton) e.getSource(); ButtonModel model = button.getModel(); - if (e.getButton() == MouseEvent.BUTTON1) + if (SwingUtilities.isLeftMouseButton(e)) { // It is important that these transitions happen in this order. model.setArmed(true); model.setPressed(true); + + if (! button.isFocusOwner() && button.isRequestFocusEnabled()) + button.requestFocus(); } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java index 0a537c4bdd8..d531133ba26 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java @@ -442,13 +442,17 @@ public class BasicButtonUI extends ButtonUI if (b.isEnabled()) { g.setColor(b.getForeground()); - g.drawString(text, textRect.x, textRect.y + fm.getAscent()); + // FIXME: Underline mnemonic. + BasicGraphicsUtils.drawString(b, g, text, -1, textRect.x, + textRect.y + fm.getAscent()); } else { String prefix = getPropertyPrefix(); g.setColor(UIManager.getColor(prefix + "disabledText")); - g.drawString(text, textRect.x, textRect.y + fm.getAscent()); + // FIXME: Underline mnemonic. + BasicGraphicsUtils.drawString(b, g, text, -1, textRect.x, + textRect.y + fm.getAscent()); } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java index 2cb1623cbc2..d98fd2afe38 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java @@ -38,8 +38,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.Color; import java.awt.Component; import java.awt.Container; @@ -62,6 +60,7 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; import javax.swing.CellRendererPane; import javax.swing.ComboBoxEditor; import javax.swing.ComboBoxModel; @@ -241,6 +240,8 @@ public class BasicComboBoxUI extends ComboBoxUI comboBox.setLayout(createLayoutManager()); comboBox.setFocusable(true); installKeyboardActions(); + comboBox.putClientProperty(BasicLookAndFeel.DONT_CANCEL_POPUP, + Boolean.TRUE); } } @@ -288,7 +289,7 @@ public class BasicComboBoxUI extends ComboBoxUI comboBox.addPropertyChangeListener(propertyChangeListener); focusListener = createFocusListener(); - editor.addFocusListener(focusListener); + comboBox.addFocusListener(focusListener); itemListener = createItemListener(); comboBox.addItemListener(itemListener); @@ -542,7 +543,9 @@ public class BasicComboBoxUI extends ComboBoxUI { editor.setFont(comboBox.getFont()); if (popupKeyListener != null) - editor.addKeyListener(popupKeyListener); + editor.addKeyListener(popupKeyListener); + if (keyListener != null) + editor.addKeyListener(keyListener); comboBox.configureEditor(comboBox.getEditor(), comboBox.getSelectedItem()); } @@ -554,6 +557,8 @@ public class BasicComboBoxUI extends ComboBoxUI { if (popupKeyListener != null) editor.removeKeyListener(popupKeyListener); + if (keyListener != null) + editor.removeKeyListener(keyListener); } /** @@ -571,6 +576,10 @@ public class BasicComboBoxUI extends ComboBoxUI arrowButton.addMouseListener(popupMouseListener); if (popupMouseMotionListener != null) arrowButton.addMouseMotionListener(popupMouseMotionListener); + + // Mark the button as not closing the popup, we handle this ourselves. + arrowButton.putClientProperty(BasicLookAndFeel.DONT_CANCEL_POPUP, + Boolean.TRUE); } } @@ -712,18 +721,51 @@ public class BasicComboBoxUI extends ComboBoxUI return new Dimension(32767, 32767); } + /** + * Returns the number of accessible children of the combobox. + * + * @param c the component (combobox) to check, ignored + * + * @return the number of accessible children of the combobox + */ public int getAccessibleChildrenCount(JComponent c) - throws NotImplementedException { - // FIXME: Need to implement - return 0; + int count = 1; + if (comboBox.isEditable()) + count = 2; + return count; } + /** + * Returns the accessible child with the specified index. + * + * @param c the component, this is ignored + * @param i the index of the accessible child to return + */ public Accessible getAccessibleChild(JComponent c, int i) - throws NotImplementedException { - // FIXME: Need to implement - return null; + Accessible child = null; + switch (i) + { + case 0: // The popup. + if (popup instanceof Accessible) + { + AccessibleContext ctx = ((Accessible) popup).getAccessibleContext(); + ctx.setAccessibleParent(comboBox); + child = (Accessible) popup; + } + break; + case 1: // The editor, if any. + if (comboBox.isEditable() && editor instanceof Accessible) + { + AccessibleContext ctx = + ((Accessible) editor).getAccessibleContext(); + ctx.setAccessibleParent(comboBox); + child = (Accessible) editor; + } + break; + } + return child; } /** @@ -735,10 +777,11 @@ public class BasicComboBoxUI extends ComboBoxUI * @return true if the specified key is a navigation key and false otherwis */ protected boolean isNavigationKey(int keyCode) - throws NotImplementedException { - // FIXME: Need to implement - return false; + return keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN + || keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_RIGHT + || keyCode == KeyEvent.VK_ENTER || keyCode == KeyEvent.VK_ESCAPE + || keyCode == KeyEvent.VK_TAB; } /** @@ -759,7 +802,7 @@ public class BasicComboBoxUI extends ComboBoxUI protected void selectPreviousPossibleValue() { int index = comboBox.getSelectedIndex(); - if (index != 0) + if (index > 0) comboBox.setSelectedIndex(index - 1); } @@ -1163,10 +1206,31 @@ public class BasicComboBoxUI extends ComboBoxUI * Invoked whenever key is pressed while JComboBox is in focus. */ public void keyPressed(KeyEvent e) - throws NotImplementedException { - // FIXME: This method calls JComboBox.selectWithKeyChar if the key that - // was pressed is not a navigation key. + if (comboBox.getModel().getSize() != 0 && comboBox.isEnabled()) + { + if (! isNavigationKey(e.getKeyCode())) + { + if (! comboBox.isEditable()) + if (comboBox.selectWithKeyChar(e.getKeyChar())) + e.consume(); + } + else + { + if (e.getKeyCode() == KeyEvent.VK_UP && comboBox.isPopupVisible()) + selectPreviousPossibleValue(); + else if (e.getKeyCode() == KeyEvent.VK_DOWN) + { + if (comboBox.isPopupVisible()) + selectNextPossibleValue(); + else + comboBox.showPopup(); + } + else if (e.getKeyCode() == KeyEvent.VK_ENTER + || e.getKeyCode() == KeyEvent.VK_ESCAPE) + popup.hide(); + } + } } } @@ -1330,5 +1394,4 @@ public class BasicComboBoxUI extends ComboBoxUI // FIXME: Need to handle changes in other bound properties. } } - } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java b/libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java index ef7a880c2ac..ed916cb5f1a 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java @@ -1,5 +1,5 @@ /* BasicDirectoryModel.java -- - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,35 +40,296 @@ package javax.swing.plaf.basic; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; import java.util.Vector; import javax.swing.AbstractListModel; import javax.swing.JFileChooser; +import javax.swing.SwingUtilities; import javax.swing.event.ListDataEvent; import javax.swing.filechooser.FileSystemView; /** - * DOCUMENT ME! + * Implements an AbstractListModel for directories where the source + * of the files is a JFileChooser object. + * + * This class is used for sorting and ordering the file list in + * a JFileChooser L&F object. */ public class BasicDirectoryModel extends AbstractListModel implements PropertyChangeListener { - /** DOCUMENT ME! */ + /** The list of files itself */ private Vector contents; - /** DOCUMENT ME! */ - private int directories; + /** + * The directories in the list. + */ + private Vector directories; + + /** + * The files in the list. + */ + private Vector files; - /** DOCUMENT ME! */ + /** The listing mode of the associated JFileChooser, + either FILES_ONLY, DIRECTORIES_ONLY or FILES_AND_DIRECTORIES */ private int listingMode; - /** DOCUMENT ME! */ + /** The JFileCooser associated with this model */ private JFileChooser filechooser; - /** DOCUMENT ME! */ + /** + * The thread that loads the file view. + */ + private DirectoryLoadThread loadThread; + + /** + * This thread is responsible for loading file lists from the + * current directory and updating the model. + */ + private class DirectoryLoadThread extends Thread + { + + /** + * Updates the Swing list model. + */ + private class UpdateSwingRequest + implements Runnable + { + + private List added; + private int addIndex; + private List removed; + private int removeIndex; + private boolean cancel; + + UpdateSwingRequest(List add, int ai, List rem, int ri) + { + added = add; + addIndex = ai; + removed = rem; + removeIndex = ri; + cancel = false; + } + + public void run() + { + if (! cancel) + { + int numRemoved = removed == null ? 0 : removed.size(); + int numAdded = added == null ? 0 : added.size(); + synchronized (contents) + { + if (numRemoved > 0) + contents.removeAll(removed); + if (numAdded > 0) + contents.addAll(added); + + files = null; + directories = null; + } + if (numRemoved > 0 && numAdded == 0) + fireIntervalRemoved(BasicDirectoryModel.this, removeIndex, + removeIndex + numRemoved - 1); + else if (numRemoved == 0 && numAdded > 0) + fireIntervalAdded(BasicDirectoryModel.this, addIndex, + addIndex + numAdded - 1); + else + fireContentsChanged(); + } + } + + void cancel() + { + cancel = true; + } + } + + /** + * The directory beeing loaded. + */ + File directory; + + /** + * Stores all UpdateSwingRequests that are sent to the event queue. + */ + private UpdateSwingRequest pending; + + /** + * Creates a new DirectoryLoadThread that loads the specified + * directory. + * + * @param dir the directory to load + */ + DirectoryLoadThread(File dir) + { + super("Basic L&F directory loader"); + directory = dir; + } + + public void run() + { + FileSystemView fsv = filechooser.getFileSystemView(); + File[] files = fsv.getFiles(directory, + filechooser.isFileHidingEnabled()); + + // Occasional check if we have been interrupted. + if (isInterrupted()) + return; + + // Check list for accepted files. + Vector accepted = new Vector(); + for (int i = 0; i < files.length; i++) + { + if (filechooser.accept(files[i])) + accepted.add(files[i]); + } + + // Occasional check if we have been interrupted. + if (isInterrupted()) + return; + + // Sort list. + sort(accepted); + + // Now split up directories from files so that we get the directories + // listed before the files. + Vector newFiles = new Vector(); + Vector newDirectories = new Vector(); + for (Iterator i = accepted.iterator(); i.hasNext();) + { + File f = (File) i.next(); + boolean traversable = filechooser.isTraversable(f); + if (traversable) + newDirectories.add(f); + else if (! traversable && filechooser.isFileSelectionEnabled()) + newFiles.add(f); + + // Occasional check if we have been interrupted. + if (isInterrupted()) + return; + + } + + // Build up new file cache. Try to update only the changed elements. + // This will be important for actions like adding new files or + // directories inside a large file list. + Vector newCache = new Vector(newDirectories); + newCache.addAll(newFiles); + + int newSize = newCache.size(); + int oldSize = contents.size(); + if (newSize < oldSize) + { + // Check for removed interval. + int start = -1; + int end = -1; + boolean found = false; + for (int i = 0; i < newSize && !found; i++) + { + if (! newCache.get(i).equals(contents.get(i))) + { + start = i; + end = i + oldSize - newSize; + found = true; + } + } + if (start >= 0 && end > start + && contents.subList(end, oldSize) + .equals(newCache.subList(start, newSize))) + { + // Occasional check if we have been interrupted. + if (isInterrupted()) + return; + + Vector removed = new Vector(contents.subList(start, end)); + UpdateSwingRequest r = new UpdateSwingRequest(null, 0, + removed, start); + invokeLater(r); + newCache = null; + } + } + else if (newSize > oldSize) + { + // Check for inserted interval. + int start = oldSize; + int end = newSize; + boolean found = false; + for (int i = 0; i < oldSize && ! found; i++) + { + if (! newCache.get(i).equals(contents.get(i))) + { + start = i; + boolean foundEnd = false; + for (int j = i; j < newSize && ! foundEnd; j++) + { + if (newCache.get(j).equals(contents.get(i))) + { + end = j; + foundEnd = true; + } + } + end = i + oldSize - newSize; + } + } + if (start >= 0 && end > start + && newCache.subList(end, newSize) + .equals(contents.subList(start, oldSize))) + { + // Occasional check if we have been interrupted. + if (isInterrupted()) + return; + + List added = newCache.subList(start, end); + UpdateSwingRequest r = new UpdateSwingRequest(added, start, + null, 0); + invokeLater(r); + newCache = null; + } + } + + // Handle complete list changes (newCache != null). + if (newCache != null && ! contents.equals(newCache)) + { + // Occasional check if we have been interrupted. + if (isInterrupted()) + return; + UpdateSwingRequest r = new UpdateSwingRequest(newCache, 0, + contents, 0); + invokeLater(r); + } + } + + /** + * Wraps SwingUtilities.invokeLater() and stores the request in + * a Vector so that we can still cancel it later. + * + * @param update the request to invoke + */ + private void invokeLater(UpdateSwingRequest update) + { + pending = update; + SwingUtilities.invokeLater(update); + } + + /** + * Cancels all pending update requests that might be in the AWT + * event queue. + */ + void cancelPending() + { + if (pending != null) + pending.cancel(); + } + } + + /** A Comparator class/object for sorting the file list. */ private Comparator comparator = new Comparator() { public int compare(Object o1, Object o2) @@ -91,14 +352,15 @@ public class BasicDirectoryModel extends AbstractListModel filechooser.addPropertyChangeListener(this); listingMode = filechooser.getFileSelectionMode(); contents = new Vector(); + validateFileCache(); } /** - * DOCUMENT ME! + * Returns whether a given (File) object is included in the list. * - * @param o DOCUMENT ME! + * @param o - The file object to test. * - * @return DOCUMENT ME! + * @return <code>true</code> if the list contains the given object. */ public boolean contains(Object o) { @@ -106,7 +368,7 @@ public class BasicDirectoryModel extends AbstractListModel } /** - * DOCUMENT ME! + * Fires a content change event. */ public void fireContentsChanged() { @@ -114,80 +376,99 @@ public class BasicDirectoryModel extends AbstractListModel } /** - * DOCUMENT ME! + * Returns a Vector of (java.io.File) objects containing + * the directories in this list. * - * @return DOCUMENT ME! + * @return a Vector */ public Vector getDirectories() { - Vector tmp = new Vector(); - for (int i = 0; i < directories; i++) - tmp.add(contents.get(i)); - return tmp; + // Synchronize this with the UpdateSwingRequest for the case when + // contents is modified. + synchronized (contents) + { + Vector dirs = directories; + if (dirs == null) + { + // Initializes this in getFiles(). + getFiles(); + dirs = directories; + } + return dirs; + } } /** - * DOCUMENT ME! + * Returns the (java.io.File) object at + * an index in the list. * - * @param index DOCUMENT ME! - * - * @return DOCUMENT ME! + * @param index The list index + * @return a File object */ public Object getElementAt(int index) { if (index > getSize() - 1) return null; - if (listingMode == JFileChooser.FILES_ONLY) - return contents.get(directories + index); - else - return contents.elementAt(index); + return contents.elementAt(index); } /** - * DOCUMENT ME! + * Returns a Vector of (java.io.File) objects containing + * the files in this list. * - * @return DOCUMENT ME! + * @return a Vector */ public Vector getFiles() { - Vector tmp = new Vector(); - for (int i = directories; i < getSize(); i++) - tmp.add(contents.get(i)); - return tmp; + synchronized (contents) + { + Vector f = files; + if (f == null) + { + f = new Vector(); + Vector d = new Vector(); // Directories; + for (Iterator i = contents.iterator(); i.hasNext();) + { + File file = (File) i.next(); + if (filechooser.isTraversable(file)) + d.add(file); + else + f.add(file); + } + files = f; + directories = d; + } + return f; + } } /** - * DOCUMENT ME! + * Returns the size of the list, which only includes directories + * if the JFileChooser is set to DIRECTORIES_ONLY. + * + * Otherwise, both directories and files are included in the count. * - * @return DOCUMENT ME! + * @return The size of the list. */ public int getSize() { - if (listingMode == JFileChooser.DIRECTORIES_ONLY) - return directories; - else if (listingMode == JFileChooser.FILES_ONLY) - return contents.size() - directories; return contents.size(); } /** - * DOCUMENT ME! + * Returns the index of an (java.io.File) object in the list. * - * @param o DOCUMENT ME! + * @param o The object - normally a File. * - * @return DOCUMENT ME! + * @return the index of that object, or -1 if it is not in the list. */ public int indexOf(Object o) { - if (listingMode == JFileChooser.FILES_ONLY) - return contents.indexOf(o) - directories; return contents.indexOf(o); } /** - * DOCUMENT ME! - * - * @param e DOCUMENT ME! + * Obsoleted method which does nothing. */ public void intervalAdded(ListDataEvent e) { @@ -195,9 +476,7 @@ public class BasicDirectoryModel extends AbstractListModel } /** - * DOCUMENT ME! - * - * @param e DOCUMENT ME! + * Obsoleted method which does nothing. */ public void intervalRemoved(ListDataEvent e) { @@ -205,7 +484,7 @@ public class BasicDirectoryModel extends AbstractListModel } /** - * DOCUMENT ME! + * Obsoleted method which does nothing. */ public void invalidateFileCache() { @@ -213,12 +492,16 @@ public class BasicDirectoryModel extends AbstractListModel } /** - * DOCUMENT ME! + * Less than, determine the relative order in the list of two files + * for sorting purposes. + * + * The order is: directories < files, and thereafter alphabetically, + * using the default locale collation. * - * @param a DOCUMENT ME! - * @param b DOCUMENT ME! + * @param a the first file + * @param b the second file * - * @return DOCUMENT ME! + * @return <code>true</code> if a > b, <code>false</code> if a < b. */ protected boolean lt(File a, File b) { @@ -241,73 +524,66 @@ public class BasicDirectoryModel extends AbstractListModel } /** - * DOCUMENT ME! + * Listens for a property change; the change in file selection mode of the + * associated JFileChooser. Reloads the file cache on that event. * - * @param e DOCUMENT ME! + * @param e - A PropertyChangeEvent. */ public void propertyChange(PropertyChangeEvent e) { - if (e.getPropertyName().equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)) - listingMode = filechooser.getFileSelectionMode(); + String property = e.getPropertyName(); + if (property.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY) + || property.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY) + || property.equals(JFileChooser.FILE_HIDING_CHANGED_PROPERTY) + || property.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY) + || property.equals(JFileChooser.FILE_VIEW_CHANGED_PROPERTY) + ) + { + validateFileCache(); + } } /** - * DOCUMENT ME! + * Renames a file - However, does <I>not</I> re-sort the list + * or replace the old file with the new one in the list. * - * @param oldFile DOCUMENT ME! - * @param newFile DOCUMENT ME! + * @param oldFile The old file + * @param newFile The new file name * - * @return DOCUMENT ME! + * @return <code>true</code> if the rename succeeded */ public boolean renameFile(File oldFile, File newFile) { - // FIXME: implement - return false; + return oldFile.renameTo( newFile ); } /** - * DOCUMENT ME! + * Sorts a Vector of File objects. * - * @param v DOCUMENT ME! + * @param v The Vector to sort. */ protected void sort(Vector v) { Collections.sort(v, comparator); - Enumeration e = Collections.enumeration(v); - Vector tmp = new Vector(); - for (; e.hasMoreElements();) - tmp.add(e.nextElement()); - - contents = tmp; } /** - * DOCUMENT ME! + * Re-loads the list of files */ public void validateFileCache() { - contents.clear(); - directories = 0; - FileSystemView fsv = filechooser.getFileSystemView(); - File[] list = fsv.getFiles(filechooser.getCurrentDirectory(), - filechooser.isFileHidingEnabled()); - - if (list == null) - return; - - for (int i = 0; i < list.length; i++) + File dir = filechooser.getCurrentDirectory(); + if (dir != null) { - if (list[i] == null) - continue; - if (filechooser.accept(list[i])) - { - contents.add(list[i]); - if (filechooser.isTraversable(list[i])) - directories++; - } + // Cancel all pending requests. + if (loadThread != null) + { + loadThread.interrupt(); + loadThread.cancelPending(); + } + loadThread = new DirectoryLoadThread(dir); + loadThread.start(); } - sort(contents); - filechooser.revalidate(); - filechooser.repaint(); } } + diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java index 1356db4aeec..dc1c051225c 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java @@ -160,6 +160,8 @@ public class BasicFileChooserUI extends FileChooserUI else { File f = new File(filechooser.getCurrentDirectory(), getFileName()); + if ( selectedDir != null ) + f = selectedDir; if (filechooser.isTraversable(f)) { filechooser.setCurrentDirectory(f); @@ -266,7 +268,14 @@ public class BasicFileChooserUI extends FileChooserUI */ public String getName(File f) { - return f.getName(); + String name = null; + if (f != null) + { + JFileChooser c = getFileChooser(); + FileSystemView v = c.getFileSystemView(); + name = v.getSystemDisplayName(f); + } + return name; } /** @@ -409,7 +418,7 @@ public class BasicFileChooserUI extends FileChooserUI closeDialog(); } } - else + else // single click { String path = p.toString(); File f = fsv.createFileObject(path); @@ -436,10 +445,11 @@ public class BasicFileChooserUI extends FileChooserUI } lastSelected = path; parentPath = path.substring(0, path.lastIndexOf("/") + 1); + if (f.isFile()) setFileName(path.substring(path.lastIndexOf("/") + 1)); - else if (filechooser.getFileSelectionMode() == - JFileChooser.DIRECTORIES_ONLY) + else if (filechooser.getFileSelectionMode() != + JFileChooser.FILES_ONLY) setFileName(path); } } @@ -538,7 +548,7 @@ public class BasicFileChooserUI extends FileChooserUI } /** - * DOCUMENT ME! + * Sets the JFileChooser to the selected file on an update * * @param e DOCUMENT ME! */ @@ -550,9 +560,15 @@ public class BasicFileChooserUI extends FileChooserUI return; File file = filechooser.getFileSystemView().createFileObject(f.toString()); if (! filechooser.isTraversable(file)) - filechooser.setSelectedFile(file); + { + selectedDir = null; + filechooser.setSelectedFile(file); + } else - filechooser.setSelectedFile(null); + { + selectedDir = file; + filechooser.setSelectedFile(null); + } } } @@ -752,6 +768,13 @@ public class BasicFileChooserUI extends FileChooserUI * @see #getUpdateAction() */ private UpdateAction updateAction; + + /** + * When in FILES_ONLY, mode a directory cannot be selected, so + * we save a reference to any it here. This is used to enter + * the directory on "Open" when in that mode. + */ + private File selectedDir; // -- end private -- @@ -874,7 +897,9 @@ public class BasicFileChooserUI extends FileChooserUI protected void installListeners(JFileChooser fc) { propertyChangeListener = createPropertyChangeListener(filechooser); - filechooser.addPropertyChangeListener(propertyChangeListener); + if (propertyChangeListener != null) + filechooser.addPropertyChangeListener(propertyChangeListener); + fc.addPropertyChangeListener(getModel()); } /** @@ -884,8 +909,12 @@ public class BasicFileChooserUI extends FileChooserUI */ protected void uninstallListeners(JFileChooser fc) { - filechooser.removePropertyChangeListener(propertyChangeListener); - propertyChangeListener = null; + if (propertyChangeListener != null) + { + filechooser.removePropertyChangeListener(propertyChangeListener); + propertyChangeListener = null; + } + fc.removePropertyChangeListener(getModel()); } /** @@ -1046,12 +1075,8 @@ public class BasicFileChooserUI extends FileChooserUI */ public PropertyChangeListener createPropertyChangeListener(JFileChooser fc) { - return new PropertyChangeListener() - { - public void propertyChange(PropertyChangeEvent e) - { - } - }; + // The RI returns null here, so do we. + return null; } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java b/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java index 068de345bec..1e84be93282 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java @@ -37,6 +37,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.SystemProperties; + import java.awt.Color; import java.awt.Dimension; import java.awt.Font; @@ -65,6 +67,14 @@ import javax.swing.SwingUtilities; public class BasicGraphicsUtils { /** + * Used as a key for a client property to store cached TextLayouts in. This + * is used for speed-up drawing of text in + * {@link #drawString(Graphics, String, int, int, int)}. + */ + static final String CACHED_TEXT_LAYOUT = + "BasicGraphicsUtils.cachedTextLayout"; + + /** * Constructor. It is utterly unclear why this class should * be constructable, but this is what the API specification * says. @@ -536,6 +546,170 @@ public class BasicGraphicsUtils g2.fill(underline); } + /** + * Draws a string on the specified component. + * + * @param c the component + * @param g the Graphics context + * @param text the string + * @param underlinedChar the character to be underlined + * @param x the X location + * @param y the Y location + */ + static void drawString(JComponent c, Graphics g, String text, + int underlinedChar, int x, int y) + { + int index = -1; + + /* It is intentional that lower case is used. In some languages, + * the set of lowercase characters is larger than the set of + * uppercase ones. Therefore, it is good practice to use lowercase + * for such comparisons (which really means that the author of this + * code can vaguely remember having read some Unicode techreport + * with this recommendation, but is too lazy to look for the URL). + */ + if ((underlinedChar >= 0) || (underlinedChar <= 0xffff)) + index = text.toLowerCase().indexOf( + Character.toLowerCase((char) underlinedChar)); + + drawStringUnderlineCharAt(c, g, text, index, x, y); + } + + + /** + * Draws a String at the given location, underlining the character + * at the specified index. Drawing is performed in the current color + * and font of <code>g</code>. + * + * <p><img src="doc-files/BasicGraphicsUtils-5.png" width="500" + * height="100" alt="[An illustration showing how to use the + * method]" /> + * + * This is an accelerated version of the method with the same name. It + * uses a pre-laid out TextLayout stored in a client property. + * + * @param c the component that is drawn + * @param g the graphics into which the String is drawn. + * + * @param text the String to draw. + * + * @param underlinedIndex the index of the underlined character in + * <code>text</code>. If <code>underlinedIndex</code> falls + * outside the range <code>[0, text.length() - 1]</code>, the + * text will be drawn without underlining anything. + * + * @param x the x coordinate of the text, as it would be passed to + * {@link java.awt.Graphics#drawString(java.lang.String, + * int, int)}. + * + * @param y the y coordinate of the text, as it would be passed to + * {@link java.awt.Graphics#drawString(java.lang.String, + * int, int)}. + */ + static void drawStringUnderlineCharAt(JComponent c, Graphics g, String text, + int underlinedIndex, + int x, int y) + { + Graphics2D g2; + Rectangle2D.Double underline; + FontRenderContext frc; + FontMetrics fmet; + LineMetrics lineMetrics; + Font font; + TextLayout layout; + double underlineX1, underlineX2; + boolean drawUnderline; + int textLength; + + textLength = text.length(); + if (textLength == 0) + return; + + drawUnderline = (underlinedIndex >= 0) && (underlinedIndex < textLength); + + // FIXME: unfortunately pango and cairo can't agree on metrics + // so for the time being we continue to *not* use TextLayouts. + if (!(g instanceof Graphics2D) + || SystemProperties.getProperty("gnu.javax.swing.noGraphics2D") != null) + { + /* Fall-back. This is likely to produce garbage for any text + * containing right-to-left (Hebrew or Arabic) characters, even + * if the underlined character is left-to-right. + */ + g.drawString(text, x, y); + if (drawUnderline) + { + fmet = g.getFontMetrics(); + g.fillRect( + /* x */ x + fmet.stringWidth(text.substring(0, underlinedIndex)), + /* y */ y + fmet.getDescent() - 1, + /* width */ fmet.charWidth(text.charAt(underlinedIndex)), + /* height */ 1); + } + + return; + } + + g2 = (Graphics2D) g; + font = g2.getFont(); + frc = g2.getFontRenderContext(); + lineMetrics = font.getLineMetrics(text, frc); + layout = (TextLayout) c.getClientProperty(CACHED_TEXT_LAYOUT); + if (layout == null) + { + layout = new TextLayout(text, font, frc); + System.err.println("Unable to use cached TextLayout for: " + text); + } + + /* Draw the text. */ + layout.draw(g2, x, y); + if (!drawUnderline) + return; + + underlineX1 = x + layout.getLogicalHighlightShape( + underlinedIndex, underlinedIndex).getBounds2D().getX(); + underlineX2 = x + layout.getLogicalHighlightShape( + underlinedIndex + 1, underlinedIndex + 1).getBounds2D().getX(); + + underline = new Rectangle2D.Double(); + if (underlineX1 < underlineX2) + { + underline.x = underlineX1; + underline.width = underlineX2 - underlineX1; + } + else + { + underline.x = underlineX2; + underline.width = underlineX1 - underlineX2; + } + + + underline.height = lineMetrics.getUnderlineThickness(); + underline.y = lineMetrics.getUnderlineOffset(); + if (underline.y == 0) + { + /* Some fonts do not specify an underline offset, although they + * actually should do so. In that case, the result of calling + * lineMetrics.getUnderlineOffset() will be zero. Since it would + * look very ugly if the underline was be positioned immediately + * below the baseline, we check for this and move the underline + * below the descent, as shown in the following ASCII picture: + * + * ##### ##### # + * # # # # + * # # # # + * # # # # + * ##### ###### ---- baseline (0) + * # + * # + * ------------------###----------- lineMetrics.getDescent() + */ + underline.y = lineMetrics.getDescent(); + } + + underline.y += y; + g2.fill(underline); + } /** * Draws a rectangle, simulating a dotted stroke by painting only diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java index 23bcdc315ee..8f2181336cb 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java @@ -38,7 +38,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import java.awt.AWTEvent; import java.awt.Color; import java.awt.Component; import java.awt.Container; @@ -692,17 +691,12 @@ public class BasicInternalFrameUI extends InternalFrameUI /** The MouseEvent target. */ private transient Component mouseEventTarget; - /** The component pressed. */ - private transient Component pressedComponent; + private Component dragTarget; - /** The last component entered. */ - private transient Component lastComponentEntered; - - /** Used to store/reset lastComponentEntered. */ - private transient Component tempComponent; - - /** The number of presses. */ - private transient int pressCount; + /** + * Indicates if we are currently in a dragging operation or not. + */ + private boolean isDragging; /** * This method is called when the mouse enters the glass pane. @@ -767,7 +761,10 @@ public class BasicInternalFrameUI extends InternalFrameUI */ public void mousePressed(MouseEvent e) { - activateFrame(frame); + // Experiments show that this seems to call the + // borderListener.mousePressed() method to activate the frame. + if (borderListener != null) + borderListener.mousePressed(e); handleEvent(e); } @@ -783,149 +780,104 @@ public class BasicInternalFrameUI extends InternalFrameUI } /** - * This method acquires a candidate component to dispatch the MouseEvent to. + * This is a helper method that dispatches the GlassPane MouseEvents to the + * proper component. * - * @param me - * The MouseEvent to acquire a component for. + * @param e the mouse event to be dispatched */ - private void acquireComponentForMouseEvent(MouseEvent me) + private void handleEvent(MouseEvent e) { - int x = me.getX(); - int y = me.getY(); - - // Find the candidate which should receive this event. - Component parent = frame.getLayeredPane(); - if (parent == null) - 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(); - } - } - - // If the only candidate we found was the native container itself, - // don't dispatch any event at all. We only care about the lightweight - // children here. - if (candidate == frame.getContentPane()) - candidate = null; - - // If our candidate is new, inform the old target we're leaving. - if (lastComponentEntered != null && lastComponentEntered.isShowing() - && lastComponentEntered != candidate) + // Find candidate component inside the JInternalFrame. + Component target = frame.getLayeredPane().findComponentAt(e.getX(), + e.getY()); + + // Now search upwards to find a component that actually has + // a MouseListener attached. + while (target != null + && target.getMouseListeners().length == 0 + && target.getMouseMotionListeners().length == 0 + && target.getMouseWheelListeners().length == 0) { - 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); + target = target.getParent(); } - // If we have a candidate, maybe enter it. - if (candidate != null) + if (target != 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); - } - } - - 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; - 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; + int id = e.getID(); + switch (id) + { + case MouseEvent.MOUSE_ENTERED: + // Now redispatch the thing. + if (! isDragging || frame.isSelected()) + { + mouseEventTarget = target; + redispatch(id, e, mouseEventTarget); + } + break; + case MouseEvent.MOUSE_EXITED: + if (! isDragging || frame.isSelected()) + { + redispatch(id, e, mouseEventTarget); + } + break; + case MouseEvent.MOUSE_PRESSED: + mouseEventTarget = target; + redispatch(id, e, mouseEventTarget); + // Start dragging. + dragTarget = target; + break; + case MouseEvent.MOUSE_RELEASED: + if (isDragging) + { + redispatch(id, e, dragTarget); + isDragging = false; + } + else + redispatch(id, e, mouseEventTarget); + break; + case MouseEvent.MOUSE_CLICKED: + redispatch(id, e, mouseEventTarget); + break; + case MouseEvent.MOUSE_MOVED: + if (target != mouseEventTarget) + { + // Create additional MOUSE_EXITED/MOUSE_ENTERED pairs. + redispatch(MouseEvent.MOUSE_EXITED, e, mouseEventTarget); + mouseEventTarget = target; + redispatch(MouseEvent.MOUSE_ENTERED, e, mouseEventTarget); + } + redispatch(id, e, mouseEventTarget); + break; + case MouseEvent.MOUSE_DRAGGED: + if (! isDragging) + isDragging = true; + redispatch(id, e, mouseEventTarget); + break; + case MouseEvent.MOUSE_WHEEL: + redispatch(id, e, mouseEventTarget); + break; + default: + assert false : "Must not reach here"; + } } } /** - * 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. + * Redispatches the event to the real target with the specified id. + * + * @param id the new event ID + * @param e the original event + * @param target the real event target */ - private void handleEvent(AWTEvent e) + private void redispatch(int id, MouseEvent e, Component target) { - if (e instanceof MouseEvent) - { - 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; - } - } - } + Point p = SwingUtilities.convertPoint(frame.getLayeredPane(), e.getX(), + e.getY(), target); + MouseEvent ev = new MouseEvent(target, id, e.getWhen(), + e.getModifiers() | e.getModifiersEx(), + p.x, p.y, e.getClickCount(), + e.isPopupTrigger()); + target.dispatchEvent(ev); } } @@ -945,41 +897,61 @@ public class BasicInternalFrameUI extends InternalFrameUI */ public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equals(JInternalFrame.IS_MAXIMUM_PROPERTY)) + String property = evt.getPropertyName(); + if (property.equals(JInternalFrame.IS_MAXIMUM_PROPERTY)) { if (frame.isMaximum()) maximizeFrame(frame); else minimizeFrame(frame); } - else if (evt.getPropertyName().equals(JInternalFrame.IS_ICON_PROPERTY)) + else if (property.equals(JInternalFrame.IS_ICON_PROPERTY)) { if (frame.isIcon()) iconifyFrame(frame); else deiconifyFrame(frame); } - else if (evt.getPropertyName().equals(JInternalFrame.IS_SELECTED_PROPERTY)) + else if (property.equals(JInternalFrame.IS_SELECTED_PROPERTY)) { + Component glassPane = frame.getGlassPane(); if (frame.isSelected()) - activateFrame(frame); + { + activateFrame(frame); + glassPane.setVisible(false); + } else - deactivateFrame(frame); + { + deactivateFrame(frame); + glassPane.setVisible(true); + } } - else if (evt.getPropertyName().equals(JInternalFrame.ROOT_PANE_PROPERTY) - || evt.getPropertyName().equals( - JInternalFrame.GLASS_PANE_PROPERTY)) + else if (property.equals(JInternalFrame.ROOT_PANE_PROPERTY) + || property.equals(JInternalFrame.GLASS_PANE_PROPERTY)) { Component old = (Component) evt.getOldValue(); - old.removeMouseListener(glassPaneDispatcher); - old.removeMouseMotionListener(glassPaneDispatcher); + if (old != null) + { + old.removeMouseListener(glassPaneDispatcher); + old.removeMouseMotionListener(glassPaneDispatcher); + } Component newPane = (Component) evt.getNewValue(); - newPane.addMouseListener(glassPaneDispatcher); - newPane.addMouseMotionListener(glassPaneDispatcher); + if (newPane != null) + { + newPane.addMouseListener(glassPaneDispatcher); + newPane.addMouseMotionListener(glassPaneDispatcher); + } frame.revalidate(); } + else if (property.equals(JInternalFrame.IS_CLOSED_PROPERTY)) + { + if (evt.getNewValue() == Boolean.TRUE) + { + closeFrame(frame); + } + } /* * FIXME: need to add ancestor properties to JComponents. else if * (evt.getPropertyName().equals(JComponent.ANCESTOR_PROPERTY)) { if diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java index 60e3a98684f..304e13ad735 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java @@ -1,5 +1,5 @@ /* BasicLabelUI.java - Copyright (C) 2002, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,20 +37,25 @@ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - 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.event.ActionEvent; +import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import javax.swing.AbstractAction; +import javax.swing.ActionMap; import javax.swing.Icon; +import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JLabel; +import javax.swing.KeyStroke; import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; import javax.swing.plaf.ComponentUI; @@ -369,14 +374,39 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener } /** - * This method installs the keyboard actions for the given {@link JLabel}. + * Installs the keyboard actions for the given {@link JLabel}. * * @param l The {@link JLabel} to install keyboard actions for. */ protected void installKeyboardActions(JLabel l) - throws NotImplementedException { - //FIXME: implement. + Component c = l.getLabelFor(); + if (c != null) + { + int mnemonic = l.getDisplayedMnemonic(); + if (mnemonic > 0) + { + // add a keystroke for the given mnemonic mapping to 'press'; + InputMap keyMap = new InputMap(); + keyMap.put(KeyStroke.getKeyStroke(mnemonic, KeyEvent.VK_ALT), + "press"); + SwingUtilities.replaceUIInputMap(l, + JComponent.WHEN_IN_FOCUSED_WINDOW, keyMap); + + // add an action to focus the component when 'press' happens + ActionMap map = new ActionMap(); + map.put("press", new AbstractAction() { + public void actionPerformed(ActionEvent event) + { + JLabel label = (JLabel) event.getSource(); + Component c = label.getLabelFor(); + if (c != null) + c.requestFocus(); + } + }); + SwingUtilities.replaceUIActionMap(l, map); + } + } } /** @@ -385,9 +415,10 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener * @param l The {@link JLabel} to uninstall keyboard actions for. */ protected void uninstallKeyboardActions(JLabel l) - throws NotImplementedException { - //FIXME: implement. + SwingUtilities.replaceUIActionMap(l, null); + SwingUtilities.replaceUIInputMap(l, JComponent.WHEN_IN_FOCUSED_WINDOW, + null); } /** @@ -426,5 +457,30 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener JLabel l = (JLabel) e.getSource(); BasicHTML.updateRenderer(l, text); } + else if (e.getPropertyName().equals("displayedMnemonic")) + { + // update the key to action mapping + JLabel label = (JLabel) e.getSource(); + if (label.getLabelFor() != null) + { + int oldMnemonic = ((Integer) e.getOldValue()).intValue(); + int newMnemonic = ((Integer) e.getNewValue()).intValue(); + InputMap keyMap = label.getInputMap( + JComponent.WHEN_IN_FOCUSED_WINDOW); + keyMap.put(KeyStroke.getKeyStroke(oldMnemonic, + KeyEvent.ALT_DOWN_MASK), null); + keyMap.put(KeyStroke.getKeyStroke(newMnemonic, + KeyEvent.ALT_DOWN_MASK), "press"); + } + } + else if (e.getPropertyName().equals("labelFor")) + { + JLabel label = (JLabel) e.getSource(); + InputMap keyMap = label.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); + int mnemonic = label.getDisplayedMnemonic(); + if (mnemonic > 0) + keyMap.put(KeyStroke.getKeyStroke(mnemonic, KeyEvent.ALT_DOWN_MASK), + "press"); + } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java index 44f6a408984..493fc0578e3 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java @@ -38,8 +38,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; @@ -61,12 +59,12 @@ import javax.swing.DefaultListSelectionModel; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JList; -import javax.swing.KeyStroke; import javax.swing.ListCellRenderer; import javax.swing.ListModel; import javax.swing.ListSelectionModel; import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; +import javax.swing.TransferHandler; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.event.ListDataEvent; @@ -76,8 +74,8 @@ 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; +import javax.swing.plaf.UIResource; /** * The Basic Look and Feel UI delegate for the @@ -215,9 +213,26 @@ public class BasicListUI extends ListUI target.actionPerformed(derivedEvent); } } - - class ListAction extends AbstractAction + + /** + * Implements the action for the JList's keyboard commands. + */ + private class ListAction + extends AbstractAction { + // TODO: Maybe make a couple of classes out of this bulk Action. + // Form logical groups of Actions when doing this. + + /** + * Creates a new ListAction for the specified command. + * + * @param cmd the actionCommand to set + */ + ListAction(String cmd) + { + putValue(ACTION_COMMAND_KEY, cmd); + } + public void actionPerformed(ActionEvent e) { int lead = list.getLeadSelectionIndex(); @@ -398,7 +413,7 @@ public class BasicListUI extends ListUI list.ensureIndexIsVisible(list.getLeadSelectionIndex()); } } - + /** * A helper class which listens for {@link MouseEvent}s * from the {@link JList}. @@ -464,7 +479,8 @@ public class BasicListUI extends ListUI */ public void mousePressed(MouseEvent event) { - // TODO: What should be done here, if anything? + // We need to explicitly request focus. + list.requestFocusInWindow(); } /** @@ -992,39 +1008,83 @@ public class BasicListUI extends ListUI */ protected void installKeyboardActions() { + // Install UI InputMap. InputMap focusInputMap = (InputMap) UIManager.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++) + SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, + focusInputMap); + + // Install UI ActionMap. + ActionMap am = (ActionMap) UIManager.get("List.actionMap"); + if (am == null) { - 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)); + // Create the actionMap once and store it in the current UIDefaults + // for use in other components. + am = new ActionMapUIResource(); + ListAction action; + action = new ListAction("selectPreviousRow"); + am.put("selectPreviousRow", action); + action = new ListAction("selectNextRow"); + am.put("selectNextRow", action); + action = new ListAction("selectPreviousRowExtendSelection"); + am.put("selectPreviousRowExtendSelection", action); + action = new ListAction("selectNextRowExtendSelection"); + am.put("selectNextRowExtendSelection", action); + + action = new ListAction("selectPreviousColumn"); + am.put("selectPreviousColumn", action); + action = new ListAction("selectNextColumn"); + am.put("selectNextColumn", action); + action = new ListAction("selectPreviousColumnExtendSelection"); + am.put("selectPreviousColumnExtendSelection", action); + action = new ListAction("selectNextColumnExtendSelection"); + am.put("selectNextColumnExtendSelection", action); + + action = new ListAction("selectFirstRow"); + am.put("selectFirstRow", action); + action = new ListAction("selectLastRow"); + am.put("selectLastRow", action); + action = new ListAction("selectFirstRowExtendSelection"); + am.put("selectFirstRowExtendSelection", action); + action = new ListAction("selectLastRowExtendSelection"); + am.put("selectLastRowExtendSelection", action); + + action = new ListAction("scrollUp"); + am.put("scrollUp", action); + action = new ListAction("scrollUpExtendSelection"); + am.put("scrollUpExtendSelection", action); + action = new ListAction("scrollDown"); + am.put("scrollDown", action); + action = new ListAction("scrollDownExtendSelection"); + am.put("scrollDownExtendSelection", action); + + action = new ListAction("selectAll"); + am.put("selectAll", action); + action = new ListAction("clearSelection"); + am.put("clearSelection", action); + + am.put("copy", TransferHandler.getCopyAction()); + am.put("cut", TransferHandler.getCutAction()); + am.put("paste", TransferHandler.getPasteAction()); + + UIManager.put("List.actionMap", am); } - // 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); + + SwingUtilities.replaceUIActionMap(list, am); } /** * Uninstalls keyboard actions for this UI in the {@link JList}. */ protected void uninstallKeyboardActions() - throws NotImplementedException { - // TODO: Implement this properly. + // Uninstall the InputMap. + InputMap im = SwingUtilities.getUIInputMap(list, JComponent.WHEN_FOCUSED); + if (im instanceof UIResource) + SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, null); + + // Uninstall the ActionMap. + if (SwingUtilities.getUIActionMap(list) instanceof UIResource) + SwingUtilities.replaceUIActionMap(list, null); } /** @@ -1151,7 +1211,7 @@ public class BasicListUI extends ListUI boolean hasFocus = (list.getLeadSelectionIndex() == row) && BasicListUI.this.list.hasFocus(); Component comp = rend.getListCellRendererComponent(list, data.getElementAt(row), - 0, isSel, hasFocus); + row, isSel, hasFocus); rendererPane.paintComponent(g, comp, list, bounds); } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java b/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java index 0f6e0243fcf..c056a2403f9 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java @@ -64,6 +64,7 @@ import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.BorderFactory; +import javax.swing.JComponent; import javax.swing.KeyStroke; import javax.swing.LookAndFeel; import javax.swing.MenuSelectionManager; @@ -129,7 +130,9 @@ public abstract class BasicLookAndFeel extends LookAndFeel if (target instanceof Container) target = ((Container) target).findComponentAt(ev.getPoint()); if (m.getSelectedPath().length > 0 - && ! m.isComponentPartOfCurrentMenu(target)) + && ! m.isComponentPartOfCurrentMenu(target) + && (((JComponent)target).getClientProperty(DONT_CANCEL_POPUP) == null + || !((JComponent)target).getClientProperty(DONT_CANCEL_POPUP).equals(Boolean.TRUE))) { m.clearSelectedPath(); } @@ -1028,6 +1031,25 @@ 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), + "PopupMenu.selectedWindowInputMapBindings", + new Object[] {"ESCAPE", "cancel", + "DOWN", "selectNext", + "KP_DOWN", "selectNext", + "UP", "selectPrevious", + "KP_UP", "selectPrevious", + "LEFT", "selectParent", + "KP_LEFT", "selectParent", + "RIGHT", "selectChild", + "KP_RIGHT", "selectChild", + "ENTER", "return", + "SPACE", "return" + }, + "PopupMenu.selectedWindowInputMapBindings.RightToLeft", + new Object[] {"LEFT", "selectChild", + "KP_LEFT", "selectChild", + "RIGHT", "selectParent", + "KP_RIGHT", "selectParent", + }, "ProgressBar.background", new ColorUIResource(Color.LIGHT_GRAY), "ProgressBar.border", new BorderUIResource.LineBorderUIResource(Color.GREEN, 2), @@ -1607,7 +1629,7 @@ public abstract class BasicLookAndFeel extends LookAndFeel }), "Tree.font", new FontUIResource("Dialog", Font.PLAIN, 12), "Tree.foreground", new ColorUIResource(Color.black), - "Tree.hash", new ColorUIResource(new Color(128, 128, 128)), + "Tree.hash", new ColorUIResource(new Color(184, 207, 228)), "Tree.leftChildIndent", new Integer(7), "Tree.rightChildIndent", new Integer(13), "Tree.rowHeight", new Integer(16), diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java index f258ebe3069..cd25a3baf77 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java @@ -38,24 +38,31 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.Dimension; +import java.awt.event.ActionEvent; 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.AbstractAction; +import javax.swing.Action; +import javax.swing.ActionMap; import javax.swing.BoxLayout; +import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.LookAndFeel; import javax.swing.MenuElement; +import javax.swing.MenuSelectionManager; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.MouseInputListener; +import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.MenuBarUI; @@ -64,6 +71,47 @@ import javax.swing.plaf.MenuBarUI; */ public class BasicMenuBarUI extends MenuBarUI { + + /** + * This action is performed for the action command 'takeFocus'. + */ + private static class FocusAction + extends AbstractAction + { + + /** + * Creates a new FocusAction. + */ + FocusAction() + { + super("takeFocus"); + } + + /** + * Performs the action. + */ + public void actionPerformed(ActionEvent event) + { + // In the JDK this action seems to pop up the first menu of the + // menu bar. + JMenuBar menuBar = (JMenuBar) event.getSource(); + MenuSelectionManager defaultManager = + MenuSelectionManager.defaultManager(); + MenuElement me[]; + MenuElement subElements[]; + JMenu menu = menuBar.getMenu(0); + if (menu != null) + { + me = new MenuElement[3]; + me[0] = (MenuElement) menuBar; + me[1] = (MenuElement) menu; + me[2] = (MenuElement) menu.getPopupMenu(); + defaultManager.setSelectedPath(me); + } + } + + } + protected ChangeListener changeListener; /*ContainerListener that listens to the ContainerEvents fired from menu bar*/ @@ -178,9 +226,46 @@ public class BasicMenuBarUI extends MenuBarUI * This method installs the keyboard actions for the JMenuBar. */ protected void installKeyboardActions() - throws NotImplementedException { - // FIXME: implement + // Install InputMap. + Object[] bindings = + (Object[]) SharedUIDefaults.get("MenuBar.windowBindings"); + InputMap inputMap = LookAndFeel.makeComponentInputMap(menuBar, bindings); + SwingUtilities.replaceUIInputMap(menuBar, + JComponent.WHEN_IN_FOCUSED_WINDOW, + inputMap); + + // Install ActionMap. + SwingUtilities.replaceUIActionMap(menuBar, getActionMap()); + } + + /** + * Creates and returns the shared action map for JTrees. + * + * @return the shared action map for JTrees + */ + private ActionMap getActionMap() + { + ActionMap am = (ActionMap) UIManager.get("MenuBar.actionMap"); + if (am == null) + { + am = createDefaultActions(); + UIManager.getLookAndFeelDefaults().put("MenuBar.actionMap", am); + } + return am; + } + + /** + * Creates the default actions when there are none specified by the L&F. + * + * @return the default actions + */ + private ActionMap createDefaultActions() + { + ActionMapUIResource am = new ActionMapUIResource(); + Action action = new FocusAction(); + am.put(action.getValue(Action.NAME), action); + return am; } /** @@ -226,9 +311,10 @@ public class BasicMenuBarUI extends MenuBarUI * This method reverses the work done in installKeyboardActions. */ protected void uninstallKeyboardActions() - throws NotImplementedException { - // FIXME: implement. + SwingUtilities.replaceUIInputMap(menuBar, + JComponent.WHEN_IN_FOCUSED_WINDOW, null); + SwingUtilities.replaceUIActionMap(menuBar, null); } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java index 63a09bff6a2..bbc08535cdc 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.SystemProperties; + import java.awt.Color; import java.awt.Component; import java.awt.Container; @@ -52,11 +54,15 @@ import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import javax.swing.AbstractAction; +import javax.swing.AbstractButton; import javax.swing.ActionMap; import javax.swing.ButtonModel; import javax.swing.Icon; @@ -237,7 +243,8 @@ public class BasicMenuItemUI extends MenuItemUI */ public void propertyChange(PropertyChangeEvent e) { - if (e.getPropertyName() == "accelerator") + String property = e.getPropertyName(); + if (property.equals("accelerator")) { InputMap map = SwingUtilities.getUIInputMap(menuItem, JComponent.WHEN_IN_FOCUSED_WINDOW); @@ -250,6 +257,22 @@ public class BasicMenuItemUI extends MenuItemUI if (accelerator != null) map.put(accelerator, "doClick"); } + // TextLayout caching for speed-up drawing of text. + else if ((property.equals(AbstractButton.TEXT_CHANGED_PROPERTY) + || property.equals("font")) + && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D") + == null) + + { + AbstractButton b = (AbstractButton) e.getSource(); + String text = b.getText(); + if (text == null) + text = ""; + FontRenderContext frc = new FontRenderContext(new AffineTransform(), + false, false); + TextLayout layout = new TextLayout(text, b.getFont(), frc); + b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, layout); + } } } @@ -833,12 +856,13 @@ public class BasicMenuItemUI extends MenuItemUI int mnemonicIndex = menuItem.getDisplayedMnemonicIndex(); if (mnemonicIndex != -1) - BasicGraphicsUtils.drawStringUnderlineCharAt(g, text, mnemonicIndex, + BasicGraphicsUtils.drawStringUnderlineCharAt(menuItem, g, text, + mnemonicIndex, textRect.x, textRect.y + fm.getAscent()); else - BasicGraphicsUtils.drawString(g, text, 0, textRect.x, + BasicGraphicsUtils.drawString(menuItem, g, text, 0, textRect.x, textRect.y + fm.getAscent()); } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java index f8936be5b66..7d8784fd15a 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java @@ -220,9 +220,8 @@ public class BasicMenuUI extends BasicMenuItemUI * */ protected void installKeyboardActions() - throws NotImplementedException { - // FIXME: Need to implement + super.installKeyboardActions(); } /** @@ -230,13 +229,12 @@ public class BasicMenuUI extends BasicMenuItemUI */ protected void installListeners() { - ((JMenu) menuItem).addMouseListener(mouseInputListener); - ((JMenu) menuItem).addMouseMotionListener(mouseInputListener); + super.installListeners(); ((JMenu) menuItem).addMenuListener(menuListener); - ((JMenu) menuItem).addMenuDragMouseListener(menuDragMouseListener); } protected void setupPostTimer(JMenu menu) + throws NotImplementedException { // TODO: Implement this properly. } @@ -265,9 +263,8 @@ public class BasicMenuUI extends BasicMenuItemUI * Basic look and feel's defaults. */ protected void uninstallKeyboardActions() - throws NotImplementedException { - // FIXME: Need to implement + super.installKeyboardActions(); } /** @@ -276,9 +273,8 @@ public class BasicMenuUI extends BasicMenuItemUI */ protected void uninstallListeners() { - ((JMenu) menuItem).removeMouseListener(mouseInputListener); + super.uninstallListeners(); ((JMenu) menuItem).removeMenuListener(menuListener); - ((JMenu) menuItem).removePropertyChangeListener(propertyChangeListener); } /** @@ -351,7 +347,7 @@ public class BasicMenuUI extends BasicMenuItemUI public void mouseMoved(MouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } public void mousePressed(MouseEvent e) @@ -472,7 +468,8 @@ public class BasicMenuUI extends BasicMenuItemUI */ public ChangeHandler(JMenu m, BasicMenuUI ui) { - // Not used. + menu = m; + this.ui = ui; } /** @@ -520,7 +517,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void menuDragMouseExited(MenuDragMouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } /** @@ -531,7 +528,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void menuDragMouseReleased(MenuDragMouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } } @@ -548,7 +545,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void menuKeyPressed(MenuKeyEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } /** @@ -558,7 +555,7 @@ public class BasicMenuUI extends BasicMenuItemUI */ public void menuKeyReleased(MenuKeyEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } /** @@ -568,6 +565,7 @@ public class BasicMenuUI extends BasicMenuItemUI * @param e A {@link MenuKeyEvent}. */ public void menuKeyTyped(MenuKeyEvent e) + throws NotImplementedException { // 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 9acf8210d9e..e2380858098 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java @@ -38,13 +38,12 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; +import java.awt.Font; import java.awt.Graphics; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; @@ -58,10 +57,14 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyVetoException; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ActionMap; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.Icon; +import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; @@ -76,6 +79,7 @@ import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.border.Border; +import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.OptionPaneUI; @@ -85,6 +89,21 @@ import javax.swing.plaf.OptionPaneUI; public class BasicOptionPaneUI extends OptionPaneUI { /** + * Implements the "close" keyboard action. + */ + static class OptionPaneCloseAction + extends AbstractAction + { + + public void actionPerformed(ActionEvent event) + { + JOptionPane op = (JOptionPane) event.getSource(); + op.setValue(new Integer(JOptionPane.CLOSED_OPTION)); + } + + } + + /** * This is a helper class that listens to the buttons located at the bottom * of the JOptionPane. * @@ -389,36 +408,20 @@ public class BasicOptionPaneUI extends OptionPaneUI */ public void propertyChange(PropertyChangeEvent e) { - if (e.getPropertyName().equals(JOptionPane.ICON_PROPERTY) - || e.getPropertyName().equals(JOptionPane.MESSAGE_TYPE_PROPERTY)) - addIcon(messageAreaContainer); - else if (e.getPropertyName().equals(JOptionPane.INITIAL_SELECTION_VALUE_PROPERTY)) - resetSelectedValue(); - else if (e.getPropertyName().equals(JOptionPane.INITIAL_VALUE_PROPERTY) - || e.getPropertyName().equals(JOptionPane.OPTIONS_PROPERTY) - || e.getPropertyName().equals(JOptionPane.OPTION_TYPE_PROPERTY)) + String property = e.getPropertyName(); + if (property.equals(JOptionPane.ICON_PROPERTY) + || property.equals(JOptionPane.INITIAL_SELECTION_VALUE_PROPERTY) + || property.equals(JOptionPane.INITIAL_VALUE_PROPERTY) + || property.equals(JOptionPane.MESSAGE_PROPERTY) + || property.equals(JOptionPane.MESSAGE_TYPE_PROPERTY) + || property.equals(JOptionPane.OPTION_TYPE_PROPERTY) + || property.equals(JOptionPane.OPTIONS_PROPERTY) + || property.equals(JOptionPane.WANTS_INPUT_PROPERTY)) { - Container newButtons = createButtonArea(); - optionPane.remove(buttonContainer); - optionPane.add(newButtons); - buttonContainer = newButtons; + uninstallComponents(); + installComponents(); + optionPane.validate(); } - - else if (e.getPropertyName().equals(JOptionPane.MESSAGE_PROPERTY) - || e.getPropertyName().equals(JOptionPane.WANTS_INPUT_PROPERTY) - || e.getPropertyName().equals(JOptionPane.SELECTION_VALUES_PROPERTY)) - { - 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(); } } @@ -460,17 +463,7 @@ public class BasicOptionPaneUI extends OptionPaneUI protected JOptionPane optionPane; /** The size of the icons. */ - // FIXME: wrong name for a constant. - private static final int iconSize = 36; - - /** The foreground color for the message area. */ - private transient Color messageForeground; - - /** The border around the message area. */ - private transient Border messageBorder; - - /** The border around the button area. */ - private transient Border buttonBorder; + private static final int ICON_SIZE = 36; /** The string used to describe OK buttons. */ private static final String OK_STRING = "OK"; @@ -505,7 +498,7 @@ public class BasicOptionPaneUI extends OptionPaneUI */ public int getIconWidth() { - return iconSize; + return ICON_SIZE; } /** @@ -515,7 +508,7 @@ public class BasicOptionPaneUI extends OptionPaneUI */ public int getIconHeight() { - return iconSize; + return ICON_SIZE; } /** @@ -566,7 +559,7 @@ public class BasicOptionPaneUI extends OptionPaneUI // Should be purple. g.setColor(Color.RED); - g.fillOval(0, 0, iconSize, iconSize); + g.fillOval(0, 0, ICON_SIZE, ICON_SIZE); g.setColor(Color.BLACK); g.drawOval(16, 6, 4, 4); @@ -615,7 +608,7 @@ public class BasicOptionPaneUI extends OptionPaneUI Color saved = g.getColor(); g.setColor(Color.GREEN); - g.fillRect(0, 0, iconSize, iconSize); + g.fillRect(0, 0, ICON_SIZE, ICON_SIZE); g.setColor(Color.BLACK); @@ -623,7 +616,7 @@ public class BasicOptionPaneUI extends OptionPaneUI g.drawOval(14, 5, 10, 10); g.setColor(Color.GREEN); - g.fillRect(0, 10, iconSize, iconSize - 10); + g.fillRect(0, 10, ICON_SIZE, ICON_SIZE - 10); g.setColor(Color.BLACK); @@ -640,10 +633,6 @@ public class BasicOptionPaneUI extends OptionPaneUI } }; - // FIXME: Uncomment when the ImageIcons are fixed. - - /* IconUIResource warningIcon, questionIcon, infoIcon, errorIcon;*/ - /** * Creates a new BasicOptionPaneUI object. */ @@ -705,6 +694,7 @@ public class BasicOptionPaneUI extends OptionPaneUI if (icon != null) { iconLabel = new JLabel(icon); + configureLabel(iconLabel); top.add(iconLabel, BorderLayout.WEST); } } @@ -766,7 +756,9 @@ public class BasicOptionPaneUI extends OptionPaneUI } else if (msg instanceof Icon) { - container.add(new JLabel((Icon) msg), cons); + JLabel label = new JLabel((Icon) msg); + configureLabel(label); + container.add(label, cons); cons.gridy++; } else @@ -783,8 +775,11 @@ public class BasicOptionPaneUI extends OptionPaneUI addMessageComponents(container, cons, tmp, maxll, true); } else - addMessageComponents(container, cons, new JLabel(msg.toString()), - maxll, true); + { + JLabel label = new JLabel(msg.toString()); + configureLabel(label); + addMessageComponents(container, cons, label, maxll, true); + } } } @@ -815,6 +810,7 @@ public class BasicOptionPaneUI extends OptionPaneUI remainder = d.substring(maxll); } JLabel label = new JLabel(line); + configureLabel(label); c.add(label); // If there is nothing left to burst, then we can stop. @@ -825,8 +821,12 @@ public class BasicOptionPaneUI extends OptionPaneUI if (remainder.length() > maxll || remainder.contains("\n")) burstStringInto(c, remainder, maxll); else - // Add the remainder to the container and be done. - c.add(new JLabel(remainder)); + { + // Add the remainder to the container and be done. + JLabel l = new JLabel(remainder); + configureLabel(l); + c.add(l); + } } /** @@ -862,6 +862,9 @@ public class BasicOptionPaneUI extends OptionPaneUI protected Container createButtonArea() { JPanel buttonPanel = new JPanel(); + Border b = UIManager.getBorder("OptionPane.buttonAreaBorder"); + if (b != null) + buttonPanel.setBorder(b); buttonPanel.setLayout(createLayoutManager()); addButtonComponents(buttonPanel, getButtons(), getInitialValueIndex()); @@ -887,6 +890,10 @@ public class BasicOptionPaneUI extends OptionPaneUI protected Container createMessageArea() { JPanel messageArea = new JPanel(); + Border messageBorder = UIManager.getBorder("OptionPane.messageAreaBorder"); + if (messageBorder != null) + messageArea.setBorder(messageBorder); + messageArea.setLayout(new BorderLayout()); addIcon(messageArea); @@ -941,8 +948,9 @@ public class BasicOptionPaneUI extends OptionPaneUI */ protected Container createSeparator() { - // FIXME: Figure out what this method is supposed to return and where - // this should be added to the OptionPane. + // The reference implementation returns null here. When overriding + // to return something non-null, the component gets added between + // the message area and the button area. See installComponents(). return null; } @@ -1143,35 +1151,17 @@ public class BasicOptionPaneUI extends OptionPaneUI */ protected void installComponents() { - // reset it. - hasCustomComponents = false; - Container msg = createMessageArea(); - if (msg != null) - { - ((JComponent) msg).setBorder(messageBorder); - msg.setForeground(messageForeground); - messageAreaContainer = msg; - optionPane.add(msg); - } + // First thing is the message area. + optionPane.add(createMessageArea()); - // FIXME: Figure out if the separator should be inserted here or what - // this thing is supposed to do. Note: The JDK does NOT insert another - // component at this place. The JOptionPane only has two panels in it - // and there actually are applications that depend on this beeing so. + // Add separator when createSeparator() is overridden to return + // something other than null. Container sep = createSeparator(); if (sep != null) optionPane.add(sep); - Container button = createButtonArea(); - if (button != null) - { - ((JComponent) button).setBorder(buttonBorder); - buttonContainer = button; - optionPane.add(button); - } - - optionPane.setBorder(BorderFactory.createEmptyBorder(12, 12, 11, 11)); - optionPane.invalidate(); + // Last thing is the button area. + optionPane.add(createButtonArea()); } /** @@ -1185,10 +1175,6 @@ public class BasicOptionPaneUI extends OptionPaneUI LookAndFeel.installBorder(optionPane, "OptionPane.border"); optionPane.setOpaque(true); - messageBorder = UIManager.getBorder("OptionPane.messageAreaBorder"); - messageForeground = UIManager.getColor("OptionPane.messageForeground"); - buttonBorder = UIManager.getBorder("OptionPane.buttonAreaBorder"); - minimumSize = UIManager.getDimension("OptionPane.minimumSize"); // FIXME: Image icons don't seem to work properly right now. @@ -1206,9 +1192,44 @@ public class BasicOptionPaneUI extends OptionPaneUI * This method installs keyboard actions for the JOptionpane. */ protected void installKeyboardActions() - throws NotImplementedException { - // FIXME: implement. + // Install the input map. + Object[] bindings = + (Object[]) SharedUIDefaults.get("OptionPane.windowBindings"); + InputMap inputMap = LookAndFeel.makeComponentInputMap(optionPane, + bindings); + SwingUtilities.replaceUIInputMap(optionPane, + JComponent.WHEN_IN_FOCUSED_WINDOW, + inputMap); + + // FIXME: The JDK uses a LazyActionMap for parentActionMap + SwingUtilities.replaceUIActionMap(optionPane, getActionMap()); + } + + /** + * Fetches the action map from the UI defaults, or create a new one + * if the action map hasn't been initialized. + * + * @return the action map + */ + private ActionMap getActionMap() + { + ActionMap am = (ActionMap) UIManager.get("OptionPane.actionMap"); + if (am == null) + { + am = createDefaultActions(); + UIManager.getLookAndFeelDefaults().put("OptionPane.actionMap", am); + } + return am; + } + + private ActionMap createDefaultActions() + { + ActionMapUIResource am = new ActionMapUIResource(); + Action action = new OptionPaneCloseAction(); + + am.put("close", action); + return am; } /** @@ -1321,10 +1342,6 @@ public class BasicOptionPaneUI extends OptionPaneUI minimumSize = null; - messageBorder = null; - buttonBorder = null; - messageForeground = null; - // FIXME: ImageIcons don't seem to work properly /* @@ -1339,9 +1356,10 @@ public class BasicOptionPaneUI extends OptionPaneUI * This method uninstalls keyboard actions for the JOptionPane. */ protected void uninstallKeyboardActions() - throws NotImplementedException { - // FIXME: implement. + SwingUtilities.replaceUIInputMap(optionPane, JComponent. + WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); + SwingUtilities.replaceUIActionMap(optionPane, null); } /** @@ -1367,4 +1385,20 @@ public class BasicOptionPaneUI extends OptionPaneUI optionPane = null; } + + /** + * Applies the proper UI configuration to labels that are added to + * the OptionPane. + * + * @param l the label to configure + */ + private void configureLabel(JLabel l) + { + Color c = UIManager.getColor("OptionPane.messageForeground"); + if (c != null) + l.setForeground(c); + Font f = UIManager.getFont("OptionPane.messageFont"); + if (f != null) + l.setFont(f); + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java index a26a5c7c46b..8c0fe6757e3 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java @@ -37,33 +37,574 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.Component; import java.awt.Dimension; +import java.awt.KeyboardFocusManager; +import java.awt.event.ActionEvent; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.MouseEvent; +import java.util.EventListener; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ActionMap; import javax.swing.BoxLayout; +import javax.swing.InputMap; +import javax.swing.JApplet; import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; +import javax.swing.JRootPane; import javax.swing.LookAndFeel; import javax.swing.MenuElement; import javax.swing.MenuSelectionManager; import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; +import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.PopupMenuUI; - /** * UI Delegate for JPopupMenu */ public class BasicPopupMenuUI extends PopupMenuUI { + /** + * Handles keyboard navigation through menus. + */ + private static class NavigateAction + extends AbstractAction + { + + /** + * Creates a new NavigateAction instance. + * + * @param name the name of the action + */ + NavigateAction(String name) + { + super(name); + } + + /** + * Actually performs the action. + */ + public void actionPerformed(ActionEvent event) + { + String name = (String) getValue(Action.NAME); + if (name.equals("selectNext")) + navigateNextPrevious(true); + else if (name.equals("selectPrevious")) + navigateNextPrevious(false); + else if (name.equals("selectChild")) + navigateParentChild(true); + else if (name.equals("selectParent")) + navigateParentChild(false); + else if (name.equals("cancel")) + cancel(); + else if (name.equals("return")) + doReturn(); + else + assert false : "Must not reach here"; + } + + /** + * Navigates to the next or previous menu item. + * + * @param dir <code>true</code>: navigate to next, <code>false</code>: + * navigate to previous + */ + private void navigateNextPrevious(boolean dir) + { + MenuSelectionManager msm = MenuSelectionManager.defaultManager(); + MenuElement path[] = msm.getSelectedPath(); + int len = path.length; + if (len >= 2) + { + + if (path[0] instanceof JMenuBar && + path[1] instanceof JMenu && len == 2) + { + + // A toplevel menu is selected, but its popup not yet shown. + // Show the popup and select the first item + JPopupMenu popup = ((JMenu)path[1]).getPopupMenu(); + MenuElement next = + findEnabledChild(popup.getSubElements(), -1, true); + MenuElement[] newPath; + + if (next != null) + { + newPath = new MenuElement[4]; + newPath[3] = next; + } + else + { + // Menu has no enabled items, show the popup anyway. + newPath = new MenuElement[3]; + } + System.arraycopy(path, 0, newPath, 0, 2); + newPath[2] = popup; + msm.setSelectedPath(newPath); + } + else if (path[len - 1] instanceof JPopupMenu && + path[len - 2] instanceof JMenu) + { + // Select next item in already shown popup menu. + JMenu menu = (JMenu) path[len - 2]; + JPopupMenu popup = menu.getPopupMenu(); + MenuElement next = + findEnabledChild(popup.getSubElements(), -1, dir); + + if (next != null) + { + MenuElement[] newPath = new MenuElement[len + 1]; + System.arraycopy(path, 0, newPath, 0, len); + newPath[len] = next; + msm.setSelectedPath(newPath); + } + else + { + // All items in the popup are disabled. + // Find the parent popup menu and select + // its next item. If there's no parent popup menu , do nothing. + if (len > 2 && path[len - 3] instanceof JPopupMenu) + { + popup = ((JPopupMenu) path[len - 3]); + next = findEnabledChild(popup.getSubElements(), + menu, dir); + if (next != null && next != menu) + { + MenuElement[] newPath = new MenuElement[len - 1]; + System.arraycopy(path, 0, newPath, 0, len - 2); + newPath[len - 2] = next; + msm.setSelectedPath(newPath); + } + } + } + } + else + { + // Only select the next item. + MenuElement subs[] = path[len - 2].getSubElements(); + MenuElement nextChild = + findEnabledChild(subs, path[len - 1], dir); + if (nextChild == null) + { + nextChild = findEnabledChild(subs, -1, dir); + } + if (nextChild != null) + { + path[len-1] = nextChild; + msm.setSelectedPath(path); + } + } + } + } + + private MenuElement findEnabledChild(MenuElement[] children, + MenuElement start, boolean dir) + { + MenuElement found = null; + for (int i = 0; i < children.length && found == null; i++) + { + if (children[i] == start) + { + found = findEnabledChild(children, i, dir); + } + } + return found; + } + + /** + * Searches the next or previous enabled child menu element. + * + * @param children the children to search through + * @param start the index at which to start + * @param dir the direction (true == forward, false == backward) + * + * @return the found element or null + */ + private MenuElement findEnabledChild(MenuElement[] children, + int start, boolean dir) + { + MenuElement result = null; + if (dir) + { + result = findNextEnabledChild(children, start + 1, children.length-1); + if (result == null) + result = findNextEnabledChild(children, 0, start - 1); + } + else + { + result = findPreviousEnabledChild(children, start - 1, 0); + if (result == null) + result = findPreviousEnabledChild(children, children.length-1, + start + 1); + } + return result; + } + + /** + * Finds the next child element that is enabled and visible. + * + * @param children the children to search through + * @param start the start index + * @param end the end index + * + * @return the found child, or null + */ + private MenuElement findNextEnabledChild(MenuElement[] children, int start, + int end) + { + MenuElement found = null; + for (int i = start; i <= end && found == null; i++) + { + if (children[i] != null) + { + Component comp = children[i].getComponent(); + if (comp != null && comp.isEnabled() && comp.isVisible()) + { + found = children[i]; + } + } + } + return found; + } + + /** + * Finds the previous child element that is enabled and visible. + * + * @param children the children to search through + * @param start the start index + * @param end the end index + * + * @return the found child, or null + */ + private MenuElement findPreviousEnabledChild(MenuElement[] children, + int start, int end) + { + MenuElement found = null; + for (int i = start; i >= end && found == null; i--) + { + if (children[i] != null) + { + Component comp = children[i].getComponent(); + if (comp != null && comp.isEnabled() && comp.isVisible()) + { + found = children[i]; + } + } + } + return found; + } + + /** + * Navigates to the parent or child menu item. + * + * @param selectChild <code>true</code>: navigate to child, + * <code>false</code>: navigate to parent + */ + private void navigateParentChild(boolean selectChild) + { + MenuSelectionManager msm = MenuSelectionManager.defaultManager(); + MenuElement path[] = msm.getSelectedPath(); + int len = path.length; + + if (selectChild) + { + if (len > 0 && path[len - 1] instanceof JMenu + && ! ((JMenu) path[len-1]).isTopLevelMenu()) + { + // We have a submenu, open it. + JMenu menu = (JMenu) path[len - 1]; + JPopupMenu popup = menu.getPopupMenu(); + MenuElement[] subs = popup.getSubElements(); + MenuElement item = findEnabledChild(subs, -1, true); + MenuElement[] newPath; + + if (item == null) + { + newPath = new MenuElement[len + 1]; + } + else + { + newPath = new MenuElement[len + 2]; + newPath[len + 1] = item; + } + System.arraycopy(path, 0, newPath, 0, len); + newPath[len] = popup; + msm.setSelectedPath(newPath); + return; + } + } + else + { + int popupIndex = len-1; + if (len > 2 + && (path[popupIndex] instanceof JPopupMenu + || path[--popupIndex] instanceof JPopupMenu) + && ! ((JMenu) path[popupIndex - 1]).isTopLevelMenu()) + { + // We have a submenu, close it. + MenuElement newPath[] = new MenuElement[popupIndex]; + System.arraycopy(path, 0, newPath, 0, popupIndex); + msm.setSelectedPath(newPath); + return; + } + } + + // If we got here, we have not selected a child or parent. + // Check if we have a toplevel menu selected. If so, then select + // another one. + if (len > 1 && path[0] instanceof JMenuBar) + { + MenuElement currentMenu = path[1]; + MenuElement nextMenu = findEnabledChild(path[0].getSubElements(), + currentMenu, selectChild); + + if (nextMenu != null && nextMenu != currentMenu) + { + MenuElement newSelection[]; + if (len == 2) + { + // Menu is selected but its popup not shown. + newSelection = new MenuElement[2]; + newSelection[0] = path[0]; + newSelection[1] = nextMenu; + } + else + { + // Menu is selected and its popup is shown. + newSelection = new MenuElement[3]; + newSelection[0] = path[0]; + newSelection[1] = nextMenu; + newSelection[2] = ((JMenu) nextMenu).getPopupMenu(); + } + msm.setSelectedPath(newSelection); + } + } + } + + /** + * Handles cancel requests (ESC key). + */ + private void cancel() + { + // Fire popup menu cancelled event. Unfortunately the + // firePopupMenuCancelled() is protected in JPopupMenu so we work + // around this limitation by fetching the listeners and notifying them + // directly. + JPopupMenu lastPopup = (JPopupMenu) getLastPopup(); + EventListener[] ll = lastPopup.getListeners(PopupMenuListener.class); + for (int i = 0; i < ll.length; i++) + { + PopupMenuEvent ev = new PopupMenuEvent(lastPopup); + ((PopupMenuListener) ll[i]).popupMenuCanceled(ev); + } + + // Close the last popup or the whole selection if there's only one + // popup left. + MenuSelectionManager msm = MenuSelectionManager.defaultManager(); + MenuElement path[] = msm.getSelectedPath(); + if(path.length > 4) + { + MenuElement newPath[] = new MenuElement[path.length - 2]; + System.arraycopy(path,0,newPath,0,path.length-2); + MenuSelectionManager.defaultManager().setSelectedPath(newPath); + } + else + msm.clearSelectedPath(); + } + + /** + * Returns the last popup menu in the current selection or null. + * + * @return the last popup menu in the current selection or null + */ + private JPopupMenu getLastPopup() + { + MenuSelectionManager msm = MenuSelectionManager.defaultManager(); + MenuElement[] p = msm.getSelectedPath(); + JPopupMenu popup = null; + for(int i = p.length - 1; popup == null && i >= 0; i--) + { + if (p[i] instanceof JPopupMenu) + popup = (JPopupMenu) p[i]; + } + return popup; + } + + /** + * Handles ENTER key requests. This normally opens submenus on JMenu + * items, or activates the menu item as if it's been clicked on it. + */ + private void doReturn() + { + KeyboardFocusManager fmgr = + KeyboardFocusManager.getCurrentKeyboardFocusManager(); + Component focusOwner = fmgr.getFocusOwner(); + if((focusOwner == null || (focusOwner instanceof JRootPane))) + { + MenuSelectionManager msm = MenuSelectionManager.defaultManager(); + MenuElement path[] = msm.getSelectedPath(); + MenuElement lastElement; + if(path.length > 0) + { + lastElement = path[path.length - 1]; + if(lastElement instanceof JMenu) + { + MenuElement newPath[] = new MenuElement[path.length + 1]; + System.arraycopy(path,0,newPath,0,path.length); + newPath[path.length] = ((JMenu) lastElement).getPopupMenu(); + msm.setSelectedPath(newPath); + } + else if(lastElement instanceof JMenuItem) + { + JMenuItem mi = (JMenuItem)lastElement; + if (mi.getUI() instanceof BasicMenuItemUI) + { + ((BasicMenuItemUI)mi.getUI()).doClick(msm); + } + else + { + msm.clearSelectedPath(); + mi.doClick(0); + } + } + } + } + } + } + + /** + * Installs keyboard actions when a popup is opened, and uninstalls the + * keyboard actions when closed. This listens on the default + * MenuSelectionManager. + */ + private class KeyboardHelper + implements ChangeListener + { + private MenuElement[] lastSelectedPath = new MenuElement[0]; + private Component lastFocused; + private JRootPane invokerRootPane; + + public void stateChanged(ChangeEvent event) + { + MenuSelectionManager msm = (MenuSelectionManager) event.getSource(); + MenuElement[] p = msm.getSelectedPath(); + JPopupMenu popup = getActivePopup(p); + if (popup == null || popup.isFocusable()) + { + if (lastSelectedPath.length != 0 && p.length != 0 ) + { + if (! invokerEquals(p[0], lastSelectedPath[0])) + { + uninstallKeyboardActionsImpl(); + lastSelectedPath = new MenuElement[0]; + } + } + + if (lastSelectedPath.length == 0 && p.length > 0) + { + JComponent invoker; + if (popup == null) + { + if (p.length == 2 && p[0] instanceof JMenuBar + && p[1] instanceof JMenu) + { + // A menu has been selected but not opened. + invoker = (JComponent)p[1]; + popup = ((JMenu)invoker).getPopupMenu(); + } + else + { + return; + } + } + else + { + Component c = popup.getInvoker(); + if(c instanceof JFrame) + { + invoker = ((JFrame) c).getRootPane(); + } + else if(c instanceof JApplet) + { + invoker = ((JApplet) c).getRootPane(); + } + else + { + while (!(c instanceof JComponent)) + { + if (c == null) + { + return; + } + c = c.getParent(); + } + invoker = (JComponent)c; + } + } + + // Remember current focus owner. + lastFocused = KeyboardFocusManager. + getCurrentKeyboardFocusManager().getFocusOwner(); + + // Install keybindings used for menu navigation. + invokerRootPane = SwingUtilities.getRootPane(invoker); + if (invokerRootPane != null) + { + invokerRootPane.requestFocus(true); + installKeyboardActionsImpl(); + } + } + else if (lastSelectedPath.length != 0 && p.length == 0) + { + // menu hidden -- return focus to where it had been before + // and uninstall menu keybindings + uninstallKeyboardActionsImpl(); + } + } + + // Remember the last path selected + lastSelectedPath = p; + } + + private JPopupMenu getActivePopup(MenuElement[] path) + { + JPopupMenu active = null; + for (int i = path.length - 1; i >= 0 && active == null; i--) + { + MenuElement elem = path[i]; + if (elem instanceof JPopupMenu) + { + active = (JPopupMenu) elem; + } + } + return active; + } + + private boolean invokerEquals(MenuElement el1, MenuElement el2) + { + Component invoker1 = el1.getComponent(); + Component invoker2 = el2.getComponent(); + if (invoker1 instanceof JPopupMenu) + invoker1 = ((JPopupMenu) invoker1).getInvoker(); + if (invoker2 instanceof JPopupMenu) + invoker2 = ((JPopupMenu) invoker2).getInvoker(); + return invoker1 == invoker2; + } + } + /* popupMenu for which this UI delegate is for*/ protected JPopupMenu popupMenu; @@ -75,6 +616,19 @@ public class BasicPopupMenuUI extends PopupMenuUI TopWindowListener topWindowListener; /** + * Counts how many popup menus are handled by this UI or a subclass. + * This is used to install a KeyboardHelper on the MenuSelectionManager + * for the first popup, and uninstall this same KeyboardHelper when the + * last popup is uninstalled. + */ + private static int numPopups; + + /** + * This is the KeyboardHelper that listens on the MenuSelectionManager. + */ + private static KeyboardHelper keyboardHelper; + + /** * Creates a new BasicPopupMenuUI object. */ public BasicPopupMenuUI() @@ -106,6 +660,16 @@ public class BasicPopupMenuUI extends PopupMenuUI public void installUI(JComponent c) { super.installUI(c); + + // Install KeyboardHelper when the first popup is initialized. + if (numPopups == 0) + { + keyboardHelper = new KeyboardHelper(); + MenuSelectionManager msm = MenuSelectionManager.defaultManager(); + msm.addChangeListener(keyboardHelper); + } + numPopups++; + popupMenu = (JPopupMenu) c; popupMenu.setLayout(new DefaultMenuLayout(popupMenu, BoxLayout.Y_AXIS)); popupMenu.setBorderPainted(true); @@ -113,6 +677,7 @@ public class BasicPopupMenuUI extends PopupMenuUI installDefaults(); installListeners(); + installKeyboardActions(); } /** @@ -139,9 +704,77 @@ public class BasicPopupMenuUI extends PopupMenuUI * This method installs the keyboard actions for this {@link JPopupMenu}. */ protected void installKeyboardActions() - throws NotImplementedException { - // FIXME: Need to implement + // We can't install the keyboard actions here, because then all + // popup menus would have their actions registered in the KeyboardManager. + // So we install it when the popup menu is opened, and uninstall it + // when it's closed. This is done in the KeyboardHelper class. + // Install InputMap. + } + + /** + * Called by the KeyboardHandler when a popup is made visible. + */ + void installKeyboardActionsImpl() + { + Object[] bindings; + if (popupMenu.getComponentOrientation().isLeftToRight()) + { + bindings = (Object[]) + SharedUIDefaults.get("PopupMenu.selectedWindowInputMapBindings"); + } + else + { + bindings = (Object[]) SharedUIDefaults.get + ("PopupMenu.selectedWindowInputMapBindings.RightToLeft"); + } + InputMap inputMap = LookAndFeel.makeComponentInputMap(popupMenu, bindings); + SwingUtilities.replaceUIInputMap(popupMenu, + JComponent.WHEN_IN_FOCUSED_WINDOW, + inputMap); + + // Install ActionMap. + SwingUtilities.replaceUIActionMap(popupMenu, getActionMap()); + } + + /** + * Creates and returns the shared action map for JTrees. + * + * @return the shared action map for JTrees + */ + private ActionMap getActionMap() + { + ActionMap am = (ActionMap) UIManager.get("PopupMenu.actionMap"); + if (am == null) + { + am = createDefaultActions(); + UIManager.getLookAndFeelDefaults().put("PopupMenu.actionMap", am); + } + return am; + } + + /** + * Creates the default actions when there are none specified by the L&F. + * + * @return the default actions + */ + private ActionMap createDefaultActions() + { + ActionMapUIResource am = new ActionMapUIResource(); + Action action = new NavigateAction("selectNext"); + am.put(action.getValue(Action.NAME), action); + action = new NavigateAction("selectPrevious"); + am.put(action.getValue(Action.NAME), action); + action = new NavigateAction("selectParent"); + am.put(action.getValue(Action.NAME), action); + action = new NavigateAction("selectChild"); + am.put(action.getValue(Action.NAME), action); + action = new NavigateAction("return"); + am.put(action.getValue(Action.NAME), action); + action = new NavigateAction("cancel"); + am.put(action.getValue(Action.NAME), action); + + return am; } /** @@ -155,7 +788,17 @@ public class BasicPopupMenuUI extends PopupMenuUI { uninstallListeners(); uninstallDefaults(); + uninstallKeyboardActions(); popupMenu = null; + + // Install KeyboardHelper when the first popup is initialized. + numPopups--; + if (numPopups == 0) + { + MenuSelectionManager msm = MenuSelectionManager.defaultManager(); + msm.removeChangeListener(keyboardHelper); + } + } /** @@ -182,9 +825,22 @@ public class BasicPopupMenuUI extends PopupMenuUI * Uninstalls any keyboard actions. */ protected void uninstallKeyboardActions() - throws NotImplementedException { - // FIXME: Need to implement + // We can't install the keyboard actions here, because then all + // popup menus would have their actions registered in the KeyboardManager. + // So we install it when the popup menu is opened, and uninstall it + // when it's closed. This is done in the KeyboardHelper class. + // Install InputMap. + } + + /** + * Called by the KeyboardHandler when a popup is made invisible. + */ + void uninstallKeyboardActionsImpl() + { + SwingUtilities.replaceUIInputMap(popupMenu, + JComponent.WHEN_IN_FOCUSED_WINDOW, null); + SwingUtilities.replaceUIActionMap(popupMenu, null); } /** @@ -278,9 +934,10 @@ public class BasicPopupMenuUI extends PopupMenuUI // Adds topWindowListener to top-level window to listener to // ComponentEvents fired by it. We need to cancel this popup menu // if topWindow to which this popup belongs was resized or moved. - Component invoker = popupMenu.getInvoker(); + Component invoker = popupMenu.getInvoker(); Component rootContainer = SwingUtilities.getRoot(invoker); - rootContainer.addComponentListener(topWindowListener); + if (rootContainer != null) + rootContainer.addComponentListener(topWindowListener); // if this popup menu is a free floating popup menu, // then by default its first element should be always selected when diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java index a7da21c4f6e..aed4d69d6d5 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java @@ -42,6 +42,7 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; +import java.awt.Insets; import java.awt.Rectangle; import javax.swing.AbstractButton; @@ -92,14 +93,6 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI protected void installDefaults(AbstractButton b) { super.installDefaults(b); - if (b.getIcon() == null) - b.setIcon(icon); - if (b.getSelectedIcon() == null) - b.setSelectedIcon(icon); - if (b.getDisabledIcon() == null) - b.setDisabledIcon(icon); - if (b.getDisabledSelectedIcon() == null) - b.setDisabledSelectedIcon(icon); } /** @@ -145,16 +138,17 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI g.setFont(f); ButtonModel m = b.getModel(); - Icon currentIcon = null; - if (m.isSelected() && m.isEnabled()) - currentIcon = b.getSelectedIcon(); - else if (! m.isSelected() && m.isEnabled()) - currentIcon = b.getIcon(); - else if (m.isSelected() && ! m.isEnabled()) - currentIcon = b.getDisabledSelectedIcon(); - else // (!m.isSelected() && ! m.isEnabled()) - currentIcon = b.getDisabledIcon(); + // FIXME: Do a filtering on any customized icon if the following property + // is set. + boolean enabled = b.isEnabled(); + + Icon currentIcon = b.getIcon(); + if (currentIcon == null) + { + currentIcon = getDefaultIcon(); + } + SwingUtilities.calculateInnerArea(b, vr); String text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f), b.getText(), currentIcon, @@ -162,15 +156,57 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI b.getVerticalTextPosition(), b.getHorizontalTextPosition(), vr, ir, tr, b.getIconTextGap() + defaultTextShiftOffset); - if (currentIcon != null) - { - currentIcon.paintIcon(c, g, ir.x, ir.y); - } + currentIcon.paintIcon(c, g, ir.x, ir.y); + if (text != null) paintText(g, b, tr, text); if (b.hasFocus() && b.isFocusPainted() && m.isEnabled()) paintFocus(g, tr, c.getSize()); } + + public Dimension getPreferredSize(JComponent c) + { + // This is basically the same code as in + // BasicGraphicsUtils.getPreferredButtonSize() but takes the default icon + // property into account. JRadioButton and subclasses always have an icon: + // the check box. If the user explicitly changes it with setIcon() that + // one will be used for layout calculations and painting instead. + // The other icon properties are ignored. + AbstractButton b = (AbstractButton) c; + + Rectangle contentRect; + Rectangle viewRect; + Rectangle iconRect = new Rectangle(); + Rectangle textRect = new Rectangle(); + Insets insets = b.getInsets(); + + Icon i = b.getIcon(); + if (i == null) + i = getDefaultIcon(); + + viewRect = new Rectangle(); + + SwingUtilities.layoutCompoundLabel( + b, // for the component orientation + b.getFontMetrics(b.getFont()), + b.getText(), + i, + b.getVerticalAlignment(), + b.getHorizontalAlignment(), + b.getVerticalTextPosition(), + b.getHorizontalTextPosition(), + viewRect, iconRect, textRect, + defaultTextIconGap + defaultTextShiftOffset); + + contentRect = textRect.union(iconRect); + + return new Dimension(insets.left + + contentRect.width + + insets.right + b.getHorizontalAlignment(), + insets.top + + contentRect.height + + insets.bottom); + } /** * Paints the focus indicator for JRadioButtons. diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java index b29026342e0..78e5168fc80 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java @@ -302,8 +302,10 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ public void mouseMoved(MouseEvent e) { - // Not interested in where the mouse - // is unless it is being dragged. + if (thumbRect.contains(e.getPoint())) + thumbRollover = true; + else + thumbRollover = false; } /** @@ -486,6 +488,9 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, /** The scrollbar this UI is acting for. */ protected JScrollBar scrollbar; + + /** True if the mouse is over the thumb. */ + boolean thumbRollover; /** * This method adds a component to the layout. @@ -1401,4 +1406,45 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, value = min; return value; } + + /** + * Returns true if the mouse is over the thumb. + * + * @return true if the mouse is over the thumb. + * + * @since 1.5 + */ + public boolean isThumbRollover() + { + return thumbRollover; + } + + /** + * Set thumbRollover to active. This indicates + * whether or not the mouse is over the thumb. + * + * @param active - true if the mouse is over the thumb. + * + * @since 1.5 + */ + protected void setThumbRollover(boolean active) + { + thumbRollover = active; + } + + /** + * Indicates whether the user can position the thumb with + * a mouse click (i.e. middle button). + * + * @return true if the user can position the thumb with a mouse + * click. + * + * @since 1.5 + */ + public boolean getSupportsAbsolutePositioning() + { + // The positioning feature has not been implemented. + // So, false is always returned. + return false; + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java index 2fb16f12e63..3811eebdfd6 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java @@ -371,6 +371,7 @@ public class BasicSliderUI extends SliderUI */ public void mouseDragged(MouseEvent e) { + dragging = true; if (slider.isEnabled()) { currentMouseX = e.getX(); @@ -450,6 +451,7 @@ public class BasicSliderUI extends SliderUI */ public void mouseReleased(MouseEvent e) { + dragging = false; if (slider.isEnabled()) { currentMouseX = e.getX(); @@ -593,6 +595,9 @@ public class BasicSliderUI extends SliderUI /** True if the slider has focus. */ private transient boolean hasFocus; + + /** True if the user is dragging the slider. */ + boolean dragging; /** * Creates a new Basic look and feel Slider UI. @@ -605,6 +610,18 @@ public class BasicSliderUI extends SliderUI } /** + * Returns true if the user is dragging the slider. + * + * @return true if the slider is being dragged. + * + * @since 1.5 + */ + protected boolean isDragging() + { + return dragging; + } + + /** * Gets the shadow color to be used for this slider. The shadow color is the * color used for drawing the top and left edges of the track. * diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java index 1b2552837c6..11f25167d21 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java @@ -1,5 +1,5 @@ /* BasicTabbedPaneUI.java -- - Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,8 +38,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.Color; import java.awt.Component; import java.awt.Container; @@ -51,6 +49,7 @@ import java.awt.Insets; import java.awt.LayoutManager; import java.awt.Point; import java.awt.Rectangle; +import java.awt.event.ActionEvent; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; @@ -60,7 +59,10 @@ import java.awt.event.MouseListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import javax.swing.AbstractAction; +import javax.swing.ActionMap; import javax.swing.Icon; +import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JTabbedPane; @@ -72,6 +74,7 @@ import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.PanelUI; import javax.swing.plaf.TabbedPaneUI; @@ -80,11 +83,127 @@ import javax.swing.text.View; /** * This is the Basic Look and Feel's UI delegate for JTabbedPane. + * + * @author Lillian Angel (langel@redhat.com) + * @author Kim Ho (kho@redhat.com) + * @author Roman Kennke (kennke@aicas.com) + * @author Robert Schuster (robertschuster@fsfe.org) */ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { + + static class NavigateAction extends AbstractAction + { + int direction; + + NavigateAction(String name, int dir) + { + super(name); + direction = dir; + } + + public void actionPerformed(ActionEvent event) + { + JTabbedPane tp = (JTabbedPane) event.getSource(); + BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI(); + + ui.navigateSelectedTab(direction); + } + + } + + static class NavigatePageDownAction extends AbstractAction + { + + public NavigatePageDownAction() + { + super("navigatePageDown"); + } + + public void actionPerformed(ActionEvent event) + { + JTabbedPane tp = (JTabbedPane) event.getSource(); + BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI(); + + int i = tp.getSelectedIndex(); + + if (i < 0) + i = 0; + + ui.selectNextTabInRun(i); + } + + } + + static class NavigatePageUpAction extends AbstractAction + { + + public NavigatePageUpAction() + { + super("navigatePageUp"); + } + + public void actionPerformed(ActionEvent event) + { + JTabbedPane tp = (JTabbedPane) event.getSource(); + BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI(); + + int i = tp.getSelectedIndex(); + + if (i < 0) + i = 0; + + ui.selectPreviousTabInRun(i); + + } + } + + static class RequestFocusAction extends AbstractAction + { + + public RequestFocusAction() + { + super("requestFocus"); + } + + public void actionPerformed(ActionEvent event) + { + ((JTabbedPane) event.getSource()).requestFocus(); + } + + } + + static class RequestFocusForVisibleComponentAction extends AbstractAction + { + + public RequestFocusForVisibleComponentAction() + { + super("requestFocusForVisibleComponent"); + } + + public void actionPerformed(ActionEvent event) + { + JTabbedPane tp = (JTabbedPane) event.getSource(); + + // FIXME: This should select a suitable component within + // the tab content. However I dont know whether we have + // to search for this component or wether the called is + // supposed to do that. + tp.getSelectedComponent().requestFocus(); + } + + } + /** - * A helper class that handles focus. + * A helper class that handles focus. + * <p>The purpose of this class is to implement a more flexible focus + * handling for the tabbed pane, which is used to determine whether the + * focus indicator should be painted or not. When in scrolling layout + * mode the area containing the tabs is a scrollpane, so simply testing + * whether the tabbed pane has the focus does not work.</p> + * <p>The <code>FocusHandler</code> is installed on the scrollpane and + * the tabbed pane and sets the variable <code>hasFocus</code> to + * <code>false</code> only when both components do not hold the focus.</p> * * @specnote Apparently this class was intended to be protected, * but was made public by a compiler bug and is now @@ -99,7 +218,11 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public void focusGained(FocusEvent e) { - // FIXME: Implement. + Object source = e.getSource(); + if (source == panel ) + tabPane.requestFocus(); + else if (source == tabPane) + tabPane.repaint(); } /** @@ -109,7 +232,10 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public void focusLost(FocusEvent e) { - // FIXME: Implement. + if (e.getOppositeComponent() == tabPane.getSelectedComponent()) + tabPane.requestFocus(); + else if (e.getSource() == tabPane) + tabPane.repaint(); } } @@ -124,6 +250,11 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public class MouseHandler extends MouseAdapter { + public void mouseReleased(MouseEvent e) + { + // Nothing to do here. + } + /** * This method is called when the mouse is pressed. The index cannot * change to a tab that is not enabled. @@ -132,14 +263,84 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public void mousePressed(MouseEvent e) { - if (tabPane.isEnabled()) + Object s = e.getSource(); + int placement = tabPane.getTabPlacement(); + + if (s == incrButton) { - int index = tabForCoordinate(tabPane, e.getX(), e.getY()); - if (index >= 0 && tabPane.isEnabledAt(index)) + if(!incrButton.isEnabled()) + return; + + currentScrollLocation++; + + switch (placement) { - tabPane.setSelectedIndex(index); + case JTabbedPane.TOP: + case JTabbedPane.BOTTOM: + currentScrollOffset = getTabAreaInsets(placement).left; + for (int i = 0; i < currentScrollLocation; i++) + currentScrollOffset += rects[i].width; + break; + default: + currentScrollOffset = getTabAreaInsets(placement).top; + for (int i = 0; i < currentScrollLocation; i++) + currentScrollOffset += rects[i].height; + break; } + + updateViewPosition(); + updateButtons(); + + tabPane.repaint(); + } + else if (s == decrButton) + { + if(!decrButton.isEnabled()) + return; + + // The scroll location may be zero but the offset + // greater than zero because of an adjustement to + // make a partially visible tab completely visible. + if (currentScrollLocation > 0) + currentScrollLocation--; + + // Set the offset back to 0 and recompute it. + currentScrollOffset = 0; + + switch (placement) + { + case JTabbedPane.TOP: + case JTabbedPane.BOTTOM: + // Take the tab area inset into account. + if (currentScrollLocation > 0) + currentScrollOffset = getTabAreaInsets(placement).left; + // Recompute scroll offset. + for (int i = 0; i < currentScrollLocation; i++) + currentScrollOffset += rects[i].width; + break; + default: + // Take the tab area inset into account. + if (currentScrollLocation > 0) + currentScrollOffset = getTabAreaInsets(placement).top; + + for (int i = 0; i < currentScrollLocation; i++) + currentScrollOffset += rects[i].height; + } + + updateViewPosition(); + updateButtons(); + + tabPane.repaint(); + } else if (tabPane.isEnabled()) + { + int index = tabForCoordinate(tabPane, e.getX(), e.getY()); + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT + && s == panel) + scrollTab(index, placement); + + tabPane.setSelectedIndex(index); } + } /** @@ -197,6 +398,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { if (e.getPropertyName().equals("tabLayoutPolicy")) { + currentScrollLocation = currentScrollOffset = 0; + layoutManager = createLayoutManager(); tabPane.setLayout(layoutManager); @@ -265,7 +468,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // Find out the minimum/preferred size to display the largest child // of the tabbed pane. - for (int i = 0; i < tabPane.getTabCount(); i++) + int count = tabPane.getTabCount(); + for (int i = 0; i < count; i++) { c = tabPane.getComponentAt(i); if (c == null) @@ -282,21 +486,19 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { - int min = calculateMaxTabWidth(tabPlacement); - width = Math.max(min, width); - int tabAreaHeight = preferredTabAreaHeight(tabPlacement, - width - tabAreaInsets.left - - tabAreaInsets.right); - height += tabAreaHeight; + width = Math.max(calculateMaxTabWidth(tabPlacement), width); + + height += preferredTabAreaHeight(tabPlacement, + width - tabAreaInsets.left + - tabAreaInsets.right); } else { - int min = calculateMaxTabHeight(tabPlacement); - height = Math.max(min, height); - int tabAreaWidth = preferredTabAreaWidth(tabPlacement, - height - tabAreaInsets.top - - tabAreaInsets.bottom); - width += tabAreaWidth; + height = Math.max(calculateMaxTabHeight(tabPlacement), height); + + width += preferredTabAreaWidth(tabPlacement, + height - tabAreaInsets.top + - tabAreaInsets.bottom); } Insets tabPaneInsets = tabPane.getInsets(); @@ -306,7 +508,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // if tab placement is LEFT OR RIGHT, they share width. // if tab placement is TOP OR BOTTOM, they share height - // PRE STEP: finds the default sizes for the labels as well as their locations. + // PRE STEP: finds the default sizes for the labels as well as their + // locations. // AND where they will be placed within the run system. // 1. calls normalizeTab Runs. // 2. calls rotate tab runs. @@ -345,7 +548,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants break; case RIGHT: maxTabWidth = calculateMaxTabWidth(tabPlacement); - x = size.width - (insets.right + tabAreaInsets.right) - maxTabWidth; + x = size.width - (insets.right + tabAreaInsets.right) + - maxTabWidth - 1; y = insets.top + tabAreaInsets.top; breakAt = size.height - (insets.bottom + tabAreaInsets.bottom); break; @@ -353,7 +557,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants maxTabHeight = calculateMaxTabHeight(tabPlacement); x = insets.left + tabAreaInsets.left; y = size.height - (insets.bottom + tabAreaInsets.bottom) - - maxTabHeight; + - maxTabHeight - 1; breakAt = size.width - (insets.right + tabAreaInsets.right); break; case TOP: @@ -372,6 +576,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants runCount = 0; selectedRun = -1; int selectedIndex = tabPane.getSelectedIndex(); + if (selectedIndex < 0) + selectedIndex = 0; Rectangle rect; @@ -409,7 +615,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants rect.height = maxTabHeight; if (i == selectedIndex) selectedRun = runCount - 1; - } } else @@ -454,9 +659,9 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int start; if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) - start = y; - else start = x; + else + start = y; normalizeTabRuns(tabPlacement, tabCount, start, breakAt); selectedRun = getRunForTab(tabCount, selectedIndex); if (shouldRotateTabRuns(tabPlacement)) @@ -464,7 +669,11 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants rotateTabRuns(tabPlacement, selectedRun); } } - + + // Suppress padding if we have only one tab run. + if (runCount == 1) + return; + // Pad the runs. int tabRunOverlay = getTabRunOverlay(tabPlacement); for (int i = runCount - 1; i >= 0; --i) @@ -604,16 +813,18 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public Dimension minimumLayoutSize(Container parent) { - return calculateSize(false); + return calculateSize(true); } - // If there is more free space in an adjacent run AND the tab in the run can fit in the - // adjacent run, move it. This method is not perfect, it is merely an approximation. + // If there is more free space in an adjacent run AND the tab + // in the run can fit in the adjacent run, move it. This method + // is not perfect, it is merely an approximation. // If you play around with Sun's JTabbedPane, you'll see that // it does do some pretty strange things with regards to not moving tabs // that should be moved. // start = the x position where the tabs will begin - // max = the maximum position of where the tabs can go to (tabAreaInsets.left + the width of the tab area) + // max = the maximum position of where the tabs can go to + // (tabAreaInsets.left + the width of the tab area) /** * This method tries to "even out" the number of tabs in each run based on @@ -631,18 +842,20 @@ 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. + // 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))]; + 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) { @@ -654,11 +867,12 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants 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; + + // Fixes the bounds of all tabs in the current + // run. + int first = tabRuns[i]; + int last = lastTabInRun(tabCount, i); + int currX = start; for (int j = first; j <= last; j++) { rects[j].x = currX; @@ -671,7 +885,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants for (int i = 1; i < runCount; i++) { Rectangle currRun = rects[lastTabInRun(tabCount, i)]; - Rectangle nextRun = rects[lastTabInRun(tabCount, getNextTabRun(i))]; + Rectangle nextRun = rects[lastTabInRun(tabCount, + getNextTabRun(i))]; int spaceInCurr = currRun.y + currRun.height; int spaceInNext = nextRun.y + nextRun.height; @@ -690,9 +905,10 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants - (spaceInNext + currRun.height); } - int first = lastTabInRun(tabCount, i) + 1; - int last = lastTabInRun(tabCount, getNextTabRun(i)); - int currY = tabAreaInsets.top; + // Fixes the bounds of tabs in the current run. + int first = tabRuns[i]; + int last = lastTabInRun(tabCount, i); + int currY = start; for (int j = first; j <= last; j++) { rects[j].y = currY; @@ -718,7 +934,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants rects[selectedIndex].height += insets.top + insets.bottom; } - // If the tabs on the run don't fill the width of the window, make it fit now. + // If the tabs on the run don't fill the width of the window, make it + // fit now. // start = starting index of the run // end = last index of the run // max = tabAreaInsets.left + width (or equivalent) @@ -742,7 +959,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants 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; @@ -750,11 +967,13 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { 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; } @@ -819,7 +1038,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // The reason why we can't use runCount: // This method is only called to calculate the size request - // for the tabbedPane. However, this size request is dependent on + // for the tabbedPane. However, this size request is dependent on // our desired width. We need to find out what the height would // be IF we got our desired width. for (int i = 0; i < tabPane.getTabCount(); i++) @@ -882,7 +1101,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants runs++; int maxTabWidth = calculateMaxTabWidth(tabPlacement); - int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, maxTabWidth); + int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, + maxTabWidth); return tabAreaWidth; } @@ -896,11 +1116,11 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected void rotateTabRuns(int tabPlacement, int selectedRun) { - if (runCount == 1 || selectedRun == 1 || selectedRun == -1) + if (runCount == 1 || selectedRun == 0 || selectedRun == -1) return; int[] newTabRuns = new int[tabRuns.length]; int currentRun = selectedRun; - int i = 1; + int i = 0; do { newTabRuns[i] = tabRuns[currentRun]; @@ -908,8 +1128,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants i++; } while (i < runCount); - if (runCount > 1) - newTabRuns[0] = tabRuns[currentRun]; tabRuns = newTabRuns; BasicTabbedPaneUI.this.selectedRun = 1; @@ -942,7 +1160,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public Dimension preferredLayoutSize(Container parent) { - return super.calculateSize(true); + return super.calculateSize(false); } /** @@ -1016,29 +1234,27 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants SwingUtilities.calculateInnerArea(tabPane, calcRect); Insets tabAreaInsets = getTabAreaInsets(tabPlacement); Insets insets = tabPane.getInsets(); - int runs = 1; - int start = 0; - int top = 0; if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { int maxHeight = calculateMaxTabHeight(tabPlacement); calcRect.width -= tabAreaInsets.left + tabAreaInsets.right; - start = tabAreaInsets.left + insets.left; int width = 0; - int runWidth = start; - top = insets.top + tabAreaInsets.top; + int runWidth = tabAreaInsets.left + insets.left; + int 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); + + // The proper instances should exists because + // assureRectsCreated() was being run already. + rects[i].setBounds(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; + tabAreaRect.height = maxTabHeight + tabAreaInsets.top + + tabAreaInsets.bottom; contentRect.width = tabAreaRect.width; contentRect.height = tabPane.getHeight() - insets.top - insets.bottom - tabAreaRect.height; @@ -1061,23 +1277,25 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom; int height = 0; - start = tabAreaInsets.top + insets.top; - int runHeight = start; + int runHeight = tabAreaInsets.top + insets.top;; int fontHeight = fm.getHeight(); - top = insets.left + tabAreaInsets.left; + int left = insets.left + tabAreaInsets.left; for (int i = 0; i < tabCount; i++) { height = calculateTabHeight(tabPlacement, i, fontHeight); - rects[i] = new Rectangle(top, runHeight, maxWidth, height); + + // The proper instances should exists because + // assureRectsCreated() was being run already. + rects[i].setBounds(left, runHeight, maxWidth, height); runHeight += height; } - tabAreaRect.width = runs * maxTabWidth - (runs - 1) * tabRunOverlay - + tabAreaInsets.left + tabAreaInsets.right; + tabAreaRect.width = maxTabWidth + tabAreaInsets.left + + tabAreaInsets.right; tabAreaRect.height = tabPane.getHeight() - insets.top - - insets.bottom; + - insets.bottom; tabAreaRect.y = insets.top; contentRect.width = tabPane.getWidth() - insets.left - insets.right - - tabAreaRect.width; + - tabAreaRect.width; contentRect.height = tabAreaRect.height; contentRect.y = insets.top; if (tabPlacement == SwingConstants.LEFT) @@ -1091,11 +1309,9 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants tabAreaRect.x = contentRect.x + contentRect.width; } } - runCount = runs; - if (runCount > tabRuns.length) - expandTabRunsArray(); - - padSelectedTab(tabPlacement, tabPane.getSelectedIndex()); + + // Unlike the behavior in the WRAP_TAB_LAYOUT the selected + // tab is not padded specially. } /** @@ -1113,8 +1329,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabCount == 0) return; int tabPlacement = tabPane.getTabPlacement(); - incrButton.setVisible(false); - decrButton.setVisible(false); + if (tabPlacement == SwingConstants.TOP || tabPlacement == SwingConstants.BOTTOM) { @@ -1124,18 +1339,49 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants 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); - + if (tabPlacement == SwingConstants.BOTTOM) + { + // Align scroll buttons with the bottom border of the tabbed + // pane's content area. + decrButton.setBounds(tabAreaRect.x + tabAreaRect.width + - incrDims.width - decrDims.width, + tabAreaRect.y, decrDims.width, + decrDims.height); + incrButton.setBounds(tabAreaRect.x + tabAreaRect.width + - incrDims.width, tabAreaRect.y, + incrDims.width, incrDims.height); + } + else + { + // Align scroll buttons with the top border of the tabbed + // pane's content area. + decrButton.setBounds(tabAreaRect.x + tabAreaRect.width + - incrDims.width - decrDims.width, + tabAreaRect.y + tabAreaRect.height + - decrDims.height, decrDims.width, + decrDims.height); + incrButton.setBounds(tabAreaRect.x + tabAreaRect.width + - incrDims.width, + tabAreaRect.y + tabAreaRect.height + - incrDims.height, + incrDims.width, incrDims.height); + } + tabAreaRect.width -= decrDims.width + incrDims.width; + + updateButtons(); + incrButton.setVisible(true); decrButton.setVisible(true); } + else + { + incrButton.setVisible(false); + decrButton.setVisible(false); + + currentScrollOffset = 0; + currentScrollLocation = 0; + } } if (tabPlacement == SwingConstants.LEFT @@ -1147,34 +1393,54 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants 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); + if (tabPlacement == SwingConstants.RIGHT) + { + // Align scroll buttons with the right border of the tabbed + // pane's content area. + decrButton.setBounds(tabAreaRect.x, + tabAreaRect.y + tabAreaRect.height + - incrDims.height - decrDims.height, + decrDims.width, decrDims.height); + incrButton.setBounds(tabAreaRect.x, + tabAreaRect.y + tabAreaRect.height + - incrDims.height, incrDims.width, + incrDims.height); + } + else + { + // Align scroll buttons with the left border of the tabbed + // pane's content area. + decrButton.setBounds(tabAreaRect.x + tabAreaRect.width + - decrDims.width, + tabAreaRect.y + tabAreaRect.height + - incrDims.height - decrDims.height, + decrDims.width, decrDims.height); + incrButton.setBounds(tabAreaRect.x + tabAreaRect.width + - incrDims.width, + tabAreaRect.y + tabAreaRect.height + - incrDims.height, incrDims.width, + incrDims.height); + } tabAreaRect.height -= decrDims.height + incrDims.height; + incrButton.setVisible(true); decrButton.setVisible(true); } + else + { + incrButton.setVisible(false); + decrButton.setVisible(false); + + currentScrollOffset = 0; + currentScrollLocation = 0; + } } 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); - } - viewport.setViewPosition(p); + + updateViewPosition(); + viewport.repaint(); } } @@ -1198,7 +1464,9 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { selectedRun = getRunForTab(tabPane.getTabCount(), tabPane.getSelectedIndex()); - tabPane.revalidate(); + + if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT) + tabPane.revalidate(); tabPane.repaint(); } } @@ -1224,7 +1492,17 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public void paint(Graphics g, JComponent c) { - paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex()); + int placement = tabPane.getTabPlacement(); + g.setColor(highlight); + if (placement == SwingUtilities.TOP + || placement == SwingUtilities.BOTTOM) + g.fillRect(currentScrollOffset, 0, + tabAreaRect.width, tabAreaRect.height); + else + g.fillRect(0, currentScrollOffset, + tabAreaRect.width, tabAreaRect.height); + + paintTabArea(g, placement, tabPane.getSelectedIndex()); } } @@ -1285,6 +1563,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants /** The starting visible tab in the run in SCROLL_TAB_MODE. * This is package-private to avoid an accessor method. */ transient int currentScrollLocation; + + transient int currentScrollOffset; /** A reusable rectangle. */ protected Rectangle calcRect; @@ -1340,16 +1620,11 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants /** The gap between text and label */ protected int textIconGap; - // Keeps track of tab runs. - // The organization of this array is as follows (lots of experimentation to - // figure this out) - // index 0 = furthest away from the component area (aka outer run) - // index 1 = closest to component area (aka selected run) - // index > 1 = listed in order leading from selected run to outer run. - // each int in the array is the tab index + 1 (counting starts at 1) - // for the last tab in the run. (same as the rects array) - - /** This array keeps track of which tabs are in which run. See above. */ + /** This array keeps track of which tabs are in which run. + * <p>The value at index i denotes the index of the first tab in run i.</p> + * <p>If the value for any index (i > 0) is 0 then (i - 1) is the last + * run.</p> + */ protected int[] tabRuns; /** @@ -1428,7 +1703,13 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants * The currently visible component. */ private Component visibleComponent; - + + private Color selectedColor; + + private Rectangle tempTextRect = new Rectangle(); + + private Rectangle tempIconRect = new Rectangle(); + /** * Creates a new BasicTabbedPaneUI object. */ @@ -1517,8 +1798,115 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants Point p = new Point(w, h); return p; } + + /** TabbedPanes in scrolling mode should use this method to + * scroll properly to the tab given by the index argument. + * + * @param index The tab to scroll to. + * @param placement The tab's placement. + */ + final void scrollTab(int index, int placement) + { + int diff; + if (index >= 0 && tabPane.isEnabledAt(index)) + { + // If the user clicked on the last tab and that one was + // only partially visible shift the scroll offset to make + // it completely visible. + switch (placement) + { + case JTabbedPane.TOP: + case JTabbedPane.BOTTOM: + if ((diff = rects[index].x + + rects[index].width + - decrButton.getX() - currentScrollOffset) > 0) + currentScrollOffset += diff; + else if ((diff = rects[index].x - currentScrollOffset) < 0) + { + if (index == 0) + currentScrollOffset = 0; + else + currentScrollOffset += diff; + } + + currentScrollLocation = tabForCoordinate(tabPane, + currentScrollOffset, + rects[index].y); + break; + default: + if ((diff = rects[index].y + rects[index].height + - decrButton.getY() - currentScrollOffset) > 0) + currentScrollOffset += diff; + else if ((diff = rects[index].y - currentScrollOffset) < 0) + { + if (index == 0) + currentScrollOffset = 0; + else + currentScrollOffset += diff; + } + + currentScrollLocation = tabForCoordinate(tabPane, + rects[index].x, + currentScrollOffset); + } + + updateViewPosition(); + updateButtons(); + } + } + + /** Sets the enabled state of the increase and decrease button + * according to the current scrolling offset and tab pane width + * (or height in TOP/BOTTOM placement). + */ + final void updateButtons() + { + int tc = tabPane.getTabCount(); + + // The increase button should be enabled as long as the + // right/bottom border of the last tab is under the left/top + // border of the decrease button. + switch (tabPane.getTabPlacement()) + { + case JTabbedPane.BOTTOM: + case JTabbedPane.TOP: + incrButton.setEnabled(currentScrollLocation + 1 < tc + && rects[tc-1].x + rects[tc-1].width + - currentScrollOffset > decrButton.getX()); + break; + default: + incrButton.setEnabled(currentScrollLocation + 1 < tc + && rects[tc-1].y + rects[tc-1].height + - currentScrollOffset > decrButton.getY()); + } + + // The decrease button is enabled when the tab pane is scrolled in any way. + decrButton.setEnabled(currentScrollOffset > 0); + + } /** + * Updates the position of the scrolling viewport's view + * according to the current scroll offset. + */ + final void updateViewPosition() + { + Point p = viewport.getViewPosition(); + + switch (tabPane.getTabPlacement()) + { + case JTabbedPane.LEFT: + case JTabbedPane.RIGHT: + p.y = currentScrollOffset; + break; + default: + p.x = currentScrollOffset; + } + + viewport.setViewPosition(p); + } + + /** * This method creates a new BasicTabbedPaneUI. * * @param c The JComponent to create a UI for. @@ -1583,22 +1971,30 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants return new TabbedPaneLayout(); else { + runCount = 1; + tabRuns[0] = 0; + incrButton = createIncreaseButton(); + incrButton.addMouseListener(mouseListener); + decrButton = createDecreaseButton(); - viewport = new ScrollingViewport(); - viewport.setLayout(null); + decrButton.addMouseListener(mouseListener); + decrButton.setEnabled(false); + panel = new ScrollingPanel(); + panel.setSize(Integer.MAX_VALUE, Integer.MAX_VALUE); + panel.addMouseListener(mouseListener); + panel.addFocusListener(focusListener); + + viewport = new ScrollingViewport(); + viewport.setBackground(Color.LIGHT_GRAY); viewport.setView(panel); + viewport.setLayout(null); + 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(); } } @@ -1616,7 +2012,14 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected void uninstallComponents() { - // Nothing to be done. + if (incrButton != null) + tabPane.remove(incrButton); + + if (decrButton != null) + tabPane.remove(decrButton); + + if (viewport != null) + tabPane.remove(viewport); } /** @@ -1629,8 +2032,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants "TabbedPane.font"); tabPane.setOpaque(false); - highlight = UIManager.getColor("TabbedPane.highlight"); - lightHighlight = UIManager.getColor("TabbedPane.lightHighlight"); + lightHighlight = UIManager.getColor("TabbedPane.highlight"); + highlight = UIManager.getColor("TabbedPane.light"); shadow = UIManager.getColor("TabbedPane.shadow"); darkShadow = UIManager.getColor("TabbedPane.darkShadow"); @@ -1641,10 +2044,18 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay"); tabInsets = UIManager.getInsets("TabbedPane.tabInsets"); - selectedTabPadInsets = UIManager.getInsets("TabbedPane.tabbedPaneTabPadInsets"); + selectedTabPadInsets + = UIManager.getInsets("TabbedPane.selectedTabPadInsets"); tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets"); - contentBorderInsets = UIManager.getInsets("TabbedPane.tabbedPaneContentBorderInsets"); + contentBorderInsets + = UIManager.getInsets("TabbedPane.contentBorderInsets"); tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque"); + + // Although 'TabbedPane.contentAreaColor' is not defined in the defaults + // of BasicLookAndFeel it is used by this class. + selectedColor = UIManager.getColor("TabbedPane.contentAreaColor"); + if (selectedColor == null) + selectedColor = UIManager.getColor("control"); calcRect = new Rectangle(); tabRuns = new int[10]; @@ -1661,6 +2072,9 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants tabAreaRect = null; contentRect = null; tabRuns = null; + + tempIconRect = null; + tempTextRect = null; contentBorderInsets = null; tabAreaInsets = null; @@ -1672,11 +2086,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants shadow = null; lightHighlight = null; highlight = null; - - // Install UI colors and fonts. - LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background", - "TabbedPane.foreground", - "TabbedPane.font"); + + selectedColor = null; } /** @@ -1704,6 +2115,18 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants tabPane.removePropertyChangeListener(propertyChangeListener); tabPane.removeChangeListener(tabChangeListener); tabPane.removeMouseListener(mouseListener); + + if (incrButton != null) + incrButton.removeMouseListener(mouseListener); + + if (decrButton != null) + decrButton.removeMouseListener(mouseListener); + + if (panel != null) + { + panel.removeMouseListener(mouseListener); + panel.removeFocusListener(focusListener); + } focusListener = null; propertyChangeListener = null; @@ -1755,18 +2178,31 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants * This method installs keyboard actions for the JTabbedPane. */ protected void installKeyboardActions() - throws NotImplementedException { - // FIXME: Implement. + InputMap keyMap = (InputMap) UIManager.get("TabbedPane.focusInputMap"); + SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, keyMap); + + keyMap = (InputMap) UIManager.get("TabbedPane.ancestorInputMap"); + SwingUtilities + .replaceUIInputMap(tabPane, + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, + keyMap); + + ActionMap map = getActionMap(); + SwingUtilities.replaceUIActionMap(tabPane, map); } /** * This method uninstalls keyboard actions for the JTabbedPane. */ protected void uninstallKeyboardActions() - throws NotImplementedException { - // FIXME: Implement. + SwingUtilities.replaceUIActionMap(tabPane, null); + SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, null); + SwingUtilities + .replaceUIInputMap(tabPane, + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, + null); } /** @@ -1806,9 +2242,25 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if (tabPane.getTabCount() == 0) return; + + int index = tabPane.getSelectedIndex(); + if (index < 0) + index = 0; + + int tabPlacement = tabPane.getTabPlacement(); + + // Paint the tab area only in WRAP_TAB_LAYOUT Mode from this method + // because it is done through the ScrollingViewport.paint() method + // for the SCROLL_TAB_LAYOUT mode. if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT) - paintTabArea(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex()); - paintContentBorder(g, tabPane.getTabPlacement(), tabPane.getSelectedIndex()); + { + g.setColor(highlight); + g.fillRect(tabAreaRect.x, tabAreaRect.y, + tabAreaRect.width, tabAreaRect.height); + paintTabArea(g, tabPlacement, index); + } + + paintContentBorder(g, tabPlacement, index); } /** @@ -1821,14 +2273,12 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) { - Rectangle ir = new Rectangle(); - Rectangle tr = new Rectangle(); - - boolean isScroll = tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT; - // Please note: the ordering of the painting is important. // we WANT to paint the outermost run first and then work our way in. + + // The following drawing code works for both tab layouts. int tabCount = tabPane.getTabCount(); + for (int i = runCount - 1; i >= 0; --i) { int start = tabRuns[i]; @@ -1842,14 +2292,16 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { if (j != selectedIndex) { - paintTab(g, tabPlacement, rects, j, ir, tr); + paintTab(g, tabPlacement, rects, j, + tempIconRect, tempTextRect); } } } - + // Paint selected tab in front of every other tab. if (selectedIndex >= 0) - paintTab(g, tabPlacement, rects, selectedIndex, ir, tr); + paintTab(g, tabPlacement, rects, selectedIndex, + tempIconRect, tempTextRect); } /** @@ -1889,8 +2341,10 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // Paint the text. paintText(g, tabPlacement, tabPane.getFont(), fm, tabIndex, title, textRect, isSelected); + // Paint icon if necessary. paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected); + // Paint focus indicator. paintFocusIndicator(g, tabPlacement, rects, tabIndex, iconRect, textRect, isSelected); @@ -2030,8 +2484,17 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) { - // No reason to shift. - return 0; + switch (tabPlacement) + { + default: + case SwingUtilities.TOP: + case SwingUtilities.BOTTOM: + return 1; + case SwingUtilities.LEFT: + return (isSelected) ? -1 : 1; + case SwingUtilities.RIGHT: + return (isSelected) ? 1 : -1; + } } /** @@ -2047,8 +2510,17 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) { - // No reason to shift. - return 0; + switch (tabPlacement) + { + default: + case SwingUtilities.TOP: + return (isSelected) ? -1 : 1; + case SwingUtilities.BOTTOM: + return (isSelected) ? 1 : -1; + case SwingUtilities.LEFT: + case SwingUtilities.RIGHT: + return 0; + } } /** @@ -2078,32 +2550,33 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants g.setColor(focus); switch (tabPlacement) - { - case LEFT: - x = rect.x + 3; - y = rect.y + 3; - w = rect.width - 5; - h = rect.height - 6; - break; - case RIGHT: - x = rect.x + 2; - y = rect.y + 3; - w = rect.width - 6; - h = rect.height - 5; - break; - case BOTTOM: - x = rect.x + 3; - y = rect.y + 2; - w = rect.width - 6; - h = rect.height - 5; - break; - case TOP: - default: - x = rect.x + 3; - y = rect.y + 3; - w = rect.width - 6; - h = rect.height - 5; - } + { + case LEFT: + x = rect.x + 3; + y = rect.y + 3; + w = rect.width - 5; + h = rect.height - 6; + break; + case RIGHT: + x = rect.x + 2; + y = rect.y + 3; + w = rect.width - 6; + h = rect.height - 5; + break; + case BOTTOM: + x = rect.x + 3; + y = rect.y + 2; + w = rect.width - 6; + h = rect.height - 5; + break; + case TOP: + default: + x = rect.x + 3; + y = rect.y + 3; + w = rect.width - 6; + h = rect.height - 5; + } + BasicGraphicsUtils.drawDashedRect(g, x, y, w, h); } } @@ -2125,34 +2598,109 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { Color saved = g.getColor(); - if (! isSelected || tabPlacement != SwingConstants.TOP) - { + switch (tabPlacement) + { + case 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); - } + // Inner right line. + g.drawLine(x + w - 2, y + 2, x + w - 2, y + h); - if (! isSelected || tabPlacement != SwingConstants.LEFT) - { g.setColor(darkShadow); - g.drawLine(x + w, y, x + w, y + h); + // Outer right line. + g.drawLine(x + w - 1, y + 2, x + w - 1, y + h); + + // Upper right corner. + g.drawLine(x + w - 2, y + 1, x + w - 1, y + 2); + + g.setColor(lightHighlight); + + // Left line. + g.drawLine(x, y + 3, x, y + h); + + // Upper line. + g.drawLine(x + 3, y, x + w - 3, y); + + // Upper left corner. + g.drawLine(x, y + 2, x + 2, y); + + break; + case SwingConstants.LEFT: + g.setColor(lightHighlight); + // Top line. + g.drawLine(x + 3, y, x + w - 1, y); + + // Top left border. + g.drawLine(x + 2, y, x, y + 2); + + // Left line. + g.drawLine(x, y + 3, x, y + h - 4); + + // Bottom left corner. + g.drawLine(x, y + h - 3, x + 1, y + h - 2); + + g.setColor(darkShadow); + // Outer bottom line. + g.drawLine(x + 2, y + h - 1, x + w - 1, y + h - 1); + g.setColor(shadow); - g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); - } + // Inner bottom line. + g.drawLine(x + 2, y + h - 2, x + w - 1, y + h - 2); + + break; + case SwingConstants.BOTTOM: + g.setColor(shadow); + // Inner right line. + g.drawLine(x + w - 2, y, x + w - 2, y + h - 2); - if (! isSelected || tabPlacement != SwingConstants.RIGHT) - { - g.setColor(lightHighlight); - g.drawLine(x, y, x, y + h); - } + // Inner bottom line. + g.drawLine(x + 2, y + h - 1, x + w - 3, y + h - 1); - if (! isSelected || tabPlacement != SwingConstants.BOTTOM) - { + g.setColor(darkShadow); + // Outer right line. + g.drawLine(x + w - 1, y, x + w - 1, y + h - 3); + + // Bottom right corner. + g.drawLine(x + w - 1, y + h - 2, x + w - 3, y + h); + + // Bottom line. + g.drawLine(x + 2, y + h, x + w - 4, y + h); + g.setColor(lightHighlight); - g.drawLine(x, y, x + w, y); - } - + // Left line. + g.drawLine(x, y, x, y + h - 3); + + // Bottom left corner. + g.drawLine(x, y + h - 2, x + 1, y + h - 1); + break; + case SwingConstants.RIGHT: + g.setColor(lightHighlight); + // Top line. + g.drawLine(x, y, x + w - 3, y); + + g.setColor(darkShadow); + // Top right corner. + g.drawLine(x + w - 2, y + 1, x + w - 1, y + 2); + + // Outer right line. + g.drawLine(x + w - 1, y + 3, x + w - 1, y + h - 3); + + // Bottom right corner. + g.drawLine(x + w - 2, y + h - 2, x + w - 3, y + h - 1); + + // Bottom line. + g.drawLine(x, y + h - 1, x + w - 4, y + h - 1); + + g.setColor(shadow); + + // Inner right line. + g.drawLine(x + w - 2, y + 2, x + w - 2, y + h - 3); + + // Inner bottom line. + g.drawLine(x, y + h - 2, x + w - 3, y + h - 2); + + break; + } + g.setColor(saved); } @@ -2173,17 +2721,32 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants boolean isSelected) { Color saved = g.getColor(); + if (isSelected) - g.setColor(Color.LIGHT_GRAY); + g.setColor(selectedColor); else { Color bg = tabPane.getBackgroundAt(tabIndex); if (bg == null) - bg = Color.GRAY; + bg = Color.LIGHT_GRAY; g.setColor(bg); } - g.fillRect(x, y, w, h); + switch (tabPlacement) + { + case SwingConstants.TOP: + g.fillRect(x + 1, y + 1, w - 1, h - 1); + break; + case SwingConstants.BOTTOM: + g.fillRect(x, y, w - 1, h - 1); + break; + case SwingConstants.LEFT: + g.fillRect(x + 1, y + 1, w - 1, h - 2); + break; + case SwingConstants.RIGHT: + g.fillRect(x, y + 1, w - 1, h - 2); + break; + } g.setColor(saved); } @@ -2260,25 +2823,27 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants Color saved = g.getColor(); g.setColor(lightHighlight); - int startgap = rects[selectedIndex].x; - int endgap = rects[selectedIndex].x + rects[selectedIndex].width; - - int diff = 0; + int startgap = rects[selectedIndex].x - currentScrollOffset; + int endgap = rects[selectedIndex].x + rects[selectedIndex].width + - currentScrollOffset; - if (tabPlacement == SwingConstants.TOP) + // Paint the highlight line with a gap if the tabs are at the top + // and the selected tab is inside the visible area. + if (tabPlacement == SwingConstants.TOP && startgap >= 0) { - 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); + g.drawLine(x, y, startgap, y); + g.drawLine(endgap, y, x + w - 1, y); + + g.setColor(selectedColor); + g.drawLine(startgap, y, endgap - 1, y); } else g.drawLine(x, y, x + w, y); - + + g.setColor(selectedColor); + g.drawLine(x, y + 1, x + w - 1, y + 1); + g.drawLine(x, y + 2, x + w - 1, y + 2); + g.setColor(saved); } @@ -2300,24 +2865,25 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants Color saved = g.getColor(); g.setColor(lightHighlight); - int startgap = rects[selectedIndex].y; - int endgap = rects[selectedIndex].y + rects[selectedIndex].height; + int startgap = rects[selectedIndex].y - currentScrollOffset; + int endgap = rects[selectedIndex].y + rects[selectedIndex].height + - currentScrollOffset; int diff = 0; - if (tabPlacement == SwingConstants.LEFT) + if (tabPlacement == SwingConstants.LEFT && startgap >= 0) { - 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); + g.drawLine(x, y, x, startgap); + g.drawLine(x, endgap, x, y + h - 1); + + g.setColor(selectedColor); + g.drawLine(x, startgap, x, endgap - 1); } else - g.drawLine(x, y, x, y + h); + g.drawLine(x, y, x, y + h - 1); + + g.setColor(selectedColor); + g.drawLine(x + 1, y + 1, x + 1, y + h - 4); g.setColor(saved); } @@ -2339,34 +2905,34 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { Color saved = g.getColor(); - int startgap = rects[selectedIndex].x; - int endgap = rects[selectedIndex].x + rects[selectedIndex].width; - - int diff = 0; + int startgap = rects[selectedIndex].x - currentScrollOffset; + int endgap = rects[selectedIndex].x + rects[selectedIndex].width + - currentScrollOffset; - if (tabPlacement == SwingConstants.BOTTOM) + if (tabPlacement == SwingConstants.BOTTOM && startgap >= 0) { - 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.drawLine(x + 1, y + h - 2, startgap, y + h - 2); + g.drawLine(endgap, y + h - 2, x + w - 2, y + h - 2); g.setColor(darkShadow); - g.drawLine(x, y + h, startgap - diff, y + h); - g.drawLine(endgap - diff, y + h, x + w, y + h); + g.drawLine(x, y + h - 1, startgap , y + h - 1); + g.drawLine(endgap, y + h - 1, x + w - 1, y + h - 1); + + g.setColor(selectedColor); + g.drawLine(startgap, y + h - 1, endgap - 1, y + h - 1); + g.drawLine(startgap, y + h - 2, endgap - 1, y + h - 2); } else { g.setColor(shadow); - g.drawLine(x + 1, y + h - 1, x + w - 1, y + h - 1); + g.drawLine(x + 1, y + h - 2, x + w - 1, y + h - 2); g.setColor(darkShadow); - g.drawLine(x, y + h, x + w, y + h); + g.drawLine(x, y + h - 1, x + w - 1, y + h - 1); } + + g.setColor(selectedColor); + g.drawLine(x + 1, y + h - 3, x + w - 2, y + h - 3); g.setColor(saved); } @@ -2387,34 +2953,36 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int w, int h) { Color saved = g.getColor(); - int startgap = rects[selectedIndex].y; - int endgap = rects[selectedIndex].y + rects[selectedIndex].height; + int startgap = rects[selectedIndex].y - currentScrollOffset; + int endgap = rects[selectedIndex].y + rects[selectedIndex].height + - currentScrollOffset; int diff = 0; - if (tabPlacement == SwingConstants.RIGHT) + if (tabPlacement == SwingConstants.RIGHT && startgap >= 0) { - 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.drawLine(x + w - 2, y + 1, x + w - 2, startgap); + g.drawLine(x + w - 2, endgap, x + w - 2, y + h - 2); g.setColor(darkShadow); - g.drawLine(x + w, y, x + w, startgap - diff); - g.drawLine(x + w, endgap - diff, x + w, y + h); + g.drawLine(x + w - 1, y, x + w - 1, startgap); + g.drawLine(x + w - 1, endgap, x + w - 1, y + h - 2); + + g.setColor(selectedColor); + g.drawLine(x + w - 2, startgap, x + w - 2, endgap - 1); + g.drawLine(x + w - 1, startgap, x + w - 1, endgap - 1); } else { g.setColor(shadow); - g.drawLine(x + w - 1, y + 1, x + w - 1, y + h - 1); + g.drawLine(x + w - 2, y + 1, x + w - 2, y + h - 2); g.setColor(darkShadow); - g.drawLine(x + w, y, x + w, y + h); + g.drawLine(x + w - 1, y, x + w - 1, y + h - 2); } + + g.setColor(selectedColor); + g.drawLine(x + w - 3, y + 1, x + w - 3, y + h - 4); g.setColor(saved); } @@ -2458,11 +3026,15 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public int tabForCoordinate(JTabbedPane pane, int x, int y) { + // Note: This code is tab layout mode agnostic. if (! tabPane.isValid()) tabPane.validate(); - + int tabCount = tabPane.getTabCount(); - int index = -1; + + // If the user clicked outside of any tab rect the + // selection should not change. + int index = tabPane.getSelectedIndex(); for (int i = 0; i < tabCount; ++i) { if (rects[i].contains(x, y)) @@ -2472,8 +3044,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants } } - // FIXME: Handle scrollable tab layout. - return index; } @@ -2569,7 +3139,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected int getRunForTab(int tabCount, int tabIndex) { if (runCount == 1 && tabIndex < tabCount && tabIndex >= 0) - return 1; + return 0; for (int i = 0; i < runCount; i++) { int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1; @@ -2688,6 +3258,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected View getTextViewForTab(int tabIndex) { + // FIXME: When the label contains HTML this should return something + // non-null. return null; } @@ -2704,7 +3276,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected int calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight) { - // FIXME: Handle HTML somehow. + // FIXME: Handle HTML by using the view (see getTextViewForTab). int height = fontHeight; Icon icon = getIconForTab(tabIndex); @@ -2921,8 +3493,9 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(), tabPane.getSelectedIndex(), - (tabPlacement == SwingConstants.RIGHT) - ? true : false); + (tabPlacement == SwingConstants.TOP) + ? direction == SwingConstants.NORTH + : direction == SwingConstants.SOUTH); selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(), offset); } @@ -2938,8 +3511,9 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(), tabPane.getSelectedIndex(), - (tabPlacement == SwingConstants.RIGHT) - ? true : false); + (tabPlacement == SwingConstants.LEFT) + ? direction == SwingConstants.WEST + : direction == SwingConstants.EAST); selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(), offset); } @@ -2953,8 +3527,13 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected void selectNextTabInRun(int current) { - tabPane.setSelectedIndex(getNextTabIndexInRun(tabPane.getTabCount(), - current)); + current = getNextTabIndexInRun(tabPane.getTabCount(), + current); + + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + scrollTab(current, tabPane.getTabPlacement()); + + tabPane.setSelectedIndex(current); } /** @@ -2964,8 +3543,13 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected void selectPreviousTabInRun(int current) { - tabPane.setSelectedIndex(getPreviousTabIndexInRun(tabPane.getTabCount(), - current)); + current = getPreviousTabIndexInRun(tabPane.getTabCount(), + current); + + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + scrollTab(current, tabPane.getTabPlacement()); + + tabPane.setSelectedIndex(current); } /** @@ -2975,7 +3559,12 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected void selectNextTab(int current) { - tabPane.setSelectedIndex(getNextTabIndex(current)); + current = getNextTabIndex(current); + + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + scrollTab(current, tabPane.getTabPlacement()); + + tabPane.setSelectedIndex(current); } /** @@ -2985,7 +3574,12 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ protected void selectPreviousTab(int current) { - tabPane.setSelectedIndex(getPreviousTabIndex(current)); + current = getPreviousTabIndex(current); + + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + scrollTab(current, tabPane.getTabPlacement()); + + tabPane.setSelectedIndex(current); } /** @@ -3019,7 +3613,11 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int index = tabForCoordinate(tabPane, x, y); if (index != -1) - tabPane.setSelectedIndex(index); + { + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + scrollTab(index, tabPlacement); + tabPane.setSelectedIndex(index); + } } // This method is called when you press up/down to cycle through tab runs. @@ -3056,6 +3654,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants else offset = rects[lastTabInRun(tabCount, nextRun)].x - rects[lastTabInRun(tabCount, currRun)].x; + return offset; } @@ -3102,9 +3701,12 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { int index = getNextTabIndex(base); int run = getRunForTab(tabCount, base); - if (index == lastTabInRun(tabCount, run) + 1) - index = lastTabInRun(tabCount, getPreviousTabRun(run)) + 1; - return getNextTabIndex(base); + if (base == lastTabInRun(tabCount, run)) + index = (run > 0) + ? lastTabInRun(tabCount, getPreviousTabRun(run)) + 1 + : 0; + + return index; } /** @@ -3122,7 +3724,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int run = getRunForTab(tabCount, base); if (index == lastTabInRun(tabCount, getPreviousTabRun(run))) index = lastTabInRun(tabCount, run); - return getPreviousTabIndex(base); + + return index; } /** @@ -3180,6 +3783,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // so I won't check it either. switch (targetPlacement) { + default: case SwingConstants.TOP: targetInsets.top = topInsets.top; targetInsets.left = topInsets.left; @@ -3206,6 +3810,44 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants break; } } + + ActionMap getActionMap() + { + ActionMap map = (ActionMap) UIManager.get("TabbedPane.actionMap"); + + if (map == null) // first time here + { + map = createActionMap(); + if (map != null) + UIManager.put("TabbedPane.actionMap", map); + } + return map; + } + + ActionMap createActionMap() + { + ActionMap map = new ActionMapUIResource(); + + map.put("navigatePageDown", new NavigatePageDownAction()); + map.put("navigatePageUp", new NavigatePageUpAction()); + map.put("navigateDown", + new NavigateAction("navigateDown", SwingConstants.SOUTH)); + + map.put("navigateUp", + new NavigateAction("navigateUp", SwingConstants.NORTH)); + + map.put("navigateLeft", + new NavigateAction("navigateLeft", SwingConstants.WEST)); + + map.put("navigateRight", + new NavigateAction("navigateRight", SwingConstants.EAST)); + + map.put("requestFocusForVisibleComponent", + new RequestFocusForVisibleComponentAction()); + map.put("requestFocus", new RequestFocusAction()); + + return map; + } /** * Sets the tab which should be highlighted when in rollover mode. And diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java index cdd44a711e7..15be4d57e62 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java @@ -38,8 +38,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.Color; import java.awt.Component; import java.awt.ComponentOrientation; @@ -48,7 +46,6 @@ 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.KeyEvent; @@ -58,6 +55,7 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.AbstractAction; +import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.CellRendererPane; import javax.swing.DefaultCellEditor; @@ -65,16 +63,16 @@ import javax.swing.DefaultListSelectionModel; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JTable; -import javax.swing.KeyStroke; import javax.swing.ListSelectionModel; import javax.swing.LookAndFeel; +import javax.swing.SwingUtilities; +import javax.swing.TransferHandler; 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.TableCellEditor; import javax.swing.table.TableCellRenderer; @@ -164,14 +162,37 @@ public class BasicTableUI extends TableUI public class FocusHandler implements FocusListener { - public void focusGained(FocusEvent e) + public void focusGained(FocusEvent e) { - // TODO: Implement this properly. + // The only thing that is affected by a focus change seems to be + // how the lead cell is painted. So we repaint this cell. + repaintLeadCell(); } - public void focusLost(FocusEvent e) + public void focusLost(FocusEvent e) + { + // The only thing that is affected by a focus change seems to be + // how the lead cell is painted. So we repaint this cell. + repaintLeadCell(); + } + + /** + * Repaints the lead cell in response to a focus change, to refresh + * the display of the focus indicator. + */ + private void repaintLeadCell() { - // TODO: Implement this properly. + int rowCount = table.getRowCount(); + int columnCount = table.getColumnCount(); + int rowLead = table.getSelectionModel().getLeadSelectionIndex(); + int columnLead = table.getColumnModel().getSelectionModel(). + getLeadSelectionIndex(); + if (rowLead >= 0 && rowLead < rowCount && columnLead >= 0 + && columnLead < columnCount) + { + Rectangle dirtyRect = table.getCellRect(rowLead, columnLead, false); + table.repaint(dirtyRect); + } } } @@ -242,19 +263,19 @@ public class BasicTableUI extends TableUI } } - public void mouseEntered(MouseEvent e) + public void mouseEntered(MouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } - public void mouseExited(MouseEvent e) + public void mouseExited(MouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } - public void mouseMoved(MouseEvent e) + public void mouseMoved(MouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } public void mousePressed(MouseEvent e) @@ -287,6 +308,9 @@ public class BasicTableUI extends TableUI colLead != colModel.getLeadSelectionIndex()) if (table.isEditing()) table.editingStopped(new ChangeEvent(e)); + + // Must request focus explicitly. + table.requestFocusInWindow(); } } @@ -456,66 +480,100 @@ public class BasicTableUI extends TableUI table.setOpaque(true); } + /** + * Installs keyboard actions on the table. + */ protected void installKeyboardActions() { - InputMap ancestorMap = (InputMap) UIManager.get("Table.ancestorInputMap"); - InputMapUIResource parentInputMap = new InputMapUIResource(); - // FIXME: The JDK uses a LazyActionMap for parentActionMap - ActionMap parentActionMap = new ActionMapUIResource(); - action = new TableAction(); - Object keys[] = ancestorMap.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) ancestorMap.get(stroke); + // Install the input map. + InputMap inputMap = + (InputMap) SharedUIDefaults.get("Table.ancestorInputMap"); + SwingUtilities.replaceUIInputMap(table, + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, + inputMap); - parentInputMap.put(KeyStroke.getKeyStroke(stroke.getKeyCode(), - stroke.getModifiers()), - actionString); - - parentActionMap.put(actionString, - new ActionListenerProxy(action, actionString)); + // FIXME: The JDK uses a LazyActionMap for parentActionMap + SwingUtilities.replaceUIActionMap(table, getActionMap()); - } - // Set the UI InputMap-ActionMap pair to be the parents of the - // JTable's InputMap-ActionMap pair - parentInputMap.setParent(table.getInputMap( - JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).getParent()); - parentActionMap.setParent(table.getActionMap().getParent()); - table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT). - setParent(parentInputMap); - table.getActionMap().setParent(parentActionMap); } /** - * 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. + * Fetches the action map from the UI defaults, or create a new one + * if the action map hasn't been initialized. + * + * @return the action map */ - private static class ActionListenerProxy - extends AbstractAction + private ActionMap getActionMap() { - ActionListener target; - String bindingCommandName; - - public ActionListenerProxy(ActionListener li, - String cmd) - { - target = li; - bindingCommandName = cmd; - } + ActionMap am = (ActionMap) UIManager.get("Table.actionMap"); + if (am == null) + { + am = createDefaultActions(); + UIManager.getLookAndFeelDefaults().put("Table.actionMap", am); + } + return am; + } - public void actionPerformed(ActionEvent e) - { - ActionEvent derivedEvent = new ActionEvent(e.getSource(), - e.getID(), - bindingCommandName, - e.getModifiers()); - target.actionPerformed(derivedEvent); - } + private ActionMap createDefaultActions() + { + ActionMapUIResource am = new ActionMapUIResource(); + Action action = new TableAction(); + + am.put("cut", TransferHandler.getCutAction()); + am.put("copy", TransferHandler.getCopyAction()); + am.put("paste", TransferHandler.getPasteAction()); + + am.put("cancel", action); + am.put("selectAll", action); + am.put("clearSelection", action); + am.put("startEditing", action); + + am.put("selectNextRow", action); + am.put("selectNextRowCell", action); + am.put("selectNextRowExtendSelection", action); + am.put("selectNextRowChangeLead", action); + + am.put("selectPreviousRow", action); + am.put("selectPreviousRowCell", action); + am.put("selectPreviousRowExtendSelection", action); + am.put("selectPreviousRowChangeLead", action); + + am.put("selectNextColumn", action); + am.put("selectNextColumnCell", action); + am.put("selectNextColumnExtendSelection", action); + am.put("selectNextColumnChangeLead", action); + + am.put("selectPreviousColumn", action); + am.put("selectPreviousColumnCell", action); + am.put("selectPreviousColumnExtendSelection", action); + am.put("selectPreviousColumnChangeLead", action); + + am.put("scrollLeftChangeSelection", action); + am.put("scrollLeftExtendSelection", action); + am.put("scrollRightChangeSelection", action); + am.put("scrollRightExtendSelection", action); + + am.put("scrollUpChangeSelection", action); + am.put("scrollUpExtendSelection", action); + am.put("scrollDownChangeSelection", action); + am.put("scrolldownExtendSelection", action); + + am.put("selectFirstColumn", action); + am.put("selectFirstColumnExtendSelection", action); + am.put("selectLastColumn", action); + am.put("selectLastColumnExtendSelection", action); + + am.put("selectFirstRow", action); + am.put("selectFirstRowExtendSelection", action); + am.put("selectLastRow", action); + am.put("selectLastRowExtendSelection", action); + + am.put("addToSelection", action); + am.put("toggleAndAnchor", action); + am.put("extendTo", action); + am.put("moveSelectionTo", action); + + return am; } /** @@ -524,7 +582,8 @@ public class BasicTableUI extends TableUI * method is called when a key that has been registered for the JTable * is received. */ - class TableAction extends AbstractAction + private static class TableAction + extends AbstractAction { /** * What to do when this action is called. @@ -533,6 +592,8 @@ public class BasicTableUI extends TableUI */ public void actionPerformed(ActionEvent e) { + JTable table = (JTable) e.getSource(); + DefaultListSelectionModel rowModel = (DefaultListSelectionModel) table.getSelectionModel(); DefaultListSelectionModel colModel @@ -543,9 +604,11 @@ public class BasicTableUI extends TableUI int colLead = colModel.getLeadSelectionIndex(); int colMax = table.getModel().getColumnCount() - 1; - - String command = e.getActionCommand(); - + + // The command with which the action has been called is stored + // in this undocumented action value. This allows us to have only + // one Action instance to serve all keyboard input for JTable. + String command = (String) getValue("__command__"); if (command.equals("selectPreviousRowExtendSelection")) { rowModel.setLeadSelectionIndex(Math.max(rowLead - 1, 0)); @@ -603,11 +666,11 @@ public class BasicTableUI extends TableUI else if (command.equals("scrollUpExtendSelection")) { int target; - if (rowLead == getFirstVisibleRowIndex()) - target = Math.max(0, rowLead - (getLastVisibleRowIndex() - - getFirstVisibleRowIndex() + 1)); + if (rowLead == getFirstVisibleRowIndex(table)) + target = Math.max(0, rowLead - (getLastVisibleRowIndex(table) + - getFirstVisibleRowIndex(table) + 1)); else - target = getFirstVisibleRowIndex(); + target = getFirstVisibleRowIndex(table); rowModel.setLeadSelectionIndex(target); colModel.setLeadSelectionIndex(colLead); @@ -620,11 +683,12 @@ public class BasicTableUI extends TableUI else if (command.equals("scrollRightChangeSelection")) { int target; - if (colLead == getLastVisibleColumnIndex()) - target = Math.min(colMax, colLead + (getLastVisibleColumnIndex() - - getFirstVisibleColumnIndex() + 1)); + if (colLead == getLastVisibleColumnIndex(table)) + target = Math.min(colMax, colLead + + (getLastVisibleColumnIndex(table) + - getFirstVisibleColumnIndex(table) + 1)); else - target = getLastVisibleColumnIndex(); + target = getLastVisibleColumnIndex(table); colModel.setSelectionInterval(target, target); rowModel.setSelectionInterval(rowLead, rowLead); @@ -637,11 +701,11 @@ public class BasicTableUI extends TableUI else if (command.equals("scrollLeftChangeSelection")) { int target; - if (colLead == getFirstVisibleColumnIndex()) - target = Math.max(0, colLead - (getLastVisibleColumnIndex() - - getFirstVisibleColumnIndex() + 1)); + if (colLead == getFirstVisibleColumnIndex(table)) + target = Math.max(0, colLead - (getLastVisibleColumnIndex(table) + - getFirstVisibleColumnIndex(table) + 1)); else - target = getFirstVisibleColumnIndex(); + target = getFirstVisibleColumnIndex(table); colModel.setSelectionInterval(target, target); rowModel.setSelectionInterval(rowLead, rowLead); @@ -723,14 +787,18 @@ public class BasicTableUI extends TableUI // If there are multiple rows and columns selected, select the next // cell and wrap at the edges of the selection. if (command.indexOf("Column") != -1) - advanceMultipleSelection(colModel, colMinSelected, colMaxSelected, - rowModel, rowMinSelected, rowMaxSelected, - command.equals("selectPreviousColumnCell"), true); + advanceMultipleSelection(table, colModel, colMinSelected, + colMaxSelected, rowModel, rowMinSelected, + rowMaxSelected, + command.equals("selectPreviousColumnCell"), + true); else - advanceMultipleSelection(rowModel, rowMinSelected, rowMaxSelected, - colModel, colMinSelected, colMaxSelected, - command.equals("selectPreviousRowCell"), false); + advanceMultipleSelection(table, rowModel, rowMinSelected, + rowMaxSelected, colModel, colMinSelected, + colMaxSelected, + command.equals("selectPreviousRowCell"), + false); } else if (command.equals("selectNextColumn")) { @@ -740,11 +808,11 @@ public class BasicTableUI extends TableUI else if (command.equals("scrollLeftExtendSelection")) { int target; - if (colLead == getFirstVisibleColumnIndex()) - target = Math.max(0, colLead - (getLastVisibleColumnIndex() - - getFirstVisibleColumnIndex() + 1)); + if (colLead == getFirstVisibleColumnIndex(table)) + target = Math.max(0, colLead - (getLastVisibleColumnIndex(table) + - getFirstVisibleColumnIndex(table) + 1)); else - target = getFirstVisibleColumnIndex(); + target = getFirstVisibleColumnIndex(table); colModel.setLeadSelectionIndex(target); rowModel.setLeadSelectionIndex(rowLead); @@ -752,11 +820,11 @@ public class BasicTableUI extends TableUI else if (command.equals("scrollDownChangeSelection")) { int target; - if (rowLead == getLastVisibleRowIndex()) - target = Math.min(rowMax, rowLead + (getLastVisibleRowIndex() - - getFirstVisibleRowIndex() + 1)); + if (rowLead == getLastVisibleRowIndex(table)) + target = Math.min(rowMax, rowLead + (getLastVisibleRowIndex(table) + - getFirstVisibleRowIndex(table) + 1)); else - target = getLastVisibleRowIndex(); + target = getLastVisibleRowIndex(table); rowModel.setSelectionInterval(target, target); colModel.setSelectionInterval(colLead, colLead); @@ -764,11 +832,11 @@ public class BasicTableUI extends TableUI else if (command.equals("scrollRightExtendSelection")) { int target; - if (colLead == getLastVisibleColumnIndex()) - target = Math.min(colMax, colLead + (getLastVisibleColumnIndex() - - getFirstVisibleColumnIndex() + 1)); + if (colLead == getLastVisibleColumnIndex(table)) + target = Math.min(colMax, colLead + (getLastVisibleColumnIndex(table) + - getFirstVisibleColumnIndex(table) + 1)); else - target = getLastVisibleColumnIndex(); + target = getLastVisibleColumnIndex(table); colModel.setLeadSelectionIndex(target); rowModel.setLeadSelectionIndex(rowLead); @@ -785,11 +853,11 @@ public class BasicTableUI extends TableUI else if (command.equals("scrollDownExtendSelection")) { int target; - if (rowLead == getLastVisibleRowIndex()) - target = Math.min(rowMax, rowLead + (getLastVisibleRowIndex() - - getFirstVisibleRowIndex() + 1)); + if (rowLead == getLastVisibleRowIndex(table)) + target = Math.min(rowMax, rowLead + (getLastVisibleRowIndex(table) + - getFirstVisibleRowIndex(table) + 1)); else - target = getLastVisibleRowIndex(); + target = getLastVisibleRowIndex(table); rowModel.setLeadSelectionIndex(target); colModel.setLeadSelectionIndex(colLead); @@ -797,11 +865,11 @@ public class BasicTableUI extends TableUI else if (command.equals("scrollUpChangeSelection")) { int target; - if (rowLead == getFirstVisibleRowIndex()) - target = Math.max(0, rowLead - (getLastVisibleRowIndex() - - getFirstVisibleRowIndex() + 1)); + if (rowLead == getFirstVisibleRowIndex(table)) + target = Math.max(0, rowLead - (getLastVisibleRowIndex(table) + - getFirstVisibleRowIndex(table) + 1)); else - target = getFirstVisibleRowIndex(); + target = getFirstVisibleRowIndex(table); rowModel.setSelectionInterval(target, target); colModel.setSelectionInterval(colLead, colLead); @@ -926,7 +994,7 @@ public class BasicTableUI extends TableUI * Returns the column index of the first visible column. * @return the column index of the first visible column. */ - int getFirstVisibleColumnIndex() + int getFirstVisibleColumnIndex(JTable table) { ComponentOrientation or = table.getComponentOrientation(); Rectangle r = table.getVisibleRect(); @@ -939,7 +1007,7 @@ public class BasicTableUI extends TableUI * Returns the column index of the last visible column. * */ - int getLastVisibleColumnIndex() + int getLastVisibleColumnIndex(JTable table) { ComponentOrientation or = table.getComponentOrientation(); Rectangle r = table.getVisibleRect(); @@ -952,7 +1020,7 @@ public class BasicTableUI extends TableUI * Returns the row index of the first visible row. * */ - int getFirstVisibleRowIndex() + int getFirstVisibleRowIndex(JTable table) { ComponentOrientation or = table.getComponentOrientation(); Rectangle r = table.getVisibleRect(); @@ -965,7 +1033,7 @@ public class BasicTableUI extends TableUI * Returns the row index of the last visible row. * */ - int getLastVisibleRowIndex() + int getLastVisibleRowIndex(JTable table) { ComponentOrientation or = table.getComponentOrientation(); Rectangle r = table.getVisibleRect(); @@ -977,7 +1045,7 @@ public class BasicTableUI extends TableUI // area is larger than the table) if (table.rowAtPoint(r.getLocation()) == -1) { - if (getFirstVisibleRowIndex() == -1) + if (getFirstVisibleRowIndex(table) == -1) return -1; else return table.getModel().getRowCount() - 1; @@ -1003,7 +1071,8 @@ public class BasicTableUI extends TableUI * @param reverse true if shift was held for the event * @param eventIsTab true if TAB was pressed, false if ENTER pressed */ - void advanceMultipleSelection(ListSelectionModel firstModel, int firstMin, + void advanceMultipleSelection(JTable table, ListSelectionModel firstModel, + int firstMin, int firstMax, ListSelectionModel secondModel, int secondMin, int secondMax, boolean reverse, boolean eventIsTab) @@ -1167,30 +1236,24 @@ public class BasicTableUI extends TableUI table.addPropertyChangeListener(propertyChangeListener); } - protected void uninstallDefaults() + /** + * Uninstalls UI defaults that have been installed by + * {@link #installDefaults()}. + */ + protected void uninstallDefaults() { - // TODO: this method used to do the following which is not - // quite right (at least it breaks apps that run fine with the - // JDK): - // - // table.setFont(null); - // table.setGridColor(null); - // table.setForeground(null); - // table.setBackground(null); - // table.setSelectionForeground(null); - // table.setSelectionBackground(null); - // - // This would leave the component in a corrupt state, which is - // not acceptable. A possible solution would be to have component - // level defaults installed, that get overridden by the UI defaults - // and get restored in this method. I am not quite sure about this - // though. / Roman Kennke + // Nothing to do here for now. } - protected void uninstallKeyboardActions() - throws NotImplementedException + /** + * Uninstalls the keyboard actions that have been installed by + * {@link #installKeyboardActions()}. + */ + protected void uninstallKeyboardActions() { - // TODO: Implement this properly. + SwingUtilities.replaceUIInputMap(table, JComponent. + WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); + SwingUtilities.replaceUIActionMap(table, null); } protected void uninstallListeners() diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java index 89c4e5a7562..6792aa065da 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Color; import java.beans.PropertyChangeEvent; import javax.swing.JComponent; @@ -94,12 +95,24 @@ public class BasicTextFieldUI extends BasicTextUI { if (event.getPropertyName().equals("editable")) { - boolean editable = ((Boolean) event.getNewValue()).booleanValue(); - // Changing the color only if the current background is an instance of // ColorUIResource is the behavior of the RI. if (textComponent.getBackground() instanceof ColorUIResource) - textComponent.setBackground(editable ? background : inactiveBackground); + { + Color c = null; + Color old = textComponent.getBackground(); + String prefix = getPropertyPrefix(); + if (! textComponent.isEnabled()) + c = SharedUIDefaults.getColor(prefix + ".disabledBackground"); + if (c == null && ! textComponent.isEditable()) + c = SharedUIDefaults.getColor(prefix + ".inactiveBackground"); + if (c == null) + c = SharedUIDefaults.getColor(prefix + ".background"); + if (c != null && c != old) + { + textComponent.setBackground(c); + } + } } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java index 34261cfe644..8e9c8c949f3 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java @@ -83,7 +83,6 @@ import javax.swing.text.Highlighter; import javax.swing.text.JTextComponent; import javax.swing.text.Keymap; import javax.swing.text.Position; -import javax.swing.text.Utilities; import javax.swing.text.View; import javax.swing.text.ViewFactory; @@ -418,7 +417,19 @@ public abstract class BasicTextUI extends TextUI if (event.getPropertyName().equals("document")) { // Document changed. - modelChanged(); + Object oldValue = event.getOldValue(); + if (oldValue != null) + { + Document oldDoc = (Document) oldValue; + oldDoc.removeDocumentListener(documentHandler); + } + Object newValue = event.getNewValue(); + if (newValue != null) + { + Document newDoc = (Document) newValue; + newDoc.addDocumentListener(documentHandler); + } + modelChanged(); } BasicTextUI.this.propertyChange(event); @@ -501,18 +512,6 @@ public abstract class BasicTextUI extends TextUI 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() @@ -558,22 +557,23 @@ public abstract class BasicTextUI extends TextUI */ public void installUI(final JComponent c) { - super.installUI(c); - textComponent = (JTextComponent) c; + installDefaults(); + textComponent.addPropertyChangeListener(updateHandler); Document doc = textComponent.getDocument(); if (doc == null) { doc = getEditorKit(textComponent).createDefaultDocument(); textComponent.setDocument(doc); } - installDefaults(); + else + { + doc.addDocumentListener(documentHandler); + modelChanged(); + } + installListeners(); installKeyboardActions(); - - // We need to trigger this so that the view hierarchy gets initialized. - modelChanged(); - } /** @@ -581,34 +581,60 @@ public abstract class BasicTextUI extends TextUI */ protected void installDefaults() { + String prefix = getPropertyPrefix(); + // Install the standard properties. + LookAndFeel.installColorsAndFont(textComponent, prefix + ".background", + prefix + ".foreground", prefix + ".font"); + LookAndFeel.installBorder(textComponent, prefix + ".border"); + textComponent.setMargin(UIManager.getInsets(prefix + ".margin")); + + // Some additional text component only properties. + Color color = textComponent.getCaretColor(); + if (color == null || color instanceof UIResource) + { + color = UIManager.getColor(prefix + ".caretForeground"); + textComponent.setCaretColor(color); + } + + // Fetch the colors for enabled/disabled text components. + color = textComponent.getDisabledTextColor(); + if (color == null || color instanceof UIResource) + { + color = UIManager.getColor(prefix + ".inactiveBackground"); + textComponent.setDisabledTextColor(color); + } + color = textComponent.getSelectedTextColor(); + if (color == null || color instanceof UIResource) + { + color = UIManager.getColor(prefix + ".selectionForeground"); + textComponent.setSelectedTextColor(color); + } + color = textComponent.getSelectionColor(); + if (color == null || color instanceof UIResource) + { + color = UIManager.getColor(prefix + ".selectionBackground"); + textComponent.setSelectionColor(color); + } + + Insets margin = textComponent.getMargin(); + if (margin == null || margin instanceof UIResource) + { + margin = UIManager.getInsets(prefix + ".margin"); + textComponent.setMargin(margin); + } + Caret caret = textComponent.getCaret(); - if (caret == null) + if (caret == null || caret instanceof UIResource) { caret = createCaret(); textComponent.setCaret(caret); + caret.setBlinkRate(UIManager.getInt(prefix + ".caretBlinkRate")); } Highlighter highlighter = textComponent.getHighlighter(); - if (highlighter == null) + if (highlighter == null || highlighter instanceof UIResource) textComponent.setHighlighter(createHighlighter()); - String prefix = getPropertyPrefix(); - 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")); } /** @@ -670,18 +696,6 @@ public abstract class BasicTextUI extends TextUI protected void installListeners() { textComponent.addFocusListener(focuslistener); - textComponent.addPropertyChangeListener(updateHandler); - installDocumentListeners(); - } - - /** - * Installs the document listeners on the textComponent's model. - */ - private void installDocumentListeners() - { - Document doc = textComponent.getDocument(); - if (doc != null) - doc.addDocumentListener(documentHandler); } /** @@ -845,9 +859,7 @@ public abstract class BasicTextUI extends TextUI */ protected void uninstallListeners() { - textComponent.removePropertyChangeListener(updateHandler); textComponent.removeFocusListener(focuslistener); - textComponent.getDocument().removeDocumentListener(documentHandler); } /** @@ -1029,7 +1041,7 @@ public abstract class BasicTextUI extends TextUI */ public void damageRange(JTextComponent t, int p0, int p1) { - damageRange(t, p0, p1, null, null); + damageRange(t, p0, p1, Position.Bias.Forward, Position.Bias.Backward); } /** @@ -1049,106 +1061,36 @@ public abstract class BasicTextUI extends TextUI public void damageRange(JTextComponent t, int p0, int p1, Position.Bias firstBias, Position.Bias secondBias) { - // Do nothing if the component cannot be properly displayed. - if (t.getWidth() == 0 || t.getHeight() == 0) - return; - - try + Rectangle alloc = getVisibleEditorRect(); + if (alloc != null) { - // Limit p0 and p1 to sane values to prevent unfriendly - // BadLocationExceptions. This makes it possible for the highlighter - // to send us illegal values which can happen when a large number - // of selected characters are removed (eg. by pressing delete - // or backspace). - // The reference implementation does not throw an exception, too. - p0 = Math.min(p0, t.getDocument().getLength()); - p1 = Math.min(p1, t.getDocument().getLength()); - - Rectangle l1 = modelToView(t, p0, firstBias); - Rectangle l2 = modelToView(t, p1, secondBias); - if (l1 == null || l2 == null) + Document doc = t.getDocument(); + + // Acquire lock here to avoid structural changes in between. + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readLock(); + try { - // Unable to determine the start or end of the selection. - t.repaint(); + rootView.setSize(alloc.width, alloc.height); + Shape damage = rootView.modelToView(p0, firstBias, p1, secondBias, + alloc); + Rectangle r = damage instanceof Rectangle ? (Rectangle) damage + : damage.getBounds(); + textComponent.repaint(r.x, r.y, r.width, r.height); } - else if (l1.y == l2.y) + catch (BadLocationException ex) { - SwingUtilities.computeUnion(l2.x, l2.y, l2.width, l2.height, l1); - t.repaint(l1); + // Lets ignore this as it causes no serious problems. + // For debugging, comment this out. + // ex.printStackTrace(); } - else + finally { - // The two rectangles lie on different lines and we need a - // different algorithm to calculate the damaged area: - // 1. The line of p0 is damaged from the position of p0 - // to the right border. - // 2. All lines between the ones where p0 and p1 lie on - // are completely damaged. Use the allocation area to find - // out the bounds. - // 3. The final line is damaged from the left bound to the - // position of p1. - Insets insets = t.getInsets(); - - // Damage first line until the end. - l1.width = insets.right + t.getWidth() - l1.x; - t.repaint(l1); - - // Note: Utilities.getPositionBelow() may return the offset - // that was put in. In that case there is no next line and - // we should stop searching for one. - - int posBelow = Utilities.getPositionBelow(t, p0, l1.x); - int p1RowStart = Utilities.getRowStart(t, p1); - - if (posBelow != -1 - && posBelow != p0 - && Utilities.getRowStart(t, posBelow) != p1RowStart) - { - // Take the rectangle of the offset we just found and grow it - // to the maximum width. Retain y because this is our start - // height. - Rectangle grow = modelToView(t, posBelow); - grow.x = insets.left; - grow.width = t.getWidth() + insets.right; - - // Find further lines which have to be damaged completely. - int nextPosBelow = posBelow; - while (nextPosBelow != -1 - && posBelow != nextPosBelow - && Utilities.getRowStart(t, nextPosBelow) != p1RowStart) - { - posBelow = nextPosBelow; - nextPosBelow = Utilities.getPositionBelow(t, posBelow, - l1.x); - - if (posBelow == nextPosBelow) - break; - } - // Now posBelow is an offset on the last line which has to be - // damaged completely. (newPosBelow is on the same line as p1) - - // Retrieve the rectangle of posBelow and use its y and height - // value to calculate the final height of the multiple line - // spanning rectangle. - Rectangle end = modelToView(t, posBelow); - grow.height = end.y + end.height - grow.y; - - // Mark that area as damage. - t.repaint(grow); - } - - // Damage last line from its beginning to the position of p1. - l2.width += l2.x; - l2.x = insets.left; - t.repaint(l2); + // Release lock. + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readUnlock(); } } - catch (BadLocationException ex) - { - AssertionError err = new AssertionError("Unexpected bad location"); - err.initCause(ex); - throw err; - } } /** @@ -1245,10 +1187,29 @@ public abstract class BasicTextUI extends TextUI public Rectangle modelToView(JTextComponent t, int pos, Position.Bias bias) throws BadLocationException { - Rectangle r = getVisibleEditorRect(); - - return (r != null) ? rootView.modelToView(pos, r, bias).getBounds() - : null; + // We need to read-lock here because we depend on the document + // structure not beeing changed in between. + Document doc = textComponent.getDocument(); + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readLock(); + Rectangle rect = null; + try + { + Rectangle r = getVisibleEditorRect(); + if (r != null) + { + rootView.setSize(r.width, r.height); + Shape s = rootView.modelToView(pos, r, bias); + if (s != null) + rect = s.getBounds(); + } + } + finally + { + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readUnlock(); + } + return rect; } /** @@ -1361,7 +1322,6 @@ public abstract class BasicTextUI extends TextUI Document doc = textComponent.getDocument(); if (doc == null) return; - installDocumentListeners(); Element elem = doc.getDefaultRootElement(); if (elem == null) return; diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java index 8fce2f08a66..1c36b408d5a 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java @@ -38,8 +38,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; @@ -50,6 +48,7 @@ import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.Window; +import java.awt.event.ActionEvent; import java.awt.event.ContainerEvent; import java.awt.event.ContainerListener; import java.awt.event.FocusEvent; @@ -62,7 +61,11 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Hashtable; +import javax.swing.AbstractAction; import javax.swing.AbstractButton; +import javax.swing.Action; +import javax.swing.ActionMap; +import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; @@ -77,6 +80,7 @@ import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.border.CompoundBorder; import javax.swing.event.MouseInputListener; +import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ToolBarUI; import javax.swing.plaf.UIResource; @@ -87,6 +91,35 @@ import javax.swing.plaf.basic.BasicBorders.ButtonBorder; */ public class BasicToolBarUI extends ToolBarUI implements SwingConstants { + + /** + * Implements the keyboard actions for JToolBar. + */ + static class ToolBarAction + extends AbstractAction + { + /** + * Performs the action. + */ + public void actionPerformed(ActionEvent event) + { + Object cmd = getValue("__command__"); + JToolBar toolBar = (JToolBar) event.getSource(); + BasicToolBarUI ui = (BasicToolBarUI) toolBar.getUI(); + + if (cmd.equals("navigateRight")) + ui.navigateFocusedComp(EAST); + else if (cmd.equals("navigateLeft")) + ui.navigateFocusedComp(WEST); + else if (cmd.equals("navigateUp")) + ui.navigateFocusedComp(NORTH); + else if (cmd.equals("navigateDown")) + ui.navigateFocusedComp(SOUTH); + else + assert false : "Shouldn't reach here"; + } + } + /** Static owner of all DragWindows. * This is package-private to avoid an accessor method. */ static JFrame owner = new JFrame(); @@ -619,9 +652,46 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants * by the look and feel. */ protected void installKeyboardActions() - throws NotImplementedException { - // FIXME: implement. + // Install the input map. + InputMap inputMap = + (InputMap) SharedUIDefaults.get("ToolBar.ancestorInputMap"); + SwingUtilities.replaceUIInputMap(toolBar, + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, + inputMap); + + // FIXME: The JDK uses a LazyActionMap for parentActionMap + SwingUtilities.replaceUIActionMap(toolBar, getActionMap()); + } + + /** + * Fetches the action map from the UI defaults, or create a new one + * if the action map hasn't been initialized. + * + * @return the action map + */ + private ActionMap getActionMap() + { + ActionMap am = (ActionMap) UIManager.get("ToolBar.actionMap"); + if (am == null) + { + am = createDefaultActions(); + UIManager.getLookAndFeelDefaults().put("ToolBar.actionMap", am); + } + return am; + } + + private ActionMap createDefaultActions() + { + ActionMapUIResource am = new ActionMapUIResource(); + Action action = new ToolBarAction(); + + am.put("navigateLeft", action); + am.put("navigateRight", action); + am.put("navigateUp", action); + am.put("navigateDown", action); + + return am; } /** @@ -643,7 +713,12 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants floatFrame.addWindowListener(windowListener); toolBarFocusListener = createToolBarFocusListener(); - toolBar.addFocusListener(toolBarFocusListener); + if (toolBarFocusListener != null) + { + int count = toolBar.getComponentCount(); + for (int i = 0; i < count; i++) + toolBar.getComponent(i).addFocusListener(toolBarFocusListener); + } } /** @@ -758,9 +833,55 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants * @param direction The direction to give focus to. */ protected void navigateFocusedComp(int direction) - throws NotImplementedException { - // FIXME: Implement. + int count = toolBar.getComponentCount(); + switch (direction) + { + case EAST: + case SOUTH: + if (focusedCompIndex >= 0 && focusedCompIndex < count) + { + int i = focusedCompIndex + 1; + boolean focusRequested = false; + // Find component to focus and request focus on it. + while (i != focusedCompIndex && ! focusRequested) + { + if (i >= count) + i = 0; + Component comp = toolBar.getComponentAtIndex(i++); + if (comp != null && comp.isFocusable() + && comp.isEnabled()) + { + comp.requestFocus(); + focusRequested = true; + } + } + } + break; + case WEST: + case NORTH: + if (focusedCompIndex >= 0 && focusedCompIndex < count) + { + int i = focusedCompIndex - 1; + boolean focusRequested = false; + // Find component to focus and request focus on it. + while (i != focusedCompIndex && ! focusRequested) + { + if (i < 0) + i = count - 1; + Component comp = toolBar.getComponentAtIndex(i--); + if (comp != null && comp.isFocusable() + && comp.isEnabled()) + { + comp.requestFocus(); + focusRequested = true; + } + } + } + break; + default: + break; + } } /** @@ -925,9 +1046,10 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants * This method uninstalls keyboard actions installed by the UI. */ protected void uninstallKeyboardActions() - throws NotImplementedException { - // FIXME: implement. + SwingUtilities.replaceUIInputMap(toolBar, JComponent. + WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); + SwingUtilities.replaceUIActionMap(toolBar, null); } /** @@ -935,8 +1057,13 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants */ protected void uninstallListeners() { - toolBar.removeFocusListener(toolBarFocusListener); - toolBarFocusListener = null; + if (toolBarFocusListener != null) + { + int count = toolBar.getComponentCount(); + for (int i = 0; i < count; i++) + toolBar.getComponent(i).removeFocusListener(toolBarFocusListener); + toolBarFocusListener = null; + } floatFrame.removeWindowListener(windowListener); windowListener = null; @@ -998,7 +1125,7 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants */ public void mouseClicked(MouseEvent e) { - // Don't care. + // Nothing to do here. } /** @@ -1020,7 +1147,7 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants */ public void mouseEntered(MouseEvent e) { - // Don't care (yet). + // Nothing to do here. } /** @@ -1030,7 +1157,7 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants */ public void mouseExited(MouseEvent e) { - // Don't care (yet). + // Nothing to do here. } /** @@ -1040,7 +1167,7 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants */ public void mouseMoved(MouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } /** @@ -1203,13 +1330,17 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants } /** - * FIXME: Do something. + * Sets the orientation of the toolbar and the + * drag window. * - * @param o DOCUMENT ME! + * @param o - the new orientation of the toolbar and drag + * window. */ public void setOrientation(int o) { - // FIXME: implement. + toolBar.setOrientation(o); + if (dragWindow != null) + dragWindow.setOrientation(o); } } @@ -1290,6 +1421,10 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants cachedBounds = toolBar.getPreferredSize(); cachedOrientation = toolBar.getOrientation(); + + Component c = e.getChild(); + if (toolBarFocusListener != null) + c.addFocusListener(toolBarFocusListener); } /** @@ -1303,6 +1438,10 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants setBorderToNormal(e.getChild()); cachedBounds = toolBar.getPreferredSize(); cachedOrientation = toolBar.getOrientation(); + + Component c = e.getChild(); + if (toolBarFocusListener != null) + c.removeFocusListener(toolBarFocusListener); } } @@ -1332,27 +1471,30 @@ public class BasicToolBarUI extends ToolBarUI implements SwingConstants */ protected ToolBarFocusListener() { - // FIXME: implement. + // Nothing to do here. } /** - * DOCUMENT ME! - * - * @param e DOCUMENT ME! + * Receives notification when the toolbar or one of it's component + * receives the keyboard input focus. + * + * @param e the focus event */ public void focusGained(FocusEvent e) { - // FIXME: implement. + Component c = e.getComponent(); + focusedCompIndex = toolBar.getComponentIndex(c); } /** - * DOCUMENT ME! - * - * @param e DOCUMENT ME! + * Receives notification when the toolbar or one of it's component + * looses the keyboard input focus. + * + * @param e the focus event */ public void focusLost(FocusEvent e) { - // FIXME: implement. + // Do nothing here. } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java index 4c139fe465b..9a193986ac5 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java @@ -174,6 +174,9 @@ public class BasicTreeUI /** * Set to false when editing and shouldSelectCall() returns true meaning the * node should be selected before editing, used in completeEditing. + * GNU Classpath editing is implemented differently, so this value is not + * actually read anywhere. However it is always set correctly to maintain + * interoperability with the derived classes that read this field. */ protected boolean stopEditingInCompleteEditing; @@ -235,9 +238,6 @@ public class BasicTreeUI /** Set to true if the editor has a different size than the renderer. */ protected boolean editorHasDifferentSize; - /** The action bound to KeyStrokes. */ - TreeAction action; - /** Boolean to keep track of editing. */ boolean isEditing; @@ -474,8 +474,18 @@ public class BasicTreeUI */ protected void setCellRenderer(TreeCellRenderer tcr) { - currentCellRenderer = tcr; + // Finish editing before changing the renderer. + completeEditing(); + + // The renderer is set in updateRenderer. updateRenderer(); + + // Refresh the layout if necessary. + if (treeState != null) + { + treeState.invalidateSizes(); + updateSize(); + } } /** @@ -845,9 +855,9 @@ public class BasicTreeUI updateRenderer(); updateDepthOffset(); setSelectionModel(tree.getSelectionModel()); - treeState = createLayoutCache(); - treeSelectionModel.setRowMapper(treeState); configureLayoutCache(); + treeState.setRootVisible(tree.isRootVisible()); + treeSelectionModel.setRowMapper(treeState); updateSize(); } @@ -1068,7 +1078,6 @@ public class BasicTreeUI */ protected void uninstallKeyboardActions() { - action = null; tree.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).setParent( null); tree.getActionMap().setParent(null); @@ -1169,10 +1178,26 @@ public class BasicTreeUI protected void updateRenderer() { if (tree != null) - currentCellRenderer = tree.getCellRenderer(); - - if (currentCellRenderer == null) - currentCellRenderer = createDefaultCellRenderer(); + { + TreeCellRenderer rend = tree.getCellRenderer(); + if (rend != null) + { + createdRenderer = false; + currentCellRenderer = rend; + if (createdCellEditor) + tree.setCellEditor(null); + } + else + { + tree.setCellRenderer(createDefaultCellRenderer()); + createdRenderer = true; + } + } + else + { + currentCellRenderer = null; + createdRenderer = false; + } updateCellEditor(); } @@ -1237,6 +1262,11 @@ public class BasicTreeUI { LookAndFeel.installColorsAndFont(tree, "Tree.background", "Tree.foreground", "Tree.font"); + + hashColor = UIManager.getColor("Tree.hash"); + if (hashColor == null) + hashColor = Color.black; + tree.setOpaque(true); rightChildIndent = UIManager.getInt("Tree.rightChildIndent"); @@ -1264,8 +1294,6 @@ public class BasicTreeUI JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, ancestorInputMap); - action = new TreeAction(); - SwingUtilities.replaceUIActionMap(tree, getActionMap()); } @@ -1295,9 +1323,6 @@ public class BasicTreeUI ActionMapUIResource am = new ActionMapUIResource(); Action action; - action = new TreeAction(); - am.put(action.getValue(Action.NAME), action); - // TreeHomeAction. action = new TreeHomeAction(-1, "selectFirst"); am.put(action.getValue(Action.NAME), action); @@ -1349,6 +1374,13 @@ public class BasicTreeUI am.put(action.getValue(Action.NAME), action); action = new TreePageAction(1, "scrollDownChangeLead"); am.put(action.getValue(Action.NAME), action); + + // Tree editing actions + action = new TreeStartEditingAction("startEditing"); + am.put(action.getValue(Action.NAME), action); + action = new TreeCancelEditingAction("cancel"); + am.put(action.getValue(Action.NAME), action); + return am; } @@ -1703,9 +1735,10 @@ public class BasicTreeUI protected void completeEditing(boolean messageStop, boolean messageCancel, boolean messageTree) { - if (! stopEditingInCompleteEditing || editingComponent == null) + // Make no attempt to complete the non existing editing session. + if (!isEditing(tree)) return; - + if (messageStop) { getCellEditor().stopCellEditing(); @@ -1812,14 +1845,25 @@ public class BasicTreeUI boolean cntlClick = false; if (! treeModel.isLeaf(path.getLastPathComponent())) { - int width = 8; // Only guessing. + int width; Icon expandedIcon = getExpandedIcon(); if (expandedIcon != null) width = expandedIcon.getIconWidth(); + else + // Only guessing. This is the width of + // the tree control icon in Metal L&F. + width = 18; Insets i = tree.getInsets(); - int left = getRowX(tree.getRowForPath(path), path.getPathCount() - 1) - - getRightChildIndent() - width / 2 + i.left; + + int depth; + if (isRootVisible()) + depth = path.getPathCount()-1; + else + depth = path.getPathCount()-2; + + int left = getRowX(tree.getRowForPath(path), depth) + - width + i.left; cntlClick = mouseX >= left && mouseX <= left + width; } return cntlClick; @@ -1848,7 +1892,8 @@ public class BasicTreeUI */ protected void toggleExpandState(TreePath path) { - if (tree.isExpanded(path)) + // tree.isExpanded(path) would do the same, but treeState knows faster. + if (treeState.isExpanded(path)) tree.collapsePath(path); else tree.expandPath(path); @@ -1975,94 +2020,35 @@ public class BasicTreeUI Object node = pathForRow.getLastPathComponent(); return treeModel.isLeaf(node); } - + /** - * This class implements the actions that we want to happen when specific keys - * are pressed for the JTree. The actionPerformed method is called when a key - * that has been registered for the JTree is received. + * The action to start editing at the current lead selection path. */ - class TreeAction + class TreeStartEditingAction extends AbstractAction { - /** - * What to do when this action is called. + * Creates the new tree cancel editing action. + * + * @param name the name of the action (used in toString). + */ + public TreeStartEditingAction(String name) + { + super(name); + } + + /** + * Start editing at the current lead selection path. * * @param e the ActionEvent that caused this action. */ public void actionPerformed(ActionEvent e) { - String command = e.getActionCommand(); TreePath lead = tree.getLeadSelectionPath(); - - if (command.equals("selectPreviousChangeLead") - || command.equals("selectPreviousExtendSelection") - || command.equals("selectPrevious") || command.equals("selectNext") - || command.equals("selectNextExtendSelection") - || command.equals("selectNextChangeLead")) - (new TreeIncrementAction(0, "")).actionPerformed(e); - else if (command.equals("selectParent") || command.equals("selectChild")) - (new TreeTraverseAction(0, "")).actionPerformed(e); - else if (command.equals("selectAll")) - { - TreePath[] paths = new TreePath[treeState.getRowCount()]; - for (int i = 0; i < paths.length; i++) - paths[i] = treeState.getPathForRow(i); - tree.addSelectionPaths(paths); - } - else if (command.equals("startEditing")) + if (!tree.isEditing()) tree.startEditingAtPath(lead); - else if (command.equals("toggle")) - { - if (tree.isEditing()) - tree.stopEditing(); - else - { - Object last = lead.getLastPathComponent(); - TreePath path = new TreePath(getPathToRoot(last, 0)); - if (! treeModel.isLeaf(last)) - toggleExpandState(path); - } - } - else if (command.equals("clearSelection")) - tree.clearSelection(); - - if (tree.isEditing() && ! command.equals("startEditing")) - tree.stopEditing(); - - tree.scrollPathToVisible(tree.getLeadSelectionPath()); } - } - - /** - * This class is used to mimic 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 static class ActionListenerProxy - extends AbstractAction - { - ActionListener target; - - String bindingCommandName; - - public ActionListenerProxy(ActionListener li, String cmd) - { - target = li; - bindingCommandName = cmd; - } - - public void actionPerformed(ActionEvent e) - { - ActionEvent derivedEvent = new ActionEvent(e.getSource(), e.getID(), - bindingCommandName, - e.getModifiers()); - - target.actionPerformed(derivedEvent); - } - } + } /** * Updates the preferred size when scrolling, if necessary. @@ -2290,9 +2276,49 @@ public class BasicTreeUI * @param e the key typed */ public void keyTyped(KeyEvent e) - throws NotImplementedException { - // TODO: What should be done here, if anything? + char typed = Character.toLowerCase(e.getKeyChar()); + for (int row = tree.getLeadSelectionRow() + 1; + row < tree.getRowCount(); row++) + { + if (checkMatch(row, typed)) + { + tree.setSelectionRow(row); + tree.scrollRowToVisible(row); + return; + } + } + + // Not found below, search above: + for (int row = 0; row < tree.getLeadSelectionRow(); row++) + { + if (checkMatch(row, typed)) + { + tree.setSelectionRow(row); + tree.scrollRowToVisible(row); + return; + } + } + } + + /** + * Check if the given tree row starts with this character + * + * @param row the tree row + * @param typed the typed char, must be converted to lowercase + * @return true if the given tree row starts with this character + */ + boolean checkMatch(int row, char typed) + { + TreePath path = treeState.getPathForRow(row); + String node = path.getLastPathComponent().toString(); + if (node.length() > 0) + { + char x = node.charAt(0); + if (typed == Character.toLowerCase(x)) + return true; + } + return false; } /** @@ -2301,9 +2327,8 @@ public class BasicTreeUI * @param e the key pressed */ public void keyPressed(KeyEvent e) - throws NotImplementedException { - // TODO: What should be done here, if anything? + // Nothing to do here. } /** @@ -2312,9 +2337,8 @@ public class BasicTreeUI * @param e the key released */ public void keyReleased(KeyEvent e) - throws NotImplementedException { - // TODO: What should be done here, if anything? + // Nothing to do here. } } @@ -2341,14 +2365,23 @@ public class BasicTreeUI */ public void mousePressed(MouseEvent e) { + // Any mouse click cancels the previous waiting edit action, initiated + // by the single click on the selected node. + if (startEditTimer != null) + { + startEditTimer.stop(); + startEditTimer = null; + } if (tree != null && tree.isEnabled()) { - // Maybe stop editing and return. - if (isEditing(tree) && tree.getInvokesStopCellEditing() - && !stopEditing(tree)) - return; - + // Always end the current editing session if clicked on the + // tree and outside the bounds of the editing component. + if (isEditing(tree)) + if (!stopEditing(tree)) + // Return if we have failed to cancel the editing session. + return; + int x = e.getX(); int y = e.getY(); TreePath path = getClosestPathForLocation(tree, x, y); @@ -2361,11 +2394,47 @@ public class BasicTreeUI if (x > bounds.x && x <= (bounds.x + bounds.width)) { - if (! startEditing(path, e)) - selectPathForEvent(path, e); + TreePath currentLead = tree.getLeadSelectionPath(); + if (currentLead != null && currentLead.equals(path) + && e.getClickCount() == 1 && tree.isEditable()) + { + // Schedule the editing session. + final TreePath editPath = path; + + // The code below handles the required click-pause-click + // functionality which must be present in the tree UI. + // If the next click comes after the + // time longer than the double click interval AND + // the same node stays focused for the WAIT_TILL_EDITING + // duration, the timer starts the editing session. + if (startEditTimer != null) + startEditTimer.stop(); + + startEditTimer = new Timer(WAIT_TILL_EDITING, + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + startEditing(editPath, EDIT); + } + }); + + startEditTimer.setRepeats(false); + startEditTimer.start(); + } + else + { + if (e.getClickCount() == 2) + toggleExpandState(path); + else + selectPathForEvent(path, e); + } } } } + + // We need to request the focus. + tree.requestFocusInWindow(); } /** @@ -2830,6 +2899,9 @@ public class BasicTreeUI } } } + + // Ensure that the lead path is visible after the increment action. + tree.scrollPathToVisible(tree.getLeadSelectionPath()); } /** @@ -2945,6 +3017,9 @@ public class BasicTreeUI tree.setAnchorSelectionPath(newPath); tree.setLeadSelectionPath(newPath); } + + // Ensure that the lead path is visible after the increment action. + tree.scrollPathToVisible(tree.getLeadSelectionPath()); } /** @@ -3249,6 +3324,9 @@ public class BasicTreeUI // and anchor. tree.setLeadSelectionPath(leadPath); tree.setAnchorSelectionPath(anchorPath); + + // Ensure that the lead path is visible after the increment action. + tree.scrollPathToVisible(tree.getLeadSelectionPath()); } } @@ -3336,6 +3414,9 @@ public class BasicTreeUI tree.expandPath(current); } } + + // Ensure that the lead path is visible after the increment action. + tree.scrollPathToVisible(tree.getLeadSelectionPath()); } /** @@ -3644,7 +3725,13 @@ public class BasicTreeUI { Rectangle bounds = getPathBounds(tree, path); TreePath parent = path.getParentPath(); - if (parent != null) + + boolean paintLine; + if (isRootVisible()) + paintLine = parent != null; + else + paintLine = parent != null && parent.getPathCount() > 1; + if (paintLine) { Rectangle parentBounds = getPathBounds(tree, parent); paintVerticalLine(g, tree, parentBounds.x + 2 * gap, diff --git a/libjava/classpath/javax/swing/plaf/basic/SharedUIDefaults.java b/libjava/classpath/javax/swing/plaf/basic/SharedUIDefaults.java index 47876491160..34278052bc1 100644 --- a/libjava/classpath/javax/swing/plaf/basic/SharedUIDefaults.java +++ b/libjava/classpath/javax/swing/plaf/basic/SharedUIDefaults.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Color; import java.util.HashMap; import javax.swing.UIManager; @@ -75,4 +76,16 @@ public class SharedUIDefaults } return o; } + + /** + * Returns a shared UI color. + * + * @param key the key + * + * @return the shared color instance + */ + static Color getColor(String key) + { + return (Color) get(key); + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java index 6a528de2b6b..265ea7ef6ae 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalComboBoxButton.java @@ -259,7 +259,9 @@ public class MetalComboBoxButton Component comp = renderer.getListCellRendererComponent(listBox, comboBox.getSelectedItem(), -1, false, false); comp.setFont(rendererPane.getFont()); - if (model.isArmed() && model.isPressed()) + + if ((model.isArmed() && model.isPressed()) + || (comboBox.isFocusOwner() && !comboBox.isPopupVisible())) { if (isOpaque()) { diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java index 1219ad9fd11..824f1d8021b 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java @@ -42,6 +42,7 @@ import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; +import java.awt.Graphics; import java.awt.GridLayout; import java.awt.Insets; import java.awt.LayoutManager; @@ -303,8 +304,9 @@ public class MetalFileChooserUI if (file == null) setFileName(null); - else - setFileName(file.getName()); + else if (file.isFile() || filechooser.getFileSelectionMode() + != JFileChooser.FILES_ONLY) + setFileName(file.getName()); int index = -1; index = getModel().indexOf(file); if (index >= 0) @@ -567,10 +569,17 @@ public class MetalFileChooserUI extends DefaultListCellRenderer { /** + * This is the icon that is displayed in the combobox. This wraps + * the standard icon and adds indendation. + */ + private IndentIcon indentIcon; + + /** * Creates a new renderer. */ public DirectoryComboBoxRenderer(JFileChooser fc) - { + { + indentIcon = new IndentIcon(); } /** @@ -586,31 +595,86 @@ public class MetalFileChooserUI * @return The list cell renderer. */ public Component getListCellRendererComponent(JList list, Object value, - int index, boolean isSelected, boolean cellHasFocus) + int index, + boolean isSelected, + boolean cellHasFocus) { - FileView fileView = getFileView(getFileChooser()); + super.getListCellRendererComponent(list, value, index, isSelected, + cellHasFocus); File file = (File) value; - setIcon(fileView.getIcon(file)); - setText(fileView.getName(file)); - - if (isSelected) - { - setBackground(list.getSelectionBackground()); - setForeground(list.getSelectionForeground()); - } - else - { - setBackground(list.getBackground()); - setForeground(list.getForeground()); - } + setText(getFileChooser().getName(file)); + + // Install indented icon. + Icon icon = getFileChooser().getIcon(file); + indentIcon.setIcon(icon); + int depth = directoryModel.getDepth(index); + indentIcon.setDepth(depth); + setIcon(indentIcon); - setEnabled(list.isEnabled()); - setFont(list.getFont()); return this; } } /** + * An icon that wraps another icon and adds indentation. + */ + class IndentIcon + implements Icon + { + + /** + * The indentation level. + */ + private static final int INDENT = 10; + + /** + * The wrapped icon. + */ + private Icon icon; + + /** + * The current depth. + */ + private int depth; + + /** + * Sets the icon to be wrapped. + * + * @param i the icon + */ + void setIcon(Icon i) + { + icon = i; + } + + /** + * Sets the indentation depth. + * + * @param d the depth to set + */ + void setDepth(int d) + { + depth = d; + } + + public int getIconHeight() + { + return icon.getIconHeight(); + } + + public int getIconWidth() + { + return icon.getIconWidth() + depth * INDENT; + } + + public void paintIcon(Component c, Graphics g, int x, int y) + { + icon.paintIcon(c, g, x + depth * INDENT, y); + } + + } + + /** * A renderer for the files and directories in the file chooser. */ protected class FileRenderer @@ -956,9 +1020,12 @@ public class MetalFileChooserUI { String text = editField.getText(); if (text != null && text != "" && !text.equals(fc.getName(editFile))) - if (editFile.renameTo(fc.getFileSystemView().createFileObject( - fc.getCurrentDirectory(), text))) + { + File f = fc.getFileSystemView(). + createFileObject(fc.getCurrentDirectory(), text); + if ( editFile.renameTo(f) ) rescanCurrentDirectory(fc); + } list.remove(editField); } startEditing = false; @@ -982,17 +1049,8 @@ public class MetalFileChooserUI */ public void actionPerformed(ActionEvent e) { - if (e.getActionCommand().equals("notify-field-accept")) + if (editField != null) completeEditing(); - else if (editField != null) - { - list.remove(editField); - startEditing = false; - editFile = null; - lastSelected = null; - editField = null; - list.repaint(); - } } } } @@ -1101,7 +1159,7 @@ public class MetalFileChooserUI lastSelected = selVal; if (f.isFile()) setFileName(path.substring(path.lastIndexOf("/") + 1)); - else if (fc.getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY) + else if (fc.getFileSelectionMode() != JFileChooser.FILES_ONLY) setFileName(path); } fileTable.repaint(); @@ -1171,16 +1229,8 @@ public class MetalFileChooserUI */ public void actionPerformed(ActionEvent e) { - if (e.getActionCommand().equals("notify-field-accept")) + if (editField != null) completeEditing(); - else if (editField != null) - { - table.remove(editField); - startEditing = false; - editFile = null; - editField = null; - table.repaint(); - } } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java b/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java index a317e3fc00d..30ec7e72b28 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalIconFactory.java @@ -1,5 +1,5 @@ /* MetalIconFactory.java -- - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -54,6 +54,7 @@ import javax.swing.JRadioButtonMenuItem; import javax.swing.JSlider; import javax.swing.SwingConstants; import javax.swing.UIManager; +import javax.swing.plaf.IconUIResource; import javax.swing.plaf.UIResource; @@ -78,7 +79,8 @@ public class MetalIconFactory implements Serializable /** * An icon displayed for {@link JCheckBoxMenuItem} components. */ - private static class CheckBoxMenuItemIcon implements Icon, Serializable + private static class CheckBoxMenuItemIcon + implements Icon, UIResource, Serializable { /** * Creates a new icon instance. @@ -153,7 +155,8 @@ public class MetalIconFactory implements Serializable * * @see MetalIconFactory#getFileChooserDetailViewIcon() */ - private static class FileChooserDetailViewIcon implements Icon, Serializable + private static class FileChooserDetailViewIcon + implements Icon, UIResource, Serializable { /** @@ -233,7 +236,8 @@ public class MetalIconFactory implements Serializable * * @see MetalIconFactory#getFileChooserHomeFolderIcon() */ - private static class FileChooserHomeFolderIcon implements Icon, Serializable + private static class FileChooserHomeFolderIcon + implements Icon, UIResource, Serializable { /** @@ -322,7 +326,8 @@ public class MetalIconFactory implements Serializable * * @see MetalIconFactory#getFileChooserListViewIcon() */ - private static class FileChooserListViewIcon implements Icon, Serializable + private static class FileChooserListViewIcon + implements Icon, UIResource, Serializable { /** * Creates a new icon. @@ -418,7 +423,8 @@ public class MetalIconFactory implements Serializable * * @see MetalIconFactory#getFileChooserNewFolderIcon() */ - private static class FileChooserNewFolderIcon implements Icon, Serializable + private static class FileChooserNewFolderIcon + implements Icon, UIResource, Serializable { /** * Creates a new icon. @@ -490,8 +496,7 @@ public class MetalIconFactory implements Serializable * * @see MetalIconFactory#getFileChooserNewFolderIcon() */ - private static class FileChooserUpFolderIcon extends FileChooserNewFolderIcon - implements Icon, Serializable + private static class FileChooserUpFolderIcon extends FileChooserNewFolderIcon { /** * Creates a new icon. @@ -883,7 +888,8 @@ public class MetalIconFactory implements Serializable /** * An icon displayed for {@link JRadioButtonMenuItem} components. */ - private static class RadioButtonMenuItemIcon implements Icon, Serializable + private static class RadioButtonMenuItemIcon + implements Icon, UIResource, Serializable { /** * Creates a new icon instance. @@ -960,7 +966,8 @@ 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, UIResource, Serializable { /** @@ -1102,7 +1109,8 @@ 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, UIResource, Serializable { /** The icon size in pixels. */ private int size; @@ -1219,7 +1227,7 @@ public class MetalIconFactory implements Serializable * The icon displayed at the top-left corner of a {@link JInternalFrame}. */ private static class InternalFrameDefaultMenuIcon - implements Icon, Serializable + implements Icon, UIResource, Serializable { /** @@ -1291,7 +1299,7 @@ public class MetalIconFactory implements Serializable * provide a 'restore' option. */ private static class InternalFrameAltMaximizeIcon - implements Icon, Serializable + implements Icon, UIResource, Serializable { /** The icon size in pixels. */ private int size; @@ -1401,7 +1409,8 @@ 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, UIResource, Serializable { /** @@ -1513,7 +1522,8 @@ public class MetalIconFactory implements Serializable /** * An icon used in the title frame of a {@link JInternalFrame}. */ - private static class InternalFrameMinimizeIcon implements Icon, Serializable + private static class InternalFrameMinimizeIcon + implements Icon, UIResource, Serializable { /** @@ -1617,7 +1627,8 @@ 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, UIResource, Serializable { /** * This mask is used to paint the gradient in the shape of the thumb. @@ -1801,78 +1812,36 @@ public class MetalIconFactory implements Serializable /** * Paints the icon at the location (x, y). * - * @param c the component. - * @param g the graphics device. - * @param x the x coordinate. - * @param y the y coordinate. + * @param c the component. + * @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) + public void paintIcon(Component c, Graphics g, int x, int y) { - x = x + 5; - y = y + 5; - if (collapsed) - { - // TODO: pick up appropriate UI colors - g.setColor(Color.black); - g.drawLine(x + 2, y, x + 5, y); - g.drawLine(x + 6, y + 1, x + 7, y + 2); - g.fillRect(x + 7, y + 3, 5, 2); - g.drawLine(x + 7, y + 5, x + 6, y + 6); - g.drawLine(x + 1, y + 1, x + 1, y + 1); - g.drawLine(x, y + 2, x, y + 5); - g.drawLine(x + 1, y + 6, x + 1, y + 6); - g.drawLine(x + 2, y + 7, x + 5, y + 7); - g.fillRect(x + 3, y + 3, 2, 2); - - g.setColor(new Color(204, 204, 255)); - g.drawLine(x + 3, y + 2, x + 4, y + 2); - g.drawLine(x + 2, y + 3, x + 2, y + 4); - g.drawLine(x + 3, y + 5, x + 3, y + 5); - g.drawLine(x + 5, y + 3, x + 5, y + 3); - - g.setColor(new Color(153, 153, 204)); - g.drawLine(x + 2, y + 2, x + 2, y + 2); - g.drawLine(x + 2, y + 5, x + 2, y + 5); - g.drawLine(x + 2, y + 6, x + 5, y + 6); - g.drawLine(x + 5, y + 2, x + 5, y + 2); - g.drawLine(x + 6, y + 2, x + 6, y + 5); - - g.setColor(new Color(102, 102, 153)); - g.drawLine(x + 2, y + 1, x + 5, y + 1); - g.drawLine(x + 1, y + 2, x + 1, y + 5); - } + // TODO: pick up appropriate UI colors + Color dark = new Color(99, 130, 191); + Color light = new Color(163, 184, 204); + Color white = Color.white; + + x += 8; + y += 6; + + final int w = 6; + final int wHalf = (w >> 2); + g.setColor(light); + g.drawOval(x, y, w, w); + g.setColor(dark); + g.fillOval(x + 1, y + 1, w - 1, w - 1); + + if (collapsed) + g.fillRect(x + w, y + wHalf + 1, w, 2); else - { - // TODO: pick up appropriate UI colors - g.setColor(Color.black); - g.drawLine(x + 2, y, x + 5, y); - g.drawLine(x + 6, y + 1, x + 7, y + 2); - g.drawLine(x + 7, y + 2, x + 7, y + 5); - g.fillRect(x + 3, y + 7, 2, 5); - g.drawLine(x + 7, y + 5, x + 6, y + 6); - g.drawLine(x + 1, y + 1, x + 1, y + 1); - g.drawLine(x, y + 2, x, y + 5); - g.drawLine(x + 1, y + 6, x + 1, y + 6); - g.drawLine(x + 2, y + 7, x + 5, y + 7); - g.fillRect(x + 3, y + 3, 2, 2); - - g.setColor(new Color(204, 204, 255)); - g.drawLine(x + 3, y + 2, x + 4, y + 2); - g.drawLine(x + 2, y + 3, x + 2, y + 4); - g.drawLine(x + 3, y + 5, x + 3, y + 5); - g.drawLine(x + 5, y + 3, x + 5, y + 3); - - g.setColor(new Color(153, 153, 204)); - g.drawLine(x + 2, y + 2, x + 2, y + 2); - g.drawLine(x + 2, y + 5, x + 2, y + 5); - g.drawLine(x + 2, y + 6, x + 5, y + 6); - g.drawLine(x + 5, y + 2, x + 5, y + 2); - g.drawLine(x + 6, y + 2, x + 6, y + 5); - - g.setColor(new Color(102, 102, 153)); - g.drawLine(x + 2, y + 1, x + 5, y + 1); - g.drawLine(x + 1, y + 2, x + 1, y + 5); - } + g.fillRect(x + wHalf + 1, y + w, 2, w); + + g.setColor(white); + g.fillRect(x + wHalf + 1, y + wHalf + 1, 2, 2); + } /** @@ -1964,7 +1933,8 @@ public class MetalIconFactory implements Serializable * * @see MetalIconFactory#getTreeHardDriveIcon() */ - private static class TreeHardDriveIcon implements Icon, Serializable + private static class TreeHardDriveIcon + implements Icon, UIResource, Serializable { /** @@ -2074,7 +2044,8 @@ public class MetalIconFactory implements Serializable * * @see MetalIconFactory#getTreeFloppyDriveIcon() */ - private static class TreeFloppyDriveIcon implements Icon, Serializable + private static class TreeFloppyDriveIcon + implements Icon, UIResource, Serializable { /** @@ -2150,7 +2121,8 @@ public class MetalIconFactory implements Serializable * * @see MetalIconFactory#getTreeComputerIcon() */ - private static class TreeComputerIcon implements Icon, Serializable + private static class TreeComputerIcon + implements Icon, UIResource, Serializable { /** @@ -2255,6 +2227,12 @@ public class MetalIconFactory implements Serializable /** The icon instance returned by {@link #getTreeHardDriveIcon()}. */ private static Icon treeHardDriveIcon; + /** The icon instance returned by {@link #getHorizontalSliderThumbIcon()}. */ + private static Icon horizontalSliderThumbIcon; + + /** The icon instance returned by {@link #getVerticalSliderThumbIcon()}. */ + private static Icon verticalSliderThumbIcon; + /** * Creates a new instance. All the methods are static, so creating an * instance isn't necessary. @@ -2383,7 +2361,9 @@ public class MetalIconFactory implements Serializable */ public static Icon getHorizontalSliderThumbIcon() { - return new HorizontalSliderThumbIcon(); + if (horizontalSliderThumbIcon == null) + horizontalSliderThumbIcon = new HorizontalSliderThumbIcon(); + return horizontalSliderThumbIcon; } /** @@ -2462,7 +2442,9 @@ public class MetalIconFactory implements Serializable */ public static Icon getVerticalSliderThumbIcon() { - return new VerticalSliderThumbIcon(); + if (verticalSliderThumbIcon == null) + verticalSliderThumbIcon = new VerticalSliderThumbIcon(); + return verticalSliderThumbIcon; } /** diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java b/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java index 09eafd40fe9..8a5a61107c1 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java @@ -85,7 +85,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel } /** - * Sets the current theme to a new instance of {@link DefaultMetalTheme}. + * Sets the current theme to a new instance of {@link OceanTheme}. */ protected void createDefaultTheme() { @@ -709,6 +709,8 @@ public class MetalLookAndFeel extends BasicLookAndFeel * @param theme the theme (<code>null</code> not permitted). * * @throws NullPointerException if <code>theme</code> is <code>null</code>. + * + * @see #getCurrentTheme() */ public static void setCurrentTheme(MetalTheme theme) { @@ -1183,20 +1185,26 @@ public class MetalLookAndFeel extends BasicLookAndFeel "SplitPaneDivider.draggingColor", Color.DARK_GRAY, "TabbedPane.background", getControlShadow(), + "TabbedPane.contentBorderInsets", new InsetsUIResource(2, 2, 3, 3), + "TabbedPane.contentOpaque", Boolean.TRUE, "TabbedPane.darkShadow", getControlDarkShadow(), "TabbedPane.focus", getPrimaryControlDarkShadow(), "TabbedPane.font", new FontUIResource("Dialog", Font.BOLD, 12), "TabbedPane.foreground", getControlTextColor(), "TabbedPane.highlight", getControlHighlight(), "TabbedPane.light", getControl(), - "TabbedPane.selected", getControl(), + "TabbedPane.selected", getControl(), // overridden in OceanTheme "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), + "TabbedPane.tabAreaBackground", getControl(), // overridden in OceanTheme + "TabbedPane.tabAreaInsets", new InsetsUIResource(4, 2, 0, 6), // dito "TabbedPane.tabInsets", new InsetsUIResource(0, 9, 1, 9), + // new properties in OceanTheme: + // TabbedPane.contentAreaColor + // TabbedPane.unselectedBackground + "Table.background", getWindowBackground(), "Table.focusCellBackground", getWindowBackground(), "Table.focusCellForeground", getControlTextColor(), @@ -1243,6 +1251,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "TextPane.selectionBackground", getTextHighlightColor(), "TextPane.selectionForeground", getHighlightedTextColor(), + "TitledBorder.border", new LineBorderUIResource(getPrimaryControl(), 1), "TitledBorder.font", new FontUIResource("Dialog", Font.BOLD, 12), "TitledBorder.titleColor", getSystemTextColor(), @@ -1335,12 +1344,17 @@ public class MetalLookAndFeel extends BasicLookAndFeel } /** - * Returns the current theme setting for the Metal L&F. + * Returns the current theme for the Metal look and feel. The default is + * an instance of {@link OceanTheme}. * - * @return the current theme setting for the Metal L&F + * @return The current theme (never <code>null</code>). + * + * @see #setCurrentTheme(MetalTheme) */ public static MetalTheme getCurrentTheme() { + if (theme == null) + theme = new OceanTheme(); return theme; } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalMenuBarUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalMenuBarUI.java index 31d8d671fa1..ff763ea9da9 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalMenuBarUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalMenuBarUI.java @@ -44,6 +44,7 @@ import javax.swing.JComponent; import javax.swing.SwingConstants; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicMenuBarUI; /** @@ -75,7 +76,9 @@ public class MetalMenuBarUI extends BasicMenuBarUI */ public void update(Graphics g, JComponent c) { - if (c.isOpaque() && UIManager.get("MenuBar.gradient") != null) + if (c.isOpaque() + && UIManager.get("MenuBar.gradient") != null + && c.getBackground() instanceof UIResource) { MetalUtils.paintGradient(g, 0, 0, c.getWidth(), c.getHeight(), SwingConstants.VERTICAL, "MenuBar.gradient"); diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java b/libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java index 84f9cfe494e..a55dc091665 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalScrollButton.java @@ -84,6 +84,7 @@ public class MetalScrollButton extends BasicArrowButton super(direction); buttonWidth = width; this.freeStanding = freeStanding; + setFocusable(false); } /** diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java index c49abe832e4..20135fc857e 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalTabbedPaneUI.java @@ -1,5 +1,5 @@ /* MetalTabbedPaneUI.java - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -170,7 +170,9 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI */ protected LayoutManager createLayoutManager() { - return new TabbedPaneLayout(); + return (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT) + ? new MetalTabbedPaneUI.TabbedPaneLayout() + : super.createLayoutManager(); } /** @@ -326,7 +328,6 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI int bottom = h - 1; int right = w - 1; - int tabCount = tabPane.getTabCount(); int currentRun = getRunForTab(tabCount, tabIndex); int firstIndex = tabRuns[currentRun]; @@ -396,14 +397,17 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI { if (tabPane.getSelectedIndex() == tabIndex - 1) { - g.drawLine(0, 5, 0, bottom); - g.setColor(oceanSelectedBorder); - g.drawLine(0, 0, 0, 5); + g.drawLine(0, 6, 0, bottom); + if (tabIndex != firstIndex) + { + g.setColor(oceanSelectedBorder); + g.drawLine(0, 0, 0, 5); + } } else if (isSelected) { g.drawLine(0, 5, 0, bottom); - if (tabIndex != 0) + if (tabIndex != firstIndex) { g.setColor(darkShadow); g.drawLine(0, 0, 0, 5); @@ -463,9 +467,10 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI { Color c; if (tabPane.getSelectedIndex() == tabIndex - 1) - c = UIManager.getColor("TabbedPane.tabAreaBackground"); + c = selectColor; else c = getUnselectedBackground(tabIndex - 1); + g.setColor(c); g.fillRect(right - 5, 0, 5, 3); g.fillRect(right - 2, 3, 2, 2); } @@ -522,10 +527,13 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI } else if (isOcean && tabPane.getSelectedIndex() == tabIndex - 1) { - g.setColor(oceanSelectedBorder); - g.drawLine(right, 0, right, 6); + if (tabIndex != firstIndex) + { + g.setColor(oceanSelectedBorder); + g.drawLine(right, 0, right, 6); + } g.setColor(darkShadow); - g.drawLine(right, 6, right, bottom); + g.drawLine(right, 7, right, bottom); } else if (tabIndex != firstIndex) { @@ -598,8 +606,10 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI if (isOcean && isSelected) { g.drawLine(0, 0, 0, bottom - 5); - if ((currentRun == 0 && tabIndex != 0) - || (currentRun > 0 && tabIndex != tabRuns[currentRun - 1])) + + // Paint a connecting line to the tab below for all + // but the first tab in the last run. + if (tabIndex != tabRuns[runCount-1]) { g.setColor(darkShadow); g.drawLine(0, bottom - 5, 0, bottom); @@ -688,6 +698,103 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI } /** + * This method paints the focus rectangle around the selected tab. + * + * @param g The Graphics object to paint with. + * @param tabPlacement The JTabbedPane's tab placement. + * @param rects The array of rectangles keeping track of size and position. + * @param tabIndex The tab index. + * @param iconRect The icon bounds. + * @param textRect The text bounds. + * @param isSelected Whether this tab is selected. + */ + protected void paintFocusIndicator(Graphics g, int tabPlacement, + Rectangle[] rects, int tabIndex, + Rectangle iconRect, Rectangle textRect, + boolean isSelected) + { + if (tabPane.hasFocus() && isSelected) + { + Rectangle rect = rects[tabIndex]; + + g.setColor(focus); + g.translate(rect.x, rect.y); + + switch (tabPlacement) + { + case LEFT: + // Top line + g.drawLine(7, 2, rect.width-2, 2); + + // Right line + g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3); + + // Bottom line + g.drawLine(rect.width-2, rect.height-2, 3, rect.height-2); + + // Left line + g.drawLine(2, rect.height-3, 2, 7); + + // Slant + g.drawLine(2, 6, 6, 2); + break; + case RIGHT: + // Top line + g.drawLine(1, 2, rect.width-8, 2); + + // Slant + g.drawLine(rect.width-7, 2, rect.width-3, 6); + + // Right line + g.drawLine(rect.width-3, 7, rect.width-3, rect.height-3); + + // Bottom line + g.drawLine(rect.width-3, rect.height-2, 2, rect.height-2); + + // Left line + g.drawLine(1, rect.height-2, 1, 2); + break; + case BOTTOM: + // Top line + g.drawLine(2, 1, rect.width-2, 1); + + // Right line + g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3); + + // Bottom line + g.drawLine(7, rect.height-3, rect.width-2, rect.height-3); + + // Slant + g.drawLine(6, rect.height-3, 2, rect.height-7); + + // Left line + g.drawLine(2, rect.height-8, 2, 2); + + break; + case TOP: + default: + // Top line + g.drawLine(6, 2, rect.width-2, 2); + + // Right line + g.drawLine(rect.width-1, 2, rect.width-1, rect.height-3); + + // Bottom line + g.drawLine(3, rect.height-3, rect.width-2, rect.height-3); + + // Left line + g.drawLine(2, rect.height-3, 2, 7); + + // Slant + g.drawLine(2, 6, 6, 2); + + } + + g.translate(-rect.x, -rect.y); + } + } + + /** * Returns <code>true</code> if the tabs in the specified run should be * padded to make the run fill the width/height of the {@link JTabbedPane}. * @@ -1144,4 +1251,19 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI bg = unselectedBackground; return bg; } + + protected int getTabLabelShiftX(int tabPlacement, + int index, + boolean isSelected) + { + return 0; + } + + protected int getTabLabelShiftY(int tabPlacement, + int index, + boolean isSelected) + { + return 0; + } + } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java b/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java index 72cbb34a6dc..0c3a38d5cc3 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java @@ -41,6 +41,7 @@ import gnu.classpath.SystemProperties; import java.awt.Color; import java.awt.Component; +import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.TexturePaint; @@ -106,7 +107,7 @@ class MetalUtils for (int mX = x + xOff; mX < (x + w); mX += 4) { - g.drawLine(mX, mY, mX, mY); + g.fillRect(mX, mY, 1, 1); } // increase x offset @@ -305,6 +306,15 @@ class MetalUtils float g1, float g2, Color c1, Color c2, Color c3, int[][] mask) { + + if (g instanceof Graphics2D + && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D") == null) + { + paintHorizontalGradient2D((Graphics2D) g, x, y, w, h, g1, g2, c1, c2, + c3, mask); + return; + } + // Calculate the coordinates. int y0 = y; int y1 = y + h; @@ -339,7 +349,7 @@ class MetalUtils y0 = mask[xc - x0][0] + y; y1 = mask[xc - x0][1] + y; } - g.drawLine(xc, y0, xc, y1); + g.fillRect(xc, y0, 1, y1 - y0); } // Paint solid c2 area. g.setColor(c2); @@ -353,7 +363,7 @@ class MetalUtils { y0 = mask[xc - x0][0] + y; y1 = mask[xc - x0][1] + y; - g.drawLine(xc, y0, xc, y1); + g.fillRect(xc, y0, 1, y1 - y0); } } @@ -377,7 +387,7 @@ class MetalUtils y0 = mask[xc - x0][0] + y; y1 = mask[xc - x0][1] + y; } - g.drawLine(xc, y0, xc, y1); + g.fillRect(xc, y0, 1, y1 - y0); } // Paint third gradient area (c1->c3). @@ -421,9 +431,17 @@ class MetalUtils * described above */ static void paintVerticalGradient(Graphics g, int x, int y, int w, int h, - double g1, double g2, Color c1, Color c2, + float g1, float g2, Color c1, Color c2, Color c3, int[][] mask) { + if (g instanceof Graphics2D + && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D") == null) + { + paintVerticalGradient2D((Graphics2D) g, x, y, w, h, g1, g2, c1, c2, + c3, mask); + return; + } + // Calculate the coordinates. int x0 = x; int x1 = x + w; @@ -458,7 +476,7 @@ class MetalUtils x0 = mask[yc - y0][0] + x; x1 = mask[yc - y0][1] + x; } - g.drawLine(x0, yc, x1, yc); + g.fillRect(x0, yc, x1 - x0, 1); } // Paint solid c2 area. g.setColor(c2); @@ -472,7 +490,7 @@ class MetalUtils { x0 = mask[yc - y0][0] + x; x1 = mask[yc - y0][1] + x; - g.drawLine(x0, yc, x1, yc); + g.fillRect(x0, yc, x1 - x0, 1); } } @@ -496,7 +514,7 @@ class MetalUtils x0 = mask[yc - y0][0] + x; x1 = mask[yc - y0][1] + x; } - g.drawLine(x0, yc, x1, yc); + g.fillRect(x0, yc, x1 - x0, 1); } // Paint third gradient area (c1->c3). @@ -519,7 +537,61 @@ class MetalUtils x0 = mask[yc - y0][0] + x; x1 = mask[yc - y0][1] + x; } - g.drawLine(x0, yc, x1, yc); + g.fillRect(x0, yc, x1 - x0, 1); } } + + /** + * Paints a horizontal gradient using Graphics2D functionality. + * + * @param g the Graphics2D instance + * @param x the X coordinate of the upper left corner of the rectangle + * @param y the Y coordinate of the upper left corner of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param g1 the relative width of the c1->c2 gradients + * @param g2 the relative width of the c2 solid area + * @param c1 the color 1 + * @param c2 the color 2 + * @param c3 the color 3 + * @param mask the mask that should be used when painting the gradient as + * described above + */ + private static void paintHorizontalGradient2D(Graphics2D g, int x, int y, + int w, int h, float g1, + float g2, Color c1, + Color c2, Color c3, + int[][] mask) + { + // FIXME: Handle the mask somehow, or do Graphics2D clipping instead. + GradientPaint p1 = new GradientPaint(x, y, c1, x + w * g1, y, c2); + g.setPaint(p1); + // This fills the first gradient and the solid area in one go. + g.fillRect(x, y, (int) (w * (g1 + g2)), h); + + GradientPaint p2 = new GradientPaint(x + (w * (g1 + g2)), y, c2, x + w, y, + c3); + g.setPaint(p2); + g.fillRect((int) (x + (w * (g1 + g2))), y, + (int) (w * (1. - (g1 + g2))), h); + } + + private static void paintVerticalGradient2D(Graphics2D g, int x, int y, + int w, int h, float g1, + float g2, Color c1, + Color c2, Color c3, + int[][] mask) + { + // FIXME: Handle the mask somehow, or do Graphics2D clipping instead. + GradientPaint p1 = new GradientPaint(x, y, c1, x, y + h * g1, c2); + g.setPaint(p1); + // This fills the first gradient and the solid area in one go. + g.fillRect(x, y, w, (int) (h * (g1 + g2))); + + GradientPaint p2 = new GradientPaint(x, y + (h * (g1 + g2)), c2, x, y + h, + c3); + g.setPaint(p2); + g.fillRect(x, (int) (y + (h * (g1 + g2))), w, + (int) (h * (1. - (g1 + g2)))); + } } diff --git a/libjava/classpath/javax/swing/table/DefaultTableColumnModel.java b/libjava/classpath/javax/swing/table/DefaultTableColumnModel.java index 24ac8fc9c39..33e68ea9fcd 100644 --- a/libjava/classpath/javax/swing/table/DefaultTableColumnModel.java +++ b/libjava/classpath/javax/swing/table/DefaultTableColumnModel.java @@ -91,10 +91,10 @@ public class DefaultTableColumnModel /** * A change event used when notifying listeners of a change to the * <code>columnMargin</code> field. This single event is reused for all - * notifications. + * notifications (it is lazily instantiated within the + * {@link #fireColumnMarginChanged()} method). */ - // FIXME: use lazy instantiation - protected transient ChangeEvent changeEvent = new ChangeEvent(this); + protected transient ChangeEvent changeEvent; /** * A flag that indicates whether or not columns can be selected. @@ -580,7 +580,9 @@ public class DefaultTableColumnModel */ protected void fireColumnMarginChanged() { - EventListener [] listeners = getListeners(TableColumnModelListener.class); + EventListener[] listeners = getListeners(TableColumnModelListener.class); + if (changeEvent == null && listeners.length > 0) + changeEvent = new ChangeEvent(this); for (int i = 0; i < listeners.length; ++i) ((TableColumnModelListener) listeners[i]).columnMarginChanged(changeEvent); } diff --git a/libjava/classpath/javax/swing/text/AbstractDocument.java b/libjava/classpath/javax/swing/text/AbstractDocument.java index 1ef81732fed..eb46a8c42f6 100644 --- a/libjava/classpath/javax/swing/text/AbstractDocument.java +++ b/libjava/classpath/javax/swing/text/AbstractDocument.java @@ -147,14 +147,19 @@ public abstract class AbstractDocument implements Document, Serializable /** * A condition variable that readers and writers wait on. */ - Object documentCV = new Object(); + private Object documentCV = new Object(); /** An instance of a DocumentFilter.FilterBypass which allows calling * the insert, remove and replace method without checking for an installed * document filter. */ - DocumentFilter.FilterBypass bypass; - + private DocumentFilter.FilterBypass bypass; + + /** + * The bidi root element. + */ + private Element bidiRoot; + /** * Creates a new <code>AbstractDocument</code> with the specified * {@link Content} model. @@ -185,6 +190,13 @@ public abstract class AbstractDocument implements Document, Serializable { content = doc; context = ctx; + + // FIXME: This is determined using a Mauve test. Make the document + // actually use this. + putProperty("i18n", Boolean.FALSE); + + // FIXME: Fully implement bidi. + bidiRoot = new BranchElement(null, null); } /** Returns the DocumentFilter.FilterBypass instance for this @@ -360,7 +372,7 @@ public abstract class AbstractDocument implements Document, Serializable */ public Element getBidiRootElement() { - return null; + return bidiRoot; } /** @@ -477,8 +489,9 @@ public abstract class AbstractDocument implements Document, Serializable */ public Element[] getRootElements() { - Element[] elements = new Element[1]; + Element[] elements = new Element[2]; elements[0] = getDefaultRootElement(); + elements[1] = getBidiRootElement(); return elements; } @@ -739,29 +752,36 @@ public abstract class AbstractDocument implements Document, Serializable void removeImpl(int offset, int length) throws BadLocationException { - // Prevent some unneccessary method invocation (observed in the RI). - if (length <= 0) - return; - - DefaultDocumentEvent event = - new DefaultDocumentEvent(offset, length, - DocumentEvent.EventType.REMOVE); - - try + // The RI silently ignores all requests that have a negative length. + // Don't ask my why, but that's how it is. + if (length > 0) { - writeLock(); + if (offset < 0 || offset > getLength()) + throw new BadLocationException("Invalid remove position", offset); + + if (offset + length > getLength()) + throw new BadLocationException("Invalid remove length", offset); + + DefaultDocumentEvent event = + new DefaultDocumentEvent(offset, length, + DocumentEvent.EventType.REMOVE); + + try + { + writeLock(); - // The order of the operations below is critical! - removeUpdate(event); - UndoableEdit temp = content.remove(offset, length); + // The order of the operations below is critical! + removeUpdate(event); + UndoableEdit temp = content.remove(offset, length); - postRemoveUpdate(event); - fireRemoveUpdate(event); + postRemoveUpdate(event); + fireRemoveUpdate(event); + } + finally + { + writeUnlock(); + } } - finally - { - writeUnlock(); - } } /** @@ -1674,20 +1694,15 @@ public abstract class AbstractDocument implements Document, Serializable /** 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]; - /** - * The cached startOffset value. This is used in the case when a - * BranchElement (temporarily) has no child elements. + * The child elements of this BranchElement. */ - private int startOffset; + private Element[] children;; /** - * The cached endOffset value. This is used in the case when a - * BranchElement (temporarily) has no child elements. + * The number of children in the branch element. */ - private int endOffset; + private int numChildren; /** * Creates a new <code>BranchElement</code> with the specified @@ -1700,8 +1715,8 @@ public abstract class AbstractDocument implements Document, Serializable public BranchElement(Element parent, AttributeSet attributes) { super(parent, attributes); - startOffset = -1; - endOffset = -1; + children = new Element[1]; + numChildren = 0; } /** @@ -1716,8 +1731,8 @@ public abstract class AbstractDocument implements Document, Serializable Vector tmp = new Vector(); - for (int index = 0; index < children.length; ++index) - tmp.add(children[index]); + for (int index = 0; index < numChildren; ++index) + tmp.add(children[index]); return tmp.elements(); } @@ -1743,8 +1758,8 @@ public abstract class AbstractDocument implements Document, Serializable */ public Element getElement(int index) { - if (index < 0 || index >= children.length) - return null; + if (index < 0 || index >= numChildren) + return null; return children[index]; } @@ -1756,7 +1771,7 @@ public abstract class AbstractDocument implements Document, Serializable */ public int getElementCount() { - return children.length; + return numChildren; } /** @@ -1777,7 +1792,7 @@ public abstract class AbstractDocument implements Document, Serializable // XXX: There is surely a better algorithm // as beginning from first element each time. - for (int index = 0; index < children.length - 1; ++index) + for (int index = 0; index < numChildren - 1; ++index) { Element elem = children[index]; @@ -1814,15 +1829,11 @@ public abstract class AbstractDocument implements Document, Serializable */ public int getEndOffset() { - if (children.length == 0) - { - if (endOffset == -1) - throw new NullPointerException("BranchElement has no children."); - } - else - endOffset = children[children.length - 1].getEndOffset(); - - return endOffset; + // This might accss one cached element or trigger an NPE for + // numChildren == 0. This is checked by a Mauve test. + Element child = numChildren > 0 ? children[numChildren - 1] + : children[0]; + return child.getEndOffset(); } /** @@ -1848,15 +1859,13 @@ public abstract class AbstractDocument implements Document, Serializable */ public int getStartOffset() { - if (children.length == 0) - { - if (startOffset == -1) - throw new NullPointerException("BranchElement has no children."); - } - else - startOffset = children[0].getStartOffset(); - - return startOffset; + // Do not explicitly throw an NPE here. If the first element is null + // then the NPE gets thrown anyway. If it isn't, then it either + // holds a real value (for numChildren > 0) or a cached value + // (for numChildren == 0) as we don't fully remove elements in replace() + // when removing single elements. + // This is checked by a Mauve test. + return children[0].getStartOffset(); } /** @@ -1884,7 +1893,7 @@ public abstract class AbstractDocument implements Document, Serializable { // 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 < numChildren; ++index) { Element elem = children[index]; @@ -1905,14 +1914,26 @@ public abstract class AbstractDocument implements Document, Serializable */ public void replace(int offset, int length, Element[] elements) { - Element[] target = new Element[children.length - length - + elements.length]; - System.arraycopy(children, 0, target, 0, offset); - System.arraycopy(elements, 0, target, offset, elements.length); - System.arraycopy(children, offset + length, target, - offset + elements.length, - children.length - offset - length); - children = target; + int delta = elements.length - length; + int copyFrom = offset + length; // From where to copy. + int copyTo = copyFrom + delta; // Where to copy to. + int numMove = numChildren - copyFrom; // How many elements are moved. + if (numChildren + delta > children.length) + { + // Gotta grow the array. + int newSize = Math.max(2 * children.length, numChildren + delta); + Element[] target = new Element[newSize]; + System.arraycopy(children, 0, target, 0, offset); + System.arraycopy(elements, 0, target, offset, elements.length); + System.arraycopy(children, copyFrom, target, copyTo, numMove); + children = target; + } + else + { + System.arraycopy(children, copyFrom, children, copyTo, numMove); + System.arraycopy(elements, 0, children, offset, elements.length); + } + numChildren += delta; } /** @@ -2165,18 +2186,6 @@ public abstract class AbstractDocument implements Document, Serializable private Position endPos; /** - * This gets possible added to the startOffset when a startOffset - * outside the document range is requested. - */ - private int startDelta; - - /** - * This gets possible added to the endOffset when a endOffset - * outside the document range is requested. - */ - private int endDelta; - - /** * Creates a new <code>LeafElement</code>. * * @param parent the parent of this <code>LeafElement</code> @@ -2188,28 +2197,21 @@ public abstract class AbstractDocument implements Document, Serializable int end) { super(parent, attributes); - int len = content.length(); - startDelta = 0; - if (start > len) - startDelta = start - len; - endDelta = 0; - if (end > len) - endDelta = end - len; try - { - startPos = createPosition(start - startDelta); - endPos = createPosition(end - endDelta); - } - catch (BadLocationException ex) - { - AssertionError as; - as = new AssertionError("BadLocationException thrown " - + "here. start=" + start - + ", end=" + end - + ", length=" + getLength()); - as.initCause(ex); - throw as; - } + { + startPos = createPosition(start); + endPos = createPosition(end); + } + catch (BadLocationException ex) + { + AssertionError as; + as = new AssertionError("BadLocationException thrown " + + "here. start=" + start + + ", end=" + end + + ", length=" + getLength()); + as.initCause(ex); + throw as; + } } /** @@ -2281,7 +2283,7 @@ public abstract class AbstractDocument implements Document, Serializable */ public int getEndOffset() { - return endPos.getOffset() + endDelta; + return endPos.getOffset(); } /** @@ -2307,7 +2309,7 @@ public abstract class AbstractDocument implements Document, Serializable */ public int getStartOffset() { - return startPos.getOffset() + startDelta; + return startPos.getOffset(); } /** diff --git a/libjava/classpath/javax/swing/text/BoxView.java b/libjava/classpath/javax/swing/text/BoxView.java index a184a813152..27e3c0f9a1b 100644 --- a/libjava/classpath/javax/swing/text/BoxView.java +++ b/libjava/classpath/javax/swing/text/BoxView.java @@ -67,6 +67,11 @@ public class BoxView private boolean[] layoutValid = new boolean[2]; /** + * Indicates if the requirements for an axis are valid. + */ + private boolean[] requirementsValid = new boolean[2]; + + /** * The spans along the X_AXIS and Y_AXIS. */ private int[][] spans = new int[2][]; @@ -265,8 +270,10 @@ public class BoxView super.replace(offset, length, views); // Invalidate layout information. - layoutChanged(X_AXIS); - layoutChanged(Y_AXIS); + layoutValid[X_AXIS] = false; + requirementsValid[X_AXIS] = false; + layoutValid[Y_AXIS] = false; + requirementsValid[Y_AXIS] = false; } /** @@ -278,19 +285,26 @@ public class BoxView */ public void paint(Graphics g, Shape a) { - Rectangle inside = getInsideAllocation(a); - // TODO: Used for debugging. - //g.drawRect(inside.x, inside.y, inside.width, inside.height); + Rectangle alloc; + if (a instanceof Rectangle) + alloc = (Rectangle) a; + else + alloc = a.getBounds(); + + int x = alloc.x + getLeftInset(); + int y = alloc.y + getTopInset(); - Rectangle copy = new Rectangle(inside); + Rectangle clip = g.getClipBounds(); + Rectangle tmp = new Rectangle(); int count = getViewCount(); for (int i = 0; i < count; ++i) { - copy.setBounds(inside); - childAllocation(i, copy); - if (!copy.isEmpty() - && g.hitClip(copy.x, copy.y, copy.width, copy.height)) - paintChild(g, copy, i); + tmp.x = x + getOffset(X_AXIS, i); + tmp.y = y + getOffset(Y_AXIS, i); + tmp.width = getSpan(X_AXIS, i); + tmp.height = getSpan(Y_AXIS, i); + if (tmp.intersects(clip)) + paintChild(g, tmp, i); } } @@ -305,7 +319,13 @@ public class BoxView public float getPreferredSpan(int axis) { updateRequirements(axis); - return requirements[axis].preferred; + // Add margin. + float margin; + if (axis == X_AXIS) + margin = getLeftInset() + getRightInset(); + else + margin = getTopInset() + getBottomInset(); + return requirements[axis].preferred + margin; } /** @@ -319,12 +339,14 @@ public class BoxView */ public float getMaximumSpan(int axis) { - float max; - if (axis == myAxis) - max = getPreferredSpan(axis); + updateRequirements(axis); + // Add margin. + float margin; + if (axis == X_AXIS) + margin = getLeftInset() + getRightInset(); else - max = Integer.MAX_VALUE; - return max; + margin = getTopInset() + getBottomInset(); + return requirements[axis].maximum + margin; } /** @@ -341,7 +363,13 @@ public class BoxView public float getMinimumSpan(int axis) { updateRequirements(axis); - return requirements[axis].minimum; + // Add margin. + float margin; + if (axis == X_AXIS) + margin = getLeftInset() + getRightInset(); + else + margin = getTopInset() + getBottomInset(); + return requirements[axis].minimum + margin; } /** @@ -435,34 +463,29 @@ public class BoxView protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements sr) { - updateChildRequirements(axis); + SizeRequirements res = sr; + if (res == null) + res = new SizeRequirements(); - SizeRequirements result = sr; - if (result == null) - result = new SizeRequirements(); + float min = 0; + float pref = 0; + float max = 0; - long minimum = 0; - long preferred = 0; - long maximum = 0; - for (int i = 0; i < children.length; i++) + int n = getViewCount(); + for (int i = 0; i < n; i++) { - minimum += childReqs[axis][i].minimum; - preferred += childReqs[axis][i].preferred; - maximum += childReqs[axis][i].maximum; + View child = getView(i); + min += child.getMinimumSpan(axis); + pref = child.getPreferredSpan(axis); + max = child.getMaximumSpan(axis); } - // 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; - - result.minimum = (int) minimum; - result.preferred = (int) preferred; - result.maximum = (int) maximum; - result.alignment = 0.5F; - return result; + + res.minimum = (int) min; + res.preferred = (int) pref; + res.maximum = (int) max; + res.alignment = 0.5F; + + return res; } /** @@ -480,44 +503,24 @@ public class BoxView protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements sr) { - updateChildRequirements(axis); - SizeRequirements res = sr; if (res == null) res = new SizeRequirements(); - float minLeft = 0; - float minRight = 0; - float prefLeft = 0; - float prefRight = 0; - float maxLeft = 0; - float maxRight = 0; - for (int i = 0; i < childReqs[axis].length; i++) + res.minimum = 0; + res.preferred = 0; + res.maximum = 0; + res.alignment = 0.5F; + int n = getViewCount(); + for (int i = 0; i < n; i++) { - float myMinLeft = childReqs[axis][i].minimum * childReqs[axis][i].alignment; - float myMinRight = childReqs[axis][i].minimum - myMinLeft; - minLeft = Math.max(myMinLeft, minLeft); - minRight = Math.max(myMinRight, minRight); - float myPrefLeft = childReqs[axis][i].preferred * childReqs[axis][i].alignment; - float myPrefRight = childReqs[axis][i].preferred - myPrefLeft; - prefLeft = Math.max(myPrefLeft, prefLeft); - prefRight = Math.max(myPrefRight, prefRight); - float myMaxLeft = childReqs[axis][i].maximum * childReqs[axis][i].alignment; - float myMaxRight = childReqs[axis][i].maximum - myMaxLeft; - maxLeft = Math.max(myMaxLeft, maxLeft); - maxRight = Math.max(myMaxRight, maxRight); + View child = getView(i); + res.minimum = Math.max((int) child.getMinimumSpan(axis), res.minimum); + res.preferred = Math.max((int) child.getPreferredSpan(axis), + res.preferred); + res.maximum = Math.max((int) child.getMaximumSpan(axis), res.maximum); } - 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; - res.alignment = align; - res.maximum = maxSize; - res.preferred = prefSize; - res.minimum = minSize; return res; } @@ -697,15 +700,62 @@ public class BoxView protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { - updateChildRequirements(axis); - updateRequirements(axis); + // Set the spans to the preferred sizes. Determine the space + // that we have to adjust the sizes afterwards. + long sumPref = 0; + int n = getViewCount(); + for (int i = 0; i < n; i++) + { + View child = getView(i); + spans[i] = (int) child.getPreferredSpan(axis); + sumPref = spans[i]; + } - // Calculate the spans and offsets using the SizeRequirements uility - // methods. - SizeRequirements.calculateTiledPositions(targetSpan, requirements[axis], - childReqs[axis], - offsets, spans); + // Try to adjust the spans so that we fill the targetSpan. + long diff = targetSpan - sumPref; + float factor = 0.0F; + int[] diffs = null; + if (diff != 0) + { + long total = 0; + diffs = new int[n]; + for (int i = 0; i < n; i++) + { + View child = getView(i); + int span; + if (diff < 0) + { + span = (int) child.getMinimumSpan(axis); + diffs[i] = spans[i] - span; + } + else + { + span = (int) child.getMaximumSpan(axis); + diffs[i] = span - spans[i]; + } + total += span; + } + float maxAdjust = Math.abs(total - sumPref); + factor = diff / maxAdjust; + factor = Math.min(factor, 1.0F); + factor = Math.max(factor, -1.0F); + } + + // Actually perform adjustments. + int totalOffs = 0; + for (int i = 0; i < n; i++) + { + offsets[i] = totalOffs; + if (diff != 0) + { + float adjust = factor * diffs[i]; + spans[i] += Math.round(adjust); + } + // Avoid overflow here. + totalOffs = (int) Math.min((long) totalOffs + (long) spans[i], + Integer.MAX_VALUE); + } } /** @@ -720,14 +770,26 @@ public class BoxView protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { - updateChildRequirements(axis); - updateRequirements(axis); - - // Calculate the spans and offsets using the SizeRequirements uility - // methods. - SizeRequirements.calculateAlignedPositions(targetSpan, requirements[axis], - childReqs[axis], offsets, - spans); + int count = getViewCount(); + for (int i = 0; i < count; i++) + { + View child = getView(i); + int max = (int) child.getMaximumSpan(axis); + if (max < targetSpan) + {System.err.println("align: " + child); + // Align child when it can't be made as wide as the target span. + float align = child.getAlignment(axis); + offsets[i] = (int) ((targetSpan - max) * align); + spans[i] = max; + } + else + { + // Expand child to target width if possible. + int min = (int) child.getMinimumSpan(axis); + offsets[i] = 0; + spans[i] = Math.max(min, targetSpan); + } + } } /** @@ -822,15 +884,8 @@ public class BoxView */ public float getAlignment(int axis) { - float align; - if (axis == myAxis) - align = 0.5F; - else - { - updateRequirements(axis); - align = requirements[axis].alignment; - } - return align; + updateRequirements(axis); + return requirements[axis].alignment; } /** @@ -843,9 +898,15 @@ public class BoxView public void preferenceChanged(View child, boolean width, boolean height) { if (width) - layoutValid[X_AXIS] = false; + { + layoutValid[X_AXIS] = false; + requirementsValid[X_AXIS] = false; + } if (height) - layoutValid[Y_AXIS] = false; + { + layoutValid[Y_AXIS] = false; + requirementsValid[Y_AXIS] = false; + } super.preferenceChanged(child, width, height); } @@ -862,7 +923,7 @@ public class BoxView if (! isAllocationValid()) { Rectangle bounds = a.getBounds(); - layout(bounds.width, bounds.height); + setSize(bounds.width, bounds.height); } return super.modelToView(pos, a, bias); } @@ -963,7 +1024,7 @@ public class BoxView */ private void updateRequirements(int axis) { - if (! layoutValid[axis]) + if (! requirementsValid[axis]) { if (axis == myAxis) requirements[axis] = calculateMajorAxisRequirements(axis, @@ -971,6 +1032,7 @@ public class BoxView else requirements[axis] = calculateMinorAxisRequirements(axis, requirements[axis]); + requirementsValid[axis] = true; } } } diff --git a/libjava/classpath/javax/swing/text/CompositeView.java b/libjava/classpath/javax/swing/text/CompositeView.java index 17f13dbedd6..6f487b8981e 100644 --- a/libjava/classpath/javax/swing/text/CompositeView.java +++ b/libjava/classpath/javax/swing/text/CompositeView.java @@ -217,25 +217,43 @@ public abstract class CompositeView public Shape modelToView(int pos, Shape a, Position.Bias bias) throws BadLocationException { - int childIndex = getViewIndex(pos, bias); - if (childIndex == -1) - throw new BadLocationException("Position " + pos + " is not represented by view.", pos); - - Shape ret = null; - - View child = getView(childIndex); - Shape childAlloc = getChildAllocation(childIndex, a); - - if (childAlloc == null) - ret = createDefaultLocation(a, bias); - - Shape result = child.modelToView(pos, childAlloc, bias); - - if (result != null) - ret = result; - else - ret = createDefaultLocation(a, bias); + boolean backward = bias == Position.Bias.Backward; + int testpos = backward ? Math.max(0, pos - 1) : pos; + Shape ret = null; + if (! backward || testpos >= getStartOffset()) + { + int childIndex = getViewIndexAtPosition(testpos); + if (childIndex != -1 && childIndex < getViewCount()) + { + View child = getView(childIndex); + if (child != null && testpos >= child.getStartOffset() + && testpos < child.getEndOffset()) + { + Shape childAlloc = getChildAllocation(childIndex, a); + if (childAlloc != null) + { + ret = child.modelToView(pos, childAlloc, bias); + // Handle corner case. + if (ret == null && child.getEndOffset() == pos) + { + childIndex++; + if (childIndex < getViewCount()) + { + child = getView(childIndex); + childAlloc = getChildAllocation(childIndex, a); + ret = child.modelToView(pos, childAlloc, bias); + } + } + } + } + } + else + { + throw new BadLocationException("Position " + pos + + " is not represented by view.", pos); + } + } return ret; } @@ -378,7 +396,10 @@ public abstract class CompositeView { if (b == Position.Bias.Backward && pos != 0) pos -= 1; - return getViewIndexAtPosition(pos); + int i = -1; + if (pos >= getStartOffset() && pos < getEndOffset()) + i = getViewIndexAtPosition(pos); + return i; } /** @@ -446,9 +467,13 @@ public abstract class CompositeView */ protected View getViewAtPosition(int pos, Rectangle a) { + View view = null; int i = getViewIndexAtPosition(pos); - View view = children[i]; - childAllocation(i, a); + if (i >= 0 && i < getViewCount() && a != null) + { + view = getView(i); + childAllocation(i, a); + } return view; } @@ -464,17 +489,10 @@ public abstract class CompositeView */ protected int getViewIndexAtPosition(int 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; + // We have a 1:1 mapping of elements to views here, so we forward + // this to the element. + Element el = getElement(); + return el.getElementIndex(pos); } /** diff --git a/libjava/classpath/javax/swing/text/DefaultCaret.java b/libjava/classpath/javax/swing/text/DefaultCaret.java index 4ad204c00c9..84f47f120de 100644 --- a/libjava/classpath/javax/swing/text/DefaultCaret.java +++ b/libjava/classpath/javax/swing/text/DefaultCaret.java @@ -221,9 +221,12 @@ public class DefaultCaret extends Rectangle if (name.equals("document")) { Document oldDoc = (Document) e.getOldValue(); - oldDoc.removeDocumentListener(documentListener); + if (oldDoc != null) + oldDoc.removeDocumentListener(documentListener); + Document newDoc = (Document) e.getNewValue(); - newDoc.addDocumentListener(documentListener); + if (newDoc != null) + newDoc.addDocumentListener(documentListener); } else if (name.equals("editable")) { @@ -549,7 +552,6 @@ public class DefaultCaret extends Rectangle */ public void mousePressed(MouseEvent event) { - int button = event.getButton(); // The implementation assumes that consuming the event makes the AWT event // mechanism forget about this event instance and not transfer focus. @@ -562,23 +564,37 @@ public class DefaultCaret extends Rectangle // - a middle-click positions the caret and pastes the clipboard // contents. // - a middle-click when shift is held down is ignored - - if (button == MouseEvent.BUTTON1) - if (event.isShiftDown()) - moveCaret(event); - else - positionCaret(event); - else if(button == MouseEvent.BUTTON2) - if (event.isShiftDown()) - event.consume(); + + if (SwingUtilities.isLeftMouseButton(event)) + { + // Handle the caret. + if (event.isShiftDown() && getDot() != -1) + { + moveCaret(event); + } else { positionCaret(event); - + } + + // Handle the focus. + if (textComponent != null && textComponent.isEnabled() + && textComponent.isRequestFocusEnabled()) + { + textComponent.requestFocus(); + } + + // TODO: Handle double click for selecting words. + } + else if(event.getButton() == MouseEvent.BUTTON2) + { + // Special handling for X11-style pasting. + if (! event.isShiftDown()) + { + positionCaret(event); textComponent.paste(); } - else - event.consume(); + } } /** @@ -708,7 +724,11 @@ public class DefaultCaret extends Rectangle propertyChangeListener = new PropertyChangeHandler(); textComponent.addPropertyChangeListener(propertyChangeListener); documentListener = new DocumentHandler(); - textComponent.getDocument().addDocumentListener(documentListener); + + Document doc = textComponent.getDocument(); + if (doc != null) + doc.addDocumentListener(documentListener); + active = textComponent.isEditable() && textComponent.isEnabled(); repaint(); @@ -891,10 +911,10 @@ public class DefaultCaret extends Rectangle } catch (BadLocationException e) { - AssertionError ae; - ae = new AssertionError("Unexpected bad caret location: " + dot); - ae.initCause(e); - throw ae; + // Let's ignore that. This shouldn't really occur. But if it + // does (it seems that this happens when the model is mutating), + // it causes no real damage. Uncomment this for debugging. + // e.printStackTrace(); } if (rect == null) @@ -1128,10 +1148,10 @@ public class DefaultCaret extends Rectangle } catch (BadLocationException e) { - AssertionError ae; - ae = new AssertionError("Unexpected bad caret location: " + dot); - ae.initCause(e); - throw ae; + // Let's ignore that. This shouldn't really occur. But if it + // does (it seems that this happens when the model is mutating), + // it causes no real damage. Uncomment this for debugging. + // e.printStackTrace(); } if (area != null) damage(area); @@ -1140,6 +1160,24 @@ public class DefaultCaret extends Rectangle } /** + * Returns <code>true</code> if this <code>Caret</code> is blinking, + * and <code>false</code> if not. The returned value is independent of + * the visiblity of this <code>Caret</code> as returned by {@link #isVisible()}. + * + * @return <code>true</code> if this <code>Caret</code> is blinking, + * and <code>false</code> if not. + * @see #isVisible() + * @since 1.5 + */ + public boolean isActive() + { + if (blinkTimer != null) + return blinkTimer.isRunning(); + + return false; + } + + /** * Returns <code>true</code> if this <code>Caret</code> is currently visible, * and <code>false</code> if it is not. * diff --git a/libjava/classpath/javax/swing/text/EmptyAttributeSet.java b/libjava/classpath/javax/swing/text/EmptyAttributeSet.java new file mode 100644 index 00000000000..98fb8828c89 --- /dev/null +++ b/libjava/classpath/javax/swing/text/EmptyAttributeSet.java @@ -0,0 +1,153 @@ +/* EmptyAttributeSet.java -- An empty attribute set + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text; + +import java.util.Enumeration; +import java.util.NoSuchElementException; + +/** + * An immutable, empty attribute set. + * + * @see SimpleAttributeSet#EMPTY + * + * @author Roman Kennke (kennke@aicas.com) + */ +final class EmptyAttributeSet + implements AttributeSet +{ + + /** + * Always return false as this AttributeSet doesn't contain any attributes. + */ + public boolean containsAttribute(Object name, Object value) + { + return false; + } + + /** + * Return true only if the attributes argument also contains no attributes. + */ + public boolean containsAttributes(AttributeSet attributes) + { + return attributes.getAttributeCount() == 0; + } + + /** + * Return this, as this is immutable. + */ + public AttributeSet copyAttributes() + { + return this; + } + + /** + * Always return null as this AttributeSet doesn't contain any attributes. + */ + public Object getAttribute(Object key) + { + return null; + } + + /** + * Always return 0. + */ + public int getAttributeCount() + { + return 0; + } + + /** + * Returns an empty Enumeration. + */ + public Enumeration getAttributeNames() + { + return new Enumeration() + { + public boolean hasMoreElements() + { + return false; + } + + public Object nextElement() + { + throw new NoSuchElementException("No more elements"); + } + + }; + } + + /** + * Always return null as this has no resolve parent. + */ + public AttributeSet getResolveParent() + { + return null; + } + + /** + * Always return false as this AttributeSet doesn't contain any attributes. + */ + public boolean isDefined(Object attrName) + { + return false; + } + + /** + * Other attribute sets are equal if they are empty too. + */ + public boolean isEqual(AttributeSet attr) + { + return attr.getAttributeCount() == 0; + } + + /** + * Other objects are equal if it's the same instance as this, or if + * it's another attribute set without attributes. + */ + public boolean equals(Object o) + { + boolean eq = o == this; + if (! eq) + { + eq = (o instanceof AttributeSet) + && ((AttributeSet) o).getAttributeCount() == 0; + } + return eq; + } +} diff --git a/libjava/classpath/javax/swing/text/FlowView.java b/libjava/classpath/javax/swing/text/FlowView.java index 8ca55d8347a..3de95ed7f8d 100644 --- a/libjava/classpath/javax/swing/text/FlowView.java +++ b/libjava/classpath/javax/swing/text/FlowView.java @@ -159,20 +159,18 @@ public abstract class FlowView extends BoxView } /** - * Lays out one row of the flow view. This is called by {@link #layout} - * to fill one row with child views until the available span is exhausted. - * - * The default implementation fills the row by calling - * {@link #createView(FlowView, int, int, int)} until the available space - * is exhausted, a forced break is encountered or there are no more views - * in the logical view. If the available space is exhausted, - * {@link #adjustRow(FlowView, int, int, int)} is called to fit the row - * into the available span. - * + * Lays out one row of the flow view. This is called by {@link #layout} to + * fill one row with child views until the available span is exhausted. The + * default implementation fills the row by calling + * {@link #createView(FlowView, int, int, int)} until the available space is + * exhausted, a forced break is encountered or there are no more views in + * the logical view. If the available space is exhausted, + * {@link #adjustRow(FlowView, int, int, int)} is called to fit the row into + * the available span. + * * @param fv the flow view for which we perform the layout * @param rowIndex the index of the row * @param pos the model position for the beginning of the row - * * @return the start position of the next row */ protected int layoutRow(FlowView fv, int rowIndex, int pos) @@ -188,34 +186,39 @@ public abstract class FlowView extends BoxView if (span == 0) span = Integer.MAX_VALUE; - while (span > 0) + Row: while (span > 0) { - if (logicalView.getViewIndex(offset, Position.Bias.Forward) == -1) + if (logicalView.getViewIndex(offset, Position.Bias.Forward) == - 1) break; View view = createView(fv, offset, span, rowIndex); if (view == null) break; + int viewSpan = (int) view.getPreferredSpan(axis); - row.append(view); int breakWeight = view.getBreakWeight(axis, x, span); - if (breakWeight >= View.ForcedBreakWeight) - break; + + row.append(view); + offset += (view.getEndOffset() - view.getStartOffset()); x += viewSpan; span -= viewSpan; - offset += (view.getEndOffset() - view.getStartOffset()); - } - if (span < 0) - { - int flowStart = fv.getFlowStart(axis); - int flowSpan = fv.getFlowSpan(axis); - adjustRow(fv, rowIndex, flowSpan, flowStart); - int rowViewCount = row.getViewCount(); - if (rowViewCount > 0) - offset = row.getView(rowViewCount - 1).getEndOffset(); - else - offset = -1; + + // Break if the line if the view does not fit in this row or the + // line just must be broken. + if (span < 0 || breakWeight >= View.ForcedBreakWeight) + { + int flowStart = fv.getFlowStart(axis); + int flowSpan = fv.getFlowSpan(axis); + adjustRow(fv, rowIndex, flowSpan, flowStart); + int rowViewCount = row.getViewCount(); + if (rowViewCount > 0) + offset = row.getView(rowViewCount - 1).getEndOffset(); + else + offset = - 1; + break Row; + } } - return offset != pos ? offset : -1; + + return offset != pos ? offset : - 1; } /** @@ -521,6 +524,7 @@ public abstract class FlowView extends BoxView */ public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory vf) { + layoutPool.removeUpdate(changes, a, vf); strategy.removeUpdate(this, changes, getInsideAllocation(a)); layoutDirty = true; } @@ -536,6 +540,7 @@ public abstract class FlowView extends BoxView */ public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory vf) { + layoutPool.changedUpdate(changes, a, vf); strategy.changedUpdate(this, changes, getInsideAllocation(a)); layoutDirty = true; } @@ -594,12 +599,14 @@ public abstract class FlowView extends BoxView protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) { - // We need to call super here so that the alignment is properly - // calculated. - SizeRequirements res = super.calculateMinorAxisRequirements(axis, r); + SizeRequirements res = r; + if (res == null) + res = new SizeRequirements(); res.minimum = (int) layoutPool.getMinimumSpan(axis); - res.preferred = (int) layoutPool.getPreferredSpan(axis); - res.maximum = (int) layoutPool.getMaximumSpan(axis); + res.preferred = Math.max(res.minimum, + (int) layoutPool.getMinimumSpan(axis)); + res.maximum = Integer.MAX_VALUE; + res.alignment = 0.5F; return res; } } diff --git a/libjava/classpath/javax/swing/text/GapContent.java b/libjava/classpath/javax/swing/text/GapContent.java index 4f06003b458..760e396a223 100644 --- a/libjava/classpath/javax/swing/text/GapContent.java +++ b/libjava/classpath/javax/swing/text/GapContent.java @@ -39,7 +39,13 @@ exception statement from your version. */ package javax.swing.text; import java.io.Serializable; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; +import java.util.List; import java.util.Set; import java.util.Vector; import java.util.WeakHashMap; @@ -73,30 +79,39 @@ public class GapContent * The index to the positionMarks array entry, which in turn holds the * mark into the buffer array. */ - int index; + Mark mark; /** * Creates a new GapContentPosition object. * - * @param mark the mark of this Position + * @param offset the offset of this Position */ - GapContentPosition(int mark) + GapContentPosition(int offset) { // Try to find the mark in the positionMarks array, and store the index // to it. synchronized (GapContent.this) { - int i = binarySearch(positionMarks, mark, numMarks); + // Try to make space. + garbageCollect(); + Mark m = new Mark(offset); + int i = search(marks, m); if (i >= 0) // mark found { - index = i; + m = (Mark) marks.get(i); } else { - index = -i - 1; - insertMark(index, mark); + i = -i - 1; + marks.add(i, m); } + m.refCount++; + mark = m; } + + // Register this position in the death queue, so we can cleanup the marks + // when this position object gets GC'ed. + new WeakReference(this, queueOfDeath); } /** @@ -106,19 +121,77 @@ public class GapContent */ public int getOffset() { - synchronized (GapContent.this) - { - // Fetch the actual mark. - int mark = positionMarks[index]; - // Check precondition. - assert mark <= gapStart || mark >= gapEnd : "mark: " + mark - + ", gapStart: " + gapStart - + ", gapEnd: " + gapEnd; - int res = mark; - if (mark > gapStart) - res -= (gapEnd - gapStart); - return res; - } + return mark.getOffset(); + } + } + + /** + * Holds a mark into the buffer that is used by GapContentPosition to find + * the actual offset of the position. This is pulled out of the + * GapContentPosition object so that the mark and position can be handled + * independently, and most important, so that the GapContentPosition can + * be garbage collected while we still hold a reference to the Mark object. + */ + private class Mark + implements Comparable + { + /** + * The actual mark into the buffer. + */ + int mark; + + /** + * The number of GapContentPosition object that reference this mark. If + * it reaches zero, it get's deleted by {@link GapContent#garbageCollect()}. + */ + int refCount; + + /** + * Creates a new Mark object for the specified offset. + * + * @param offset the offset + */ + Mark(int offset) + { + mark = offset; + if (mark >= gapStart && mark != 0) + mark += (gapEnd - gapStart); + } + + /** + * Returns the offset of the mark. + * + * @return the offset of the mark + */ + int getOffset() + { + assert mark == 0 || mark < gapStart || mark >= gapEnd : + "Invalid mark: " + mark + ", gapStart: " + gapStart + + ", gapEnd: " + gapEnd; + + int res = mark; + if (mark >= gapEnd) + res -= (gapEnd - gapStart); + return res; + } + + /** + * Implementation of Comparable. + */ + public int compareTo(Object o) + { + Mark other = (Mark) o; + return mark - other.mark; + } + /** + * Adjustment for equals(). + */ + public boolean equals(Object o) + { + if (o == null || !(o instanceof Mark)) + return false; + else + return ((Mark) o).mark == mark; } } @@ -230,19 +303,21 @@ public class GapContent /** * Holds the marks for positions. These marks are referenced by the * GapContentPosition instances by an index into this array. + * + * This is package private to avoid accessor synthetic methods. */ - int[] positionMarks; + ArrayList marks; - /** - * The number of elements in the positionMarks array. The positionMarks array - * might be bigger than the actual number of elements. - */ - int numMarks; + WeakHashMap positions; /** - * (Weakly) Stores the GapContentPosition instances. + * Queues all references to GapContentPositions that are about to be + * GC'ed. This is used to remove the corresponding marks from the + * positionMarks array if the number of references to that mark reaches zero. + * + * This is package private to avoid accessor synthetic methods. */ - WeakHashMap positions; + ReferenceQueue queueOfDeath; /** * Creates a new GapContent object. @@ -265,8 +340,8 @@ public class GapContent gapEnd = size; buffer[0] = '\n'; positions = new WeakHashMap(); - positionMarks = new int[10]; - numMarks = 0; + marks = new ArrayList(); + queueOfDeath = new ReferenceQueue(); } /** @@ -417,6 +492,8 @@ public class GapContent if ((where + len) > length) throw new BadLocationException("len plus where cannot be greater" + " than the content length", len + where); + if (len < 0) + throw new BadLocationException("negative length not allowed: ", len); // check if requested segment is contiguous if ((where < gapStart) && ((gapStart - where) < len)) @@ -455,6 +532,11 @@ public class GapContent */ public Position createPosition(final int offset) throws BadLocationException { + // Implementation note: We used to perform explicit check on the offset + // here. However, this makes some Mauve and Intel/Harmony tests fail + // and luckily enough the GapContent can very well deal with offsets + // outside the buffer bounds. So I removed that check. + // We try to find a GapContentPosition at the specified offset and return // that. Otherwise we must create a new one. GapContentPosition pos = null; @@ -472,10 +554,7 @@ public class GapContent // If none was found, then create and return a new one. if (pos == null) { - int mark = offset; - if (mark >= gapStart) - mark += (gapEnd - gapStart); - pos = new GapContentPosition(mark); + pos = new GapContentPosition(offset); positions.put(pos, null); } @@ -497,7 +576,7 @@ public class GapContent int delta = newSize - gapEnd + gapStart; // Update the marks after the gapEnd. - adjustPositionsInRange(gapEnd, buffer.length - gapEnd, delta); + adjustPositionsInRange(gapEnd, -1, delta); // Copy the data around. char[] newBuf = (char[]) allocateArray(length() + newSize); @@ -523,7 +602,7 @@ public class GapContent { // Update the positions between newGapStart and (old) gapStart. The marks // must be shifted by (gapEnd - gapStart). - adjustPositionsInRange(newGapStart, gapStart - newGapStart, gapEnd - gapStart); + adjustPositionsInRange(newGapStart, gapStart, gapEnd - gapStart); System.arraycopy(buffer, newGapStart, buffer, newGapEnd, gapStart - newGapStart); gapStart = newGapStart; @@ -533,14 +612,13 @@ public class GapContent { // Update the positions between newGapEnd and (old) gapEnd. The marks // must be shifted by (gapEnd - gapStart). - adjustPositionsInRange(gapEnd, newGapEnd - gapEnd, -(gapEnd - gapStart)); + adjustPositionsInRange(gapEnd, newGapEnd, -(gapEnd - gapStart)); System.arraycopy(buffer, gapEnd, buffer, gapStart, newGapStart - gapStart); gapStart = newGapStart; gapEnd = newGapEnd; } - if (gapStart == 0) - resetMarksAtZero(); + resetMarksAtZero(); } /** @@ -560,6 +638,7 @@ public class GapContent + "old gap start."; setPositionsInRange(newGapStart, gapStart, false); gapStart = newGapStart; + resetMarksAtZero(); } /** @@ -579,6 +658,7 @@ public class GapContent + "old gap end."; setPositionsInRange(gapEnd, newGapEnd, false); gapEnd = newGapEnd; + resetMarksAtZero(); } /** @@ -617,10 +697,6 @@ public class GapContent if (addItems != null) { System.arraycopy(addItems, 0, buffer, gapStart, addSize); - - - resetMarksAtZero(); - gapStart += addSize; } } @@ -689,102 +765,61 @@ public class GapContent */ private void setPositionsInRange(int start, int end, boolean toStart) { - // We slump together all the GapContentPositions to point to - // one mark. So this is implemented as follows: - // 1. Remove all the marks in the specified range. - // 2. Insert one new mark at the correct location. - // 3. Adjust all affected GapContentPosition instances to point to - // this new mark. - synchronized (this) { - int startIndex = binarySearch(positionMarks, start, numMarks); + // Find the start and end indices in the positionMarks array. + Mark m = new Mark(0); // For comparison / search only. + m.mark = start; + int startIndex = search(marks, m); if (startIndex < 0) // Translate to insertion index, if not found. startIndex = - startIndex - 1; - int endIndex = binarySearch(positionMarks, end, numMarks); + m.mark = end; + int endIndex = search(marks, m); if (endIndex < 0) // Translate to insertion index - 1, if not found. endIndex = - endIndex - 2; - // Update the marks. - // We have inclusive interval bounds, but let one element over for - // filling in the new value. - int removed = endIndex - startIndex; - if (removed <= 0) - return; - System.arraycopy(positionMarks, endIndex + 1, positionMarks, - startIndex + 1, positionMarks.length - endIndex - 1); - numMarks -= removed; - if (toStart) - { - positionMarks[startIndex] = start; - } - else - { - positionMarks[startIndex] = end; - } + // Actually adjust the marks. + for (int i = startIndex; i <= endIndex; i++) + ((Mark) marks.get(i)).mark = toStart ? start : end; + } - // Update all affected GapContentPositions to point to the new index - // and all GapContentPositions that come after the interval to - // have their index moved by -removed. - Set positionSet = positions.keySet(); - for (Iterator i = positionSet.iterator(); i.hasNext();) - { - GapContentPosition p = (GapContentPosition) i.next(); - if (p.index > startIndex || p.index <= endIndex) - p.index = startIndex; - else if (p.index > endIndex) - p.index -= removed; - } - } } - + /** * 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 startOffs the start offset of the range to search + * @param endOffs the length of the range to search, -1 means all to the end * @param incr the increment */ - private void adjustPositionsInRange(int offset, int length, int incr) + private void adjustPositionsInRange(int startOffs, int endOffs, int incr) { - int endMark = offset + length; - synchronized (this) { // Find the start and end indices in the positionMarks array. - int startIndex = binarySearch(positionMarks, offset, numMarks); + Mark m = new Mark(0); // For comparison / search only. + + m.mark = startOffs; + int startIndex = search(marks, m); if (startIndex < 0) // Translate to insertion index, if not found. startIndex = - startIndex - 1; - int endIndex = binarySearch(positionMarks, endMark, numMarks); - if (endIndex < 0) // Translate to insertion index - 1, if not found. - endIndex = - endIndex - 2; - - // We must not change the order of the marks, this would have - // unpredictable results while binary-searching the marks. - assert (startIndex <= 0 - || positionMarks[startIndex - 1] - <= positionMarks [startIndex] + incr) - && (endIndex >= numMarks - 1 - || positionMarks[endIndex + 1] - >= positionMarks[endIndex] + incr) - : "Adjusting the marks must not change their order"; - - // Some debug helper output to determine if the start or end of the - // should ever be coalesced together with adjecent marks. - if (startIndex > 0 && positionMarks[startIndex - 1] - == positionMarks[startIndex] + incr) - System.err.println("DEBUG: We could coalesce the start of the region" - + " in GapContent.adjustPositionsInRange()"); - if (endIndex < numMarks - 1 && positionMarks[endIndex + 1] - == positionMarks[endIndex] + incr) - System.err.println("DEBUG: We could coalesce the end of the region" - + " in GapContent.adjustPositionsInRange()"); + m.mark = endOffs; + int endIndex; + if (endOffs == -1) + endIndex = marks.size() - 1; + else + { + endIndex = search(marks, m); + if (endIndex < 0) // Translate to insertion index - 1, if not found. + endIndex = - endIndex - 2; + } // Actually adjust the marks. - for (int i = startIndex; i <= endIndex; i++) - positionMarks[i] += incr; + for (int i = startIndex; i <= endIndex; i++) { + ((Mark) marks.get(i)).mark += incr; + } } } @@ -800,7 +835,12 @@ public class GapContent if (gapStart != 0) return; - positionMarks[0] = 0; + for (int i = 0; i < marks.size(); i++) + { + Mark m = (Mark) marks.get(i); + if (m.mark <= gapEnd) + m.mark = 0; + } } /** @@ -845,89 +885,60 @@ public class GapContent */ private void dumpMarks() { - System.err.print("positionMarks: "); - for (int i = 0; i < numMarks; i++) - System.err.print(positionMarks[i] + ", "); - System.err.println(); + System.out.print("positionMarks: "); + for (int i = 0; i < marks.size(); i++) + System.out.print(((Mark) marks.get(i)).mark + ", "); + System.out.println(); } /** - * Inserts a mark into the positionMarks array. This must update all the - * GapContentPosition instances in positions that come after insertionPoint. + * Polls the queue of death for GapContentPositions, updates the + * corresponding reference count and removes the corresponding mark + * if the refcount reaches zero. * - * This is package private to avoid synthetic accessor methods. - * - * @param insertionPoint the index at which to insert the mark - * @param mark the mark to insert + * This is package private to avoid accessor synthetic methods. */ - void insertMark(int insertionPoint, int mark) + void garbageCollect() { - synchronized (this) + Reference ref = queueOfDeath.poll(); + while (ref != null) { - // Update the positions. - Set positionSet = positions.keySet(); - for (Iterator i = positionSet.iterator(); i.hasNext();) - { - GapContentPosition p = (GapContentPosition) i.next(); - if (p.index >= insertionPoint) - p.index++; - } - - // Update the position marks. - if (positionMarks.length <= numMarks) + if (ref != null) { - int[] newMarks = new int[positionMarks.length + 10]; - System.arraycopy(positionMarks, 0, newMarks, 0, insertionPoint); - newMarks[insertionPoint] = mark; - System.arraycopy(positionMarks, insertionPoint, newMarks, - insertionPoint + 1, - numMarks - insertionPoint); - positionMarks = newMarks; + GapContentPosition pos = (GapContentPosition) ref.get(); + Mark m = pos.mark; + m.refCount--; + if (m.refCount == 0) + marks.remove(m); } - else - { - System.arraycopy(positionMarks, insertionPoint, positionMarks, - insertionPoint + 1, - numMarks - insertionPoint); - positionMarks[insertionPoint] = mark; - } - numMarks++; + ref = queueOfDeath.poll(); } } /** - * An adaption of {@link java.util.Arrays#binarySearch(int[], int)} to - * specify a maximum index up to which the array is searched. This allows - * us to have some trailing entries that we ignore. - * - * This is package private to avoid synthetic accessor methods. - * - * @param a the array - * @param key the key to search for - * @param maxIndex the maximum index up to which the search is performed + * Searches the first occurance of object <code>o</code> in list + * <code>l</code>. This performs a binary search by calling + * {@link Collections#binarySearch(List, Object)} and when an object has been + * found, it searches backwards to the first occurance of that object in the + * list. The meaning of the return value is the same as in + * <code>Collections.binarySearch()</code>. * - * @return the index of the found entry, or (-(index)-1) for the - * insertion point when not found + * @param l the list to search through + * @param o the object to be searched * - * @see java.util.Arrays#binarySearch(int[], int) + * @return the index of the first occurance of o in l, or -i + 1 if not found */ - int binarySearch(int[] a, int key, int maxIndex) + private int search(List l, Object o) { - int low = 0; - int hi = maxIndex - 1; - int mid = 0; - while (low <= hi) + int i = Collections.binarySearch(l, o); + while (i > 0) { - mid = (low + hi) >>> 1; - final int d = a[mid]; - if (d == key) - return mid; - else if (d > key) - hi = mid - 1; + Object o2 = l.get(i - 1); + if (o2.equals(o)) + i--; else - // This gets the insertion point right on the last loop. - low = ++mid; + break; } - return -mid - 1; + return i; } } diff --git a/libjava/classpath/javax/swing/text/IconView.java b/libjava/classpath/javax/swing/text/IconView.java index 699cda90eba..7bb7635b4e7 100644 --- a/libjava/classpath/javax/swing/text/IconView.java +++ b/libjava/classpath/javax/swing/text/IconView.java @@ -44,7 +44,6 @@ import java.awt.Shape; import javax.swing.Icon; import javax.swing.JTextPane; -import javax.swing.SwingConstants; /** * A View that can render an icon. This view is created by the @@ -156,4 +155,21 @@ public class IconView return el.getStartOffset(); } + /** + * Returns the alignment for this view. This will be 1.0 for the Y_AXIS, + * and the super behaviour for the X_AXIS. + * + * @param axis the axis for which to calculate the alignment + * + * @return the alignment + */ + public float getAlignment(int axis) + { + float align; + if (axis == Y_AXIS) + align = 1.0F; + else + align = super.getAlignment(axis); + return align; + } } diff --git a/libjava/classpath/javax/swing/text/JTextComponent.java b/libjava/classpath/javax/swing/text/JTextComponent.java index 9de151dfbac..6da84bfe7d8 100644 --- a/libjava/classpath/javax/swing/text/JTextComponent.java +++ b/libjava/classpath/javax/swing/text/JTextComponent.java @@ -42,6 +42,7 @@ import gnu.classpath.NotImplementedException; import java.awt.AWTEvent; import java.awt.Color; +import java.awt.Container; import java.awt.Dimension; import java.awt.Insets; import java.awt.Point; @@ -90,9 +91,10 @@ public abstract class JTextComponent extends JComponent implements Scrollable, Accessible { /** - * This class implements accessibility support for the JTextComponent class. - * It provides an implementation of the Java Accessibility API appropriate - * to menu user-interface elements. + * AccessibleJTextComponent implements accessibility hooks for + * JTextComponent. It allows an accessibility driver to read and + * manipulate the text component's contents as well as update UI + * elements such as the caret. */ public class AccessibleJTextComponent extends AccessibleJComponent implements AccessibleText, CaretListener, DocumentListener, AccessibleAction, @@ -100,15 +102,18 @@ public abstract class JTextComponent extends JComponent { private static final long serialVersionUID = 7664188944091413696L; - /** The caret's offset. */ + /** + * The caret's offset. + */ int dot = 0; - - /** The current JTextComponent. */ + + /** + * The current JTextComponent. + */ JTextComponent textComp = JTextComponent.this; - + /** - * Constructs an AccessibleJTextComponent. - * Adds a listener to track caret change. + * Construct an AccessibleJTextComponent. */ public AccessibleJTextComponent() { @@ -117,11 +122,10 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the zero-based offset of the caret. Note: The character - * to the right of the caret will have the same index value as the - * offset (the caret is between two characters). - * - * @return offset of caret + * Retrieve the current caret position. The index of the first + * caret position is 0. + * + * @return caret position */ public int getCaretPosition() { @@ -130,9 +134,10 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the portion of the text that is selected. - * - * @return null if no text is selected. + * Retrieve the current text selection. If no text is selected + * this method returns null. + * + * @return the currently selected text or null */ public String getSelectedText() { @@ -140,11 +145,14 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the start offset within the selected text. If there is no - * selection, but there is a caret, the start and end offsets will be - * the same. Return 0 if the text is empty, or the caret position if no selection. - * - * @return index of the start of the text >= 0. + * Retrieve the index of the first character in the current text + * selection. If there is no text in the text component, this + * method returns 0. If there is text in the text component, but + * there is no selection, this method returns the current caret + * position. + * + * @return the index of the first character in the selection, the + * current caret position or 0 */ public int getSelectionStart() { @@ -154,12 +162,14 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the end offset within the selected text. If there is no - * selection, but there is a caret, the start and end offsets will - * be the same. Return 0 if the text is empty, or the caret position - * if no selection. - * - * @return index of the end of the text >= 0. + * Retrieve the index of the last character in the current text + * selection. If there is no text in the text component, this + * method returns 0. If there is text in the text component, but + * there is no selection, this method returns the current caret + * position. + * + * @return the index of the last character in the selection, the + * current caret position or 0 */ public int getSelectionEnd() { @@ -169,13 +179,10 @@ public abstract class JTextComponent extends JComponent } /** - * Handles caret updates (fire appropriate property change event, which are - * AccessibleContext.ACCESSIBLE_CARET_PROPERTY and - * AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY). This keeps track of - * the dot position internally. When the caret moves, the internal position - * is updated after firing the event. - * - * @param e - caret event + * Handle a change in the caret position and fire any applicable + * property change events. + * + * @param e - the caret update event */ public void caretUpdate(CaretEvent e) throws NotImplementedException @@ -185,7 +192,7 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the accessible state set of this component. + * Retreive the accessible state set of this component. * * @return the accessible state set of this component */ @@ -198,7 +205,7 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the accessible role of this component. + * Retrieve the accessible role of this component. * * @return the accessible role of this component * @@ -210,20 +217,19 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the AccessibleEditableText interface for this text component. - * + * Retrieve an AccessibleEditableText object that controls this + * text component. + * * @return this */ public AccessibleEditableText getAccessibleEditableText() { return this; } - + /** - * Get the AccessibleText associated with this object. In the implementation - * of the Java Accessibility API for this class, return this object, - * which is responsible for implementing the AccessibleText interface on - * behalf of itself. + * Retrieve an AccessibleText object that controls this text + * component. * * @return this * @@ -235,10 +241,11 @@ public abstract class JTextComponent extends JComponent } /** - * Insert update. Fire appropriate property change event which - * is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY. - * - * @param e - document event + * Handle a text insertion event and fire an + * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change + * event. + * + * @param e - the insertion event */ public void insertUpdate(DocumentEvent e) throws NotImplementedException @@ -247,10 +254,11 @@ public abstract class JTextComponent extends JComponent } /** - * Remove update. Fire appropriate property change event which - * is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY. - * - * @param e - document event + * Handle a text removal event and fire an + * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change + * event. + * + * @param e - the removal event */ public void removeUpdate(DocumentEvent e) throws NotImplementedException @@ -259,10 +267,11 @@ public abstract class JTextComponent extends JComponent } /** - * Changed update. Fire appropriate property change event which - * is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY. - * - * @param e - document event + * Handle a text change event and fire an + * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change + * event. + * + * @param e - text change event */ public void changedUpdate(DocumentEvent e) throws NotImplementedException @@ -271,11 +280,13 @@ public abstract class JTextComponent extends JComponent } /** - * Given a point in the coordinate system of this object, return the - * 0-based index of the character at that point, or -1 if there is none. + * Get the index of the character at the given point, in component + * pixel co-ordinates. If the point argument is invalid this + * method returns -1. * - * @param p the point to look at - * @return the character index, or -1 + * @param p - a point in component pixel co-ordinates + * + * @return a character index, or -1 */ public int getIndexAtPoint(Point p) throws NotImplementedException @@ -284,17 +295,14 @@ public abstract class JTextComponent extends JComponent } /** - * Determines the bounding box of the indexed character. Returns an empty - * rectangle if the index is out of bounds. The bounds are returned in local coordinates. - * If the index is invalid a null rectangle is returned. The screen coordinates returned are - * "unscrolled coordinates" if the JTextComponent is contained in a JScrollPane in which - * case the resulting rectangle should be composed with the parent coordinates. - * Note: the JTextComponent must have a valid size (e.g. have been added to a parent - * container whose ancestor container is a valid top-level window) for this method to - * be able to return a meaningful (non-null) value. + * Calculate the bounding box of the character at the given index. + * The returned x and y co-ordinates are relative to this text + * component's top-left corner. If the index is invalid this + * method returns null. + * + * @param index - the character index * - * @param index the 0-based character index - * @return the bounding box, may be empty or null. + * @return a character's bounding box, or null */ public Rectangle getCharacterBounds(int index) throws NotImplementedException @@ -303,9 +311,9 @@ public abstract class JTextComponent extends JComponent } /** - * Return the number of characters. + * Return the length of the text in this text component. * - * @return the character count + * @return a character length */ public int getCharCount() { @@ -313,10 +321,11 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the attributes of a character at an index, or null if the index - * is out of bounds. + * Gets the character attributes of the character at index. If + * the index is out of bounds, null is returned. * - * @param index the 0-based character index + * @param index - index of the character + * * @return the character's attributes */ public AttributeSet getCharacterAttribute(int index) @@ -326,26 +335,28 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the section of text at the index, or null if the index or part - * is invalid. - * - * @param part {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE} - * @param index the 0-based character index - * @return the selection of text at that index, or null + * Gets the text located at index. null is returned if the index + * or part is invalid. + * + * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE} + * @param index - index of the part + * + * @return the part of text at that index, or null */ public String getAtIndex(int part, int index) throws NotImplementedException { return null; // TODO } - + /** - * Returns the section of text after the index, or null if the index or part - * is invalid. - * - * @param part {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE} - * @param index the 0-based character index - * @return the selection of text after that index, or null + * Gets the text located after index. null is returned if the index + * or part is invalid. + * + * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE} + * @param index - index after the part + * + * @return the part of text after that index, or null */ public String getAfterIndex(int part, int index) throws NotImplementedException @@ -354,12 +365,13 @@ public abstract class JTextComponent extends JComponent } /** - * Returns the section of text before the index, or null if the index or part - * is invalid. - * - * @param part {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE} - * @param index the 0-based character index - * @return the selection of text before that index, or null + * Gets the text located before index. null is returned if the index + * or part is invalid. + * + * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE} + * @param index - index before the part + * + * @return the part of text before that index, or null */ public String getBeforeIndex(int part, int index) throws NotImplementedException @@ -368,10 +380,10 @@ public abstract class JTextComponent extends JComponent } /** - * Get the number possible actions for this object, with the zeroth - * representing the default action. + * Returns the number of actions for this object. The zero-th + * object represents the default action. * - * @return the 0-based number of actions + * @return the number of actions (0-based). */ public int getAccessibleActionCount() throws NotImplementedException @@ -380,11 +392,12 @@ public abstract class JTextComponent extends JComponent } /** - * Get a description for the specified action. Returns null if out of - * bounds. + * Returns the description of the i-th action. Null is returned if + * i is out of bounds. * - * @param i the action to describe, 0-based - * @return description of the action + * @param i - the action to get the description for + * + * @return description of the i-th action */ public String getAccessibleActionDescription(int i) throws NotImplementedException @@ -394,10 +407,12 @@ public abstract class JTextComponent extends JComponent } /** - * Perform the specified action. Does nothing if out of bounds. + * Performs the i-th action. Nothing happens if i is + * out of bounds. * - * @param i the action to perform, 0-based - * @return true if the action was performed + * @param i - the action to perform + * + * @return true if the action was performed successfully */ public boolean doAccessibleAction(int i) throws NotImplementedException @@ -406,9 +421,9 @@ public abstract class JTextComponent extends JComponent } /** - * Set the text contents to the given string. + * Sets the text contents. * - * @param s the new text + * @param s - the new text contents. */ public void setTextContents(String s) throws NotImplementedException @@ -417,10 +432,10 @@ public abstract class JTextComponent extends JComponent } /** - * Inserts the given string at the specified location. + * Inserts the text at the given index. * - * @param index the index for insertion - * @param s the new text + * @param index - the index to insert the new text at. + * @param s - the new text */ public void insertTextAtIndex(int index, String s) throws NotImplementedException @@ -429,10 +444,10 @@ public abstract class JTextComponent extends JComponent } /** - * Return the text between two points. + * Gets the text between two indexes. * - * @param start the start position, inclusive - * @param end the end position, exclusive + * @param start - the starting index (inclusive) + * @param end - the ending index (exclusive) */ public String getTextRange(int start, int end) { @@ -447,10 +462,10 @@ public abstract class JTextComponent extends JComponent } /** - * Delete the text between two points. + * Deletes the text between two indexes. * - * @param start the start position, inclusive - * @param end the end position, exclusive + * @param start - the starting index (inclusive) + * @param end - the ending index (exclusive) */ public void delete(int start, int end) { @@ -458,10 +473,11 @@ public abstract class JTextComponent extends JComponent } /** - * Cut the text between two points to the system clipboard. + * Cuts the text between two indexes. The text is put + * into the system clipboard. * - * @param start the start position, inclusive - * @param end the end position, exclusive + * @param start - the starting index (inclusive) + * @param end - the ending index (exclusive) */ public void cut(int start, int end) { @@ -470,9 +486,9 @@ public abstract class JTextComponent extends JComponent } /** - * Paste the text from the system clipboard at the given index. + * Pastes the text from the system clipboard to the given index. * - * @param start the start position + * @param start - the starting index */ public void paste(int start) { @@ -481,11 +497,12 @@ public abstract class JTextComponent extends JComponent } /** - * Replace the text between two points with the given string. + * Replaces the text between two indexes with the given text. * - * @param start the start position, inclusive - * @param end the end position, exclusive - * @param s the string to paste + * + * @param start - the starting index (inclusive) + * @param end - the ending index (exclusive) + * @param s - the text to paste */ public void replaceText(int start, int end, String s) { @@ -494,10 +511,10 @@ public abstract class JTextComponent extends JComponent } /** - * Select the text between two points. + * Selects the text between two indexes. * - * @param start the start position, inclusive - * @param end the end position, exclusive + * @param start - the starting index (inclusive) + * @param end - the ending index (exclusive) */ public void selectText(int start, int end) { @@ -505,11 +522,11 @@ public abstract class JTextComponent extends JComponent } /** - * Set the attributes of text between two points. + * Sets the attributes of all the text between two indexes. * - * @param start the start position, inclusive - * @param end the end position, exclusive - * @param s the new attribute set for the range + * @param start - the starting index (inclusive) + * @param end - the ending index (exclusive) + * @param s - the new attribute set for the text in the range */ public void setAttributes(int start, int end, AttributeSet s) throws NotImplementedException @@ -1163,8 +1180,19 @@ public abstract class JTextComponent extends JComponent public void setDocument(Document newDoc) { Document oldDoc = doc; - doc = newDoc; - firePropertyChange("document", oldDoc, newDoc); + try + { + if (oldDoc instanceof AbstractDocument) + ((AbstractDocument) oldDoc).readLock(); + + doc = newDoc; + firePropertyChange("document", oldDoc, newDoc); + } + finally + { + if (oldDoc instanceof AbstractDocument) + ((AbstractDocument) oldDoc).readUnlock(); + } revalidate(); repaint(); } @@ -1641,10 +1669,12 @@ public abstract class JTextComponent extends JComponent public boolean getScrollableTracksViewportWidth() { - if (getParent() instanceof JViewport) - return getParent().getWidth() > getPreferredSize().width; + boolean res = false;; + Container c = getParent(); + if (c instanceof JViewport) + res = ((JViewport) c).getExtentSize().width > getPreferredSize().width; - return false; + return res; } /** diff --git a/libjava/classpath/javax/swing/text/ParagraphView.java b/libjava/classpath/javax/swing/text/ParagraphView.java index 15bed781825..c4857863d35 100644 --- a/libjava/classpath/javax/swing/text/ParagraphView.java +++ b/libjava/classpath/javax/swing/text/ParagraphView.java @@ -74,6 +74,39 @@ public class ParagraphView extends FlowView implements TabExpander return align; } + /** + * Allows rows to span the whole parent view. + */ + public float getMaximumSpan(int axis) + { + float max; + if (axis == X_AXIS) + max = Float.MAX_VALUE; + else + max = super.getMaximumSpan(axis); + return max; + } + + /** + * Overridden because child views are not necessarily laid out in model + * order. + */ + protected int getViewIndexAtPosition(int pos) + { + int index = -1; + if (pos >= getStartOffset() && pos < getEndOffset()) + { + int nviews = getViewCount(); + for (int i = 0; i < nviews && index == -1; i++) + { + View child = getView(i); + if (pos >= child.getStartOffset() && pos < child.getEndOffset()) + index = i; + } + } + return index; + } + protected void loadChildren(ViewFactory vf) { // Do nothing here. The children are added while layouting. @@ -140,7 +173,7 @@ public class ParagraphView extends FlowView implements TabExpander { float align; if (axis == X_AXIS) - align = super.getAlignment(axis); + align = 0.5F; else if (getViewCount() > 0) { float prefHeight = getPreferredSpan(Y_AXIS); @@ -148,7 +181,7 @@ public class ParagraphView extends FlowView implements TabExpander align = (firstRowHeight / 2.F) / prefHeight; } else - align = 0.0F; + align = 0.5F; return align; } diff --git a/libjava/classpath/javax/swing/text/PlainDocument.java b/libjava/classpath/javax/swing/text/PlainDocument.java index c699dcad2aa..730a619da9f 100644 --- a/libjava/classpath/javax/swing/text/PlainDocument.java +++ b/libjava/classpath/javax/swing/text/PlainDocument.java @@ -56,8 +56,12 @@ public class PlainDocument extends AbstractDocument public static final String lineLimitAttribute = "lineLimit"; public static final String tabSizeAttribute = "tabSize"; - private BranchElement rootElement; - private int tabSize; + /** + * The default root element of this document. This is made type Element + * because the RI seems to accept other types of elements as well from + * createDefaultRoot() (when overridden by a subclass). + */ + private Element rootElement; public PlainDocument() { @@ -67,8 +71,10 @@ public class PlainDocument extends AbstractDocument public PlainDocument(AbstractDocument.Content content) { super(content); - tabSize = 8; - rootElement = (BranchElement) createDefaultRoot(); + rootElement = createDefaultRoot(); + + // This property has been determined using a Mauve test. + putProperty("tabSize", new Integer(8)); } private void reindex() @@ -105,10 +111,10 @@ public class PlainDocument extends AbstractDocument protected AbstractDocument.AbstractElement createDefaultRoot() { BranchElement root = - (BranchElement) createBranchElement(null, SimpleAttributeSet.EMPTY); + (BranchElement) createBranchElement(null, null); Element[] array = new Element[1]; - array[0] = createLeafElement(root, SimpleAttributeSet.EMPTY, 0, 1); + array[0] = createLeafElement(root, null, 0, 1); root.replace(0, 0, array); return root; @@ -117,116 +123,97 @@ public class PlainDocument extends AbstractDocument protected void insertUpdate(DefaultDocumentEvent event, AttributeSet attributes) { + + String text = null; int offset = event.getOffset(); - int eventLength = event.getLength(); - int end = offset + event.getLength(); - int oldElementIndex, elementIndex = rootElement.getElementIndex(offset); - Element firstElement = rootElement.getElement(elementIndex); - oldElementIndex = elementIndex; - - // If we're inserting immediately after a newline we have to fix the - // Element structure (but only if we are dealing with a line which - // has not existed as Element before). - if (offset > 0 && firstElement.getStartOffset() != offset) + int length = event.getLength(); + try { - try - { - String s = getText(offset - 1, 1); - if (s.equals("\n") ) - { - int newEl2EndOffset = end; - boolean replaceNext = false; - if (rootElement.getElementCount() > elementIndex + 1) - { - replaceNext = true; - newEl2EndOffset = - rootElement.getElement(elementIndex + 1).getEndOffset(); - } - Element newEl1 = - createLeafElement(rootElement, firstElement.getAttributes(), - firstElement.getStartOffset(), offset); - Element newEl2 = - createLeafElement (rootElement, firstElement.getAttributes(), - offset, newEl2EndOffset); - if (replaceNext) - rootElement.replace(elementIndex, 2, new Element[] { newEl1, newEl2 }); - else - rootElement.replace(elementIndex, 1, new Element[] { newEl1, newEl2 }); - firstElement = newEl2; - elementIndex ++; - } - } - catch (BadLocationException ble) - { - // This shouldn't happen. - AssertionError ae = new AssertionError(); - ae.initCause(ble); - throw ae; - } + text = getText(offset, length); + } + catch (BadLocationException ex) + { + AssertionError err = new AssertionError(); + err.initCause(ex); + throw err; } - // added and removed are Element arrays used to add an ElementEdit - // to the DocumentEvent if there were entire lines added or removed. - Element[] removed = new Element[1]; - Element[] added; - try + boolean hasLineBreak = text.indexOf('\n') != -1; + boolean prevCharIsLineBreak = false; + try { - String str = content.getString(offset, eventLength); - ArrayList elts = new ArrayList(); + prevCharIsLineBreak = + offset > 0 && getText(offset - 1, 1).charAt(0) == '\n'; + } + catch (BadLocationException ex) + { + AssertionError err = new AssertionError(); + err.initCause(ex); + throw err; + } + boolean lastCharIsLineBreak = text.charAt(text.length() - 1) == '\n'; + int lineIndex = -1; + int lineStart = -1; + int lineEnd = -1; + Element[] removed = null; + BranchElement root = (BranchElement) rootElement; + boolean updateStructure = true; - // Determine how many NEW lines were added by finding the newline - // characters within the newly inserted text - int j = firstElement.getStartOffset(); - int i = str.indexOf('\n', 0); - int contentLength = content.length(); - - while (i != -1 && i <= eventLength) - { - // For each new line, create a new element - elts.add(createLeafElement(rootElement, SimpleAttributeSet.EMPTY, - j, offset + i + 1)); - - j = offset + i + 1; - if (j >= contentLength) - break; - i = str.indexOf('\n', i + 1); - } + if (prevCharIsLineBreak && ! lastCharIsLineBreak) + { + // We must fix the structure a little if the previous char + // is a linebreak and the last char isn't. + lineIndex = root.getElementIndex(offset - 1); + Element prevLine = root.getElement(lineIndex); + Element nextLine = root.getElement(lineIndex + 1); + lineStart = prevLine.getStartOffset(); + lineEnd = nextLine.getEndOffset(); + removed = new Element[]{ prevLine, nextLine }; + } + else if (hasLineBreak) + { + lineIndex = root.getElementIndex(offset); + Element line = root.getElement(lineIndex); + lineStart = line.getStartOffset(); + lineEnd = line.getEndOffset(); + removed = new Element[]{ line }; + } + else + { + updateStructure = false; + } - // If there were new lines added we have to add an ElementEdit to - // the DocumentEvent and we have to call rootElement.replace to - // insert the new lines - if (elts.size() != 0) + if (updateStructure) + { + // Break the lines between lineStart and lineEnd. + ArrayList lines = new ArrayList(); + int len = lineEnd - lineStart; + try { - // If we have created new lines test whether there are remaining - // characters in firstElement after the inserted text and if so - // create a new element for them. - if (j < firstElement.getEndOffset()) - elts.add(createLeafElement(rootElement, SimpleAttributeSet.EMPTY, j, firstElement.getEndOffset())); - - // Set up the ElementEdit by filling the added and removed - // arrays with the proper Elements - added = new Element[elts.size()]; - elts.toArray(added); - - removed[0] = firstElement; - - // Now create and add the ElementEdit - ElementEdit e = new ElementEdit(rootElement, elementIndex, removed, - added); - event.addEdit(e); - - // And call replace to actually make the changes - ((BranchElement) rootElement).replace(elementIndex, 1, added); + text = getText(lineStart, len); } + catch (BadLocationException ex) + { + AssertionError err = new AssertionError(); + err.initCause(ex); + throw err; + } + int prevLineBreak = 0; + int lineBreak = text.indexOf('\n'); + do + { + lineBreak++; + lines.add(createLeafElement(root, null, lineStart + prevLineBreak, + lineStart + lineBreak)); + prevLineBreak = lineBreak; + lineBreak = text.indexOf('\n', prevLineBreak); + } while (prevLineBreak < len); + + // Update the element structure and prepare document event. + Element[] added = (Element[]) lines.toArray(new Element[lines.size()]); + event.addEdit(new ElementEdit(root, lineIndex, removed, added)); + root.replace(lineIndex, removed.length, added); } - catch (BadLocationException e) - { - // This shouldn't happen so we throw an AssertionError - AssertionError ae = new AssertionError(); - ae.initCause(e); - throw ae; - } - super.insertUpdate(event, attributes); } @@ -264,7 +251,7 @@ public class PlainDocument extends AbstractDocument event.addEdit(e); // collapse elements if the removal spans more than 1 line - rootElement.replace(i1, i2 - i1 + 1, added); + ((BranchElement) rootElement).replace(i1, i2 - i1 + 1, added); } } diff --git a/libjava/classpath/javax/swing/text/Segment.java b/libjava/classpath/javax/swing/text/Segment.java index d2364e05a10..63c5fa09dbc 100644 --- a/libjava/classpath/javax/swing/text/Segment.java +++ b/libjava/classpath/javax/swing/text/Segment.java @@ -165,8 +165,9 @@ public class Segment implements Cloneable, CharacterIterator /** * Sets the current index to point to the last character in the segment and - * returns that character. If the segment contains zero characters, this - * method returns {@link #DONE}. + * returns that character. If the segment contains zero characters, the + * current index is set to {@link #getEndIndex()} and this method returns + * {@link #DONE}. * * @return The last character in the segment, or {@link #DONE} if the * segment contains zero characters. @@ -174,7 +175,10 @@ public class Segment implements Cloneable, CharacterIterator public char last() { if (count == 0) - return DONE; + { + current = getEndIndex(); + return DONE; + } current = getEndIndex() - 1; return array[current]; diff --git a/libjava/classpath/javax/swing/text/SimpleAttributeSet.java b/libjava/classpath/javax/swing/text/SimpleAttributeSet.java index 8dbcb0c6a14..8684ef87d34 100644 --- a/libjava/classpath/javax/swing/text/SimpleAttributeSet.java +++ b/libjava/classpath/javax/swing/text/SimpleAttributeSet.java @@ -51,8 +51,10 @@ public class SimpleAttributeSet /** The serialization UID (compatible with JDK1.5). */ private static final long serialVersionUID = 8267656273837665219L; - /** An empty attribute set. */ - public static final AttributeSet EMPTY = new SimpleAttributeSet(); + /** + * An empty attribute set. + */ + public static final AttributeSet EMPTY = new EmptyAttributeSet(); /** Storage for the attributes. */ Hashtable tab; diff --git a/libjava/classpath/javax/swing/text/StringContent.java b/libjava/classpath/javax/swing/text/StringContent.java index 0a31505f3a6..8014dc3bce6 100644 --- a/libjava/classpath/javax/swing/text/StringContent.java +++ b/libjava/classpath/javax/swing/text/StringContent.java @@ -178,11 +178,13 @@ public final class StringContent } /** - * Creates a new instance containing the string "\n". + * Creates a new instance containing the string "\n". This is equivalent + * to calling {@link #StringContent(int)} with an <code>initialLength</code> + * of 10. */ public StringContent() { - this(1); + this(10); } /** diff --git a/libjava/classpath/javax/swing/text/TabSet.java b/libjava/classpath/javax/swing/text/TabSet.java index ecad9444ea5..0f2c8c7c1ee 100644 --- a/libjava/classpath/javax/swing/text/TabSet.java +++ b/libjava/classpath/javax/swing/text/TabSet.java @@ -1,5 +1,5 @@ /* TabSet.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,23 +39,54 @@ package javax.swing.text; import java.io.Serializable; +/** + * A set of tab stops. Instances of this class are immutable. + */ public class TabSet implements Serializable { /** The serialization UID (compatible with JDK1.5). */ private static final long serialVersionUID = 2367703481999080593L; + /** Storage for the tab stops. */ TabStop[] tabs; + /** + * Creates a new <code>TabSet</code> containing the specified tab stops. + * + * @param t the tab stops (<code>null</code> permitted). + */ public TabSet(TabStop[] t) { - tabs = t; + if (t != null) + tabs = (TabStop[]) t.clone(); + else + tabs = new TabStop[0]; } + /** + * Returns the tab stop with the specified index. + * + * @param i the index. + * + * @return The tab stop. + * + * @throws IllegalArgumentException if <code>i</code> is not in the range + * <code>0</code> to <code>getTabCount() - 1</code>. + */ public TabStop getTab(int i) { + if (i < 0 || i >= tabs.length) + throw new IllegalArgumentException("Index out of bounds."); return tabs[i]; } + /** + * Returns the tab following the specified location. + * + * @param location the location. + * + * @return The tab following the specified location (or <code>null</code>). + */ public TabStop getTabAfter(float location) { int idx = getTabIndexAfter(location); @@ -65,11 +96,23 @@ public class TabSet implements Serializable return tabs[idx]; } + /** + * Returns the number of tab stops in this tab set. + * + * @return The number of tab stops in this tab set. + */ public int getTabCount() { return tabs.length; } + /** + * Returns the index of the specified tab, or -1 if the tab is not found. + * + * @param tab the tab (<code>null</code> permitted). + * + * @return The index of the specified tab, or -1. + */ public int getTabIndex(TabStop tab) { for (int i = 0; i < tabs.length; ++i) @@ -78,28 +121,88 @@ public class TabSet implements Serializable return -1; } + /** + * Returns the index of the tab at or after the specified location. + * + * @param location the tab location. + * + * @return The index of the tab stop, or -1. + */ public int getTabIndexAfter(float location) { - int idx = -1; - for (int i = 0; i < tabs.length; ++i) + for (int i = 0; i < tabs.length; i++) + { + if (location <= tabs[i].getPosition()) + return i; + } + return -1; + } + + /** + * Tests this <code>TabSet</code> for equality with an arbitrary object. + * + * @param obj the object (<code>null</code> permitted). + * + * @return <code>true</code> if this <code>TabSet</code> is equal to + * <code>obj</code>, and <code>false</code> otherwise. + * + * @since 1.5 + */ + public boolean equals(Object obj) + { + if (obj == this) + return true; + if (!(obj instanceof TabSet)) + return false; + TabSet that = (TabSet) obj; + int tabCount = getTabCount(); + if (tabCount != that.getTabCount()) + return false; + for (int i = 0; i < tabCount; i++) + { + if (!this.getTab(i).equals(that.getTab(i))) + return false; + } + return true; + } + + /** + * Returns a hash code for this <code>TabSet</code>. + * + * @return A hash code. + * + * @since 1.5 + */ + public int hashCode() + { + // this hash code won't match Sun's, but that shouldn't matter... + int result = 193; + int tabs = getTabCount(); + for (int i = 0; i < tabs; i++) { - if (location < tabs[i].getPosition()) - idx = i; + TabStop t = getTab(i); + if (t != null) + result = 37 * result + t.hashCode(); } - return idx; + return result; } + /** + * Returns a string representation of this <code>TabSet</code>. + * + * @return A string representation of this <code>TabSet</code>. + */ public String toString() { StringBuffer sb = new StringBuffer(); - sb.append("["); + sb.append("[ "); for (int i = 0; i < tabs.length; ++i) { if (i != 0) sb.append(" - "); sb.append(tabs[i].toString()); } - sb.append("]"); + sb.append(" ]"); return sb.toString(); } } diff --git a/libjava/classpath/javax/swing/text/TabStop.java b/libjava/classpath/javax/swing/text/TabStop.java index 56f862fdae4..f4c3f851406 100644 --- a/libjava/classpath/javax/swing/text/TabStop.java +++ b/libjava/classpath/javax/swing/text/TabStop.java @@ -1,5 +1,5 @@ -/* TabSet.java -- - Copyright (C) 2004 Free Software Foundation, Inc. +/* TabStop.java -- + Copyright (C) 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,6 +39,9 @@ package javax.swing.text; import java.io.Serializable; +/** + * Represents a tab position in some text. + */ public class TabStop implements Serializable { /** The serialization UID (compatible with JDK1.5). */ @@ -61,18 +64,42 @@ public class TabStop implements Serializable int align; int leader; + /** + * Creates a new <code>TabStop</code> for the specified tab position. + * + * @param pos the tab position. + */ public TabStop(float pos) { this(pos, ALIGN_LEFT, LEAD_NONE); } + /** + * Creates a new <code>TabStop</code> with the specified attributes. + * + * @param pos the tab position. + * @param align the alignment (one of {@link #ALIGN_LEFT}, + * {@link #ALIGN_CENTER}, {@link #ALIGN_RIGHT}, {@link #ALIGN_DECIMAL} + * or {@link #ALIGN_BAR}). + * @param leader the leader (one of {@link #LEAD_NONE}, {@link #LEAD_DOTS}, + * {@link #LEAD_EQUALS}, {@link #LEAD_HYPHENS}, {@link #LEAD_THICKLINE} + * or {@link #LEAD_UNDERLINE}). + */ public TabStop(float pos, int align, int leader) { this.pos = pos; this.align = align; this.leader = leader; } - + + /** + * Tests this <code>TabStop</code> for equality with an arbitrary object. + * + * @param other the other object (<code>null</code> permitted). + * + * @return <code>true</code> if this <code>TabStop</code> is equal to + * the specified object, and <code>false</code> otherwise. + */ public boolean equals(Object other) { return (other != null) @@ -82,34 +109,60 @@ public class TabStop implements Serializable && (((TabStop)other).getAlignment() == this.getAlignment()); } + /** + * Returns the tab alignment. This should be one of {@link #ALIGN_LEFT}, + * {@link #ALIGN_CENTER}, {@link #ALIGN_RIGHT}, {@link #ALIGN_DECIMAL} or + * {@link #ALIGN_BAR}. + * + * @return The tab alignment. + */ public int getAlignment() { return align; } + /** + * Returns the leader type. This should be one of {@link #LEAD_NONE}, + * {@link #LEAD_DOTS}, {@link #LEAD_EQUALS}, {@link #LEAD_HYPHENS}, + * {@link #LEAD_THICKLINE} or {@link #LEAD_UNDERLINE}. + * + * @return The leader type. + */ public int getLeader() { return leader; } + /** + * Returns the tab position. + * + * @return The tab position. + */ public float getPosition() { return pos; } + /** + * Returns a hash code for this <code>TabStop</code>. + * + * @return A hash code. + */ public int hashCode() { return (int) pos + (int) leader + (int) align; } + /** + * Returns a string describing this <code>TabStop</code>. + * + * @return A string describing this <code>TabStop</code>. + */ public String toString() { String prefix = ""; switch (align) { - case ALIGN_LEFT: - prefix = "left "; - break; case ALIGN_RIGHT: prefix = "right "; break; @@ -130,7 +183,8 @@ public class TabStop implements Serializable break; } - return (prefix + "tab @" + pos + ((leader == LEAD_NONE) ? "" : "(w/leaders)")); + return prefix + "tab @" + pos + + ((leader == LEAD_NONE) ? "" : " (w/leaders)"); } } diff --git a/libjava/classpath/javax/swing/text/View.java b/libjava/classpath/javax/swing/text/View.java index d8ad5f5858e..55a63f6b668 100644 --- a/libjava/classpath/javax/swing/text/View.java +++ b/libjava/classpath/javax/swing/text/View.java @@ -401,7 +401,10 @@ public abstract class View implements SwingConstants Element el = getElement(); DocumentEvent.ElementChange ec = ev.getChange(el); if (ec != null) - updateChildren(ec, ev, vf); + { + if (! updateChildren(ec, ev, vf)) + ec = null; + } forwardUpdate(ec, ev, shape, vf); updateLayout(ec, ev, shape); } @@ -493,27 +496,66 @@ public abstract class View implements SwingConstants int count = getViewCount(); if (count > 0) { + // Determine start index. int startOffset = ev.getOffset(); - int endOffset = startOffset + ev.getLength(); int startIndex = getViewIndex(startOffset, Position.Bias.Backward); - int endIndex = getViewIndex(endOffset, Position.Bias.Forward); - int index = -1; - int addLength = -1; - if (ec != null) + + // For REMOVE events we have to forward the event to the last element, + // for the case that an Element has been removed that represente + // the offset. + if (startIndex == -1 && ev.getType() == DocumentEvent.EventType.REMOVE + && startOffset >= getEndOffset()) { - index = ec.getIndex(); - addLength = ec.getChildrenAdded().length; + startIndex = getViewCount() - 1; } - if (startIndex >= 0 && endIndex >= 0) + // When startIndex is on a view boundary, forward event to the + // previous view too. + if (startIndex >= 0) { - for (int i = startIndex; i <= endIndex; i++) + View v = getView(startIndex); + if (v != null) + { + if (v.getStartOffset() == startOffset && startOffset > 0) + startIndex = Math.max(0, startIndex - 1); + } + } + startIndex = Math.max(0, startIndex); + + // Determine end index. + int endIndex = startIndex; + if (ev.getType() != DocumentEvent.EventType.REMOVE) + { + endIndex = getViewIndex(startOffset + ev.getLength(), + Position.Bias.Forward); + if (endIndex < 0) + endIndex = getViewCount() - 1; + } + + // Determine hole that comes from added elements (we don't forward + // the event to newly added views. + int startAdded = endIndex + 1; + int endAdded = startAdded; + Element[] added = (ec != null) ? ec.getChildrenAdded() : null; + if (added != null && added.length > 0) + { + startAdded = ec.getIndex(); + endAdded = startAdded + added.length - 1; + } + + // Forward event to all views between startIndex and endIndex, + // and leave out all views in the hole. + for (int i = startIndex; i <= endIndex; i++) + { + // Skip newly added child views. + if (! (i >= startAdded && i <= endAdded)) { - // Skip newly added child views. - if (index >= 0 && i >= index && i < (index+addLength)) - continue; View child = getView(i); - forwardUpdateToView(child, ev, shape, vf); + if (child != null) + { + Shape childAlloc = getChildAllocation(i, shape); + forwardUpdateToView(child, ev, childAlloc, vf); + } } } } @@ -611,9 +653,46 @@ public abstract class View implements SwingConstants if (b2 != Position.Bias.Forward && b2 != Position.Bias.Backward) throw new IllegalArgumentException ("b2 must be either Position.Bias.Forward or Position.Bias.Backward"); - Rectangle s1 = (Rectangle) modelToView(p1, a, b1); - Rectangle s2 = (Rectangle) modelToView(p2, a, b2); - return SwingUtilities.computeUnion(s1.x, s1.y, s1.width, s1.height, s2); + + Shape s1 = modelToView(p1, a, b1); + // Special case for p2 == end index. + Shape s2; + if (p2 != getEndOffset()) + { + s2 = modelToView(p2, a, b2); + } + else + { + try + { + s2 = modelToView(p2, a, b2); + } + catch (BadLocationException ex) + { + // Assume the end rectangle to be at the right edge of the + // view. + Rectangle aRect = a instanceof Rectangle ? (Rectangle) a + : a.getBounds(); + s2 = new Rectangle(aRect.x + aRect.width - 1, aRect.y, 1, + aRect.height); + } + } + + // Need to modify the rectangle, so we create a copy in all cases. + Rectangle r1 = s1.getBounds(); + Rectangle r2 = s2 instanceof Rectangle ? (Rectangle) s2 + : s2.getBounds(); + + // For multiline view, let the resulting rectangle span the whole view. + if (r1.y != r2.y) + { + Rectangle aRect = a instanceof Rectangle ? (Rectangle) a + : a.getBounds(); + r1.x = aRect.x; + r1.width = aRect.width; + } + + return SwingUtilities.computeUnion(r2.x, r2.y, r2.width, r2.height, r1); } /** diff --git a/libjava/classpath/javax/swing/text/html/HTMLTableView.java b/libjava/classpath/javax/swing/text/html/BRView.java index cac44d8dc27..5521fed8edf 100644 --- a/libjava/classpath/javax/swing/text/html/HTMLTableView.java +++ b/libjava/classpath/javax/swing/text/html/BRView.java @@ -1,4 +1,4 @@ -/* HTMLTableView.java -- A table view for HTML tables +/* BRView.java -- HTML BR tag view Copyright (C) 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,44 +39,33 @@ exception statement from your version. */ package javax.swing.text.html; import javax.swing.text.Element; -import javax.swing.text.TableView; -import javax.swing.text.View; -import javax.swing.text.ViewFactory; /** - * A conrete implementation of TableView that renders HTML tables. - * - * @author Roman Kennke (kennke@aicas.com) + * Handled the HTML BR tag. */ -class HTMLTableView - extends TableView -{ +class BRView + extends NullView +{ /** - * Creates a new HTMLTableView for the specified element. - * - * @param el the element for the table view + * Creates the new BR view. + * + * @param elem the HTML element, representing the view. */ - public HTMLTableView(Element el) + public BRView(Element elem) { - super(el); + super(elem); } - + /** - * Loads the children of the Table. This completely bypasses the ViewFactory - * and creates instances of TableRow instead. - * - * @param vf ignored + * Always return ForcedBreakWeight for the X_AXIS, BadBreakWeight for the + * Y_AXIS. */ - protected void loadChildren(ViewFactory vf) + public int getBreakWeight(int axis, float pos, float len) { - Element el = getElement(); - int numChildren = el.getElementCount(); - View[] rows = new View[numChildren]; - for (int i = 0; i < numChildren; ++i) - { - rows[i] = createTableRow(el.getElement(i)); - } - replace(0, getViewCount(), rows); + if (axis == X_AXIS) + return ForcedBreakWeight; + else + return BadBreakWeight; } } diff --git a/libjava/classpath/javax/swing/text/html/HRuleView.java b/libjava/classpath/javax/swing/text/html/HRuleView.java new file mode 100644 index 00000000000..3bae5eb8e83 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/HRuleView.java @@ -0,0 +1,189 @@ +/* HRuleView.java -- Horizontal dash in HTML documents. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Shape; + +import javax.swing.text.Element; +import javax.swing.text.View; + +/** + * Represents the long horizontal separating dash that can be inserted into the + * HTML documents with HR tag. + */ +class HRuleView extends InlineView +{ + /** + * The null view, indicating, that nothing should be painted ahead the + * breaking point. + */ + View nullView; + + /** + * The height of the horizontal dash area. + */ + static int HEIGHT = 4; + + /** + * The imaginary invisible view that stays after end of line after the + * breaking procedure. It occupies on character. + */ + class Beginning extends NullView + { + /** + * The break offset that becomes the views start offset. + */ + int breakOffset; + + /** + * Return the end offset that is always one char after the break offset. + */ + public int getEndOffset() + { + return breakOffset + 1; + } + + /** + * Return the start offset that has been passed in a constructor. + */ + public int getStartOffset() + { + return breakOffset; + } + + /** + * Create the new instance of this view. + * + * @param element the element (inherited from the HR view) + * @param offset the position where the HR view has been broken + */ + public Beginning(Element element, int offset) + { + super(element); + breakOffset = offset; + } + } + + /** + * Creates the new HR view. + */ + public HRuleView(Element element) + { + super(element); + } + + /** + * Returns the ForcedBreakWeight for the vertical axis, indicating, the the + * view must be broken to be displayed correctly. The horizontal dash is + * not breakeable along the Y axis. + */ + public int getBreakWeight(int axis, float pos, float len) + { + if (axis == X_AXIS && ((getEndOffset() - getStartOffset()) > 1)) + return ForcedBreakWeight; + else + return BadBreakWeight; + } + + /** + * Draws the double line, upped black and the lower light gray. + */ + public void paint(Graphics g, Shape a) + { + Rectangle bounds = a.getBounds(); + + int x = bounds.x; + int y = bounds.y; + + int w = bounds.x + bounds.width; + + // We move "half pixel up" from the actual horizontal position - + // this will be rounded to the closest actual int co-ordinate. + int h = bounds.y + (int) Math.round(bounds.height * 0.5 - 0.5); + + g.setColor(Color.black); + g.drawLine(x, y++, w, h++); + g.setColor(Color.lightGray); + g.drawLine(x, y, w, h); + } + + /** + * Break the view into this view and the invisible imaginary view that + * stays on the end of line that is broken by HR dash. The view is broken + * only if its length is longer than one (the two characters are expected + * in the initial length). + */ + public View breakView(int axis, int offset, float pos, float len) + { + if (getEndOffset() - getStartOffset() > 1) + return new Beginning(getElement(), offset); + else + return this; + } + + /** + * Returns the width of the container for the horizontal axis and the + * thickness of the dash area for the vertical axis. + */ + public float getMaximumSpan(int axis) + { + if (axis == X_AXIS) + { + Component container = getContainer(); + if (container != null) + return getContainer().getWidth(); + else + return 640; + } + else + return HEIGHT; + } + + /** + * Returns the same values as {@link #getMaximumSpan(int)} + */ + public float getPreferredSpan(int axis) + { + return getMaximumSpan(axis); + } +} diff --git a/libjava/classpath/javax/swing/text/html/HTMLDocument.java b/libjava/classpath/javax/swing/text/html/HTMLDocument.java index e714a857b61..0bfc338df45 100644 --- a/libjava/classpath/javax/swing/text/html/HTMLDocument.java +++ b/libjava/classpath/javax/swing/text/html/HTMLDocument.java @@ -40,14 +40,18 @@ package javax.swing.text.html; import gnu.classpath.NotImplementedException; import gnu.javax.swing.text.html.CharacterAttributeTranslator; +import gnu.javax.swing.text.html.parser.htmlAttributeSet; import java.io.IOException; +import java.io.StringReader; import java.net.URL; import java.util.HashMap; import java.util.Stack; import java.util.Vector; import javax.swing.JEditorPane; +import javax.swing.event.DocumentEvent; +import javax.swing.event.HyperlinkEvent.EventType; import javax.swing.text.AbstractDocument; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; @@ -515,19 +519,23 @@ public class HTMLDocument extends DefaultStyledDocument */ public class HTMLReader extends HTMLEditorKit.ParserCallback { - /** Holds the current character attribute set **/ + /** + * Holds the current character attribute set * + */ protected MutableAttributeSet charAttr = new SimpleAttributeSet(); protected Vector parseBuffer = new Vector(); - /** A stack for character attribute sets **/ + /** + * A stack for character attribute sets * + */ Stack charAttrStack = new Stack(); /** * The parse stack. This stack holds HTML.Tag objects that reflect the * current position in the parsing process. */ - private Stack parseStack = new Stack(); + Stack parseStack = new Stack(); /** A mapping between HTML.Tag objects and the actions that handle them **/ HashMap tagToAction; @@ -535,10 +543,31 @@ public class HTMLDocument extends DefaultStyledDocument /** Tells us whether we've received the '</html>' tag yet **/ boolean endHTMLEncountered = false; - /** Variables related to the constructor with explicit insertTag **/ - int popDepth, pushDepth, offset; + /** + * Related to the constructor with explicit insertTag + */ + int popDepth; + + /** + * Related to the constructor with explicit insertTag + */ + int pushDepth; + + /** + * Related to the constructor with explicit insertTag + */ + int offset; + + /** + * The tag (inclusve), after that the insertion should start. + */ HTML.Tag insertTag; - boolean insertTagEncountered = false; + + /** + * This variable becomes true after the insert tag has been encountered. + */ + boolean insertTagEncountered; + /** A temporary variable that helps with the printing out of debug information **/ boolean debug = false; @@ -1139,8 +1168,21 @@ public class HTMLDocument extends DefaultStyledDocument } /** - * This method is called by the parser and should route the call to - * the proper handler for the tag. + * Checks if the HTML tag should be inserted. The tags before insert tag (if + * specified) are not inserted. Also, the tags after the end of the html are + * not inserted. + * + * @return true if the tag should be inserted, false otherwise. + */ + private boolean shouldInsert() + { + return ! endHTMLEncountered + && (insertTagEncountered || insertTag == null); + } + + /** + * This method is called by the parser and should route the call to the + * proper handler for the tag. * * @param t the HTML.Tag * @param a the attribute set @@ -1148,13 +1190,15 @@ public class HTMLDocument extends DefaultStyledDocument */ public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) { - // Don't call the Action if we've already seen </html>. - if (endHTMLEncountered) - return; - - TagAction action = (TagAction) tagToAction.get(t); - if (action != null) - action.start(t, a); + if (t == insertTag) + insertTagEncountered = true; + + if (shouldInsert()) + { + TagAction action = (TagAction) tagToAction.get(t); + if (action != null) + action.start(t, a); + } } /** @@ -1165,42 +1209,41 @@ public class HTMLDocument extends DefaultStyledDocument */ public void handleComment(char[] data, int pos) { - // Don't call the Action if we've already seen </html>. - if (endHTMLEncountered) - return; - - TagAction action = (TagAction) tagToAction.get(HTML.Tag.COMMENT); - if (action != null) + if (shouldInsert()) { - action.start(HTML.Tag.COMMENT, new SimpleAttributeSet()); - action.end (HTML.Tag.COMMENT); + TagAction action = (TagAction) tagToAction.get(HTML.Tag.COMMENT); + if (action != null) + { + action.start(HTML.Tag.COMMENT, + htmlAttributeSet.EMPTY_HTML_ATTRIBUTE_SET); + action.end(HTML.Tag.COMMENT); + } } } /** - * This method is called by the parser and should route the call to - * the proper handler for the tag. + * This method is called by the parser and should route the call to the + * proper handler for the tag. * * @param t the HTML.Tag * @param pos the position at which the tag was encountered */ public void handleEndTag(HTML.Tag t, int pos) { - // Don't call the Action if we've already seen </html>. - if (endHTMLEncountered) - return; - - // If this is the </html> tag we need to stop calling the Actions - if (t == HTML.Tag.HTML) - endHTMLEncountered = true; - - TagAction action = (TagAction) tagToAction.get(t); - if (action != null) - action.end(t); + if (shouldInsert()) + { + // If this is the </html> tag we need to stop calling the Actions + if (t == HTML.Tag.HTML) + endHTMLEncountered = true; + + TagAction action = (TagAction) tagToAction.get(t); + if (action != null) + action.end(t); + } } /** - * This is a callback from the parser that should be routed to the + * This is a callback from the parser that should be routed to the * appropriate handler for the tag. * * @param t the HTML.Tag that was encountered @@ -1209,15 +1252,17 @@ public class HTMLDocument extends DefaultStyledDocument */ public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) { - // Don't call the Action if we've already seen </html>. - if (endHTMLEncountered) - return; - - TagAction action = (TagAction) tagToAction.get (t); - if (action != null) + if (t == insertTag) + insertTagEncountered = true; + + if (shouldInsert()) { - action.start(t, a); - action.end(t); + TagAction action = (TagAction) tagToAction.get(t); + if (action != null) + { + action.start(t, a); + action.end(t); + } } } @@ -1230,7 +1275,6 @@ public class HTMLDocument extends DefaultStyledDocument * @since 1.3 */ public void handleEndOfLineString(String eol) - throws NotImplementedException { // FIXME: Implement. print ("HTMLReader.handleEndOfLineString not implemented yet"); @@ -1273,16 +1317,6 @@ public class HTMLDocument extends DefaultStyledDocument printBuffer(); DefaultStyledDocument.ElementSpec element; - // If the previous tag is content and the parent is p-implied, then - // we must also close the p-implied. - if (parseStack.size() > 0 && parseStack.peek() == HTML.Tag.IMPLIED) - { - element = new DefaultStyledDocument.ElementSpec(null, - DefaultStyledDocument.ElementSpec.EndTagType); - parseBuffer.addElement(element); - parseStack.pop(); - } - parseStack.push(t); AbstractDocument.AttributeContext ctx = getAttributeContext(); AttributeSet copy = attr.copyAttributes(); @@ -1320,16 +1354,6 @@ public class HTMLDocument extends DefaultStyledDocument new char[0], 0, 0); parseBuffer.add(element); } - // If the previous tag is content and the parent is p-implied, then - // we must also close the p-implied. - else if (parseStack.peek() == HTML.Tag.IMPLIED) - { - element = new DefaultStyledDocument.ElementSpec(null, - DefaultStyledDocument.ElementSpec.EndTagType); - parseBuffer.addElement(element); - if (parseStack.size() > 0) - parseStack.pop(); - } element = new DefaultStyledDocument.ElementSpec(null, DefaultStyledDocument.ElementSpec.EndTagType); @@ -1369,27 +1393,6 @@ public class HTMLDocument extends DefaultStyledDocument DefaultStyledDocument.ElementSpec element; AttributeSet attributes = null; - // Content must always be embedded inside a paragraph element, - // so we create this if the previous element is not one of - // <p>, <h1> .. <h6>. - boolean createImpliedParagraph = false; - HTML.Tag parent = (HTML.Tag) parseStack.peek(); - if (parent != HTML.Tag.P && parent != HTML.Tag.H1 - && parent != HTML.Tag.H2 - && parent != HTML.Tag.H3 && parent != HTML.Tag.H4 - && parent != HTML.Tag.H5 && parent != HTML.Tag.H6 - && parent != HTML.Tag.TD) - { - attributes = ctx.getEmptySet(); - attributes = ctx.addAttribute(attributes, - StyleConstants.NameAttribute, - HTML.Tag.IMPLIED); - element = new DefaultStyledDocument.ElementSpec(attributes, - DefaultStyledDocument.ElementSpec.StartTagType); - parseBuffer.add(element); - parseStack.push(HTML.Tag.IMPLIED); - } - // Copy the attribute set, don't use the same object because // it may change if (charAttr != null) @@ -1433,14 +1436,14 @@ public class HTMLDocument extends DefaultStyledDocument // Migrate from the rather htmlAttributeSet to the faster, lighter and // unchangeable alternative implementation. AttributeSet copy = a.copyAttributes(); - - // TODO: Figure out why we must always insert this single character - // (otherwise the element does not appear). Either fix or add explaining - // comment or at least report a normal bug. - DefaultStyledDocument.ElementSpec spec; - spec = new DefaultStyledDocument.ElementSpec(copy, - DefaultStyledDocument.ElementSpec.ContentType, - new char[] {' '}, 0, 1 ); + + // The two spaces are required because some special elements like HR + // must be broken. At least two characters are needed to break into the + // two parts. + DefaultStyledDocument.ElementSpec spec = + new DefaultStyledDocument.ElementSpec(copy, + DefaultStyledDocument.ElementSpec.ContentType, + new char[] {' ', ' '}, 0, 2 ); parseBuffer.add(spec); } @@ -1481,7 +1484,61 @@ public class HTMLDocument extends DefaultStyledDocument HTML.Tag insertTag) { return new HTMLReader(pos, popDepth, pushDepth, insertTag); - } + } + + /** + * Gets the reader for the parser to use when inserting the HTML fragment into + * the document. Checks if the parser is present, sets the parent in the + * element stack and removes any actions for BODY (it can be only one body in + * a HTMLDocument). + * + * @param pos - the starting position + * @param popDepth - the number of EndTagTypes to generate before inserting + * @param pushDepth - the number of StartTagTypes with a direction of + * JoinNextDirection that should be generated before inserting, but + * after the end tags have been generated. + * @param insertTag - the first tag to start inserting into document + * @param parent the element that will be the parent in the document. HTML + * parsing includes checks for the parent, so it must be available. + * @return - the reader + * @throws IllegalStateException if the parsert is not set. + */ + public HTMLEditorKit.ParserCallback getInsertingReader(int pos, int popDepth, + int pushDepth, + HTML.Tag insertTag, + final Element parent) + throws IllegalStateException + { + if (parser == null) + throw new IllegalStateException("Parser has not been set"); + + HTMLReader reader = new HTMLReader(pos, popDepth, pushDepth, insertTag) + { + /** + * Ignore BODY. + */ + public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) + { + if (t != HTML.Tag.BODY) + super.handleStartTag(t, a, pos); + } + + /** + * Ignore BODY. + */ + public void handleEndTag(HTML.Tag t, int pos) + { + if (t != HTML.Tag.BODY) + super.handleEndTag(t, pos); + } + }; + + // Set the parent HTML tag. + reader.parseStack.push(parent.getAttributes().getAttribute( + StyleConstants.NameAttribute)); + + return reader; + } /** * Gets the child element that contains the attribute with the value or null. @@ -1490,8 +1547,8 @@ public class HTMLDocument extends DefaultStyledDocument * @param e - the element to begin search at * @param attribute - the desired attribute * @param value - the desired value - * @return the element found with the attribute and value specified or null - * if it is not found. + * @return the element found with the attribute and value specified or null if + * it is not found. */ public Element getElement(Element e, Object attribute, Object value) { @@ -1516,16 +1573,17 @@ public class HTMLDocument extends DefaultStyledDocument } /** - * Returns the element that has the given id Attribute. If it is not found, - * null is returned. This method works on an Attribute, not a character tag. - * This is not thread-safe. + * Returns the element that has the given id Attribute (for instance, <p id + * ='my paragraph >'). If it is not found, null is returned. The HTML tag, + * having this attribute, is not checked by this method and can be any. The + * method is not thread-safe. * - * @param attrId - the Attribute id to look for + * @param attrId - the value of the attribute id to look for * @return the element that has the given id. */ public Element getElement(String attrId) { - return getElement(getDefaultRootElement(), HTML.getAttributeKey(attrId), + return getElement(getDefaultRootElement(), HTML.Attribute.ID, attrId); } @@ -1542,22 +1600,30 @@ public class HTMLDocument extends DefaultStyledDocument * @throws IllegalStateException - if an HTMLEditorKit.Parser has not been set */ public void setInnerHTML(Element elem, String htmlText) - throws BadLocationException, IOException, NotImplementedException + throws BadLocationException, IOException { if (elem.isLeaf()) throw new IllegalArgumentException("Element is a leaf"); - if (parser == null) - throw new IllegalStateException("Parser has not been set"); - // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit? - System.out.println("setInnerHTML not implemented"); + + int start = elem.getStartOffset(); + int end = elem.getEndOffset(); + + HTMLEditorKit.ParserCallback reader = getInsertingReader( + end, 0, 0, HTML.Tag.BODY, elem); + + // TODO charset + getParser().parse(new StringReader(htmlText), reader, true); + + // Remove the previous content + remove(start, end - start); } /** - * Replaces the given element in the parent with the string. When replacing - * a leaf, this will attempt to make sure there is a newline present if one is - * needed. This may result in an additional element being inserted. - * This will be seen as at least two events, n inserts followed by a remove. - * The HTMLEditorKit.Parser must be set. + * Replaces the given element in the parent with the string. When replacing a + * leaf, this will attempt to make sure there is a newline present if one is + * needed. This may result in an additional element being inserted. This will + * be seen as at least two events, n inserts followed by a remove. The + * HTMLEditorKit.Parser must be set. * * @param elem - the branch element whose parent will be replaced * @param htmlText - the string to be parsed and assigned to elem @@ -1565,18 +1631,25 @@ public class HTMLDocument extends DefaultStyledDocument * @throws IOException * @throws IllegalStateException - if parser is not set */ - public void setOuterHTML(Element elem, String htmlText) - throws BadLocationException, IOException, NotImplementedException - { - if (parser == null) - throw new IllegalStateException("Parser has not been set"); - // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit? - System.out.println("setOuterHTML not implemented"); - } +public void setOuterHTML(Element elem, String htmlText) + throws BadLocationException, IOException + { + // Remove the current element: + int start = elem.getStartOffset(); + int end = elem.getEndOffset(); + + remove(start, end-start); + + HTMLEditorKit.ParserCallback reader = getInsertingReader( + start, 0, 0, HTML.Tag.BODY, elem); + + // TODO charset + getParser().parse(new StringReader(htmlText), reader, true); + } /** - * Inserts the string before the start of the given element. - * The parser must be set. + * Inserts the string before the start of the given element. The parser must + * be set. * * @param elem - the element to be the root for the new text. * @param htmlText - the string to be parsed and assigned to elem @@ -1585,18 +1658,19 @@ public class HTMLDocument extends DefaultStyledDocument * @throws IllegalStateException - if parser has not been set */ public void insertBeforeStart(Element elem, String htmlText) - throws BadLocationException, IOException, NotImplementedException + throws BadLocationException, IOException { - if (parser == null) - throw new IllegalStateException("Parser has not been set"); - // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit? - System.out.println("insertBeforeStart not implemented"); + HTMLEditorKit.ParserCallback reader = getInsertingReader( + elem.getStartOffset(), 0, 0, HTML.Tag.BODY, elem); + + // TODO charset + getParser().parse(new StringReader(htmlText), reader, true); } /** - * Inserts the string at the end of the element. If elem's children - * are leaves, and the character at elem.getEndOffset() - 1 is a newline, - * then it will be inserted before the newline. The parser must be set. + * Inserts the string at the end of the element. If elem's children are + * leaves, and the character at elem.getEndOffset() - 1 is a newline, then it + * will be inserted before the newline. The parser must be set. * * @param elem - the element to be the root for the new text * @param htmlText - the text to insert @@ -1605,12 +1679,14 @@ public class HTMLDocument extends DefaultStyledDocument * @throws IllegalStateException - if parser is not set */ public void insertBeforeEnd(Element elem, String htmlText) - throws BadLocationException, IOException, NotImplementedException + throws BadLocationException, IOException { - if (parser == null) - throw new IllegalStateException("Parser has not been set"); - // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit? - System.out.println("insertBeforeEnd not implemented"); + HTMLEditorKit.ParserCallback reader = getInsertingReader( + elem.getEndOffset(), 0, 0, HTML.Tag.BODY, elem); + + // TODO charset + getParser().parse(new StringReader(htmlText), reader, true); + } /** @@ -1624,12 +1700,13 @@ public class HTMLDocument extends DefaultStyledDocument * @throws IllegalStateException - if parser is not set */ public void insertAfterEnd(Element elem, String htmlText) - throws BadLocationException, IOException, NotImplementedException + throws BadLocationException, IOException { - if (parser == null) - throw new IllegalStateException("Parser has not been set"); - // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit? - System.out.println("insertAfterEnd not implemented"); + HTMLEditorKit.ParserCallback reader = getInsertingReader( + elem.getEndOffset(), 0, 0, HTML.Tag.BODY, elem); + + // TODO charset + getParser().parse(new StringReader(htmlText), reader, true); } /** @@ -1643,11 +1720,12 @@ public class HTMLDocument extends DefaultStyledDocument * @throws IllegalStateException - if parser is not set */ public void insertAfterStart(Element elem, String htmlText) - throws BadLocationException, IOException, NotImplementedException + throws BadLocationException, IOException { - if (parser == null) - throw new IllegalStateException("Parser has not been set"); - // FIXME: Not implemented fully, use InsertHTML* in HTMLEditorKit? - System.out.println("insertAfterStart not implemented"); + HTMLEditorKit.ParserCallback reader = getInsertingReader( + elem.getStartOffset(), 0, 0, HTML.Tag.BODY, elem); + + // TODO charset + getParser().parse(new StringReader(htmlText), reader, true); } } diff --git a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java index adda4922d57..5d77be8fdd4 100644 --- a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java +++ b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java @@ -71,6 +71,11 @@ import javax.swing.text.View; import javax.swing.text.ViewFactory; import javax.swing.text.html.parser.ParserDelegator; +/* Move these imports here after javax.swing.text.html to make it compile + with jikes. */ +import gnu.javax.swing.text.html.parser.GnuParserDelegator; +import gnu.javax.swing.text.html.parser.HTML_401Swing; + /** * @author Lillian Angel (langel at redhat dot com) */ @@ -557,19 +562,18 @@ public class HTMLEditorKit else if (tag == HTML.Tag.HEAD) view = new NullView(element); else if (tag.equals(HTML.Tag.TABLE)) - view = new HTMLTableView(element); + view = new javax.swing.text.html.TableView(element); else if (tag.equals(HTML.Tag.TD)) view = new ParagraphView(element); - + else if (tag.equals(HTML.Tag.HR)) + view = new HRuleView(element); + else if (tag.equals(HTML.Tag.BR)) + view = new BRView(element); /* else if (tag.equals(HTML.Tag.MENU) || tag.equals(HTML.Tag.DIR) || tag.equals(HTML.Tag.UL) || tag.equals(HTML.Tag.OL)) view = new ListView(element); - else if (tag.equals(HTML.Tag.HR)) - view = new HRuleView(element); - else if (tag.equals(HTML.Tag.BR)) - view = new BRView(element); else if (tag.equals(HTML.Tag.INPUT) || tag.equals(HTML.Tag.SELECT) || tag.equals(HTML.Tag.TEXTAREA)) view = new FormView(element); @@ -887,7 +891,9 @@ public class HTMLEditorKit protected Parser getParser() { if (parser == null) - parser = new ParserDelegator(); + { + parser = new GnuParserDelegator(HTML_401Swing.getInstance()); + } return parser; } diff --git a/libjava/classpath/javax/swing/text/html/StyleSheet.java b/libjava/classpath/javax/swing/text/html/StyleSheet.java index 2466a2808fe..d92abde7825 100644 --- a/libjava/classpath/javax/swing/text/html/StyleSheet.java +++ b/libjava/classpath/javax/swing/text/html/StyleSheet.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.text.html; +import gnu.javax.swing.text.html.CharacterAttributeTranslator; + import java.awt.Color; import java.awt.Font; import java.awt.Graphics; @@ -585,47 +587,15 @@ public class StyleSheet extends StyleContext } /** - * Converst a color string to a color. If it is not found, null is returned. - * - * @param color - the color string such as "RED" or "#NNNNNN" - * @return the Color, or null if not found. - */ - public Color stringToColor(String color) - { - color = color.toLowerCase(); - if (color.equals("black") || color.equals("#000000")) - return Color.BLACK; - else if (color.equals("aqua") || color.equals("#00FFFF")) - return new Color(127, 255, 212); - else if (color.equals("gray") || color.equals("#808080")) - return Color.GRAY; - else if (color.equals("navy") || color.equals("#000080")) - return new Color(0, 0, 128); - else if (color.equals("silver") || color.equals("#C0C0C0")) - return Color.LIGHT_GRAY; - else if (color.equals("green") || color.equals("#008000")) - return Color.GREEN; - else if (color.equals("olive") || color.equals("#808000")) - return new Color(128, 128, 0); - else if (color.equals("teal") || color.equals("#008080")) - return new Color(0, 128, 128); - else if (color.equals("blue") || color.equals("#0000FF")) - return Color.BLUE; - else if (color.equals("lime") || color.equals("#00FF00")) - return new Color(0, 255, 0); - else if (color.equals("purple") || color.equals("#800080")) - return new Color(128, 0, 128); - else if (color.equals("white") || color.equals("#FFFFFF")) - return Color.WHITE; - else if (color.equals("fuchsia") || color.equals("#FF00FF")) - return Color.MAGENTA; - else if (color.equals("maroon") || color.equals("#800000")) - return new Color(128, 0, 0); - else if (color.equals("Red") || color.equals("#FF0000")) - return Color.RED; - else if (color.equals("Yellow") || color.equals("#FFFF00")) - return Color.YELLOW; - return null; + * Convert the color string represenation into java.awt.Color. The valid + * values are like "aqua" , "#00FFFF" or "rgb(1,6,44)". + * + * @param colorName the color to convert. + * @return the matching java.awt.color + */ + public Color stringToColor(String colorName) + { + return CharacterAttributeTranslator.getColor(colorName); } /** diff --git a/libjava/classpath/javax/swing/text/html/TableView.java b/libjava/classpath/javax/swing/text/html/TableView.java new file mode 100644 index 00000000000..c2edc8cdd64 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/TableView.java @@ -0,0 +1,137 @@ +/* TableView.java -- A table view for HTML tables + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html; + +import javax.swing.text.Document; +import javax.swing.text.Element; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; + +/** + * A conrete implementation of TableView that renders HTML tables. + * + * @author Roman Kennke (kennke@aicas.com) + */ +class TableView + extends javax.swing.text.TableView +{ + /** + * Represents a single table row. + */ + public class RowView extends TableRow + { + /** + * Creates a new instance of the <code>RowView</code>. + * + * @param el the element for which to create a row view + */ + public RowView(Element el) + { + super(el); + } + + /** + * Get the associated style sheet from the document. + * + * @return the associated style sheet. + */ + protected StyleSheet getStyleSheet() + { + Document d = getElement().getDocument(); + if (d instanceof HTMLDocument) + return ((HTMLDocument) d).getStyleSheet(); + else + return null; + } + } + + /** + * Creates a new HTML table view for the specified element. + * + * @param el the element for the table view + */ + public TableView(Element el) + { + super(el); + } + + /** + * Get the associated style sheet from the document. + * + * @return the associated style sheet. + */ + protected StyleSheet getStyleSheet() + { + Document d = getElement().getDocument(); + if (d instanceof HTMLDocument) + return ((HTMLDocument) d).getStyleSheet(); + else + return null; + } + + /** + * Creates a view for a table row. + * + * @param el the element that represents the table row + * @return a view for rendering the table row + * (and instance of {@link RowView}). + */ + protected TableRow createTableRow(Element el) + { + return new RowView(el); + } + + /** + * Loads the children of the Table. This completely bypasses the ViewFactory + * and creates instances of TableRow instead. + * + * @param vf ignored + */ + protected void loadChildren(ViewFactory vf) + { + Element el = getElement(); + int numChildren = el.getElementCount(); + View[] rows = new View[numChildren]; + for (int i = 0; i < numChildren; ++i) + { + rows[i] = createTableRow(el.getElement(i)); + } + replace(0, getViewCount(), rows); + } +} diff --git a/libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java b/libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java index e5d2db4df7c..70636d92923 100644 --- a/libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java +++ b/libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java @@ -52,9 +52,6 @@ import javax.swing.text.html.HTMLEditorKit.ParserCallback; * This class instantiates and starts the working instance of * html parser, being responsible for providing the default DTD. * - * TODO Later this class must be derived from the totally abstract class - * HTMLEditorKit.Parser. HTMLEditorKit that does not yet exist. - * * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) */ public class ParserDelegator diff --git a/libjava/classpath/javax/swing/text/rtf/RTFParser.java b/libjava/classpath/javax/swing/text/rtf/RTFParser.java index 4f0f967c117..de1b1c6ff15 100644 --- a/libjava/classpath/javax/swing/text/rtf/RTFParser.java +++ b/libjava/classpath/javax/swing/text/rtf/RTFParser.java @@ -140,9 +140,17 @@ class RTFParser parseHeader(); parseDocument(); - Token t2 = scanner.readToken(); - if (t2.type != Token.RCURLY) - throw new RTFParseException("expected right curly braces"); + Token t2 = scanner.peekToken(); + if (t2.type == Token.RCURLY) + { + // Eat the token. + scanner.readToken(); + } + else + { + // Ignore this for maximum robustness when file is broken. + System.err.println("RTF warning: expected right curly braces"); + } } diff --git a/libjava/classpath/javax/swing/text/rtf/RTFScanner.java b/libjava/classpath/javax/swing/text/rtf/RTFScanner.java index 3cdd6e8e0b9..060e087eb67 100644 --- a/libjava/classpath/javax/swing/text/rtf/RTFScanner.java +++ b/libjava/classpath/javax/swing/text/rtf/RTFScanner.java @@ -71,6 +71,11 @@ class RTFScanner private StringBuffer buffer; /** + * Lookahead token. + */ + private Token lastToken; + + /** * Constructs a new RTFScanner without initializing the {@link Reader}. */ private RTFScanner() @@ -120,7 +125,7 @@ class RTFScanner * * @throws IOException if the underlying stream has problems */ - public Token readToken() + private Token readTokenImpl() throws IOException { Token token = null; @@ -156,6 +161,27 @@ class RTFScanner return token; } + Token peekToken() + throws IOException + { + lastToken = readTokenImpl(); + return lastToken; + } + + Token readToken() + throws IOException + { + Token token; + if (lastToken != null) + { + token = lastToken; + lastToken = null; + } + else + token = readTokenImpl(); + return token; + } + /** * Reads in a control word and optional parameter. * diff --git a/libjava/classpath/javax/swing/tree/TreePath.java b/libjava/classpath/javax/swing/tree/TreePath.java index 93b59b07edf..1c4d78787f4 100644 --- a/libjava/classpath/javax/swing/tree/TreePath.java +++ b/libjava/classpath/javax/swing/tree/TreePath.java @@ -174,7 +174,7 @@ public class TreePath implements Serializable return false; for (index = 0; index < path.length; index++) { - if (!treepath[index].equals(path[index])) + if (!path[index].equals(treepath[index])) return false; } |