diff options
author | mark <mark@138bc75d-0d04-0410-961f-82ee72b054a4> | 2006-03-10 21:46:48 +0000 |
---|---|---|
committer | mark <mark@138bc75d-0d04-0410-961f-82ee72b054a4> | 2006-03-10 21:46:48 +0000 |
commit | ce57ab760f69de6db452def7ffbf5b114a2d8694 (patch) | |
tree | ea38c56431c5d4528fb54254c3f8e50f517bede3 /libjava/classpath/javax/swing | |
parent | 50996fe55769882de3f410896032c887f0ff0d04 (diff) | |
download | gcc-ce57ab760f69de6db452def7ffbf5b114a2d8694.tar.gz |
Imported GNU Classpath 0.90
* scripts/makemake.tcl: Set gnu/java/awt/peer/swing to ignore.
* gnu/classpath/jdwp/VMFrame.java (SIZE): New constant.
* java/lang/VMCompiler.java: Use gnu.java.security.hash.MD5.
* java/lang/Math.java: New override file.
* java/lang/Character.java: Merged from Classpath.
(start, end): Now 'int's.
(canonicalName): New field.
(CANONICAL_NAME, NO_SPACES_NAME, CONSTANT_NAME): New constants.
(UnicodeBlock): Added argument.
(of): New overload.
(forName): New method.
Updated unicode blocks.
(sets): Updated.
* sources.am: Regenerated.
* Makefile.in: Likewise.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@111942 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava/classpath/javax/swing')
152 files changed, 15186 insertions, 5620 deletions
diff --git a/libjava/classpath/javax/swing/AbstractAction.java b/libjava/classpath/javax/swing/AbstractAction.java index bd3167e1e93..4a2334570aa 100644 --- a/libjava/classpath/javax/swing/AbstractAction.java +++ b/libjava/classpath/javax/swing/AbstractAction.java @@ -1,5 +1,5 @@ /* AbstractAction.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,6 +38,7 @@ exception statement from your version. */ package javax.swing; +import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.IOException; import java.io.ObjectInputStream; @@ -74,28 +75,30 @@ public abstract class AbstractAction private transient HashMap store = new HashMap(); /** - * Creates a new action with an empty string for the name. All other - * properties are initialised to <code>null</code> + * Creates a new action with no properties set. */ public AbstractAction() { - this(null); + // Nothing to do. } /** - * Creates a new action with the specified name. All other properties are - * initialised to <code>null</code>. + * Creates a new action with the specified name. The name is stored as a + * property with the key {@link Action#NAME}, and no other properties are + * initialised. * * @param name the name (<code>null</code> permitted). */ public AbstractAction(String name) { - this(name, null); + putValue(NAME, name); } /** - * Creates a new action with the specified name and icon. All other - * properties are initialised to <code>null</code>. + * Creates a new action with the specified name and icon. The name is stored + * as a property with the key {@link Action#NAME}, the icon is stored as a + * property with the key {@link Action#SMALL_ICON}, and no other properties + * are initialised. * * @param name the name (<code>null</code> permitted). * @param icon the icon (<code>null</code> permitted). @@ -133,11 +136,12 @@ public abstract class AbstractAction } /** - * clone + * Returns a clone of the action. * - * @return Object + * @return A clone of the action. * - * @exception CloneNotSupportedException TODO + * @exception CloneNotSupportedException if there is a problem cloning the + * action. */ protected Object clone() throws CloneNotSupportedException { @@ -153,6 +157,8 @@ public abstract class AbstractAction * * @return The value associated with the specified key, or * <code>null</code> if the key is not found. + * + * @see #putValue(String, Object) */ public Object getValue(String key) { @@ -162,11 +168,17 @@ public abstract class AbstractAction /** * Sets the value associated with the specified key and sends a * {@link java.beans.PropertyChangeEvent} to all registered listeners. - * The standard keys are: {@link #NAME}, {@link #SHORT_DESCRIPTION}, - * {@link #LONG_DESCRIPTION}, {@link #SMALL_ICON}, - * {@link #ACTION_COMMAND_KEY}, {@link #ACCELERATOR_KEY} and - * {@link #MNEMONIC_KEY}. Any existing value associated with the key will be - * overwritten. + * The standard keys are: + * <ul> + * <li>{@link #NAME}</li> + * <li>{@link #SHORT_DESCRIPTION}</li> + * <li>{@link #LONG_DESCRIPTION}</li> + * <li>{@link #SMALL_ICON}</li> + * <li>{@link #ACTION_COMMAND_KEY}</li> + * <li>{@link #ACCELERATOR_KEY}</li> + * <li>{@link #MNEMONIC_KEY}</li> + * </ul> + * Any existing value associated with the key will be overwritten. * * @param key the key (not <code>null</code>). * @param value the value (<code>null</code> permitted). @@ -174,7 +186,7 @@ public abstract class AbstractAction public void putValue(String key, Object value) { Object old = getValue(key); - if (old == null || !old.equals(value)) + if ((old == null && value != null) || (old != null && !old.equals(value))) { store.put(key, value); firePropertyChange(key, old, value); @@ -185,6 +197,8 @@ public abstract class AbstractAction * Returns the flag that indicates whether or not the action is enabled. * * @return The flag. + * + * @see #setEnabled(boolean) */ public boolean isEnabled() { @@ -194,9 +208,12 @@ public abstract class AbstractAction /** * Sets the flag that indicates whether or not the action is enabled and, if * the value of the flag changed from the previous setting, sends a - * {@link java.beans.PropertyChangeEvent} to all registered listeners. + * {@link java.beans.PropertyChangeEvent} to all registered listeners (using + * the property name 'enabled'). * * @param enabled the new flag value. + * + * @see #isEnabled() */ public void setEnabled(boolean enabled) { @@ -208,8 +225,11 @@ public abstract class AbstractAction } /** - * getKeys - * @returns Object[] + * Returns an array of the keys for the property values that have been + * defined via the {@link #putValue(String, Object)} method (or the class + * constructor). + * + * @return An array of keys. */ public Object[] getKeys() { @@ -217,12 +237,12 @@ public abstract class AbstractAction } /** - * This method fires a PropertyChangeEvent given the propertyName - * and the old and new values. + * Sends a {@link PropertyChangeEvent} for the named property to all + * registered listeners. * - * @param propertyName The property that changed. - * @param oldValue The old value of the property. - * @param newValue The new value of the property. + * @param propertyName the property name. + * @param oldValue the old value of the property. + * @param newValue the new value of the property. */ protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) @@ -231,22 +251,27 @@ public abstract class AbstractAction } /** - * This convenience method fires a PropertyChangeEvent given - * the propertyName and the old and new values. + * Sends a {@link PropertyChangeEvent} for the named property to all + * registered listeners. This private method is called by the + * {@link #setEnabled(boolean)} method. * - * @param propertyName The property that changed. - * @param oldValue The old value of the property. - * @param newValue The new value of the property. + * @param propertyName the property name. + * @param oldValue the old value of the property. + * @param newValue the new value of the property. */ - private void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) + private void firePropertyChange(String propertyName, boolean oldValue, + boolean newValue) { changeSupport.firePropertyChange(propertyName, oldValue, newValue); } /** - * addPropertyChangeListener + * Registers a listener to receive {@link PropertyChangeEvent} notifications + * from this action. * - * @param listener the listener to add + * @param listener the listener. + * + * @see #removePropertyChangeListener(PropertyChangeListener) */ public void addPropertyChangeListener(PropertyChangeListener listener) { @@ -254,9 +279,12 @@ public abstract class AbstractAction } /** - * removePropertyChangeListener + * Deregisters a listener so that it no longer receives + * {@link PropertyChangeEvent} notifications from this action. * - * @param listener the listener to remove + * @param listener the listener. + * + * @see #addPropertyChangeListener(PropertyChangeListener) */ public void removePropertyChangeListener(PropertyChangeListener listener) { @@ -266,7 +294,7 @@ public abstract class AbstractAction /** * Returns all registered listeners. * - * @return array of listeners. + * @return An array of listeners. * * @since 1.4 */ diff --git a/libjava/classpath/javax/swing/AbstractButton.java b/libjava/classpath/javax/swing/AbstractButton.java index 376b3a056ae..3d289084e20 100644 --- a/libjava/classpath/javax/swing/AbstractButton.java +++ b/libjava/classpath/javax/swing/AbstractButton.java @@ -159,6 +159,14 @@ public abstract class AbstractButton extends JComponent private static final long serialVersionUID = 1471056094226600578L; /** + * The spec has no public/protected constructor for this class, so do we. + */ + ButtonChangeListener() + { + // Nothing to do here. + } + + /** * Notified when the target of the listener changes its state. * * @param ev the ChangeEvent describing the change diff --git a/libjava/classpath/javax/swing/AbstractCellEditor.java b/libjava/classpath/javax/swing/AbstractCellEditor.java index 4ed15809a83..df0d3db12b5 100644 --- a/libjava/classpath/javax/swing/AbstractCellEditor.java +++ b/libjava/classpath/javax/swing/AbstractCellEditor.java @@ -1,5 +1,5 @@ /* AbstractCellEditor.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. @@ -69,10 +69,11 @@ public abstract class AbstractCellEditor /** * Creates a new instance of AbstractCellEditor. */ - public AbstractCellEditor() { + public AbstractCellEditor() + { listenerList = new EventListenerList(); changeEvent = new ChangeEvent(this); - } // AbstractCellEditor() + } /** * Returns <code>true</code> if the cell is editable using @@ -84,9 +85,10 @@ public abstract class AbstractCellEditor * @return <code>true</code> if the cell is editable using * <code>event</code>, <code>false</code> if it's not */ - public boolean isCellEditable(EventObject event) { + public boolean isCellEditable(EventObject event) + { return true; - } // isCellEditable() + } /** * Returns <code>true</code> if the editing cell should be selected, @@ -99,29 +101,32 @@ public abstract class AbstractCellEditor * @return <code>true</code> if the editing cell should be selected, * <code>false</code> otherwise */ - public boolean shouldSelectCell(EventObject event) { + public boolean shouldSelectCell(EventObject event) + { return true; - } // shouldSelectCell() + } /** * Stop editing the cell and accept any partial value that has been entered * into the cell. * - * @returns <code>true</code> if editing has been stopped successfully, + * @return <code>true</code> if editing has been stopped successfully, * <code>false</code>otherwise */ - public boolean stopCellEditing() { + public boolean stopCellEditing() + { fireEditingStopped(); return true; - } // stopCellEditing() + } /** * Stop editing the cell and do not accept any partial value that has * been entered into the cell. */ - public void cancelCellEditing() { + public void cancelCellEditing() + { fireEditingCanceled(); - } // cancelCellEditing() + } /** * Adds a CellEditorListener to the list of CellEditorListeners of this diff --git a/libjava/classpath/javax/swing/AbstractListModel.java b/libjava/classpath/javax/swing/AbstractListModel.java index 8973e529232..4b89689ddda 100644 --- a/libjava/classpath/javax/swing/AbstractListModel.java +++ b/libjava/classpath/javax/swing/AbstractListModel.java @@ -1,5 +1,5 @@ /* AbstractListModel.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. @@ -58,6 +58,9 @@ public abstract class AbstractListModel implements ListModel, Serializable /** List of ListDataListeners called for each change to the list. */ protected EventListenerList listenerList; + /** + * Creates a new model instance - initialises the event listener list. + */ public AbstractListModel() { listenerList = new EventListenerList(); @@ -88,7 +91,7 @@ public abstract class AbstractListModel implements ListModel, Serializable /** * Call {@link ListDataListener#contentsChanged} on each element of the * {@link #listenerList} which is a {@link ListDataListener}. The event - * fired has type {@ListDataEvent.CONTENTS_CHANGED} and represents a + * fired has type {@link ListDataEvent#CONTENTS_CHANGED} and represents a * change to the data elements in the range [startIndex, endIndex] * inclusive. * @@ -110,7 +113,7 @@ public abstract class AbstractListModel implements ListModel, Serializable /** * Call {@link ListDataListener#intervalAdded} on each element of the * {@link #listenerList} which is a {@link ListDataListener}. The event - * fired has type {@ListDataEvent.INTERVAL_ADDED} and represents an + * fired has type {@link ListDataEvent#INTERVAL_ADDED} and represents an * addition of the data elements in the range [startIndex, endIndex] * inclusive. * @@ -132,7 +135,7 @@ public abstract class AbstractListModel implements ListModel, Serializable /** * Call {@link ListDataListener#intervalRemoved} on each element of the * {@link #listenerList} which is a {@link ListDataListener}. The event - * fired has type {@ListDataEvent.INTERVAL_REMOVED} and represents a + * fired has type {@link ListDataEvent#INTERVAL_REMOVED} and represents a * removal of the data elements in the range [startIndex, endIndex] * inclusive. * diff --git a/libjava/classpath/javax/swing/CellEditor.java b/libjava/classpath/javax/swing/CellEditor.java index 3d229b26675..9eb083ab25d 100644 --- a/libjava/classpath/javax/swing/CellEditor.java +++ b/libjava/classpath/javax/swing/CellEditor.java @@ -1,5 +1,5 @@ /* CellEditor.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. @@ -41,6 +41,7 @@ package javax.swing; import java.util.EventObject; import javax.swing.event.CellEditorListener; +import javax.swing.event.ChangeEvent; /** * Provides edit capabilities for components that display cells like @@ -51,46 +52,57 @@ import javax.swing.event.CellEditorListener; public interface CellEditor { /** - * getCellEditorValue - * @returns Object + * Returns the current value for the <code>CellEditor</code>. + * + * @return The value. */ Object getCellEditorValue(); /** - * isCellEditable - * @param event TODO - * @returns boolean + * Returns <code>true</code> if the specified event makes the editor + * editable, and <code>false</code> otherwise. + * + * @param event the event. + * + * @return A boolean. */ boolean isCellEditable(EventObject event); /** * shouldSelectCell * @param event TODO - * @returns boolean + * @return boolean */ boolean shouldSelectCell(EventObject event); /** - * stopCellEditing - * @returns boolean + * Signals to the <code>CellEditor</code> that it should stop editing, + * accepting the current cell value, and returns <code>true</code> if the + * editor actually stops editing, and <code>false</code> otherwise. + * + * @return A boolean. */ boolean stopCellEditing(); /** - * cancelCellEditing + * Signals to the <code>CellEditor</code> that it should cancel editing. */ void cancelCellEditing(); /** - * addCellEditorListener - * @param listener TODO + * Registers a listener to receive {@link ChangeEvent} notifications from the + * <code>CellEditor</code>. + * + * @param listener the listener. */ void addCellEditorListener(CellEditorListener listener); /** - * removeCellEditorListener - * @param listener TODO + * Deregisters a listener so that it no longer receives {@link ChangeEvent} + * notifications from the <code>CellEditor</code>. + * + * @param listener the listener. */ void removeCellEditorListener(CellEditorListener listener); -} // CellEditor +} diff --git a/libjava/classpath/javax/swing/CellRendererPane.java b/libjava/classpath/javax/swing/CellRendererPane.java index c59afd3188a..b3d6f6a7364 100644 --- a/libjava/classpath/javax/swing/CellRendererPane.java +++ b/libjava/classpath/javax/swing/CellRendererPane.java @@ -1,5 +1,5 @@ /* CellRendererPane.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. @@ -75,7 +75,7 @@ public class CellRendererPane extends Container implements Accessible /** * getAccessibleRole - * @returns AccessibleRole + * @return AccessibleRole */ public AccessibleRole getAccessibleRole() { @@ -169,24 +169,32 @@ public class CellRendererPane extends Container implements Accessible addImpl(c, null, 0); Rectangle oldClip = graphics.getClipBounds(); - // translate to (x,y) - graphics.translate(x, y); - graphics.clipRect(0, 0, w, h); - // set bounds of c - c.setBounds(0, 0, w, h); - - // validate if necessary - if (shouldValidate) + boolean translated = false; + try { - c.validate(); + // translate to (x,y) + graphics.translate(x, y); + translated = true; + graphics.clipRect(0, 0, w, h); + // set bounds of c + c.setBounds(0, 0, w, h); + + // validate if necessary + if (shouldValidate) + { + c.validate(); + } + + // paint component + c.paint(graphics); + } + finally + { + // untranslate g + if (translated) + graphics.translate(-x, -y); + graphics.setClip(oldClip); } - - // paint component - c.paint(graphics); - - // untranslate g - graphics.translate(-x, -y); - graphics.setClip(oldClip); } /** diff --git a/libjava/classpath/javax/swing/ComboBoxModel.java b/libjava/classpath/javax/swing/ComboBoxModel.java index 6968db49091..61052758758 100644 --- a/libjava/classpath/javax/swing/ComboBoxModel.java +++ b/libjava/classpath/javax/swing/ComboBoxModel.java @@ -1,5 +1,5 @@ /* ComboBoxModel.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,31 +37,33 @@ exception statement from your version. */ package javax.swing; +import javax.swing.event.ListDataEvent; +import javax.swing.event.ListDataListener; /** - * The data model for {@link JComboBox}. This model keeps - * track of elements contained in the JComboBox as well as the current - * combo box selection. Whenever selection in the JComboBox changes, the - * ComboBoxModel should fire ListDataEvents to ComboBox's ListDataListeners. + * The data model for a {@link JComboBox}. This model keeps track of elements + * contained in the <code>JComboBox</code> as well as the current + * combo box selection. Whenever the selection in the <code>JComboBox</code> + * changes, the <code>ComboBoxModel</code> should fire a {@link ListDataEvent} + * to the model's {@link ListDataListener}s. * * @author Andrew Selkirk */ public interface ComboBoxModel extends ListModel { /** - * This method sets the selected item in the combo box. Class - * implementing this interface should fire ListDataEvents to - * all registered ListDataListeners to indicated that the - * selection has changed. + * Sets the selected item in the combo box. Classes implementing this + * interface should fire a {@link ListDataEvent} to all registered + * {@link ListDataListener}s to indicate that the selection has changed. * - * @param item item in the combo box that should be selected + * @param item the selected item (<code>null</code> permitted). */ void setSelectedItem(Object item); /** - * The method returns currently selected item in the combo box + * Returns the currently selected item in the combo box. * - * @returns item that is currently selected in the combo box. + * @return The selected item (possibly <code>null</code>). */ Object getSelectedItem(); -} // ComboBoxModel +} diff --git a/libjava/classpath/javax/swing/DefaultCellEditor.java b/libjava/classpath/javax/swing/DefaultCellEditor.java index 39e48551efb..7f1c395ad03 100644 --- a/libjava/classpath/javax/swing/DefaultCellEditor.java +++ b/libjava/classpath/javax/swing/DefaultCellEditor.java @@ -1,5 +1,5 @@ /* DefaultCellEditor.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. @@ -59,8 +59,7 @@ import javax.swing.tree.TreeCellEditor; * some standard object types. * * @author Andrew Selkirk - * - * @status mostly unimplemented + * @author Audrius Meskauskas */ public class DefaultCellEditor extends AbstractCellEditor @@ -69,17 +68,26 @@ public class DefaultCellEditor private static final long serialVersionUID = 3564035141373880027L; /** - * Delegates a couple of method calls (such as {@link #isCellEditable} - * to the component it contains and listens for events that indicate - * that editing has stopped. + * This changeable module access the editor component in the component + * specific way. For instance, to set the value for JTextField, we need to + * call setText(String), and for JCheckBox we need to call + * setSelected(boolean). Each default editor has the component specific + * derivative of this class. These derivatives are private inner classes of + * the DefaultCellEditor. + * + * The editor delegate is also set for the editor component as the action + * listener. It listens for the events that indicate that editing has stopped. */ protected class EditorDelegate implements ActionListener, ItemListener, Serializable { + /** + * Use the serial version UID for interoperability. + */ private static final long serialVersionUID = -1420007406015481933L; /** - * value + * The object value (updated when getting and setting the value). */ protected Object value; @@ -90,35 +98,38 @@ public class DefaultCellEditor { // Nothing to do here. } - + /** - * setValue + * Set the value for the editor component. This method is normally + * overridden to set the value in the way, specific for the text + * component, check box or combo box. * - * @param value TODO + * @param aValue the value to set (String, Boolean or Number). */ - public void setValue(Object value) + public void setValue(Object aValue) { - // TODO: should be setting the value in the editorComp - this.value = value; + value = aValue; } - /** - * getCellEditorValue - * - * @returns Object + /** + * Get the value for the editor component. This method is normally + * overridden to obtain the value in the way, specific for the text + * component, check box or combo box. + * + * @return value the value of the component (String, Boolean or Number). */ public Object getCellEditorValue() { - // TODO: should be getting the updated value from the editorComp return value; - } // getCellEditorValue() + } /** - * isCellEditable + * The default method returns true for the {@link MouseEvent} and false + * for any other events. * - * @param event TODO + * @param event the event to check * - * @returns boolean + * @return true if the passed event is the mouse event and false otherwise. */ public boolean isCellEditable(EventObject event) { @@ -129,22 +140,27 @@ public class DefaultCellEditor } // isCellEditable() /** - * shouldSelectCell + * Returns true to indicate that the editing cell can be selected. + * + * The default method returns true without action but may be overridden + * in derived classes for more specific behavior. * - * @param event TODO + * @param event unused in default method * - * @returns boolean + * @return true always */ public boolean shouldSelectCell(EventObject event) { // return true to indicate that the editing cell may be selected return true; - } // shouldSelectCell() + } /** - * stopCellEditing + * Finish the cell editing session. This method notifies the registered + * cell editor listeners (including the table) that the editing has been + * stopped. * - * @returns boolean + * @return boolean */ public boolean stopCellEditing() { @@ -153,7 +169,11 @@ public class DefaultCellEditor } // stopCellEditing() /** - * cancelCellEditing + * Cancel the cell editing session. This method notifies the registered + * cell editor listeners (including the table) that the editing has been + * canceled. + * + * @returns boolean */ public void cancelCellEditing() { @@ -161,11 +181,13 @@ public class DefaultCellEditor } // cancelCellEditing() /** - * startCellEditing + * Start editing session and returns true to indicate the editing has begun. + * The default method returns true without action but may be overridden + * in derived classes for more specific behavior. * - * @param event TODO - * - * @returns boolean + * @param event the event. + * + * @return true, always */ public boolean startCellEditing(EventObject event) { @@ -174,9 +196,11 @@ public class DefaultCellEditor } // startCellEditing() /** - * actionPerformed + * This event is fired by the editor component (for instance, by pressing + * ENTER in the {@link JTextField}. The default method delegates call to + * the {@link #stopCellEditing}, finishing the editing session. * - * @param event TODO + * @param event unused in default method */ public void actionPerformed(ActionEvent event) { @@ -184,15 +208,20 @@ public class DefaultCellEditor } // actionPerformed() /** - * itemStateChanged + * This event is fired by the editor component.The default method delegates + * call to the {@link #stopCellEditing}, finishing the editing session. * - * @param event TODO + * @param event unused in default method */ public void itemStateChanged(ItemEvent event) { stopCellEditing(); } // itemStateChanged() + /** + * Notify the registered listeners (including the table) that the editing + * has been completed. + */ void fireEditingStopped() { CellEditorListener[] listeners = getCellEditorListeners(); @@ -201,6 +230,10 @@ public class DefaultCellEditor } + /** + * Notify the registered listeners (including the table) that the editing + * has been canceled. + */ void fireEditingCanceled() { CellEditorListener[] listeners = getCellEditorListeners(); @@ -208,59 +241,185 @@ public class DefaultCellEditor listeners[index].editingCanceled(changeEvent); } } // EditorDelegate + + /** + * Provides getter and setter methods to work with the text component. + * + * @author Audrius Meskauskas (audriusa@Bioinformatics.org) + */ + private class JTextFieldDelegate extends EditorDelegate + { + /** + * Use the serial version UID for interoperability. + */ + private static final long serialVersionUID = 1; + + /** + * Set the value for the editor component. + * + * @param aValue the value to set (toString() will be called). + */ + public void setValue(Object aValue) + { + value = aValue; + JTextField f = (JTextField) editorComponent; + if (value == null) + f.setText(""); + else + f.setText(value.toString()); + } + + /** + * Get the value for the editor component. + * + * @return value the value of the component (String) + */ + public Object getCellEditorValue() + { + JTextField f = (JTextField) editorComponent; + return value = f.getText(); + } + } + + /** + * Provides getter and setter methods to work with the combo box. + * + * @author Audrius Meskauskas (audriusa@Bioinformatics.org) + */ + private class JComboBoxDelegate extends EditorDelegate + { + /** + * Use the serial version UID for interoperability. + */ + private static final long serialVersionUID = 1; + + /** + * Set the value for the editor component. + * + * @param aValue the value to set. + */ + public void setValue(Object aValue) + { + value = aValue; + JComboBox c = (JComboBox) editorComponent; + if (value != null) + c.setSelectedItem(value); + } - /** - * editorComponent + /** + * Get the value for the editor component. + * + * @return value the value of the component (as String) + */ + public Object getCellEditorValue() + { + JComboBox c = (JComboBox) editorComponent; + return value = c.getSelectedItem(); + } + } + + /** + * Provides getter and setter methods to work with the check box. + * + * @author Audrius Meskauskas (audriusa@Bioinformatics.org) + */ + private class JCheckBoxDelegate extends EditorDelegate + { + /** + * Use the serial version UID for interoperability. + */ + private static final long serialVersionUID = 1; + + /** + * Set the value for the editor component. + * + * @param value the value to set (must be Boolean). + */ + public void setValue(Object value) + { + JCheckBox c = (JCheckBox) editorComponent; + + if (value == null) + c.setSelected(false); + else + c.setSelected( ((Boolean) value).booleanValue()); + } + + /** + * Get the value for the editor component. + * + * @return value the value of the component (must be CharSequence) + */ + public Object getCellEditorValue() + { + JCheckBox c = (JCheckBox) editorComponent; + value = c.isSelected() ? Boolean.TRUE : Boolean.FALSE; + return value; + } + } + + /** + * The Swing JComponent, performing the editing session. */ protected JComponent editorComponent; /** - * delegate + * The editor delegate, responsible for listening the {@link #editorComponent} + * events and getting/setting its value. */ protected EditorDelegate delegate; /** - * clickCountToStart + * The number of the mouse clicks, required to start the editing session. */ protected int clickCountToStart; /** - * Constructor DefaultCellEditor + * Create the DefaultCellEditor that uses the text field as its editor + * component (appropriate for the text content) * - * @param textfield TODO + * @param textfield the text field as will be used as the editor component */ public DefaultCellEditor(JTextField textfield) { editorComponent = textfield; - clickCountToStart = 3; + clickCountToStart = 2; + delegate = new JTextFieldDelegate(); + textfield.addActionListener(delegate); } // DefaultCellEditor() /** - * Constructor DefaultCellEditor + * Constructor DefaultCellEditor that uses the checkbox (appropriate + * for boolean values) * - * @param checkbox TODO + * @param checkbox the checkbox that will be used with this editor. */ public DefaultCellEditor(JCheckBox checkbox) { editorComponent = checkbox; clickCountToStart = 1; + delegate = new JCheckBoxDelegate(); + checkbox.addActionListener(delegate); } // DefaultCellEditor() /** - * Constructor DefaultCellEditor + * Constructor DefaultCellEditor that uses the combo box. * - * @param combobox TODO + * @param combobox the combo box that will be used with this editor. */ public DefaultCellEditor(JComboBox combobox) { editorComponent = combobox; clickCountToStart = 1; + delegate = new JComboBoxDelegate(); + combobox.addActionListener(delegate); } // DefaultCellEditor() /** - * getComponent + * Get the component that performs the editing sessions. It is the same + * component that was passed in constructor. * - * @returns Component + * @return the component, performing the editing sessions. */ public Component getComponent() { @@ -268,9 +427,9 @@ public class DefaultCellEditor } // getComponent() /** - * getClickCountToStart + * Get the number of mouse clicks, required to start the editing session. * - * @returns int + * @return int the number of mouse clicks, required to start the session */ public int getClickCountToStart() { @@ -278,9 +437,9 @@ public class DefaultCellEditor } // getClickCountToStart() /** - * setClickCountToStart + * Set the number of mouse clicks, required to start the editing session. * - * @param count TODO + * @param count the number of clicks, required to start the session */ public void setClickCountToStart(int count) { @@ -288,9 +447,10 @@ public class DefaultCellEditor } // setClickCountToStart() /** - * getCellEditorValue + * Get the value, currently being displayed by the editor component. The + * call is forwarded to the {@link #delegate}. * - * @returns Object + * @return Object the value (class depends on the editor component) */ public Object getCellEditorValue() { @@ -298,11 +458,11 @@ public class DefaultCellEditor } // getCellEditorValue() /** - * isCellEditable + * Forwards call to the {@link #delegate}. * - * @param event TODO + * @param event forwarded to the delegate. * - * @returns boolean + * @return boolean returned by delegate */ public boolean isCellEditable(EventObject event) { @@ -310,11 +470,11 @@ public class DefaultCellEditor } // isCellEditable() /** - * shouldSelectCell + * Forwards call to the {@link #delegate}. * - * @param event TODO + * @param event forwarded to the delegate. * - * @returns boolean + * @return boolean returned by delegate */ public boolean shouldSelectCell(EventObject event) { @@ -322,9 +482,9 @@ public class DefaultCellEditor } // shouldSelectCell() /** - * stopCellEditing + * Forwards call to the {@link #delegate}. * - * @returns boolean + * @return boolean returned by delegate */ public boolean stopCellEditing() { @@ -332,7 +492,7 @@ public class DefaultCellEditor } // stopCellEditing() /** - * cancelCellEditing + * Forwards call to the {@link #delegate}. */ public void cancelCellEditing() { @@ -356,45 +516,30 @@ public class DefaultCellEditor * @param leaf - true if the node is a leaf node * @param row - the row index of the node being edited * - * @returns Component the component for editing + * @return Component the component for editing */ public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { - if (editorComponent instanceof JTextField) - { - ((JTextField)editorComponent).setText(value.toString()); - delegate = new EditorDelegate(); - ((JTextField)editorComponent).addActionListener(delegate); - } - else if (editorComponent instanceof JCheckBox) - { - ((JCheckBox)editorComponent).setText(value.toString()); - delegate = new EditorDelegate(); - ((JCheckBox)editorComponent).addActionListener(delegate); - } - else if (editorComponent instanceof JComboBox) - { - ((JComboBox)editorComponent).setSelectedItem(value.toString()); - delegate = new EditorDelegate(); - ((JComboBox)editorComponent).addActionListener(delegate); - } - + delegate.setValue(value); return editorComponent; } // getTreeCellEditorComponent() /** - * getTableCellEditorComponent + * Get the cell editor component that will perform the editing session. If + * returned once, the same component is also returned on the repetetive calls + * again (reused). * - * @param table TODO - * @param value TODO - * @param isSelected TODO - * @param row TODO - * @param column TODO - * - * @returns Component + * @param table the table where the editing is performed + * @param value the current value of the table. It is set as the initial + * component value. + * @param isSelected if true, the cell is currently selected + * @param row the row of the cell being edited + * @param column the column of the cell being edited + * + * @return Component the component that will perform the editing session */ public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, @@ -402,24 +547,9 @@ public class DefaultCellEditor { // NOTE: as specified by Sun, we don't call new() everytime, we return // editorComponent on each call to getTableCellEditorComponent or - // getTreeCellEditorComponent. However, currently JTextFields have a - // problem with getting rid of old text, so without calling new() there - // are some strange results. If you edit more than one cell in the table - // text from previously edited cells may unexpectedly show up in the - // cell you are currently editing. This will be fixed automatically - // when JTextField is fixed. - if (editorComponent instanceof JTextField) - { - ((JTextField)editorComponent).setText(value.toString()); - delegate = new EditorDelegate(); - ((JTextField)editorComponent).addActionListener(delegate); - } - else - { - // TODO - } + // getTreeCellEditorComponent. + delegate.setValue(value); return editorComponent; } // getTableCellEditorComponent() - } diff --git a/libjava/classpath/javax/swing/DefaultListCellRenderer.java b/libjava/classpath/javax/swing/DefaultListCellRenderer.java index 9a8e07071b5..598627fac35 100644 --- a/libjava/classpath/javax/swing/DefaultListCellRenderer.java +++ b/libjava/classpath/javax/swing/DefaultListCellRenderer.java @@ -93,7 +93,7 @@ public class DefaultListCellRenderer extends JLabel int index, boolean isSelected, boolean cellHasFocus) { - String s = value.toString(); + String s = value != null ? value.toString() : ""; setText(s); setOpaque(true); setHorizontalAlignment(LEFT); diff --git a/libjava/classpath/javax/swing/DefaultListSelectionModel.java b/libjava/classpath/javax/swing/DefaultListSelectionModel.java index ce1dfdd79c5..7ec4e614c8f 100644 --- a/libjava/classpath/javax/swing/DefaultListSelectionModel.java +++ b/libjava/classpath/javax/swing/DefaultListSelectionModel.java @@ -447,6 +447,9 @@ public class DefaultListSelectionModel implements Cloneable, */ public void addSelectionInterval(int index0, int index1) { + if (index0 == -1 || index1 == -1) + return; + int lo = Math.min(index0, index1); int hi = Math.max(index0, index1); oldSel = sel.clone(); @@ -508,6 +511,9 @@ public class DefaultListSelectionModel implements Cloneable, public void removeSelectionInterval(int index0, int index1) { + if (index0 == -1 || index1 == -1) + return; + oldSel = sel.clone(); int lo = Math.min(index0, index1); int hi = Math.max(index0, index1); @@ -551,6 +557,9 @@ public class DefaultListSelectionModel implements Cloneable, */ public void setSelectionInterval(int index0, int index1) { + if (index0 == -1 || index1 == -1) + return; + oldSel = sel.clone(); sel.clear(); if (selectionMode == SINGLE_SELECTION) diff --git a/libjava/classpath/javax/swing/ImageIcon.java b/libjava/classpath/javax/swing/ImageIcon.java index b6ed949d8dc..9e6265830a3 100644 --- a/libjava/classpath/javax/swing/ImageIcon.java +++ b/libjava/classpath/javax/swing/ImageIcon.java @@ -205,13 +205,13 @@ public class ImageIcon private static final long serialVersionUID = 532615968316031794L; /** A dummy Component that is used in the MediaTracker. */ - protected static Component component = new Component() + protected static final Component component = new Component() { // No need to implement this. }; /** The MediaTracker used to monitor the loading of images. */ - protected static MediaTracker tracker = new MediaTracker(component); + protected static final MediaTracker tracker = new MediaTracker(component); /** The ID that is used in the tracker. */ private static int id; diff --git a/libjava/classpath/javax/swing/JApplet.java b/libjava/classpath/javax/swing/JApplet.java index e90c451891e..68eb983dd01 100644 --- a/libjava/classpath/javax/swing/JApplet.java +++ b/libjava/classpath/javax/swing/JApplet.java @@ -66,7 +66,7 @@ public class JApplet extends Applet /** * Creates a new instance of <code>AccessibleJApplet</code>. */ - public AccessibleJApplet() + protected AccessibleJApplet() { super(); // Nothing to do here. diff --git a/libjava/classpath/javax/swing/JCheckBox.java b/libjava/classpath/javax/swing/JCheckBox.java index 74fda8f6dbe..26f9f6ca595 100644 --- a/libjava/classpath/javax/swing/JCheckBox.java +++ b/libjava/classpath/javax/swing/JCheckBox.java @@ -67,7 +67,7 @@ public class JCheckBox extends JToggleButton implements Accessible /** * Creates a new instance of <code>AccessibleJCheckBox</code>. */ - public AccessibleJCheckBox() + protected AccessibleJCheckBox() { // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/JComponent.java b/libjava/classpath/javax/swing/JComponent.java index 747eba54db4..ddd70860869 100644 --- a/libjava/classpath/javax/swing/JComponent.java +++ b/libjava/classpath/javax/swing/JComponent.java @@ -1,5 +1,5 @@ /* JComponent.java -- Every component in swing inherits from this class. - 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. @@ -64,10 +64,10 @@ import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; -import java.awt.geom.Rectangle2D; import java.awt.peer.LightweightPeer; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; import java.beans.PropertyVetoException; import java.beans.VetoableChangeListener; import java.io.Serializable; @@ -88,7 +88,6 @@ import javax.swing.border.TitledBorder; import javax.swing.event.AncestorEvent; import javax.swing.event.AncestorListener; import javax.swing.event.EventListenerList; -import javax.swing.event.SwingPropertyChangeSupport; import javax.swing.plaf.ComponentUI; /** @@ -165,11 +164,11 @@ public abstract class JComponent extends Container implements Serializable /** * Manages the property change listeners; */ - private SwingPropertyChangeSupport changeSupport; + private PropertyChangeSupport changeSupport; protected AccessibleJComponent() { - changeSupport = new SwingPropertyChangeSupport(this); + changeSupport = new PropertyChangeSupport(this); } /** @@ -528,14 +527,6 @@ public abstract class JComponent extends Container implements Serializable protected EventListenerList listenerList = new EventListenerList(); /** - * Support for {@link PropertyChangeEvent} events. This is constructed - * lazily when the component gets its first {@link - * PropertyChangeListener} subscription; until then it's an empty slot. - */ - private SwingPropertyChangeSupport changeSupport; - - - /** * Storage for "client properties", which are key/value pairs associated * with this component by a "client", such as a user application or a * layout manager. This is lazily constructed when the component gets its @@ -697,36 +688,6 @@ public abstract class JComponent extends Container implements Serializable } /** - * Unregister a <code>PropertyChangeListener</code>. - * - * @param listener The listener to register - * - * @see #addPropertyChangeListener(PropertyChangeListener) - * @see #changeSupport - */ - public void removePropertyChangeListener(PropertyChangeListener listener) - { - if (changeSupport != null) - changeSupport.removePropertyChangeListener(listener); - } - - /** - * Unregister a <code>PropertyChangeListener</code>. - * - * @param propertyName The property name to unregister the listener from - * @param listener The listener to unregister - * - * @see #addPropertyChangeListener(String, PropertyChangeListener) - * @see #changeSupport - */ - public void removePropertyChangeListener(String propertyName, - PropertyChangeListener listener) - { - if (changeSupport != null) - changeSupport.removePropertyChangeListener(propertyName, listener); - } - - /** * Unregister a <code>VetoableChangeChangeListener</code>. * * @param listener The listener to unregister @@ -751,24 +712,6 @@ public abstract class JComponent extends Container implements Serializable } /** - * Register a <code>PropertyChangeListener</code>. This listener will - * receive any PropertyChangeEvent, regardless of property name. To - * listen to a specific property name, use {@link - * #addPropertyChangeListener(String,PropertyChangeListener)} instead. - * - * @param listener The listener to register - * - * @see #removePropertyChangeListener(PropertyChangeListener) - * @see #changeSupport - */ - public void addPropertyChangeListener(PropertyChangeListener listener) - { - if (changeSupport == null) - changeSupport = new SwingPropertyChangeSupport(this); - changeSupport.addPropertyChangeListener(listener); - } - - /** * Register a <code>PropertyChangeListener</code> for a specific, named * property. To listen to all property changes, regardless of name, use * {@link #addPropertyChangeListener(PropertyChangeListener)} instead. @@ -819,7 +762,10 @@ public abstract class JComponent extends Container implements Serializable */ public EventListener[] getListeners(Class listenerType) { - return listenerList.getListeners(listenerType); + if (listenerType == PropertyChangeListener.class) + return getPropertyChangeListeners(); + else + return listenerList.getListeners(listenerType); } /** @@ -845,134 +791,48 @@ public abstract class JComponent extends Container implements Serializable } /** - * Return all <code>PropertyChangeListener</code> objects registered to listen - * for a particular property. - * - * @param property The property to return the listeners of - * - * @return The set of <code>PropertyChangeListener</code> objects in - * {@link #changeSupport} registered to listen on the specified property - */ - public PropertyChangeListener[] getPropertyChangeListeners(String property) - { - return changeSupport == null ? new PropertyChangeListener[0] - : changeSupport.getPropertyChangeListeners(property); - } - - /** * A variant of {@link #firePropertyChange(String,Object,Object)} * for properties with <code>boolean</code> values. + * + * @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. */ public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { - if (changeSupport != null) - changeSupport.firePropertyChange(propertyName, Boolean.valueOf(oldValue), - Boolean.valueOf(newValue)); - } - - /** - * A variant of {@link #firePropertyChange(String,Object,Object)} - * for properties with <code>byte</code> values. - */ - public void firePropertyChange(String propertyName, byte oldValue, - byte newValue) - { - if (changeSupport != null) - changeSupport.firePropertyChange(propertyName, new Byte(oldValue), - new Byte(newValue)); + super.firePropertyChange(propertyName, oldValue, newValue); } /** * A variant of {@link #firePropertyChange(String,Object,Object)} * for properties with <code>char</code> values. + * + * @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. */ public void firePropertyChange(String propertyName, char oldValue, char newValue) { - if (changeSupport != null) - changeSupport.firePropertyChange(propertyName, new Character(oldValue), - new Character(newValue)); - } - - /** - * A variant of {@link #firePropertyChange(String,Object,Object)} - * for properties with <code>double</code> values. - */ - public void firePropertyChange(String propertyName, double oldValue, - double newValue) - { - if (changeSupport != null) - changeSupport.firePropertyChange(propertyName, new Double(oldValue), - new Double(newValue)); - } - - /** - * A variant of {@link #firePropertyChange(String,Object,Object)} - * for properties with <code>float</code> values. - */ - public void firePropertyChange(String propertyName, float oldValue, - float newValue) - { - if (changeSupport != null) - changeSupport.firePropertyChange(propertyName, new Float(oldValue), - new Float(newValue)); + super.firePropertyChange(propertyName, oldValue, newValue); } /** * A variant of {@link #firePropertyChange(String,Object,Object)} * for properties with <code>int</code> values. + * + * @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. */ public void firePropertyChange(String propertyName, int oldValue, int newValue) { - if (changeSupport != null) - changeSupport.firePropertyChange(propertyName, new Integer(oldValue), - new Integer(newValue)); - } - - /** - * A variant of {@link #firePropertyChange(String,Object,Object)} - * for properties with <code>long</code> values. - */ - public void firePropertyChange(String propertyName, long oldValue, - long newValue) - { - if (changeSupport != null) - changeSupport.firePropertyChange(propertyName, new Long(oldValue), - new Long(newValue)); - } - - /** - * Call {@link PropertyChangeListener#propertyChange} 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 - * - * @see #changeSupport - * @see #addPropertyChangeListener(PropertyChangeListener) - * @see #removePropertyChangeListener(PropertyChangeListener) - */ - protected void firePropertyChange(String propertyName, Object oldValue, - Object newValue) - { - if (changeSupport != null) - changeSupport.firePropertyChange(propertyName, oldValue, newValue); - } - - /** - * A variant of {@link #firePropertyChange(String,Object,Object)} - * for properties with <code>short</code> values. - */ - public void firePropertyChange(String propertyName, short oldValue, - short newValue) - { - if (changeSupport != null) - changeSupport.firePropertyChange(propertyName, new Short(oldValue), - new Short(newValue)); + super.firePropertyChange(propertyName, oldValue, newValue); } /** @@ -1518,9 +1378,8 @@ public abstract class JComponent extends Container implements Serializable { ((JComponent) c).computeVisibleRect(rect); rect.translate(-getX(), -getY()); - Rectangle2D.intersect(rect, - new Rectangle(0, 0, getWidth(), getHeight()), - rect); + rect = SwingUtilities.computeIntersection(0, 0, getWidth(), + getHeight(), rect); } else rect.setRect(0, 0, getWidth(), getHeight()); @@ -1530,7 +1389,7 @@ public abstract class JComponent extends Container implements Serializable * Return the component's visible rectangle in a new {@link Rectangle}, * rather than via a return slot. * - * @return The component's visible rectangle + * @return the component's visible rectangle * * @see #computeVisibleRect(Rectangle) */ @@ -1691,7 +1550,10 @@ public abstract class JComponent extends Container implements Serializable // screen. if (!isPaintingDoubleBuffered && isDoubleBuffered() && rm.isDoubleBufferingEnabled()) - paintDoubleBuffered(g); + { + Rectangle clip = g.getClipBounds(); + paintDoubleBuffered(clip); + } else { if (g.getClip() == null) @@ -1755,11 +1617,10 @@ public abstract class JComponent extends Container implements Serializable // optimizedDrawingEnabled (== it tiles its children). if (! isOptimizedDrawingEnabled()) { - Rectangle clip = g.getClipBounds(); for (int i = 0; i < children.length; i++) { Rectangle childBounds = children[i].getBounds(); - if (children[i].isOpaque() + if (children[i].isOpaque() && children[i].isVisible() && SwingUtilities.isRectangleContainingRectangle(childBounds, g.getClipBounds())) { @@ -1892,33 +1753,29 @@ public abstract class JComponent extends Container implements Serializable void paintImmediately2(Rectangle r) { RepaintManager rm = RepaintManager.currentManager(this); - Graphics g = getGraphics(); - g.setClip(r.x, r.y, r.width, r.height); if (rm.isDoubleBufferingEnabled() && isDoubleBuffered()) - paintDoubleBuffered(g); + paintDoubleBuffered(r); else - paintSimple(g); - g.dispose(); + paintSimple(r); } /** * Performs double buffered repainting. - * - * @param g the graphics context to paint to */ - void paintDoubleBuffered(Graphics g) + private void paintDoubleBuffered(Rectangle r) { - - Rectangle r = g.getClipBounds(); - if (r == null) - r = new Rectangle(0, 0, getWidth(), getHeight()); RepaintManager rm = RepaintManager.currentManager(this); // Paint on the offscreen buffer. - Image buffer = rm.getOffscreenBuffer(this, getWidth(), getHeight()); + Component root = SwingUtilities.getRoot(this); + Image 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(); - g2 = getComponentGraphics(g2); + g2.translate(translation.x, translation.y); g2.setClip(r.x, r.y, r.width, r.height); + g2 = getComponentGraphics(g2); isPaintingDoubleBuffered = true; try { @@ -1929,20 +1786,27 @@ public abstract class JComponent extends Container implements Serializable isPaintingDoubleBuffered = false; g2.dispose(); } - + // Paint the buffer contents on screen. - g.drawImage(buffer, 0, 0, this); + rm.commitBuffer(root, new Rectangle(translation.x + r.x, + translation.y + r.y, r.width, + r.height)); } /** * Performs normal painting without double buffering. * - * @param g the graphics context to use + * @param r the area that should be repainted */ - void paintSimple(Graphics g) + void paintSimple(Rectangle r) { + Graphics g = getGraphics(); Graphics g2 = getComponentGraphics(g); + g2.setClip(r); paint(g2); + g2.dispose(); + if (g != g2) + g.dispose(); } /** @@ -2339,12 +2203,8 @@ public abstract class JComponent extends Container implements Serializable */ public void repaint(long tm, int x, int y, int width, int height) { - Rectangle dirty = new Rectangle(x, y, width, height); - Rectangle vis = getVisibleRect(); - dirty = dirty.intersection(vis); - RepaintManager.currentManager(this).addDirtyRegion(this, dirty.x, dirty.y, - dirty.width, - dirty.height); + RepaintManager.currentManager(this).addDirtyRegion(this, x, y, width, + height); } /** @@ -2356,8 +2216,8 @@ public abstract class JComponent extends Container implements Serializable */ public void repaint(Rectangle r) { - repaint((long) 0, (int) r.getX(), (int) r.getY(), (int) r.getWidth(), - (int) r.getHeight()); + RepaintManager.currentManager(this).addDirtyRegion(this, r.x, r.y, r.width, + r.height); } /** @@ -2879,7 +2739,7 @@ public abstract class JComponent extends Container implements Serializable * * @since 1.4 */ - public boolean requestFocusInWindow(boolean temporary) + protected boolean requestFocusInWindow(boolean temporary) { return super.requestFocusInWindow(temporary); } @@ -3046,19 +2906,6 @@ public abstract class JComponent extends Container implements Serializable } /** - * Return all <code>PropertyChangeListener</code> objects registered. - * - * @return The set of <code>PropertyChangeListener</code> objects - */ - public PropertyChangeListener[] getPropertyChangeListeners() - { - if (changeSupport == null) - return new PropertyChangeListener[0]; - else - return changeSupport.getPropertyChangeListeners(); - } - - /** * Prints this component to the given Graphics context. A call to this * method results in calls to the methods {@link #printComponent}, * {@link #printBorder} and {@link #printChildren} in this order. @@ -3098,7 +2945,7 @@ public abstract class JComponent extends Container implements Serializable * * @since 1.3 */ - public void printComponent(Graphics g) + protected void printComponent(Graphics g) { paintComponent(g); } @@ -3112,7 +2959,7 @@ public abstract class JComponent extends Container implements Serializable * * @since 1.3 */ - public void printChildren(Graphics g) + protected void printChildren(Graphics g) { paintChildren(g); } @@ -3126,7 +2973,7 @@ public abstract class JComponent extends Container implements Serializable * * @since 1.3 */ - public void printBorder(Graphics g) + protected void printBorder(Graphics g) { paintBorder(g); } @@ -3245,62 +3092,25 @@ public abstract class JComponent extends Container implements Serializable while (parent != null && !(parent instanceof Window)) { Container newParent = parent.getParent(); - if (newParent == null) + if (newParent == null || newParent instanceof Window) break; // If the parent is optimizedDrawingEnabled, then its children are // tiled and cannot have an overlapping child. Go directly to next // parent. - if (newParent instanceof JComponent - && ((JComponent) newParent).isOptimizedDrawingEnabled()) + if ((newParent instanceof JComponent + && ((JComponent) newParent).isOptimizedDrawingEnabled())) + { parent = newParent; continue; } - - // First we must check if the new parent itself somehow clips the - // target rectangle. This can happen in JViewports. - Rectangle parRect = new Rectangle(0, 0, newParent.getWidth(), - newParent.getHeight()); + // If the parent is not optimizedDrawingEnabled, we must paint the + // parent. Rectangle target = SwingUtilities.convertRectangle(found, currentClip, newParent); - if (! target.intersection(parRect).equals(target)) - { - found = newParent; - currentClip = target; - parent = newParent; - continue; - } - - // Otherwise we must check if one of the children of this parent - // overlaps with the current component. - Component[] children = newParent.getComponents(); - // This flag is used to skip components that are 'below' the component - // in question. - boolean skip = true; - for (int i = children.length - 1; i >= 0; i--) - { - boolean nextSkip = skip; - if (children[i] == parent) - nextSkip = false; - if (skip) - continue; - skip = nextSkip; - Component c = children[i]; - Rectangle compBounds = c.getBounds(); - // If the component completely overlaps the clip in question, we - // don't need to repaint. Return null. - if (compBounds.contains(target)) - return null; - if (compBounds.intersects(target)) - { - // We found a parent whose children overlap with our current - // component. Make this the current component. - found = newParent; - currentClip = target; - break; - } - } + found = newParent; + currentClip = target; parent = newParent; } return found; diff --git a/libjava/classpath/javax/swing/JDialog.java b/libjava/classpath/javax/swing/JDialog.java index b3f7c011f68..08dada2fd81 100644 --- a/libjava/classpath/javax/swing/JDialog.java +++ b/libjava/classpath/javax/swing/JDialog.java @@ -74,7 +74,7 @@ public class JDialog extends Dialog implements Accessible, WindowConstants, /** * Creates a new instance of <code>AccessibleJDialog</code>. */ - public AccessibleJDialog() + protected AccessibleJDialog() { super(); // Nothing to do here. @@ -107,7 +107,7 @@ public class JDialog extends Dialog implements Accessible, WindowConstants, */ public JDialog() { - this(SwingUtilities.getOwnerFrame(), "", false, null); + this((Frame) SwingUtilities.getOwnerFrame(null), "", false, null); } /** @@ -234,8 +234,7 @@ public class JDialog extends Dialog implements Accessible, WindowConstants, public JDialog(Frame owner, String title, boolean modal, GraphicsConfiguration gc) { - super((owner == null) ? SwingUtilities.getOwnerFrame() : owner, - title, modal, gc); + super((Frame) SwingUtilities.getOwnerFrame(owner), title, modal, gc); dialogInit(); } diff --git a/libjava/classpath/javax/swing/JEditorPane.java b/libjava/classpath/javax/swing/JEditorPane.java index 3560ffd57d4..73b775738de 100644 --- a/libjava/classpath/javax/swing/JEditorPane.java +++ b/libjava/classpath/javax/swing/JEditorPane.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing; +import java.awt.Container; import java.awt.Dimension; import java.io.IOException; import java.io.InputStream; @@ -682,27 +683,59 @@ public class JEditorPane extends JTextComponent } /** - * Returns the preferred size for the JEditorPane. + * Returns the preferred size for the JEditorPane. This is implemented to + * return the super's preferred size, unless one of + * {@link #getScrollableTracksViewportHeight()} or + * {@link #getScrollableTracksViewportWidth()} returns <code>true</code>, + * in which case the preferred width and/or height is replaced by the UI's + * minimum size. + * + * @return the preferred size for the JEditorPane */ public Dimension getPreferredSize() { - return super.getPreferredSize(); + Dimension pref = super.getPreferredSize(); + if (getScrollableTracksViewportWidth()) + pref.width = getUI().getMinimumSize(this).width; + if (getScrollableTracksViewportHeight()) + pref.height = getUI().getMinimumSize(this).height; + return pref; } + /** + * Returns <code>true</code> when a Viewport should force the height of + * this component to match the viewport height. This is implemented to return + * <code>true</code> when the parent is an instance of JViewport and + * the viewport height > the UI's minimum height. + * + * @return <code>true</code> when a Viewport should force the height of + * this component to match the viewport height + */ public boolean getScrollableTracksViewportHeight() { - /* Container parent = getParent(); - return (parent instanceof JViewport && - parent.isValid());*/ - return isValid(); + // Tests show that this returns true when the parent is a JViewport + // and has a height > minimum UI height. + Container parent = getParent(); + return parent instanceof JViewport + && parent.getHeight() > getUI().getMinimumSize(this).height; } + /** + * Returns <code>true</code> when a Viewport should force the width of + * this component to match the viewport width. This is implemented to return + * <code>true</code> when the parent is an instance of JViewport and + * the viewport width > the UI's minimum width. + * + * @return <code>true</code> when a Viewport should force the width of + * this component to match the viewport width + */ public boolean getScrollableTracksViewportWidth() { - /*Container parent = getParent(); - return (parent instanceof JViewport && - parent.isValid());*/ - return isValid(); + // Tests show that this returns true when the parent is a JViewport + // and has a width > minimum UI width. + Container parent = getParent(); + return parent != null && parent instanceof JViewport + && parent.getWidth() > getUI().getMinimumSize(this).width; } public URL getPage() @@ -893,7 +926,7 @@ public class JEditorPane extends JTextComponent // Remove the current content. Document doc = getDocument(); doc.remove(0, doc.getLength()); - if (t == null || t == "") + if (t == null || t.equals("")) return; // Let the EditorKit read the text into the Document. diff --git a/libjava/classpath/javax/swing/JFileChooser.java b/libjava/classpath/javax/swing/JFileChooser.java index 3a9d6a01f38..72bd2bb28f5 100644 --- a/libjava/classpath/javax/swing/JFileChooser.java +++ b/libjava/classpath/javax/swing/JFileChooser.java @@ -40,7 +40,6 @@ package javax.swing; import java.awt.Component; import java.awt.Frame; import java.awt.HeadlessException; -import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; @@ -658,8 +657,7 @@ public class JFileChooser extends JComponent implements Accessible retval = ERROR_OPTION; - Insets i = d.getInsets(); - d.setSize(500 + i.top + i.bottom, d.getPreferredSize().height); + d.pack(); d.show(); return retval; } @@ -683,8 +681,7 @@ public class JFileChooser extends JComponent implements Accessible retval = ERROR_OPTION; - Insets i = d.getInsets(); - d.setSize(500 + i.top + i.bottom, d.getPreferredSize().height); + d.pack(); d.show(); return retval; } @@ -710,8 +707,7 @@ public class JFileChooser extends JComponent implements Accessible retval = ERROR_OPTION; - Insets i = d.getInsets(); - d.setSize(500 + i.top + i.bottom, d.getPreferredSize().height); + d.pack(); d.show(); return retval; } @@ -729,7 +725,7 @@ public class JFileChooser extends JComponent implements Accessible { Frame toUse = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, parent); if (toUse == null) - toUse = SwingUtilities.getOwnerFrame(); + toUse = (Frame) SwingUtilities.getOwnerFrame(null); JDialog dialog = new JDialog(toUse); setSelectedFile(null); diff --git a/libjava/classpath/javax/swing/JFrame.java b/libjava/classpath/javax/swing/JFrame.java index 8d4dcb53b3c..d2512056085 100644 --- a/libjava/classpath/javax/swing/JFrame.java +++ b/libjava/classpath/javax/swing/JFrame.java @@ -76,7 +76,7 @@ public class JFrame extends Frame /** * Creates a new instance of <code>AccessibleJFrame</code>. */ - public AccessibleJFrame() + protected AccessibleJFrame() { super(); // Nothing to do here. @@ -150,6 +150,15 @@ public class JFrame extends Frame super.setLayout(new BorderLayout(1, 1)); enableEvents(AWTEvent.WINDOW_EVENT_MASK); getRootPane(); // will do set/create + + // Setup the defaultLookAndFeelDecoration if requested. + if (isDefaultLookAndFeelDecorated() + && UIManager.getLookAndFeel().getSupportsWindowDecorations()) + { + setUndecorated(true); + getRootPane().setWindowDecorationStyle(JRootPane.FRAME); + } + // We're now done the init stage. setRootPaneCheckingEnabled(true); } diff --git a/libjava/classpath/javax/swing/JInternalFrame.java b/libjava/classpath/javax/swing/JInternalFrame.java index 948988c24a7..5bd6f781cd0 100644 --- a/libjava/classpath/javax/swing/JInternalFrame.java +++ b/libjava/classpath/javax/swing/JInternalFrame.java @@ -559,6 +559,8 @@ public class JInternalFrame extends JComponent implements Accessible, this.iconable = iconifiable; storedBounds = new Rectangle(); setRootPane(createRootPane()); + // JInternalFrames are invisible by default. + setVisible(false); updateUI(); setRootPaneCheckingEnabled(true); // Done the init stage, now adds go to content pane. } @@ -616,7 +618,7 @@ public class JInternalFrame extends JComponent implements Accessible, */ public void dispose() { - hide(); + setVisible(false); JDesktopPane pane = getDesktopPane(); if (pane != null) pane.setSelectedFrame(null); @@ -647,11 +649,11 @@ public class JInternalFrame extends JComponent implements Accessible, switch (getDefaultCloseOperation()) { case HIDE_ON_CLOSE: - hide(); - break; + setVisible(false); + break; case DISPOSE_ON_CLOSE: - dispose(); - break; + dispose(); + break; } } @@ -1257,13 +1259,14 @@ public class JInternalFrame extends JComponent implements Accessible, { if (b && ! isClosed()) { - fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_CLOSING); - fireVetoableChange(IS_CLOSED_PROPERTY, false, true); + fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_CLOSING); + fireVetoableChange(IS_CLOSED_PROPERTY, false, true); - isClosed = b; + isClosed = b; + dispose(); - firePropertyChange(IS_CLOSED_PROPERTY, false, true); - fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_CLOSED); + firePropertyChange(IS_CLOSED_PROPERTY, false, true); + fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_CLOSED); } } @@ -1628,27 +1631,27 @@ public class JInternalFrame extends JComponent implements Accessible, { if (! isVisible()) { - super.show(); - - JDesktopPane pane = getDesktopPane(); - if (pane != null) - pane.setSelectedFrame(this); - else - { - try - { - setSelected(true); - } - catch (PropertyVetoException e) - { - // Do nothing. if they don't want to be selected. - } - } - if (isFirstTimeVisible) - { - isFirstTimeVisible = false; - fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_OPENED); - } + super.show(); + + JDesktopPane pane = getDesktopPane(); + if (pane != null) + pane.setSelectedFrame(this); + else + { + try + { + setSelected(true); + } + catch (PropertyVetoException e) + { + // 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/JLayeredPane.java b/libjava/classpath/javax/swing/JLayeredPane.java index dc8b10d2178..ffd803c8706 100644 --- a/libjava/classpath/javax/swing/JLayeredPane.java +++ b/libjava/classpath/javax/swing/JLayeredPane.java @@ -43,11 +43,7 @@ import java.awt.Component; import java.awt.Container; import java.awt.Graphics; import java.awt.Rectangle; -import java.awt.Shape; import java.util.Hashtable; -import java.util.Iterator; -import java.util.Map; -import java.util.TreeMap; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; @@ -119,6 +115,7 @@ import javax.accessibility.AccessibleRole; * component indexing and position order</p> * * @author Graydon Hoare (graydon@redhat.com) + * @author Roman Kennke (kennke@aicas.com) */ public class JLayeredPane extends JComponent implements Accessible { @@ -131,7 +128,7 @@ public class JLayeredPane extends JComponent implements Accessible /** * Creates a new instance of <code>AccessibleJLayeredPane</code>. */ - public AccessibleJLayeredPane() + protected AccessibleJLayeredPane() { // Nothing to do here. } @@ -150,22 +147,18 @@ public class JLayeredPane extends JComponent implements Accessible public static final String LAYER_PROPERTY = "layeredContainerLayer"; - public static Integer FRAME_CONTENT_LAYER = new Integer (-30000); + public static final Integer FRAME_CONTENT_LAYER = new Integer (-30000); - public static Integer DEFAULT_LAYER = new Integer (0); - public static Integer PALETTE_LAYER = new Integer (100); - public static Integer MODAL_LAYER = new Integer (200); - public static Integer POPUP_LAYER = new Integer (300); - public static Integer DRAG_LAYER = new Integer (400); + public static final Integer DEFAULT_LAYER = new Integer (0); + public static final Integer PALETTE_LAYER = new Integer (100); + public static final Integer MODAL_LAYER = new Integer (200); + public static final Integer POPUP_LAYER = new Integer (300); + public static final Integer DRAG_LAYER = new Integer (400); - TreeMap layers; // Layer Number (Integer) -> Layer Size (Integer) - Hashtable componentToLayer; // Component -> Layer Number (Integer) + private Hashtable componentToLayer; // Component -> Layer Number (Integer) - private transient Rectangle rectCache; - public JLayeredPane() { - layers = new TreeMap (); componentToLayer = new Hashtable (); setLayout(null); } @@ -173,47 +166,50 @@ public class JLayeredPane extends JComponent implements Accessible /** * Looks up the layer a child component is currently assigned to. * + * If <code>c</code> is an instance of {@link JComponent}, then the layer + * is fetched from the client property with the key {@link #LAYER_PROPERTY}. + * Otherwise it is looked up in an internal hashtable that maps + * non-JComponent components to layers. If the components cannot be found + * in either way, the {@link #DEFAULT_LAYER} is returned. + * * @param c the component to look up. - * @return the layer the component is currently assigned to, in this container. - * @throws IllegalArgumentException if the component is not a child of this container. + * + * @return the layer the component is currently assigned to; if the component + * is not in this layered pane, then 0 (DEFAULT_LAYER) is returned */ public int getLayer(Component c) { - Component myComp = c; - while(! componentToLayer.containsKey(myComp)) + Integer layerObj; + if (c instanceof JComponent) { - myComp = myComp.getParent(); - if (myComp == null) - break; + JComponent jc = (JComponent) c; + layerObj = (Integer) jc.getClientProperty(LAYER_PROPERTY); } - if (myComp == null) - throw new IllegalArgumentException - ("component is not in this JLayeredPane"); - Integer layerObj = (Integer) componentToLayer.get(myComp); + else + layerObj = (Integer) componentToLayer.get(c); + + if (layerObj == null) + layerObj = DEFAULT_LAYER; + return layerObj.intValue(); } /** - * Looks up the layer of <code>comp</code> in the component's nearest - * JLayeredPane ancestor. If <code>comp</code> is not contained - * in a JLayeredPane, the value 0 (default layer) is returned. - * + * Looks up the layer in the client property with the key + * {@link #LAYER_PROPERTY} of <code>comp</code>. If no such property can be + * found, we return <code>0</code> ({@link #DEFAULT_LAYER}). + * * @param comp the component for which the layer is looked up * - * @return the layer of <code>comp</code> in its nearest JLayeredPane - * ancestor + * @return the layer of <code>comp</code> as stored in the corresponding + * client property, or <code>0</code> if there is no such property */ public static int getLayer(JComponent comp) { - JLayeredPane lp = (JLayeredPane) SwingUtilities.getAncestorOfClass - (JLayeredPane.class, comp); - if (lp == null) - return 0; - else - // The cast here forces the call to the instance method getLayer() - // instead of the static method (this would lead to infinite - // recursion). - return lp.getLayer((Component) comp); + Integer layerObj = (Integer) comp.getClientProperty(LAYER_PROPERTY); + if (layerObj == null) + layerObj = DEFAULT_LAYER; + return layerObj.intValue(); } /** @@ -236,105 +232,49 @@ public class JLayeredPane extends JComponent implements Accessible } /** - * <p>Returns a pair of ints representing a half-open interval - * <code>[top, bottom)</code>, which is the range of component indices - * the provided layer number corresponds to.</p> - * - * <p>Note that "bottom" is <em>not</em> included in the interval of - * component indices in this layer: a layer with 0 elements in it has - * <code>ret[0] == ret[1]</code>.</p> - * - * @param layer the layer to look up. - * @return the half-open range of indices this layer spans. - * @throws IllegalArgumentException if layer does not refer to an active layer - * in this container. - */ - private int[] layerToRange (Integer layer) - { - int[] ret = new int[2]; - ret[1] = getComponents ().length; - Iterator i = layers.entrySet ().iterator (); - while (i.hasNext()) - { - Map.Entry pair = (Map.Entry) i.next(); - Integer layerNum = (Integer) pair.getKey (); - Integer layerSz = (Integer) pair.getValue (); - int layerInt = layerNum.intValue(); - if (layerInt == layer.intValue()) - { - ret[0] = ret[1] - layerSz.intValue (); - break; - } - // In the following case there exists no layer with the specified - // number, so we return an empty interval here with the index at which - // such a layer would be inserted - else if (layerInt > layer.intValue()) - { - ret[1] = ret[0]; - break; - } - else - { - ret[1] -= layerSz.intValue (); - } - } - return ret; - } - - /** - * Increments the recorded size of a given layer. - * - * @param layer the layer number to increment. - * @see #incrLayer - */ - private void incrLayer(Integer layer) - { - int sz = 1; - if (layers.containsKey (layer)) - sz += ((Integer)(layers.get (layer))).intValue (); - layers.put (layer, new Integer(sz)); - } - - /** - * Decrements the recorded size of a given layer. - * - * @param layer the layer number to decrement. - * @see #incrLayer - */ - private void decrLayer(Integer layer) - { - int sz = 0; - if (layers.containsKey (layer)) - sz = ((Integer)(layers.get (layer))).intValue () - 1; - layers.put (layer, new Integer(sz)); - } - - /** * Return the greatest layer number currently in use, in this container. * This number may legally be positive <em>or</em> negative. * - * @return the least layer number. + * @return the highest layer number + * * @see #lowestLayer() */ public int highestLayer() { - if (layers.size() == 0) - return 0; - return ((Integer)(layers.lastKey ())).intValue (); + Component[] components = getComponents(); + int highest; + if (components.length == 0) + highest = 0; + else + { + highest = Integer.MIN_VALUE; + for (int i = 0; i < components.length; i++) + highest = Math.max(highest, getLayer(components[i])); + } + return highest; } /** * Return the least layer number currently in use, in this container. * This number may legally be positive <em>or</em> negative. * - * @return the least layer number. + * @return the least layer number + * * @see #highestLayer() */ public int lowestLayer() { - if (layers.size() == 0) - return 0; - return ((Integer)(layers.firstKey ())).intValue (); + Component[] components = getComponents(); + int lowest; + if (components.length == 0) + lowest = 0; + else + { + lowest = Integer.MAX_VALUE; + for (int i = 0; i < components.length; i++) + lowest = Math.max(lowest, getLayer(components[i])); + } + return lowest; } /** @@ -343,9 +283,8 @@ public class JLayeredPane extends JComponent implements Accessible * layer, so is usually the component which occludes the most other * components in its layer. * - * @param c the component to move to the front of its layer. - * @throws IllegalArgumentException if the component is not a child of - * this container. + * @param c the component to move to the front of its layer + * * @see #moveToBack */ public void moveToFront(Component c) @@ -363,8 +302,7 @@ public class JLayeredPane extends JComponent implements Accessible * other components in its layer.</p> * * @param c the component to move to the back of its layer. - * @throws IllegalArgumentException if the component is not a child of - * this container. + * * @see #moveToFront */ public void moveToBack(Component c) @@ -377,25 +315,30 @@ public class JLayeredPane extends JComponent implements Accessible * from the "front" (position 0) to the "back" (position N-1), and drawn from * the back towards the front. * - * @param c the component to get the position of. - * @throws IllegalArgumentException if the component is not a child of - * this container. + * @param c the component to get the position of + * + * @return the position of <code>c</code> within its layer or -1 if + * <code>c</code> is not a child of this layered pane + * * @see #setPosition */ public int getPosition(Component c) { - int layer = getLayer (c); - int[] range = layerToRange(new Integer(layer)); - int top = range[0]; - int bot = range[1]; - Component[] comps = getComponents (); - for (int i = top; i < bot; ++i) - { - if (comps[i] == c) - return i - top; - } - // should have found it - throw new IllegalArgumentException (); + int pos = -1; + int index = getIndexOf(c); + Component[] components = getComponents(); + int layer = getLayer(c); + if (index >= 0) + { + for (int i = index; i >= 0; --i) + { + if (layer == getLayer(components[i])) + pos++; + else + break; + } + } + return pos; } /** @@ -403,47 +346,16 @@ public class JLayeredPane extends JComponent implements Accessible * from the "front" (position 0) to the "back" (position N-1), and drawn from * the back towards the front. * - * @param c the component to change the position of. - * @param position the position to assign the component to. - * @throws IllegalArgumentException if the component is not a child of - * this container. + * @param c the component to change the position of + * @param position the position to assign the component to + * * @see #getPosition */ public void setPosition(Component c, int position) { - int layer = getLayer (c); - int[] range = layerToRange(new Integer(layer)); - if (range[0] == range[1]) - throw new IllegalArgumentException (); - - int top = range[0]; - int bot = range[1]; - if (position == -1) - position = (bot - top) - 1; - int targ = Math.min(top + position, bot-1); - int curr = -1; - - Component[] comps = getComponents(); - for (int i = top; i < bot; ++i) - { - if (comps[i] == c) - { - curr = i; - break; - } - } - if (curr == -1) - // should have found it - throw new IllegalArgumentException(); - - if (curr == 0) - super.swapComponents(curr, targ); - else - while (curr > 0) - super.swapComponents (curr, --curr); - - revalidate(); - repaint(); + int layer = getLayer(c); + int index = insertIndexForLayer(layer, position); + setComponentZOrder(c, index); } /** @@ -451,39 +363,44 @@ public class JLayeredPane extends JComponent implements Accessible * container. Components are ordered front-to-back, with the "front" * element (which draws last) at position 0 of the returned array. * - * @param layer the layer to return components from. - * @return the components in the layer. + * @param layer the layer to return components from + * + * @return the components in the layer */ public Component[] getComponentsInLayer(int layer) { - int[] range = layerToRange (getObjectForLayer (layer)); - if (range[0] == range[1]) - return new Component[0]; - else - { - Component[] comps = getComponents (); - int sz = range[1] - range[0]; - Component[] nc = new Component[sz]; - for (int i = 0; i < sz; ++i) - nc[i] = comps[range[0] + i]; - return nc; - } + Component[] inLayer = new Component[getComponentCountInLayer(layer)]; + Component[] components = getComponents(); + int j = 0; + for (int i = 0; i < components.length; ++i) + { + if (layer == getLayer(components[i])) + { + inLayer[j] = components[i]; + j++; + } + } + return inLayer; } /** * Return the number of components within a layer of this * container. * - * @param layer the layer count components in. - * @return the number of components in the layer. + * @param layer the layer count components in + * + * @return the number of components in the layer */ public int getComponentCountInLayer(int layer) { - int[] range = layerToRange (getObjectForLayer (layer)); - if (range[0] == range[1]) - return 0; - else - return (range[1] - range[0]); + Component[] components = getComponents(); + int count = 0; + for (int i = components.length - 1; i >= 0; --i) + { + if (getLayer(components[i]) == layer) + count++; + } + return count; } /** @@ -502,23 +419,14 @@ public class JLayeredPane extends JComponent implements Accessible * drawing order of all children of the container. * * @param c the component to look up. - * @return the external index of the component. - * @throws IllegalArgumentException if the component is not a child of - * this container. + * + * @return the external index of the component or <code>-1</code> if + * <code>c</code> is not a child of this layered pane */ public int getIndexOf(Component c) { - int layer = getLayer (c); - int[] range = layerToRange(new Integer(layer)); - Component[] comps = getComponents(); - for (int i = range[0]; i < range[1]; ++i) - { - if (comps[i] == c) - return i; - } - // should have found the component during iteration - throw new IllegalArgumentException (); - } + return getComponentZOrder(c); + } /** * Return an Integer object which holds the same int value as the @@ -526,6 +434,7 @@ public class JLayeredPane extends JComponent implements Accessible * identical Integer objects which we allocate. * * @param layer the layer number as an int. + * * @return the layer number as an Integer, possibly shared. */ protected Integer getObjectForLayer(int layer) @@ -564,25 +473,39 @@ public class JLayeredPane extends JComponent implements Accessible * * @param layer the layer in which to insert a component. * @param position the position in the layer at which to insert a component. + * * @return the index at which to insert the component. */ protected int insertIndexForLayer(int layer, int position) { + // position < 0 means insert at greatest position within layer. + if (position < 0) + position = Integer.MAX_VALUE; - Integer lobj = getObjectForLayer (layer); - if (! layers.containsKey(lobj)) - layers.put (lobj, new Integer (0)); - int[] range = layerToRange (lobj); - if (range[0] == range[1]) - return range[0]; - - int top = range[0]; - int bot = range[1]; - - if (position == -1 || position > (bot - top)) - return bot; - else - return top + position; + Component[] components = getComponents(); + int index = 0; + + // Try to find the start index of the specified layer. + int p = -1; + for (int i = 0; i < components.length; 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) + { + p++; + if (p < position) + index++; + else + break; + } + // No need to look further if the layer at i is smaller than layer. + else + break; + } + return index; } /** @@ -594,12 +517,20 @@ public class JLayeredPane extends JComponent implements Accessible public void remove(int index) { Component c = getComponent(index); - int layer = getLayer(c); - decrLayer(new Integer(layer)); - componentToLayer.remove(c); + if (! (c instanceof JComponent)) + componentToLayer.remove(c); super.remove(index); - // FIXME: Figure out if this call is correct. - revalidate(); + } + + /** + * Removes all components from this container. + * + * @since 1.5 + */ + public void removeAll() + { + componentToLayer.clear(); + super.removeAll(); } /** @@ -615,7 +546,7 @@ public class JLayeredPane extends JComponent implements Accessible */ public void setLayer(Component c, int layer) { - componentToLayer.put (c, getObjectForLayer (layer)); + setLayer(c, layer, -1); } /** @@ -625,15 +556,20 @@ public class JLayeredPane extends JComponent implements Accessible * @param layer the layer number to assign to the component. * @param position the position number to assign to the component. */ - public void setLayer(Component c, - int layer, - int position) + public void setLayer(Component c, int layer, int position) { - remove(c); - add(c, getObjectForLayer (layer)); - setPosition(c, position); - revalidate(); - repaint(); + Integer layerObj = getObjectForLayer(layer); + 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); } /** @@ -642,26 +578,27 @@ public class JLayeredPane extends JComponent implements Accessible * Integer}, specifying the layer to which the component will be added * (at the bottom position). * - * @param comp the component to add. - * @param layerConstraint an integer specifying the layer to add the component to. - * @param index an ignored parameter, for compatibility. + * The argument <code>index</code> specifies the position within the layer + * at which the component should be added, where <code>0</code> is the top + * position greater values specify positions below that and <code>-1</code> + * specifies the bottom position. + * + * @param comp the component to add + * @param layerConstraint an integer specifying the layer to add the + * component to + * @param index the position within the layer */ protected void addImpl(Component comp, Object layerConstraint, int index) { - Integer layer; + int layer; if (layerConstraint != null && layerConstraint instanceof Integer) - layer = (Integer) layerConstraint; - else if (componentToLayer.containsKey (comp)) - layer = (Integer) componentToLayer.remove (comp); + layer = ((Integer) layerConstraint).intValue(); else - layer = DEFAULT_LAYER; - - int newIdx = insertIndexForLayer(layer.intValue (), index); + layer = getLayer(comp); - componentToLayer.put (comp, layer); - incrLayer (layer); - - super.addImpl(comp, null, newIdx); + int newIdx = insertIndexForLayer(layer, index); + setLayer(comp, layer); + super.addImpl(comp, layerConstraint, newIdx); } /** @@ -672,7 +609,7 @@ public class JLayeredPane extends JComponent implements Accessible */ public static void putLayer(JComponent component, int layer) { - getLayeredPaneAbove(component).setLayer(component, layer); + component.putClientProperty(LAYER_PROPERTY, new Integer(layer)); } /** @@ -711,13 +648,42 @@ public class JLayeredPane extends JComponent implements Accessible } /** - * Overridden to return <code>false</code>, since <code>JLayeredPane</code> - * cannot guarantee that its children don't overlap. + * Returns <code>false</code> if components in this layered pane can overlap, + * otherwise <code>true</code>. * - * @return <code>false</code> + * @return <code>false</code> if components in this layered pane can overlap, + * otherwise <code>true</code> */ public boolean isOptimizedDrawingEnabled() { - return false; + int numChildren = getComponentCount(); + boolean result = true; + for (int i = 0; i < numChildren; ++i) + { + Component c1 = getComponent(i); + if (! c1.isVisible()) + continue; + Rectangle r1 = c1.getBounds(); + if (r1.isEmpty()) + continue; + + for (int j = i + 1; j < numChildren; ++j) + { + Component c2 = getComponent(j); + if (! c2.isVisible()) + continue; + Rectangle r2 = c2.getBounds(); + if (r2.isEmpty()) + continue; + if (r1.intersects(r2)) + { + result = false; + break; + } + if (result == false) + break; + } + } + return result; } } diff --git a/libjava/classpath/javax/swing/JMenu.java b/libjava/classpath/javax/swing/JMenu.java index 369c44d40c6..a160dd44857 100644 --- a/libjava/classpath/javax/swing/JMenu.java +++ b/libjava/classpath/javax/swing/JMenu.java @@ -906,7 +906,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement /** * This class listens to PropertyChangeEvents occuring in menu's action */ - protected class ActionChangedListener implements PropertyChangeListener + private class ActionChangedListener implements PropertyChangeListener { /** menu item associated with the action */ private JMenuItem menuItem; diff --git a/libjava/classpath/javax/swing/JMenuBar.java b/libjava/classpath/javax/swing/JMenuBar.java index f018daabf80..60726fb39bf 100644 --- a/libjava/classpath/javax/swing/JMenuBar.java +++ b/libjava/classpath/javax/swing/JMenuBar.java @@ -1,5 +1,5 @@ /* JMenuBar.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. @@ -51,6 +51,8 @@ import javax.accessibility.AccessibleSelection; import javax.accessibility.AccessibleStateSet; import javax.swing.plaf.MenuBarUI; +import javax.swing.border.Border; + /** * JMenuBar is a container for menu's. For a menu bar to be seen on the * screen, at least one menu should be added to it. Just like adding @@ -437,8 +439,12 @@ public class JMenuBar extends JComponent implements Accessible, MenuElement protected void paintBorder(Graphics g) { if (borderPainted) - getBorder().paintBorder(this, g, 0, 0, getSize(null).width, - getSize(null).height); + { + Border border = getBorder(); + if (border != null) + getBorder().paintBorder(this, g, 0, 0, getSize(null).width, + getSize(null).height); + } } /** diff --git a/libjava/classpath/javax/swing/JOptionPane.java b/libjava/classpath/javax/swing/JOptionPane.java index 057326cd209..705eca832fe 100644 --- a/libjava/classpath/javax/swing/JOptionPane.java +++ b/libjava/classpath/javax/swing/JOptionPane.java @@ -197,7 +197,7 @@ public class JOptionPane extends JComponent implements Accessible public static final String WANTS_INPUT_PROPERTY = "wantsInput"; /** The value returned when the inputValue is uninitialized. */ - public static Object UNINITIALIZED_VALUE = "uninitializedValue"; + public static final Object UNINITIALIZED_VALUE = "uninitializedValue"; /** The icon displayed in the dialog/internal frame. */ protected Icon icon; @@ -236,7 +236,7 @@ public class JOptionPane extends JComponent implements Accessible protected boolean wantsInput; /** The common frame used when no parent is provided. */ - private static Frame privFrame = SwingUtilities.getOwnerFrame(); + private static Frame privFrame = (Frame) SwingUtilities.getOwnerFrame(null); /** * Creates a new JOptionPane object using a message of "JOptionPane diff --git a/libjava/classpath/javax/swing/JPanel.java b/libjava/classpath/javax/swing/JPanel.java index c02a9cfad6f..815e452dc05 100644 --- a/libjava/classpath/javax/swing/JPanel.java +++ b/libjava/classpath/javax/swing/JPanel.java @@ -63,7 +63,7 @@ public class JPanel extends JComponent implements Accessible /** * Creates a new instance of <code>AccessibleJPanel</code>. */ - public AccessibleJPanel() + protected AccessibleJPanel() { // Nothing to do here. } diff --git a/libjava/classpath/javax/swing/JPopupMenu.java b/libjava/classpath/javax/swing/JPopupMenu.java index 1f2282e2326..74f733e921e 100644 --- a/libjava/classpath/javax/swing/JPopupMenu.java +++ b/libjava/classpath/javax/swing/JPopupMenu.java @@ -866,7 +866,7 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement /* This class resizes popup menu and repaints popup menu appropriately if one of item's action has changed */ - protected class ActionChangeListener implements PropertyChangeListener + private class ActionChangeListener implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent evt) { diff --git a/libjava/classpath/javax/swing/JProgressBar.java b/libjava/classpath/javax/swing/JProgressBar.java index abca3e7ae02..e7ee8004c09 100644 --- a/libjava/classpath/javax/swing/JProgressBar.java +++ b/libjava/classpath/javax/swing/JProgressBar.java @@ -1,5 +1,5 @@ /* JProgressBar.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. @@ -174,8 +174,8 @@ public class JProgressBar extends JComponent implements SwingConstants, /** Whether the ProgressBar is determinate. */ private transient boolean indeterminate = false; - /** The orientation of the ProgressBar */ - protected int orientation = HORIZONTAL; + /** The orientation of the ProgressBar. Always set by constructor. */ + protected int orientation; /** Whether borders should be painted. */ protected boolean paintBorder = true; @@ -245,8 +245,9 @@ public class JProgressBar extends JComponent implements SwingConstants, { model = new DefaultBoundedRangeModel(minimum, 0, minimum, maximum); if (orientation != HORIZONTAL && orientation != VERTICAL) - throw new IllegalArgumentException(orientation + " is not a legal orientation"); - setOrientation(orientation); + throw new IllegalArgumentException(orientation + + " is not a legal orientation"); + this.orientation = orientation; changeListener = createChangeListener(); model.addChangeListener(changeListener); updateUI(); @@ -316,11 +317,14 @@ public class JProgressBar extends JComponent implements SwingConstants, * JProgressBar can be either horizontal or vertical. * * @param orientation The orientation of the JProgressBar. + * @throws IllegalArgumentException if <code>orientation</code> is not + * either {@link #HORIZONTAL} or {@link #VERTICAL}. */ public void setOrientation(int orientation) { if (orientation != VERTICAL && orientation != HORIZONTAL) - throw new IllegalArgumentException("orientation must be one of VERTICAL or HORIZONTAL"); + throw new IllegalArgumentException(orientation + + " is not a legal orientation"); if (this.orientation != orientation) { int oldOrientation = this.orientation; diff --git a/libjava/classpath/javax/swing/JRootPane.java b/libjava/classpath/javax/swing/JRootPane.java index dea4ee4b195..dec43956ca3 100644 --- a/libjava/classpath/javax/swing/JRootPane.java +++ b/libjava/classpath/javax/swing/JRootPane.java @@ -120,11 +120,6 @@ public class JRootPane extends JComponent implements Accessible private Rectangle menuBarBounds; /** - * The cached preferred size. - */ - private Dimension prefSize; - - /** * Creates a new <code>RootLayout</code> object. */ protected RootLayout() @@ -191,7 +186,6 @@ public class JRootPane extends JComponent implements Accessible layeredPaneBounds = null; contentPaneBounds = null; menuBarBounds = null; - prefSize = null; } } @@ -251,7 +245,7 @@ public class JRootPane extends JComponent implements Accessible layeredPane.setBounds(layeredPaneBounds); if (menuBar != null) menuBar.setBounds(menuBarBounds); - contentPane.setBounds(contentPaneBounds); + getContentPane().setBounds(contentPaneBounds); } /** @@ -287,29 +281,20 @@ public class JRootPane extends JComponent implements Accessible */ public Dimension preferredLayoutSize(Container c) { - // We must synchronize here, otherwise we cannot guarantee that the - // prefSize is still non-null when returning. - synchronized (this) + Dimension prefSize = new Dimension(); + Insets i = getInsets(); + prefSize = new Dimension(i.left + i.right, i.top + i.bottom); + Dimension contentPrefSize = getContentPane().getPreferredSize(); + prefSize.width += contentPrefSize.width; + prefSize.height += contentPrefSize.height; + if (menuBar != null) { - if (prefSize == null) - { - Insets i = getInsets(); - prefSize = new Dimension(i.left + i.right, i.top + i.bottom); - Dimension contentPrefSize = contentPane.getPreferredSize(); - prefSize.width += contentPrefSize.width; - prefSize.height += contentPrefSize.height; - if (menuBar != null) - { - Dimension menuBarSize = menuBar.getPreferredSize(); - if (menuBarSize.width > contentPrefSize.width) - prefSize.width += menuBarSize.width - contentPrefSize.width; - prefSize.height += menuBarSize.height; - } - } - // Return a copy here so the cached value won't get trashed by some - // other component. - return new Dimension(prefSize); - } + Dimension menuBarSize = menuBar.getPreferredSize(); + if (menuBarSize.width > contentPrefSize.width) + prefSize.width += menuBarSize.width - contentPrefSize.width; + prefSize.height += menuBarSize.height; + } + return prefSize; } /** @@ -541,6 +526,7 @@ public class JRootPane extends JComponent implements Accessible getGlassPane(); getLayeredPane(); getContentPane(); + setOpaque(true); updateUI(); } @@ -674,4 +660,18 @@ public class JRootPane extends JComponent implements Accessible windowDecorationStyle = style; firePropertyChange("windowDecorationStyle", oldStyle, style); } + + /** + * This returns <code>true</code> if the <code>glassPane</code> is not + * visible because then the root pane can guarantee to tile its children + * (the only other direct child is a JLayeredPane which must figure its + * <code>optimizeDrawingEnabled</code> state on its own). + * + * @return <code>true</code> if the <code>glassPane</code> is not + * visible + */ + public boolean isOptimizedDrawingEnable() + { + return ! glassPane.isVisible(); + } } diff --git a/libjava/classpath/javax/swing/JSpinner.java b/libjava/classpath/javax/swing/JSpinner.java index af34d9cf67d..882d216e126 100644 --- a/libjava/classpath/javax/swing/JSpinner.java +++ b/libjava/classpath/javax/swing/JSpinner.java @@ -1,5 +1,5 @@ /* JSpinner.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. @@ -45,7 +45,9 @@ import java.awt.Insets; import java.awt.LayoutManager; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.text.DateFormat; import java.text.DecimalFormat; +import java.text.NumberFormat; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -53,10 +55,15 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.SpinnerUI; import javax.swing.text.DateFormatter; +import javax.swing.text.DefaultFormatterFactory; +import javax.swing.text.NumberFormatter; /** - * A JSpinner is a component which typically contains a numeric value and a - * way to manipulate the value. + * A <code>JSpinner</code> is a component that displays a single value from + * a sequence of values, and provides a convenient means for selecting the + * previous and next values in the sequence. Typically the spinner displays + * a numeric value, but it is possible to display dates or arbitrary items + * from a list. * * @author Ka-Hing Cheung * @@ -65,12 +72,15 @@ import javax.swing.text.DateFormatter; public class JSpinner extends JComponent { /** - * DOCUMENT ME! + * The base class for the editor used by the {@link JSpinner} component. + * The editor is in fact a panel containing a {@link JFormattedTextField} + * component. */ - public static class DefaultEditor extends JPanel implements ChangeListener, - PropertyChangeListener, - LayoutManager + public static class DefaultEditor + extends JPanel + implements ChangeListener, PropertyChangeListener, LayoutManager { + /** The spinner that the editor is allocated to. */ private JSpinner spinner; /** The JFormattedTextField that backs the editor. */ @@ -82,7 +92,8 @@ public class JSpinner extends JComponent private static final long serialVersionUID = -5317788736173368172L; /** - * Creates a new <code>DefaultEditor</code> object. + * Creates a new <code>DefaultEditor</code> object. The editor is + * registered with the spinner as a {@link ChangeListener} here. * * @param spinner the <code>JSpinner</code> associated with this editor */ @@ -94,11 +105,15 @@ public class JSpinner extends JComponent ftf = new JFormattedTextField(); add(ftf); ftf.setValue(spinner.getValue()); + ftf.addPropertyChangeListener(this); spinner.addChangeListener(this); } /** - * Returns the <code>JSpinner</code> object for this editor. + * Returns the <code>JSpinner</code> component that the editor is assigned + * to. + * + * @return The spinner that the editor is assigned to. */ public JSpinner getSpinner() { @@ -114,9 +129,10 @@ public class JSpinner extends JComponent } /** - * DOCUMENT ME! + * Removes the editor from the {@link ChangeListener} list maintained by + * the specified <code>spinner</code>. * - * @param spinner DOCUMENT ME! + * @param spinner the spinner (<code>null</code> not permitted). */ public void dismiss(JSpinner spinner) { @@ -124,9 +140,10 @@ public class JSpinner extends JComponent } /** - * DOCUMENT ME! + * Returns the text field used to display and edit the current value in + * the spinner. * - * @return DOCUMENT ME! + * @return The text field. */ public JFormattedTextField getTextField() { @@ -134,9 +151,10 @@ public class JSpinner extends JComponent } /** - * DOCUMENT ME! + * Sets the bounds for the child components in this container. In this + * case, the text field is the only component to be laid out. * - * @param parent DOCUMENT ME! + * @param parent the parent container. */ public void layoutContainer(Container parent) { @@ -148,11 +166,13 @@ public class JSpinner extends JComponent } /** - * DOCUMENT ME! + * Calculates the minimum size for this component. In this case, the + * text field is the only subcomponent, so the return value is the minimum + * size of the text field plus the insets of this component. * - * @param parent DOCUMENT ME! + * @param parent the parent container. * - * @return DOCUMENT ME! + * @return The minimum size. */ public Dimension minimumLayoutSize(Container parent) { @@ -163,11 +183,13 @@ public class JSpinner extends JComponent } /** - * DOCUMENT ME! + * Calculates the preferred size for this component. In this case, the + * text field is the only subcomponent, so the return value is the + * preferred size of the text field plus the insets of this component. * - * @param parent DOCUMENT ME! + * @param parent the parent container. * - * @return DOCUMENT ME! + * @return The preferred size. */ public Dimension preferredLayoutSize(Container parent) { @@ -178,35 +200,51 @@ public class JSpinner extends JComponent } /** - * DOCUMENT ME! + * Receives notification of property changes. If the text field's 'value' + * property changes, the spinner's model is updated accordingly. * - * @param event DOCUMENT ME! + * @param event the event. */ public void propertyChange(PropertyChangeEvent event) { - // TODO: Implement this properly. + if (event.getSource() == ftf) + { + if (event.getPropertyName().equals("value")) + spinner.getModel().setValue(event.getNewValue()); + } } /** - * DOCUMENT ME! + * Receives notification of changes in the state of the {@link JSpinner} + * that the editor belongs to - the content of the text field is updated + * accordingly. * - * @param event DOCUMENT ME! + * @param event the change event. */ public void stateChanged(ChangeEvent event) { - // TODO: Implement this properly. + ftf.setValue(spinner.getValue()); } + /** + * This method does nothing. It is required by the {@link LayoutManager} + * interface, but since this component has a single child, there is no + * need to use this method. + * + * @param child the child component to remove. + */ public void removeLayoutComponent(Component child) { // Nothing to do here. } /** - * DOCUMENT ME! - * - * @param name DOCUMENT ME! - * @param child DOCUMENT ME! + * This method does nothing. It is required by the {@link LayoutManager} + * interface, but since this component has a single child, there is no + * need to use this method. + * + * @param name the name. + * @param child the child component to add. */ public void addLayoutComponent(String name, Component child) { @@ -215,7 +253,11 @@ public class JSpinner extends JComponent } /** - * DOCUMENT ME! + * A panel containing a {@link JFormattedTextField} that is configured for + * displaying and editing numbers. The panel is used as a subcomponent of + * a {@link JSpinner}. + * + * @see JSpinner#createEditor(SpinnerModel) */ public static class NumberEditor extends DefaultEditor { @@ -225,40 +267,72 @@ public class JSpinner extends JComponent private static final long serialVersionUID = 3791956183098282942L; /** - * Creates a new NumberEditor object. + * Creates a new <code>NumberEditor</code> object for the specified + * <code>spinner</code>. The editor is registered with the spinner as a + * {@link ChangeListener}. * - * @param spinner DOCUMENT ME! + * @param spinner the component the editor will be used with. */ public NumberEditor(JSpinner spinner) { super(spinner); + NumberEditorFormatter nef = new NumberEditorFormatter(); + nef.setMinimum(getModel().getMinimum()); + nef.setMaximum(getModel().getMaximum()); + ftf.setFormatterFactory(new DefaultFormatterFactory(nef)); } /** - * Creates a new NumberEditor object. + * Creates a new <code>NumberEditor</code> object. * - * @param spinner DOCUMENT ME! + * @param spinner the spinner. + * @param decimalFormatPattern the number format pattern. */ public NumberEditor(JSpinner spinner, String decimalFormatPattern) { super(spinner); + NumberEditorFormatter nef + = new NumberEditorFormatter(decimalFormatPattern); + nef.setMinimum(getModel().getMinimum()); + nef.setMaximum(getModel().getMaximum()); + ftf.setFormatterFactory(new DefaultFormatterFactory(nef)); } /** - * DOCUMENT ME! + * Returns the format used by the text field. * - * @return DOCUMENT ME! + * @return The format used by the text field. */ public DecimalFormat getFormat() { - return null; + NumberFormatter formatter = (NumberFormatter) ftf.getFormatter(); + return (DecimalFormat) formatter.getFormat(); } + /** + * Returns the model used by the editor's {@link JSpinner} component, + * cast to a {@link SpinnerNumberModel}. + * + * @return The model. + */ public SpinnerNumberModel getModel() { return (SpinnerNumberModel) getSpinner().getModel(); } } + + static class NumberEditorFormatter + extends NumberFormatter + { + public NumberEditorFormatter() + { + super(NumberFormat.getInstance()); + } + public NumberEditorFormatter(String decimalFormatPattern) + { + super(new DecimalFormat(decimalFormatPattern)); + } + } /** * A <code>JSpinner</code> editor used for the {@link SpinnerListModel}. @@ -279,6 +353,11 @@ public class JSpinner extends JComponent super(spinner); } + /** + * Returns the spinner's model cast as a {@link SpinnerListModel}. + * + * @return The spinner's model. + */ public SpinnerListModel getModel() { return (SpinnerListModel) getSpinner().getModel(); @@ -299,9 +378,6 @@ public class JSpinner extends JComponent /** The serialVersionUID. */ private static final long serialVersionUID = -4279356973770397815L; - /** The DateFormat instance used to format the date. */ - SimpleDateFormat dateFormat; - /** * Creates a new instance of DateEditor for the specified * <code>JSpinner</code>. @@ -312,7 +388,10 @@ public class JSpinner extends JComponent public DateEditor(JSpinner spinner) { super(spinner); - init(new SimpleDateFormat()); + DateEditorFormatter nef = new DateEditorFormatter(); + nef.setMinimum(getModel().getStart()); + nef.setMaximum(getModel().getEnd()); + ftf.setFormatterFactory(new DefaultFormatterFactory(nef)); } /** @@ -329,26 +408,10 @@ public class JSpinner extends JComponent public DateEditor(JSpinner spinner, String dateFormatPattern) { super(spinner); - init(new SimpleDateFormat(dateFormatPattern)); - } - - /** - * Initializes the JFormattedTextField for this editor. - * - * @param format the date format to use in the formatted text field - */ - private void init(SimpleDateFormat format) - { - dateFormat = format; - getTextField().setFormatterFactory( - new JFormattedTextField.AbstractFormatterFactory() - { - public JFormattedTextField.AbstractFormatter - getFormatter(JFormattedTextField ftf) - { - return new DateFormatter(dateFormat); - } - }); + DateEditorFormatter nef = new DateEditorFormatter(dateFormatPattern); + nef.setMinimum(getModel().getStart()); + nef.setMaximum(getModel().getEnd()); + ftf.setFormatterFactory(new DefaultFormatterFactory(nef)); } /** @@ -360,7 +423,8 @@ public class JSpinner extends JComponent */ public SimpleDateFormat getFormat() { - return dateFormat; + DateFormatter formatter = (DateFormatter) ftf.getFormatter(); + return (SimpleDateFormat) formatter.getFormat(); } /** @@ -374,25 +438,59 @@ public class JSpinner extends JComponent } } - private static final long serialVersionUID = 3412663575706551720L; + static class DateEditorFormatter + extends DateFormatter + { + public DateEditorFormatter() + { + super(DateFormat.getInstance()); + } + public DateEditorFormatter(String dateFormatPattern) + { + super(new SimpleDateFormat(dateFormatPattern)); + } + } + + /** + * A listener that forwards {@link ChangeEvent} notifications from the model + * to the {@link JSpinner}'s listeners. + */ + class ModelListener implements ChangeListener + { + /** + * Creates a new listener. + */ + public ModelListener() + { + // nothing to do here + } + + /** + * Receives notification from the model that its state has changed. + * + * @param event the event (ignored). + */ + public void stateChanged(ChangeEvent event) + { + fireStateChanged(); + } + } - /** DOCUMENT ME! */ + /** + * The model that defines the current value and permitted values for the + * spinner. + */ private SpinnerModel model; - /** DOCUMENT ME! */ + /** The current editor. */ private JComponent editor; - /** DOCUMENT ME! */ - private ChangeListener listener = new ChangeListener() - { - public void stateChanged(ChangeEvent evt) - { - fireStateChanged(); - } - }; + private static final long serialVersionUID = 3412663575706551720L; /** - * Creates a JSpinner with <code>SpinnerNumberModel</code> + * Creates a new <code>JSpinner</code> with default instance of + * {@link SpinnerNumberModel} (that is, a model with value 0, step size 1, + * and no upper or lower limit). * * @see javax.swing.SpinnerNumberModel */ @@ -402,15 +500,19 @@ public class JSpinner extends JComponent } /** - * Creates a JSpinner with the specific model and sets the default editor + * Creates a new <code>JSpinner with the specified model. The + * {@link #createEditor(SpinnerModel)} method is used to create an editor + * that is suitable for the model. * - * @param model DOCUMENT ME! + * @param model the model (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>model</code> is <code>null</code>. */ public JSpinner(SpinnerModel model) { this.model = model; - model.addChangeListener(listener); - setEditor(createEditor(model)); + this.editor = createEditor(model); + model.addChangeListener(new ModelListener()); updateUI(); } @@ -439,12 +541,13 @@ public class JSpinner extends JComponent } /** - * Changes the current editor to the new editor. This methods should remove - * the old listeners (if any) and adds the new listeners (if any). + * Changes the current editor to the new editor. The old editor is + * removed from the spinner's {@link ChangeEvent} list. * - * @param editor the new editor + * @param editor the new editor (<code>null</code> not permitted. * - * @throws IllegalArgumentException DOCUMENT ME! + * @throws IllegalArgumentException if <code>editor</code> is + * <code>null</code>. * * @see #getEditor */ @@ -453,21 +556,22 @@ public class JSpinner extends JComponent if (editor == null) throw new IllegalArgumentException("editor may not be null"); - if (this.editor instanceof DefaultEditor) - ((DefaultEditor) editor).dismiss(this); - else if (this.editor instanceof ChangeListener) - removeChangeListener((ChangeListener) this.editor); - - if (editor instanceof ChangeListener) - addChangeListener((ChangeListener) editor); - + JComponent oldEditor = this.editor; + if (oldEditor instanceof DefaultEditor) + ((DefaultEditor) oldEditor).dismiss(this); + else if (oldEditor instanceof ChangeListener) + removeChangeListener((ChangeListener) oldEditor); + this.editor = editor; + firePropertyChange("editor", oldEditor, editor); } /** - * Gets the underly model. + * Returns the model used by the {@link JSpinner} component. * - * @return the underly model + * @return The model. + * + * @see #setModel(SpinnerModel) */ public SpinnerModel getModel() { @@ -492,9 +596,7 @@ public class JSpinner extends JComponent SpinnerModel oldModel = model; model = newModel; firePropertyChange("model", oldModel, newModel); - - if (editor == null) - setEditor(createEditor(model)); + setEditor(createEditor(model)); } /** @@ -545,9 +647,9 @@ public class JSpinner extends JComponent } /** - * DOCUMENT ME! + * Sets the value in the model. * - * @param value DOCUMENT ME! + * @param value the new value. */ public void setValue(Object value) { @@ -555,10 +657,10 @@ public class JSpinner extends JComponent } /** - * This method returns a name to identify which look and feel class will be + * Returns the ID that identifies which look and feel class will be * the UI delegate for this spinner. * - * @return The UIClass identifier. "SpinnerUI" + * @return <code>"SpinnerUI"</code>. */ public String getUIClassID() { @@ -575,7 +677,7 @@ public class JSpinner extends JComponent } /** - * This method sets the spinner's UI delegate. + * Sets the UI delegate for the component. * * @param ui The spinner's UI delegate. */ @@ -628,14 +730,11 @@ public class JSpinner extends JComponent } /** - * Creates an editor for this <code>JSpinner</code>. Really, it should be a - * <code>JSpinner.DefaultEditor</code>, but since that should be - * implemented by a JFormattedTextField, and one is not written, I am just - * using a dummy one backed by a JLabel. + * Creates an editor that is appropriate for the specified <code>model</code>. * - * @param model DOCUMENT ME! + * @param model the model. * - * @return the default editor + * @return The editor. */ protected JComponent createEditor(SpinnerModel model) { @@ -643,6 +742,8 @@ public class JSpinner extends JComponent return new DateEditor(this); else if (model instanceof SpinnerNumberModel) return new NumberEditor(this); + else if (model instanceof SpinnerListModel) + return new ListEditor(this); else return new DefaultEditor(this); } diff --git a/libjava/classpath/javax/swing/JSplitPane.java b/libjava/classpath/javax/swing/JSplitPane.java index 70feefab26e..dc75dfe3184 100644 --- a/libjava/classpath/javax/swing/JSplitPane.java +++ b/libjava/classpath/javax/swing/JSplitPane.java @@ -343,10 +343,13 @@ public class JSplitPane extends JComponent implements Accessible throw new IllegalArgumentException("Constraints is not a known identifier."); + // If no dividerLocation has been set, then we need to trigger an + // initial layout. + if (getDividerLocation() != -1) + resetToPreferredSizes(); + super.addImpl(comp, constraints, index); } - invalidate(); - layout(); } /** diff --git a/libjava/classpath/javax/swing/JTabbedPane.java b/libjava/classpath/javax/swing/JTabbedPane.java index 8a7d4c07fa7..3c91a5ea397 100644 --- a/libjava/classpath/javax/swing/JTabbedPane.java +++ b/libjava/classpath/javax/swing/JTabbedPane.java @@ -44,12 +44,14 @@ import java.awt.Point; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.io.Serializable; +import java.util.Locale; import java.util.Vector; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleRole; import javax.accessibility.AccessibleSelection; +import javax.accessibility.AccessibleStateSet; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.TabbedPaneUI; @@ -136,7 +138,12 @@ public class JTabbedPane extends JComponent implements Serializable, */ public Accessible getAccessibleChild(int i) { - return null; + // Testing shows that the reference implementation returns instances + // of page here. + Accessible child = null; + if (i >= 0 && i < tabs.size()) + child = (Page) tabs.get(i); + return child; } /** @@ -273,6 +280,8 @@ public class JTabbedPane extends JComponent implements Serializable, * A private class that holds all the information for each tab. */ private class Page + extends AccessibleContext + implements Accessible { /** The tooltip string. */ private String tip; @@ -553,6 +562,74 @@ public class JTabbedPane extends JComponent implements Serializable, underlinedChar = index; } + + /** + * Returns the accessible context, which is this object itself. + * + * @return the accessible context, which is this object itself + */ + public AccessibleContext getAccessibleContext() + { + return this; + } + + /** + * Returns the accessible role of this tab, which is always + * {@link AccessibleRole#PAGE_TAB}. + * + * @return the accessible role of this tab + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.PAGE_TAB; + } + + public AccessibleStateSet getAccessibleStateSet() + { + // FIXME: Implement this properly. + return null; + } + + public int getAccessibleIndexInParent() + { + // FIXME: Implement this properly. + return 0; + } + + /** + * Returns the number of accessible children, which is always one (the + * component of this tab). + * + * @return the number of accessible children + */ + public int getAccessibleChildrenCount() + { + return 1; + } + + /** + * Returns the accessible child of this tab, which is the component + * displayed by the tab. + * + * @return the accessible child of this tab + */ + public Accessible getAccessibleChild(int i) + { + // A quick test shows that this method always returns the component + // displayed by the tab, regardless of the index. + return (Accessible) component; + } + + /** + * Returns the locale of this accessible object. + * + * @return the locale of this accessible object + */ + public Locale getLocale() + { + // TODO: Is this ok? + return Locale.getDefault(); + } } private static final long serialVersionUID = 1614381073220130939L; @@ -1088,7 +1165,7 @@ public class JTabbedPane extends JComponent implements Serializable, */ public void remove(int index) { - remove(getComponentAt(index)); + super.remove(index); removeTabAt(index); } diff --git a/libjava/classpath/javax/swing/JTable.java b/libjava/classpath/javax/swing/JTable.java index 0875306a012..fbf74934a0a 100644 --- a/libjava/classpath/javax/swing/JTable.java +++ b/libjava/classpath/javax/swing/JTable.java @@ -1,5 +1,5 @@ /* JTable.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. @@ -46,8 +46,6 @@ import java.awt.Font; import java.awt.FontMetrics; import java.awt.Point; import java.awt.Rectangle; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.FocusListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -86,8 +84,16 @@ import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; -import javax.swing.text.Caret; +/** + * The table component, displaying information, organized in rows and columns. + * The table can be placed in the scroll bar and have the optional header + * that is always visible. Cell values may be editable after double clicking + * on the cell. Cell columns may have various data types, that are + * displayed and edited by the different renderers and editors. It is possible + * to set different column width. The columns are also resizeable by + * dragging the column boundary in the header. + */ public class JTable extends JComponent implements TableModelListener, Scrollable, TableColumnModelListener, @@ -588,7 +594,7 @@ public class JTable return lastColumn; } } - + /** * Creates a new <code>AccessibleJTable</code>. * @@ -979,9 +985,9 @@ public class JTable class TableColumnPropertyChangeHandler implements PropertyChangeListener { /** - * Receives notification that a property of the observed TableColumns - * has changed. - * + * Receives notification that a property of the observed TableColumns has + * changed. + * * @param ev the property change event */ public void propertyChange(PropertyChangeEvent ev) @@ -989,13 +995,15 @@ public class JTable if (ev.getPropertyName().equals("preferredWidth")) { JTableHeader header = getTableHeader(); - if (header != null) - { - TableColumn col = (TableColumn) ev.getSource(); - header.setResizingColumn(col); - doLayout(); - header.setResizingColumn(null); - } + if (header != null) + // Do nothing if the table is in the resizing mode. + if (header.getResizingColumn() == null) + { + TableColumn col = (TableColumn) ev.getSource(); + header.setResizingColumn(col); + doLayout(); + header.setResizingColumn(null); + } } } } @@ -1006,11 +1014,30 @@ public class JTable private class BooleanCellRenderer extends DefaultTableCellRenderer { - /** * The CheckBox that is used for rendering. */ - private JCheckBox checkBox = new JCheckBox(); + private final JCheckBox checkBox = new JCheckBox(); + + /** + * The check box must have the text field background and be centered. + */ + private BooleanCellRenderer() + { + // Render the checkbox identically as the text field. + JTextField f = new JTextField(); + checkBox.setForeground(f.getForeground()); + checkBox.setBackground(f.getBackground()); + checkBox.setHorizontalAlignment(SwingConstants.CENTER); + } + + /** + * Get the check box. + */ + JCheckBox getCheckBox() + { + return checkBox; + } /** * Returns the component that is used for rendering the value. @@ -1029,8 +1056,14 @@ public class JTable boolean hasFocus, int row, int column) { - Boolean boolValue = (Boolean) value; - checkBox.setSelected(boolValue.booleanValue()); + // Null is rendered as false. + if (value == null) + checkBox.setSelected(false); + else + { + Boolean boolValue = (Boolean) value; + checkBox.setSelected(boolValue.booleanValue()); + } return checkBox; } } @@ -1200,12 +1233,52 @@ public class JTable { Icon iconValue = (Icon) value; setIcon(iconValue); + setText(""); } return this; } } + + /** + * The JTable text component (used in editing) always has the table + * as its parent. The scrollRectToVisible must be adjusted taking the + * relative component position. + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ + private class TableTextField extends JTextField + { + /** + * Create the text field without the border. + */ + TableTextField() + { + setBorder(null); + } + + /** + * Scroll the table, making the given rectangle of this component + * visible. Mind the component position with relate to the table. + * With not this method overridden, the scroll pane scrolls to the + * top left cornec (untranslated position of the caret) after the first + * keystroke. + */ + public void scrollRectToVisible(Rectangle r) + { + // In private class we known that the rectangle data will not be + // reused and we need not to clone it. + r.translate(getX(), getY()); + super.scrollRectToVisible(r); + } + } + private static final long serialVersionUID = 3876025080382781659L; + + /** + * This table, for referring identically name methods from inner classes. + */ + final JTable this_table = this; /** @@ -1343,7 +1416,7 @@ public class JTable protected boolean rowSelectionAllowed; /** - * @deprecated Use {@link #rowSelectionAllowed}, {@link + * Obsolete. Use {@link #rowSelectionAllowed}, {@link * #getColumnSelectionAllowed}, or the combined methods {@link * #getCellSelectionEnabled} and {@link #setCellSelectionEnabled(boolean)}. */ @@ -1477,27 +1550,6 @@ public class JTable protected JTableHeader tableHeader; /** - * The row of the cell being edited. - */ - int rowBeingEdited = -1; - - /** - * The column of the cell being edited. - */ - int columnBeingEdited = -1; - - /** - * The action listener for the editor's Timer. - */ - Timer editorTimer = new EditorUpdateTimer(); - - /** - * Stores the old value of a cell before it was edited, in case - * editing is cancelled - */ - Object oldCellValue; - - /** * The property handler for this table's columns. */ TableColumnPropertyChangeHandler tableColumnPropertyChangeHandler = @@ -1510,6 +1562,11 @@ public class JTable private boolean surrendersFocusOnKeystroke = false; /** + * A Rectangle object to be reused in {@link #getCellRect}. + */ + private Rectangle rectCache = new Rectangle(); + + /** * Creates a new <code>JTable</code> instance. */ public JTable () @@ -1518,7 +1575,8 @@ public class JTable } /** - * Creates a new <code>JTable</code> instance. + * Creates a new <code>JTable</code> instance with the given number + * of rows and columns. * * @param numRows an <code>int</code> value * @param numColumns an <code>int</code> value @@ -1529,10 +1587,12 @@ public class JTable } /** - * Creates a new <code>JTable</code> instance. + * Creates a new <code>JTable</code> instance, storing the given data + * array and heaving the given column names. To see the column names, + * you must place the JTable into the {@link JScrollPane}. * - * @param data an <code>Object[][]</code> value - * @param columnNames an <code>Object[]</code> value + * @param data an <code>Object[][]</code> the table data + * @param columnNames an <code>Object[]</code> the column headers */ public JTable(Object[][] data, Object[] columnNames) { @@ -1540,20 +1600,31 @@ public class JTable } /** - * Creates a new <code>JTable</code> instance. - * - * @param dm a <code>TableModel</code> value + * Creates a new <code>JTable</code> instance, using the given data model + * object that provides information about the table content. The table model + * object is asked for the table size, other features and also receives + * notifications in the case when the table has been edited by the user. + * + * @param model + * the table model. */ - public JTable (TableModel dm) + public JTable (TableModel model) { - this(dm, null, null); + this(model, null, null); } /** - * Creates a new <code>JTable</code> instance. - * - * @param dm a <code>TableModel</code> value - * @param cm a <code>TableColumnModel</code> value + * Creates a new <code>JTable</code> instance, using the given model object + * that provides information about the table content. The table data model + * object is asked for the table size, other features and also receives + * notifications in the case when the table has been edited by the user. The + * table column model provides more detailed control on the table column + * related features. + * + * @param dm + * the table data mode + * @param cm + * the table column model */ public JTable (TableModel dm, TableColumnModel cm) { @@ -1561,11 +1632,13 @@ public class JTable } /** - * Creates a new <code>JTable</code> instance. + * Creates a new <code>JTable</code> instance, providing data model, + * column model and list selection model. The list selection model + * manages the selections. * - * @param dm a <code>TableModel</code> value - * @param cm a <code>TableColumnModel</code> value - * @param sm a <code>ListSelectionModel</code> value + * @param dm data model (manages table data) + * @param cm column model (manages table columns) + * @param sm list selection model (manages table selections) */ public JTable (TableModel dm, TableColumnModel cm, ListSelectionModel sm) { @@ -1593,8 +1666,23 @@ public class JTable columnModel.getSelectionModel().setAnchorSelectionIndex(0); columnModel.getSelectionModel().setLeadSelectionIndex(0); updateUI(); - } - + } + + /** + * Creates a new <code>JTable</code> instance that uses data and column + * names, stored in {@link Vector}s. + * + * @param data the table data + * @param columnNames the table column names. + */ + public JTable(Vector data, Vector columnNames) + { + this(new DefaultTableModel(data, columnNames)); + } + + /** + * Initialize local variables to default values. + */ protected void initializeLocalVars() { setTableHeader(createDefaultTableHeader()); @@ -1623,63 +1711,18 @@ public class JTable this.editingRow = -1; setIntercellSpacing(new Dimension(1,1)); } - - /** - * Creates a new <code>JTable</code> instance. - * - * @param data a <code>Vector</code> value - * @param columnNames a <code>Vector</code> value - */ - public JTable(Vector data, Vector columnNames) - { - this(new DefaultTableModel(data, columnNames)); - } - + /** - * The timer that updates the editor component. + * Add the new table column. The table column class allows to specify column + * features more precisely, setting the preferred width, column data type + * (column class) and table headers. + * + * There is no need the add columns to the table if the default column + * handling is sufficient. + * + * @param column + * the new column to add. */ - private class EditorUpdateTimer - extends Timer - implements ActionListener - { - /** - * Creates a new EditorUpdateTimer object with a default delay of 0.5 seconds. - */ - public EditorUpdateTimer() - { - super(500, null); - addActionListener(this); - } - - /** - * Lets the caret blink and repaints the table. - */ - public void actionPerformed(ActionEvent ev) - { - Caret c = ((JTextField)JTable.this.editorComp).getCaret(); - if (c != null) - c.setVisible(!c.isVisible()); - JTable.this.repaint(); - } - - /** - * Updates the blink delay according to the current caret. - */ - public void update() - { - stop(); - Caret c = ((JTextField)JTable.this.editorComp).getCaret(); - if (c != null) - { - setDelay(c.getBlinkRate()); - if (((JTextField)JTable.this.editorComp).isEditable()) - start(); - else - c.setVisible(false); - } - } - } - public void addColumn(TableColumn column) { if (column.getHeaderValue() == null) @@ -1691,12 +1734,24 @@ public class JTable columnModel.addColumn(column); column.addPropertyChangeListener(tableColumnPropertyChangeHandler); } - + + /** + * Create the default editors for this table. The default method creates + * the editor for Booleans. + * + * Other fields are edited as strings at the moment. + */ protected void createDefaultEditors() { - //FIXME: Create the editor object. + JCheckBox box = new BooleanCellRenderer().getCheckBox(); + setDefaultEditor(Boolean.class, new DefaultCellEditor(box)); } - + + /** + * Create the default renderers for this table. The default method creates + * renderers for Boolean, Number, Double, Date, Icon and ImageIcon. + * + */ protected void createDefaultRenderers() { setDefaultRenderer(Boolean.class, new BooleanCellRenderer()); @@ -1705,6 +1760,7 @@ public class JTable setDefaultRenderer(Double.class, new FloatCellRenderer()); setDefaultRenderer(Date.class, new DateCellRenderer()); setDefaultRenderer(Icon.class, new IconCellRenderer()); + setDefaultRenderer(ImageIcon.class, new IconCellRenderer()); } /** @@ -1714,112 +1770,148 @@ public class JTable { return new JScrollPane(table); } - + + /** + * Create the default table column model that is used if the user-defined + * column model is not provided. The default method creates + * {@link DefaultTableColumnModel}. + * + * @return the created table column model. + */ protected TableColumnModel createDefaultColumnModel() { return new DefaultTableColumnModel(); } + /** + * Create the default table data model that is used if the user-defined + * data model is not provided. The default method creates + * {@link DefaultTableModel}. + * + * @return the created table data model. + */ protected TableModel createDefaultDataModel() { return new DefaultTableModel(); } + /** + * Create the default table selection model that is used if the user-defined + * selection model is not provided. The default method creates + * {@link DefaultListSelectionModel}. + * + * @return the created table data model. + */ protected ListSelectionModel createDefaultSelectionModel() { return new DefaultListSelectionModel(); } - + + /** + * Create the default table header, if the user - defined table header is not + * provided. + * + * @return the default table header. + */ protected JTableHeader createDefaultTableHeader() { return new JTableHeader(columnModel); } - - // listener support - + + /** + * Invoked when the column is added. Revalidates and repains the table. + */ public void columnAdded (TableColumnModelEvent event) { revalidate(); repaint(); } + /** + * Invoked when the column margin is changed. + * Revalidates and repains the table. + */ public void columnMarginChanged (ChangeEvent event) { revalidate(); repaint(); } + /** + * Invoked when the column is moved. Revalidates and repains the table. + */ public void columnMoved (TableColumnModelEvent event) { revalidate(); repaint(); } + /** + * Invoked when the column is removed. Revalidates and repains the table. + */ public void columnRemoved (TableColumnModelEvent event) { revalidate(); repaint(); } + /** + * Invoked when the the column selection changes. + */ public void columnSelectionChanged (ListSelectionEvent event) { repaint(); } - + + /** + * Invoked when the editing is cancelled. + */ public void editingCanceled (ChangeEvent event) { - if (rowBeingEdited > -1 && columnBeingEdited > -1) + if (editorComp!=null) { - if (getValueAt(rowBeingEdited, columnBeingEdited) instanceof JTextField) - { - remove ((Component)getValueAt(rowBeingEdited, columnBeingEdited)); - setValueAt(oldCellValue, rowBeingEdited, columnBeingEdited); - } - rowBeingEdited = -1; - columnBeingEdited = -1; + remove(editorComp); + repaint(editorComp.getBounds()); + editorComp = null; } - editorTimer.stop(); - editorComp = null; - cellEditor = null; - requestFocusInWindow(false); - repaint(); } - + + /** + * Finish the current editing session and update the table with the + * new value by calling {@link #setValueAt}. + * + * @param event the change event + */ public void editingStopped (ChangeEvent event) { - if (rowBeingEdited > -1 && columnBeingEdited > -1) + if (editorComp!=null) { - if (getValueAt(rowBeingEdited, columnBeingEdited) instanceof JTextField) - { - remove((Component)getValueAt(rowBeingEdited, columnBeingEdited)); - setValueAt(((JTextField)editorComp).getText(), - rowBeingEdited, columnBeingEdited); - } - rowBeingEdited = -1; - columnBeingEdited = -1; + remove(editorComp); + setValueAt(cellEditor.getCellEditorValue(), editingRow, editingColumn); + repaint(editorComp.getBounds()); + editorComp = null; } - editorTimer.stop(); - editorComp = null; - cellEditor = null; - requestFocusInWindow(false); - repaint(); + requestFocusInWindow(); } + /** + * Invoked when the table changes. + * <code>null</code> means everything changed. + */ public void tableChanged (TableModelEvent event) { // update the column model from the table model if the structure has // changed and the flag autoCreateColumnsFromModel is set - if ((event.getFirstRow() ==TableModelEvent.HEADER_ROW) - && autoCreateColumnsFromModel) - + if ((event == null || (event.getFirstRow() == TableModelEvent.HEADER_ROW)) + && autoCreateColumnsFromModel) createDefaultColumnsFromModel(); // If the structure changes, we need to revalidate, since that might // affect the size parameters of the JTable. Otherwise we only need // to perform a repaint to update the view. - if (event.getType() == TableModelEvent.INSERT) + if (event == null || event.getType() == TableModelEvent.INSERT) revalidate(); - else if (event.getType() == TableModelEvent.DELETE) + if (event == null || event.getType() == TableModelEvent.DELETE) { if (dataModel.getRowCount() == 0) clearSelection(); @@ -1828,6 +1920,9 @@ public class JTable repaint(); } + /** + * Invoked when another table row is selected. + */ public void valueChanged (ListSelectionEvent event) { repaint(); @@ -1863,29 +1958,30 @@ public class JTable } /** - * Returns index of the row that contains specified point or - * -1 if this table doesn't contain this point. - * - * @param point point to identify the row - * @return index of the row that contains specified point or - * -1 if this table doesn't contain this point. + * Returns index of the row that contains specified point or -1 if this table + * doesn't contain this point. + * + * @param point + * point to identify the row + * @return index of the row that contains specified point or -1 if this table + * doesn't contain this point. */ public int rowAtPoint(Point point) { if (point != null) { int nrows = getRowCount(); - int height = getRowHeight(); + int height = getRowHeight() + getRowMargin(); int y = point.y; - for (int i = 0; i < nrows; ++i) - { - if (0 <= y && y < height) - return i; - y -= height; - } + int r = y / height; + if (r < 0 || r >= nrows) + return -1; + else + return r; } - return -1; + else + return -1; } /** @@ -1908,6 +2004,9 @@ public class JTable int column, boolean includeSpacing) { + // moveToCellBeingEdited expects the cached value and clones it. + // If the caching would be removed later, uplate moveToCellBeingEdited + // as well. int height = getRowHeight(row); int width = columnModel.getColumn(column).getWidth(); int x_gap = columnModel.getColumnMargin(); @@ -1923,9 +2022,10 @@ public class JTable x += columnModel.getColumn(i).getWidth(); if (includeSpacing) - return new Rectangle(x, y, width, height); + rectCache.setBounds(x, y, width, height +y_gap); else - return new Rectangle(x, y, width - x_gap, height - y_gap); + rectCache.setBounds(x, y, width - x_gap, height); + return rectCache; } public void clearSelection() @@ -2008,44 +2108,88 @@ public class JTable } + /** + * Get the cell editor, suitable for editing the given cell. The default + * method requests the editor from the column model. If the column model does + * not provide the editor, the call is forwarded to the + * {@link #getDefaultEditor(Class)} with the parameter, obtained from + * {@link TableModel#getColumnClass(int)}. + * + * @param row the cell row + * @param column the cell column + * @return the editor to edit that cell + */ public TableCellEditor getCellEditor(int row, int column) { TableCellEditor editor = columnModel.getColumn(column).getCellEditor(); if (editor == null) - editor = getDefaultEditor(dataModel.getColumnClass(column)); - + { + int mcolumn = convertColumnIndexToModel(column); + editor = getDefaultEditor(dataModel.getColumnClass(mcolumn)); + } + return editor; } - + + /** + * Get the default editor for editing values of the given type + * (String, Boolean and so on). + * + * @param columnClass the class of the value that will be edited. + * + * @return the editor, suitable for editing this data type + */ public TableCellEditor getDefaultEditor(Class columnClass) { if (defaultEditorsByColumnClass.containsKey(columnClass)) return (TableCellEditor) defaultEditorsByColumnClass.get(columnClass); else { - // FIXME: We have at least an editor for Object.class in our defaults. - TableCellEditor r = new DefaultCellEditor(new JTextField()); + JTextField t = new TableTextField(); + TableCellEditor r = new DefaultCellEditor(t); defaultEditorsByColumnClass.put(columnClass, r); return r; } } - + + /** + * Get the cell renderer for rendering the given cell. + * + * @param row the cell row + * @param column the cell column + * @return the cell renderer to render that cell. + */ public TableCellRenderer getCellRenderer(int row, int column) { - TableCellRenderer renderer = - columnModel.getColumn(column).getCellRenderer(); + TableCellRenderer renderer = columnModel.getColumn(column).getCellRenderer(); if (renderer == null) - renderer = getDefaultRenderer(getColumnClass(column)); - + { + int mcolumn = convertColumnIndexToModel(column); + renderer = getDefaultRenderer(dataModel.getColumnClass(mcolumn)); + } return renderer; } - + + /** + * Set default renderer for rendering the given data type. + * + * @param columnClass the data type (String, Boolean and so on) that must be + * rendered. + * @param rend the renderer that will rend this data type + */ public void setDefaultRenderer(Class columnClass, TableCellRenderer rend) { defaultRenderersByColumnClass.put(columnClass, rend); } - + + /** + * Get the default renderer for rendering the given data type. + * + * @param columnClass the data that must be rendered + * + * @return the appropriate defauld renderer for rendering that data type. + */ public TableCellRenderer getDefaultRenderer(Class columnClass) { if (defaultRenderersByColumnClass.containsKey(columnClass)) @@ -2057,7 +2201,19 @@ public class JTable return r; } } - + + /** + * Convert the table model index into the table column number. + * The model number need not match the real column position. The columns + * may be rearranged by the user with mouse at any time by dragging the + * column headers. + * + * @param vc the column number (0=first). + * + * @return the table column model index of this column. + * + * @see TableColumn#getModelIndex() + */ public int convertColumnIndexToModel(int vc) { if (vc < 0) @@ -2065,7 +2221,19 @@ public class JTable else return columnModel.getColumn(vc).getModelIndex(); } - + + /** + * Convert the table column number to the table column model index. + * The model number need not match the real column position. The columns + * may be rearranged by the user with mouse at any time by dragging the + * column headers. + * + * @param mc the table column index (0=first). + * + * @return the table column number in the model + * + * @see TableColumn#getModelIndex() + */ public int convertColumnIndexToView(int mc) { if (mc < 0) @@ -2078,7 +2246,16 @@ public class JTable } return -1; } - + + /** + * Prepare the renderer for rendering the given cell. + * + * @param renderer the renderer being prepared + * @param row the row of the cell being rendered + * @param column the column of the cell being rendered + * + * @return the component which .paint() method will paint the cell. + */ public Component prepareRenderer(TableCellRenderer renderer, int row, int column) @@ -2640,8 +2817,13 @@ public class JTable if (dataModel != null && columnModel != null) { int ncols = getColumnCount(); + TableColumn column; for (int i = 0; i < ncols; ++i) - columnModel.getColumn(i).setHeaderValue(dataModel.getColumnName(i)); + { + column = columnModel.getColumn(i); + if (column.getHeaderValue()==null) + column.setHeaderValue(dataModel.getColumnName(i)); + } } // according to Sun's spec we also have to set the tableHeader's @@ -2899,10 +3081,36 @@ public class JTable for (int i = 0; i < cols.length; i++) { if (cols[i] != null) - cols[i].setWidth(cols[i].getWidth() + average); + cols[i].setWidth(cols[i].getPreferredWidth() + average); } } - + + /** + * This distributes the superfluous width in a table, setting the width of the + * column being resized strictly to its preferred width. + */ + private void distributeSpillResizing(TableColumn[] cols, int spill, + TableColumn resizeIt) + { + int average = 0; + if (cols.length != 1) + average = spill / (cols.length-1); + for (int i = 0; i < cols.length; i++) + { + if (cols[i] != null && !cols[i].equals(resizeIt)) + cols[i].setWidth(cols[i].getPreferredWidth() + average); + } + resizeIt.setWidth(resizeIt.getPreferredWidth()); + } + + /** + * Set the widths of all columns, taking they preferred widths into + * consideration. The excess space, if any, will be distrubuted between + * all columns. This method also handles special cases when one of the + * collumns is currently being resized. + * + * @see TableColumn#setPreferredWidth(int) + */ public void doLayout() { TableColumn resizingColumn = null; @@ -2911,7 +3119,6 @@ public class JTable if (ncols < 1) return; - int[] pref = new int[ncols]; int prefSum = 0; int rCol = -1; @@ -2921,8 +3128,7 @@ public class JTable for (int i = 0; i < ncols; ++i) { TableColumn col = columnModel.getColumn(i); - int p = col.getWidth(); - pref[i] = p; + int p = col.getPreferredWidth(); prefSum += p; if (resizingColumn == col) rCol = i; @@ -2951,14 +3157,38 @@ public class JTable cols = new TableColumn[ncols]; for (int i = 0; i < ncols; ++i) cols[i] = columnModel.getColumn(i); - distributeSpill(cols, spill); + distributeSpillResizing(cols, spill, resizingColumn); break; case AUTO_RESIZE_SUBSEQUENT_COLUMNS: - cols = new TableColumn[ncols]; - for (int i = rCol; i < ncols; ++i) - cols[i] = columnModel.getColumn(i); - distributeSpill(cols, spill); + + // Subtract the width of the non-resized columns from the spill. + int w = 0; + int wp = 0; + TableColumn column; + for (int i = 0; i < rCol; i++) + { + column = columnModel.getColumn(i); + w += column.getWidth(); + wp+= column.getPreferredWidth(); + } + + // The number of columns right from the column being resized. + int n = ncols-rCol-1; + if (n>0) + { + // If there are any columns on the right sied to resize. + spill = (getWidth()-w) - (prefSum-wp); + int average = spill / n; + + // For all columns right from the column being resized: + for (int i = rCol+1; i < ncols; i++) + { + column = columnModel.getColumn(i); + column.setWidth(column.getPreferredWidth() + average); + } + } + resizingColumn.setWidth(resizingColumn.getPreferredWidth()); break; case AUTO_RESIZE_OFF: @@ -2974,6 +3204,16 @@ public class JTable cols[i] = columnModel.getColumn(i); distributeSpill(cols, spill); } + + if (editorComp!=null) + moveToCellBeingEdited(editorComp); + + // Repaint fixes the invalid view after the first keystroke if the cell + // editing is started immediately after the program start or cell + // resizing. + repaint(); + if (tableHeader!=null) + tableHeader.repaint(); } /** @@ -2983,7 +3223,7 @@ public class JTable { doLayout(); } - + /** * Obsolete since JDK 1.4. Please use <code>doLayout()</code>. */ @@ -3023,48 +3263,109 @@ public class JTable revalidate(); repaint(); } - + + /** + * Get the class (datatype) of the column. The cells are rendered and edited + * differently, depending from they data type. + * + * @param column the column (not the model index). + * + * @return the class, defining data type of that column (String.class for + * String, Boolean.class for boolean and so on). + */ public Class getColumnClass(int column) { - return getModel().getColumnClass(column); + return getModel().getColumnClass(convertColumnIndexToModel(column)); } + /** + * Get the name of the column. If the column has the column identifier set, + * the return value is the result of the .toString() method call on that + * identifier. If the identifier is not explicitly set, the returned value + * is calculated by + * {@link javax.swing.table.AbstractTableModel#getColumnName(int)}. + * + * @param column the column + * + * @return the name of that column. + */ public String getColumnName(int column) { int modelColumn = columnModel.getColumn(column).getModelIndex(); return dataModel.getColumnName(modelColumn); } - + + /** + * Get the column, currently being edited + * + * @return the column, currently being edited. + */ public int getEditingColumn() { return editingColumn; } - + + /** + * Set the column, currently being edited + * + * @param column the column, currently being edited. + */ public void setEditingColumn(int column) { editingColumn = column; } + /** + * Get the row currently being edited. + * + * @return the row, currently being edited. + */ public int getEditingRow() { return editingRow; } - - public void setEditingRow(int column) + + /** + * Set the row currently being edited. + * + * @param row the row, that will be edited + */ + public void setEditingRow(int row) { - editingRow = column; + editingRow = row; } + /** + * Get the editor component that is currently editing one of the cells + * + * @return the editor component or null, if none of the cells is being + * edited. + */ public Component getEditorComponent() { return editorComp; } + /** + * Check if one of the table cells is currently being edited. + * + * @return true if there is a cell being edited. + */ public boolean isEditing() { return editorComp != null; } - + + /** + * Set the default editor for the given column class (column data type). + * By default, String is handled by text field and Boolean is handled by + * the check box. + * + * @param columnClass the column data type + * @param editor the editor that will edit this data type + * + * @see TableModel#getColumnClass(int) + */ public void setDefaultEditor(Class columnClass, TableCellEditor editor) { if (editor != null) @@ -3072,7 +3373,7 @@ public class JTable else defaultEditorsByColumnClass.remove(columnClass); } - + public void addColumnSelectionInterval(int index0, int index1) { if ((index0 < 0 || index0 > (getColumnCount()-1) @@ -3127,21 +3428,49 @@ public class JTable getSelectionModel().removeSelectionInterval(index0, index1); } + /** + * Checks if the given column is selected. + * + * @param column the column + * + * @return true if the column is selected (as reported by the selection + * model, associated with the column model), false otherwise. + */ public boolean isColumnSelected(int column) { return getColumnModel().getSelectionModel().isSelectedIndex(column); } - + + /** + * Checks if the given row is selected. + * + * @param row the row + * + * @return true if the row is selected (as reported by the selection model), + * false otherwise. + */ public boolean isRowSelected(int row) { return getSelectionModel().isSelectedIndex(row); } - + + /** + * Checks if the given cell is selected. The cell is selected if both + * the cell row and the cell column are selected. + * + * @param row the cell row + * @param column the cell column + * + * @return true if the cell is selected, false otherwise + */ public boolean isCellSelected(int row, int column) { return isRowSelected(row) && isColumnSelected(column); } + /** + * Select all table. + */ public void selectAll() { // rowLead and colLead store the current lead selection indices @@ -3156,22 +3485,51 @@ public class JTable addColumnSelectionInterval(colLead,colLead); addRowSelectionInterval(rowLead, rowLead); } - + + /** + * Get the cell value at the given position. + * + * @param row the row to get the value + * @param column the actual column number (not the model index) + * to get the value. + * + * @return the cell value, as returned by model. + */ public Object getValueAt(int row, int column) { return dataModel.getValueAt(row, convertColumnIndexToModel(column)); } - + + /** + * Set value for the cell at the given position. If the cell is not + * editable, this method returns without action. The modified cell is + * repainted. + * + * @param value the value to set + * @param row the row of the cell being modified + * @param column the column of the cell being modified + */ public void setValueAt(Object value, int row, int column) { if (!isCellEditable(row, column)) return; - - if (value instanceof Component) - add((Component)value); dataModel.setValueAt(value, row, convertColumnIndexToModel(column)); + + repaint(getCellRect(row, column, true)); } - + + /** + * Get table column with the given identified. + * + * @param identifier the column identifier + * + * @return the table column with this identifier + * + * @throws IllegalArgumentException if <code>identifier</code> is + * <code>null</code> or there is no column with that identifier. + * + * @see TableColumn#setIdentifier(Object) + */ public TableColumn getColumn(Object identifier) { return columnModel.getColumn(columnModel.getColumnIndex(identifier)); @@ -3184,7 +3542,7 @@ public class JTable * @param row the row index. * @param column the column index. * - * @return A boolean. + * @return true if the cell is editable, false otherwise. */ public boolean isCellEditable(int row, int column) { @@ -3273,19 +3631,50 @@ public class JTable */ public boolean editCellAt (int row, int column) { - oldCellValue = getValueAt(row, column); + // Complete the previous editing session, if still active. + if (isEditing()) + editingStopped(new ChangeEvent("editingStopped")); + + editingRow = row; + editingColumn = column; + setCellEditor(getCellEditor(row, column)); editorComp = prepareEditor(cellEditor, row, column); - cellEditor.addCellEditorListener(this); - rowBeingEdited = row; - columnBeingEdited = column; - setValueAt(editorComp, row, column); - ((JTextField)editorComp).requestFocusInWindow(false); - editorTimer.start(); + + // Remove the previous editor components, if present. Only one + // editor component at time is allowed in the table. + removeAll(); + add(editorComp); + moveToCellBeingEdited(editorComp); + scrollRectToVisible(editorComp.getBounds()); + editorComp.requestFocusInWindow(); return true; } /** + * Move the given component under the cell being edited. + * The table must be in the editing mode. + * + * @param component the component to move. + */ + private void moveToCellBeingEdited(Component component) + { + Rectangle r = getCellRect(editingRow, editingColumn, true); + // Place the text field so that it would not touch the table + // border. + + // TODO Figure out while 5 and which constant should here be. + int xOffset = 5; + r.x+=xOffset; + r.y++; + r.width -=xOffset; + r.height --; + + // Clone rectangle as getCellRect returns the cached value. + component.setBounds(new Rectangle(r)); + } + + /** * Programmatically starts editing the specified cell. * * @param row the row of the cell to edit. @@ -3343,7 +3732,7 @@ public class JTable // TODO: Implement functionality of this property (in UI impl). surrendersFocusOnKeystroke = value; } - + /** * Returns whether cell editors of this table should receive keyboard focus * when the editor is activated by a keystroke. The default setting is diff --git a/libjava/classpath/javax/swing/JTextField.java b/libjava/classpath/javax/swing/JTextField.java index c4903106131..01c5c06a350 100644 --- a/libjava/classpath/javax/swing/JTextField.java +++ b/libjava/classpath/javax/swing/JTextField.java @@ -41,6 +41,7 @@ package javax.swing; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; +import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; @@ -352,7 +353,10 @@ public class JTextField extends JTextComponent Dimension size = super.getPreferredSize(); if (columns != 0) - size.width = columns * getColumnWidth(); + { + Insets i = getInsets(); + size.width = columns * getColumnWidth() + i.left + i.right; + } return size; } @@ -526,4 +530,18 @@ public class JTextField extends JTextComponent // javax.swing.text.FieldView. return horizontalVisibility; } + + /** + * Returns <code>true</code>, unless this is embedded in a + * <code>JViewport</code> in which case the viewport takes responsibility of + * validating. + * + * @return <code>true</code>, unless this is embedded in a + * <code>JViewport</code> in which case the viewport takes + * responsibility of validating + */ + public boolean isValidateRoot() + { + return ! (getParent() instanceof JViewport); + } } diff --git a/libjava/classpath/javax/swing/JTextPane.java b/libjava/classpath/javax/swing/JTextPane.java index 7c95d7682c5..c0a5f80cfc8 100644 --- a/libjava/classpath/javax/swing/JTextPane.java +++ b/libjava/classpath/javax/swing/JTextPane.java @@ -327,9 +327,11 @@ public class JTextPane if (start == dot && end == dot) // There is no selection, update insertAttributes instead { - MutableAttributeSet inputAttributes = - getStyledEditorKit().getInputAttributes(); - inputAttributes.addAttributes(attribute); + MutableAttributeSet inputAttributes = + getStyledEditorKit().getInputAttributes(); + if (replace) + inputAttributes.removeAttributes(inputAttributes); + inputAttributes.addAttributes(attribute); } else getStyledDocument().setCharacterAttributes(start, end - start, attribute, diff --git a/libjava/classpath/javax/swing/JTree.java b/libjava/classpath/javax/swing/JTree.java index cfcb2291b2c..7876eeb6aaa 100644 --- a/libjava/classpath/javax/swing/JTree.java +++ b/libjava/classpath/javax/swing/JTree.java @@ -1482,6 +1482,9 @@ public class JTree extends JComponent implements Scrollable, Accessible setModel(model); setSelectionModel(new EmptySelectionModel()); selectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + + // The root node appears expanded by default. + nodeStates.put(new TreePath(model.getRoot()), EXPANDED); } /** @@ -2497,8 +2500,9 @@ public class JTree extends JComponent implements Scrollable, Accessible { TreeUI ui = getUI(); - if (ui != null) - return ui.stopEditing(this); + if (isEditing()) + if (ui != null) + return ui.stopEditing(this); return false; } @@ -2506,9 +2510,10 @@ public class JTree extends JComponent implements Scrollable, Accessible public void cancelEditing() { TreeUI ui = getUI(); - - if (ui != null) - ui.cancelEditing(this); + + if (isEditing()) + if (ui != null) + ui.cancelEditing(this); } public void startEditingAtPath(TreePath path) @@ -2738,7 +2743,7 @@ public class JTree extends JComponent implements Scrollable, Accessible * * @return a String representation of this JTree */ - public String paramString() + protected String paramString() { // TODO: this is completely legal, but it would possibly be nice // to return some more content, like the tree structure, some properties diff --git a/libjava/classpath/javax/swing/JViewport.java b/libjava/classpath/javax/swing/JViewport.java index debb5742e2c..2b5d1cd5a2f 100644 --- a/libjava/classpath/javax/swing/JViewport.java +++ b/libjava/classpath/javax/swing/JViewport.java @@ -48,6 +48,7 @@ import java.awt.Insets; import java.awt.LayoutManager; import java.awt.Point; import java.awt.Rectangle; +import java.awt.Shape; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.io.Serializable; @@ -113,7 +114,7 @@ public class JViewport extends JComponent implements Accessible /** * Creates a new instance of <code>AccessibleJViewport</code>. */ - public AccessibleJViewport() + protected AccessibleJViewport() { // Nothing to do here. } @@ -252,6 +253,13 @@ public class JViewport extends JComponent implements Accessible boolean sizeChanged = true; /** + * Indicates if this JViewport is the paint root or not. If it is not, then + * we may not assume that the offscreen buffer still has the right content + * because parent components may have cleared the background already. + */ + private boolean isPaintRoot = false; + + /** * Initializes the default setting for the scrollMode property. */ static @@ -635,11 +643,19 @@ public class JViewport extends JComponent implements Accessible */ public void repaint(long tm, int x, int y, int w, int h) { - Component parent = getParent(); - if (parent != null) - { - parent.repaint(tm, x + getX(), y + getY(), w, h); - } +// Component parent = getParent(); +// if (parent != null) +// parent.repaint(tm, x + getX(), y + getY(), w, h); +// else +// super.repaint(tm, x, y, w, h); + + // The specs suggest to implement something like the above. This however + // breaks blit painting, because the parent (most likely a JScrollPane) + // clears the background of the offscreen area of the JViewport, thus + // destroying the pieces that we want to clip. So we simply call super here + // instead. + super.repaint(tm, x, y, w, h); + } protected void addImpl(Component comp, Object constraints, int index) @@ -710,9 +726,11 @@ public class JViewport extends JComponent implements Accessible protected boolean computeBlit(int dx, int dy, Point blitFrom, Point blitTo, Dimension blitSize, Rectangle blitPaint) { - if ((dx != 0 && dy != 0) || damaged) + if ((dx != 0 && dy != 0) || (dy == 0 && dy == 0) || damaged) // We cannot blit if the viewport is scrolled in both directions at - // once. + // once. Also, we do not want to blit if the viewport is not scrolled at + // all, because that probably means the view component repaints itself + // and the buffer needs updating. return false; Rectangle portBounds = SwingUtilities.calculateInnerArea(this, getBounds()); @@ -791,6 +809,8 @@ public class JViewport extends JComponent implements Accessible Point pos = getViewPosition(); Component view = getView(); + Shape oldClip = g.getClip(); + g.clipRect(0, 0, getWidth(), getHeight()); boolean translated = false; try { @@ -802,6 +822,7 @@ public class JViewport extends JComponent implements Accessible { if (translated) g.translate (pos.x, pos.y); + g.setClip(oldClip); } } @@ -854,6 +875,11 @@ public class JViewport extends JComponent implements Accessible // everything. else { + // If the image has not been scrolled at all, only the changed + // clip must be updated in the buffer. + if (dx==0 && dy==0) + g2.setClip(g.getClip()); + paintSimple(g2); } g2.dispose(); @@ -877,23 +903,49 @@ public class JViewport extends JComponent implements Accessible */ void paintBlit(Graphics g) { - // We cannot perform blitted painting as it is described in Sun's API docs. - // There it is suggested that this painting method should blit directly - // on the parent window's surface. This is not possible because when using - // Swing's double buffering (at least our implementation), it would - // immediatly be painted when the buffer is painted on the screen. For this - // to work we would need a kind of hole in the buffer image. And honestly - // I find this method not very elegant. - // The alternative, blitting directly on the buffer image, is also not - // possible because the buffer image gets cleared everytime when an opaque - // parent component is drawn on it. - - // What we do instead is falling back to the backing store approach which - // is in fact a mixed blitting/backing store approach where the blitting - // is performed on the backing store image and this is then drawn to the - // graphics context. This is very robust and works independent of the - // painting mechanism that is used by Swing. And it should have comparable - // performance characteristics as the blitting method. - paintBackingStore(g); + // First we move the part that remains visible after scrolling, then + // we only need to paint the bit that becomes newly visible. + Point viewPosition = getViewPosition(); + int dx = viewPosition.x - lastPaintPosition.x; + int dy = viewPosition.y - lastPaintPosition.y; + boolean canBlit = computeBlit(dx, dy, cachedBlitFrom, cachedBlitTo, + cachedBlitSize, cachedBlitPaint); + if (canBlit && isPaintRoot) + { + // Copy the part that remains visible during scrolling. + g.copyArea(cachedBlitFrom.x, cachedBlitFrom.y, + cachedBlitSize.width, cachedBlitSize.height, + cachedBlitTo.x - cachedBlitFrom.x, + cachedBlitTo.y - cachedBlitFrom.y); + // Now paint the part that becomes newly visible. + Shape oldClip = g.getClip(); + g.clipRect(cachedBlitPaint.x, cachedBlitPaint.y, + cachedBlitPaint.width, cachedBlitPaint.height); + try + { + paintSimple(g); + } + finally + { + g.setClip(oldClip); + } + } + // If blitting is not possible for some reason, fall back to repainting + // everything. + else + paintSimple(g); + lastPaintPosition.setLocation(getViewPosition()); + } + + /** + * Overridden from JComponent to set the {@link #isPaintRoot} flag. + * + * @param r the rectangle to paint + */ + void paintImmediately2(Rectangle r) + { + isPaintRoot = true; + super.paintImmediately2(r); + isPaintRoot = false; } } diff --git a/libjava/classpath/javax/swing/JWindow.java b/libjava/classpath/javax/swing/JWindow.java index cc0ac7fd95a..19d830ed1f7 100644 --- a/libjava/classpath/javax/swing/JWindow.java +++ b/libjava/classpath/javax/swing/JWindow.java @@ -68,7 +68,7 @@ public class JWindow extends Window implements Accessible, RootPaneContainer /** * Creates a new instance of <code>AccessibleJWindow</code>. */ - public AccessibleJWindow() + protected AccessibleJWindow() { super(); // Nothing to do here. @@ -86,33 +86,73 @@ public class JWindow extends Window implements Accessible, RootPaneContainer protected AccessibleContext accessibleContext; + /** + * Creates a new <code>JWindow</code> that has a shared invisible owner frame + * as its parent. + */ public JWindow() { - super(SwingUtilities.getOwnerFrame()); + super(SwingUtilities.getOwnerFrame(null)); windowInit(); } + /** + * Creates a new <code>JWindow</code> that uses the specified graphics + * environment. This can be used to open a window on a different screen for + * example. + * + * @param gc the graphics environment to use + */ public JWindow(GraphicsConfiguration gc) { - super(SwingUtilities.getOwnerFrame(), gc); + super(SwingUtilities.getOwnerFrame(null), gc); windowInit(); } - + + /** + * Creates a new <code>JWindow</code> that has the specified + * <code>owner</code> frame. If <code>owner</code> is <code>null</code>, then + * an invisible shared owner frame is installed as owner frame. + * + * @param owner the owner frame of this window; if <code>null</code> a shared + * invisible owner frame is used + */ public JWindow(Frame owner) { - super(owner); + super(SwingUtilities.getOwnerFrame(owner)); windowInit(); } + /** + * Creates a new <code>JWindow</code> that has the specified + * <code>owner</code> window. If <code>owner</code> is <code>null</code>, + * then an invisible shared owner frame is installed as owner frame. + * + * @param owner the owner window of this window; if <code>null</code> a + * shared invisible owner frame is used + */ public JWindow(Window owner) { - super(owner); + super(SwingUtilities.getOwnerFrame(owner)); windowInit(); } + /** + * Creates a new <code>JWindow</code> for the given graphics configuration + * and that has the specified <code>owner</code> window. If + * <code>owner</code> is <code>null</code>, then an invisible shared owner + * frame is installed as owner frame. + * + * The <code>gc</code> parameter can be used to open the window on a + * different screen for example. + * + * @param owner the owner window of this window; if <code>null</code> a + * shared invisible owner frame is used + * @param gc the graphics configuration to use + */ public JWindow(Window owner, GraphicsConfiguration gc) { - super(owner, gc); + super(SwingUtilities.getOwnerFrame(owner), gc); windowInit(); } diff --git a/libjava/classpath/javax/swing/Popup.java b/libjava/classpath/javax/swing/Popup.java index 203ee3c9b0c..c3de69e05ab 100644 --- a/libjava/classpath/javax/swing/Popup.java +++ b/libjava/classpath/javax/swing/Popup.java @@ -161,7 +161,7 @@ public class Popup super(owner, contents, x, y); this.contents = contents; - window = new JWindow(); + window = new JWindow(SwingUtilities.getWindowAncestor(owner)); window.getContentPane().add(contents); window.setLocation(x, y); window.setFocusableWindowState(false); diff --git a/libjava/classpath/javax/swing/PopupFactory.java b/libjava/classpath/javax/swing/PopupFactory.java index 7bb2529cd54..b326205999c 100644 --- a/libjava/classpath/javax/swing/PopupFactory.java +++ b/libjava/classpath/javax/swing/PopupFactory.java @@ -152,13 +152,18 @@ public class PopupFactory // If we have a root pane and the contents fits within the root pane and // lightweight popups are enabled, than we can use a lightweight popup. JRootPane root = SwingUtilities.getRootPane(owner); - Point rootLoc = root.getLocationOnScreen(); - Dimension contentsSize = contents.getSize(); - Dimension rootSize = root.getSize(); - if (x >= rootLoc.x && y > rootLoc.y - && (x - rootLoc.x) + contentsSize.width < rootSize.width - && (y - rootLoc.y) + contentsSize.height < rootSize.height) - popup = new Popup.LightweightPopup(owner, contents, x, y); + if (root != null) + { + Point rootLoc = root.getLocationOnScreen(); + Dimension contentsSize = contents.getSize(); + Dimension rootSize = root.getSize(); + if (x >= rootLoc.x && y > rootLoc.y + && (x - rootLoc.x) + contentsSize.width < rootSize.width + && (y - rootLoc.y) + contentsSize.height < rootSize.height) + popup = new Popup.LightweightPopup(owner, contents, x, y); + else + popup = new Popup.JWindowPopup(owner, contents, x, y); + } else popup = new Popup.JWindowPopup(owner, contents, x, y); return popup; diff --git a/libjava/classpath/javax/swing/RepaintManager.java b/libjava/classpath/javax/swing/RepaintManager.java index 0be81053dc5..ed0500992c5 100644 --- a/libjava/classpath/javax/swing/RepaintManager.java +++ b/libjava/classpath/javax/swing/RepaintManager.java @@ -40,14 +40,18 @@ package javax.swing; import java.awt.Component; import java.awt.Dimension; +import java.awt.Graphics; import java.awt.Image; import java.awt.Rectangle; +import java.awt.Window; import java.awt.image.VolatileImage; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; +import java.util.Map; +import java.util.Set; import java.util.WeakHashMap; /** @@ -62,6 +66,7 @@ import java.util.WeakHashMap; * href="http://java.sun.com/products/jfc/tsc/articles/painting/index.html">this * document</a> for more details.</p> * + * @author Roman Kennke (kennke@aicas.com) * @author Graydon Hoare (graydon@redhat.com) */ public class RepaintManager @@ -69,8 +74,13 @@ public class RepaintManager /** * The current repaint managers, indexed by their ThreadGroups. */ - static WeakHashMap currentRepaintManagers; - + private static WeakHashMap currentRepaintManagers; + + /** + * A rectangle object to be reused in damaged regions calculation. + */ + private static Rectangle rectCache = new Rectangle(); + /** * <p>A helper class which is placed into the system event queue at * various times in order to facilitate repainting and layout. There is @@ -84,7 +94,7 @@ public class RepaintManager * swing paint thread, which revalidates all invalid components and * repaints any damage in the swing scene.</p> */ - protected class RepaintWorker + private class RepaintWorker implements Runnable { @@ -107,12 +117,18 @@ public class RepaintManager public void run() { - ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); - RepaintManager rm = - (RepaintManager) currentRepaintManagers.get(threadGroup); - setLive(false); - rm.validateInvalidComponents(); - rm.paintDirtyRegions(); + try + { + ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); + RepaintManager rm = + (RepaintManager) currentRepaintManagers.get(threadGroup); + rm.validateInvalidComponents(); + rm.paintDirtyRegions(); + } + finally + { + setLive(false); + } } } @@ -135,41 +151,23 @@ public class RepaintManager * @param o1 the first component * @param o2 the second component * - * @return a negative integer, if <code>o1</code> is higher in the - * hierarchy than <code>o2</code>, zero, if both are at the same - * level and a positive integer, if <code>o1</code> is deeper in - * the hierarchy than <code>o2</code> + * @return a negative integer, if <code>o1</code> is bigger in than + * <code>o2</code>, zero, if both are at the same size and a + * positive integer, if <code>o1</code> is smaller than + * <code>o2</code> */ public int compare(Object o1, Object o2) { if (o1 instanceof JComponent && o2 instanceof JComponent) { JComponent c1 = (JComponent) o1; + Rectangle d1 = (Rectangle) dirtyComponents.get(c1); JComponent c2 = (JComponent) o2; - return getDepth(c1) - getDepth(c2); - } - else - throw new ClassCastException("This comparator can only be used with " - + "JComponents"); - } - - /** - * Computes the depth for a given JComponent. - * - * @param c the component to compute the depth for - * - * @return the depth of the component - */ - private int getDepth(JComponent c) - { - Component comp = c; - int depth = 0; - while (comp != null) - { - comp = comp.getParent(); - depth++; + Rectangle d2 = (Rectangle) dirtyComponents.get(c2); + return d2.width * d2.height - d1.width * d1.height; } - return depth; + throw new ClassCastException("This comparator can only be used with " + + "JComponents"); } } @@ -179,6 +177,9 @@ public class RepaintManager * to exactly one rectangle. When more regions are marked as dirty on a * component, they are union'ed with the existing rectangle. * + * This is package private to avoid a synthetic accessor method in inner + * class. + * * @see #addDirtyRegion * @see #getDirtyRegion * @see #isCompletelyDirty @@ -187,18 +188,10 @@ public class RepaintManager */ HashMap dirtyComponents; - HashMap workDirtyComponents; - - /** - * Stores the order in which the components get repainted. - */ - ArrayList repaintOrder; - ArrayList workRepaintOrder; - /** * The comparator used for ordered inserting into the repaintOrder list. */ - Comparator comparator; + private transient Comparator comparator; /** * A single, shared instance of the helper class. Any methods which mark @@ -209,7 +202,7 @@ public class RepaintManager * @see #addDirtyRegion * @see #addInvalidComponent */ - RepaintWorker repaintWorker; + private RepaintWorker repaintWorker; /** * The set of components which need revalidation, in the "layout" sense. @@ -221,8 +214,7 @@ public class RepaintManager * @see #removeInvalidComponent * @see #validateInvalidComponents */ - ArrayList invalidComponents; - ArrayList workInvalidComponents; + private ArrayList invalidComponents; /** * Whether or not double buffering is enabled on this repaint @@ -232,17 +224,27 @@ public class RepaintManager * @see #isDoubleBufferingEnabled * @see #setDoubleBufferingEnabled */ - boolean doubleBufferingEnabled; + private boolean doubleBufferingEnabled; - /** - * The current offscreen buffer. This is reused for all requests for - * offscreen drawing buffers. It grows as necessary, up to {@link - * #doubleBufferMaximumSize}, but there is only one shared instance. - * - * @see #getOffscreenBuffer - * @see #doubleBufferMaximumSize + /** + * The offscreen buffers. This map holds one offscreen buffer per + * Window/Applet and releases them as soon as the Window/Applet gets garbage + * collected. */ - Image doubleBuffer; + private WeakHashMap offscreenBuffers; + + /** + * Indicates if the RepaintManager is currently repainting an area. + */ + private boolean repaintUnderway; + + /** + * This holds buffer commit requests when the RepaintManager is working. + * This maps Component objects (the top level components) to Rectangle + * objects (the area of the corresponding buffer that must be blitted on + * the component). + */ + private HashMap commitRequests; /** * The maximum width and height to allocate as a double buffer. Requests @@ -252,7 +254,7 @@ public class RepaintManager * @see #getDoubleBufferMaximumSize * @see #setDoubleBufferMaximumSize */ - Dimension doubleBufferMaximumSize; + private Dimension doubleBufferMaximumSize; /** @@ -261,14 +263,13 @@ public class RepaintManager public RepaintManager() { dirtyComponents = new HashMap(); - workDirtyComponents = new HashMap(); - repaintOrder = new ArrayList(); - workRepaintOrder = new ArrayList(); invalidComponents = new ArrayList(); - workInvalidComponents = new ArrayList(); repaintWorker = new RepaintWorker(); doubleBufferMaximumSize = new Dimension(2000,2000); doubleBufferingEnabled = true; + offscreenBuffers = new WeakHashMap(); + repaintUnderway = false; + commitRequests = new HashMap(); } /** @@ -346,9 +347,9 @@ public class RepaintManager * * @see #removeInvalidComponent */ - public synchronized void addInvalidComponent(JComponent component) + public void addInvalidComponent(JComponent component) { - Component ancestor = component.getParent(); + Component ancestor = component; while (ancestor != null && (! (ancestor instanceof JComponent) @@ -363,8 +364,11 @@ public class RepaintManager if (invalidComponents.contains(component)) return; - invalidComponents.add(component); - + synchronized (invalidComponents) + { + invalidComponents.add(component); + } + if (! repaintWorker.isLive()) { repaintWorker.setLive(true); @@ -379,9 +383,12 @@ public class RepaintManager * * @see #addInvalidComponent */ - public synchronized void removeInvalidComponent(JComponent component) + public void removeInvalidComponent(JComponent component) { - invalidComponents.remove(component); + synchronized (invalidComponents) + { + invalidComponents.remove(component); + } } /** @@ -402,41 +409,40 @@ public class RepaintManager * @see #markCompletelyClean * @see #markCompletelyDirty */ - public synchronized void addDirtyRegion(JComponent component, int x, int y, - int w, int h) + public void addDirtyRegion(JComponent component, int x, int y, + int w, int h) { - if (w == 0 || h == 0 || !component.isShowing()) + if (w <= 0 || h <= 0 || !component.isShowing()) return; - Rectangle r = new Rectangle(x, y, w, h); - if (dirtyComponents.containsKey(component)) - r = r.union((Rectangle)dirtyComponents.get(component)); - else - insertInRepaintOrder(component); - dirtyComponents.put(component, r); - if (! repaintWorker.isLive()) + + component.computeVisibleRect(rectCache); + SwingUtilities.computeIntersection(x, y, w, h, rectCache); + + if (! rectCache.isEmpty()) { - repaintWorker.setLive(true); - SwingUtilities.invokeLater(repaintWorker); + if (dirtyComponents.containsKey(component)) + { + SwingUtilities.computeUnion(rectCache.x, rectCache.y, + rectCache.width, rectCache.height, + (Rectangle) dirtyComponents.get(component)); + } + else + { + synchronized (dirtyComponents) + { + dirtyComponents.put(component, rectCache.getBounds()); + } + } + + if (! repaintWorker.isLive()) + { + repaintWorker.setLive(true); + SwingUtilities.invokeLater(repaintWorker); + } } } /** - * Inserts a component into the repaintOrder list in an ordered fashion, - * using a binary search. - * - * @param c the component to be inserted - */ - private void insertInRepaintOrder(JComponent c) - { - if (comparator == null) - comparator = new ComponentComparator(); - int insertIndex = Collections.binarySearch(repaintOrder, c, comparator); - if (insertIndex < 0) - insertIndex = -(insertIndex + 1); - repaintOrder.add(insertIndex, c); - } - - /** * Get the dirty region associated with a component, or <code>null</code> * if the component has no dirty region. * @@ -489,7 +495,7 @@ public class RepaintManager */ public void markCompletelyClean(JComponent component) { - synchronized (this) + synchronized (dirtyComponents) { dirtyComponents.remove(component); } @@ -523,63 +529,58 @@ public class RepaintManager */ public void validateInvalidComponents() { - // In order to keep the blocking of application threads minimal, we switch - // the invalidComponents field with the workInvalidComponents field and - // work with the workInvalidComponents field. - synchronized(this) - { - ArrayList swap = invalidComponents; - invalidComponents = workInvalidComponents; - workInvalidComponents = swap; - } - for (Iterator i = workInvalidComponents.iterator(); i.hasNext(); ) + // We don't use an iterator here because that would fail when there are + // components invalidated during the validation of others, which happens + // quite frequently. Instead we synchronize the access a little more. + while (invalidComponents.size() > 0) { - Component comp = (Component) i.next(); - // Find validate root. - while ((!(comp instanceof JComponent) - || !((JComponent) comp).isValidateRoot()) - && comp.getParent() != null) - comp = comp.getParent(); - - // Validate the validate root. + Component comp; + synchronized (invalidComponents) + { + comp = (Component) invalidComponents.remove(0); + } + // Validate the validate component. if (! (comp.isVisible() && comp.isShowing())) continue; comp.validate(); } - workInvalidComponents.clear(); } /** * Repaint all regions of all components which have been marked dirty in * the {@link #dirtyComponents} table. */ - public synchronized void paintDirtyRegions() + public void paintDirtyRegions() { - // In order to keep the blocking of application threads minimal, we switch - // the dirtyComponents field with the workdirtyComponents field and the - // repaintOrder field with the workRepaintOrder field and work with the - // work* fields. - synchronized(this) - { - ArrayList swap = workRepaintOrder; - workRepaintOrder = repaintOrder; - repaintOrder = swap; - HashMap swap2 = workDirtyComponents; - workDirtyComponents = dirtyComponents; - dirtyComponents = swap2; - } - for (Iterator i = workRepaintOrder.iterator(); i.hasNext();) + // Short cicuit if there is nothing to paint. + if (dirtyComponents.size() == 0) + return; + + synchronized (dirtyComponents) { - JComponent comp = (JComponent) i.next(); - // If a component is marked completely clean in the meantime, then skip - // it. - Rectangle damaged = (Rectangle) workDirtyComponents.get(comp); - if (damaged == null || damaged.isEmpty()) - continue; - comp.paintImmediately(damaged); + // We sort the components by their size here. This way we have a good + // chance that painting the bigger components also paints the smaller + // components and we don't need to paint them twice. + ArrayList repaintOrder = new ArrayList(dirtyComponents.size()); + repaintOrder.addAll(dirtyComponents.keySet()); + if (comparator == null) + comparator = new ComponentComparator(); + Collections.sort(repaintOrder, comparator); + repaintUnderway = true; + for (Iterator i = repaintOrder.iterator(); i.hasNext();) + { + JComponent comp = (JComponent) i.next(); + // If a component is marked completely clean in the meantime, then skip + // it. + Rectangle damaged = (Rectangle) dirtyComponents.get(comp); + if (damaged == null || damaged.isEmpty()) + continue; + comp.paintImmediately(damaged); + dirtyComponents.remove(comp); + } + repaintUnderway = false; + commitRemainingBuffers(); } - workRepaintOrder.clear(); - workDirtyComponents.clear(); } /** @@ -592,21 +593,114 @@ public class RepaintManager * @param proposedHeight The proposed height of the offscreen buffer * * @return A shared offscreen buffer for painting - * - * @see #doubleBuffer */ public Image getOffscreenBuffer(Component component, int proposedWidth, int proposedHeight) { - if (doubleBuffer == null - || (((doubleBuffer.getWidth(null) < proposedWidth) - || (doubleBuffer.getHeight(null) < proposedHeight)) - && (proposedWidth < doubleBufferMaximumSize.width) - && (proposedHeight < doubleBufferMaximumSize.height))) + Component root = SwingUtilities.getRoot(component); + Image buffer = (Image) offscreenBuffers.get(root); + if (buffer == null + || buffer.getWidth(null) < proposedWidth + || buffer.getHeight(null) < proposedHeight) + { + int width = Math.max(proposedWidth, root.getWidth()); + width = Math.min(doubleBufferMaximumSize.width, width); + int height = Math.max(proposedHeight, root.getHeight()); + height = Math.min(doubleBufferMaximumSize.height, height); + buffer = component.createImage(width, height); + offscreenBuffers.put(root, buffer); + } + return buffer; + } + + /** + * Blits the back buffer of the specified root component to the screen. If + * the RepaintManager is currently working on a paint request, the commit + * requests are queued up and committed at once when the paint request is + * 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 + */ + void commitBuffer(Component root, Rectangle area) + { + // 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) + { + 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, dx1, dy1, dx2, dy2, + dx1, dy1, dx2, dy2, root); + g.dispose(); + } + // 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, + (Rectangle) commitRequests.get(root)); + else + commitRequests.put(root, area); + } + } + } + + /** + * Commits the queued up back buffers to screen all at once. + */ + private void commitRemainingBuffers() + { + // We synchronize on dirtyComponents here because that is what + // paintDirtyRegions also synchronizes on while painting. + synchronized (dirtyComponents) { - doubleBuffer = component.createImage(proposedWidth, proposedHeight); + Set entrySet = commitRequests.entrySet(); + Iterator i = entrySet.iterator(); + while (i.hasNext()) + { + Map.Entry entry = (Map.Entry) i.next(); + Component root = (Component) entry.getKey(); + Rectangle area = (Rectangle) entry.getValue(); + commitBuffer(root, area); + i.remove(); + } } - return doubleBuffer; } /** diff --git a/libjava/classpath/javax/swing/ScrollPaneLayout.java b/libjava/classpath/javax/swing/ScrollPaneLayout.java index edf1f1f4292..b00b5c4e7ae 100644 --- a/libjava/classpath/javax/swing/ScrollPaneLayout.java +++ b/libjava/classpath/javax/swing/ScrollPaneLayout.java @@ -285,20 +285,20 @@ public class ScrollPaneLayout // Sun's implementation simply throws a ClassCastException if // parent is no JScrollPane, so do we. JScrollPane sc = (JScrollPane) parent; - Dimension viewportSize = viewport.getMinimumSize(); - int width = viewportSize.width; - int height = viewportSize.height; - if (hsb != null && hsb.isVisible()) - height += hsb.getMinimumSize().height; - if (vsb != null && vsb.isVisible()) - width += vsb.getMinimumSize().width; - if (rowHead != null && rowHead.isVisible()) - width += rowHead.getMinimumSize().width; - if (colHead != null && colHead.isVisible()) - height += colHead.getMinimumSize().height; Insets i = sc.getInsets(); - return new Dimension(width + i.left + i.right, - height + i.top + i.bottom); + Dimension viewportMinSize = sc.getViewport().getMinimumSize(); + + int width = i.left + i.right + viewportMinSize.width; + if (sc.getVerticalScrollBarPolicy() + != JScrollPane.VERTICAL_SCROLLBAR_NEVER) + width += sc.getVerticalScrollBar().getMinimumSize().width; + + int height = i.top + i.bottom + viewportMinSize.height; + if (sc.getHorizontalScrollBarPolicy() + != JScrollPane.HORIZONTAL_SCROLLBAR_NEVER) + height += sc.getHorizontalScrollBar().getMinimumSize().height; + + return new Dimension(width, height); } /** diff --git a/libjava/classpath/javax/swing/SpinnerDateModel.java b/libjava/classpath/javax/swing/SpinnerDateModel.java index c0de7d55c8e..e0ccab776fb 100644 --- a/libjava/classpath/javax/swing/SpinnerDateModel.java +++ b/libjava/classpath/javax/swing/SpinnerDateModel.java @@ -1,5 +1,5 @@ /* SpinnerDateModel.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. @@ -42,21 +42,37 @@ import java.io.Serializable; import java.util.Calendar; import java.util.Date; +import javax.swing.event.ChangeEvent; + /** - * SpinnerDateModel - * - * Implements a SpinnerModel for dates, rotating a calendar field such as - * month, year, day, week, hour, minute. + * A date model used by the {@link JSpinner} component. This implements a + * spinner model for dates, rotating a calendar field such as month, year, + * day, week, hour, minute. * * @author Sven de Marothy - * @version 0.1 (first implementation) + * @since 1.4 */ public class SpinnerDateModel extends AbstractSpinnerModel implements Serializable { + /** The current date. */ private Calendar date; + + /** + * The start or earliest permitted date (<code>null</code> for no + * minimum). + */ private Comparable start; + + /** + * The end or latest permitted date (<code>null</code> for no + * maximum). + */ private Comparable end; + + /** + * The calendar field used to calculate the previous or next date. + */ private int calendarField; /** @@ -66,8 +82,9 @@ public class SpinnerDateModel extends AbstractSpinnerModel private static final long serialVersionUID = -4802518107105940612L; /** - * Constructs a SpinnerDateModel using the current date, - * no start or end limit, and Calendar.DAY_OF_MONTH as the calendar field. + * Constructs a <code>SpinnerDateModel</code> using the current date, + * no start or end limit, and {@link Calendar#DAY_OF_MONTH} as the calendar + * field. */ public SpinnerDateModel() { @@ -87,6 +104,12 @@ public class SpinnerDateModel extends AbstractSpinnerModel public SpinnerDateModel(Date value, Comparable start, Comparable end, int calendarField) { + if (value == null) + throw new IllegalArgumentException("Null 'value' argument."); + if (start != null && value.compareTo(start) < 0) + throw new IllegalArgumentException("Require value on or after start."); + if (end != null && value.compareTo(end) > 0) + throw new IllegalArgumentException("Require value on or before end."); date = Calendar.getInstance(); date.setTime(value); this.start = start; @@ -95,7 +118,10 @@ public class SpinnerDateModel extends AbstractSpinnerModel } /** - * Returns the value of the Calendar field to spin. + * Returns the {@link Calendar} field used to calculate the previous and + * next dates in the sequence. + * + * @return The date field code. */ public int getCalendarField() { @@ -103,8 +129,9 @@ public class SpinnerDateModel extends AbstractSpinnerModel } /** - * Returns the current date in the sequence. - * @return a <code>Date</code> object. + * Returns the current date. + * + * @return The current date. */ public Date getDate() { @@ -112,8 +139,9 @@ public class SpinnerDateModel extends AbstractSpinnerModel } /** - * Returns the starting limit of the SpinnerModel. - * @return a Date object, or <code>null</code> if there is no limit. + * Returns the start date, or <code>null</code> if there is no minimum date. + * + * @return The start date. */ public Comparable getStart() { @@ -121,8 +149,9 @@ public class SpinnerDateModel extends AbstractSpinnerModel } /** - * Returns the end limit of the SpinnerModel. - * @return a Date object, or <code>null</code> if there is no limit. + * Returns the end date, or <code>null</code> if there is no maximum date. + * + * @return The end date. */ public Comparable getEnd() { @@ -130,9 +159,10 @@ public class SpinnerDateModel extends AbstractSpinnerModel } /** - * Returns the current date in the sequence, - * this method returns the same as <code>getDate()</code>. - * @return a <code>Date</code> object. + * Returns the current date in the sequence (this method returns the same as + * {@link #getDate()}. + * + * @return The current date. */ public Object getValue() { @@ -141,8 +171,10 @@ public class SpinnerDateModel extends AbstractSpinnerModel /** * Returns the next date in the sequence, or <code>null</code> if the - * next date is equal to or past the end limit. - * @return a Date object, or <code>null</code>. + * next date is after the end date. The current date is not changed. + * + * @return The next date, or <code>null</code> if the current value is + * the latest date represented by the model. */ public Object getNextValue() { @@ -152,14 +184,17 @@ public class SpinnerDateModel extends AbstractSpinnerModel Date nextDate = nextCal.getTime(); if (end != null) if (end.compareTo(nextDate) < 0) - return null; + return null; return nextDate; } /** * Returns the previous date in the sequence, or <code>null</code> if the - * next date is equal to or past the end limit. - * @return a Date object, or <code>null</code>. + * previous date is prior to the start date. The current date is not + * changed. + * + * @return The previous date, or <code>null</code> if the current value is + * the earliest date represented by the model. */ public Object getPreviousValue() { @@ -167,16 +202,21 @@ public class SpinnerDateModel extends AbstractSpinnerModel prevCal.setTime(date.getTime()); prevCal.roll(calendarField, false); Date prevDate = prevCal.getTime(); - if (end != null) - if (end.compareTo(prevDate) > 0) - return null; + if (start != null) + if (start.compareTo(prevDate) > 0) + return null; return prevDate; } /** - * Sets the date field to change. It must be a valid Calendar field, - * excluding Calendar.ZONE_OFFSET and Calendar.DST_OFFSET. - * @param calendarField - the calendar field to set. + * Sets the date field to change when calculating the next and previous + * values. It must be a valid {@link Calendar} field, excluding + * {@link Calendar#ZONE_OFFSET} and {@link Calendar#DST_OFFSET}. + * + * @param calendarField the calendar field to set. + * + * @throws IllegalArgumentException if <code>calendarField</code> is not + * a valid code. */ public void setCalendarField(int calendarField) { @@ -187,51 +227,67 @@ public class SpinnerDateModel extends AbstractSpinnerModel if (this.calendarField != calendarField) { - this.calendarField = calendarField; - fireStateChanged(); + this.calendarField = calendarField; + fireStateChanged(); } } /** - * Sets the starting date limit for the sequence. - * - * @param start - a Date object of the limit date, - * or <code>null</code> for no limit. + * Sets the start date and, if the new date is different to the old date, + * sends a {@link ChangeEvent} to all registered listeners. A + * <code>null</code> date is interpreted as "no start date". No check + * is made to ensure that the new start date is on or before the current + * date - the caller is responsible for ensuring that this relationship + * holds. + * + * @param start the new start date (<code>null</code> permitted). */ public void setStart(Comparable start) { if (this.start != start) { - this.start = start; - fireStateChanged(); + this.start = start; + fireStateChanged(); } } /** - * Sets the end date limit for the sequence. - * - * @param end - a Date object of the limit date, - * or <code>null</code> for no limit. + * Sets the end date and, if the new date is different to the old date, + * sends a {@link ChangeEvent} to all registered listeners. A + * <code>null</code> date is interpreted as "no end date". No check + * is made to ensure that the new end date is on or after the current date - + * the caller is responsible for ensuring that this relationship holds. + * + * @param end the new end date (<code>null</code> permitted). */ public void setEnd(Comparable end) { if (this.end != end) { - this.end = end; - fireStateChanged(); + this.end = end; + fireStateChanged(); } } /** - * Sets the current date in the sequence. + * Sets the current date and, if the new value is different to the old + * value, sends a {@link ChangeEvent} to all registered listeners. * - * @param value - a Date object. + * @param value the new date (<code>null</code> not permitted, must be an + * instance of <code>Date</code>). + * + * @throws IllegalArgumentException if <code>value</code> is not an instance + * of <code>Date</code>. */ public void setValue(Object value) { if (! (value instanceof Date) || value == null) throw new IllegalArgumentException("Value not a date."); - date.setTime((Date) value); - fireStateChanged(); + + if (!date.getTime().equals(value)) + { + date.setTime((Date) value); + fireStateChanged(); + } } } diff --git a/libjava/classpath/javax/swing/SpinnerNumberModel.java b/libjava/classpath/javax/swing/SpinnerNumberModel.java index 2274c9ec038..389c536e47f 100644 --- a/libjava/classpath/javax/swing/SpinnerNumberModel.java +++ b/libjava/classpath/javax/swing/SpinnerNumberModel.java @@ -1,5 +1,5 @@ /* SpinnerNumberModel.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. @@ -39,11 +39,13 @@ package javax.swing; import java.io.Serializable; +import javax.swing.event.ChangeEvent; + /** - * SpinnerNumberModel + * A model used by the {@link JSpinner} component. * * @author Ka-Hing Cheung - * @version 1.0 + * @since 1.4 */ public class SpinnerNumberModel extends AbstractSpinnerModel implements Serializable @@ -53,16 +55,16 @@ public class SpinnerNumberModel extends AbstractSpinnerModel */ private static final long serialVersionUID = 7279176385485777821L; - /** DOCUMENT ME! */ + /** The current value. */ private Number value; - /** DOCUMENT ME! */ + /** The minimum value (or <code>null</code>). */ private Comparable minimum; - /** DOCUMENT ME! */ + /** The maximum value (or <code>null</code>). */ private Comparable maximum; - /** DOCUMENT ME! */ + /** The step size. */ private Number stepSize; /** @@ -75,14 +77,14 @@ public class SpinnerNumberModel extends AbstractSpinnerModel } /** - * Creates a <code>SpinnerNumberModel</code> with double precision + * Creates a <code>SpinnerNumberModel</code> with double precision. * * @param value the initial value * @param minimum the minimum value * @param maximum the maximum value * @param stepSize the step size - * @throws IllegalArgumentException if minimum <= value <= maximum does not - * hold + * @throws IllegalArgumentException if minimum <= value <= maximum does + * not hold. */ public SpinnerNumberModel(double value, double minimum, double maximum, double stepSize) @@ -92,14 +94,14 @@ public class SpinnerNumberModel extends AbstractSpinnerModel } /** - * Creates a <code>SpinnerNumberModel</code> with integer precision + * Creates a <code>SpinnerNumberModel</code> with integer precision. * * @param value the initial value * @param minimum the minimum value * @param maximum the maximum value * @param stepSize the step size - * @throws IllegalArgumentException if minimum <= value <= maximum does not - * hold + * @throws IllegalArgumentException if minimum <= value <= maximum does + * not hold. */ public SpinnerNumberModel(int value, int minimum, int maximum, int stepSize) { @@ -108,16 +110,19 @@ public class SpinnerNumberModel extends AbstractSpinnerModel } /** - * Creates a <code>SpinnerNumberModel</code> with <code>Number</code>s and - * <code>Comparable</code>s. + * Creates a <code>SpinnerNumberModel</code> with the given attributes. * - * @param value the initial value - * @param minimum the minimum value, if null there's no minimum - * @param maximum the maximum value, if null there's no maximum - * @param stepSize the step size + * @param value the initial value. + * @param minimum the minimum value (<code>null</code> permitted). + * @param maximum the maximum value (<code>null</code> permitted). + * @param stepSize the step size. * * @throws IllegalArgumentException if minimum <= value <= maximum * does not hold + * @throws IllegalArgumentException if <code>value</code> is + * <code>null</code>. + * @throws IllegalArgumentException if <code>stepSize</code> is + * <code>null</code>. */ public SpinnerNumberModel(Number value, Comparable minimum, Comparable maximum, Number stepSize) @@ -128,33 +133,14 @@ public class SpinnerNumberModel extends AbstractSpinnerModel throw new IllegalArgumentException("value may not be null"); if (minimum != null) { - if (minimum.compareTo(value) > 0) - throw new IllegalArgumentException("minimum is not <= value"); + if (minimum.compareTo(value) > 0) + throw new IllegalArgumentException("minimum is not <= value"); } - else - minimum = new Comparable() - { - public int compareTo(Object obj) - { - return -1; - } - }; - - if (maximum != null) { - if (maximum.compareTo(value) < 0) - throw new IllegalArgumentException("maximum is not >= value"); + if (maximum.compareTo(value) < 0) + throw new IllegalArgumentException("maximum is not >= value"); } - else - maximum = new Comparable() - { - public int compareTo(Object obj) - { - return 1; - } - }; - this.value = value; this.stepSize = stepSize; @@ -163,26 +149,31 @@ public class SpinnerNumberModel extends AbstractSpinnerModel } /** - * Sets the new value and fire a change event + * Sets the current value and, if the new value is different to the old + * value, sends a {@link ChangeEvent} to all registered listeners. * - * @param value the new value + * @param value the new value (<code>null</code> not permitted, must be an + * instance of <code>Number</code>). * - * @throws IllegalArgumentException if minimum <= value <= maximum - * does not hold + * @throws IllegalArgumentException if <code>value</code> is not an instance + * of <code>Number</code>. */ public void setValue(Object value) { if (! (value instanceof Number)) throw new IllegalArgumentException("value must be a Number"); - this.value = (Number) value; - fireStateChanged(); + if (!this.value.equals(value)) + { + this.value = (Number) value; + fireStateChanged(); + } } /** - * Gets the current value + * Returns the current value. * - * @return the current value + * @return The current value. */ public Object getValue() { @@ -190,10 +181,12 @@ public class SpinnerNumberModel extends AbstractSpinnerModel } /** - * Gets the next value without changing the current value, or null if the - * current value is maximum. + * Returns the next value, or <code>null</code> if adding the step size to + * the current value results in a value greater than the maximum value. + * The current value is not changed. * - * @return the next value + * @return The next value, or <code>null</code> if the current value is the + * maximum value represented by this model. */ public Object getNextValue() { @@ -211,15 +204,21 @@ public class SpinnerNumberModel extends AbstractSpinnerModel num = new Short((short) (value.shortValue() + stepSize.shortValue())); else num = new Byte((byte) (value.byteValue() + stepSize.byteValue())); - - return maximum.compareTo(num) >= 0 ? num : null; + + // check upper bound if set + if ((maximum != null) && maximum.compareTo(num) < 0) + num = null; + + return num; } /** - * Gets the previous value without changing the current value, or null if - * the current value is minimum. + * Returns the previous value, or <code>null</code> if subtracting the + * step size from the current value results in a value less than the minimum + * value. The current value is not changed. * - * @return the previous value + * @return The previous value, or <code>null</code> if the current value + * is the minimum value represented by this model. */ public Object getPreviousValue() { @@ -237,62 +236,110 @@ public class SpinnerNumberModel extends AbstractSpinnerModel num = new Short((short) (value.shortValue() - stepSize.shortValue())); else num = new Byte((byte) (value.byteValue() - stepSize.byteValue())); + + // check lower bound if set + if ((minimum != null) && minimum.compareTo(num) > 0) + num = null; - return minimum.compareTo(num) <= 0 ? num : null; + return num; } /** - * DOCUMENT ME! + * Returns the current value. * - * @return DOCUMENT ME! + * @return The current value. */ public Number getNumber() { return value; } + /** + * Returns the minimum value, or <code>null</code> if there is no minimum. + * + * @return The minimum value. + */ public Comparable getMinimum() { return minimum; } + /** + * Sets the minimum value and, if the new value is different to the old + * value, sends a {@link ChangeEvent} to all registered listeners. A + * <code>null</code> value is interpreted as "no minimum value". No check + * is made to ensure that the new minimum is less than or equal to the + * current value, the caller is responsible for ensuring that this + * relationship holds. + * + * @param newMinimum the new minimum value (<code>null</code> permitted). + */ public void setMinimum(Comparable newMinimum) { - if (minimum != newMinimum) + if (minimum != null ? !minimum.equals(newMinimum) : newMinimum != null) { - minimum = newMinimum; - fireStateChanged(); + minimum = newMinimum; + fireStateChanged(); } } + /** + * Returns the maximum value, or <code>null</code> if there is no maximum. + * + * @return The maximum value. + */ public Comparable getMaximum() { return maximum; } + /** + * Sets the maximum value and, if the new value is different to the old + * value, sends a {@link ChangeEvent} to all registered listeners. A + * <code>null</code> value is interpreted as "no maximum value". No check + * is made to ensure that the new maximum is greater than or equal to the + * current value, the caller is responsible for ensuring that this + * relationship holds. + * + * @param newMaximum the new maximum (<code>null</code> permitted). + */ public void setMaximum(Comparable newMaximum) { - if (maximum != newMaximum) + if (maximum != null ? !maximum.equals(newMaximum) : newMaximum != null) { - maximum = newMaximum; - fireStateChanged(); + maximum = newMaximum; + fireStateChanged(); } } + /** + * Returns the step size. + * + * @return The step size. + */ public Number getStepSize() { return stepSize; } + /** + * Sets the step size and, if the new step size is different to the old + * step size, sends a {@link ChangeEvent} to all registered listeners. + * + * @param newStepSize the new step size (<code>null</code> not permitted). + * + * @throws IllegalArgumentException if <code>newStepSize</code> is + * <code>null</code>. + */ public void setStepSize(Number newStepSize) { if (newStepSize == null) throw new IllegalArgumentException(); - if (stepSize != newStepSize) + if (!stepSize.equals(newStepSize)) { - stepSize = newStepSize; - fireStateChanged(); + stepSize = newStepSize; + fireStateChanged(); } } } diff --git a/libjava/classpath/javax/swing/Spring.java b/libjava/classpath/javax/swing/Spring.java index 8f7105d496d..b9890c7147f 100644 --- a/libjava/classpath/javax/swing/Spring.java +++ b/libjava/classpath/javax/swing/Spring.java @@ -37,6 +37,9 @@ exception statement from your version. */ package javax.swing; +import java.awt.Component; +import java.awt.Dimension; + /** * Calculates the space between component edges, that are layed out by * {@link SpringLayout}. @@ -168,6 +171,139 @@ public abstract class Spring } /** + * Return a new Spring which computes its values by scaling + * the values of another spring by a constant factor. If the + * factor is negative, the minimum and maximum values of + * the argument spring will be interchanged. + * @param spring the spring to track + * @param factor the factor by which to scale + * @return a new multiplicative Spring + * @since 1.5 + */ + public static Spring scale(final Spring spring, final float factor) + { + if (spring == null) + throw new NullPointerException("spring argument is null"); + return new Spring() + { + public int getMaximumValue() + { + return (int) ((factor < 0 ? spring.getMinimumValue() + : spring.getMaximumValue()) + * factor); + } + + public int getMinimumValue() + { + return (int) ((factor < 0 ? spring.getMaximumValue() + : spring.getMinimumValue()) + * factor); + } + + public int getPreferredValue() + { + return (int) (spring.getPreferredValue() * factor); + } + + public int getValue() + { + return (int) (spring.getValue() * factor); + } + + public void setValue(int value) + { + spring.setValue((int) (value / factor)); + } + }; + } + + /** + * Return a new Spring which takes its values from the specified + * Component. In particular, the maximum value is taken from + * the maximumSize, the minimum value is taken from the minimumSize, + * the preferred value is taken from the preferredSize, and the + * value is taken from the component's current size. These values + * change as the component changes size. + * @param component the component + * @return a new Spring which tracks the component's width + * @since 1.5 + */ + public static Spring width(final Component component) + { + return new Spring() + { + public int getMaximumValue() + { + return component.getMaximumSize().width; + } + + public int getMinimumValue() + { + return component.getMinimumSize().width; + } + + public int getPreferredValue() + { + return component.getPreferredSize().width; + } + + public int getValue() + { + return component.getSize().width; + } + + public void setValue(int value) + { + Dimension d = component.getSize(); + component.setSize(value, d.height); + } + }; + } + + /** + * Return a new Spring which takes its values from the specified + * Component. In particular, the maximum value is taken from + * the maximumSize, the minimum value is taken from the minimumSize, + * the preferred value is taken from the preferredSize, and the + * value is taken from the component's current size. These values + * change as the component changes size. + * @param component the component + * @return a new Spring which tracks the component's height + * @since 1.5 + */ + public static Spring height(final Component component) + { + return new Spring() + { + public int getMaximumValue() + { + return component.getMaximumSize().height; + } + + public int getMinimumValue() + { + return component.getMinimumSize().height; + } + + public int getPreferredValue() + { + return component.getPreferredSize().height; + } + + public int getValue() + { + return component.getSize().height; + } + + public void setValue(int value) + { + Dimension d = component.getSize(); + component.setSize(d.width, value); + } + }; + } + + /** * A simple Spring, that holds constant values for min, pref and max. * * @author Roman Kennke (roman@ontographics.com) diff --git a/libjava/classpath/javax/swing/SpringLayout.java b/libjava/classpath/javax/swing/SpringLayout.java index 592cc0e02a9..8d46a736a58 100644 --- a/libjava/classpath/javax/swing/SpringLayout.java +++ b/libjava/classpath/javax/swing/SpringLayout.java @@ -1,5 +1,5 @@ /* SpringLayout.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -150,6 +150,25 @@ public class SpringLayout implements LayoutManager2 } /** + * Create a new Constraints object which tracks the indicated + * component. The x and y positions for this Constraints object + * are constant Springs created with the component's location at + * the time this constructor is called. The width and height + * of this Constraints are Springs created using + * {@link Spring#width(Component)} and {@link Spring#height(Component)}, + * respectively. + * @param component the component to track + * @since 1.5 + */ + public Constraints(Component component) + { + this(Spring.constant(component.getX()), + Spring.constant(component.getY()), + Spring.width(component), + Spring.height(component)); + } + + /** * Returns the constraint for the edge with the <code>edgeName</code>. * This is expected to be one of * {@link #EAST}, {@link #WEST}, {@link #NORTH} or {@link #SOUTH}. @@ -343,8 +362,8 @@ public class SpringLayout implements LayoutManager2 /** * Adds a layout component and a constraint object to this layout. - * This method is usually only called by a {@java.awt.Container}s add - * Method. + * This method is usually only called by a {@link java.awt.Container}s add + * method. * * @param component the component to be added. * @param constraint the constraint to be set. @@ -357,8 +376,8 @@ public class SpringLayout implements LayoutManager2 /** * Adds a layout component and a constraint object to this layout. - * This method is usually only called by a {@java.awt.Container}s add - * Method. This method does nothing, since SpringLayout does not manage + * This method is usually only called by a {@link java.awt.Container}s add + * method. This method does nothing, since SpringLayout does not manage * String-indexed components. * * @param name the name. diff --git a/libjava/classpath/javax/swing/SwingUtilities.java b/libjava/classpath/javax/swing/SwingUtilities.java index 2d859b7448e..6762ccd804a 100644 --- a/libjava/classpath/javax/swing/SwingUtilities.java +++ b/libjava/classpath/javax/swing/SwingUtilities.java @@ -83,31 +83,6 @@ public class SwingUtilities { // Do nothing. } - - /** - * Calculates the portion of the base rectangle which is inside the - * insets. - * - * @param base The rectangle to apply the insets to - * @param insets The insets to apply to the base rectangle - * @param ret A rectangle to use for storing the return value, or - * <code>null</code> - * - * @return The calculated area inside the base rectangle and its insets, - * either stored in ret or a new Rectangle if ret is <code>null</code> - * - * @see #calculateInnerArea - */ - public static Rectangle calculateInsetArea(Rectangle base, Insets insets, - Rectangle ret) - { - if (ret == null) - ret = new Rectangle(); - ret.setBounds(base.x + insets.left, base.y + insets.top, - base.width - (insets.left + insets.right), - base.height - (insets.top + insets.bottom)); - return ret; - } /** * Calculates the portion of the component's bounds which is inside the @@ -122,13 +97,18 @@ public class SwingUtilities * * @return The calculated area inside the component and its border * insets - * - * @see #calculateInsetArea */ public static Rectangle calculateInnerArea(JComponent c, Rectangle r) { Rectangle b = getLocalBounds(c); - return calculateInsetArea(b, c.getInsets(), r); + if (r == null) + r = new Rectangle(); + Insets i = c.getInsets(); + r.x = b.x + i.left; + r.width = b.width - i.left - i.right; + r.y = b.y + i.top; + r.height = b.height - i.top - i.bottom; + return r; } /** @@ -1021,11 +1001,16 @@ public class SwingUtilities * * @return The common Frame */ - static Frame getOwnerFrame() + static Window getOwnerFrame(Window owner) { - if (ownerFrame == null) - ownerFrame = new OwnerFrame(); - return ownerFrame; + Window result = owner; + if (result == null) + { + if (ownerFrame == null) + ownerFrame = new OwnerFrame(); + result = ownerFrame; + } + return result; } /** @@ -1262,26 +1247,31 @@ public class SwingUtilities } /** - * Calculates the intersection of two rectangles. + * Calculates the intersection of two rectangles. The result is stored + * in <code>rect</code>. This is basically the same + * like {@link Rectangle#intersection(Rectangle)}, only that it does not + * create new Rectangle instances. The tradeoff is that you loose any data in + * <code>rect</code>. * * @param x upper-left x coodinate of first rectangle * @param y upper-left y coodinate of first rectangle * @param w width of first rectangle * @param h height of first rectangle * @param rect a Rectangle object of the second rectangle - * @throws NullPointerException if rect is null. + * + * @throws NullPointerException if rect is null * * @return a rectangle corresponding to the intersection of the - * two rectangles. A zero rectangle is returned if the rectangles - * do not overlap. + * two rectangles. An empty rectangle is returned if the rectangles + * do not overlap */ public static Rectangle computeIntersection(int x, int y, int w, int h, Rectangle rect) { - int x2 = (int) rect.getX(); - int y2 = (int) rect.getY(); - int w2 = (int) rect.getWidth(); - int h2 = (int) rect.getHeight(); + int x2 = (int) rect.x; + int y2 = (int) rect.y; + int w2 = (int) rect.width; + int h2 = (int) rect.height; int dx = (x > x2) ? x : x2; int dy = (y > y2) ? y : y2; @@ -1289,9 +1279,11 @@ public class SwingUtilities int dh = (y + h < y2 + h2) ? (y + h - dy) : (y2 + h2 - dy); if (dw >= 0 && dh >= 0) - return new Rectangle(dx, dy, dw, dh); + rect.setBounds(dx, dy, dw, dh); + else + rect.setBounds(0, 0, 0, 0); - return new Rectangle(0, 0, 0, 0); + return rect; } /** @@ -1308,26 +1300,31 @@ public class SwingUtilities } /** - * Calculates the union of two rectangles. + * Calculates the union of two rectangles. The result is stored in + * <code>rect</code>. This is basically the same as + * {@link Rectangle#union(Rectangle)} except that it avoids creation of new + * Rectangle objects. The tradeoff is that you loose any data in + * <code>rect</code>. * * @param x upper-left x coodinate of first rectangle * @param y upper-left y coodinate of first rectangle * @param w width of first rectangle * @param h height of first rectangle * @param rect a Rectangle object of the second rectangle - * @throws NullPointerException if rect is null. + * + * @throws NullPointerException if rect is null * * @return a rectangle corresponding to the union of the - * two rectangles. A rectangle encompassing both is returned if the - * rectangles do not overlap. + * two rectangles; a rectangle encompassing both is returned if the + * rectangles do not overlap */ public static Rectangle computeUnion(int x, int y, int w, int h, Rectangle rect) { - int x2 = (int) rect.getX(); - int y2 = (int) rect.getY(); - int w2 = (int) rect.getWidth(); - int h2 = (int) rect.getHeight(); + int x2 = (int) rect.x; + int y2 = (int) rect.y; + int w2 = (int) rect.width; + int h2 = (int) rect.height; int dx = (x < x2) ? x : x2; int dy = (y < y2) ? y : y2; @@ -1335,9 +1332,10 @@ public class SwingUtilities int dh = (y + h > y2 + h2) ? (y + h - dy) : (y2 + h2 - dy); if (dw >= 0 && dh >= 0) - return new Rectangle(dx, dy, dw, dh); - - return new Rectangle(0, 0, 0, 0); + rect.setBounds(dx, dy, dw, dh); + else + rect.setBounds(0, 0, 0, 0); + return rect; } /** diff --git a/libjava/classpath/javax/swing/Timer.java b/libjava/classpath/javax/swing/Timer.java index cf91c23e8ec..231b71d73bb 100644 --- a/libjava/classpath/javax/swing/Timer.java +++ b/libjava/classpath/javax/swing/Timer.java @@ -68,11 +68,11 @@ public class Timer public void run() { if (logTimers) - System.out.println("javax.swing.Timer -> queueEvent()"); + System.out.println("javax.swing.Timer -> queueEvent()"); queueEvent(); if (!repeats) - task = null; + task = null; } } @@ -141,8 +141,9 @@ public class Timer /** * The task that calls queueEvent(). When null this Timer is stopped. + * This is package private to avoid synthetic accessor method. */ - private Task task; + Task task; /** * This object manages a "queue" of virtual actionEvents, maintained as a diff --git a/libjava/classpath/javax/swing/ToolTipManager.java b/libjava/classpath/javax/swing/ToolTipManager.java index 289149fb603..c7de4db8330 100644 --- a/libjava/classpath/javax/swing/ToolTipManager.java +++ b/libjava/classpath/javax/swing/ToolTipManager.java @@ -40,9 +40,6 @@ package javax.swing; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.LayoutManager; -import java.awt.Panel; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; diff --git a/libjava/classpath/javax/swing/UIManager.java b/libjava/classpath/javax/swing/UIManager.java index fbf1c7c79cb..bf8739daca2 100644 --- a/libjava/classpath/javax/swing/UIManager.java +++ b/libjava/classpath/javax/swing/UIManager.java @@ -43,11 +43,11 @@ import java.awt.Dimension; import java.awt.Font; import java.awt.Insets; import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; import java.io.Serializable; import java.util.Locale; import javax.swing.border.Border; -import javax.swing.event.SwingPropertyChangeSupport; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.metal.MetalLookAndFeel; @@ -138,8 +138,8 @@ public class UIManager implements Serializable static UIDefaults userUIDefaults; /** Property change listener mechanism. */ - static SwingPropertyChangeSupport listeners - = new SwingPropertyChangeSupport(UIManager.class); + static PropertyChangeSupport listeners + = new PropertyChangeSupport(UIManager.class); static { diff --git a/libjava/classpath/javax/swing/UnsupportedLookAndFeelException.java b/libjava/classpath/javax/swing/UnsupportedLookAndFeelException.java index f99c0ac19f7..b65119a00bc 100644 --- a/libjava/classpath/javax/swing/UnsupportedLookAndFeelException.java +++ b/libjava/classpath/javax/swing/UnsupportedLookAndFeelException.java @@ -1,5 +1,5 @@ /* UnsupportedLookAndFeelException.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,11 +37,21 @@ exception statement from your version. */ package javax.swing; - +/** + * Thrown by the {@link UIManager#setLookAndFeel(LookAndFeel)} method when the + * specified look and feel is not supported on the current platform. + * + * @see LookAndFeel#isSupportedLookAndFeel() + */ public class UnsupportedLookAndFeelException extends Exception { - public UnsupportedLookAndFeelException(String a) + /** + * Creates a new exception instance with the specified message. + * + * @param s the exception message. + */ + public UnsupportedLookAndFeelException(String s) { - super(a); + super(s); } } diff --git a/libjava/classpath/javax/swing/ViewportLayout.java b/libjava/classpath/javax/swing/ViewportLayout.java index 79fd26c56df..674de959f6e 100644 --- a/libjava/classpath/javax/swing/ViewportLayout.java +++ b/libjava/classpath/javax/swing/ViewportLayout.java @@ -1,5 +1,5 @@ /* ViewportLayout.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. @@ -46,9 +46,16 @@ import java.awt.Rectangle; import java.io.Serializable; /** - * ViewportLayout - * @author Andrew Selkirk - * @author Graydon Hoare + * The default layout for {@link JViewport}. The viewport makes its view the + * same size as itself, but not smaller than its minimum size. + * + * If the port extends extends into space <em>past</em> the edge of the view, + * this layout manager moves the port up or to the left, in view space, by the + * amount of empty space (keep the lower and right edges lined up). + * + * @author Andrew Selkirk + * @author Graydon Hoare + * @author Audrius Meskauskas (audriusa@Bioinformatics.org) */ public class ViewportLayout implements LayoutManager, Serializable { @@ -58,17 +65,31 @@ public class ViewportLayout implements LayoutManager, Serializable { // Nothing to do here. } - + + /** + * The method is not used with this manager. + */ public void addLayoutComponent(String name, Component c) { // Nothing to do here. } + /** + * The method is not used with this manager. + */ public void removeLayoutComponent(Component c) { // Nothing to do here. } - + + /** + * Get the preferred layout size. If the view implements + * {@link Scrollable}, this method returns + * {@link Scrollable#getPreferredScrollableViewportSize}. + * Otherwise, it returns {@link Component#getPreferredSize()}. + * + * @return the preferred layout size, as described about. + */ public Dimension preferredLayoutSize(Container parent) { JViewport vp = (JViewport)parent; @@ -83,14 +104,19 @@ public class ViewportLayout implements LayoutManager, Serializable return new Dimension(); } + /** + * Get the minimum layout size. Normally this method returns the value, + * returned by the view method {@link Component#getMinimumSize()}. + * + * If the view is not set, the zero size is returned. + * + * @param parent the viewport + * @return the minimum layout size. + */ public Dimension minimumLayoutSize(Container parent) { - JViewport vp = (JViewport)parent; - Component view = vp.getView(); - if (view != null) - return view.getMinimumSize(); - else - return new Dimension(); + // These values have been determined by the Mauve test for this method. + return new Dimension(4, 4); } /** @@ -101,15 +127,13 @@ public class ViewportLayout implements LayoutManager, Serializable * * <ol> * - * <li>If the port is larger than the view's minimum size, put the port - * at view position <code>(0,0)</code> and make the view's size equal to - * the port's.</li> - * * <li>If the port is smaller than the view, leave the view at its - * minimum size. also, do not move the port, <em>unless</em> the port + * current size. Also, do not move the port, <em>unless</em> the port * extends into space <em>past</em> the edge of the view. If so, move the * port up or to the left, in view space, by the amount of empty space * (keep the lower and right edges lined up)</li> + * <li>In {@link JViewport#setViewSize(Dimension)}, the view size is never + * set smaller that its minimum size.</li> * * </ol> * @@ -118,7 +142,6 @@ public class ViewportLayout implements LayoutManager, Serializable * @see JViewport#getViewPosition * @see JViewport#setViewPosition */ - public void layoutContainer(Container parent) { // The way to interpret this function is basically to ignore the names @@ -141,23 +164,22 @@ public class ViewportLayout implements LayoutManager, Serializable Rectangle portBounds = port.getViewRect(); Dimension viewPref = view.getPreferredSize(); Dimension viewMinimum = view.getMinimumSize(); + Point portLowerRight = new Point(portBounds.x + portBounds.width, portBounds.y + portBounds.height); + int overextension; // vertical implementation of the above rules if ((! (view instanceof Scrollable) && viewPref.height < portBounds.height || (view instanceof Scrollable && ((Scrollable) view).getScrollableTracksViewportHeight()))) viewPref.height = portBounds.height; - - if (portBounds.height >= viewMinimum.height) - portBounds.y = 0; - else - { - int overextension = portLowerRight.y - viewPref.height; - if (overextension > 0) - portBounds.y -= overextension; - } + + // If the view is larger than the port, and port is partly outside + // the view, it is moved fully into the view area. + overextension = portLowerRight.y - viewPref.height; + if (overextension > 0) + portBounds.y -= overextension; // horizontal implementation of the above rules if ((! (view instanceof Scrollable) && viewPref.width < portBounds.width @@ -165,16 +187,13 @@ public class ViewportLayout implements LayoutManager, Serializable && ((Scrollable) view).getScrollableTracksViewportWidth()))) viewPref.width = portBounds.width; - if (portBounds.width >= viewMinimum.width) - portBounds.x = 0; - else - { - int overextension = portLowerRight.x - viewPref.width; - if (overextension > 0) - portBounds.x -= overextension; - } + // If the view is larger than the port, and port is partly outside + // the view, it is moved fully into the view area. + overextension = portLowerRight.x - viewPref.width; + if (overextension > 0) + portBounds.x -= overextension; - port.setViewPosition(portBounds.getLocation()); port.setViewSize(viewPref); + port.setViewPosition(portBounds.getLocation()); } } diff --git a/libjava/classpath/javax/swing/event/CaretEvent.java b/libjava/classpath/javax/swing/event/CaretEvent.java index c4870a8008f..7de05a81b74 100644 --- a/libjava/classpath/javax/swing/event/CaretEvent.java +++ b/libjava/classpath/javax/swing/event/CaretEvent.java @@ -1,5 +1,5 @@ /* CaretEvent.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,43 +37,34 @@ exception statement from your version. */ package javax.swing.event; -// Imports import java.util.EventObject; /** * CaretEvent * @author Andrew Selkirk */ -public abstract class CaretEvent extends EventObject { +public abstract class CaretEvent extends EventObject +{ - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * CaretEvent constructor - * @param source Source object - */ - public CaretEvent(Object source) { - super(source); - } // CaretEvent() + /** + * CaretEvent constructor + * @param source Source object + */ + public CaretEvent(Object source) + { + super(source); + } - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * Get caret location - * @returns the dot - */ - public abstract int getDot(); + /** + * Get caret location + * @return the dot + */ + public abstract int getDot(); - /** - * Get mark - * @returns the mark - */ - public abstract int getMark(); + /** + * Get mark + * @return the mark + */ + public abstract int getMark(); - -} // CaretEvent +} diff --git a/libjava/classpath/javax/swing/event/DocumentEvent.java b/libjava/classpath/javax/swing/event/DocumentEvent.java index 6cd8e61fee4..82230492520 100644 --- a/libjava/classpath/javax/swing/event/DocumentEvent.java +++ b/libjava/classpath/javax/swing/event/DocumentEvent.java @@ -1,5 +1,5 @@ /* DocumentEvent.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. @@ -54,25 +54,25 @@ public interface DocumentEvent { /** * getIndex - * @returns int + * @return int */ int getIndex(); /** * getElement - * @returns Element + * @return Element */ Element getElement(); /** * getChildrenRemoved - * @returns Element[] + * @return Element[] */ Element[] getChildrenRemoved(); /** * getChildrenAdded - * @returns Element[] + * @return Element[] */ Element[] getChildrenAdded(); @@ -81,7 +81,7 @@ public interface DocumentEvent /** * EventType */ - class EventType + final class EventType { /** * INSERT @@ -114,7 +114,7 @@ public interface DocumentEvent /** * toString - * @returns String + * @return String */ public String toString() { @@ -124,32 +124,32 @@ public interface DocumentEvent /** * getType - * @returns EventType + * @return EventType */ EventType getType(); /** * getOffset - * @returns int + * @return int */ int getOffset(); /** * getLength - * @returns int + * @return int */ int getLength(); /** * getDocument - * @returns Document + * @return Document */ Document getDocument(); /** * getChange * @param element TODO - * @returns ElementChange + * @return ElementChange */ ElementChange getChange(Element element); diff --git a/libjava/classpath/javax/swing/event/EventListenerList.java b/libjava/classpath/javax/swing/event/EventListenerList.java index 147d68ef184..a7fbec44d36 100644 --- a/libjava/classpath/javax/swing/event/EventListenerList.java +++ b/libjava/classpath/javax/swing/event/EventListenerList.java @@ -188,7 +188,7 @@ public class EventListenerList /** * Get a list of listenerType/listener pairs - * @returns Listener list + * @return Listener list */ public Object[] getListenerList() { @@ -214,7 +214,7 @@ public class EventListenerList * @throws NullPointerException if <code>c</code> is * <code>null</code>. * - * @returns an array of <code>c</code> whose elements are the + * @return an array of <code>c</code> whose elements are the * currently subscribed listeners of the specified type. If there * are no such listeners, an empty array is returned. * diff --git a/libjava/classpath/javax/swing/event/ListSelectionEvent.java b/libjava/classpath/javax/swing/event/ListSelectionEvent.java index e5e4c33bad9..d79cbfa507f 100644 --- a/libjava/classpath/javax/swing/event/ListSelectionEvent.java +++ b/libjava/classpath/javax/swing/event/ListSelectionEvent.java @@ -1,5 +1,5 @@ /* ListSelectionEvent.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,90 +37,99 @@ exception statement from your version. */ package javax.swing.event; -// Imports import java.util.EventObject; +import javax.swing.ListSelectionModel; + /** - * ListSelectionEvent + * An event that indicates a change to a list selection, including the source + * of the change (a {@link ListSelectionModel}) and the range of items in the + * list that have potentially changed their selection status. + * * @author Andrew Selkirk * @author Ronald Veldema */ -public class ListSelectionEvent extends EventObject { - - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- - - /** - * firstIndex - */ - private int firstIndex = 0; - - /** - * lastIndex - */ - private int lastIndex = 0; - - /** - * isAdjusting - */ - private boolean isAdjusting = false; - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor ListSelectionEvent - * @param source Source - * @param firstIndex First index - * @param lastIndex Last index - * @param isAdjusting Is Adjusting? - */ - public ListSelectionEvent(Object source, int firstIndex, - int lastIndex, boolean isAdjusting) { - super(source); - this.firstIndex = firstIndex; - this.lastIndex = lastIndex; - this.isAdjusting = isAdjusting; - } // ListSelectionEvent() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * getFirstIndex - * @returns firstIndex - */ - public int getFirstIndex() { - return firstIndex; - } // getFirstIndex() - - /** - * getLastIndex - * @returns lastIndex - */ - public int getLastIndex() { - return lastIndex; - } // getLastIndex() - - /** - * getValueIsAdjusting - * @returns isAdjusting - */ - public boolean getValueIsAdjusting() { - return isAdjusting; - } // getValueIsAdjusting() - - /** - * String representation - * @returns String representation - */ - public String toString() { - return null; // TODO - } // toString() - - -} // ListSelectionEvent +public class ListSelectionEvent extends EventObject +{ + + /** + * The index of the first list item in the range of items that has + * potentially had its selection status modified. + */ + private int firstIndex = 0; + + /** + * The index of the last list item in the range of items that has + * potentially had its selection status modified. + */ + private int lastIndex = 0; + + /** A flag that indicates that this event is one in a series of events. */ + private boolean isAdjusting = false; + + /** + * Creates a new <code>ListSelectionEvent</code>. + * + * @param source the event source (<code>null</code> not permitted). + * @param firstIndex the first index. + * @param lastIndex the last index. + * @param isAdjusting a flag indicating that this event is one in a series + * of events updating a selection. + * + * @throws IllegalArgumentException if <code>source</code> is + * <code>null</code>. + */ + public ListSelectionEvent(Object source, int firstIndex, + int lastIndex, boolean isAdjusting) + { + super(source); + this.firstIndex = firstIndex; + this.lastIndex = lastIndex; + this.isAdjusting = isAdjusting; + } + + /** + * Returns the first index. + * + * @return The first index. + */ + public int getFirstIndex() + { + return firstIndex; + } + + /** + * Returns the last index. + * + * @return The last index. + */ + public int getLastIndex() + { + return lastIndex; + } + + /** + * Returns the flag that indicates that this event is one in a series of + * events updating a selection. + * + * @return A boolean. + */ + public boolean getValueIsAdjusting() + { + return isAdjusting; + } + + /** + * Returns a string representation of the event, typically used for debugging + * purposes. + * + * @return A string representation of the event. + */ + public String toString() + { + return this.getClass().toString() + "[ source=" + source.toString() + + " firstIndex= " + firstIndex + " lastIndex= " + lastIndex + + " isAdjusting= " + isAdjusting + " ]"; + } + +} diff --git a/libjava/classpath/javax/swing/event/ListSelectionListener.java b/libjava/classpath/javax/swing/event/ListSelectionListener.java index 4ebf5830432..a21dc7365bc 100644 --- a/libjava/classpath/javax/swing/event/ListSelectionListener.java +++ b/libjava/classpath/javax/swing/event/ListSelectionListener.java @@ -1,5 +1,5 @@ /* ListSelectionListener.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,21 +37,25 @@ exception statement from your version. */ package javax.swing.event; -// Imports import java.util.EventListener; +import javax.swing.ListSelectionModel; + /** - * ListSelectionListener public interface + * A listener that receives {@link ListSelectionEvent} notifications, + * typically from a {@link ListSelectionModel} when it is modified. + * * @author Andrew Selkirk * @author Ronald Veldema */ -public interface ListSelectionListener extends EventListener { - - /** - * Value changed - * @param event List Selection Event - */ - void valueChanged(ListSelectionEvent event); +public interface ListSelectionListener extends EventListener +{ + /** + * Receives notification of a {@link ListSelectionEvent}. + * + * @param event the event. + */ + void valueChanged(ListSelectionEvent event); -} // ListSelectionListener +}
\ No newline at end of file diff --git a/libjava/classpath/javax/swing/event/MenuDragMouseEvent.java b/libjava/classpath/javax/swing/event/MenuDragMouseEvent.java index 99761670629..6be11bcca71 100644 --- a/libjava/classpath/javax/swing/event/MenuDragMouseEvent.java +++ b/libjava/classpath/javax/swing/event/MenuDragMouseEvent.java @@ -1,5 +1,5 @@ /* MenuDragMouseEvent.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. @@ -48,68 +48,57 @@ import javax.swing.MenuSelectionManager; * MenuDragMouseEvent * @author Andrew Selkirk */ -public class MenuDragMouseEvent extends MouseEvent { +public class MenuDragMouseEvent extends MouseEvent +{ - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- - - /** - * path - */ - private MenuElement[] path = null; + /** + * path + */ + private MenuElement[] path = null; - /** - * manager - */ - private MenuSelectionManager manager = null; - - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor MenuDragMouseEvent - * @param source Source - * @param id MouseEvent type - * @param when Time - * @param modifiers Key modifiers - * @param x Horizontal position - * @param y Vertical position - * @param clickCount Click count - * @param popupTrigger Popup trigger? - * @param path Path - * @param manager MenuSelectionManager - */ - public MenuDragMouseEvent(Component source, int id, long when, int modifiers, - int x, int y, int clickCount, boolean popupTrigger, - MenuElement[] path, MenuSelectionManager manager) { - super(source, id, when, modifiers, x, y, clickCount, popupTrigger); - this.path = path; - this.manager = manager; - } // MenuDragMouseEvent() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * Get path - * @returns path - */ - public MenuElement[] getPath() { - return path; - } // getPath() - - /** - * Get menu selection manager - * @returns manager - */ - public MenuSelectionManager getMenuSelectionManager() { - return manager; - } // getMenuSelectionManager() - - -} // MenuDragMouseEvent + /** + * manager + */ + private MenuSelectionManager manager = null; + + /** + * Constructor MenuDragMouseEvent + * @param source Source + * @param id MouseEvent type + * @param when Time + * @param modifiers Key modifiers + * @param x Horizontal position + * @param y Vertical position + * @param clickCount Click count + * @param popupTrigger Popup trigger? + * @param path Path + * @param manager MenuSelectionManager + */ + public MenuDragMouseEvent(Component source, int id, long when, int modifiers, + int x, int y, int clickCount, boolean popupTrigger, + MenuElement[] path, MenuSelectionManager manager) + { + super(source, id, when, modifiers, x, y, clickCount, popupTrigger); + this.path = path; + this.manager = manager; + } + + /** + * Get path + * @return path + */ + public MenuElement[] getPath() + { + return path; + } + + /** + * Get menu selection manager + * @return manager + */ + public MenuSelectionManager getMenuSelectionManager() + { + return manager; + } + +} diff --git a/libjava/classpath/javax/swing/event/MenuKeyEvent.java b/libjava/classpath/javax/swing/event/MenuKeyEvent.java index 511cb2254cd..3335850bced 100644 --- a/libjava/classpath/javax/swing/event/MenuKeyEvent.java +++ b/libjava/classpath/javax/swing/event/MenuKeyEvent.java @@ -1,5 +1,5 @@ /* MenuKeyEvent.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. @@ -48,66 +48,55 @@ import javax.swing.MenuSelectionManager; * MenuKeyEvent * @author Andrew Selkirk */ -public class MenuKeyEvent extends KeyEvent { +public class MenuKeyEvent extends KeyEvent +{ - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- - - /** - * path - */ - private MenuElement[] path = null; + /** + * path + */ + private MenuElement[] path = null; - /** - * manager - */ - private MenuSelectionManager manager = null; - - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor MenuKeyEvent - * @param source Source - * @param id KeyEvent ID - * @param when Time - * @param modifiers Modifier keys - * @param keyCode Key code - * @param keyChar Key char - * @param path Path - * @param manager MenuSelectionManager - */ - public MenuKeyEvent(Component source, int id, long when, int modifiers, - int keyCode, char keyChar, MenuElement[] path, - MenuSelectionManager manager) { - super(source, id, when, modifiers, keyCode, keyChar); - this.path = path; - this.manager = manager; - } // MenuKeyEvent() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * getPath - * @returns path - */ - public MenuElement[] getPath() { - return path; - } // getPath() - - /** - * getMenuSelectionManager - * @returns MenuSelectionManager - */ - public MenuSelectionManager getMenuSelectionManager() { - return manager; - } // getMenuSelectionManager() - - -} // MenuKeyEvent + /** + * manager + */ + private MenuSelectionManager manager = null; + + /** + * Constructor MenuKeyEvent + * @param source Source + * @param id KeyEvent ID + * @param when Time + * @param modifiers Modifier keys + * @param keyCode Key code + * @param keyChar Key char + * @param path Path + * @param manager MenuSelectionManager + */ + public MenuKeyEvent(Component source, int id, long when, int modifiers, + int keyCode, char keyChar, MenuElement[] path, + MenuSelectionManager manager) + { + super(source, id, when, modifiers, keyCode, keyChar); + this.path = path; + this.manager = manager; + } + + /** + * getPath + * @return path + */ + public MenuElement[] getPath() + { + return path; + } + + /** + * getMenuSelectionManager + * @return MenuSelectionManager + */ + public MenuSelectionManager getMenuSelectionManager() + { + return manager; + } + +} diff --git a/libjava/classpath/javax/swing/event/SwingPropertyChangeSupport.java b/libjava/classpath/javax/swing/event/SwingPropertyChangeSupport.java index 7e8ff0dc2e9..7fb8aa67d7a 100644 --- a/libjava/classpath/javax/swing/event/SwingPropertyChangeSupport.java +++ b/libjava/classpath/javax/swing/event/SwingPropertyChangeSupport.java @@ -1,5 +1,5 @@ /* SwingPropertyChangeSupport.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. @@ -39,48 +39,24 @@ package javax.swing.event; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeListenerProxy; import java.beans.PropertyChangeSupport; -import java.util.ArrayList; -import java.util.EventListener; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; /** * Provides a mechanism for registering {@link PropertyChangeListener}s and * forwarding {@link PropertyChangeEvent}s to those listeners. - * + * + * As of JDK1.5 this class is no longer in use. Use + * {@link PropertyChangeSupport} instead. + * * @author Andrew Selkirk */ public final class SwingPropertyChangeSupport - extends PropertyChangeSupport + extends PropertyChangeSupport { private static final long serialVersionUID = 7162625831330845068L; /** - * Storage for the listeners that are not linked to a specific property. - */ - private transient EventListenerList listeners; - - /** - * Storage for the listeners that are linked (by name) to a specific property. - * The hash table maps <code>String</code> objects (the property names) to - * {@link EventListenerList} instances (which record the listener(s) for the - * given property). - */ - private Hashtable propertyListeners; - - /** - * The object that is used as the default source for the - * {@link PropertyChangeEvent}s generated by this class. - */ - private Object source; - - /** * Creates a new instance. * * @param source the source (<code>null</code> not permitted). @@ -90,247 +66,5 @@ public final class SwingPropertyChangeSupport public SwingPropertyChangeSupport(Object source) { super(source); - this.source = source; - this.listeners = new EventListenerList(); - this.propertyListeners = new Hashtable(); } - - /** - * Registers <code>listener</code> to receive notification of any future - * {@link PropertyChangeEvent}s generated by this instance. - * - * @param listener the listener (<code>null</code> is ignored). - * - * @see #removePropertyChangeListener(PropertyChangeListener) - */ - public synchronized void addPropertyChangeListener(PropertyChangeListener - listener) - { - listeners.add(PropertyChangeListener.class, listener); - } - - /** - * Registers <code>listener</code> to receive notification of any future - * {@link PropertyChangeEvent}s generated by this instance for the named - * property. - * - * @param propertyName the property name. - * @param listener the listener. - * - * @see #removePropertyChangeListener(String, PropertyChangeListener) - */ - public synchronized void addPropertyChangeListener(String propertyName, - PropertyChangeListener listener) - { - EventListenerList list; - list = (EventListenerList) propertyListeners.get(propertyName); - if (list == null) - { - list = new EventListenerList(); - propertyListeners.put(propertyName, list); - } - list.add(PropertyChangeListener.class, listener); - } - - /** - * Removes <code>listener</code> from the list of registered listeners, so - * that it will no longer receive notification of property change events. - * - * @param listener the listener to remove. - */ - public synchronized void removePropertyChangeListener(PropertyChangeListener - listener) - { - listeners.remove(PropertyChangeListener.class, listener); - } - - /** - * Removes <code>listener</code> from the list of registered listeners for - * the named property, so that it will no longer receive notification of - * property change events. - * - * @param propertyName the property name. - * @param listener the listener to remove. - */ - public synchronized void removePropertyChangeListener(String propertyName, - PropertyChangeListener listener) - { - EventListenerList list; - list = (EventListenerList) propertyListeners.get(propertyName); - if (list == null) - return; - list.remove(PropertyChangeListener.class, listener); - if (list.getListenerCount() == 0) - { - propertyListeners.remove(propertyName); - } - } - - /** - * Returns an array of the {@link PropertyChangeListener}s registered with - * this <code>SwingPropertyChangeSupport</code> instance. - * - * @return The array of listeners. - * - * @since 1.4 - */ - public synchronized PropertyChangeListener[] getPropertyChangeListeners() - { - // fetch the named listeners first so we know how many there are - List namedListeners = new ArrayList(); - Set namedListenerEntries = propertyListeners.entrySet(); - Iterator iterator = namedListenerEntries.iterator(); - while (iterator.hasNext()) - { - Map.Entry e = (Map.Entry) iterator.next(); - String propertyName = (String) e.getKey(); - EventListenerList ell = (EventListenerList) e.getValue(); - if (ell != null) - { - Object[] list = ell.getListenerList(); - for (int i = 0; i < list.length; i += 2) - { - namedListeners.add(new PropertyChangeListenerProxy(propertyName, - (PropertyChangeListener) list[i + 1])); - } - } - } - - // create an array that can hold everything - int size = listeners.getListenerCount() + namedListeners.size(); - PropertyChangeListener[] result = new PropertyChangeListener[size]; - - // copy in the general listeners - Object[] list = listeners.getListenerList(); - int index = 0; - for (int i = 0; i < list.length; i += 2) - result[index++] = (PropertyChangeListener) list[i + 1]; - - // ...and the named listeners - Iterator iterator2 = namedListeners.iterator(); - while (iterator2.hasNext()) - result[index++] = (PropertyChangeListenerProxy) iterator2.next(); - - return result; - } - - /** - * Returns an array of all listeners that are registered to receive - * notification of changes to the named property. This includes the general - * listeners as well as those registered specifically for the named - * property. - * - * @param propertyName the property name. - * - * @return An array of all listeners for the named property. - */ - public synchronized PropertyChangeListener[] getPropertyChangeListeners( - String propertyName) - { - EventListenerList list - = (EventListenerList) propertyListeners.get(propertyName); - if (list == null) - return getPropertyChangeListeners(); - int size = listeners.getListenerCount() + list.getListenerCount(); - PropertyChangeListener[] result = new PropertyChangeListener[size]; - - // copy in the general listeners - int index = 0; - for (int i = 0; i < listeners.listenerList.length; i += 2) - { - result[index++] - = (PropertyChangeListener) listeners.listenerList[i + 1]; - } - - // copy in the specific listeners - Object[] specificListeners = list.getListenerList(); - for (int i = 0; i < specificListeners.length; i += 2) - { - result[index++] = (PropertyChangeListener) specificListeners[i + 1]; - } - return result; - } - - /** - * Creates a new {@link PropertyChangeEvent} using the given arguments (and - * the default <code>source</code> for this - * <code>SwingPropertyChangeSupport</code> instance) and forwards it to all - * registered listeners via the - * {@link PropertyChangeListener#propertyChange(PropertyChangeEvent)} method. - * <p> - * Note that if <code>oldValue</code> and <code>newValue</code> are non-null - * and equal, no listeners will be notified. - * - * @param propertyName the property name. - * @param oldValue the old value - * @param newValue the new value. - */ - public void firePropertyChange(String propertyName, Object oldValue, - Object newValue) - { - PropertyChangeEvent event; - event = new PropertyChangeEvent(source, propertyName, oldValue, newValue); - firePropertyChange(event); - } - - /** - * Forwards <code>event</code> to registered listeners. - * <p> - * Note that if the event's <code>getOldValue()</code> and - * <code>getNewValue()</code> methods return non-null and equal values, no - * listeners will be notified. - * - * @param event the event. - */ - public void firePropertyChange(PropertyChangeEvent event) - { - EventListenerList list; - EventListener[] listenerList; - int index; - PropertyChangeListener listener; - - // if the old and new values are non-null and equal, don't notify listeners - if (event.getOldValue() != null && event.getNewValue() != null && - event.getOldValue().equals(event.getNewValue())) - return; - - // Process Main Listener List - listenerList = listeners.getListeners(PropertyChangeListener.class); - for (index = 0; index < listenerList.length; index++) - { - listener = (PropertyChangeListener) listenerList[index]; - listener.propertyChange(event); - } - - // Process Property Listener List - list = (EventListenerList) propertyListeners.get(event.getPropertyName()); - if (list != null) - { - listenerList = list.getListeners(PropertyChangeListener.class); - for (index = 0; index < listenerList.length; index++) - { - listener = (PropertyChangeListener) listenerList[index]; - listener.propertyChange(event); - } - } - - } - - /** - * Tell whether the specified property is being listened on or not. This - * will only return <code>true</code> if there are listeners on all - * properties or if there is a listener specifically on this property. - * - * @param propertyName the property that may be listened on - * @return whether the property is being listened on - * @throws NullPointerException if propertyName is null - */ - public synchronized boolean hasListeners(String propertyName) - { - if (listeners.getListenerCount() > 0) - return true; - else - return (propertyListeners.get(propertyName) != null); - } - } diff --git a/libjava/classpath/javax/swing/event/TableColumnModelEvent.java b/libjava/classpath/javax/swing/event/TableColumnModelEvent.java index 2ca4148aadb..cff49130e35 100644 --- a/libjava/classpath/javax/swing/event/TableColumnModelEvent.java +++ b/libjava/classpath/javax/swing/event/TableColumnModelEvent.java @@ -1,5 +1,5 @@ /* TableColumnModelEvent.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. @@ -46,60 +46,48 @@ import javax.swing.table.TableColumnModel; * TableColumnModelEvent * @author Andrew Selkirk */ -public class TableColumnModelEvent extends EventObject { - - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- - - /** - * fromIndex - */ - protected int fromIndex = 0; - - /** - * toIndex - */ - protected int toIndex = 0; - - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor TableColumnModelEvent - * @param source Source TableColumnModel - * @param from From index - * @param to To index - */ - public TableColumnModelEvent(TableColumnModel source, - int from, int to) { - super(source); - fromIndex = from; - toIndex = to; - } // TableColumnModelEvent() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * getFromIndex. - * @returns From index - */ - public int getFromIndex() { - return fromIndex; - } // getFromIndex() - - /** - * getToIndex. - * @returns To index - */ - public int getToIndex() { - return toIndex; - } // getToIndex() - - -} // TableColumnModelEvent +public class TableColumnModelEvent extends EventObject +{ + + /** + * fromIndex + */ + protected int fromIndex = 0; + + /** + * toIndex + */ + protected int toIndex = 0; + + /** + * Constructor TableColumnModelEvent + * @param source Source TableColumnModel + * @param from From index + * @param to To index + */ + public TableColumnModelEvent(TableColumnModel source, int from, int to) + { + super(source); + fromIndex = from; + toIndex = to; + } + + /** + * getFromIndex. + * @return From index + */ + public int getFromIndex() + { + return fromIndex; + } + + /** + * getToIndex. + * @return To index + */ + public int getToIndex() + { + return toIndex; + } + +}
\ No newline at end of file diff --git a/libjava/classpath/javax/swing/event/TableModelListener.java b/libjava/classpath/javax/swing/event/TableModelListener.java index c8d6e8f8dbc..21e5ea0474e 100644 --- a/libjava/classpath/javax/swing/event/TableModelListener.java +++ b/libjava/classpath/javax/swing/event/TableModelListener.java @@ -1,5 +1,5 @@ /* TableModelListener.java -- - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,16 +40,21 @@ package javax.swing.event; import java.util.EventListener; /** - * TableModelListener public interface + * A <code>TableModelListener</code> can register with a + * {@link javax.swing.table.TableModel} and receive notification of updates to + * the model. + * * @author Andrew Selkirk */ -public interface TableModelListener extends EventListener { - - /** - * Table changed - * @param event Table Model Event - */ - void tableChanged(TableModelEvent event); - - -} // TableModelListener +public interface TableModelListener extends EventListener +{ + + /** + * Called to notify the listener that the + * {@link javax.swing.table.TableModel} has been updated. + * + * @param event contains details of the update. + */ + void tableChanged(TableModelEvent event); + +} diff --git a/libjava/classpath/javax/swing/event/TreeExpansionEvent.java b/libjava/classpath/javax/swing/event/TreeExpansionEvent.java index c4b33134694..5820b339172 100644 --- a/libjava/classpath/javax/swing/event/TreeExpansionEvent.java +++ b/libjava/classpath/javax/swing/event/TreeExpansionEvent.java @@ -1,5 +1,5 @@ /* TreeExpansionEvent.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. @@ -46,44 +46,32 @@ import javax.swing.tree.TreePath; * TreeExpansionEvent * @author Andrew Selkirk */ -public class TreeExpansionEvent extends EventObject { - - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- - - /** - * path - */ - protected TreePath path = null; - - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor TreeExpansionEvent - * @param source Source object - * @param path Path - */ - public TreeExpansionEvent(Object source, TreePath path) { - super(source); - this.path = path; - } // TreeExpansionEvent() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * getPath - * @returns Tree path - */ - public TreePath getPath() { - return path; - } // getPath() - - -} // TreeExpansionEvent +public class TreeExpansionEvent extends EventObject +{ + + /** + * path + */ + protected TreePath path = null; + + /** + * Constructor TreeExpansionEvent + * @param source Source object + * @param path Path + */ + public TreeExpansionEvent(Object source, TreePath path) + { + super(source); + this.path = path; + } + + /** + * getPath + * @return Tree path + */ + public TreePath getPath() + { + return path; + } + +} diff --git a/libjava/classpath/javax/swing/event/TreeModelEvent.java b/libjava/classpath/javax/swing/event/TreeModelEvent.java index 8fa28a7eadb..2d562a5c4a8 100644 --- a/libjava/classpath/javax/swing/event/TreeModelEvent.java +++ b/libjava/classpath/javax/swing/event/TreeModelEvent.java @@ -1,5 +1,5 @@ /* TreeModelEvent.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. @@ -46,128 +46,123 @@ import javax.swing.tree.TreePath; * TreeModelEvent * @author Andrew Selkirk */ -public class TreeModelEvent extends EventObject { - - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- - - /** - * childIndices - */ - protected int[] childIndices = null; - - /** - * children - */ - protected Object[] children = null; - - /** - * path - */ - protected TreePath path = null; - - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- +public class TreeModelEvent extends EventObject +{ + + /** + * childIndices + */ + protected int[] childIndices = null; + + /** + * children + */ + protected Object[] children = null; + + /** + * path + */ + protected TreePath path = null; - /** - * Constructor TreeModelEvent - * @param source Source object - * @param path - */ - public TreeModelEvent(Object source, Object[] path) { - super(source); - this.path = new TreePath(path); - } // TreeModelEvent() - - /** - * Constructor TreeModelEvent - * @param source Source object - * @param path path - * @param childIndices Child indices - * @param children Children - */ - public TreeModelEvent(Object source, Object[] path, - int[] childIndices, Object[] children) { - super(source); - this.path = new TreePath(path); - this.childIndices = childIndices; - this.children = children; - } // TreeModelEvent() - - /** - * Constructor TreeModelEvent - * @param source Source object - * @param path Path - */ - public TreeModelEvent(Object source, TreePath path) { - super(source); - this.path = path; - } // TreeModelEvent() - - /** - * Constructor TreeModelEvent - * @param source Source object - * @param path Path - * @param childIndices Child indices - * @param children Children - */ - public TreeModelEvent(Object source, TreePath path, - int[] childIndices, Object[] children) { - super(source); - this.path = path; - this.childIndices = childIndices; - this.children = children; - } // TreeModelEvent() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * getChildIndices - * @returns child indices - */ - public int[] getChildIndices() { - return childIndices; - } // getChildIndices() - - /** - * getChildren - * @returns children - */ - public Object[] getChildren() { - return children; - } // getChildren() - - /** - * getPath - * @returns path - */ - public Object[] getPath() { - return path.getPath(); - } // getPath() - - /** - * getTreePath - * @returns TreePath - */ - public TreePath getTreePath() { - return path; - } // getTreePath() - - /** - * String representation - * @returns String representation - */ - public String toString() { - return getClass() + " [Source: " + getSource() + ", TreePath: " + getTreePath() + - ", Child Indicies: " + getChildIndices() + ", Children: " + getChildren() + - ", Path: " + getPath() +"]"; - } // toString() - - -} // TreeModelEvent + /** + * Constructor TreeModelEvent + * @param source Source object + * @param path + */ + public TreeModelEvent(Object source, Object[] path) + { + super(source); + this.path = new TreePath(path); + } + + /** + * Constructor TreeModelEvent + * @param source Source object + * @param path path + * @param childIndices Child indices + * @param children Children + */ + public TreeModelEvent(Object source, Object[] path, + int[] childIndices, Object[] children) + { + super(source); + this.path = new TreePath(path); + this.childIndices = childIndices; + this.children = children; + } + + /** + * Constructor TreeModelEvent + * @param source Source object + * @param path Path + */ + public TreeModelEvent(Object source, TreePath path) + { + super(source); + this.path = path; + } + + /** + * Constructor TreeModelEvent + * @param source Source object + * @param path Path + * @param childIndices Child indices + * @param children Children + */ + public TreeModelEvent(Object source, TreePath path, + int[] childIndices, Object[] children) + { + super(source); + this.path = path; + this.childIndices = childIndices; + this.children = children; + } + + /** + * getChildIndices + * @return child indices + */ + public int[] getChildIndices() + { + return childIndices; + } + + /** + * getChildren + * @return children + */ + public Object[] getChildren() + { + return children; + } + + /** + * getPath + * @return path + */ + public Object[] getPath() + { + return path.getPath(); + } + + /** + * getTreePath + * @return TreePath + */ + public TreePath getTreePath() + { + return path; + } + + /** + * String representation + * @return String representation + */ + public String toString() + { + return getClass() + " [Source: " + getSource() + ", TreePath: " + + getTreePath() + ", Child Indicies: " + getChildIndices() + + ", Children: " + getChildren() + ", Path: " + getPath() +"]"; + } + +} diff --git a/libjava/classpath/javax/swing/event/TreeSelectionEvent.java b/libjava/classpath/javax/swing/event/TreeSelectionEvent.java index 9b87667a387..1930677af9f 100644 --- a/libjava/classpath/javax/swing/event/TreeSelectionEvent.java +++ b/libjava/classpath/javax/swing/event/TreeSelectionEvent.java @@ -1,5 +1,5 @@ /* TreeSelectionEvent.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. @@ -49,10 +49,6 @@ import javax.swing.tree.TreePath; */ public class TreeSelectionEvent extends EventObject { - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- - /** * paths */ @@ -73,11 +69,6 @@ public class TreeSelectionEvent extends EventObject { */ protected TreePath newLeadSelectionPath; - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - /** * Constructor TreeSelectionEvent * @param source TODO @@ -95,7 +86,7 @@ public class TreeSelectionEvent extends EventObject { this.areNew = areNew; this.oldLeadSelectionPath = oldLeadSelectionPath; this.newLeadSelectionPath = newLeadSelectionPath; - } // TreeSelectionEvent() + } /** * Constructor TreeSelectionEvent @@ -114,29 +105,24 @@ public class TreeSelectionEvent extends EventObject { this.areNew = new boolean[]{isNew}; this.oldLeadSelectionPath = oldLeadSelectionPath; this.newLeadSelectionPath = newLeadSelectionPath; - } // TreeSelectionEvent() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- + } /** - * @returns the first path element + * @return the first path element */ public TreePath getPath() { return paths[0]; - } // getPath() + } /** * - * @returns the paths with selection changed + * @return the paths with selection changed */ public TreePath[] getPaths() { return (TreePath[]) paths.clone(); - } // getPaths() + } /** * @return true if the first path is added to the selection, false otherwise @@ -144,7 +130,7 @@ public class TreeSelectionEvent extends EventObject { public boolean isAddedPath() { return areNew[0]; - } // isAddedPath() + } /** * @param path the path to check @@ -157,7 +143,7 @@ public class TreeSelectionEvent extends EventObject { return areNew[i]; return false; - } // isAddedPath() + } /** * @param index the index'th path @@ -166,7 +152,7 @@ public class TreeSelectionEvent extends EventObject { public boolean isAddedPath(int index) { return areNew[index]; - } // isAddedPath() + } /** * @return the previous lead selection path @@ -174,15 +160,15 @@ public class TreeSelectionEvent extends EventObject { public TreePath getOldLeadSelectionPath() { return oldLeadSelectionPath; - } // getOldLeadSelectionPath() + } /** - * @returns the current lead selection path + * @return the current lead selection path */ public TreePath getNewLeadSelectionPath() { return newLeadSelectionPath; - } // getNewLeadSelectionPath() + } /** * @param source the new event source @@ -193,7 +179,6 @@ public class TreeSelectionEvent extends EventObject { return new TreeSelectionEvent (source, paths, areNew, oldLeadSelectionPath, newLeadSelectionPath); - } // cloneWithSource() - + } -} // TreeSelectionEvent +} diff --git a/libjava/classpath/javax/swing/event/UndoableEditEvent.java b/libjava/classpath/javax/swing/event/UndoableEditEvent.java index 147c2e5b1c5..b59ceadc9c2 100644 --- a/libjava/classpath/javax/swing/event/UndoableEditEvent.java +++ b/libjava/classpath/javax/swing/event/UndoableEditEvent.java @@ -1,5 +1,5 @@ /* UndoableEditEvent.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. @@ -47,46 +47,34 @@ import javax.swing.undo.UndoableEdit; * @author Andrew Selkirk * @author Ronald Veldema */ -public class UndoableEditEvent extends EventObject { +public class UndoableEditEvent extends EventObject +{ private static final long serialVersionUID = 4418044561759134484L; - //------------------------------------------------------------- - // Variables -------------------------------------------------- - //------------------------------------------------------------- - - /** - * edit - */ - private UndoableEdit edit; - - - //------------------------------------------------------------- - // Initialization --------------------------------------------- - //------------------------------------------------------------- - - /** - * Constructor UndoableEditEvent - * @param source TODO - * @param edit TODO - */ - public UndoableEditEvent(Object source, UndoableEdit edit) { - super(source); - this.edit = edit; - } // UndoableEditEvent() - - - //------------------------------------------------------------- - // Methods ---------------------------------------------------- - //------------------------------------------------------------- - - /** - * getEdit - * @returns UndoableEdit - */ - public UndoableEdit getEdit() { - return edit; - } // getEdit() - - -} // UndoableEditEvent + /** + * edit + */ + private UndoableEdit edit; + + /** + * Constructor UndoableEditEvent + * @param source TODO + * @param edit TODO + */ + public UndoableEditEvent(Object source, UndoableEdit edit) + { + super(source); + this.edit = edit; + } + + /** + * getEdit + * @return UndoableEdit + */ + public UndoableEdit getEdit() + { + return edit; + } + +} diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicBorders.java b/libjava/classpath/javax/swing/plaf/basic/BasicBorders.java index 5d4ce18932b..5e2cf2e48ca 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicBorders.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicBorders.java @@ -299,9 +299,7 @@ public class BasicBorders public static Border getSplitPaneDividerBorder() { /* See comment in methods above for why this border is not shared. */ - return new SplitPaneDividerBorder( - UIManager.getColor("SplitPane.highlight"), - UIManager.getColor("SplitPane.darkShadow")); + return new SplitPaneDividerBorder(); } @@ -1518,34 +1516,15 @@ public class BasicBorders implements Border, UIResource, Serializable { /** - * The highlight color, which is drawn on the left or top edge - * depending on the orientation of the JSplitPanel. - */ - protected Color highlight; - - - /** - * The highlight color, which is drawn on the right or bottom edge - * depending on the orientation of the JSplitPanel. - */ - protected Color shadow; - - - /** * Constructs a new border for drawing the divider of a JSplitPane * in the Basic look and feel. The outer parts of the JSplitPane have * their own border class, <code>SplitPaneBorder</code>. - * - * @param shadow the shadow color. - * @param highlight the highlight color. */ - public SplitPaneDividerBorder(Color highlight, Color shadow) + public SplitPaneDividerBorder() { - this.highlight = (highlight != null) ? highlight : Color.white; - this.shadow = (shadow != null) ? shadow : Color.black; + // Nothing to do here. } - /** * Paints the border around the divider of a <code>JSplitPane</code>. * @@ -1564,6 +1543,8 @@ public class BasicBorders public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + Color highlight = UIManager.getColor("SplitPane.highlight"); + Color shadow = UIManager.getColor("SplitPane.shadow"); Color oldColor, dcol; int x2, y2; JSplitPane sp; @@ -1624,17 +1605,15 @@ public class BasicBorders return new Insets(1, 1, 1, 1); } - /** * Determines whether this border fills every pixel in its area * when painting. * - * @return <code>true</code> if both highlight and shadow - * color are fully opaque. + * @return <code>true</code> */ public boolean isBorderOpaque() { - return (highlight.getAlpha() == 255) && (shadow.getAlpha() == 255); + return true; } @@ -1785,4 +1764,5 @@ public class BasicBorders return insets; } } + } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java index 95e0dc98257..e45970ed02c 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java @@ -45,7 +45,6 @@ import javax.swing.JMenuItem; import javax.swing.MenuElement; import javax.swing.MenuSelectionManager; import javax.swing.UIDefaults; -import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java index 5a872ae6368..f37cbd7b838 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java @@ -141,10 +141,9 @@ public class BasicColorChooserUI extends ColorChooserUI protected PropertyChangeListener propertyChangeListener; /** - * The JColorChooser. - * This is package-private to avoid an accessor method. + * The JColorChooser this is installed on. */ - JColorChooser chooser; + protected JColorChooser chooser; /** The JTabbedPane that is used. */ JTabbedPane pane; diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java index 08dab7f9f36..798101d0d85 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java @@ -69,6 +69,7 @@ import javax.swing.ListSelectionModel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.Timer; +import javax.swing.UIManager; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; import javax.swing.event.ListSelectionEvent; @@ -193,8 +194,23 @@ public class BasicComboPopup extends JPopupMenu implements ComboPopup if (selectedIndex > comboBox.getMaximumRowCount()) scrollbar.setValue(getPopupHeightForRowCount(selectedIndex)); + // We put the autoclose-registration inside an InvocationEvent, so that + // the same event that triggered this show() call won't hide the popup + // immediately. + SwingUtilities.invokeLater + (new Runnable() + { + public void run() + { + // Register this popup to be autoclosed when user clicks outside the + // popup. + BasicLookAndFeel laf = (BasicLookAndFeel) UIManager.getLookAndFeel(); + laf.registerForAutoClose(BasicComboPopup.this); + }}); + // location specified is relative to comboBox super.show(comboBox, 0, cbBounds.height); + } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicHTML.java b/libjava/classpath/javax/swing/plaf/basic/BasicHTML.java index b9891e14401..98c9cb277f4 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicHTML.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicHTML.java @@ -38,12 +38,21 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Container; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Shape; import java.io.IOException; import java.io.StringReader; import javax.swing.JComponent; +import javax.swing.SwingConstants; +import javax.swing.event.DocumentEvent; import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.EditorKit; import javax.swing.text.Element; +import javax.swing.text.Position; import javax.swing.text.View; import javax.swing.text.ViewFactory; import javax.swing.text.html.HTMLDocument; @@ -59,6 +68,287 @@ public class BasicHTML { /** + * This class serves as the root view for HTML rendering components. + * Its purpose and implementation is similar to the BasicTextUI.RootView + * class, only that is implements some stuff differently due to the nature + * of not beeing inside a JTextComponent. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private static class HTMLRootView extends View + { + /** + * The real root view. + */ + private View view; + + /** + * The component on which to render the view. + */ + private JComponent component; + + /** + * The EditorKit. + */ + private EditorKit editorKit; + + /** + * The document to use. + */ + private Document document; + + /** + * Creates a new RootView. + */ + public HTMLRootView(JComponent c, View view, EditorKit kit, Document doc) + { + super(null); + component = c; + editorKit = kit; + document = doc; + setView(view); + } + + /** + * Returns the ViewFactory for this RootView. If the current EditorKit + * provides a ViewFactory, this is used. Otherwise the TextUI itself + * is returned as a ViewFactory. + * + * @return the ViewFactory for this RootView + */ + public ViewFactory getViewFactory() + { + return editorKit.getViewFactory(); + } + + /** + * Indicates that the preferences of one of the child view has changed. + * This calls revalidate on the text component. + * + * @param v the child view which's preference has changed + * @param width <code>true</code> if the width preference has changed + * @param height <code>true</code> if the height preference has changed + */ + public void preferenceChanged(View v, boolean width, boolean height) + { + component.revalidate(); + } + + /** + * Sets the real root view. + * + * @param v the root view to set + */ + public void setView(View v) + { + if (view != null) + view.setParent(null); + + if (v != null) + v.setParent(this); + + view = v; + } + + /** + * Returns the real root view, regardless of the index. + * + * @param index not used here + * + * @return the real root view, regardless of the index. + */ + public View getView(int index) + { + return view; + } + + /** + * Returns <code>1</code> since the RootView always contains one + * child, that is the real root of the View hierarchy. + * + * @return <code>1</code> since the RootView always contains one + * child, that is the real root of the View hierarchy + */ + public int getViewCount() + { + int count = 0; + if (view != null) + count = 1; + return count; + } + + /** + * Returns the <code>Container</code> that contains this view. This + * normally will be the text component that is managed by this TextUI. + * + * @return the <code>Container</code> that contains this view + */ + public Container getContainer() + { + return component; + } + + /** + * Returns the preferred span along the specified <code>axis</code>. + * This is delegated to the real root view. + * + * @param axis the axis for which the preferred span is queried + * + * @return the preferred span along the axis + */ + public float getPreferredSpan(int axis) + { + if (view != null) + return view.getPreferredSpan(axis); + + return Integer.MAX_VALUE; + } + + /** + * Paints the view. This is delegated to the real root view. + * + * @param g the <code>Graphics</code> context to paint to + * @param s the allocation for the View + */ + public void paint(Graphics g, Shape s) + { + if (view != null) + { + Rectangle b = s.getBounds(); + view.setSize(b.width, b.height); + view.paint(g, s); + } + } + + + /** + * Maps a position in the document into the coordinate space of the View. + * The output rectangle usually reflects the font height but has a width + * of zero. + * + * This is delegated to the real root view. + * + * @param position the position of the character in the model + * @param a the area that is occupied by the view + * @param bias either {@link Position.Bias#Forward} or + * {@link Position.Bias#Backward} depending on the preferred + * direction bias. If <code>null</code> this defaults to + * <code>Position.Bias.Forward</code> + * + * @return a rectangle that gives the location of the document position + * inside the view coordinate space + * + * @throws BadLocationException if <code>pos</code> is invalid + * @throws IllegalArgumentException if b is not one of the above listed + * valid values + */ + public Shape modelToView(int position, Shape a, Position.Bias bias) + throws BadLocationException + { + return view.modelToView(position, a, bias); + } + + /** + * Maps coordinates from the <code>View</code>'s space into a position + * in the document model. + * + * @param x the x coordinate in the view space + * @param y the y coordinate in the view space + * @param a the allocation of this <code>View</code> + * @param b the bias to use + * + * @return the position in the document that corresponds to the screen + * coordinates <code>x, y</code> + */ + public int viewToModel(float x, float y, Shape a, Position.Bias[] b) + { + return view.viewToModel(x, y, a, b); + } + + /** + * Notification about text insertions. These are forwarded to the + * real root view. + * + * @param ev the DocumentEvent describing the change + * @param shape the current allocation of the view's display + * @param vf the ViewFactory to use for creating new Views + */ + public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) + { + view.insertUpdate(ev, shape, vf); + } + + /** + * Notification about text removals. These are forwarded to the + * real root view. + * + * @param ev the DocumentEvent describing the change + * @param shape the current allocation of the view's display + * @param vf the ViewFactory to use for creating new Views + */ + public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) + { + view.removeUpdate(ev, shape, vf); + } + + /** + * Notification about text changes. These are forwarded to the + * real root view. + * + * @param ev the DocumentEvent describing the change + * @param shape the current allocation of the view's display + * @param vf the ViewFactory to use for creating new Views + */ + public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) + { + view.changedUpdate(ev, shape, vf); + } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param a the allocation for the view + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset in + * the document model + */ + public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a, + int d, Position.Bias[] biasRet) + throws BadLocationException + { + return view.getNextVisualPositionFrom(pos, b, a, d, biasRet); + } + + public int getStartOffset() + { + return 0; + } + + public int getEndOffset() + { + return getDocument().getLength(); + } + + public Document getDocument() + { + return document; + } + } + + /** * The key that is used to store a HTML view in a JComponent's client * properties. */ @@ -116,7 +406,8 @@ public class BasicHTML ViewFactory vf = kit.getViewFactory(); Element root = doc.getDefaultRootElement(); View view = vf.create(root); - return view; + HTMLRootView rootView = new HTMLRootView(c, view, kit, doc); + return rootView; } /** @@ -132,13 +423,13 @@ public class BasicHTML { // We consider a string to be HTML if it contains both the '<' and '>' // character at least once. - return s.contains("<") && s.contains(">"); + return (s != null) && s.contains("<") && s.contains(">"); } /** * Stores a HTML renderer in <code>c</code>'s client property if * <code>text</code> is HTML, otherwise it clears the corresponding client - * property. This is useful for {@link java.swing.plaf.ComponentUI} + * property. This is useful for {@link javax.swing.plaf.ComponentUI} * implementations that are shared between it's components. * * @param c the component to update the renderer for diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java index f9653bd2edd..f6cbeec8879 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java @@ -54,8 +54,6 @@ import java.awt.event.ComponentListener; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.beans.PropertyVetoException; -import java.beans.VetoableChangeListener; import javax.swing.DefaultDesktopManager; import javax.swing.DesktopManager; @@ -94,7 +92,7 @@ public class BasicInternalFrameUI extends InternalFrameUI */ public void internalFrameActivated(InternalFrameEvent e) { - // FIXME: Implement. + frame.getGlassPane().setVisible(false); } /** @@ -124,7 +122,7 @@ public class BasicInternalFrameUI extends InternalFrameUI */ public void internalFrameDeactivated(InternalFrameEvent e) { - // FIXME: Implement. + frame.getGlassPane().setVisible(true); } /** @@ -464,8 +462,6 @@ public class BasicInternalFrameUI extends InternalFrameUI dims.width -= insets.left + insets.right; dims.height -= insets.top + insets.bottom; - frame.getRootPane().getGlassPane().setBounds(0, 0, dims.width, - dims.height); int nh = 0; int sh = 0; int ew = 0; @@ -526,18 +522,6 @@ public class BasicInternalFrameUI extends InternalFrameUI } /** - * This method returns the maximum layout size. - * - * @param c - * The Container to find a maximum layout size for. - * @return The maximum dimensions for the JInternalFrame. - */ - public Dimension maximumLayoutSize(Container c) - { - return preferredLayoutSize(c); - } - - /** * Th8is method returns the preferred layout size. * * @param c @@ -891,40 +875,12 @@ public class BasicInternalFrameUI extends InternalFrameUI * This helper class listens for PropertyChangeEvents from the * JInternalFrame. */ - public class InternalFramePropertyChangeListener implements - PropertyChangeListener, VetoableChangeListener + public class InternalFramePropertyChangeListener + implements PropertyChangeListener { /** * This method is called when one of the JInternalFrame's properties change. - * This method is to allow JInternalFrame to veto an attempt to close the - * internal frame. This allows JInternalFrame to honour its - * defaultCloseOperation if that is DO_NOTHING_ON_CLOSE. - */ - public void vetoableChange(PropertyChangeEvent e) - throws PropertyVetoException - { - if (e.getPropertyName().equals(JInternalFrame.IS_CLOSED_PROPERTY)) - { - if (frame.getDefaultCloseOperation() == JInternalFrame.HIDE_ON_CLOSE) - { - frame.setVisible(false); - frame.getDesktopPane().repaint(); - throw new PropertyVetoException( - "close operation is HIDE_ON_CLOSE\n", - e); - } - else if (frame.getDefaultCloseOperation() == JInternalFrame.DISPOSE_ON_CLOSE) - closeFrame(frame); - else - throw new PropertyVetoException( - "close operation is DO_NOTHING_ON_CLOSE\n", - e); - } - } - - /** - * This method is called when one of the JInternalFrame's properties change. * * @param evt * The PropertyChangeEvent. @@ -1091,13 +1047,6 @@ public class BasicInternalFrameUI extends InternalFrameUI */ protected PropertyChangeListener propertyChangeListener; - /** - * The VetoableChangeListener. Listens to PropertyChangeEvents - * from the JInternalFrame and allows the JInternalFrame to - * veto attempts to close it. - */ - private VetoableChangeListener internalFrameVetoableChangeListener; - /** The InternalFrameListener that listens to the JInternalFrame. */ private transient BasicInternalFrameListener internalFrameListener; @@ -1165,14 +1114,15 @@ public class BasicInternalFrameUI extends InternalFrameUI { frame = (JInternalFrame) c; - ((JComponent) frame.getRootPane().getGlassPane()).setOpaque(false); - frame.getRootPane().getGlassPane().setVisible(true); - installDefaults(); installListeners(); installComponents(); installKeyboardActions(); + ((JComponent) frame.getRootPane().getGlassPane()).setOpaque(false); + if (! frame.isSelected()) + frame.getRootPane().getGlassPane().setVisible(true); + frame.setOpaque(true); frame.invalidate(); } @@ -1205,8 +1155,6 @@ public class BasicInternalFrameUI extends InternalFrameUI frame.setLayout(internalFrameLayout); LookAndFeel.installBorder(frame, "InternalFrame.border"); frame.setFrameIcon(UIManager.getIcon("InternalFrame.icon")); - // InternalFrames are invisible by default. - frame.setVisible(false); } /** @@ -1238,13 +1186,11 @@ public class BasicInternalFrameUI extends InternalFrameUI borderListener = createBorderListener(frame); componentListener = createComponentListener(); propertyChangeListener = createPropertyChangeListener(); - internalFrameVetoableChangeListener = new InternalFramePropertyChangeListener(); frame.addMouseListener(borderListener); frame.addMouseMotionListener(borderListener); frame.addInternalFrameListener(internalFrameListener); frame.addPropertyChangeListener(propertyChangeListener); - frame.addVetoableChangeListener(internalFrameVetoableChangeListener); frame.getRootPane().getGlassPane().addMouseListener(glassPaneDispatcher); frame.getRootPane().getGlassPane().addMouseMotionListener(glassPaneDispatcher); } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java index fd4cff56895..d0964f4733e 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java @@ -53,6 +53,7 @@ import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.LabelUI; +import javax.swing.text.View; /** * This is the Basic Look and Feel class for the JLabel. One BasicLabelUI @@ -64,11 +65,22 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener protected static BasicLabelUI labelUI; /** + * These fields hold the rectangles for the whole label, + * the icon and the text. + */ + private Rectangle vr; + private Rectangle ir; + private Rectangle tr; + + /** * Creates a new BasicLabelUI object. */ public BasicLabelUI() { super(); + vr = new Rectangle(); + ir = new Rectangle(); + tr = new Rectangle(); } /** @@ -99,13 +111,11 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener public Dimension getPreferredSize(JComponent c) { JLabel lab = (JLabel) c; - Rectangle vr = new Rectangle(); - Rectangle ir = new Rectangle(); - Rectangle tr = new Rectangle(); Insets insets = lab.getInsets(); FontMetrics fm = lab.getFontMetrics(lab.getFont()); layoutCL(lab, fm, lab.getText(), lab.getIcon(), vr, ir, tr); - Rectangle cr = tr.union(ir); + Rectangle cr = SwingUtilities.computeUnion(tr.x, tr.y, tr.width, tr.height, + ir); return new Dimension(insets.left + cr.width + insets.right, insets.top + cr.height + insets.bottom); @@ -148,11 +158,6 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener public void paint(Graphics g, JComponent c) { JLabel b = (JLabel) c; - - Rectangle tr = new Rectangle(); - Rectangle ir = new Rectangle(); - Rectangle vr = new Rectangle(); - FontMetrics fm = g.getFontMetrics(); vr = SwingUtilities.calculateInnerArea(c, vr); @@ -168,13 +173,21 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener if (icon != null) icon.paintIcon(b, g, ir.x, ir.y); - if (text != null && !text.equals("")) - { - if (b.isEnabled()) - paintEnabledText(b, g, text, tr.x, tr.y + fm.getAscent()); - else - paintDisabledText(b, g, text, tr.x, tr.y + fm.getAscent()); - } + Object htmlRenderer = b.getClientProperty(BasicHTML.propertyKey); + if (htmlRenderer == null) + { + if (text != null && !text.equals("")) + { + if (b.isEnabled()) + paintEnabledText(b, g, text, tr.x, tr.y + fm.getAscent()); + else + paintDisabledText(b, g, text, tr.x, tr.y + fm.getAscent()); + } + } + else + { + ((View) htmlRenderer).paint(g, tr); + } } /** @@ -312,7 +325,7 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener */ protected void installComponents(JLabel c) { - //FIXME: fix javadoc + implement. + BasicHTML.updateRenderer(c, c.getText()); } /** @@ -322,7 +335,8 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener */ protected void uninstallComponents(JLabel c) { - //FIXME: fix javadoc + implement. + c.putClientProperty(BasicHTML.propertyKey, null); + c.putClientProperty(BasicHTML.documentBaseKey, null); } /** @@ -402,6 +416,11 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener */ public void propertyChange(PropertyChangeEvent e) { - // What to do here? + if (e.getPropertyName().equals("text")) + { + String text = (String) e.getNewValue(); + JLabel l = (JLabel) e.getSource(); + BasicHTML.updateRenderer(l, text); + } } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java index 00d157a62c4..19dfe21f889 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java @@ -64,6 +64,7 @@ import javax.swing.ListCellRenderer; import javax.swing.ListModel; import javax.swing.ListSelectionModel; import javax.swing.LookAndFeel; +import javax.swing.SwingUtilities; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.event.ListDataEvent; @@ -135,6 +136,7 @@ public class BasicListUI extends ListUI */ public void contentsChanged(ListDataEvent e) { + updateLayoutStateNeeded |= modelChanged; list.revalidate(); } @@ -145,6 +147,7 @@ public class BasicListUI extends ListUI */ public void intervalAdded(ListDataEvent e) { + updateLayoutStateNeeded |= modelChanged; list.revalidate(); } @@ -155,6 +158,7 @@ public class BasicListUI extends ListUI */ public void intervalRemoved(ListDataEvent e) { + updateLayoutStateNeeded |= modelChanged; list.revalidate(); } } @@ -541,17 +545,21 @@ public class BasicListUI extends ListUI */ public void propertyChange(PropertyChangeEvent e) { - if (e.getSource() == BasicListUI.this.list) + if (e.getPropertyName().equals("model")) { if (e.getOldValue() != null && e.getOldValue() instanceof ListModel) - ((ListModel) e.getOldValue()).removeListDataListener(BasicListUI.this.listDataListener); - + { + ListModel oldModel = (ListModel) e.getOldValue(); + oldModel.removeListDataListener(listDataListener); + } if (e.getNewValue() != null && e.getNewValue() instanceof ListModel) - ((ListModel) e.getNewValue()).addListDataListener(BasicListUI.this.listDataListener); + { + ListModel newModel = (ListModel) e.getNewValue(); + newModel.addListDataListener(BasicListUI.this.listDataListener); + } + + updateLayoutStateNeeded |= modelChanged; } - // Update the updateLayoutStateNeeded flag. - if (e.getPropertyName().equals("model")) - updateLayoutStateNeeded |= modelChanged; else if (e.getPropertyName().equals("selectionModel")) updateLayoutStateNeeded |= selectionModelChanged; else if (e.getPropertyName().equals("font")) @@ -720,14 +728,20 @@ public class BasicListUI extends ListUI int minIndex = Math.min(index1, index2); int maxIndex = Math.max(index1, index2); Point loc = indexToLocation(list, minIndex); - Rectangle bounds = new Rectangle(loc.x, loc.y, cellWidth, + + // When the layoutOrientation is VERTICAL, then the width == the list + // width. Otherwise the cellWidth field is used. + int width = cellWidth; + if (l.getLayoutOrientation() == JList.VERTICAL) + width = l.getWidth(); + + Rectangle bounds = new Rectangle(loc.x, loc.y, width, getCellHeight(minIndex)); for (int i = minIndex + 1; i <= maxIndex; i++) { Point hiLoc = indexToLocation(list, i); - Rectangle hibounds = new Rectangle(hiLoc.x, hiLoc.y, cellWidth, - getCellHeight(i)); - bounds = bounds.union(hibounds); + bounds = SwingUtilities.computeUnion(hiLoc.x, hiLoc.y, width, + getCellHeight(i), bounds); } return bounds; @@ -883,8 +897,6 @@ public class BasicListUI extends ListUI Dimension dim = flyweight.getPreferredSize(); cellWidth = Math.max(cellWidth, dim.width); } - if (list.getLayoutOrientation() == JList.VERTICAL) - cellWidth = Math.max(cellWidth, list.getSize().width); } } @@ -894,7 +906,7 @@ public class BasicListUI extends ListUI */ protected void maybeUpdateLayoutState() { - if (updateLayoutStateNeeded != 0 || !list.isValid()) + if (updateLayoutStateNeeded != 0) { updateLayoutState(); updateLayoutStateNeeded = 0; diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java b/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java index f5217be1ff6..3451224beeb 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java @@ -38,15 +38,24 @@ 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; import java.awt.Dimension; import java.awt.Font; +import java.awt.Toolkit; +import java.awt.event.AWTEventListener; import java.awt.event.ActionEvent; +import java.awt.event.MouseEvent; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.util.Enumeration; +import java.util.Iterator; import java.util.ResourceBundle; +import java.util.Set; +import java.util.WeakHashMap; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; @@ -57,8 +66,11 @@ import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.BorderFactory; +import javax.swing.JPopupMenu; import javax.swing.KeyStroke; import javax.swing.LookAndFeel; +import javax.swing.MenuSelectionManager; +import javax.swing.SwingUtilities; import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.border.BevelBorder; @@ -77,6 +89,96 @@ import javax.swing.plaf.InsetsUIResource; public abstract class BasicLookAndFeel extends LookAndFeel implements Serializable { + + /** + * Helps closing menu popups when the user clicks outside of any menu area. + * This is implemented as an AWTEventListener that listens on the event + * queue directly, grabs all mouse events from there and finds out of they + * are targetted at a menu/submenu/menubar or not. If not, + * the MenuSelectionManager is messaged to close the currently opened menus, + * if any. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class PopupHelper implements AWTEventListener + { + + /** + * Registered popups for autoclose. + */ + private WeakHashMap autoClosePopups = new WeakHashMap(); + + /** + * Receives an event from the event queue. + * + * @param event + */ + public void eventDispatched(AWTEvent event) + { + if (event instanceof MouseEvent) + { + MouseEvent mouseEvent = (MouseEvent) event; + if (mouseEvent.getID() == MouseEvent.MOUSE_PRESSED) + mousePressed(mouseEvent); + } + } + + /** + * Handles mouse pressed events from the event queue. + * + * @param ev the mouse pressed event + */ + private void mousePressed(MouseEvent ev) + { + // Autoclose all menus managed by the MenuSelectionManager. + MenuSelectionManager m = MenuSelectionManager.defaultManager(); + Component target = ev.getComponent(); + if (target instanceof Container) + target = ((Container) target).findComponentAt(ev.getPoint()); + if (! m.isComponentPartOfCurrentMenu(target)) + m.clearSelectedPath(); + + // Handle other registered popup instances, like ComboBox popups. + autoClosePopups(ev, target); + } + + /** + * Registers Popup and its content to be autoclosed when a mouseclick + * occurs outside of the popup. + * + * @param popup the popup to be autoclosed when clicked outside + */ + void registerForAutoClose(JPopupMenu popup) + { + autoClosePopups.put(popup, null); + } + + /** + * Automatically closes all popups that are not 'hit' by the mouse event. + * + * @param ev the mouse event + * @param target the target of the mouse event + */ + private void autoClosePopups(MouseEvent ev, Component target) + { + if (autoClosePopups.size() != 0) + { + Set popups = autoClosePopups.keySet(); + Iterator i = popups.iterator(); + while (i.hasNext()) + { + JPopupMenu popup = (JPopupMenu) i.next(); + if (!(target == popup + || SwingUtilities.isDescendingFrom(target, popup))) + { + popup.setVisible(false); + i.remove(); + } + } + } + } + } + /** * An action that can play an audio file. * @@ -137,6 +239,11 @@ public abstract class BasicLookAndFeel extends LookAndFeel static final long serialVersionUID = -6096995660290287879L; + /** + * Helps closing menu popups when user clicks outside of the menu area. + */ + private transient PopupHelper popupHelper; + private ActionMap audioActionMap; /** @@ -1023,6 +1130,8 @@ public abstract class BasicLookAndFeel extends LookAndFeel "SplitPane.dividerSize", new Integer(7), "SplitPane.highlight", new ColorUIResource(highLight), "SplitPane.shadow", new ColorUIResource(shadow), + "SplitPaneDivider.border", BasicBorders.getSplitPaneDividerBorder(), + "SplitPaneDivider.draggingColor", new ColorUIResource(Color.DARK_GRAY), "TabbedPane.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] { "ctrl PAGE_DOWN","navigatePageDown", "ctrl PAGE_UP", "navigatePageUp", @@ -1520,4 +1629,36 @@ public abstract class BasicLookAndFeel extends LookAndFeel } } + /** + * Initializes the Look and Feel. + */ + public void initialize() + { + Toolkit toolkit = Toolkit.getDefaultToolkit(); + popupHelper = new PopupHelper(); + toolkit.addAWTEventListener(popupHelper, AWTEvent.MOUSE_EVENT_MASK); + } + + /** + * Uninitializes the Look and Feel. + */ + public void uninitialize() + { + Toolkit toolkit = Toolkit.getDefaultToolkit(); + toolkit.removeAWTEventListener(popupHelper); + popupHelper = null; + } + + /** + * Registers a JPopupMenu for autoclosing when a mouseclick occurs outside + * of the JPopupMenu. This must be called when the popup gets opened. The + * popup is unregistered from autoclosing as soon as it either got closed + * by this helper, or when it has been garbage collected. + * + * @param popup the popup menu to autoclose + */ + void registerForAutoClose(JPopupMenu popup) + { + popupHelper.registerForAutoClose(popup); + } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java index 63f0ce2068b..9166c49ee83 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java @@ -610,8 +610,7 @@ public class BasicMenuItemUI extends MenuItemUI Font f = m.getFont(); g.setFont(f); FontMetrics fm = g.getFontMetrics(f); - SwingUtilities.calculateInnerArea(m, br); - SwingUtilities.calculateInsetArea(br, m.getInsets(), vr); + SwingUtilities.calculateInnerArea(m, vr); paintBackground(g, m, background); /* diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java index e15a17bab28..6ecd06b3988 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java @@ -37,28 +37,20 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import java.awt.AWTEvent; import java.awt.Component; -import java.awt.Container; -import java.awt.Cursor; import java.awt.Dimension; -import java.awt.Point; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.MouseEvent; import javax.swing.BoxLayout; import javax.swing.JComponent; -import javax.swing.JLayeredPane; -import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.LookAndFeel; import javax.swing.MenuElement; import javax.swing.MenuSelectionManager; -import javax.swing.RootPaneContainer; import javax.swing.SwingUtilities; -import javax.swing.event.MouseInputListener; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import javax.swing.plaf.ComponentUI; @@ -73,9 +65,6 @@ public class BasicPopupMenuUI extends PopupMenuUI /* popupMenu for which this UI delegate is for*/ protected JPopupMenu popupMenu; - /* MouseInputListener listens to mouse events. Package private for inner classes. */ - static transient MouseInputListener mouseInputListener; - /* PopupMenuListener listens to popup menu events fired by JPopupMenu*/ private transient PopupMenuListener popupMenuListener; @@ -270,30 +259,9 @@ public class BasicPopupMenuUI extends PopupMenuUI // remove listener that listens to component events fired // by the top - level window that this popup belongs to. Component invoker = popupMenu.getInvoker(); - - RootPaneContainer rootContainer = (RootPaneContainer) SwingUtilities - .getRoot(invoker); + Component rootContainer = SwingUtilities.getRoot(invoker); if (rootContainer != null) - { - ((Container) rootContainer).removeComponentListener(topWindowListener); - - // If this popup menu is the last popup menu visible on the screen, - // then - // stop interrupting mouse events in the glass pane before hiding this - // last popup menu. - boolean topLevelMenu = (popupMenu.getInvoker() instanceof JMenu) - && ((JMenu) popupMenu.getInvoker()).isTopLevelMenu(); - - if (topLevelMenu || !(popupMenu.getInvoker() instanceof MenuElement)) - { - // set glass pane not to interrupt mouse events and remove - // mouseInputListener - Container glassPane = (Container) rootContainer.getGlassPane(); - glassPane.setVisible(false); - glassPane.removeMouseListener(mouseInputListener); - mouseInputListener = null; - } - } + rootContainer.removeComponentListener(topWindowListener); } /** @@ -307,20 +275,8 @@ public class BasicPopupMenuUI extends PopupMenuUI // 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(); - RootPaneContainer rootContainer = (RootPaneContainer) SwingUtilities - .getRoot(invoker); - ((Container) rootContainer).addComponentListener(topWindowListener); - - // Set the glass pane to interrupt all mouse events originating in root - // container - if (mouseInputListener == null) - { - Container glassPane = (Container) rootContainer.getGlassPane(); - glassPane.setVisible(true); - mouseInputListener = new MouseInputHandler(rootContainer); - glassPane.addMouseListener(mouseInputListener); - glassPane.addMouseMotionListener(mouseInputListener); - } + Component rootContainer = SwingUtilities.getRoot(invoker); + rootContainer.addComponentListener(topWindowListener); // if this popup menu is a free floating popup menu, // then by default its first element should be always selected when @@ -399,275 +355,4 @@ public class BasicPopupMenuUI extends PopupMenuUI } } - /** - * MouseInputHandler listens to all mouse events originated in the root - * container. This class is responsible for closing menu hierarchy when the - * user presses mouse over any component that do not belong to the current - * menu hierarchy. This is acomplished by interrupting all mouse event in - * the glass pane and checking if other component was pressed while menu - * was open, before redestributing events further to intended components - */ - private class MouseInputHandler implements MouseInputListener - { - private JLayeredPane layeredPane; - private Container glassPane; - private Cursor nativeCursor; - private transient Component mouseEventTarget; - private transient Component pressedComponent; - private transient Component lastComponentEntered; - private transient Component tempComponent; - private transient int pressCount; - - /** - * Creates a new MouseInputHandler object. - * - * @param c the top most root container - */ - public MouseInputHandler(RootPaneContainer c) - { - layeredPane = c.getLayeredPane(); - glassPane = (Container) c.getGlassPane(); - } - - /** - * Handles mouse clicked event - * - * @param e Mouse event - */ - public void mouseClicked(MouseEvent e) - { - handleEvent(e); - } - - /** - * Handles mouseDragged event - * - * @param e MouseEvent - */ - public void mouseDragged(MouseEvent e) - { - handleEvent(e); - } - - /** - * Handles mouseEntered event - * - * @param e MouseEvent - */ - public void mouseEntered(MouseEvent e) - { - handleEvent(e); - } - - /** - * Handles mouseExited event - * - * @param e MouseEvent - */ - public void mouseExited(MouseEvent e) - { - handleEvent(e); - } - - /** - * Handles mouse moved event - * - * @param e MouseEvent - */ - public void mouseMoved(MouseEvent e) - { - handleEvent(e); - } - - /** - * Handles mouse pressed event - * - * @param e MouseEvent - */ - public void mousePressed(MouseEvent e) - { - handleEvent(e); - } - - /** - * Handles mouse released event - * - * @param e MouseEvent - */ - public void mouseReleased(MouseEvent e) - { - handleEvent(e); - } - - /* - * This method determines component that was intended to received mouse - * event, before it was interrupted within the glass pane. This method - * also redispatches mouse entered and mouse exited events to the - * appropriate components. This code is slightly modified code from - * Container.LightweightDispatcher class, which is private inside - * Container class and cannot be used here. - */ - public void acquireComponentForMouseEvent(MouseEvent me) - { - int x = me.getX(); - int y = me.getY(); - - // Find the candidate which should receive this event. - Component parent = layeredPane; - Component candidate = null; - Point p = me.getPoint(); - while ((candidate == null) && (parent != null)) - { - p = SwingUtilities.convertPoint(glassPane, p.x, p.y, parent); - 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 == layeredPane) - candidate = null; - - // If our candidate is new, inform the old target we're leaving. - if ((lastComponentEntered != null) && lastComponentEntered.isShowing() - && (lastComponentEntered != candidate)) - { - // Old candidate could have been removed from - // the layeredPane so we check first. - if (SwingUtilities.isDescendingFrom(lastComponentEntered, layeredPane)) - { - Point tp = SwingUtilities.convertPoint(layeredPane, x, y, - lastComponentEntered); - MouseEvent exited = new MouseEvent(lastComponentEntered, - MouseEvent.MOUSE_EXITED, - me.getWhen(), - me.getModifiersEx(), tp.x, - tp.y, me.getClickCount(), - me.isPopupTrigger(), - me.getButton()); - - tempComponent = lastComponentEntered; - lastComponentEntered = null; - tempComponent.dispatchEvent(exited); - } - - lastComponentEntered = null; - } - - // If we have a candidate, maybe enter it. - if (candidate != null) - { - mouseEventTarget = candidate; - - if (candidate.isLightweight() && candidate.isShowing() - && (candidate != layeredPane) - && (candidate != lastComponentEntered)) - { - lastComponentEntered = mouseEventTarget; - - Point cp = SwingUtilities.convertPoint(layeredPane, 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 - if (SwingUtilities.isDescendingFrom(pressedComponent, layeredPane)) - 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; - } - } - } - - /* - * This method handles mouse events interrupted by glassPane. It - * redispatches the mouse events appropriately to the intended components. - * The code in this method is also taken from - * Container.LightweightDispatcher class. The code is slightly modified - * to handle the case when mouse is released over non-menu component. In - * this case this method closes current menu hierarchy before - * redispatching the event further. - */ - public void handleEvent(AWTEvent e) - { - if (e instanceof MouseEvent) - { - MouseEvent me = (MouseEvent) e; - - acquireComponentForMouseEvent(me); - - // Avoid dispatching ENTERED and EXITED events twice. - if (mouseEventTarget != null && mouseEventTarget.isShowing() - && (e.getID() != MouseEvent.MOUSE_ENTERED) - && (e.getID() != MouseEvent.MOUSE_EXITED)) - { - MouseEvent newEvt = SwingUtilities.convertMouseEvent(glassPane, - me, - mouseEventTarget); - - mouseEventTarget.dispatchEvent(newEvt); - - // If mouse was clicked over the component that is not part - // of menu hierarchy,then must close the menu hierarchy */ - if (e.getID() == MouseEvent.MOUSE_RELEASED) - { - boolean partOfMenuHierarchy = false; - MenuSelectionManager manager = MenuSelectionManager - .defaultManager(); - - partOfMenuHierarchy = manager.isComponentPartOfCurrentMenu(mouseEventTarget); - - if (! partOfMenuHierarchy) - manager.clearSelectedPath(); - } - - 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; - } - } - } - } - } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java index 8af5ff7f95c..f8f62e15651 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java @@ -45,7 +45,6 @@ import javax.swing.JMenuItem; import javax.swing.MenuElement; import javax.swing.MenuSelectionManager; import javax.swing.UIDefaults; -import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java index 2a698e8a162..28e3b67c1a5 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java @@ -1,4 +1,4 @@ -/* BasicPanelUI.java -- +/* BasicRootPaneUI.java -- Copyright (C) 2002, 2004 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -43,7 +43,6 @@ import java.beans.PropertyChangeListener; import javax.swing.JComponent; import javax.swing.JRootPane; -import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.RootPaneUI; @@ -75,8 +74,7 @@ public class BasicRootPaneUI extends RootPaneUI */ protected void installDefaults(JRootPane rp) { - // Is this ok? - rp.setBackground(UIManager.getColor("control")); + // TODO: What to do here, if anything? (might be a hook method) } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java index a2f5b82dbec..c8713c934dd 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java @@ -653,19 +653,17 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL) { - width += incrButton.getPreferredSize().getWidth(); - width += decrButton.getPreferredSize().getWidth(); - - width += (scrollbar.getMaximum() - scrollbar.getMinimum()); - height = UIManager.getInt("ScrollBar.width"); + width += incrButton.getPreferredSize().getWidth(); + width += decrButton.getPreferredSize().getWidth(); + width += 16; + height = UIManager.getInt("ScrollBar.width"); } else { - height += incrButton.getPreferredSize().getHeight(); - height += decrButton.getPreferredSize().getHeight(); - - height += (scrollbar.getMaximum() - scrollbar.getMinimum()); - width = UIManager.getInt("ScrollBar.width"); + height += incrButton.getPreferredSize().getHeight(); + height += decrButton.getPreferredSize().getHeight(); + height += 16; + width = UIManager.getInt("ScrollBar.width"); } Insets insets = scrollbar.getInsets(); @@ -721,18 +719,6 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected void installComponents() { - if (incrButton != null) - scrollbar.add(incrButton); - if (decrButton != null) - scrollbar.add(decrButton); - } - - /** - * This method installs the defaults for the scrollbar specified by the - * Basic Look and Feel. - */ - protected void installDefaults() - { int orientation = scrollbar.getOrientation(); switch (orientation) { @@ -746,6 +732,18 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, break; } + if (incrButton != null) + scrollbar.add(incrButton); + if (decrButton != null) + scrollbar.add(decrButton); + } + + /** + * This method installs the defaults for the scrollbar specified by the + * Basic Look and Feel. + */ + protected void installDefaults() + { LookAndFeel.installColors(scrollbar, "ScrollBar.background", "ScrollBar.foreground"); LookAndFeel.installBorder(scrollbar, "ScrollBar.border"); diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java index 3b7399eafaa..6f7a41a1d96 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java @@ -1,5 +1,5 @@ -/* SpinnerUI.java -- - Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. +/* BasicSpinnerUI.java -- + Copyright (C) 2003, 2004, 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -41,6 +41,7 @@ package javax.swing.plaf.basic; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; +import java.awt.Font; import java.awt.Insets; import java.awt.LayoutManager; import java.awt.event.ActionEvent; @@ -59,22 +60,21 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.SpinnerUI; /** - * DOCUMENT ME! + * A UI delegate for the {@link JSpinner} component. * * @author Ka-Hing Cheung * - * @see javax.swing.JSpinner * @since 1.4 */ public class BasicSpinnerUI extends SpinnerUI { /** - * Creates a new <code>ComponentUI</code> for the specified + * Creates a new <code>BasicSpinnerUI</code> for the specified * <code>JComponent</code> * - * @param c DOCUMENT ME! + * @param c the component (ignored). * - * @return a ComponentUI + * @return A new instance of {@link BasicSpinnerUI}. */ public static ComponentUI createUI(JComponent c) { @@ -144,14 +144,15 @@ public class BasicSpinnerUI extends SpinnerUI { return new PropertyChangeListener() { - public void propertyChange(PropertyChangeEvent evt) - { - // FIXME: Add check for enabled property change. Need to - // disable the buttons. - if ("editor".equals(evt.getPropertyName())) - BasicSpinnerUI.this.replaceEditor((JComponent) evt.getOldValue(), - (JComponent) evt.getNewValue()); - } + public void propertyChange(PropertyChangeEvent event) + { + // FIXME: Add check for enabled property change. Need to + // disable the buttons. + if ("editor".equals(event.getPropertyName())) + BasicSpinnerUI.this.replaceEditor((JComponent) event.getOldValue(), + (JComponent) event.getNewValue()); + // FIXME: Handle 'font' property change + } }; } @@ -169,6 +170,12 @@ public class BasicSpinnerUI extends SpinnerUI LookAndFeel.installColorsAndFont(spinner, "Spinner.background", "Spinner.foreground", "Spinner.font"); LookAndFeel.installBorder(spinner, "Spinner.border"); + JComponent e = spinner.getEditor(); + if (e instanceof JSpinner.DefaultEditor) + { + JSpinner.DefaultEditor de = (JSpinner.DefaultEditor) e; + de.getTextField().setBorder(null); + } spinner.setLayout(createLayout()); spinner.setOpaque(true); } @@ -352,7 +359,8 @@ public class BasicSpinnerUI extends SpinnerUI private PropertyChangeListener listener = createPropertyChangeListener(); /** - * DOCUMENT ME! + * A layout manager for the {@link JSpinner} component. The spinner has + * three subcomponents: an editor, a 'next' button and a 'previous' button. */ private class DefaultLayoutManager implements LayoutManager { @@ -365,58 +373,52 @@ public class BasicSpinnerUI extends SpinnerUI { synchronized (parent.getTreeLock()) { - Insets i = parent.getInsets(); - boolean l2r = parent.getComponentOrientation().isLeftToRight(); - /* - -------------- -------------- - | | n | | n | | - | e | - | or | - | e | - | | p | | p | | - -------------- -------------- - */ - Dimension e = minSize(editor); - Dimension n = minSize(next); - Dimension p = minSize(previous); - Dimension s = spinner.getPreferredSize(); - - int x = l2r ? i.left : i.right; - int y = i.top; - int w = Math.max(p.width, n.width); - int h = Math.max(p.height, n.height); - h = Math.max(h, e.height / 2); - int e_width = s.width - w; - - if (l2r) - { - setBounds(editor, x, y + (s.height - e.height) / 2, e_width, - e.height); - x += e_width; - - setBounds(next, x, y, w, h); - y += h; - - setBounds(previous, x, y, w, h); - } - else - { - setBounds(next, x, y + (s.height - e.height) / 2, w, h); - y += h; - - setBounds(previous, x, y, w, h); - x += w; - y -= h; - - setBounds(editor, x, y, e_width, e.height); - } + Insets i = parent.getInsets(); + boolean l2r = parent.getComponentOrientation().isLeftToRight(); + /* + -------------- -------------- + | | n | | n | | + | e | - | or | - | e | + | | p | | p | | + -------------- -------------- + */ + Dimension e = prefSize(editor); + Dimension n = prefSize(next); + Dimension p = prefSize(previous); + Dimension s = spinner.getPreferredSize(); + + int x = l2r ? i.left : i.right; + int y = i.top; + int w = Math.max(p.width, n.width); + int h = e.height / 2; + int e_width = s.width - w - i.left - i.right; + + if (l2r) + { + setBounds(editor, x, y, e_width, 2 * h); + x += e_width; + setBounds(next, x, y, w, h); + y += h; + setBounds(previous, x, y, w, h); + } + else + { + setBounds(next, x, y + (s.height - e.height) / 2, w, h); + y += h; + setBounds(previous, x, y + (s.height - e.height) / 2, w, h); + x += w; + y -= h; + setBounds(editor, x, y, e_width, e.height); + } } } /** - * DOCUMENT ME! + * Calculates the minimum layout size. * - * @param parent DOCUMENT ME! + * @param parent the parent. * - * @return DOCUMENT ME! + * @return The minimum layout size. */ public Dimension minimumLayoutSize(Container parent) { @@ -424,36 +426,32 @@ public class BasicSpinnerUI extends SpinnerUI if (editor != null) { - Dimension tmp = editor.getMinimumSize(); - d.width += tmp.width; - d.height = tmp.height; + Dimension tmp = editor.getMinimumSize(); + d.width += tmp.width; + d.height = tmp.height; } int nextWidth = 0; int previousWidth = 0; - int otherHeight = 0; if (next != null) { - Dimension tmp = next.getMinimumSize(); - nextWidth = tmp.width; - otherHeight += tmp.height; + Dimension tmp = next.getMinimumSize(); + nextWidth = tmp.width; } if (previous != null) { - Dimension tmp = previous.getMinimumSize(); - previousWidth = tmp.width; - otherHeight += tmp.height; + Dimension tmp = previous.getMinimumSize(); + previousWidth = tmp.width; } - d.height = Math.max(d.height, otherHeight); d.width += Math.max(nextWidth, previousWidth); return d; } /** - * DOCUMENT ME! + * Returns the preferred layout size of the container. * * @param parent DOCUMENT ME! * @@ -465,31 +463,29 @@ public class BasicSpinnerUI extends SpinnerUI if (editor != null) { - Dimension tmp = editor.getPreferredSize(); - d.width += Math.max(tmp.width, 40); - d.height = tmp.height; + Dimension tmp = editor.getPreferredSize(); + d.width += Math.max(tmp.width, 40); + d.height = tmp.height; } int nextWidth = 0; int previousWidth = 0; - int otherHeight = 0; if (next != null) { - Dimension tmp = next.getPreferredSize(); - nextWidth = tmp.width; - otherHeight += tmp.height; + Dimension tmp = next.getPreferredSize(); + nextWidth = tmp.width; } if (previous != null) { - Dimension tmp = previous.getPreferredSize(); - previousWidth = tmp.width; - otherHeight += tmp.height; + Dimension tmp = previous.getPreferredSize(); + previousWidth = tmp.width; } - d.height = Math.max(d.height, otherHeight); d.width += Math.max(nextWidth, previousWidth); - + Insets insets = parent.getInsets(); + d.width = d.width + insets.left + insets.right; + d.height = d.height + insets.top + insets.bottom; return d; } @@ -501,11 +497,11 @@ public class BasicSpinnerUI extends SpinnerUI public void removeLayoutComponent(Component child) { if (child == editor) - editor = null; + editor = null; else if (child == next) - next = null; + next = null; else if (previous == child) - previous = null; + previous = null; } /** @@ -517,11 +513,11 @@ public class BasicSpinnerUI extends SpinnerUI public void addLayoutComponent(String name, Component child) { if ("Editor".equals(name)) - editor = child; + editor = child; else if ("Next".equals(name)) - next = child; + next = child; else if ("Previous".equals(name)) - previous = child; + previous = child; } /** @@ -531,36 +527,36 @@ public class BasicSpinnerUI extends SpinnerUI * * @return DOCUMENT ME! */ - private Dimension minSize(Component c) + private Dimension prefSize(Component c) { if (c == null) - return new Dimension(); + return new Dimension(); else - return c.getMinimumSize(); + return c.getPreferredSize(); } /** - * DOCUMENT ME! + * Sets the bounds for the specified component. * - * @param c DOCUMENT ME! - * @param x DOCUMENT ME! - * @param y DOCUMENT ME! - * @param w DOCUMENT ME! - * @param h DOCUMENT ME! + * @param c the component. + * @param x the x-coordinate for the top-left of the component bounds. + * @param y the y-coordinate for the top-left of the component bounds. + * @param w the width of the bounds. + * @param h the height of the bounds. */ private void setBounds(Component c, int x, int y, int w, int h) { if (c != null) - c.setBounds(x, y, w, h); + c.setBounds(x, y, w, h); } - /** DOCUMENT ME! */ + /** The editor component. */ private Component editor; - /** DOCUMENT ME! */ + /** The next button. */ private Component next; - /** DOCUMENT ME! */ + /** The previous button. */ private Component previous; } } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java index ff17ff084c2..06d32984efb 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java @@ -38,7 +38,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; @@ -161,31 +160,6 @@ public class BasicSplitPaneDivider extends Container */ transient int currentDividerLocation = 1; - /** DOCUMENT ME! */ - private transient Border tmpBorder = new Border() - { - public Insets getBorderInsets(Component c) - { - return new Insets(2, 2, 2, 2); - } - - public boolean isBorderOpaque() - { - return false; - } - - public void paintBorder(Component c, Graphics g, int x, int y, - int width, int height) - { - Color saved = g.getColor(); - g.setColor(Color.BLACK); - - g.drawRect(x + 2, y + 2, width - 4, height - 4); - - g.setColor(saved); - } - }; - /** * Constructs a new divider. * @@ -196,7 +170,6 @@ public class BasicSplitPaneDivider extends Container setLayout(new DividerLayout()); setBasicSplitPaneUI(ui); setDividerSize(splitPane.getDividerSize()); - setBorder(tmpBorder); } /** @@ -212,8 +185,6 @@ public class BasicSplitPaneDivider extends Container if (splitPane != null) { splitPane.removePropertyChangeListener(this); - splitPane.removeMouseListener(mouseHandler); - splitPane.removeMouseMotionListener(mouseHandler); removeMouseListener(mouseHandler); removeMouseMotionListener(mouseHandler); splitPane = null; @@ -227,8 +198,6 @@ public class BasicSplitPaneDivider extends Container if (splitPane != null) { splitPane.addPropertyChangeListener(this); - splitPane.addMouseListener(mouseHandler); - splitPane.addMouseMotionListener(mouseHandler); addMouseListener(mouseHandler); addMouseMotionListener(mouseHandler); hiddenDivider = splitPaneUI.getNonContinuousLayoutDivider(); diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java index cf31e8b5df1..8a7c9d2a290 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java @@ -62,6 +62,7 @@ import javax.swing.LookAndFeel; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.SplitPaneUI; +import javax.swing.plaf.UIResource; /** * This is the Basic Look and Feel implementation of the SplitPaneUI class. @@ -253,20 +254,21 @@ public class BasicSplitPaneUI extends SplitPaneUI JSplitPane split = (JSplitPane) container; distributeExtraSpace(); Insets insets = split.getInsets(); - int width = getInitialLocation(insets); Dimension dims = split.getSize(); - for (int i = 0; i < components.length; i += 2) - { - if (components[i] == null) - continue; - setComponentToSize(components[i], sizes[i], width, insets, dims); - width += sizes[i]; - } - if (components[1] != null) - { - setComponentToSize(components[1], sizes[1], width, insets, dims); - width += sizes[1]; - } + int loc = getInitialLocation(insets); + int available = getAvailableSize(dims, insets); + sizes[0] = getDividerLocation(split) - loc; + sizes[1] = available - sizes[0] - sizes[2]; + // The size of the divider won't change. + + // Layout component#1. + setComponentToSize(components[0], sizes[0], loc, insets, dims); + // Layout divider. + loc += sizes[0]; + setComponentToSize(components[2], sizes[2], loc, insets, dims); + // Layout component#2. + loc += sizes[2]; + setComponentToSize(components[1], sizes[1], loc, insets, dims); } } @@ -388,6 +390,8 @@ public class BasicSplitPaneUI extends SplitPaneUI { for (int i = 0; i < components.length; i++) resetSizeAt(i); + setDividerLocation(splitPane, + getInitialLocation(splitPane.getInsets()) + sizes[0]); } /** @@ -451,21 +455,7 @@ public class BasicSplitPaneUI extends SplitPaneUI */ void distributeExtraSpace() { - int availSize = getAvailableSize(splitPane.getSize(), - splitPane.getInsets()); - int[] newSizes = new int[3]; - double weight = splitPane.getResizeWeight(); - - int oldLen = sizes[0] + sizes[1]; - - // dividers don't change size. - availSize -= sizes[2] + oldLen; - - int rightAlloc = (int) (availSize * (1 - weight)); - int leftAlloc = availSize - rightAlloc; - - sizes[0] += leftAlloc; - sizes[1] += rightAlloc; + // FIXME: This needs to be reimplemented correctly. } /** @@ -835,8 +825,6 @@ public class BasicSplitPaneUI extends SplitPaneUI if (prop <= 1 && prop >= 0) splitPane.setDividerLocation(prop); } - layoutManager.layoutContainer(splitPane); - splitPane.repaint(); // Don't have to deal with continuous_layout - only // necessary in dragging modes (and it's checked // every time you drag there) @@ -933,6 +921,8 @@ public class BasicSplitPaneUI extends SplitPaneUI /** The JSplitPane that this UI draws. */ protected JSplitPane splitPane; + private int dividerLocation; + /** * Creates a new BasicSplitPaneUI object. */ @@ -992,6 +982,7 @@ public class BasicSplitPaneUI extends SplitPaneUI "SplitPane.foreground"); LookAndFeel.installBorder(splitPane, "SplitPane.border"); divider = createDefaultDivider(); + divider.setBorder(UIManager.getBorder("SplitPaneDivider.border")); resetLayoutManager(); nonContinuousLayoutDivider = createDefaultNonContinuousLayoutDivider(); splitPane.add(divider, JSplitPane.DIVIDER); @@ -1012,8 +1003,10 @@ public class BasicSplitPaneUI extends SplitPaneUI divider = null; nonContinuousLayoutDivider = null; - splitPane.setBackground(null); - splitPane.setBorder(null); + if (splitPane.getBackground() instanceof UIResource) + splitPane.setBackground(null); + if (splitPane.getBorder() instanceof UIResource) + splitPane.setBorder(null); } /** @@ -1219,7 +1212,8 @@ public class BasicSplitPaneUI extends SplitPaneUI if (nonContinuousLayoutDivider == null) { nonContinuousLayoutDivider = new Canvas(); - nonContinuousLayoutDivider.setBackground(Color.DARK_GRAY); + Color c = UIManager.getColor("SplitPaneDivider.draggingColor"); + nonContinuousLayoutDivider.setBackground(c); } return nonContinuousLayoutDivider; } @@ -1298,44 +1292,7 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public void setDividerLocation(JSplitPane jc, int location) { - location = validLocation(location); - Container p = jc.getParent(); - Component right = jc.getRightComponent(); - Dimension rightPrefSize = right == null ? new Dimension(0, 0) - : right.getPreferredSize(); - Dimension size = jc.getSize(); - // check if the size has been set for the splitpane - if (size.width == 0 && size.height == 0) - size = jc.getPreferredSize(); - - if (getOrientation() == 0 && location > size.height) - { - location = size.height; - while (p != null) - { - p.setSize(p.getWidth(), p.getHeight() + rightPrefSize.height); - p = p.getParent(); - } - } - else if (location > size.width) - { - location = size.width; - while (p != null) - { - p.setSize(p.getWidth() + rightPrefSize.width, p.getHeight()); - p = p.getParent(); - } - } - - setLastDragLocation(getDividerLocation(splitPane)); - splitPane.setLastDividerLocation(getDividerLocation(splitPane)); - int[] tmpSizes = layoutManager.getSizes(); - tmpSizes[0] = location - - layoutManager.getInitialLocation(splitPane.getInsets()); - tmpSizes[1] = layoutManager.getAvailableSize(splitPane.getSize(), - splitPane.getInsets()) - - tmpSizes[0]; - layoutManager.setSizes(tmpSizes); + dividerLocation = location; splitPane.revalidate(); splitPane.repaint(); } @@ -1349,8 +1306,7 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public int getDividerLocation(JSplitPane jc) { - return layoutManager.sizes[0] - + layoutManager.getInitialLocation(splitPane.getInsets()); + return dividerLocation; } /** @@ -1365,7 +1321,7 @@ public class BasicSplitPaneUI extends SplitPaneUI { int value = layoutManager.getInitialLocation(jc.getInsets()); if (layoutManager.components[0] != null) - value -= layoutManager.minimumSizeOfComponent(0); + value += layoutManager.minimumSizeOfComponent(0); return value; } @@ -1501,8 +1457,6 @@ public class BasicSplitPaneUI extends SplitPaneUI nonContinuousLayoutDivider.setVisible(true); nonContinuousLayoutDivider.setBounds(divider.getBounds()); } - splitPane.revalidate(); - splitPane.repaint(); } /** @@ -1544,11 +1498,9 @@ public class BasicSplitPaneUI extends SplitPaneUI nonContinuousLayoutDivider.setVisible(false); draggingHW = false; location = validLocation(location); - dragDividerTo(location); splitPane.setDividerLocation(location); splitPane.setLastDividerLocation(beginDragDividerLocation); beginDragDividerLocation = -1; - splitPane.repaint(); } /** diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java index a8f52cef617..5b1e1ff0f60 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java @@ -451,6 +451,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants } } runCount = runs; + if (runCount > tabRuns.length) + expandTabRunsArray(); tabRuns[0] = 0; normalizeTabRuns(tabPlacement, tabCount, start, max); @@ -1025,6 +1027,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants } } runCount = runs; + if (runCount > tabRuns.length) + expandTabRunsArray(); padSelectedTab(tabPlacement, tabPane.getSelectedIndex()); } @@ -1733,9 +1737,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int tabCount = tabPane.getTabCount(); int currRun = 1; - if (tabCount > runCount) - runCount = tabCount; - if (tabCount < 1) return; diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java index 9c8a5ef9598..1e8e39f38c2 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java @@ -39,14 +39,18 @@ exception statement from your version. */ package javax.swing.plaf.basic; import java.awt.Component; +import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import javax.swing.CellRendererPane; import javax.swing.JComponent; import javax.swing.LookAndFeel; +import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.event.MouseInputListener; @@ -57,62 +61,346 @@ import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; +/** + * Basic pluggable look and feel interface for JTableHeader. + */ public class BasicTableHeaderUI extends TableHeaderUI { - + /** + * The width of the space (in both direction) around the column boundary, + * where mouse cursor changes shape into "resize" + */ + static int COLUMN_BOUNDARY_TOLERANCE = 3; + public static ComponentUI createUI(JComponent h) { return new BasicTableHeaderUI(); } - + + /** + * The table header that is using this interface. + */ protected JTableHeader header; + + /** + * The mouse input listener, responsible for mouse manipulations with + * the table header. + */ protected MouseInputListener mouseInputListener; + + /** + * Paint the header cell. + */ protected CellRendererPane rendererPane; + + /** + * The header cell border. + */ protected Border cellBorder; - - public class MouseInputHandler implements MouseInputListener + + /** + * If not null, one of the columns is currently being dragged. + */ + Rectangle draggingHeaderRect; + + /** + * Handles column movement and rearrangement by mouse. The same instance works + * both as mouse listener and the mouse motion listner. + */ + public class MouseInputHandler + implements MouseInputListener { + /** + * If true, the cursor is being already shown in the alternative "resize" + * shape. + */ + boolean showingResizeCursor; + + /** + * The position, from where the cursor is dragged during resizing. Double + * purpose field (absolute value during resizing and relative offset during + * column dragging). + */ + int draggingFrom = - 1; + + /** + * The number of the column being dragged. + */ + int draggingColumnNumber; + + /** + * The previous preferred width of the column. + */ + int prevPrefWidth = - 1; + + /** + * The timer to coalesce column resizing events. + */ + Timer timer; + + /** + * Returns without action, part of the MouseInputListener interface. + */ public void mouseClicked(MouseEvent e) { - // TODO: Implement this properly. + // Nothing to do. } + /** + * If being in the resizing mode, handle resizing. + */ public void mouseDragged(MouseEvent e) { - // TODO: Implement this properly. + TableColumn resizeIt = header.getResizingColumn(); + if (resizeIt != null && header.getResizingAllowed()) + { + // The timer is intialised on demand. + if (timer == null) + { + // The purpose of timer is to coalesce events. If the queue + // is free, the repaint event is fired immediately. + timer = new Timer(1, new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + header.getTable().doLayout(); + } + }); + timer.setRepeats(false); + timer.setCoalesce(true); + } + resizeIt.setPreferredWidth(prevPrefWidth + e.getX() - draggingFrom); + timer.restart(); + } + else if (draggingHeaderRect != null && header.getReorderingAllowed()) + { + draggingHeaderRect.x = e.getX() + draggingFrom; + header.repaint(); + } } + /** + * Returns without action, part of the MouseInputListener interface. + */ public void mouseEntered(MouseEvent e) { - // TODO: Implement this properly. + // Nothing to do. } + /** + * Reset drag information of the column resizing. + */ public void mouseExited(MouseEvent e) { - // TODO: Implement this properly. + if (header.getResizingColumn() != null && header.getResizingAllowed()) + endResizing(); + if (header.getDraggedColumn() != null && header.getReorderingAllowed()) + endDragging(null); } + /** + * Change the mouse cursor if the mouse if above the column boundary. + */ public void mouseMoved(MouseEvent e) { - // TODO: Implement this properly. + // When dragging, the functionality is handled by the mouseDragged. + if (e.getButton() == 0 && header.getResizingAllowed()) + { + TableColumnModel model = header.getColumnModel(); + int n = model.getColumnCount(); + if (n < 2) + // It must be at least two columns to have at least one boundary. + // Otherwise, nothing to do. + return; + + boolean onBoundary = false; + + int x = e.getX(); + int a = x - COLUMN_BOUNDARY_TOLERANCE; + int b = x + COLUMN_BOUNDARY_TOLERANCE; + + int p = 0; + + Scan: for (int i = 0; i < n - 1; i++) + { + p += model.getColumn(i).getWidth(); + + if (p >= a && p <= b) + { + TableColumn column = model.getColumn(i); + onBoundary = true; + + draggingFrom = x; + prevPrefWidth = column.getWidth(); + header.setResizingColumn(column); + break Scan; + } + } + + if (onBoundary != showingResizeCursor) + { + // Change the cursor shape, if needed. + if (onBoundary) + { + + if (p < x) + header.setCursor(Cursor.getPredefinedCursor + (Cursor.W_RESIZE_CURSOR)); + else + header.setCursor(Cursor.getPredefinedCursor + (Cursor.E_RESIZE_CURSOR)); + } + else + { + header.setCursor(Cursor.getDefaultCursor()); + header.setResizingColumn(null); + } + + showingResizeCursor = onBoundary; + } + } } + /** + * Starts the dragging/resizing procedure. + */ public void mousePressed(MouseEvent e) { - // TODO: Implement this properly. + if (header.getResizingAllowed()) + { + TableColumn resizingColumn = header.getResizingColumn(); + if (resizingColumn != null) + { + resizingColumn.setPreferredWidth(resizingColumn.getWidth()); + return; + } + } + + if (header.getReorderingAllowed()) + { + TableColumnModel model = header.getColumnModel(); + int n = model.getColumnCount(); + if (n < 2) + // It must be at least two columns to change the column location. + return; + + boolean onBoundary = false; + + int x = e.getX(); + int p = 0; + int col = - 1; + + Scan: for (int i = 0; i < n; i++) + { + p += model.getColumn(i).getWidth(); + if (p > x) + { + col = i; + break Scan; + } + } + if (col < 0) + return; + + TableColumn dragIt = model.getColumn(col); + header.setDraggedColumn(dragIt); + + draggingFrom = (p - dragIt.getWidth()) - x; + draggingHeaderRect = new Rectangle(header.getHeaderRect(col)); + draggingColumnNumber = col; + } } + /** + * Set all column preferred width to the current width to prevend abrupt + * width changes during the next resize. + */ public void mouseReleased(MouseEvent e) { - // TODO: Implement this properly. + if (header.getResizingColumn() != null && header.getResizingAllowed()) + endResizing(); + if (header.getDraggedColumn() != null && header.getReorderingAllowed()) + endDragging(e); + } + + /** + * Stop resizing session. + */ + void endResizing() + { + TableColumnModel model = header.getColumnModel(); + int n = model.getColumnCount(); + if (n > 2) + { + TableColumn c; + for (int i = 0; i < n; i++) + { + c = model.getColumn(i); + c.setPreferredWidth(c.getWidth()); + } + } + header.setResizingColumn(null); + showingResizeCursor = false; + if (timer != null) + timer.stop(); + header.setCursor(Cursor.getDefaultCursor()); } - } + /** + * Stop the dragging session. + * + * @param e the "mouse release" mouse event, needed to determing the final + * location for the dragged column. + */ + void endDragging(MouseEvent e) + { + header.setDraggedColumn(null); + + // Return if the mouse have left the header area while pressed. + if (e == null) + { + header.repaint(draggingHeaderRect); + draggingHeaderRect = null; + return; + } + else + draggingHeaderRect = null; + + TableColumnModel model = header.getColumnModel(); + + // Find where have we dragged the column. + int x = e.getX(); + int p = 0; + int col = - 1; + int n = model.getColumnCount(); + + Scan: for (int i = 0; i < n; i++) + { + p += model.getColumn(i).getWidth(); + if (p > x) + { + col = i; + break Scan; + } + } + if (col >= 0) + header.getTable().moveColumn(draggingColumnNumber, col); + } + } + + /** + * Create and return the mouse input listener. + * + * @return the mouse listener ({@link MouseInputHandler}, if not overridden. + */ protected MouseInputListener createMouseInputListener() { return new MouseInputHandler(); } - + + /** + * Construct a new BasicTableHeaderUI, create mouse listeners. + */ public BasicTableHeaderUI() { mouseInputListener = createMouseInputListener(); @@ -131,9 +419,15 @@ public class BasicTableHeaderUI extends TableHeaderUI // TODO: Implement this properly. } + /** + * Add the mouse listener and the mouse motion listener to the table + * header. The listeners support table column resizing and rearrangement + * by mouse. + */ protected void installListeners() { header.addMouseListener(mouseInputListener); + header.addMouseMotionListener(mouseInputListener); } public void installUI(JComponent c) @@ -156,10 +450,14 @@ public class BasicTableHeaderUI extends TableHeaderUI { // TODO: Implement this properly. } - + + /** + * Remove the previously installed listeners. + */ protected void uninstallListeners() { header.removeMouseListener(mouseInputListener); + header.removeMouseMotionListener(mouseInputListener); } public void uninstallUI(JComponent c) @@ -168,7 +466,10 @@ public class BasicTableHeaderUI extends TableHeaderUI uninstallKeyboardActions(); uninstallDefaults(); } - + + /** + * Repaint the table header. + */ public void paint(Graphics gfx, JComponent c) { TableColumnModel cmod = header.getColumnModel(); @@ -206,10 +507,26 @@ public class BasicTableHeaderUI extends TableHeaderUI bounds.width, bounds.height); } } - + + // This displays a running rectangle that is much simplier than the total + // animation, as it is seen in Sun's application. + // TODO animate the collumn dragging like in Sun's jre. + if (draggingHeaderRect!=null) + { + gfx.setColor(header.getForeground()); + gfx.drawRect(draggingHeaderRect.x, draggingHeaderRect.y+2, + draggingHeaderRect.width-1, draggingHeaderRect.height-6); + } } - public Dimension getPreferredSize(JComponent c) + /** + * Get the preferred header size. + * + * @param ignored unused + * + * @return the preferred size of the associated header. + */ + public Dimension getPreferredSize(JComponent ignored) { TableColumnModel cmod = header.getColumnModel(); TableCellRenderer defaultRend = header.getDefaultRenderer(); diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java index 18b69120d11..8360a9ec771 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java @@ -58,11 +58,11 @@ import java.beans.PropertyChangeListener; import javax.swing.AbstractAction; import javax.swing.ActionMap; import javax.swing.CellRendererPane; +import javax.swing.DefaultCellEditor; import javax.swing.DefaultListSelectionModel; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JTable; -import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.ListSelectionModel; import javax.swing.LookAndFeel; @@ -74,8 +74,8 @@ 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; -import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import javax.swing.table.TableModel; @@ -193,10 +193,31 @@ public class BasicTableUI extends TableUI colModel.setSelectionInterval(lo_col, hi_col); } } - - public void mouseClicked(MouseEvent e) + + /** + * For the double click, start the cell editor. + */ + public void mouseClicked(MouseEvent e) { - // TODO: What should be done here, if anything? + Point p = e.getPoint(); + int row = table.rowAtPoint(p); + int col = table.columnAtPoint(p); + if (table.isCellEditable(row, col)) + { + // If the cell editor is the default editor, we request the + // number of the required clicks from it. Otherwise, + // require two clicks (double click). + TableCellEditor editor = table.getCellEditor(row, col); + if (editor instanceof DefaultCellEditor) + { + DefaultCellEditor ce = (DefaultCellEditor) editor; + if (e.getClickCount() < ce.getClickCountToStart()) + return; + } + else if (e.getClickCount() < 2) + return; + table.editCellAt(row, col); + } } public void mouseDragged(MouseEvent e) @@ -354,7 +375,8 @@ public class BasicTableUI extends TableUI maxTotalColumnWidth += table.getColumnModel().getColumn(i).getMaxWidth(); if (maxTotalColumnWidth == 0 || table.getRowCount() == 0) return null; - return new Dimension(maxTotalColumnWidth, table.getRowCount()*table.getRowHeight()); + return new Dimension(maxTotalColumnWidth, table.getRowCount()* + (table.getRowHeight()+table.getRowMargin())); } /** @@ -380,7 +402,7 @@ public class BasicTableUI extends TableUI public Dimension getPreferredSize(JComponent comp) { int width = table.getColumnModel().getTotalColumnWidth(); - int height = table.getRowCount() * table.getRowHeight(); + int height = table.getRowCount() * (table.getRowHeight()+table.getRowMargin()); return new Dimension(width, height); } @@ -854,6 +876,10 @@ public class BasicTableUI extends TableUI rowModel.setAnchorSelectionIndex(rowLead); colModel.setAnchorSelectionIndex(colLead); } + else if (command.equals("stopEditing")) + { + table.editingStopped(new ChangeEvent(command)); + } else { // If we're here that means we bound this TableAction class @@ -1185,30 +1211,17 @@ public class BasicTableUI extends TableUI * system beginning at <code>(0,0)</code> in the upper left corner of the * table * @param rend A cell renderer to paint with - * @param data The data to provide to the cell renderer - * @param rowLead The lead selection for the rows of the table. - * @param colLead The lead selection for the columns of the table. */ void paintCell(Graphics g, int row, int col, Rectangle bounds, - TableCellRenderer rend, TableModel data, - int rowLead, int colLead) + TableCellRenderer rend) { Component comp = table.prepareRenderer(rend, row, col); rendererPane.paintComponent(g, comp, table, bounds); - - // FIXME: this is manual painting of the Caret, why doesn't the - // JTextField take care of this itself? - if (comp instanceof JTextField) - { - Rectangle oldClip = g.getClipBounds(); - g.translate(bounds.x, bounds.y); - g.clipRect(0, 0, bounds.width, bounds.height); - ((JTextField)comp).getCaret().paint(g); - g.translate(-bounds.x, -bounds.y); - g.setClip(oldClip); - } } + /** + * Paint the associated table. + */ public void paint(Graphics gfx, JComponent ignored) { int ncols = table.getColumnCount(); @@ -1217,59 +1230,72 @@ public class BasicTableUI extends TableUI return; Rectangle clip = gfx.getClipBounds(); - TableColumnModel cols = table.getColumnModel(); - - int height = table.getRowHeight(); - int x0 = 0, y0 = 0; - int x = x0; - int y = y0; - - Dimension gap = table.getIntercellSpacing(); - int ymax = clip.y + clip.height; - int xmax = clip.x + clip.width; + // Determine the range of cells that are within the clip bounds. + Point p1 = new Point(clip.x, clip.y); + int c0 = table.columnAtPoint(p1); + if (c0 == -1) + c0 = 0; + int r0 = table.rowAtPoint(p1); + if (r0 == -1) + r0 = 0; + Point p2 = new Point(clip.x + clip.width, clip.y + clip.height); + int cn = table.columnAtPoint(p2); + if (cn == -1) + cn = table.getColumnCount() - 1; + int rn = table.rowAtPoint(p2); + if (rn == -1) + rn = table.getRowCount() - 1; + + TableColumnModel cmodel = table.getColumnModel(); + int [] widths = new int[cn+1]; + for (int i = c0; i <=cn ; i++) + { + widths[i] = cmodel.getColumn(i).getWidth(); + } + + Rectangle bounds = table.getCellRect(r0, c0, false); + bounds.height = table.getRowHeight()+table.getRowMargin(); + + // The left boundary of the area being repainted. + int left = bounds.x; + + // The top boundary of the area being repainted. + int top = bounds.y; + + // The bottom boundary of the area being repainted. + int bottom; + + // The cell height. + int height = bounds.height; + // paint the cell contents - for (int c = 0; c < ncols && x < xmax; ++c) + Color grid = table.getGridColor(); + for (int r = r0; r <= rn; ++r) { - y = y0; - TableColumn col = cols.getColumn(c); - int width = col.getWidth(); - int halfGapWidth = gap.width / 2; - int halfGapHeight = gap.height / 2; - for (int r = 0; r < nrows && y < ymax; ++r) + for (int c = c0; c <= cn; ++c) { - Rectangle bounds = new Rectangle(x + halfGapWidth, - y + halfGapHeight + 1, - width - gap.width + 1, - height - gap.height); - if (bounds.intersects(clip)) - { - paintCell(gfx, r, c, bounds, table.getCellRenderer(r, c), - table.getModel(), - table.getSelectionModel().getLeadSelectionIndex(), - table.getColumnModel().getSelectionModel().getLeadSelectionIndex()); - } - y += height; + bounds.width = widths[c]; + paintCell(gfx, r, c, bounds, table.getCellRenderer(r, c)); + bounds.x += widths[c]; } - x += width; + bounds.y += height; + bounds.x = left; } - - // tighten up the x and y max bounds - ymax = y; - xmax = x; - - Color grid = table.getGridColor(); + + bottom = bounds.y; // paint vertical grid lines if (grid != null && table.getShowVerticalLines()) { - x = x0; Color save = gfx.getColor(); gfx.setColor(grid); - for (int c = 0; c < ncols && x < xmax; ++c) + int x = left; + + for (int c = c0; c <= cn; ++c) { - x += cols.getColumn(c).getWidth(); - gfx.drawLine(x, y0, x, ymax); + gfx.drawLine(x, top, x, bottom); + x += widths[c]; } gfx.setColor(save); } @@ -1277,13 +1303,13 @@ public class BasicTableUI extends TableUI // paint horizontal grid lines if (grid != null && table.getShowHorizontalLines()) { - y = y0; Color save = gfx.getColor(); gfx.setColor(grid); - for (int r = 0; r < nrows && y < ymax; ++r) + int y = top; + for (int r = r0; r <= rn; ++r) { + gfx.drawLine(left, y, p2.x, y); y += height; - gfx.drawLine(x0, y, xmax, y); } gfx.setColor(save); } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java index fc388948419..beb1a6dfeac 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java @@ -1,5 +1,5 @@ /* BasicTextUI.java -- - Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -46,15 +46,11 @@ import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.Shape; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; -import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.InputMap; @@ -70,6 +66,7 @@ import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.InputMapUIResource; import javax.swing.plaf.TextUI; import javax.swing.plaf.UIResource; +import javax.swing.text.AbstractDocument; import javax.swing.text.BadLocationException; import javax.swing.text.Caret; import javax.swing.text.DefaultCaret; @@ -82,6 +79,7 @@ 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; @@ -161,11 +159,11 @@ public abstract class BasicTextUI extends TextUI * Indicates that the preferences of one of the child view has changed. * This calls revalidate on the text component. * - * @param view the child view which's preference has changed + * @param v the child view which's preference has changed * @param width <code>true</code> if the width preference has changed * @param height <code>true</code> if the height preference has changed */ - public void preferenceChanged(View view, boolean width, boolean height) + public void preferenceChanged(View v, boolean width, boolean height) { textComponent.revalidate(); } @@ -181,7 +179,7 @@ public abstract class BasicTextUI extends TextUI view.setParent(null); if (v != null) - v.setParent(null); + v.setParent(this); view = v; } @@ -207,10 +205,10 @@ public abstract class BasicTextUI extends TextUI */ public int getViewCount() { + int count = 0; if (view != null) - return 1; - else - return 0; + count = 1; + return count; } /** @@ -249,7 +247,11 @@ public abstract class BasicTextUI extends TextUI public void paint(Graphics g, Shape s) { if (view != null) - view.paint(g, s); + { + Rectangle b = s.getBounds(); + view.setSize(b.width, b.height); + view.paint(g, s); + } } @@ -277,7 +279,7 @@ public abstract class BasicTextUI extends TextUI public Shape modelToView(int position, Shape a, Position.Bias bias) throws BadLocationException { - return ((View) view).modelToView(position, a, bias); + return view.modelToView(position, a, bias); } /** @@ -363,12 +365,44 @@ public abstract class BasicTextUI extends TextUI { return view.getNextVisualPositionFrom(pos, b, a, d, biasRet); } + + /** + * Returns the startOffset of this view, which is always the beginning + * of the document. + * + * @return the startOffset of this view + */ + public int getStartOffset() + { + return 0; + } + + /** + * Returns the endOffset of this view, which is always the end + * of the document. + * + * @return the endOffset of this view + */ + public int getEndOffset() + { + return getDocument().getLength(); + } + + /** + * Returns the document associated with this view. + * + * @return the document associated with this view + */ + public Document getDocument() + { + return textComponent.getDocument(); + } } /** * Receives notifications when properties of the text component change. */ - class PropertyChangeHandler implements PropertyChangeListener + private class PropertyChangeHandler implements PropertyChangeListener { /** * Notifies when a property of the text component changes. @@ -448,7 +482,7 @@ public abstract class BasicTextUI extends TextUI /** * Receives notification when the model changes. */ - PropertyChangeHandler updateHandler = new PropertyChangeHandler(); + private PropertyChangeHandler updateHandler = new PropertyChangeHandler(); /** The DocumentEvent handler. */ DocumentHandler documentHandler = new DocumentHandler(); @@ -515,20 +549,19 @@ public abstract class BasicTextUI extends TextUI c.setOpaque(true); textComponent = (JTextComponent) c; - Document doc = textComponent.getDocument(); if (doc == null) { - doc = getEditorKit(textComponent).createDefaultDocument(); - textComponent.setDocument(doc); + doc = getEditorKit(textComponent).createDefaultDocument(); + textComponent.setDocument(doc); } - - textComponent.addPropertyChangeListener(updateHandler); - modelChanged(); - installDefaults(); installListeners(); installKeyboardActions(); + + // We need to trigger this so that the view hierarchy gets initialized. + modelChanged(); + } /** @@ -584,6 +617,7 @@ public abstract class BasicTextUI extends TextUI protected void installListeners() { textComponent.addFocusListener(focuslistener); + textComponent.addPropertyChangeListener(updateHandler); installDocumentListeners(); } @@ -621,6 +655,11 @@ public abstract class BasicTextUI extends TextUI */ protected Keymap createKeymap() { + // FIXME: It seems to me that this method implementation is wrong. It seems + // to fetch the focusInputMap and transform it to the KeyBinding/Keymap + // implemenation. I would think that it should be done the other way, + // fetching the keybindings (from prefix + ".bindings") and transform + // it to the newer InputMap/ActionMap implementation. JTextComponent.KeyBinding[] bindings = null; String prefix = getPropertyPrefix(); InputMapUIResource m = (InputMapUIResource) UIManager.get(prefix + ".focusInputMap"); @@ -637,10 +676,7 @@ public abstract class BasicTextUI extends TextUI } } if (bindings == null) - { - bindings = new JTextComponent.KeyBinding[0]; - UIManager.put(prefix + ".focusInputMap", bindings); - } + bindings = new JTextComponent.KeyBinding[0]; Keymap km = JTextComponent.addKeymap(getKeymapName(), JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP)); @@ -726,8 +762,6 @@ public abstract class BasicTextUI extends TextUI super.uninstallUI(component); rootView.setView(null); - textComponent.removePropertyChangeListener(updateHandler); - uninstallDefaults(); uninstallListeners(); uninstallKeyboardActions(); @@ -750,6 +784,7 @@ public abstract class BasicTextUI extends TextUI */ protected void uninstallListeners() { + textComponent.removePropertyChangeListener(updateHandler); textComponent.removeFocusListener(focuslistener); textComponent.getDocument().removeDocumentListener(documentHandler); } @@ -786,7 +821,9 @@ public abstract class BasicTextUI extends TextUI float w = v.getPreferredSpan(View.X_AXIS); float h = v.getPreferredSpan(View.Y_AXIS); - return new Dimension((int) w, (int) h); + Insets i = c.getInsets(); + return new Dimension((int) w + i.left + i.right, + (int) h + i.top + i.bottom); } /** @@ -817,18 +854,49 @@ public abstract class BasicTextUI extends TextUI } /** - * Paints the text component. + * Paints the text component. This acquires a read lock on the model and then + * calls {@link #paintSafely(Graphics)} in order to actually perform the + * painting. * * @param g the <code>Graphics</code> context to paint to * @param c not used here */ public final void paint(Graphics g, JComponent c) { - paintSafely(g); + try + { + Document doc = textComponent.getDocument(); + if (doc instanceof AbstractDocument) + { + AbstractDocument aDoc = (AbstractDocument) doc; + aDoc.readLock(); + } + + paintSafely(g); + } + finally + { + Document doc = textComponent.getDocument(); + if (doc instanceof AbstractDocument) + { + AbstractDocument aDoc = (AbstractDocument) doc; + aDoc.readUnlock(); + } + } } /** - * Actually performs the painting. + * This paints the text component while beeing sure that the model is not + * modified while painting. + * + * The following is performed in this order: + * <ol> + * <li>If the text component is opaque, the background is painted by + * calling {@link #paintBackground(Graphics)}.</li> + * <li>If there is a highlighter, the highlighter is painted.</li> + * <li>The view hierarchy is painted.</li> + * <li>The Caret is painter.</li> + * </ol> * * @param g the <code>Graphics</code> context to paint to */ @@ -840,9 +908,19 @@ public abstract class BasicTextUI extends TextUI if (textComponent.isOpaque()) paintBackground(g); - if (highlighter != null - && textComponent.getSelectionStart() != textComponent.getSelectionEnd()) - highlighter.paint(g); + // Try painting with the highlighter without checking whether there + // is a selection because a highlighter can be used to do more than + // marking selected text. + if (highlighter != null) + { + // Handle restoring of the color here to prevent + // drawing problems when the Highlighter implementor + // forgets to restore it. + Color oldColor = g.getColor(); + highlighter.paint(g); + g.setColor(oldColor); + } + rootView.paint(g, getVisibleEditorRect()); @@ -857,10 +935,23 @@ public abstract class BasicTextUI extends TextUI */ protected void paintBackground(Graphics g) { - // This method does nothing. All the background filling is done by the - // ComponentUI update method. However, the method is called by paint - // to provide a way for subclasses to draw something different (e.g. - // background images etc) on the background. + Color old = g.getColor(); + g.setColor(textComponent.getBackground()); + g.fillRect(0, 0, textComponent.getWidth(), textComponent.getHeight()); + g.setColor(old); + } + + /** + * Overridden for better control over background painting. This now simply + * calls {@link #paint} and this delegates the background painting to + * {@link #paintBackground}. + * + * @param g the graphics to use + * @param c the component to be painted + */ + public void update(Graphics g, JComponent c) + { + paint(g, c); } /** @@ -895,7 +986,84 @@ public abstract class BasicTextUI extends TextUI public void damageRange(JTextComponent t, int p0, int p1, Position.Bias firstBias, Position.Bias secondBias) { - // TODO: Implement me. + try + { + // 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.y == l2.y) + t.repaint(l1.union(l2)); + else + { + // 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); + if (posBelow < p1 && posBelow != -1 && posBelow != p0) + { + // 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 < p1 && nextPosBelow != -1 && posBelow != nextPosBelow) + { + posBelow = nextPosBelow; + nextPosBelow = Utilities.getPositionBelow(t, posBelow, l1.x); + } + // 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); + } + } + catch (BadLocationException ex) + { + AssertionError err = new AssertionError("Unexpected bad location"); + err.initCause(ex); + throw err; + } } /** @@ -1061,7 +1229,6 @@ public abstract class BasicTextUI extends TextUI */ protected Rectangle getVisibleEditorRect() { - JTextComponent textComponent = getComponent(); int width = textComponent.getWidth(); int height = textComponent.getHeight(); @@ -1082,7 +1249,6 @@ public abstract class BasicTextUI extends TextUI protected final void setView(View view) { rootView.setView(view); - view.setParent(rootView); textComponent.revalidate(); textComponent.repaint(); } diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java index f2ebcfca9ac..1c6e6c5e502 100644 --- a/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java +++ b/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java @@ -45,6 +45,7 @@ import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; +import java.awt.Label; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; @@ -114,9 +115,18 @@ import javax.swing.tree.TreeSelectionModel; * @see javax.swing.JTree * @author Lillian Angel (langel@redhat.com) * @author Sascha Brawer (brawer@dandelis.ch) + * @author Audrius Meskauskas (audriusa@bioinformatics.org) */ public class BasicTreeUI extends TreeUI { + /** + * The tree cell editing may be started by the single mouse click on the + * selected cell. To separate it from the double mouse click, the editing + * session starts after this time (in ms) after that single click, and only + * no other clicks were performed during that time. + */ + static int WAIT_TILL_EDITING = 900; + /** Collapse Icon for the tree. */ protected transient Icon collapsedIcon; @@ -225,12 +235,6 @@ public class BasicTreeUI extends TreeUI /** Set to true if the editor has a different size than the renderer. */ protected boolean editorHasDifferentSize; - /** The action listener for the editor's Timer. */ - Timer editorTimer = new EditorUpdateTimer(); - - /** The new value of the node after editing. */ - Object newVal; - /** The action bound to KeyStrokes. */ TreeAction action; @@ -266,6 +270,20 @@ public class BasicTreeUI extends TreeUI private TreeExpansionListener treeExpansionListener; private TreeModelListener treeModelListener; + + /** + * This timer fires the editing action after about 1200 ms if not reset during + * that time. It handles the editing start with the single mouse click + * (and not the double mouse click) on the selected tree node. + */ + Timer startEditTimer; + + /** + * The special value of the mouse event is sent indicating that this is not + * just the mouse click, but the mouse click on the selected node. Sending + * such event forces to start the cell editing session. + */ + static final MouseEvent EDIT = new MouseEvent(new Label(), 7,7,7,7,7,7, false); /** * Creates a new BasicTreeUI object. @@ -303,7 +321,7 @@ public class BasicTreeUI extends TreeUI { return new BasicTreeUI(); } - + /** * Returns the Hash color. * @@ -796,7 +814,10 @@ public class BasicTreeUI extends TreeUI public boolean stopEditing(JTree tree) { if (isEditing(tree)) - completeEditing(true, false, false); + { + completeEditing(false, false, true); + finish(); + } return !isEditing(tree); } @@ -807,9 +828,12 @@ public class BasicTreeUI extends TreeUI * is the tree to cancel the editing session on. */ public void cancelEditing(JTree tree) - { - if (isEditing(tree)) - completeEditing(false, true, false); + { + // There is no need to send the cancel message to the editor, + // as the cancellation event itself arrives from it. This would + // only be necessary when cancelling the editing programatically. + completeEditing(false, false, false); + finish(); } /** @@ -1213,6 +1237,7 @@ public class BasicTreeUI extends TreeUI protected void updateCachedPreferredSize() { int maxWidth = 0; + updateCurrentVisiblePath(); boolean isLeaf = false; if (currentVisiblePath != null) { @@ -1246,7 +1271,7 @@ public class BasicTreeUI extends TreeUI protected void pathWasExpanded(TreePath path) { validCachedPreferredSize = false; - tree.repaint(); + tree.repaint(); } /** @@ -1271,7 +1296,6 @@ public class BasicTreeUI extends TreeUI leftChildIndent = UIManager.getInt("Tree.leftChildIndent"); setRowHeight(UIManager.getInt("Tree.rowHeight")); tree.setRowHeight(getRowHeight()); - tree.requestFocusInWindow(false); tree.setScrollsOnExpand(UIManager.getBoolean("Tree.scrollsOnExpand")); setExpandedIcon(UIManager.getIcon("Tree.expandedIcon")); setCollapsedIcon(UIManager.getIcon("Tree.collapsedIcon")); @@ -1621,7 +1645,14 @@ public class BasicTreeUI extends TreeUI } if (messageTree) - treeModel.valueForPathChanged(tree.getLeadSelectionPath(), newVal); + { + TreeCellEditor editor = getCellEditor(); + if (editor != null) + { + Object value = editor.getCellEditorValue(); + treeModel.valueForPathChanged(tree.getLeadSelectionPath(), value); + } + } } /** @@ -1636,44 +1667,48 @@ public class BasicTreeUI extends TreeUI */ protected boolean startEditing(TreePath path, MouseEvent event) { - int x; - int y; - if (event == null) - { - Rectangle bounds = getPathBounds(tree, path); - x = bounds.x; - y = bounds.y; - } - else - { - x = event.getX(); - y = event.getY(); - } + // Force to recalculate the maximal row height. + maxHeight = 0; + + // Force to recalculate the cached preferred size. + validCachedPreferredSize = false; updateCellEditor(); TreeCellEditor ed = getCellEditor(); - if (ed != null && ed.shouldSelectCell(event) && ed.isCellEditable(event)) + + if (ed != null + && (event == EDIT || ed.shouldSelectCell(event)) + && ed.isCellEditable(event)) { + Rectangle bounds = getPathBounds(tree, path); + + // Extend the right boundary till the tree width. + bounds.width = tree.getWidth() - bounds.x; + editingPath = path; editingRow = tree.getRowForPath(editingPath); - Object val = editingPath.getLastPathComponent(); - cellEditor.addCellEditorListener(cellEditorListener); + Object value = editingPath.getLastPathComponent(); + stopEditingInCompleteEditing = false; boolean expanded = tree.isExpanded(editingPath); isEditing = true; - editingComponent = ed.getTreeCellEditorComponent(tree, val, true, + editingComponent = ed.getTreeCellEditorComponent(tree, value, true, expanded, isLeaf(editingRow), editingRow); - editingComponent.getParent().setVisible(true); - editingComponent.getParent().validate(); - tree.add(editingComponent.getParent()); - editingComponent.getParent().validate(); - validCachedPreferredSize = false; - - ((JTextField) editingComponent).requestFocusInWindow(false); - editorTimer.start(); + + // Remove all previous components (if still present). Only one + // container with the editing component inside is allowed in the tree. + tree.removeAll(); + + // The editing component must be added to its container. We add the + // container, not the editing component itself. + Component container = editingComponent.getParent(); + container.setBounds(bounds); + tree.add(container); + editingComponent.requestFocus(); + return true; } return false; @@ -1922,7 +1957,7 @@ public class BasicTreeUI extends TreeUI tree.clearSelection(); if (tree.isEditing() && !e.getActionCommand().equals("startEditing")) - tree.cancelEditing(); + tree.stopEditing(); tree.scrollPathToVisible(lead); } @@ -1957,51 +1992,7 @@ public class BasicTreeUI extends TreeUI } } - /** - * The timer that updates the editor component. - */ - private class EditorUpdateTimer extends Timer implements ActionListener - { - /** - * Creates a new EditorUpdateTimer object with a default delay of 0.3 - * seconds. - */ - public EditorUpdateTimer() - { - super(300, null); - addActionListener(this); - } - - /** - * Lets the caret blink and repaints the table. - */ - public void actionPerformed(ActionEvent ev) - { - Caret c = ((JTextField) editingComponent).getCaret(); - if (c != null) - c.setVisible(!c.isVisible()); - tree.repaint(); - } - - /** - * Updates the blink delay according to the current caret. - */ - public void update() - { - stop(); - Caret c = ((JTextField) editingComponent).getCaret(); - if (c != null) - { - setDelay(c.getBlinkRate()); - if (((JTextField) editingComponent).isEditable()) - start(); - else - c.setVisible(false); - } - } - } - - /** + /** * Updates the preferred size when scrolling, if necessary. */ public class ComponentHandler extends ComponentAdapter implements @@ -2089,29 +2080,7 @@ public class BasicTreeUI extends TreeUI */ public void editingStopped(ChangeEvent e) { - editingPath = null; - editingRow = -1; - stopEditingInCompleteEditing = false; - if (editingComponent != null) - { - tree.remove(editingComponent.getParent()); - editingComponent = null; - } - if (cellEditor != null) - { - newVal = ((JTextField) getCellEditor().getCellEditorValue()).getText(); - completeEditing(false, false, true); - if (cellEditor instanceof DefaultTreeCellEditor) - tree.removeTreeSelectionListener((DefaultTreeCellEditor) cellEditor); - cellEditor.removeCellEditorListener(cellEditorListener); - setCellEditor(null); - createdCellEditor = false; - } - isEditing = false; - tree.requestFocusInWindow(false); - editorTimer.stop(); - validCachedPreferredSize = false; - tree.repaint(); + stopEditing(tree); } /** @@ -2123,25 +2092,7 @@ public class BasicTreeUI extends TreeUI */ public void editingCanceled(ChangeEvent e) { - editingPath = null; - editingRow = -1; - stopEditingInCompleteEditing = false; - if (editingComponent != null) - tree.remove(editingComponent.getParent()); - editingComponent = null; - if (cellEditor != null) - { - if (cellEditor instanceof DefaultTreeCellEditor) - tree.removeTreeSelectionListener((DefaultTreeCellEditor) cellEditor); - cellEditor.removeCellEditorListener(cellEditorListener); - setCellEditor(null); - createdCellEditor = false; - } - tree.requestFocusInWindow(false); - editorTimer.stop(); - isEditing = false; - validCachedPreferredSize = false; - tree.repaint(); + cancelEditing(tree); } }// CellEditorHandler @@ -2261,7 +2212,15 @@ public class BasicTreeUI extends TreeUI * is the mouse event that occured */ 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; + } + Point click = e.getPoint(); TreePath path = getClosestPathForLocation(tree, click.x, click.y); @@ -2298,9 +2257,37 @@ public class BasicTreeUI extends TreeUI { if (inBounds) { - selectPath(tree, path); - if (e.getClickCount() == 2 && !isLeaf(row)) - toggleExpandState(path); + TreePath currentLead = tree.getLeadSelectionPath(); + if ( + currentLead != null && + currentLead.equals(path) && + e.getClickCount() == 1 && + tree.isEditable() + ) + { + // Schedule the editing session. + final TreePath editPath = path; + + 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 + { + selectPath(tree, path); + if (e.getClickCount() == 2 && !isLeaf(row)) + toggleExpandState(path); + } } if (cntlClick) @@ -2686,7 +2673,7 @@ public class BasicTreeUI extends TreeUI public class TreeHomeAction extends AbstractAction { - /** direction is either home or end */ + /** The direction, either home or end */ protected int direction; /** @@ -2983,7 +2970,7 @@ public class BasicTreeUI extends TreeUI public void valueChanged(TreeSelectionEvent event) { if (tree.isEditing()) - tree.cancelEditing(); + tree.stopEditing(); } }// TreeSelectionHandler @@ -3650,25 +3637,14 @@ public class BasicTreeUI extends TreeUI if (row != 0) bounds.x += gap; bounds.width = preferredSize.width + bounds.x; - if (editingComponent != null && editingPath != null && isEditing(tree) - && node.equals(editingPath.getLastPathComponent())) - { - rendererPane.paintComponent(g, editingComponent.getParent(), null, - bounds); - } - else - { - TreeCellRenderer dtcr = tree.getCellRenderer(); - if (dtcr == null) - dtcr = createDefaultCellRenderer(); - - Component c = dtcr.getTreeCellRendererComponent(tree, node, - selected, - isExpanded, isLeaf, - row, - tree.hasFocus()); - rendererPane.paintComponent(g, c, c.getParent(), bounds); - } + TreeCellRenderer dtcr = tree.getCellRenderer(); + if (dtcr == null) + dtcr = createDefaultCellRenderer(); + + Component c = dtcr.getTreeCellRendererComponent(tree, node, selected, + isExpanded, isLeaf, + row, tree.hasFocus()); + rendererPane.paintComponent(g, c, c.getParent(), bounds); } } @@ -3801,4 +3777,21 @@ public class BasicTreeUI extends TreeUI } return null; } + + /** + * Finish the editing session. + */ + void finish() + { + editingPath = null; + editingRow = -1; + stopEditingInCompleteEditing = false; + isEditing = false; + tree.removeAll(); + validCachedPreferredSize = false; + + // Repaint the region, where was the editing component. + tree.repaint(editingComponent.getParent().getBounds()); + editingComponent = null; + } } // BasicTreeUI diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java b/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java index 28143d51ecd..99c90acdbee 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalBorders.java @@ -1,5 +1,5 @@ /* MetalBorders.java - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006, Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -249,30 +249,27 @@ public class MetalBorders /** * Returns the insets of the <code>ButtonBorder</code>. * - * @param c the component for which the border is used + * @param c the component for which the border is used (ignored). * - * @return The insets of the ButtonBorder + * @return The insets of the <code>ButtonBorder</code>. */ public Insets getBorderInsets(Component c) { - return getBorderInsets(c, null); + return borderInsets; } /** * Returns the insets of the <code>ButtonBorder</code> in the specified * <code>newInsets</code> object. * - * @param c the component for which the border is used - * @param newInsets the insets object where to put the values (if - * <code>null</code>, a new instance is created). + * @param c the component for which the border is used (ignored). + * @param newInsets the insets object where to put the values ( + * <code>null</code> not permitted). * - * @return The insets. + * @return The <code>newInsets</code> reference. */ public Insets getBorderInsets(Component c, Insets newInsets) { - if (newInsets == null) - newInsets = new Insets(0, 0, 0, 0); - newInsets.bottom = borderInsets.bottom; newInsets.left = borderInsets.left; newInsets.right = borderInsets.right; @@ -352,6 +349,8 @@ public class MetalBorders public static class Flush3DBorder extends AbstractBorder implements UIResource { + private static final Insets borderInsets = new Insets(2, 2, 2, 2); + /** * Creates a new border instance. */ @@ -369,26 +368,25 @@ public class MetalBorders */ public Insets getBorderInsets(Component c) { - return getBorderInsets(c, null); + return borderInsets; } /** * Returns the border insets. * * @param c the component (ignored). - * @return The border insets. + * @param newInsets an existing insets instance, that will be populated + * with the border insets and returned as the result + * (<code>null</code> not permitted). + * + * @return The <code>newInsets</code> reference. */ public Insets getBorderInsets(Component c, Insets newInsets) { - if (newInsets == null) - newInsets = new Insets(2, 2, 2, 2); - else - { - newInsets.top = 2; - newInsets.left = 2; - newInsets.bottom = 2; - newInsets.right = 2; - } + newInsets.top = borderInsets.top; + newInsets.left = borderInsets.left; + newInsets.bottom = borderInsets.bottom; + newInsets.right = borderInsets.right; return newInsets; } @@ -427,6 +425,8 @@ public class MetalBorders public static class PaletteBorder extends AbstractBorder implements UIResource { + private static final Insets borderInsets = new Insets(1, 1, 1, 1); + /** * Creates a new <code>PaletteBorder</code>. */ @@ -444,29 +444,25 @@ public class MetalBorders */ public Insets getBorderInsets(Component c) { - return getBorderInsets(c, null); + return borderInsets; } /** * Returns the border insets. * * @param c the component (ignored). - * @param newInsets the insets object that, if non-<code>null</code>, will - * be populated with the result from this method. - * - * @return The border insets. + * @param newInsets an existing insets instance, that will be populated + * with the border insets and returned as the result + * (<code>null</code> not permitted). + * + * @return The <code>newInsets</code> reference. */ public Insets getBorderInsets(Component c, Insets newInsets) { - if (newInsets == null) - newInsets = new Insets(1, 1, 1, 1); - else - { - newInsets.top = 1; - newInsets.left = 1; - newInsets.bottom = 1; - newInsets.right = 1; - } + newInsets.top = borderInsets.top; + newInsets.left = borderInsets.left; + newInsets.bottom = borderInsets.bottom; + newInsets.right = borderInsets.right; return newInsets; } @@ -555,6 +551,8 @@ public class MetalBorders public static class InternalFrameBorder extends AbstractBorder implements UIResource { + private static final Insets borderInsets = new Insets(5, 5, 5, 5); + /** * Creates a new border instance. */ @@ -572,26 +570,25 @@ public class MetalBorders */ public Insets getBorderInsets(Component c) { - return getBorderInsets(c, null); + return borderInsets; } /** * Returns the border insets. * * @param c the component (ignored). - * @return The border insets. + * @param newInsets an existing insets instance, that will be populated + * with the border insets and returned as the result + * (<code>null</code> not permitted). + * + * @return The <code>newInsets</code> reference. */ public Insets getBorderInsets(Component c, Insets newInsets) { - if (newInsets == null) - newInsets = new Insets(5, 5, 5, 5); - else - { - newInsets.top = 5; - newInsets.left = 5; - newInsets.bottom = 5; - newInsets.right = 5; - } + newInsets.top = borderInsets.top; + newInsets.left = borderInsets.left; + newInsets.bottom = borderInsets.bottom; + newInsets.right = borderInsets.right; return newInsets; } @@ -763,7 +760,7 @@ public class MetalBorders implements UIResource { /** The border insets. */ - protected static Insets borderInsets = new Insets(1, 1, 1, 1); + protected static Insets borderInsets = new Insets(2, 2, 2, 2); /** * Creates a new border instance. diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java index 967c40d29ad..cb94c87b846 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalFileChooserUI.java @@ -1545,8 +1545,6 @@ public class MetalFileChooserUI fileListPanel = new JPanel(new BorderLayout()); fileList = new JList(getModel()); scrollPane = new JScrollPane(fileList); - scrollPane.setVerticalScrollBarPolicy - (JScrollPane.VERTICAL_SCROLLBAR_NEVER); fileList.setLayoutOrientation(JList.VERTICAL_WRAP); fileList.setCellRenderer(new FileRenderer()); } @@ -1557,7 +1555,10 @@ public class MetalFileChooserUI scrollPane.getViewport().setView(fileList); } fileListPanel.add(scrollPane); - + // This size was determined using BeanShell and dumping the JFileChooser + // component hierarchy. Sun has an internal FilePane class in there, but + // that probably doesn't matter atm. + fileListPanel.setPreferredSize(new Dimension(405, 135)); return fileListPanel; } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java b/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java index c60b55c9e7b..e84644615ce 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalLookAndFeel.java @@ -1171,6 +1171,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel "Spinner.arrowButtonInsets", new InsetsUIResource(0, 0, 0, 0), "Spinner.background", getControl(), + "Spinner.border", MetalBorders.getTextFieldBorder(), "Spinner.font", new FontUIResource("Dialog", Font.BOLD, 12), "Spinner.foreground", getControl(), @@ -1342,4 +1343,16 @@ public class MetalLookAndFeel extends BasicLookAndFeel { return theme; } + + /** + * Returns <code>true</code> because the Metal look + * and feel supports window decorations for toplevel + * containers. + * + * @return <code>true</code> + */ + public boolean getSupportsWindowDecorations() + { + return true; + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java index faed80382d0..23051e9bcea 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalRootPaneUI.java @@ -1,5 +1,5 @@ /* MetalRootPaneUI.java - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,23 +38,842 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.LayoutManager; +import java.awt.LayoutManager2; +import java.awt.Rectangle; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.WindowEvent; +import java.awt.event.WindowFocusListener; +import java.beans.PropertyChangeEvent; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.Icon; +import javax.swing.JButton; import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JLayeredPane; +import javax.swing.JMenu; +import javax.swing.JMenuBar; import javax.swing.JRootPane; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.border.AbstractBorder; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicRootPaneUI; /** - * A UI delegate for the {@link JRootPane} component. This class is not fully - * implemented. + * A UI delegate for the {@link JRootPane} component. This implementation + * supports the JRootPane <code>windowDecorationStyle</code> property. * + * @author Roman Kennke (kennke@aicas.com) + * * @since 1.4 */ public class MetalRootPaneUI extends BasicRootPaneUI { - // FIXME: maybe replace by a Map of instances when this becomes stateful - /** The shared UI instance for MetalRootPaneUIs */ + /** + * The border that is used on JRootPane when the windowDecorationStyle + * property of the JRootPane is set to a different value than NONE. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private static class MetalFrameBorder + extends AbstractBorder + { + /** + * Returns the border insets. + * + * @param c the component + * @param newInsets the insets to be filled with the return value, may be + * <code>null</code> in which case a new object is created + * + * @return the border insets + */ + public Insets getBorderInsets(Component c, Insets newInsets) + { + if (newInsets == null) + newInsets = new Insets(5, 5, 5, 5); + else + { + newInsets.top = 5; + newInsets.left = 5; + newInsets.bottom = 5; + newInsets.right = 5; + } + return newInsets; + } + + /** + * Returns the border insets. + * + * @param c the component + * + * @return the border insets + */ + public Insets getBorderInsets(Component c) + { + return getBorderInsets(c, null); + } + + /** + * Paints the border for the specified component. + * + * @param c the component + * @param g the graphics device + * @param x the x-coordinate + * @param y the y-coordinate + * @param w the width + * @param h the height + */ + public void paintBorder(Component c, Graphics g, int x, int y, int w, + int h) + { + JRootPane f = (JRootPane) c; + Window frame = SwingUtilities.getWindowAncestor(f); + if (frame.isActive()) + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + else + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + + // Fill the border background. + g.fillRect(x, y, w, 5); + g.fillRect(x, y, 5, h); + g.fillRect(x + w - 5, y, 5, h); + g.fillRect(x, y + h - 5, w, 5); + + // Draw a dot in each corner. + g.setColor(MetalLookAndFeel.getControl()); + g.fillRect(x, y, 1, 1); + g.fillRect(x + w - 1, y, 1, 1); + g.fillRect(x + w - 1, y + h - 1, 1, 1); + g.fillRect(x, y + h - 1, 1, 1); + + // Draw the lines. + g.setColor(MetalLookAndFeel.getBlack()); + g.drawLine(x + 14, y + 2, x + w - 15, y + 2); + g.drawLine(x + 14, y + h - 3, x + w - 15, y + h - 3); + g.drawLine(x + 2, y + 14, x + 2, y + h - 15); + g.drawLine(x + w - 3, y + 14, x + w - 3, y + h - 15); + + // Draw the line highlights. + if (frame.isActive()) + g.setColor(MetalLookAndFeel.getPrimaryControlShadow()); + else + g.setColor(MetalLookAndFeel.getControlShadow()); + g.drawLine(x + 15, y + 3, x + w - 14, y + 3); + g.drawLine(x + 15, y + h - 2, x + w - 14, y + h - 2); + g.drawLine(x + 3, y + 15, x + 3, y + h - 14); + g.drawLine(x + w - 2, y + 15, x + w - 2, y + h - 14); + } + } + + /** + * The component that renders the title bar for frames. This duplicates + * most of {@link MetalInternalFrameTitlePane}. It is not reasonably possible + * to reuse that class because that is bound to the JInternalFrame and we + * need to handle JFrames/JRootPanes here. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private static class MetalTitlePane extends JComponent + { + /** + * The Action responsible for closing the JInternalFrame. + */ + private class CloseAction extends AbstractAction + { + /** + * Creates a new action. + */ + public CloseAction() + { + super("Close"); + } + + /** + * This method is called when something closes the frame. + * + * @param e the ActionEvent + */ + public void actionPerformed(ActionEvent e) + { + Window frame = SwingUtilities.getWindowAncestor(rootPane); + if (frame instanceof JFrame) + { + JFrame jframe = (JFrame) frame; + switch (jframe.getDefaultCloseOperation()) + { + case JFrame.EXIT_ON_CLOSE: + jframe.setVisible(false); + jframe.dispose(); + System.exit(0); + break; + case JFrame.DISPOSE_ON_CLOSE: + jframe.setVisible(false); + jframe.dispose(); + break; + case JFrame.HIDE_ON_CLOSE: + jframe.setVisible(false); + break; + case JFrame.DO_NOTHING_ON_CLOSE: + default: + break; + } + } + else if (frame instanceof JDialog) + { + JDialog jdialog = (JDialog) frame; + switch (jdialog.getDefaultCloseOperation()) + { + case JFrame.DISPOSE_ON_CLOSE: + jdialog.setVisible(false); + jdialog.dispose(); + break; + case JFrame.HIDE_ON_CLOSE: + jdialog.setVisible(false); + break; + case JFrame.DO_NOTHING_ON_CLOSE: + default: + break; + } + } + } + } + + /** + * This helper class is used to create the minimize, maximize and close + * buttons in the top right corner of the Title Pane. These buttons are + * special since they cannot be given focus and have no border. + */ + private class PaneButton extends JButton + { + /** + * Creates a new PaneButton object with the given Action. + * + * @param a The Action that the button uses. + */ + public PaneButton(Action a) + { + super(a); + setMargin(new Insets(0, 0, 0, 0)); + } + + /** + * This method returns true if the Component can be focused. + * + * @return false. + */ + public boolean isFocusable() + { + // These buttons cannot be given focus. + return false; + } + + } + + /** + * The layout for the JRootPane when the <code>windowDecorationStyle</code> + * property is set. In addition to the usual JRootPane.RootLayout behaviour + * this lays out the titlePane. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class MetalTitlePaneLayout implements LayoutManager + { + /** + * Creates a new <code>TitlePaneLayout</code> object. + */ + public MetalTitlePaneLayout() + { + // Do nothing. + } + + /** + * Adds a Component to the Container. + * + * @param name The name to reference the added Component by. + * @param c The Component to add. + */ + public void addLayoutComponent(String name, Component c) + { + // Do nothing. + } + + /** + * This method is called to lay out the children of the Title Pane. + * + * @param c The Container to lay out. + */ + public void layoutContainer(Container c) + { + + Dimension size = c.getSize(); + Insets insets = c.getInsets(); + int width = size.width - insets.left - insets.right; + int height = size.height - insets.top - insets.bottom; + + int loc = width - insets.right - 1; + int top = insets.top + 2; + int buttonHeight = height - 4; + if (closeButton.isVisible()) + { + int buttonWidth = closeIcon.getIconWidth(); + loc -= buttonWidth + 2; + closeButton.setBounds(loc, top, buttonWidth, buttonHeight); + loc -= 6; + } + + if (maxButton.isVisible()) + { + int buttonWidth = maxIcon.getIconWidth(); + loc -= buttonWidth + 4; + maxButton.setBounds(loc, top, buttonWidth, buttonHeight); + } + + if (iconButton.isVisible()) + { + int buttonWidth = minIcon.getIconWidth(); + loc -= buttonWidth + 4; + iconButton.setBounds(loc, top, buttonWidth, buttonHeight); + loc -= 2; + } + + Dimension titlePreferredSize = title.getPreferredSize(); + title.setBounds(insets.left + 5, insets.top, + Math.min(titlePreferredSize.width, loc - insets.left - 10), + height); + + } + + /** + * This method returns the minimum size of the given Container given the + * children that it has. + * + * @param c The Container to get a minimum size for. + * + * @return The minimum size of the Container. + */ + public Dimension minimumLayoutSize(Container c) + { + return preferredLayoutSize(c); + } + + /** + * Returns the preferred size of the given Container taking + * into account the children that it has. + * + * @param c The Container to lay out. + * + * @return The preferred size of the Container. + */ + public Dimension preferredLayoutSize(Container c) + { + return new Dimension(22, 22); + } + + /** + * Removes a Component from the Container. + * + * @param c The Component to remove. + */ + public void removeLayoutComponent(Component c) + { + // Nothing to do here. + } + } + + JRootPane rootPane; + + /** The button that closes the JInternalFrame. */ + JButton closeButton; + + /** The button that iconifies the JInternalFrame. */ + JButton iconButton; + + /** The button that maximizes the JInternalFrame. */ + JButton maxButton; + + Icon minIcon; + + /** The icon displayed in the maximize button. */ + Icon maxIcon; + + /** The icon displayed in the iconify button. */ + private Icon iconIcon; + + /** The icon displayed in the close button. */ + Icon closeIcon; + + /** + * The background color of the TitlePane when the JInternalFrame is not + * selected. + */ + private Color notSelectedTitleColor; + + /** + * The background color of the TitlePane when the JInternalFrame is + * selected. + */ + private Color selectedTitleColor; + + /** + * The label used to display the title. This label is not added to the + * TitlePane. + */ + JLabel title; + + /** The action associated with closing the JInternalFrame. */ + private Action closeAction; + + /** The action associated with iconifying the JInternalFrame. */ + private Action iconifyAction; + + /** The action associated with maximizing the JInternalFrame. */ + private Action maximizeAction; + + /** The JMenuBar that is located at the top left of the Title Pane. */ + private JMenuBar menuBar; + + /** The JMenu inside the menuBar. */ + protected JMenu windowMenu; + + MetalTitlePane(JRootPane rp) + { + rootPane = rp; + setLayout(createLayout()); + title = new JLabel(); + title.setHorizontalAlignment(SwingConstants.LEFT); + title.setHorizontalTextPosition(SwingConstants.LEFT); + title.setOpaque(false); + installTitlePane(); + } + + protected LayoutManager createLayout() + { + return new MetalTitlePaneLayout(); + } + + /** + * This method installs the TitlePane onto the JInternalFrameTitlePane. It + * also creates any children components that need to be created and adds + * listeners to the appropriate components. + */ + protected void installTitlePane() + { + installDefaults(); + installListeners(); + createActions(); + assembleSystemMenu(); + createButtons(); + setButtonIcons(); + addSubComponents(); + enableActions(); + } + + private void enableActions() + { + // TODO: Implement this. + } + + private void addSubComponents() + { + add(menuBar); + add(closeButton); + add(iconButton); + add(maxButton); + } + + private void installListeners() + { + Window window = SwingUtilities.getWindowAncestor(rootPane); + window.addWindowFocusListener(new WindowFocusListener() + { + public void windowGainedFocus(WindowEvent ev) + { + repaint(); + } + public void windowLostFocus(WindowEvent ev) + { + repaint(); + } + }); + } + + private void createActions() + { + closeAction = new CloseAction(); + } + + private void assembleSystemMenu() + { + menuBar = createSystemMenuBar(); + windowMenu = createSystemMenu(); + menuBar.add(windowMenu); + addSystemMenuItems(windowMenu); + enableActions(); + } + + protected JMenuBar createSystemMenuBar() + { + if (menuBar == null) + menuBar = new JMenuBar(); + menuBar.removeAll(); + return menuBar; + } + + protected JMenu createSystemMenu() + { + if (windowMenu == null) + windowMenu = new JMenu(); + windowMenu.removeAll(); + return windowMenu; + } + + private void addSystemMenuItems(JMenu menu) + { + // TODO: Implement this. + } + + protected void createButtons() + { + closeButton = new PaneButton(closeAction); + closeButton.setText(null); + iconButton = new PaneButton(iconifyAction); + iconButton.setText(null); + maxButton = new PaneButton(maximizeAction); + maxButton.setText(null); + closeButton.setBorderPainted(false); + closeButton.setContentAreaFilled(false); + iconButton.setBorderPainted(false); + iconButton.setContentAreaFilled(false); + maxButton.setBorderPainted(false); + maxButton.setContentAreaFilled(false); + } + + protected void setButtonIcons() + { + if (closeIcon != null && closeButton != null) + closeButton.setIcon(closeIcon); + if (iconIcon != null && iconButton != null) + iconButton.setIcon(iconIcon); + if (maxIcon != null && maxButton != null) + maxButton.setIcon(maxIcon); + } + + /** + * Paints a representation of the current state of the internal frame. + * + * @param g the graphics device. + */ + public void paintComponent(Graphics g) + { + Window frame = SwingUtilities.getWindowAncestor(rootPane); + Color savedColor = g.getColor(); + paintTitleBackground(g); + paintChildren(g); + Dimension d = getSize(); + if (frame.isActive()) + g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow()); + else + g.setColor(MetalLookAndFeel.getControlDarkShadow()); + + // put a dot in each of the top corners + g.drawLine(0, 0, 0, 0); + g.drawLine(d.width - 1, 0, d.width - 1, 0); + + g.drawLine(0, d.height - 1, d.width - 1, d.height - 1); + + // draw the metal pattern + if (UIManager.get("InternalFrame.activeTitleGradient") != null + && frame.isActive()) + { + MetalUtils.paintGradient(g, 0, 0, getWidth(), getHeight(), + SwingConstants.VERTICAL, + "InternalFrame.activeTitleGradient"); + } + + Rectangle b = title.getBounds(); + int startX = b.x + b.width + 5; + int endX = startX; + if (iconButton.isVisible()) + endX = Math.max(iconButton.getX(), endX); + else if (maxButton.isVisible()) + endX = Math.max(maxButton.getX(), endX); + else if (closeButton.isVisible()) + endX = Math.max(closeButton.getX(), endX); + endX -= 7; + if (endX > startX) + MetalUtils.fillMetalPattern(this, g, startX, 3, endX - startX, getHeight() - 6, Color.white, Color.gray); + g.setColor(savedColor); + } + + /** + * This method paints the TitlePane's background. + * + * @param g The Graphics object to paint with. + */ + protected void paintTitleBackground(Graphics g) + { + Window frame = SwingUtilities.getWindowAncestor(rootPane); + + if (!isOpaque()) + return; + + Color saved = g.getColor(); + Dimension dims = getSize(); + + Color bg = getBackground(); + if (frame.isActive()) + bg = selectedTitleColor; + else + bg = notSelectedTitleColor; + g.setColor(bg); + g.fillRect(0, 0, dims.width, dims.height); + g.setColor(saved); + } + + /** + * This method installs the defaults determined by the look and feel. + */ + private void installDefaults() + { + title.setFont(UIManager.getFont("InternalFrame.titleFont")); + selectedTitleColor = UIManager.getColor("InternalFrame.activeTitleBackground"); + notSelectedTitleColor = UIManager.getColor("InternalFrame.inactiveTitleBackground"); + closeIcon = UIManager.getIcon("InternalFrame.closeIcon"); + iconIcon = UIManager.getIcon("InternalFrame.iconifyIcon"); + maxIcon = UIManager.getIcon("InternalFrame.maximizeIcon"); + minIcon = MetalIconFactory.getInternalFrameAltMaximizeIcon(16); + Frame frame = (Frame) SwingUtilities.getWindowAncestor(rootPane); + title = new JLabel(frame.getTitle(), + MetalIconFactory.getInternalFrameDefaultMenuIcon(), + SwingConstants.LEFT); + } + } + + private static class MetalRootLayout + implements LayoutManager2 + { + + /** + * The cached layout info for the glass pane. + */ + private Rectangle glassPaneBounds; + + /** + * The cached layout info for the layered pane. + */ + private Rectangle layeredPaneBounds; + + /** + * The cached layout info for the content pane. + */ + private Rectangle contentPaneBounds; + + /** + * The cached layout info for the menu bar. + */ + private Rectangle menuBarBounds; + + /** + * The cached layout info for the title pane. + */ + private Rectangle titlePaneBounds; + + /** + * The cached preferred size. + */ + private Dimension prefSize; + + public void addLayoutComponent(Component component, Object constraints) + { + // Nothing to do here. + } + + public Dimension maximumLayoutSize(Container target) + { + return preferredLayoutSize(target); + } + + public float getLayoutAlignmentX(Container target) + { + return 0.0F; + } + + public float getLayoutAlignmentY(Container target) + { + return 0.0F; + } + + public void invalidateLayout(Container target) + { + synchronized (this) + { + glassPaneBounds = null; + layeredPaneBounds = null; + contentPaneBounds = null; + menuBarBounds = null; + titlePaneBounds = null; + prefSize = null; + } + } + + public void addLayoutComponent(String name, Component component) + { + // Nothing to do here. + } + + public void removeLayoutComponent(Component component) + { + // TODO Auto-generated method stub + + } + + public Dimension preferredLayoutSize(Container parent) + { + JRootPane rp = (JRootPane) parent; + JLayeredPane layeredPane = rp.getLayeredPane(); + Component contentPane = layeredPane.getComponent(0); + Component titlePane = layeredPane.getComponent(1); + Component menuBar = null; + if (layeredPane.getComponentCount() > 2 + && layeredPane.getComponent(2) instanceof JMenuBar) + menuBar = layeredPane.getComponent(2); + + // We must synchronize here, otherwise we cannot guarantee that the + // prefSize is still non-null when returning. + synchronized (this) + { + if (prefSize == null) + { + Insets i = parent.getInsets(); + prefSize = new Dimension(i.left + i.right, i.top + i.bottom); + Dimension contentPrefSize = contentPane.getPreferredSize(); + prefSize.width += contentPrefSize.width; + prefSize.height += contentPrefSize.height + + titlePane.getPreferredSize().height; + if (menuBar != null) + { + Dimension menuBarSize = menuBar.getPreferredSize(); + if (menuBarSize.width > contentPrefSize.width) + prefSize.width += menuBarSize.width - contentPrefSize.width; + prefSize.height += menuBarSize.height; + } + } + // Return a copy here so the cached value won't get trashed by some + // other component. + return new Dimension(prefSize); + } + } + + public Dimension minimumLayoutSize(Container parent) + { + return preferredLayoutSize(parent); + } + + public void layoutContainer(Container parent) + { + JRootPane rp = (JRootPane) parent; + JLayeredPane layeredPane = rp.getLayeredPane(); + Component contentPane = layeredPane.getComponent(0); + Component titlePane = layeredPane.getComponent(1); + Component menuBar = null; + if (layeredPane.getComponentCount() > 2 + && layeredPane.getComponent(2) instanceof JMenuBar) + menuBar = layeredPane.getComponent(2); + Component glassPane = rp.getGlassPane(); + + if (glassPaneBounds == null || layeredPaneBounds == null + || contentPaneBounds == null || menuBarBounds == null) + { + Insets i = rp.getInsets(); + int containerWidth = parent.getBounds().width - i.left - i.right; + int containerHeight = parent.getBounds().height - i.top - i.bottom; + + // 1. The glassPane fills entire viewable region (bounds - insets). + // 2. The layeredPane filles entire viewable region. + // 3. The titlePane is placed at the upper edge of the layeredPane. + // 4. The menuBar is positioned at the upper edge of layeredPane. + // 5. The contentPane fills viewable region minus menuBar minus + // titlePane, if present. + + // +-------------------------------+ + // | JLayeredPane | + // | +--------------------------+ | + // | | titlePane + | + // | +--------------------------+ | + // | +--------------------------+ | + // | | menuBar | | + // | +--------------------------+ | + // | +--------------------------+ | + // | |contentPane | | + // | | | | + // | | | | + // | | | | + // | +--------------------------+ | + // +-------------------------------+ + + // Setup titlePaneBounds. + if (titlePaneBounds == null) + titlePaneBounds = new Rectangle(); + titlePaneBounds.width = containerWidth; + titlePaneBounds.height = titlePane.getPreferredSize().height; + + // Setup menuBarBounds. + if (menuBarBounds == null) + menuBarBounds = new Rectangle(); + menuBarBounds.setBounds(0, + titlePaneBounds.y + titlePaneBounds.height, + containerWidth, 0); + if (menuBar != null) + { + Dimension menuBarSize = menuBar.getPreferredSize(); + if (menuBarSize.height > containerHeight) + menuBarBounds.height = containerHeight; + else + menuBarBounds.height = menuBarSize.height; + } + + // Setup contentPaneBounds. + if (contentPaneBounds == null) + contentPaneBounds = new Rectangle(); + contentPaneBounds.setBounds(0, + menuBarBounds.y + menuBarBounds.height, + containerWidth, + containerHeight - menuBarBounds.y + - menuBarBounds.height); + glassPaneBounds = new Rectangle(i.left, i.top, containerWidth, containerHeight); + layeredPaneBounds = new Rectangle(i.left, i.top, containerWidth, containerHeight); + } + + // Layout components. + glassPane.setBounds(glassPaneBounds); + layeredPane.setBounds(layeredPaneBounds); + if (menuBar != null) + menuBar.setBounds(menuBarBounds); + contentPane.setBounds(contentPaneBounds); + titlePane.setBounds(titlePaneBounds); + } + + } + + /** + * The shared UI instance for MetalRootPaneUIs. + */ private static MetalRootPaneUI instance = null; /** @@ -78,4 +897,84 @@ public class MetalRootPaneUI instance = new MetalRootPaneUI(); return instance; } + + /** + * Installs this UI to the root pane. If the + * <code>windowDecorationsStyle</code> property is set on the root pane, + * the Metal window decorations are installed on the root pane. + * + * @param c + */ + public void installUI(JComponent c) + { + super.installUI(c); + JRootPane rp = (JRootPane) c; + if (rp.getWindowDecorationStyle() != JRootPane.NONE) + installWindowDecorations(rp); + } + + /** + * Uninstalls the UI from the root pane. This performs the superclass + * behaviour and uninstalls the window decorations that have possibly been + * installed by {@link #installUI}. + * + * @param c the root pane + */ + public void uninstallUI(JComponent c) + { + JRootPane rp = (JRootPane) c; + if (rp.getWindowDecorationStyle() != JRootPane.NONE) + uninstallWindowDecorations(rp); + super.uninstallUI(c); + } + + /** + * Receives notification if any of the JRootPane's property changes. In + * particular this catches changes to the <code>windowDecorationStyle</code> + * property and installs the window decorations accordingly. + * + * @param ev the property change event + */ + public void propertyChange(PropertyChangeEvent ev) + { + String propertyName = ev.getPropertyName(); + if (propertyName.equals("windowDecorationStyle")) + { + JRootPane rp = (JRootPane) ev.getSource(); + if (rp.getWindowDecorationStyle() != JRootPane.NONE) + installWindowDecorations(rp); + else + uninstallWindowDecorations(rp); + } + } + + /** + * Installs the window decorations to the root pane. This sets up a border, + * a title pane and a layout manager that can layout the root pane with that + * title pane. + * + * @param rp the root pane. + */ + private void installWindowDecorations(JRootPane rp) + { + rp.setBorder(new MetalFrameBorder()); + rp.setLayout(new MetalRootLayout()); + // We should have a contentPane already. + assert rp.getLayeredPane().getComponentCount() == 1 + : "We should have a contentPane already"; + rp.getLayeredPane().add(new MetalTitlePane(rp), + JLayeredPane.FRAME_CONTENT_LAYER); + } + + /** + * Uninstalls the window decorations from the root pane. This should rarely + * be necessary, but we do it anyway. + * + * @param rp the root pane + */ + private void uninstallWindowDecorations(JRootPane rp) + { + rp.setBorder(null); + rp.getLayeredPane().remove(1); + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java b/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java index 0ff501f89a9..155bb814689 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalScrollBarUI.java @@ -41,6 +41,7 @@ package javax.swing.plaf.metal; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; +import java.awt.Insets; import java.awt.Rectangle; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -48,6 +49,7 @@ import java.beans.PropertyChangeListener; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JScrollBar; +import javax.swing.SwingConstants; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicScrollBarUI; @@ -465,11 +467,60 @@ public class MetalScrollBarUI extends BasicScrollBarUI */ protected Dimension getMinimumThumbSize() { - if (isFreeStanding) - return MIN_THUMB_SIZE_FREE_STANDING; + Dimension retVal; + if (scrollbar != null) + { + if (isFreeStanding) + retVal = MIN_THUMB_SIZE_FREE_STANDING; + else + retVal = MIN_THUMB_SIZE; + } else - return MIN_THUMB_SIZE; + retVal = new Dimension(0, 0); + return retVal; } - + + /** + * Returns the <code>preferredSize</code> for the specified scroll bar. + * For a vertical scrollbar the height is the sum of the preferred heights + * of the buttons plus <code>30</code>. The width is fetched from the + * <code>UIManager</code> property <code>ScrollBar.width</code>. + * + * For horizontal scrollbars the width is the sum of the preferred widths + * of the buttons plus <code>30</code>. The height is fetched from the + * <code>UIManager</code> property <code>ScrollBar.height</code>. + * + * @param c the scrollbar for which to calculate the preferred size + * + * @return the <code>preferredSize</code> for the specified scroll bar + */ + public Dimension getPreferredSize(JComponent c) + { + int height; + int width; + height = width = 0; + + if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL) + { + width += incrButton.getPreferredSize().getWidth(); + width += decrButton.getPreferredSize().getWidth(); + width += 30; + height = UIManager.getInt("ScrollBar.width"); + } + else + { + height += incrButton.getPreferredSize().getHeight(); + height += decrButton.getPreferredSize().getHeight(); + height += 30; + width = UIManager.getInt("ScrollBar.width"); + } + + Insets insets = scrollbar.getInsets(); + + height += insets.top + insets.bottom; + width += insets.left + insets.right; + + return new Dimension(width, height); + } } diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java index 34a964cb339..9c592bd5116 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalSplitPaneDivider.java @@ -42,11 +42,13 @@ import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; +import java.awt.Insets; import java.awt.LayoutManager; import java.awt.Point; import javax.swing.JSplitPane; import javax.swing.SwingConstants; +import javax.swing.border.Border; import javax.swing.plaf.basic.BasicArrowButton; import javax.swing.plaf.basic.BasicSplitPaneDivider; @@ -93,6 +95,12 @@ class MetalSplitPaneDivider extends BasicSplitPaneDivider public void paint(Graphics g) { Dimension s = getSize(); + + // Paint border if one exists. + Border border = getBorder(); + if (border != null) + border.paintBorder(this, g, 0, 0, s.width, s.height); + MetalUtils.fillMetalPattern(splitPane, g, 2, 2, s.width - 4, s.height - 4, light, dark); if (splitPane.isOneTouchExpandable()) diff --git a/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java b/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java index 50112ce2161..b9d5ea76434 100644 --- a/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java +++ b/libjava/classpath/javax/swing/plaf/metal/MetalUtils.java @@ -37,6 +37,8 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import gnu.classpath.SystemProperties; + import java.awt.Color; import java.awt.Component; import java.awt.Graphics; @@ -88,7 +90,8 @@ class MetalUtils static void fillMetalPattern(Component c, Graphics g, int x, int y, int w, int h, Color light, Color dark) { - if (g instanceof Graphics2D) + if (g instanceof Graphics2D + && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D") != null) fillMetalPattern2D((Graphics2D) g, x, y, w, h, light, dark); else { diff --git a/libjava/classpath/javax/swing/plaf/synth/ColorType.java b/libjava/classpath/javax/swing/plaf/synth/ColorType.java new file mode 100644 index 00000000000..954e309e1d6 --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/synth/ColorType.java @@ -0,0 +1,130 @@ +/* ColorType.java -- En enumeration of color types + 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.plaf.synth; + +/** + * A typesafe enumeration of color types. + * + * @author Roman Kennke (kennke@aicas.com) + * + * @since 1.5 + */ +public class ColorType +{ + + /** + * A constant used to identify the foreground color of a component. + */ + public static final ColorType FOREGROUND = new ColorType("Foreground"); + + /** + * A constant used to identify the background color of a component. + */ + public static final ColorType BACKGROUND = new ColorType("Background"); + + /** + * A constant used to identify the foreground color of text of a component. + */ + public static final ColorType TEXT_FOREGROUND + = new ColorType("TextForeground"); + + /** + * A constant used to identify the background color of text of a component. + */ + public static final ColorType TEXT_BACKGROUND + = new ColorType("TextBackground"); + + /** + * A constant used to identify the focus color of a component. + */ + public static final ColorType FOCUS = new ColorType("Focus"); + + /** + * The maximum number of color types. + */ + public static final int MAX_COUNT = 5; + + /** + * A counter used to assign an ID to the created color types. + */ + private static int count = 0; + + /** + * The ID of the color type. + */ + private int id; + + /** + * The description of the color type. + */ + private String description; + + /** + * Creates a new <code>Color</code> color type with the specified + * description. + * + * @param desc the textual description of the color type + */ + protected ColorType(String desc) + { + description = desc; + id = count; + count++; + } + + /** + * Returns the unique ID of the color type. + * + * @return the unique ID of the color type + */ + public final int getID() + { + return id; + } + + /** + * Returns the textual description of the color type. + * + * @return the textual description of the color type + */ + public String toString() + { + return description; + } +} diff --git a/libjava/classpath/javax/swing/plaf/synth/Region.java b/libjava/classpath/javax/swing/plaf/synth/Region.java new file mode 100644 index 00000000000..7ede65fc87b --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/synth/Region.java @@ -0,0 +1,474 @@ +/* Region.java -- Describes a region within a component + 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.plaf.synth; + +/** + * Describes a region of a component or the complete component. + * + * @author Roman Kennke (kennke@aicas.com) + * + * @since 1.5 + */ +public class Region +{ + + // FIXME: What should ui be for the non-component regions that have + // subregion==false? + + /** + * Specifies an arrow button region. + */ + public static final Region ARROW_BUTTON = + new Region("ArrowButton", null, false); + + /** + * Specifies the region of a standard button. + */ + public static final Region BUTTON = + new Region("Button", "ButtonUI", false); + + /** + * Specifies the region of a check box. + */ + public static final Region CHECK_BOX = + new Region("CheckBox", "CheckBoxUI", false); + + /** + * Specifies the region of a check box menu item. + */ + public static final Region CHECK_BOX_MENU_ITEM = + new Region("CheckBoxMenuItem", "CheckBoxMenuItemUI", false); + + /** + * Specifies the region of a colorchooser. + */ + public static final Region COLOR_CHOOSER = + new Region("ColorChooser", "ColorChooserUI", false); + + /** + * Specifies the region of a combo box. + */ + public static final Region COMBO_BOX = + new Region("ComboBox", "ComboBoxUI", false); + + /** + * Specifies the region of a desktop pane. + */ + public static final Region DESKTOP_PANE = + new Region("DesktopPane", "DesktopPaneUI", false); + + /** + * Specifies the region of a desktop icon. + */ + public static final Region DESKTOP_ICON = + new Region("DesktopIcon", "DesktopIconUI", false); + + /** + * Specifies the region of an editor pane. + */ + public static final Region EDITOR_PANE = + new Region("EditorPane", "EditorPaneUI", false); + + /** + * Specifies the region of a file chooser. + */ + public static final Region FILECHOOSER = + new Region("FileChooser", "FileChooserUI", false); + + /** + * Specifies the region of a formatted text field. + */ + public static final Region FormattedTextField = + new Region("FormattedTextField", "FormattedTextFieldUI", false); + + /** + * Specifies the region of an internal frame. + */ + public static final Region INTERNAL_FRAME = + new Region("InternalFrame", "InternalFrameUI", false); + + /** + * Specifies the region of the title pane of an internal frame. + */ + public static final Region INTERNAL_FRAME_TITLE_PANE = + new Region("InternalFrameTitlePane", "InternalFrameTitlePaneUI", false); + + /** + * Specifies the region of a label. + */ + public static final Region LABEL = + new Region("Label", "LabelUI", false); + + /** + * Specifies the region of a list. + */ + public static final Region LIST = + new Region("List", "ListUI", false); + + /** + * Specifies the region of a menu. + */ + public static final Region MENU = + new Region("Menu", "MenuUI", false); + + /** + * Specifies the region of a menu bar. + */ + public static final Region MENU_BAR = + new Region("MenuBar", "MenuBarUI", false); + + /** + * Specifies the region of a menu item. + */ + public static final Region MENU_ITEM = + new Region("MenuItem", "MenuItemUI", false); + + /** + * Specifies the region of a menu item accelerator. This is a subregion + * of menu item. + */ + public static final Region MENU_ITEM_ACCELERATOR = + new Region("MenuItemAccelerator", null, true); + + /** + * Specifies the region of an option pane. + */ + public static final Region OPTION_PANE = + new Region("OptionPane", "OptionPaneUI", false); + + /** + * Specifies the region of a panel. + */ + public static final Region PANEL = + new Region("Panel", "PanelUI", false); + + /** + * Specifies the region of a password field. + */ + public static final Region PASSWORD_FIELD = + new Region("PasswordField", "PasswordFieldUI", false); + + /** + * Specifies the region of a popup menu. + */ + public static final Region POPUP_MENU = + new Region("PopupMenu", "PopupMenuUI", false); + + /** + * Specifies the region of a popup menu separator. + */ + public static final Region POPUP_MENU_SEPARATOR = + new Region("PopupMenuSeparator", null, false); + + /** + * Specifies the region of a progress bar. + */ + public static final Region PROGRESS_BAR = + new Region("ProgressBar", "ProgressBarUI", false); + + /** + * Specifies the region of a radio button. + */ + public static final Region RADIO_BUTTON = + new Region("RadioButton", "RadioButtonUI", false); + + /** + * Specifies the region of a radio button menu item. + */ + public static final Region RADIO_BUTTON_MENU_ITEM = + new Region("RadioButtonMenuItem", "RadioButtonMenuItemUI", false); + + /** + * Specifies the region of a root pane. + */ + public static final Region ROOT_PANE = + new Region("RootPane", "RootPaneUI", false); + + /** + * Specifies the region of a scroll bar. + */ + public static final Region SCROLL_BAR = + new Region("ScrollBar", "ScrollBarUI", false); + + /** + * Specifies the region of a scroll bar track. This is a subregion of + * scroll bars. + */ + public static final Region SCROLL_BAR_TRACK = + new Region("ScrollBarTrack", null, true); + + /** + * Specifies the region of a scroll bar thumb. This is a subregion of + * scroll bars. + */ + public static final Region SCROLL_BAR_THUMB = + new Region("ScrollBarThumb", null, true); + + /** + * Specifies the region of a scroll pane. + */ + public static final Region SCROLL_PANE = + new Region("ScrollPane", "ScrollPaneUI", false); + + /** + * Specifies the region of a separator. + */ + public static final Region SEPARATOR = + new Region("Separator", "SeparatorUI", false); + + /** + * Specifies the region of a slider. + */ + public static final Region SLIDER = + new Region("Slider", "SliderUI", false); + + /** + * Specifies the region of a slider track. This is a subregion of a slider. + */ + public static final Region SLIDER_TRACK = + new Region("SliderTrack", null, true); + + /** + * Specifies the region of a slider thumb. This is a subregion of a slider. + */ + public static final Region SLIDER_THUMB = + new Region("SliderThumb", null, true); + + /** + * Specifies the region of a spinner. + */ + public static final Region SPINNER = + new Region("Spinner", "SpinnerUI", false); + + /** + * Specifies the region of a split pane. + */ + public static final Region SPLIT_PANE = + new Region("SplitPane", "SplitPaneUI", false); + + /** + * Specifies the region of a split pane divider. This is a subregion of + * a split pane. + */ + public static final Region SPLIT_PANE_DIVIDER = + new Region("SplitPaneDivider", null, true); + + /** + * Specifies the region of a tabbed pane. + */ + public static final Region TABBED_PANE = + new Region("TabbedPane", "TabbedPaneUI", false); + + /** + * This specifies the region of a tab of a tabbed pane. This is a subregion + * of a tabbed pane. + */ + public static final Region TABBED_PANE_TAB = + new Region("TabbedPaneTab", null, true); + + /** + * This specifies the region underneath the tabs of a tabbed pane. This is a + * subregion of a tabbed pane. + */ + public static final Region TABBED_PANE_TAB_AREA = + new Region("TabbedPaneTabArea", null, true); + + /** + * This specifies the region for the content of a tabbed pane. This is a + * subregion of a tabbed pane. + */ + public static final Region TABBED_PANE_CONTENT = + new Region("TabbedPaneContent", null, true); + + /** + * Specifies the region of a table. + */ + public static final Region TABLE = + new Region("Table", "TableUI", false); + + /** + * Specifies the region of a table header. + */ + public static final Region TABLE_HEADER = + new Region("TableHeader", "TableHeaderUI", false); + + /** + * Specifies the region of a text area. + */ + public static final Region TEXT_AREA = + new Region("TextArea", "TextAreaUI", false); + + /** + * Specifies the region of a text field. + */ + public static final Region TEXT_FIELD = + new Region("TextField", "TextFieldUI", false); + + /** + * Specifies the region of a text pane. + */ + public static final Region TEXT_PANE = + new Region("TextPane", "TextPaneUI", false); + + /** + * Specifies the region of a toggle button. + */ + public static final Region TOGGLE_BUTTON = + new Region("ToggleButton", "ToggleButtonUI", false); + + /** + * Specifies the region of a tool bar. + */ + public static final Region TOOL_BAR = + new Region("ToolBar", "ToolBarUI", false); + + /** + * Specifies the content region of a tool bar. This is a subregion of a tool + * bar. + */ + public static final Region TOOL_BAR_CONTENT = + new Region("ToolBarContent", null, true); + + /** + * Specifies the drag window region of a tool bar. This is a subregion of a + * tool bar. + */ + public static final Region TOOL_BAR_DRAG_WINDOW = + new Region("ToolBarDragWindow", null, false); + + /** + * Specifies the region of a tool tip. + */ + public static final Region TOOL_TIP = + new Region("ToolTip", "ToolTipUI", false); + + /** + * Specifies the region of a separator of a tool bar. This is a subregion of + * a tool bar. + */ + public static final Region TOOL_BAR_SEPARATOR = + new Region("ToolBarSeparator", null, false); + + /** + * Specifies the region of a tree. + */ + public static final Region TREE = + new Region("Tree", "TreeUI", false); + + /** + * Specifies the region of a tree cell. This is a subregion of a tree. + */ + public static final Region TREE_CELL = + new Region("TreeCell", null, true); + + /** + * Specifies the region of a viewport. + */ + public static final Region VIEWPORT = + new Region("Viewport", "ViewportUI", false); + + + /** + * The UI class id for the region. This is package private because this will + * be used by other classes in that package. + */ + String ui; + + /** + * The name of the region. + */ + private String name; + + /** + * If this region is a subregion or not. + */ + private boolean subregion; + + /** + * Creates a new <code>Region</code> with the specified name and ui ID. + * The <code>ui</code> must be the same what + * {@link javax.swing.JComponent#getUIClassID()} returns for toplevel regions. For + * subregions this should be <code>null</code>. + * + * @param name the name of the region + * @param ui the UI class ID of the region or <code>null</code> for + * subregions + * @param subregion <code>true</code> if this region is a subregion, + * <code>false</code> otherwise + */ + protected Region(String name, String ui, boolean subregion) + { + this.name = name; + this.ui = ui; + this.subregion = subregion; + } + + /** + * Returns <code>true</code> if this region describes a subregion of a + * component, <code>false</code> if it describes a component region itself. + * + * @return <code>true</code> if this region describes a subregion of a + * component, <code>false</code> if it describes a component region + * itself + */ + public boolean isSubregion() + { + return subregion; + } + + /** + * Returns the name of the region. + * + * @return the name of the region + */ + public String getName() + { + return name; + } + + /** + * Returns the name of the region. + * + * @return the name of the region + */ + public String toString() + { + return name; + } +} diff --git a/libjava/classpath/javax/swing/plaf/synth/SynthConstants.java b/libjava/classpath/javax/swing/plaf/synth/SynthConstants.java new file mode 100644 index 00000000000..306024c00b0 --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/synth/SynthConstants.java @@ -0,0 +1,85 @@ +/* SynthConstants.java -- A couple of constants used by Synth + 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.plaf.synth; + +/** + * A couple of constants used by the Synth Look and Feel. + * + * @author Roman Kennke (kennke@aicas.com) + * + * @since 1.5 + */ +public interface SynthConstants +{ + /** + * A primary state indicating that a component is enabled. + */ + static final int ENABLED = 1; + + /** + * A primary state indicating that a component is disabled. + */ + static final int DISABLED = 8; + + /** + * A primary state indicating that the mouse is over a region. + */ + static final int MOUSE_OVER = 2; + + /** + * A primary state indicating that the component is in a pressed state (which + * does not necessarily mean that the mouse is pressed over the component). + */ + static final int PRESSED = 4; + + /** + * Indicates that a region has focus. + */ + static final int FOCUSED = 256; + + /** + * Indicates that a region is selected. + */ + static final int SELECTED = 512; + + /** + * Indicates that a region is in its default state. + */ + static final int DEFAULT = 1024; +} diff --git a/libjava/classpath/javax/swing/plaf/synth/SynthContext.java b/libjava/classpath/javax/swing/plaf/synth/SynthContext.java new file mode 100644 index 00000000000..83536dae948 --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/synth/SynthContext.java @@ -0,0 +1,134 @@ +/* SynthContext.java -- Contextual information about a region + 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.plaf.synth; + +import javax.swing.JComponent; + +/** + * Contains some contextual information about a region. The information passed + * in objects of this class can only be considered valid during the method call + * that it was passed to. + * + * @author Roman Kennke (kennke@aicas.com) + * + * @since 1.5 + */ +public class SynthContext +{ + + /** + * The component. + */ + private JComponent component; + + /** + * The region of the component. + */ + private Region region; + + /** + * The style of the component. + */ + private SynthStyle style; + + /** + * The state of the component. + */ + private int state; + + /** + * Creates a new <code>SynthContext</code> object. + * + * @param component the component for which this context is used + * @param region the region of the component + * @param style the style associated with the component + * @param state a or'ed bitmask of the constants from {@link SynthConstants} + */ + public SynthContext(JComponent component, Region region, SynthStyle style, + int state) + { + this.component = component; + this.region = region; + this.style = style; + this.state = state; + } + + /** + * Returns the component that contains the region. + * + * @return the component that contains the region + */ + public JComponent getComponent() + { + return component; + } + + /** + * Returns the region that identifies this state. + * + * @return the region that identifies this state + */ + public Region getRegion() + { + return region; + } + + /** + * Returns the style of the region. + * + * @return the style of the region + */ + public SynthStyle getStyle() + { + return style; + } + + /** + * Returns the state of the component. This is a or'ed bitmask of the + * constants defined in {@link SynthConstants}. + * + * @return the state of the component + * + * @see SynthConstants + */ + public int getComponentState() + { + return state; + } +} diff --git a/libjava/classpath/javax/swing/plaf/synth/SynthGraphicsUtils.java b/libjava/classpath/javax/swing/plaf/synth/SynthGraphicsUtils.java new file mode 100644 index 00000000000..a68b6f6ec2f --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/synth/SynthGraphicsUtils.java @@ -0,0 +1,283 @@ +/* SynthGraphicsUtils.java -- Wrapper for graphics primitives used in Synth + 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.plaf.synth; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Rectangle; + +import javax.swing.Icon; +import javax.swing.SwingUtilities; + +/** + * Wrapper for graphics primitives used in Synth. + * + * @author Roman Kennke (kennke@aicas.com) + * + * @since 1.5 + */ +public class SynthGraphicsUtils + +{ + /** + * Creates a new <code>SynthGraphicsUtils</code> object. + */ + public SynthGraphicsUtils() + { + // Nothing to do here. + } + + /** + * Draws a line from (x1,y1) to (x2,y2). + * + * @param ctx the synth context, identifies the region + * @param paintKey identifies the portion of the component to be painted, may + * be <code>null</code> + * @param g the graphics context to use for painting + * @param x1 the x coordinate of the start point + * @param y1 the y coordinate of the start point + * @param x2 the x coordinate of the end point + * @param y2 the y coordinate of the end point + */ + public void drawLine(SynthContext ctx, Object paintKey, Graphics g, int x1, + int y1, int x2, int y2) + { + // TODO: Correct? + g.drawLine(x1, y1, x2, y2); + } + + /** + * Lays out a label and (if non-null) an icon. The calculated coordinates are + * then stored in <code>viewR</code>, <code>iconR</code> and + * <code>textR</code>. + * + * The alignment and position parameters may be one of the alignment or + * position constants defined in {@link javax.swing.SwingConstants}. + * + * @param ctx the synth context, identifies the current region + * @param fm the font metrics to use to fetch the text measures + * @param text the text to lay out, may be <code>null</code> + * @param icon the icon to lay out, may be <code>null</code> + * @param hAlign the horizontal alignment of the label + * @param vAlign the vertical alignment of the label + * @param hTextPos the horizontal text position + * @param vTextPos the vertical text position + * @param viewR the view rectangle (return parameter) + * @param iconR the icon rectangle (return parameter) + * @param textR the text rectangle (return parameter) + * @param iconTextGap the gap between text and label + * + * @return the label text, may be shortened + */ + public String layoutText(SynthContext ctx, FontMetrics fm, String text, + Icon icon, int hAlign, int vAlign, int hTextPos, + int vTextPos, Rectangle viewR, Rectangle iconR, + Rectangle textR, int iconTextGap) + { + return SwingUtilities.layoutCompoundLabel(fm, text, icon, vAlign, hAlign, + vTextPos, hTextPos, viewR, iconR, + textR, iconTextGap); + } + + /** + * Returns the width of the string <code>text</code> for the specified font + * and font metrics. + * + * @param ctx identifies the current region + * @param font the font + * @param fm the font metrics to use + * @param text the text to be measured + * + * @return the width of the string <code>text</code> for the specified font + * and font metrics + */ + public int computeStringWidth(SynthContext ctx, Font font, FontMetrics fm, + String text) + { + return fm.stringWidth(text); + } + + /** + * Calculates the minimums size that is needed to render the label with + * <code>text</code> and <code>icon</code> correctly. + * + * @param ctx identifies the current region + * @param font the font to use + * @param text the label text + * @param icon the label icon + * @param hAlign the horizontal alignment + * @param vAlign the vertical alignment + * @param hTextPosition the horizontal text position + * @param vTextPosition the vertical text position + * @param iconTextGap the gap between icon and text + * @param mnemonicIndex index to the mnemonic character within + * <code>text</code> + * + * @return the minimums size that is needed to render the label with + * <code>text</code> and <code>icon</code> correctly + */ + public Dimension getMinimumSize(SynthContext ctx, Font font, String text, + Icon icon, int hAlign, int vAlign, + int hTextPosition,int vTextPosition, + int iconTextGap,int mnemonicIndex) + { + // FIXME: Implement this correctly. + return new Dimension(0, 0); + } + + /** + * Calculates the preferred size that is needed to render the label with + * <code>text</code> and <code>icon</code> correctly. + * + * @param ctx identifies the current region + * @param font the font to use + * @param text the label text + * @param icon the label icon + * @param hAlign the horizontal alignment + * @param vAlign the vertical alignment + * @param hTextPosition the horizontal text position + * @param vTextPosition the vertical text position + * @param iconTextGap the gap between icon and text + * @param mnemonicIndex index to the mnemonic character within + * <code>text</code> + * + * @return the preferred size that is needed to render the label with + * <code>text</code> and <code>icon</code> correctly + */ + public Dimension getPreferredSize(SynthContext ctx, Font font, String text, + Icon icon, int hAlign, int vAlign, + int hTextPosition,int vTextPosition, + int iconTextGap,int mnemonicIndex) + { + // FIXME: Implement this correctly. + return new Dimension(0, 0); + } + + /** + * Calculates the maximum size that is needed to render the label with + * <code>text</code> and <code>icon</code> correctly. + * + * @param ctx identifies the current region + * @param font the font to use + * @param text the label text + * @param icon the label icon + * @param hAlign the horizontal alignment + * @param vAlign the vertical alignment + * @param hTextPosition the horizontal text position + * @param vTextPosition the vertical text position + * @param iconTextGap the gap between icon and text + * @param mnemonicIndex index to the mnemonic character within + * <code>text</code> + * + * @return the maximum size that is needed to render the label with + * <code>text</code> and <code>icon</code> correctly + */ + public Dimension getMaximumSize(SynthContext ctx, Font font, String text, + Icon icon, int hAlign, int vAlign, + int hTextPosition,int vTextPosition, + int iconTextGap,int mnemonicIndex) + { + // FIXME: Implement this correctly. + return new Dimension(0, 0); + } + + /** + * Returns the maximum character height of the font from the component of the + * passed in <code>context</code>. + * + * @param context identifies the current component and region + * + * @return the maximum character height of the font from the component of the + * passed in <code>context</code> + */ + public int getMaximumCharHeight(SynthContext context) + { + Component comp = context.getComponent(); + Font font = comp.getFont(); + return comp.getFontMetrics(font).getHeight(); + } + + /** + * Renders the specified <code>text</code> within the <code>bounds</code>. + * + * @param ctx identifies the component and region + * @param g the graphics context for drawing the tetx + * @param text the text to be rendered + * @param bounds the bounds within which the text should be rendered + * @param mnemonicIndex the index of the mnemonic character within + * <code>text</code> + */ + public void paintText(SynthContext ctx, Graphics g, String text, + Rectangle bounds, int mnemonicIndex) + { + // FIXME: This is very primitive and should be improved to paint the + // mnemonic char. + g.drawString(text, bounds.x, bounds.y); + } + + /** + * Renders the specified <code>text</code> at the specified location. + * + * @param ctx identifies the component and region + * @param g the graphics context for drawing the tetx + * @param text the text to be rendered + * @param x the X location where the text should be rendered + * @param y the Y location where the text should be rendered + * @param mnemonicIndex the index of the mnemonic character within + * <code>text</code> + */ + public void paintText(SynthContext ctx, Graphics g, String text, + int x, int y, int mnemonicIndex) + { + // FIXME: This is very primitive and should be improved to paint the + // mnemonic char. + g.drawString(text, x, y); + } + + public void paintText(SynthContext ctx, Graphics g, String text, Icon icon, + int hAlign, int vAlign, int hTextPosition, + int vTextPosition, int iconTextGap, int mnemonicIndex, + int textOffset) + { + // FIXME: Implement this correctly. + } +} diff --git a/libjava/classpath/javax/swing/plaf/synth/SynthLookAndFeel.java b/libjava/classpath/javax/swing/plaf/synth/SynthLookAndFeel.java new file mode 100644 index 00000000000..8d0596d416e --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/synth/SynthLookAndFeel.java @@ -0,0 +1,272 @@ +/* SynthLookAndFeel.java -- A skinnable Swing look and feel + 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.plaf.synth; + +import java.awt.Component; +import java.io.InputStream; +import java.text.ParseException; + +import javax.swing.JComponent; +import javax.swing.UIDefaults; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicLookAndFeel; + + +/** + * A look and feel that can be customized either by providing a file to + * {@link #load} or by setting a {@link SynthStyleFactory} using + * {@link #setStyleFactory}. + * + * @author Roman Kennke (kennke@aicas.com) + * + * @since 1.5 + */ +public class SynthLookAndFeel + extends BasicLookAndFeel +{ + + /** + * The style factory that will be used by the UI classes to load their + * style sets from. + */ + private static SynthStyleFactory styleFactory; + + /** + * Creates a new instance of <code>SynthLookAndFeel</code>. In order to use + * the Synth look and feel you either need to call {@link #load} to load a + * set of styles from an XML file, or you need to call + * {@link #setStyleFactory} to provide your own style factory. + */ + public SynthLookAndFeel() + { + // FIXME: What to do here, if anything? + } + + /** + * Sets the style factory that the UI classes of Synth will use to load their + * sets of styles. + * + * @param sf the style factory to set + */ + public static void setStyleFactory(SynthStyleFactory sf) + { + styleFactory = sf; + } + + /** + * Returns the current style factory that the UI classes of Synth will use to + * load their sets of styles. + * + * @return the current style factory + */ + public static SynthStyleFactory getStyleFactory() + { + return styleFactory; + } + + /** + * Returns the style for the specified component and region. + * + * @param c the component for which to return the style + * @param r the region of the component for which to return the style + * + * @return the style for the specified component and region + */ + public static SynthStyle getStyle(JComponent c, Region r) + { + return getStyleFactory().getStyle(c, r); + } + + /** + * Updates all style information of the component and it's children. + * + * @param c the componenent for which to update the style + */ + public static void updateStyles(Component c) + { + // FIXME: Implement this properly. + } + + /** + * Returns the region for a given Swing component. + * + * @param c the Swing component for which to fetch the region + * + * @return the region for a given Swing component + */ + public static Region getRegion(JComponent c) + { + // FIXME: This can be implemented as soon as we have the component UI + // classes in place, since this region will be matched via the UI classes. + return null; + } + + /** + * Creates the Synth look and feel component UI instance for the given + * component. + * + * @param c the component for which to create a UI instance + * + * @return the Synth look and feel component UI instance for the given + * component + */ + public static ComponentUI createUI(JComponent c) + { + // FIXME: This can be implemented as soon as we have the component UI + // classes in place. + return null; + } + + /** + * Initializes this look and feel. + */ + public void initialize() + { + super.initialize(); + // TODO: Implement at least the following here: + // if (styleFactory != null) + // styleFactory = new DefaultStyleFactory(); + } + + /** + * Uninitializes the look and feel. + */ + public void uninitialize() + { + super.uninitialize(); + // TODO: What to do here? + } + + /** + * Returns the UI defaults of this look and feel. + * + * @return the UI defaults of this look and feel + */ + public UIDefaults getDefaults() + { + // FIXME: This is certainly wrong. The defaults should be fetched/merged + // from the file from which the l&f is loaded. + return super.getDefaults(); + } + + /** + * FIXME: DOCUMENT ME! + * + * @return FIXME + */ + public boolean shouldUpdateStyleOnAncestorChanged() + { + return false; + } + + /** + * Loads a set of {@link SynthStyle}s that are used for the look and feel of + * the components. The <code>resourceBase</code> parameter is used to resolve + * references against, like icons and other files. + * + * @param in the input stream from where to load the styles + * @param resourceBase the base against which references are resolved. + * + * @throws ParseException if the input stream cannot be parsed + * @throws IllegalArgumentException if one of the parameters is + * <code>null</code> + */ + // FIXME: The signature in the JDK has a Class<?> here. Should be fixed as + // soon as we switch to the generics branch. + public void load(InputStream in, Class resourceBase) + throws ParseException, IllegalArgumentException + { + // FIXME: Implement this correctly. + } + + /** + * Returns a textual description of the Synth look and feel. This returns + * "Synt look and feel". + * + * @return a textual description of the Synth look and feel + */ + public String getDescription() + { + return "Synth look and feel"; + } + + /** + * Returns the ID of the Synth look and feel. This returns "Synth". + * + * @return the ID of the Synth look and feel + */ + public String getID() + { + return "Synth"; + } + + /** + * Returns the name of the Synth look and feel. This returns + * "Synt look and feel". + * + * @return the name of the Synth look and feel + */ + public String getName() + { + return "Synth look and feel"; + } + + /** + * Returns <code>false</code> since the Synth look and feel is not a native + * look and feel. + * + * @return <code>false</code> + */ + public boolean isNativeLookAndFeel() + { + return false; + } + + /** + * Returns <code>true</code> since the Synth look and feel is always a + * supported look and feel. + * + * @return <code>true</code> + */ + public boolean isSupportedLookAndFeel() + { + return true; + } + +} diff --git a/libjava/classpath/javax/swing/plaf/synth/SynthPainter.java b/libjava/classpath/javax/swing/plaf/synth/SynthPainter.java new file mode 100644 index 00000000000..0d63c6db76f --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/synth/SynthPainter.java @@ -0,0 +1,80 @@ +/* SynthPainter.java -- An abstract painter for synth components + 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.plaf.synth; + +import java.awt.Graphics; + +/** + * The abstract definition of a delegate that takes the responsibility of + * painting for the components. + * + * This class is defined to be abstract and all methods are no-ops. + * + * @author Roman Kennke (kennke@aicas.com) + * + * @since 1.5 + */ +public abstract class SynthPainter +{ + + /** + * Creates a new <code>SynthPainter</code> object. + */ + public SynthPainter() + { + // Nothing to do here. + } + + /** + * Paints the background of an arrow button. + * + * @param ctx the synth context identifying the component and region for + * painting + * @param g the graphics context to use for painting + * @param x the X coordinate of the area to paint + * @param y the Y coordinate of the area to paint + * @param w the width of the area to paint + * @param h the height of the area to paint + */ + public void paintArrowButtonBackground(SynthContext ctx, Graphics g, int x, + int y, int w, int h) + { + // Nothing to do here. + } +} diff --git a/libjava/classpath/javax/swing/plaf/synth/SynthStyle.java b/libjava/classpath/javax/swing/plaf/synth/SynthStyle.java new file mode 100644 index 00000000000..e0a8dbcc7b9 --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/synth/SynthStyle.java @@ -0,0 +1,144 @@ +/* SynthStyle.java -- A set of style properties + 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.plaf.synth; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Insets; + +import javax.swing.Icon; + +/** + * A set of style properties that can be installed on a component. + * + * @author Roman Kennke (kennke@aicas.com) + * + * @since 1.5 + */ +public abstract class SynthStyle +{ + + /** + * Creates a new <code>SynthStyle</code> object. + */ + public SynthStyle() + { + // FIXME: Implement this correctly. + } + + public SynthGraphicsUtils getGraphicsUtils(SynthContext ctx) + { + // FIXME: Implement this correctly. + return null; + } + + public Color getColor(SynthContext ctx, ColorType type) + { + // FIXME: Implement this correctly. + return null; + } + + public abstract Color getColorForState(SynthContext ctx, ColorType type); + + public Font getFont(SynthContext ctx) + { + // FIXME: Implement this correctly. + return null; + } + + public abstract Font getFontForState(SynthContext ctx); + + public Insets getInsets(SynthContext ctx) + { + // FIXME: Implement this correctly. + return null; + } + + public SynthPainter getPainted(SynthContext ctx) + { + // FIXME: Implement this correctly. + return null; + } + + public boolean isOpaque(SynthContext ctx) + { + // FIXME: Implement this correctly. + return true; + } + + public Object get(SynthContext ctx, Object key) + { + // FIXME: Implement this correctly. + return null; + } + + public void installDefaults(SynthContext ctx) + { + // FIXME: Implement this correctly. + } + + public void uninstallDefaults(SynthContext ctx) + { + // FIXME: Implement this correctly. + } + + public int getInt(SynthContext ctx, Object key, int defaultValue) + { + // FIXME: Implement this correctly. + return -1; + } + + public boolean getBoolean(SynthContext ctx, Object key, boolean defaultValue) + { + // FIXME: Implement this correctly. + return false; + } + + public Icon getIcon(SynthContext ctx, Object key) + { + // FIXME: Implement this correctly. + return null; + } + + public String getString(SynthContext ctx, Object key, String defaultValue) + { + // FIXME: Implement this correctly. + return null; + } +} diff --git a/libjava/classpath/javax/swing/plaf/synth/SynthStyleFactory.java b/libjava/classpath/javax/swing/plaf/synth/SynthStyleFactory.java new file mode 100644 index 00000000000..569753d8ad7 --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/synth/SynthStyleFactory.java @@ -0,0 +1,64 @@ +/* SynthStyleFactory.java -- A factory for SynthStyles + 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.plaf.synth; + +import javax.swing.JComponent; + +public abstract class SynthStyleFactory +{ + + /** + * Creates a new <code>SynthStyleFactory</code>. + */ + public SynthStyleFactory() + { + // Nothing to do here. + } + + /** + * Returns a {@link SynthStyle} for the specified region of the specified + * component. + * + * @param c the component for which to create a style + * @param id the region of the component + * + * @return a style for the specified region of the specified component + */ + public abstract SynthStyle getStyle(JComponent c, Region id); +} diff --git a/libjava/classpath/javax/swing/plaf/synth/package.html b/libjava/classpath/javax/swing/plaf/synth/package.html new file mode 100644 index 00000000000..b977e468c90 --- /dev/null +++ b/libjava/classpath/javax/swing/plaf/synth/package.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- package.html - describes classes in javax.swing.plaf.metal package. + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. --> + +<html> +<head><title>GNU Classpath - javax.swing.plaf.synth</title></head> + +<body> +<p>Provides a look and feel that can be customized by and XML file or by + providing a custom {@link SynthStyleFactory}. +</p> +</body> +</html> diff --git a/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java b/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java index 233528c7d67..0d9b62567f6 100644 --- a/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java +++ b/libjava/classpath/javax/swing/table/DefaultTableCellRenderer.java @@ -127,7 +127,8 @@ public class DefaultTableCellRenderer extends JLabel * Get the string value of the object and pass it to setText(). * * @param table the JTable - * @param value the value of the object + * @param value the value of the object. For the text content, + * null is rendered as an empty cell. * @param isSelected is the cell selected? * @param hasFocus has the cell the focus? * @param row the row to render @@ -140,13 +141,7 @@ public class DefaultTableCellRenderer extends JLabel boolean hasFocus, int row, int column) { - if (value != null) - { - if (value instanceof JTextField) - return new JTextField(((JTextField)value).getText()); - super.setText(value.toString()); - } - + setValue(value); setOpaque(true); if (table == null) @@ -274,6 +269,10 @@ public class DefaultTableCellRenderer extends JLabel */ protected void setValue(Object value) { - super.setText((value!=null) ? value.toString() : ""); + if (value != null) + setText(value.toString()); + else + // null is rendered as an empty cell. + setText(""); } } diff --git a/libjava/classpath/javax/swing/table/DefaultTableModel.java b/libjava/classpath/javax/swing/table/DefaultTableModel.java index 6844f2a27ec..c281caa0791 100644 --- a/libjava/classpath/javax/swing/table/DefaultTableModel.java +++ b/libjava/classpath/javax/swing/table/DefaultTableModel.java @@ -486,7 +486,10 @@ public class DefaultTableModel extends AbstractTableModel } /** - * Returns the name of the specified column. + * Get the name of the column. If the column has the column identifier set, + * the return value is the result of the .toString() method call on that + * identifier. If the identifier is not explicitly set, the returned value + * is calculated by {@link AbstractTableModel#getColumnName(int)}. * * @param column the column index. * diff --git a/libjava/classpath/javax/swing/table/JTableHeader.java b/libjava/classpath/javax/swing/table/JTableHeader.java index 163509a45c2..4e8dcd7b7ef 100644 --- a/libjava/classpath/javax/swing/table/JTableHeader.java +++ b/libjava/classpath/javax/swing/table/JTableHeader.java @@ -67,6 +67,11 @@ import javax.swing.event.TableColumnModelEvent; import javax.swing.event.TableColumnModelListener; import javax.swing.plaf.TableHeaderUI; +/** + * Represents the table header. The header displays the column header values, + * is always visible event if the rest of the table scrolls up and down and + * supports column reordering and resizing with mouse. + */ public class JTableHeader extends JComponent implements TableColumnModelListener, Accessible { @@ -306,7 +311,10 @@ public class JTableHeader extends JComponent } }; } - + + /** + * Use serialVersionUid for interoperability. + */ private static final long serialVersionUID = 5144633983372967710L; /** @@ -409,9 +417,10 @@ public class JTableHeader extends JComponent } /** - * Get the value of the {@link #draggedColumn} property. + * Get the column that is currently being dragged. This is used when + * handling the column reordering with mouse. * - * @return The current value of the property + * @return the column being dragged, null if none. */ public TableColumn getDraggedColumn() { @@ -429,29 +438,34 @@ public class JTableHeader extends JComponent } /** - * Get the value of the {@link #reorderingAllowed} property. + * Check if it is possible to reorder the table columns by dragging column + * header with mouse. The table reordering is enabled by default, but can be + * disabled with {@link #setReorderingAllowed(boolean)}. * - * @return The current value of the property - */ + * @return true if reordering is allowed, false otherwise. + */ public boolean getReorderingAllowed() { return reorderingAllowed; } /** - * Get the value of the {@link #resizingAllowed} property. + * Check if it is possible to resize the table columns by dragging the column + * boundary in the table header with mouse. The resizing is enabled + * by default, but can be disabled with {@link #setResizingAllowed(boolean)}. * - * @return The current value of the property - */ + * @return true if resizing is allowed, false otherwise. + */ public boolean getResizingAllowed() { return resizingAllowed; } /** - * Get the value of the {@link #resizingColumn} property. + * Get the column that is currently being resized. This is used when + * handling the column resizing with mouse. * - * @return The current value of the property + * @return the column being currently resized, null if none. */ public TableColumn getResizingColumn() { @@ -459,9 +473,9 @@ public class JTableHeader extends JComponent } /** - * Get the value of the {@link #table} property. + * Get the table, having this header. * - * @return The current value of the property + * @return the table, having this header. */ public JTable getTable() { @@ -501,13 +515,15 @@ public class JTableHeader extends JComponent } /** - * Set the value of the {@link #draggedColumn} property. + * Set the column that is currently being dragged. This is used when + * dragging the column with mouse. Setting to null will stop the + * dragging session immediately. * - * @param d The new value of the property + * @param draggingIt the column being currently dragged, null if none. */ - public void setDraggedColumn(TableColumn d) + public void setDraggedColumn(TableColumn draggingIt) { - draggedColumn = d; + draggedColumn = draggingIt; } /** @@ -531,33 +547,39 @@ public class JTableHeader extends JComponent } /** - * Set the value of the {@link #reorderingAllowed} property. + * Set the table ability to reorder columns by dragging column header + * with mouse. The table reordering is enabled by default, but can be + * disabled with this method. * - * @param r The new value of the property + * @param allowed true if reordering is allowed, false otherwise. */ - public void setReorderingAllowed(boolean r) + public void setReorderingAllowed(boolean allowed) { - reorderingAllowed = r; + reorderingAllowed = allowed; } /** - * Set the value of the {@link #resizingAllowed} property. + * Set the table ability to resize columns by dragging the column + * boundary in the table header with mouse. The resizing is enabled + * by default, but can be disabled using this method. * - * @param r The new value of the property + * @param allowed true if resizing is allowed, false otherwise. */ - public void setResizingAllowed(boolean r) + public void setResizingAllowed(boolean allowed) { - resizingAllowed = r; + resizingAllowed = allowed; } /** - * Set the value of the {@link #resizingColumn} property. + * The the column that is currently being resized. This property is used + * when handling table resizing with mouse. Setting to null would stop + * the resizing session immediately. * - * @param r The new value of the property + * @param resizingIt the column being currently resized */ - public void setResizingColumn(TableColumn r) + public void setResizingColumn(TableColumn resizingIt) { - resizingColumn = r; + resizingColumn = resizingIt; } /** @@ -609,7 +631,14 @@ public class JTableHeader extends JComponent { this.cellRenderer = cellRenderer; } - + + /** + * Get the rectangle, occupied by the header of the given column. + * + * @param column the column, for that the header area is requested. + * + * @return the column header area. + */ public Rectangle getHeaderRect(int column) { Rectangle r = getTable().getCellRect(-1, column, false); diff --git a/libjava/classpath/javax/swing/text/AbstractDocument.java b/libjava/classpath/javax/swing/text/AbstractDocument.java index c7353885e12..a3e8c463bd3 100644 --- a/libjava/classpath/javax/swing/text/AbstractDocument.java +++ b/libjava/classpath/javax/swing/text/AbstractDocument.java @@ -538,18 +538,24 @@ public abstract class AbstractDocument implements Document, Serializable DefaultDocumentEvent event = new DefaultDocumentEvent(offset, text.length(), DocumentEvent.EventType.INSERT); - - writeLock(); - UndoableEdit undo = content.insertString(offset, text); - if (undo != null) - event.addEdit(undo); - insertUpdate(event, attributes); - writeUnlock(); + try + { + writeLock(); + UndoableEdit undo = content.insertString(offset, text); + if (undo != null) + event.addEdit(undo); + + insertUpdate(event, attributes); - fireInsertUpdate(event); - if (undo != null) - fireUndoableEditUpdate(new UndoableEditEvent(this, undo)); + fireInsertUpdate(event); + if (undo != null) + fireUndoableEditUpdate(new UndoableEditEvent(this, undo)); + } + finally + { + writeUnlock(); + } } /** @@ -640,6 +646,12 @@ public abstract class AbstractDocument implements Document, Serializable // more times than you've previously called lock, but it doesn't make // sure that the threads calling unlock were the same ones that called lock + // If the current thread holds the write lock, and attempted to also obtain + // a readLock, then numReaders hasn't been incremented and we don't need + // to unlock it here. + if (currentWriter == Thread.currentThread()) + return; + // FIXME: the reference implementation throws a // javax.swing.text.StateInvariantError here if (numReaders == 0) @@ -675,18 +687,21 @@ public abstract class AbstractDocument implements Document, Serializable new DefaultDocumentEvent(offset, length, DocumentEvent.EventType.REMOVE); - removeUpdate(event); - - boolean shouldFire = content.getString(offset, length).length() != 0; - - writeLock(); - UndoableEdit temp = content.remove(offset, length); - writeUnlock(); - - postRemoveUpdate(event); - - if (shouldFire) - fireRemoveUpdate(event); + try + { + writeLock(); + + // The order of the operations below is critical! + removeUpdate(event); + UndoableEdit temp = content.remove(offset, length); + + postRemoveUpdate(event); + fireRemoveUpdate(event); + } + finally + { + writeUnlock(); + } } /** @@ -841,7 +856,7 @@ public abstract class AbstractDocument implements Document, Serializable */ protected void writeLock() { - if (currentWriter!= null && currentWriter.equals(Thread.currentThread())) + if (currentWriter != null && currentWriter.equals(Thread.currentThread())) return; synchronized (documentCV) { @@ -1330,11 +1345,11 @@ public abstract class AbstractDocument implements Document, Serializable public Object getAttribute(Object key) { Object result = attributes.getAttribute(key); - if (result == null && element_parent != null) + if (result == null) { - AttributeSet parentSet = element_parent.getAttributes(); - if (parentSet != null) - result = parentSet.getAttribute(key); + AttributeSet resParent = getResolveParent(); + if (resParent != null) + result = resParent.getAttribute(key); } return result; } @@ -1371,9 +1386,7 @@ public abstract class AbstractDocument implements Document, Serializable */ public AttributeSet getResolveParent() { - if (attributes.getResolveParent() != null) - return attributes.getResolveParent(); - return element_parent.getAttributes(); + return attributes.getResolveParent(); } /** @@ -1573,6 +1586,18 @@ public abstract class AbstractDocument implements Document, Serializable private Element[] children = new Element[0]; /** + * The cached startOffset value. This is used in the case when a + * BranchElement (temporarily) has no child elements. + */ + private int startOffset; + + /** + * The cached endOffset value. This is used in the case when a + * BranchElement (temporarily) has no child elements. + */ + private int endOffset; + + /** * Creates a new <code>BranchElement</code> with the specified * parent and attributes. * @@ -1583,6 +1608,8 @@ public abstract class AbstractDocument implements Document, Serializable public BranchElement(Element parent, AttributeSet attributes) { super(parent, attributes); + startOffset = -1; + endOffset = -1; } /** @@ -1655,7 +1682,7 @@ public abstract class AbstractDocument implements Document, Serializable // return 0 if (offset < getStartOffset()) return 0; - + // XXX: There is surely a better algorithm // as beginning from first element each time. for (int index = 0; index < children.length - 1; ++index) @@ -1695,9 +1722,15 @@ public abstract class AbstractDocument implements Document, Serializable */ public int getEndOffset() { - if (getElementCount() == 0) - throw new NullPointerException("This BranchElement has no children."); - return children[children.length - 1].getEndOffset(); + if (children.length == 0) + { + if (endOffset == -1) + throw new NullPointerException("BranchElement has no children."); + } + else + endOffset = children[children.length - 1].getEndOffset(); + + return endOffset; } /** @@ -1718,13 +1751,20 @@ public abstract class AbstractDocument implements Document, Serializable * * @return the start offset of this element inside the document model * - * @throws NullPointerException if this branch element has no children + * @throws NullPointerException if this branch element has no children and + * no startOffset value has been cached */ public int getStartOffset() { - if (getElementCount() == 0) - throw new NullPointerException("This BranchElement has no children."); - return children[0].getStartOffset(); + if (children.length == 0) + { + if (startOffset == -1) + throw new NullPointerException("BranchElement has no children."); + } + else + startOffset = children[0].getStartOffset(); + + return startOffset; } /** @@ -2022,13 +2062,29 @@ public abstract class AbstractDocument implements Document, Serializable /** The serialization UID (compatible with JDK1.5). */ private static final long serialVersionUID = -8906306331347768017L; - /** Manages the start offset of this element. */ - Position startPos; + /** + * Manages the start offset of this element. + */ + private Position startPos; + + /** + * Manages the end offset of this element. + */ + private Position endPos; - /** Manages the end offset of this element. */ - 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> @@ -2040,20 +2096,18 @@ public abstract class AbstractDocument implements Document, Serializable int end) { super(parent, attributes); - { - try + int len = content.length(); + startDelta = 0; + if (start > len) + startDelta = start - len; + endDelta = 0; + if (end > len) + endDelta = end - len; + try { - if (parent != null) - { - startPos = parent.getDocument().createPosition(start); - endPos = parent.getDocument().createPosition(end); - } - else - { - startPos = createPosition(start); - endPos = createPosition(end); + startPos = createPosition(start - startDelta); + endPos = createPosition(end - endDelta); } - } catch (BadLocationException ex) { AssertionError as; @@ -2064,7 +2118,6 @@ public abstract class AbstractDocument implements Document, Serializable as.initCause(ex); throw as; } - } } /** @@ -2136,7 +2189,7 @@ public abstract class AbstractDocument implements Document, Serializable */ public int getEndOffset() { - return endPos.getOffset(); + return endPos.getOffset() + endDelta; } /** @@ -2162,7 +2215,7 @@ public abstract class AbstractDocument implements Document, Serializable */ public int getStartOffset() { - return startPos.getOffset(); + return startPos.getOffset() + startDelta; } /** diff --git a/libjava/classpath/javax/swing/text/AsyncBoxView.java b/libjava/classpath/javax/swing/text/AsyncBoxView.java new file mode 100644 index 00000000000..1988bbadcc8 --- /dev/null +++ b/libjava/classpath/javax/swing/text/AsyncBoxView.java @@ -0,0 +1,1480 @@ +/* AsyncBoxView.java -- A box view that performs layout asynchronously + 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.awt.Component; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Shape; +import java.util.ArrayList; + +import javax.swing.event.DocumentEvent; +import javax.swing.text.Position.Bias; + +/** + * A {@link View} implementation that lays out its child views in a box, either + * vertically or horizontally. The difference to {@link BoxView} is that the + * layout is performed in an asynchronous manner. This helps to keep the + * eventqueue free from non-GUI related tasks. + * + * This view is currently not used in standard text components. In order to + * use it you would have to implement a special {@link EditorKit} with a + * {@link ViewFactory} that returns this view. For example: + * + * <pre> + * static class AsyncEditorKit extends StyledEditorKit implements ViewFactory + * { + * public View create(Element el) + * { + * if (el.getName().equals(AbstractDocument.SectionElementName)) + * return new AsyncBoxView(el, View.Y_AXIS); + * return super.getViewFactory().create(el); + * } + * public ViewFactory getViewFactory() { + * return this; + * } + * } + * </pre> + * + * @author Roman Kennke (kennke@aicas.com) + * + * @since 1.3 + */ +public class AsyncBoxView + extends View +{ + + /** + * Manages the effective position of child views. That keeps the visible + * layout stable while the AsyncBoxView might be changing until the layout + * thread decides to publish the new layout. + */ + public class ChildLocator + { + + /** + * The last valid location. + */ + protected ChildState lastValidOffset; + + /** + * The last allocation. + */ + protected Rectangle lastAlloc; + + /** + * A Rectangle used for child allocation calculation to avoid creation + * of lots of garbage Rectangle objects. + */ + protected Rectangle childAlloc; + + /** + * Creates a new ChildLocator. + */ + public ChildLocator() + { + lastAlloc = new Rectangle(); + childAlloc = new Rectangle(); + } + + /** + * Receives notification that a child has changed. This is called by + * child state objects that have changed it's major span. + * + * This sets the {@link #lastValidOffset} field to <code>cs</code> if + * the new child state's view start offset is smaller than the start offset + * of the current child state's view or when <code>lastValidOffset</code> + * is <code>null</code>. + * + * @param cs the child state object that has changed + */ + public synchronized void childChanged(ChildState cs) + { + if (lastValidOffset == null + || cs.getChildView().getStartOffset() + < lastValidOffset.getChildView().getStartOffset()) + { + lastValidOffset = cs; + } + } + + /** + * Returns the view index of the view that occupies the specified area, or + * <code>-1</code> if there is no such child view. + * + * @param x the x coordinate (relative to <code>a</code>) + * @param y the y coordinate (relative to <code>a</code>) + * @param a the current allocation of this view + * + * @return the view index of the view that occupies the specified area, or + * <code>-1</code> if there is no such child view + */ + public int getViewIndexAtPoint(float x, float y, Shape a) + { + setAllocation(a); + float targetOffset = (getMajorAxis() == X_AXIS) ? x - lastAlloc.x + : y - lastAlloc.y; + int index = getViewIndexAtVisualOffset(targetOffset); + return index; + } + + /** + * Returns the current allocation for a child view. This updates the + * offsets for all children <em>before</em> the requested child view. + * + * @param index the index of the child view + * @param a the current allocation of this view + * + * @return the current allocation for a child view + */ + public synchronized Shape getChildAllocation(int index, Shape a) + { + if (a == null) + return null; + setAllocation(a); + ChildState cs = getChildState(index); + if (cs.getChildView().getStartOffset() + > lastValidOffset.getChildView().getStartOffset()) + { + updateChildOffsetsToIndex(index); + } + Shape ca = getChildAllocation(index); + return ca; + } + + /** + * Paints all child views. + * + * @param g the graphics context to use + */ + public synchronized void paintChildren(Graphics g) + { + Rectangle clip = g.getClipBounds(); + float targetOffset = (getMajorAxis() == X_AXIS) ? clip.x - lastAlloc.x + : clip.y - lastAlloc.y; + int index = getViewIndexAtVisualOffset(targetOffset); + int n = getViewCount(); + float offs = getChildState(index).getMajorOffset(); + for (int i = index; i < n; i++) + { + ChildState cs = getChildState(i); + cs.setMajorOffset(offs); + Shape ca = getChildAllocation(i); + if (ca.intersects(clip)) + { + synchronized (cs) + { + View v = cs.getChildView(); + v.paint(g, ca); + } + } + else + { + // done painting intersection + break; + } + offs += cs.getMajorSpan(); + } + } + + /** + * Returns the current allocation of the child view with the specified + * index. Note that this will <em>not</em> update any location information. + * + * @param index the index of the requested child view + * + * @return the current allocation of the child view with the specified + * index + */ + protected Shape getChildAllocation(int index) + { + ChildState cs = getChildState(index); + if (! cs.isLayoutValid()) + cs.run(); + + if (getMajorAxis() == X_AXIS) + { + childAlloc.x = lastAlloc.x + (int) cs.getMajorOffset(); + childAlloc.y = lastAlloc.y + (int) cs.getMinorOffset(); + childAlloc.width = (int) cs.getMajorSpan(); + childAlloc.height = (int) cs.getMinorSpan(); + } + else + { + childAlloc.y = lastAlloc.y + (int) cs.getMajorOffset(); + childAlloc.x = lastAlloc.x + (int) cs.getMinorOffset(); + childAlloc.height = (int) cs.getMajorSpan(); + childAlloc.width = (int) cs.getMinorSpan(); + } + return childAlloc; + } + + /** + * Sets the current allocation for this view. + * + * @param a the allocation to set + */ + protected void setAllocation(Shape a) + { + if (a instanceof Rectangle) + lastAlloc.setBounds((Rectangle) a); + else + lastAlloc.setBounds(a.getBounds()); + + setSize(lastAlloc.width, lastAlloc.height); + } + + /** + * Returns the index of the view at the specified offset along the major + * layout axis. + * + * @param targetOffset the requested offset + * + * @return the index of the view at the specified offset along the major + * layout axis + */ + protected int getViewIndexAtVisualOffset(float targetOffset) + { + int n = getViewCount(); + if (n > 0) + { + if (lastValidOffset == null) + lastValidOffset = getChildState(0); + if (targetOffset > majorSpan) + return 0; + else if (targetOffset > lastValidOffset.getMajorOffset()) + return updateChildOffsets(targetOffset); + else + { + float offs = 0f; + for (int i = 0; i < n; i++) + { + ChildState cs = getChildState(i); + float nextOffs = offs + cs.getMajorSpan(); + if (targetOffset < nextOffs) + return i; + offs = nextOffs; + } + } + } + return n - 1; + } + + /** + * Updates all the child view offsets up to the specified targetOffset. + * + * @param targetOffset the offset up to which the child view offsets are + * updated + * + * @return the index of the view at the specified offset + */ + private int updateChildOffsets(float targetOffset) + { + int n = getViewCount(); + int targetIndex = n - 1;; + int pos = lastValidOffset.getChildView().getStartOffset(); + int startIndex = getViewIndexAtPosition(pos, Position.Bias.Forward); + float start = lastValidOffset.getMajorOffset(); + float lastOffset = start; + for (int i = startIndex; i < n; i++) + { + ChildState cs = getChildState(i); + cs.setMajorOffset(lastOffset); + lastOffset += cs.getMajorSpan(); + if (targetOffset < lastOffset) + { + targetIndex = i; + lastValidOffset = cs; + break; + } + } + return targetIndex; + } + + /** + * Updates the offsets of the child views up to the specified index. + * + * @param index the index up to which the offsets are updated + */ + private void updateChildOffsetsToIndex(int index) + { + int pos = lastValidOffset.getChildView().getStartOffset(); + int startIndex = getViewIndexAtPosition(pos, Position.Bias.Forward); + float lastOffset = lastValidOffset.getMajorOffset(); + for (int i = startIndex; i <= index; i++) + { + ChildState cs = getChildState(i); + cs.setMajorOffset(lastOffset); + lastOffset += cs.getMajorSpan(); + } + } + } + + /** + * Represents the layout state of a child view. + */ + public class ChildState + implements Runnable + { + + /** + * The child view for this state record. + */ + private View childView; + + /** + * Indicates if the minor axis requirements of this child view are valid + * or not. + */ + private boolean minorValid; + + /** + * Indicates if the major axis requirements of this child view are valid + * or not. + */ + private boolean majorValid; + + /** + * Indicates if the current child size is valid. This is package private + * to avoid synthetic accessor method. + */ + boolean childSizeValid; + + /** + * The child views minimumSpan. This is package private to avoid accessor + * method. + */ + float minimum; + + /** + * The child views preferredSpan. This is package private to avoid accessor + * method. + */ + float preferred; + + /** + * The current span of the child view along the major axis. + */ + private float majorSpan; + + /** + * The current offset of the child view along the major axis. + */ + private float majorOffset; + + /** + * The current span of the child view along the minor axis. + */ + private float minorSpan; + + /** + * The current offset of the child view along the major axis. + */ + private float minorOffset; + + /** + * The child views maximumSpan. + */ + private float maximum; + + /** + * Creates a new <code>ChildState</code> object for the specified child + * view. + * + * @param view the child view for which to create the state record + */ + public ChildState(View view) + { + childView = view; + } + + /** + * Returns the child view for which this <code>ChildState</code> represents + * the layout state. + * + * @return the child view for this child state object + */ + public View getChildView() + { + return childView; + } + + /** + * Returns <code>true</code> if the current layout information is valid, + * <code>false</code> otherwise. + * + * @return <code>true</code> if the current layout information is valid, + * <code>false</code> otherwise + */ + public boolean isLayoutValid() + { + return minorValid && majorValid && childSizeValid; + } + + /** + * Performs the layout update for the child view managed by this + * <code>ChildState</code>. + */ + public void run() + { + Document doc = getDocument(); + if (doc instanceof AbstractDocument) + { + AbstractDocument abstractDoc = (AbstractDocument) doc; + abstractDoc.readLock(); + } + + try + { + + if (!(minorValid && majorValid && childSizeValid) + && childView.getParent() == AsyncBoxView.this) + { + synchronized(AsyncBoxView.this) + { + changing = this; + } + update(); + synchronized(AsyncBoxView.this) + { + changing = null; + } + // Changing the major axis may cause the minor axis + // requirements to have changed, so we need to do this again. + update(); + } + } + finally + { + if (doc instanceof AbstractDocument) + { + AbstractDocument abstractDoc = (AbstractDocument) doc; + abstractDoc.readUnlock(); + } + } + } + + /** + * Performs the actual update after the run methods has made its checks + * and locked the document. + */ + private void update() + { + int majorAxis = getMajorAxis(); + boolean minorUpdated = false; + synchronized (this) + { + if (! minorValid) + { + int minorAxis = getMinorAxis(); + minimum = childView.getMinimumSpan(minorAxis); + preferred = childView.getPreferredSpan(minorAxis); + maximum = childView.getMaximumSpan(minorAxis); + minorValid = true; + minorUpdated = true; + } + } + if (minorUpdated) + minorRequirementChange(this); + + boolean majorUpdated = false; + float delta = 0.0F; + synchronized (this) + { + if (! majorValid) + { + float oldSpan = majorSpan; + majorSpan = childView.getPreferredSpan(majorAxis); + delta = majorSpan - oldSpan; + majorValid = true; + majorUpdated = true; + } + } + if (majorUpdated) + { + majorRequirementChange(this, delta); + locator.childChanged(this); + } + + synchronized (this) + { + if (! childSizeValid) + { + float w; + float h; + if (majorAxis == X_AXIS) + { + w = majorSpan; + h = getMinorSpan(); + } + else + { + w = getMinorSpan(); + h = majorSpan; + } + childSizeValid = true; + childView.setSize(w, h); + } + } + } + + /** + * Returns the span of the child view along the minor layout axis. + * + * @return the span of the child view along the minor layout axis + */ + public float getMinorSpan() + { + float retVal; + if (maximum < minorSpan) + retVal = maximum; + else + retVal = Math.max(minimum, minorSpan); + return retVal; + } + + /** + * Returns the offset of the child view along the minor layout axis. + * + * @return the offset of the child view along the minor layout axis + */ + public float getMinorOffset() + { + float retVal; + if (maximum < minorSpan) + { + float align = childView.getAlignment(getMinorAxis()); + retVal = ((minorSpan - maximum) * align); + } + else + retVal = 0f; + + return retVal; + } + + /** + * Returns the span of the child view along the major layout axis. + * + * @return the span of the child view along the major layout axis + */ + + public float getMajorSpan() + { + return majorSpan; + } + + /** + * Returns the offset of the child view along the major layout axis. + * + * @return the offset of the child view along the major layout axis + */ + public float getMajorOffset() + { + return majorOffset; + } + + /** + * Sets the offset of the child view along the major layout axis. This + * should only be called by the ChildLocator of that child view. + * + * @param offset the offset to set + */ + public void setMajorOffset(float offset) + { + majorOffset = offset; + } + + /** + * Mark the preferences changed for that child. This forwards to + * {@link AsyncBoxView#preferenceChanged}. + * + * @param width <code>true</code> if the width preference has changed + * @param height <code>true</code> if the height preference has changed + */ + public void preferenceChanged(boolean width, boolean height) + { + if (getMajorAxis() == X_AXIS) + { + if (width) + majorValid = false; + if (height) + minorValid = false; + } + else + { + if (width) + minorValid = false; + if (height) + majorValid = false; + } + childSizeValid = false; + } + } + + /** + * Flushes the requirements changes upwards asynchronously. + */ + private class FlushTask implements Runnable + { + /** + * Starts the flush task. This obtains a readLock on the document + * and then flushes all the updates using + * {@link AsyncBoxView#flushRequirementChanges()} after updating the + * requirements. + */ + public void run() + { + try + { + // Acquire a lock on the document. + Document doc = getDocument(); + if (doc instanceof AbstractDocument) + { + AbstractDocument abstractDoc = (AbstractDocument) doc; + abstractDoc.readLock(); + } + + int n = getViewCount(); + if (minorChanged && (n > 0)) + { + LayoutQueue q = getLayoutQueue(); + ChildState min = getChildState(0); + ChildState pref = getChildState(0); + for (int i = 1; i < n; i++) + { + ChildState cs = getChildState(i); + if (cs.minimum > min.minimum) + min = cs; + if (cs.preferred > pref.preferred) + pref = cs; + } + synchronized (AsyncBoxView.this) + { + minReq = min; + prefReq = pref; + } + } + + flushRequirementChanges(); + } + finally + { + // Release the lock on the document. + Document doc = getDocument(); + if (doc instanceof AbstractDocument) + { + AbstractDocument abstractDoc = (AbstractDocument) doc; + abstractDoc.readUnlock(); + } + } + } + + } + + /** + * The major layout axis. + */ + private int majorAxis; + + /** + * The top inset. + */ + private float topInset; + + /** + * The bottom inset. + */ + private float bottomInset; + + /** + * The left inset. + */ + private float leftInset; + + /** + * Indicates if the major span should be treated as beeing estimated or not. + */ + private boolean estimatedMajorSpan; + + /** + * The right inset. + */ + private float rightInset; + + /** + * The children and their layout statistics. + */ + private ArrayList childStates; + + /** + * The currently changing child state. May be null if there is no child state + * updating at the moment. This is package private to avoid a synthetic + * accessor method inside ChildState. + */ + ChildState changing; + + /** + * Represents the minimum requirements. This is used in + * {@link #getMinimumSpan(int)}. + */ + ChildState minReq; + + /** + * Represents the minimum requirements. This is used in + * {@link #getPreferredSpan(int)}. + */ + ChildState prefReq; + + /** + * Indicates that the major axis requirements have changed. + */ + private boolean majorChanged; + + /** + * Indicates that the minor axis requirements have changed. This is package + * private to avoid synthetic accessor method. + */ + boolean minorChanged; + + /** + * The current span along the major layout axis. This is package private to + * avoid synthetic accessor method. + */ + float majorSpan; + + /** + * The current span along the minor layout axis. This is package private to + * avoid synthetic accessor method. + */ + float minorSpan; + + /** + * This tasked is placed on the layout queue to flush updates up to the + * parent view. + */ + private Runnable flushTask; + + /** + * The child locator for this view. + */ + protected ChildLocator locator; + + /** + * Creates a new <code>AsyncBoxView</code> that represents the specified + * element and layouts its children along the specified axis. + * + * @param elem the element + * @param axis the layout axis + */ + public AsyncBoxView(Element elem, int axis) + { + super(elem); + majorAxis = axis; + childStates = new ArrayList(); + flushTask = new FlushTask(); + locator = new ChildLocator(); + minorSpan = Short.MAX_VALUE; + } + + /** + * Returns the major layout axis. + * + * @return the major layout axis + */ + public int getMajorAxis() + { + return majorAxis; + } + + /** + * Returns the minor layout axis, that is the axis orthogonal to the major + * layout axis. + * + * @return the minor layout axis + */ + public int getMinorAxis() + { + return majorAxis == X_AXIS ? Y_AXIS : X_AXIS; + } + + /** + * Returns the view at the specified <code>index</code>. + * + * @param index the index of the requested child view + * + * @return the view at the specified <code>index</code> + */ + public View getView(int index) + { + View view = null; + synchronized(childStates) + { + if ((index >= 0) && (index < childStates.size())) + { + ChildState cs = (ChildState) childStates.get(index); + view = cs.getChildView(); + } + } + return view; + } + + /** + * Returns the number of child views. + * + * @return the number of child views + */ + public int getViewCount() + { + synchronized(childStates) + { + return childStates.size(); + } + } + + /** + * Returns the view index of the child view that represents the specified + * model position. + * + * @param pos the model position for which we search the view index + * @param bias the bias + * + * @return the view index of the child view that represents the specified + * model position + */ + public int getViewIndex(int pos, Position.Bias bias) + { + int retVal = -1; + + if (bias == Position.Bias.Backward) + pos = Math.max(0, pos - 1); + + // TODO: A possible optimization would be to implement a binary search + // here. + int numChildren = childStates.size(); + if (numChildren > 0) + { + for (int i = 0; i < numChildren; ++i) + { + View child = ((ChildState) childStates.get(i)).getChildView(); + if (child.getStartOffset() <= pos && child.getEndOffset() > pos) + { + retVal = i; + break; + } + } + } + return retVal; + } + + /** + * Returns the top inset. + * + * @return the top inset + */ + public float getTopInset() + { + return topInset; + } + + /** + * Sets the top inset. + * + * @param top the top inset + */ + public void setTopInset(float top) + { + topInset = top; + } + + /** + * Returns the bottom inset. + * + * @return the bottom inset + */ + public float getBottomInset() + { + return bottomInset; + } + + /** + * Sets the bottom inset. + * + * @param bottom the bottom inset + */ + public void setBottomInset(float bottom) + { + bottomInset = bottom; + } + + /** + * Returns the left inset. + * + * @return the left inset + */ + public float getLeftInset() + { + return leftInset; + } + + /** + * Sets the left inset. + * + * @param left the left inset + */ + public void setLeftInset(float left) + { + leftInset = left; + } + + /** + * Returns the right inset. + * + * @return the right inset + */ + public float getRightInset() + { + return rightInset; + } + + /** + * Sets the right inset. + * + * @param right the right inset + */ + public void setRightInset(float right) + { + rightInset = right; + } + + /** + * Loads the child views of this view. This is triggered by + * {@link #setParent(View)}. + * + * @param f the view factory to build child views with + */ + protected void loadChildren(ViewFactory f) + { + Element e = getElement(); + int n = e.getElementCount(); + if (n > 0) + { + View[] added = new View[n]; + for (int i = 0; i < n; i++) + { + added[i] = f.create(e.getElement(i)); + } + replace(0, 0, added); + } + } + + /** + * Returns the span along an axis that is taken up by the insets. + * + * @param axis the axis + * + * @return the span along an axis that is taken up by the insets + * + * @since 1.4 + */ + protected float getInsetSpan(int axis) + { + float span; + if (axis == X_AXIS) + span = leftInset + rightInset; + else + span = topInset + bottomInset; + return span; + } + + /** + * Sets the <code>estimatedMajorSpan</code> property that determines if + * the major span should be treated as beeing estimated. + * + * @param estimated if the major span should be treated as estimated or not + * + * @since 1.4 + */ + public void setEstimatedMajorSpan(boolean estimated) + { + estimatedMajorSpan = estimated; + } + + /** + * Determines whether the major span should be treated as estimated or as + * beeing accurate. + * + * @return <code>true</code> if the major span should be treated as + * estimated, <code>false</code> if the major span should be treated + * as accurate + * + * @since 1.4 + */ + public boolean getEstimatedMajorSpan() + { + return estimatedMajorSpan; + } + + /** + * Receives notification from the child states that the requirements along + * the minor axis have changed. + * + * @param cs the child state from which this notification is messaged + */ + protected synchronized void minorRequirementChange(ChildState cs) + { + minorChanged = true; + } + + /** + * Receives notification from the child states that the requirements along + * the major axis have changed. + * + * @param cs the child state from which this notification is messaged + */ + protected void majorRequirementChange(ChildState cs, float delta) + { + if (! estimatedMajorSpan) + majorSpan += delta; + majorChanged = true; + } + + /** + * Sets the parent for this view. This calls loadChildren if + * <code>parent</code> is not <code>null</code> and there have not been any + * child views initializes. + * + * @param parent the new parent view; <code>null</code> if this view is + * removed from the view hierarchy + * + * @see View#setParent(View) + */ + public void setParent(View parent) + { + super.setParent(parent); + if ((parent != null) && (getViewCount() == 0)) + { + ViewFactory f = getViewFactory(); + loadChildren(f); + } + } + + /** + * Sets the size of this view. This is ususally called before {@link #paint} + * is called to make sure the view has a valid layout. + * + * This implementation queues layout requests for every child view if the + * minor axis span has changed. (The major axis span is requested to never + * change for this view). + * + * @param width the width of the view + * @param height the height of the view + */ + public void setSize(float width, float height) + { + float targetSpan; + if (majorAxis == X_AXIS) + targetSpan = height - getTopInset() - getBottomInset(); + else + targetSpan = width - getLeftInset() - getRightInset(); + + if (targetSpan != minorSpan) + { + minorSpan = targetSpan; + + int n = getViewCount(); + LayoutQueue q = getLayoutQueue(); + for (int i = 0; i < n; i++) + { + ChildState cs = getChildState(i); + cs.childSizeValid = false; + q.addTask(cs); + } + q.addTask(flushTask); + } + } + + /** + * Replaces child views with new child views. + * + * This creates ChildState objects for all the new views and adds layout + * requests for them to the layout queue. + * + * @param offset the offset at which to remove/insert + * @param length the number of child views to remove + * @param views the new child views to insert + */ + public void replace(int offset, int length, View[] views) + { + synchronized(childStates) + { + LayoutQueue q = getLayoutQueue(); + for (int i = 0; i < length; i++) + childStates.remove(offset); + + for (int i = views.length - 1; i >= 0; i--) + childStates.add(offset, createChildState(views[i])); + + // We need to go through the new child states _after_ they have been + // added to the childStates list, otherwise the layout tasks may find + // an incomplete child list. That means we have to loop through + // them again, but what else can we do? + if (views.length != 0) + { + for (int i = 0; i < views.length; i++) + { + ChildState cs = (ChildState) childStates.get(i + offset); + cs.getChildView().setParent(this); + q.addTask(cs); + } + q.addTask(flushTask); + } + } + } + + /** + * Paints the view. This requests the {@link ChildLocator} to paint the views + * after setting the allocation on it. + * + * @param g the graphics context to use + * @param s the allocation for this view + */ + public void paint(Graphics g, Shape s) + { + synchronized (locator) + { + locator.setAllocation(s); + locator.paintChildren(g); + } + } + + /** + * Returns the preferred span of this view along the specified layout axis. + * + * @return the preferred span of this view along the specified layout axis + */ + public float getPreferredSpan(int axis) + { + float retVal; + if (majorAxis == axis) + retVal = majorSpan; + + else if (prefReq != null) + { + View child = prefReq.getChildView(); + retVal = child.getPreferredSpan(axis); + } + + // If we have no layout information yet, then return insets + 30 as + // an estimation. + else + { + if (axis == X_AXIS) + retVal = getLeftInset() + getRightInset() + 30; + else + retVal = getTopInset() + getBottomInset() + 30; + } + return retVal; + } + + /** + * Maps a model location to view coordinates. + * + * @param pos the model location + * @param a the current allocation of this view + * @param b the bias + * + * @return the view allocation for the specified model location + */ + public Shape modelToView(int pos, Shape a, Bias b) + throws BadLocationException + { + int index = getViewIndexAtPosition(pos, b); + Shape ca = locator.getChildAllocation(index, a); + + ChildState cs = getChildState(index); + synchronized (cs) + { + View cv = cs.getChildView(); + Shape v = cv.modelToView(pos, ca, b); + return v; + } + } + + /** + * Maps view coordinates to a model location. + * + * @param x the x coordinate (relative to <code>a</code>) + * @param y the y coordinate (relative to <code>a</code>) + * @param b holds the bias of the model location on method exit + * + * @return the model location for the specified view location + */ + public int viewToModel(float x, float y, Shape a, Bias[] b) + { + int pos; + int index; + Shape ca; + + synchronized (locator) + { + index = locator.getViewIndexAtPoint(x, y, a); + ca = locator.getChildAllocation(index, a); + } + + ChildState cs = getChildState(index); + synchronized (cs) + { + View v = cs.getChildView(); + pos = v.viewToModel(x, y, ca, b); + } + return pos; + } + + /** + * Returns the child allocation for the child view with the specified + * <code>index</code>. + * + * @param index the index of the child view + * @param a the current allocation of this view + * + * @return the allocation of the child view + */ + public Shape getChildAllocation(int index, Shape a) + { + Shape ca = locator.getChildAllocation(index, a); + return ca; + } + + /** + * Returns the maximum span of this view along the specified axis. + * This is implemented to return the <code>preferredSpan</code> for the + * major axis (that means the box can't be resized along the major axis) and + * {@link Short#MAX_VALUE} for the minor axis. + * + * @param axis the axis + * + * @return the maximum span of this view along the specified axis + */ + public float getMaximumSpan(int axis) + { + float max; + if (axis == majorAxis) + max = getPreferredSpan(axis); + else + max = Short.MAX_VALUE; + return max; + } + + /** + * Returns the minimum span along the specified axis. + */ + public float getMinimumSpan(int axis) + { + float min; + if (axis == majorAxis) + min = getPreferredSpan(axis); + else + { + if (minReq != null) + { + View child = minReq.getChildView(); + min = child.getMinimumSpan(axis); + } + else + { + // No layout information yet. Return insets + 5 as some kind of + // estimation. + if (axis == X_AXIS) + min = getLeftInset() + getRightInset() + 5; + else + min = getTopInset() + getBottomInset() + 5; + } + } + return min; + } + + /** + * Receives notification that one of the child views has changed its + * layout preferences along one or both axis. + * + * This queues a layout request for that child view if necessary. + * + * @param view the view that has changed its preferences + * @param width <code>true</code> if the width preference has changed + * @param height <code>true</code> if the height preference has changed + */ + public synchronized void preferenceChanged(View view, boolean width, + boolean height) + { + if (view == null) + getParent().preferenceChanged(this, width, height); + else + { + if (changing != null) + { + View cv = changing.getChildView(); + if (cv == view) + { + changing.preferenceChanged(width, height); + return; + } + } + int index = getViewIndexAtPosition(view.getStartOffset(), + Position.Bias.Forward); + ChildState cs = getChildState(index); + cs.preferenceChanged(width, height); + LayoutQueue q = getLayoutQueue(); + q.addTask(cs); + q.addTask(flushTask); + } + } + + /** + * Updates the layout for this view. This is implemented to trigger + * {link ChildLocator#childChanged} for the changed view, if there is + * any. + * + * @param ec the element change, may be <code>null</code> if there were + * no changes to the element of this view + * @param e the document event + * @param a the current allocation of this view + */ + protected void updateLayout(DocumentEvent.ElementChange ec, + DocumentEvent e, Shape a) + { + if (ec != null) + { + int index = Math.max(ec.getIndex() - 1, 0); + ChildState cs = getChildState(index); + locator.childChanged(cs); + } + } + + + /** + * Returns the <code>ChildState</code> object associated with the child view + * at the specified <code>index</code>. + * + * @param index the index of the child view for which to query the state + * + * @return the child state for the specified child view + */ + protected ChildState getChildState(int index) { + synchronized (childStates) + { + return (ChildState) childStates.get(index); + } + } + + /** + * Returns the <code>LayoutQueue</code> used for layouting the box view. + * This simply returns {@link LayoutQueue#getDefaultQueue()}. + * + * @return the <code>LayoutQueue</code> used for layouting the box view + */ + protected LayoutQueue getLayoutQueue() + { + return LayoutQueue.getDefaultQueue(); + } + + /** + * Returns the child view index of the view that represents the specified + * position in the document model. + * + * @param pos the position in the model + * @param b the bias + * + * @return the child view index of the view that represents the specified + * position in the document model + */ + protected synchronized int getViewIndexAtPosition(int pos, Position.Bias b) + { + if (b == Position.Bias.Backward) + pos = Math.max(0, pos - 1); + Element elem = getElement(); + return elem.getElementIndex(pos); + } + + /** + * Creates a <code>ChildState</code> object for the specified view. + * + * @param v the view for which to create a child state object + * + * @return the created child state + */ + protected ChildState createChildState(View v) + { + return new ChildState(v); + } + + /** + * Flushes the requirements changes upwards to the parent view. This is + * called from the layout thread. + */ + protected synchronized void flushRequirementChanges() + { + if (majorChanged || minorChanged) + { + View p = getParent(); + if (p != null) + { + boolean horizontal; + boolean vertical; + if (majorAxis == X_AXIS) + { + horizontal = majorChanged; + vertical = minorChanged; + } + else + { + vertical = majorChanged; + horizontal = minorChanged; + } + + p.preferenceChanged(this, horizontal, vertical); + majorChanged = false; + minorChanged = false; + + Component c = getContainer(); + if (c != null) + c.repaint(); + } + } + } +} diff --git a/libjava/classpath/javax/swing/text/BoxView.java b/libjava/classpath/javax/swing/text/BoxView.java index 5c9587dfe5d..b5907dcbbe6 100644 --- a/libjava/classpath/javax/swing/text/BoxView.java +++ b/libjava/classpath/javax/swing/text/BoxView.java @@ -43,6 +43,7 @@ import java.awt.Rectangle; import java.awt.Shape; import javax.swing.SizeRequirements; +import javax.swing.event.DocumentEvent; /** * An implementation of {@link CompositeView} that arranges its children in @@ -58,49 +59,37 @@ public class BoxView /** * The axis along which this <code>BoxView</code> is laid out. */ - int myAxis; + private int myAxis; /** - * Indicates wether the layout in X_AXIS is valid. + * Indicates if the layout is valid along X_AXIS or Y_AXIS. */ - boolean xLayoutValid; + private boolean[] layoutValid = new boolean[2]; /** - * Indicates whether the layout in Y_AXIS is valid. + * The spans along the X_AXIS and Y_AXIS. */ - boolean yLayoutValid; + private int[][] spans = new int[2][]; /** - * The spans in X direction of the children. + * The offsets of the children along the X_AXIS and Y_AXIS. */ - int[] spansX; + private int[][] offsets = new int[2][]; /** - * The spans in Y direction of the children. + * The size requirements along the X_AXIS and Y_AXIS. */ - int[] spansY; + private SizeRequirements[] requirements = new SizeRequirements[2]; /** - * The offsets of the children in X direction relative to this BoxView's - * inner bounds. + * The current span along X_AXIS or Y_AXIS. */ - int[] offsetsX; + private int[] span = new int[2]; /** - * The offsets of the children in Y direction relative to this BoxView's - * inner bounds. + * The SizeRequirements of the child views along the X_AXIS and Y_AXIS. */ - int[] offsetsY; - - /** - * The current width. - */ - int width; - - /** - * The current height. - */ - int height; + private SizeRequirements[][] childReqs = new SizeRequirements[2][]; /** * Creates a new <code>BoxView</code> for the given @@ -114,23 +103,26 @@ public class BoxView { super(element); myAxis = axis; - xLayoutValid = false; - yLayoutValid = false; + layoutValid[0] = false; + layoutValid[1] = false; + span[0] = 0; + span[1] = 0; + requirements[0] = new SizeRequirements(); + requirements[1] = new SizeRequirements(); // Initialize the cache arrays. - spansX = new int[0]; - spansY = new int[0]; - offsetsX = new int[0]; - offsetsY = new int[0]; - - width = 0; - height = 0; + spans[0] = new int[0]; + spans[1] = new int[0]; + offsets[0] = new int[0]; + offsets[1] = new int[0]; } /** * Returns the axis along which this <code>BoxView</code> is laid out. * * @return the axis along which this <code>BoxView</code> is laid out + * + * @since 1.3 */ public int getAxis() { @@ -144,6 +136,8 @@ public class BoxView * {@link View#Y_AXIS}. * * @param axis the axis along which this <code>BoxView</code> is laid out + * + * @since 1.3 */ public void setAxis(int axis) { @@ -163,20 +157,14 @@ public class BoxView * {@link View#Y_AXIS}. * * @param axis an <code>int</code> value + * + * @since 1.3 */ public void layoutChanged(int axis) { - switch (axis) - { - case X_AXIS: - xLayoutValid = false; - break; - case Y_AXIS: - yLayoutValid = false; - break; - default: - throw new IllegalArgumentException("Invalid axis parameter."); - } + if (axis != X_AXIS && axis != Y_AXIS) + throw new IllegalArgumentException("Invalid axis parameter."); + layoutValid[axis] = false; } /** @@ -190,22 +178,14 @@ public class BoxView * * @return <code>true</code> if the layout along the specified * <code>axis</code> is valid, <code>false</code> otherwise + * + * @since 1.4 */ protected boolean isLayoutValid(int axis) { - boolean valid = false; - switch (axis) - { - case X_AXIS: - valid = xLayoutValid; - break; - case Y_AXIS: - valid = yLayoutValid; - break; - default: - throw new IllegalArgumentException("Invalid axis parameter."); - } - return valid; + if (axis != X_AXIS && axis != Y_AXIS) + throw new IllegalArgumentException("Invalid axis parameter."); + return layoutValid[axis]; } /** @@ -242,40 +222,44 @@ public class BoxView */ public void replace(int offset, int length, View[] views) { + int numViews = 0; + if (views != null) + numViews = views.length; + // Resize and copy data for cache arrays. // The spansX cache. int oldSize = getViewCount(); - int[] newSpansX = new int[oldSize - length + views.length]; - System.arraycopy(spansX, 0, newSpansX, 0, offset); - System.arraycopy(spansX, offset + length, newSpansX, - offset + views.length, + int[] newSpansX = new int[oldSize - length + numViews]; + System.arraycopy(spans[X_AXIS], 0, newSpansX, 0, offset); + System.arraycopy(spans[X_AXIS], offset + length, newSpansX, + offset + numViews, oldSize - (offset + length)); - spansX = newSpansX; + spans[X_AXIS] = newSpansX; // The spansY cache. - int[] newSpansY = new int[oldSize - length + views.length]; - System.arraycopy(spansY, 0, newSpansY, 0, offset); - System.arraycopy(spansY, offset + length, newSpansY, - offset + views.length, + int[] newSpansY = new int[oldSize - length + numViews]; + System.arraycopy(spans[Y_AXIS], 0, newSpansY, 0, offset); + System.arraycopy(spans[Y_AXIS], offset + length, newSpansY, + offset + numViews, oldSize - (offset + length)); - spansY = newSpansY; + spans[Y_AXIS] = newSpansY; // The offsetsX cache. - int[] newOffsetsX = new int[oldSize - length + views.length]; - System.arraycopy(offsetsX, 0, newOffsetsX, 0, offset); - System.arraycopy(offsetsX, offset + length, newOffsetsX, - offset + views.length, + int[] newOffsetsX = new int[oldSize - length + numViews]; + System.arraycopy(offsets[X_AXIS], 0, newOffsetsX, 0, offset); + System.arraycopy(offsets[X_AXIS], offset + length, newOffsetsX, + offset + numViews, oldSize - (offset + length)); - offsetsX = newOffsetsX; + offsets[X_AXIS] = newOffsetsX; // The offsetsY cache. - int[] newOffsetsY = new int[oldSize - length + views.length]; - System.arraycopy(offsetsY, 0, newOffsetsY, 0, offset); - System.arraycopy(offsetsY, offset + length, newOffsetsY, - offset + views.length, + int[] newOffsetsY = new int[oldSize - length + numViews]; + System.arraycopy(offsets[Y_AXIS], 0, newOffsetsY, 0, offset); + System.arraycopy(offsets[Y_AXIS], offset + length, newOffsetsY, + offset + numViews, oldSize - (offset + length)); - offsetsY = newOffsetsY; + offsets[Y_AXIS] = newOffsetsY; // Actually perform the replace. super.replace(offset, length, views); @@ -294,13 +278,10 @@ public class BoxView */ public void paint(Graphics g, Shape a) { - // Adjust size if the size is changed. - Rectangle bounds = a.getBounds(); - - if (bounds.width != getWidth() || bounds.height != getHeight()) - setSize(bounds.width, bounds.height); - Rectangle inside = getInsideAllocation(a); + // TODO: Used for debugging. + //g.drawRect(inside.x, inside.y, inside.width, inside.height); + Rectangle copy = new Rectangle(inside); int count = getViewCount(); for (int i = 0; i < count; ++i) @@ -323,22 +304,50 @@ public class BoxView */ public float getPreferredSpan(int axis) { - SizeRequirements sr = new SizeRequirements(); - int pref = baselineRequirements(axis, sr).preferred; - return (float) pref; + updateRequirements(axis); + return requirements[axis].preferred; } + /** + * Returns the maximum span of this view along the specified axis. + * This returns <code>Integer.MAX_VALUE</code> for the minor axis + * and the preferred span for the major axis. + * + * @param axis the axis + * + * @return the maximum span of this view along the specified axis + */ public float getMaximumSpan(int axis) { - if (axis == getAxis()) - return getPreferredSpan(axis); + float max; + if (axis == myAxis) + max = getPreferredSpan(axis); else - return Integer.MAX_VALUE; + max = Integer.MAX_VALUE; + return max; } /** - * Calculates the size requirements for this <code>BoxView</code> along - * the specified axis. + * Returns the minimum span of this view along the specified axis. + * This calculates the minimum span using + * {@link #calculateMajorAxisRequirements} or + * {@link #calculateMinorAxisRequirements} (depending on the axis) and + * returns the resulting minimum span. + * + * @param axis the axis + * + * @return the minimum span of this view along the specified axis + */ + public float getMinimumSpan(int axis) + { + updateRequirements(axis); + return requirements[axis].minimum; + } + + /** + * This method is obsolete and no longer in use. It is replaced by + * {@link #calculateMajorAxisRequirements(int, SizeRequirements)} and + * {@link #calculateMinorAxisRequirements(int, SizeRequirements)}. * * @param axis the axis that is examined * @param sr the <code>SizeRequirements</code> object to hold the result, @@ -350,12 +359,45 @@ public class BoxView protected SizeRequirements baselineRequirements(int axis, SizeRequirements sr) { - SizeRequirements result; - if (axis == myAxis) - result = calculateMajorAxisRequirements(axis, sr); - else - result = calculateMinorAxisRequirements(axis, sr); - return result; + 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++) + { + 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); + } + 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; } /** @@ -370,10 +412,13 @@ public class BoxView protected void baselineLayout(int span, int axis, int[] offsets, int[] spans) { - if (axis == myAxis) - layoutMajorAxis(span, axis, offsets, spans); - else - layoutMinorAxis(span, axis, offsets, spans); + updateChildRequirements(axis); + updateRequirements(axis); + + // Calculate the spans and offsets using the SizeRequirements uility + // methods. + SizeRequirements.calculateAlignedPositions(span, requirements[axis], + childReqs[axis], offsets, spans); } /** @@ -390,8 +435,34 @@ public class BoxView protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements sr) { - SizeRequirements[] childReqs = getChildRequirements(axis); - return SizeRequirements.getTiledSizeRequirements(childReqs); + updateChildRequirements(axis); + + SizeRequirements result = sr; + if (result == null) + result = new SizeRequirements(); + + long minimum = 0; + long preferred = 0; + long maximum = 0; + for (int i = 0; i < children.length; i++) + { + minimum += childReqs[axis][i].minimum; + preferred += childReqs[axis][i].preferred; + maximum += childReqs[axis][i].maximum; + } + // Overflow check. + if (minimum > Integer.MAX_VALUE) + minimum = Integer.MAX_VALUE; + if (preferred > Integer.MAX_VALUE) + preferred = Integer.MAX_VALUE; + if (maximum > Integer.MAX_VALUE) + maximum = Integer.MAX_VALUE; + + result.minimum = (int) minimum; + result.preferred = (int) preferred; + result.maximum = (int) maximum; + result.alignment = 0.5F; + return result; } /** @@ -407,11 +478,49 @@ public class BoxView * the specified axis */ protected SizeRequirements calculateMinorAxisRequirements(int axis, - SizeRequirements sr) + SizeRequirements sr) { - SizeRequirements[] childReqs = getChildRequirements(axis); - return SizeRequirements.getAlignedSizeRequirements(childReqs); + 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++) + { + 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); + } + 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; } + /** * Returns <code>true</code> if the specified point lies before the @@ -511,10 +620,10 @@ public class BoxView if (! isAllocationValid()) layout(a.width, a.height); - a.x += offsetsX[index]; - a.y += offsetsY[index]; - a.width = spansX[index]; - a.height = spansY[index]; + a.x += offsets[X_AXIS][index]; + a.y += offsets[Y_AXIS][index]; + a.width = spans[X_AXIS][index]; + a.height = spans[Y_AXIS][index]; } /** @@ -528,8 +637,49 @@ public class BoxView */ protected void layout(int width, int height) { - baselineLayout(width, X_AXIS, offsetsX, spansX); - baselineLayout(height, Y_AXIS, offsetsY, spansY); + int[] newSpan = new int[]{ width, height }; + int count = getViewCount(); + + // Update minor axis as appropriate. We need to first update the minor + // axis layout because that might affect the children's preferences along + // the major axis. + int minorAxis = myAxis == X_AXIS ? Y_AXIS : X_AXIS; + if ((! isLayoutValid(minorAxis)) || newSpan[minorAxis] != span[minorAxis]) + { + layoutValid[minorAxis] = false; + span[minorAxis] = newSpan[minorAxis]; + layoutMinorAxis(span[minorAxis], minorAxis, offsets[minorAxis], + spans[minorAxis]); + + // Update the child view's sizes. + for (int i = 0; i < count; ++i) + { + getView(i).setSize(spans[X_AXIS][i], spans[Y_AXIS][i]); + } + layoutValid[minorAxis] = true; + } + + + // Update major axis as appropriate. + if ((! isLayoutValid(myAxis)) || newSpan[myAxis] != span[myAxis]) + { + layoutValid[myAxis] = false; + span[myAxis] = newSpan[myAxis]; + layoutMajorAxis(span[myAxis], myAxis, offsets[myAxis], + spans[myAxis]); + + // Update the child view's sizes. + for (int i = 0; i < count; ++i) + { + getView(i).setSize(spans[X_AXIS][i], spans[Y_AXIS][i]); + } + layoutValid[myAxis] = true; + } + + if (layoutValid[myAxis] == false) + System.err.println("WARNING: Major axis layout must be valid after layout"); + if (layoutValid[minorAxis] == false) + System.err.println("Minor axis layout must be valid after layout"); } /** @@ -544,12 +694,15 @@ public class BoxView protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { - SizeRequirements[] childReqs = getChildRequirements(axis); + updateChildRequirements(axis); + updateRequirements(axis); + // Calculate the spans and offsets using the SizeRequirements uility // methods. - SizeRequirements.calculateTiledPositions(targetSpan, null, childReqs, + SizeRequirements.calculateTiledPositions(targetSpan, requirements[axis], + childReqs[axis], offsets, spans); - validateLayout(axis); + } /** @@ -564,18 +717,14 @@ public class BoxView protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { - SizeRequirements[] childReqs = getChildRequirements(axis); + updateChildRequirements(axis); + updateRequirements(axis); + // Calculate the spans and offsets using the SizeRequirements uility // methods. - // TODO: This might be an opportunity for performance optimization. Here - // we could use a cached instance of SizeRequirements instead of passing - // null to baselineRequirements. However, this would involve rewriting - // the baselineRequirements() method to not use the SizeRequirements - // utility method, since they cannot reuse a cached instance. - SizeRequirements total = baselineRequirements(axis, null); - SizeRequirements.calculateAlignedPositions(targetSpan, total, childReqs, - offsets, spans); - validateLayout(axis); + SizeRequirements.calculateAlignedPositions(targetSpan, requirements[axis], + childReqs[axis], offsets, + spans); } /** @@ -597,7 +746,7 @@ public class BoxView */ public int getWidth() { - return width; + return span[X_AXIS]; } /** @@ -607,7 +756,7 @@ public class BoxView */ public int getHeight() { - return height; + return span[Y_AXIS]; } /** @@ -619,54 +768,7 @@ public class BoxView */ public void setSize(float width, float height) { - if (this.width != (int) width) - layoutChanged(X_AXIS); - if (this.height != (int) height) - layoutChanged(Y_AXIS); - - this.width = (int) width; - this.height = (int) height; - - Rectangle outside = new Rectangle(0, 0, this.width, this.height); - Rectangle inside = getInsideAllocation(outside); - if (!isAllocationValid()) - layout(inside.width, inside.height); - } - - /** - * Sets the layout to valid for a specific axis. - * - * @param axis the axis for which to validate the layout - */ - void validateLayout(int axis) - { - if (axis == X_AXIS) - xLayoutValid = true; - if (axis == Y_AXIS) - yLayoutValid = true; - } - - /** - * Returns the size requirements of this view's children for the major - * axis. - * - * @return the size requirements of this view's children for the major - * axis - */ - SizeRequirements[] getChildRequirements(int axis) - { - // Allocate SizeRequirements for each child view. - int count = getViewCount(); - SizeRequirements[] childReqs = new SizeRequirements[count]; - for (int i = 0; i < count; ++i) - { - View view = getView(i); - childReqs[i] = new SizeRequirements((int) view.getMinimumSpan(axis), - (int) view.getPreferredSpan(axis), - (int) view.getMaximumSpan(axis), - view.getAlignment(axis)); - } - return childReqs; + layout((int) width, (int) height); } /** @@ -682,10 +784,9 @@ public class BoxView */ protected int getSpan(int axis, int childIndex) { - if (axis == X_AXIS) - return spansX[childIndex]; - else - return spansY[childIndex]; + if (axis != X_AXIS && axis != Y_AXIS) + throw new IllegalArgumentException("Illegal axis argument"); + return spans[axis][childIndex]; } /** @@ -701,10 +802,9 @@ public class BoxView */ protected int getOffset(int axis, int childIndex) { - if (axis == X_AXIS) - return offsetsX[childIndex]; - else - return offsetsY[childIndex]; + if (axis != X_AXIS && axis != Y_AXIS) + throw new IllegalArgumentException("Illegal axis argument"); + return offsets[axis][childIndex]; } /** @@ -719,10 +819,15 @@ public class BoxView */ public float getAlignment(int axis) { + float align; if (axis == myAxis) - return 0.5F; + align = 0.5F; else - return baselineRequirements(axis, null).alignment; + { + updateRequirements(axis); + align = requirements[axis].alignment; + } + return align; } /** @@ -732,12 +837,12 @@ public class BoxView * @param height indicates that the preferred height of the child changed. * @param child the child View. */ - public void preferenceChanged (View child, boolean width, boolean height) + public void preferenceChanged(View child, boolean width, boolean height) { if (width) - xLayoutValid = false; + layoutValid[X_AXIS] = false; if (height) - yLayoutValid = false; + layoutValid[Y_AXIS] = false; super.preferenceChanged(child, width, height); } @@ -751,11 +856,118 @@ public class BoxView throws BadLocationException { // Make sure everything is allocated properly and then call super - if (!isAllocationValid()) + if (! isAllocationValid()) { Rectangle bounds = a.getBounds(); - setSize(bounds.width, bounds.height); + layout(bounds.width, bounds.height); } return super.modelToView(pos, a, bias); } + + /** + * Returns the resize weight of this view. A value of <code>0</code> or less + * means this view is not resizeable. Positive values make the view + * resizeable. This implementation returns <code>0</code> for the major + * axis and <code>1</code> for the minor axis of this box view. + * + * @param axis the axis + * + * @return the resizability of this view along the specified axis + * + * @throws IllegalArgumentException if <code>axis</code> is invalid + */ + public int getResizeWeight(int axis) + { + if (axis != X_AXIS && axis != Y_AXIS) + throw new IllegalArgumentException("Illegal axis argument"); + int weight = 1; + if (axis == myAxis) + weight = 0; + return weight; + } + + /** + * Returns the child allocation for the child view with the specified + * <code>index</code>. If the layout is invalid, this returns + * <code>null</code>. + * + * @param index the child view index + * @param a the allocation to this view + * + * @return the child allocation for the child view with the specified + * <code>index</code> or <code>null</code> if the layout is invalid + * or <code>a</code> is null + */ + public Shape getChildAllocation(int index, Shape a) + { + Shape ret = null; + if (isAllocationValid() && a != null) + ret = super.getChildAllocation(index, a); + return ret; + } + + protected void forwardUpdate(DocumentEvent.ElementChange ec, DocumentEvent e, + Shape a, ViewFactory vf) + { + // FIXME: What to do here? + super.forwardUpdate(ec, e, a, vf); + } + + public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) + { + // FIXME: What to do here? + return super.viewToModel(x, y, a, bias); + } + + protected boolean flipEastAndWestEnds(int position, Position.Bias bias) + { + // FIXME: What to do here? + return super.flipEastAndWestAtEnds(position, bias); + } + + /** + * Updates the child requirements along the specified axis. The requirements + * are only updated if the layout for the specified axis is marked as + * invalid. + * + * @param axis the axis to be updated + */ + private void updateChildRequirements(int axis) + { + if (! isLayoutValid(axis)) + { + int numChildren = getViewCount(); + if (childReqs[axis] == null || childReqs[axis].length != numChildren) + childReqs[axis] = new SizeRequirements[numChildren]; + for (int i = 0; i < numChildren; ++i) + { + View child = getView(i); + childReqs[axis][i] = + new SizeRequirements((int) child.getMinimumSpan(axis), + (int) child.getPreferredSpan(axis), + (int) child.getMaximumSpan(axis), + child.getAlignment(axis)); + } + } + } + + /** + * Updates the view's cached requirements along the specified axis if + * necessary. The requirements are only updated if the layout for the + * specified axis is marked as invalid. + * + * @param axis the axis + */ + private void updateRequirements(int axis) + { + if (! layoutValid[axis]) + { + if (axis == myAxis) + requirements[axis] = calculateMajorAxisRequirements(axis, + requirements[axis]); + else + requirements[axis] = calculateMinorAxisRequirements(axis, + requirements[axis]); + } + } } diff --git a/libjava/classpath/javax/swing/text/ComponentView.java b/libjava/classpath/javax/swing/text/ComponentView.java index 2846f8b536b..a7d237ab73a 100644 --- a/libjava/classpath/javax/swing/text/ComponentView.java +++ b/libjava/classpath/javax/swing/text/ComponentView.java @@ -228,8 +228,9 @@ public class ComponentView extends View * * @param p the parent view to set */ - void setParentImpl(View p) + private void setParentImpl(View p) { + super.setParent(p); if (p != null) { Component c = getComponent(); diff --git a/libjava/classpath/javax/swing/text/CompositeView.java b/libjava/classpath/javax/swing/text/CompositeView.java index cd664521542..a10aca7e625 100644 --- a/libjava/classpath/javax/swing/text/CompositeView.java +++ b/libjava/classpath/javax/swing/text/CompositeView.java @@ -218,20 +218,43 @@ public abstract class CompositeView throws BadLocationException { int childIndex = getViewIndex(pos, bias); + Shape ret = null; if (childIndex != -1) { View child = getView(childIndex); - Rectangle r = a.getBounds(); - childAllocation(childIndex, r); - Shape result = child.modelToView(pos, r, bias); - if (result == null) - throw new AssertionError("" + child.getClass().getName() - + ".modelToView() must not return null"); - return result; + 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); } else - throw new BadLocationException("No child view for the specified location", - pos); + ret = createDefaultLocation(a, bias); + return ret; + } + + /** + * A helper method for {@link #modelToView(int, Position.Bias, int, + * Position.Bias, Shape)}. This creates a default location when there is + * no child view that can take responsibility for mapping the position to + * view coordinates. Depending on the specified bias this will be the + * left or right edge of this view's allocation. + * + * @param a the allocation for this view + * @param bias the bias + * + * @return a default location + */ + private Shape createDefaultLocation(Shape a, Position.Bias bias) + { + Rectangle alloc = a.getBounds(); + Rectangle location = new Rectangle(alloc.x, alloc.y, 1, alloc.height); + if (bias == Position.Bias.Forward) + location.x = alloc.x + alloc.width; + return location; } /** @@ -350,7 +373,8 @@ public abstract class CompositeView */ public int getViewIndex(int pos, Position.Bias b) { - // FIXME: Handle bias somehow. + if (b == Position.Bias.Backward && pos != 0) + pos -= 1; return getViewIndexAtPosition(pos); } diff --git a/libjava/classpath/javax/swing/text/DefaultCaret.java b/libjava/classpath/javax/swing/text/DefaultCaret.java index 776ef69e5b3..f2a68c00db1 100644 --- a/libjava/classpath/javax/swing/text/DefaultCaret.java +++ b/libjava/classpath/javax/swing/text/DefaultCaret.java @@ -148,14 +148,16 @@ public class DefaultCaret extends Rectangle */ public void removeUpdate(DocumentEvent event) { - if (policy == ALWAYS_UPDATE || - (SwingUtilities.isEventDispatchThread() && - policy == UPDATE_WHEN_ON_EDT)) + if (policy == ALWAYS_UPDATE + || (SwingUtilities.isEventDispatchThread() + && policy == UPDATE_WHEN_ON_EDT)) { int dot = getDot(); setDot(dot - event.getLength()); } - else if (policy == NEVER_UPDATE) + else if (policy == NEVER_UPDATE + || (! SwingUtilities.isEventDispatchThread() + && policy == UPDATE_WHEN_ON_EDT)) { int docLength = event.getDocument().getLength(); if (getDot() > docLength) @@ -364,7 +366,8 @@ public class DefaultCaret extends Rectangle * <ul> * <li>If we receive a double click, the caret position (dot) is set * to the position associated to the mouse click and the word at - * this location is selected.</li> + * this location is selected. If there is no word at the pointer + * the gap is selected instead.</li> * <li>If we receive a triple click, the caret position (dot) is set * to the position associated to the mouse click and the line at * this location is selected.</li> @@ -374,7 +377,50 @@ public class DefaultCaret extends Rectangle */ public void mouseClicked(MouseEvent event) { - // TODO: Implement double- and triple-click behaviour here. + int count = event.getClickCount(); + + if (count >= 2) + { + int newDot = getComponent().viewToModel(event.getPoint()); + JTextComponent t = getComponent(); + + try + { + if (count == 3) + t.select(Utilities.getRowStart(t, newDot), Utilities.getRowEnd(t, newDot)); + else + { + int nextWord = Utilities.getNextWord(t, newDot); + + // When the mouse points at the offset of the first character + // in a word Utilities().getPreviousWord will not return that + // word but we want to select that. We have to use + // Utilities.nextWord() to get it. + if (newDot == nextWord) + t.select(nextWord, Utilities.getNextWord(t, nextWord)); + else + { + int previousWord = Utilities.getPreviousWord(t, newDot); + int previousWordEnd = Utilities.getWordEnd(t, previousWord); + + // If the user clicked in the space between two words, + // then select the space. + if (newDot >= previousWordEnd && newDot <= nextWord) + t.select(previousWordEnd, nextWord); + // Otherwise select the word under the mouse pointer. + else + t.select(previousWord, previousWordEnd); + } + } + } + catch(BadLocationException ble) + { + // TODO: Swallowing ok here? + } + + dot = newDot; + } + } /** @@ -409,7 +455,10 @@ public class DefaultCaret extends Rectangle */ public void mousePressed(MouseEvent event) { - positionCaret(event); + if (event.isShiftDown()) + moveCaret(event); + else + positionCaret(event); } /** @@ -575,7 +624,39 @@ public class DefaultCaret extends Rectangle { return mark; } - + + private void clearHighlight() + { + Highlighter highlighter = textComponent.getHighlighter(); + + if (highlighter == null) + return; + + if (selectionVisible) + { + try + { + if (highlightEntry == null) + highlightEntry = highlighter.addHighlight(0, 0, getSelectionPainter()); + else + highlighter.changeHighlight(highlightEntry, 0, 0); + } + catch (BadLocationException e) + { + // This should never happen. + throw new InternalError(); + } + } + else + { + if (highlightEntry != null) + { + highlighter.removeHighlight(highlightEntry); + highlightEntry = null; + } + } + } + private void handleHighlight() { Highlighter highlighter = textComponent.getHighlighter(); @@ -586,7 +667,7 @@ public class DefaultCaret extends Rectangle int p0 = Math.min(dot, mark); int p1 = Math.max(dot, mark); - if (selectionVisible && p0 != p1) + if (selectionVisible) { try { @@ -659,7 +740,10 @@ public class DefaultCaret extends Rectangle if (comp == null) return; - int dot = getDot(); + // Make sure the dot has a sane position. + dot = Math.min(dot, textComponent.getDocument().getLength()); + dot = Math.max(dot, 0); + Rectangle rect = null; try @@ -668,10 +752,10 @@ public class DefaultCaret extends Rectangle } catch (BadLocationException e) { - AssertionError ae; - ae = new AssertionError("Unexpected bad caret location: " + dot); - ae.initCause(e); - throw ae; + AssertionError ae; + ae = new AssertionError("Unexpected bad caret location: " + dot); + ae.initCause(e); + throw ae; } if (rect == null) @@ -812,7 +896,11 @@ public class DefaultCaret extends Rectangle { if (dot >= 0) { - this.dot = dot; + Document doc = textComponent.getDocument(); + if (doc != null) + this.dot = Math.min(dot, doc.getLength()); + this.dot = Math.max(this.dot, 0); + handleHighlight(); adjustVisibility(this); appear(); @@ -836,8 +924,9 @@ public class DefaultCaret extends Rectangle if (doc != null) this.dot = Math.min(dot, doc.getLength()); this.dot = Math.max(this.dot, 0); - this.mark = dot; - handleHighlight(); + this.mark = this.dot; + + clearHighlight(); adjustVisibility(this); appear(); } diff --git a/libjava/classpath/javax/swing/text/DefaultEditorKit.java b/libjava/classpath/javax/swing/text/DefaultEditorKit.java index 88094b898f7..c0056963c78 100644 --- a/libjava/classpath/javax/swing/text/DefaultEditorKit.java +++ b/libjava/classpath/javax/swing/text/DefaultEditorKit.java @@ -707,16 +707,14 @@ public class DefaultEditorKit extends EditorKit JTextComponent t = getTextComponent(event); try { - // TODO: There is a more efficent solution, but - // viewToModel doesn't work properly. - Point p = t.modelToView(t.getCaret().getDot()).getLocation(); - int cur = t.getCaretPosition(); - int y = p.y; - while (y == p.y && cur > 0) - y = t.modelToView(--cur).getLocation().y; - if (cur != 0) - cur++; - t.setCaretPosition(cur); + int offs = Utilities.getRowStart(t, t.getCaretPosition()); + + if (offs > -1) + { + Caret c = t.getCaret(); + c.setDot(offs); + c.setMagicCaretPosition(t.modelToView(offs).getLocation()); + } } catch (BadLocationException ble) { @@ -729,17 +727,16 @@ public class DefaultEditorKit extends EditorKit public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); - try + try { - Point p = t.modelToView(t.getCaret().getDot()).getLocation(); - int cur = t.getCaretPosition(); - int y = p.y; - int length = t.getDocument().getLength(); - while (y == p.y && cur < length) - y = t.modelToView(++cur).getLocation().y; - if (cur != length) - cur--; - t.setCaretPosition(cur); + int offs = Utilities.getRowEnd(t, t.getCaretPosition()); + + if (offs > -1) + { + Caret c = t.getCaret(); + c.setDot(offs); + c.setMagicCaretPosition(t.modelToView(offs).getLocation()); + } } catch (BadLocationException ble) { @@ -756,11 +753,17 @@ public class DefaultEditorKit extends EditorKit { try { - int pos = t.getCaret().getDot(); - if (pos < t.getDocument().getEndPosition().getOffset()) - { - t.getDocument().remove(t.getCaret().getDot(), 1); - } + int pos = t.getSelectionStart(); + int len = t.getSelectionEnd() - pos; + + if (len > 0) + t.getDocument().remove(pos, len); + else if (pos < t.getDocument().getLength()) + t.getDocument().remove(pos, 1); + + Caret c = t.getCaret(); + c.setDot(pos); + c.setMagicCaretPosition(t.modelToView(pos).getLocation()); } catch (BadLocationException e) { @@ -778,11 +781,18 @@ public class DefaultEditorKit extends EditorKit { try { - int pos = t.getCaret().getDot(); - if (pos > t.getDocument().getStartPosition().getOffset()) + int pos = t.getSelectionStart(); + int len = t.getSelectionEnd() - pos; + + if (len > 0) + t.getDocument().remove(pos, len); + else if (pos > 0) { - t.getDocument().remove(pos - 1, 1); - t.getCaret().setDot(pos - 1); + pos--; + t.getDocument().remove(pos, 1); + Caret c = t.getCaret(); + c.setDot(pos); + c.setMagicCaretPosition(t.modelToView(pos).getLocation()); } } catch (BadLocationException e) @@ -799,8 +809,21 @@ public class DefaultEditorKit extends EditorKit JTextComponent t = getTextComponent(event); if (t != null) { - t.getCaret().setDot(Math.max(t.getCaret().getDot() - 1, - t.getDocument().getStartPosition().getOffset())); + int offs = t.getCaretPosition() - 1; + if (offs >= 0) + { + Caret c = t.getCaret(); + c.setDot(offs); + + try + { + c.setMagicCaretPosition(t.modelToView(offs).getLocation()); + } + catch (BadLocationException ble) + { + // Should not happen. + } + } } } }, @@ -811,8 +834,74 @@ public class DefaultEditorKit extends EditorKit JTextComponent t = getTextComponent(event); if (t != null) { - t.getCaret().setDot(Math.min(t.getCaret().getDot() + 1, - t.getDocument().getEndPosition().getOffset())); + int offs = t.getCaretPosition() + 1; + if (offs <= t.getDocument().getLength()) + { + Caret c = t.getCaret(); + c.setDot(offs); + + try + { + c.setMagicCaretPosition(t.modelToView(offs).getLocation()); + } + catch (BadLocationException ble) + { + // Should not happen. + } + } + } + + } + }, + new TextAction(upAction) + { + public void actionPerformed(ActionEvent event) + { + JTextComponent t = getTextComponent(event); + try + { + if (t != null) + { + Caret c = t.getCaret(); + // The magic caret position may be null when the caret + // has not moved yet. + Point mcp = c.getMagicCaretPosition(); + int x = (mcp != null) ? mcp.x : 0; + int pos = Utilities.getPositionAbove(t, t.getCaretPosition(), x); + + if (pos > -1) + t.setCaretPosition(pos); + } + } + catch(BadLocationException ble) + { + // FIXME: Swallowing allowed? + } + } + }, + new TextAction(downAction) + { + public void actionPerformed(ActionEvent event) + { + JTextComponent t = getTextComponent(event); + try + { + if (t != null) + { + Caret c = t.getCaret(); + // The magic caret position may be null when the caret + // has not moved yet. + Point mcp = c.getMagicCaretPosition(); + int x = (mcp != null) ? mcp.x : 0; + int pos = Utilities.getPositionBelow(t, t.getCaretPosition(), x); + + if (pos > -1) + t.setCaretPosition(pos); + } + } + catch(BadLocationException ble) + { + // FIXME: Swallowing allowed? } } }, @@ -823,8 +912,21 @@ public class DefaultEditorKit extends EditorKit JTextComponent t = getTextComponent(event); if (t != null) { - t.getCaret().moveDot(Math.max(t.getCaret().getDot() - 1, - t.getDocument().getStartPosition().getOffset())); + int offs = t.getCaretPosition() - 1; + + if(offs >= 0) + { + Caret c = t.getCaret(); + c.moveDot(offs); + try + { + c.setMagicCaretPosition(t.modelToView(offs).getLocation()); + } + catch(BadLocationException ble) + { + // Can't happen. + } + } } } }, @@ -835,11 +937,167 @@ public class DefaultEditorKit extends EditorKit JTextComponent t = getTextComponent(event); if (t != null) { - t.getCaret().moveDot(Math.min(t.getCaret().getDot() + 1, - t.getDocument().getEndPosition().getOffset())); + int offs = t.getCaretPosition() + 1; + + if(offs <= t.getDocument().getLength()) + { + Caret c = t.getCaret(); + c.moveDot(offs); + try + { + c.setMagicCaretPosition(t.modelToView(offs).getLocation()); + } + catch(BadLocationException ble) + { + // Can't happen. + } + } + } + } + }, + new TextAction(selectionUpAction) + { + public void actionPerformed(ActionEvent event) + { + JTextComponent t = getTextComponent(event); + try + { + if (t != null) + { + Caret c = t.getCaret(); + // The magic caret position may be null when the caret + // has not moved yet. + Point mcp = c.getMagicCaretPosition(); + int x = (mcp != null) ? mcp.x : 0; + int pos = Utilities.getPositionAbove(t, t.getCaretPosition(), x); + + if (pos > -1) + t.moveCaretPosition(pos); + } + } + catch(BadLocationException ble) + { + // FIXME: Swallowing allowed? + } + } + }, + new TextAction(selectionDownAction) + { + public void actionPerformed(ActionEvent event) + { + JTextComponent t = getTextComponent(event); + try + { + if (t != null) + { + Caret c = t.getCaret(); + // The magic caret position may be null when the caret + // has not moved yet. + Point mcp = c.getMagicCaretPosition(); + int x = (mcp != null) ? mcp.x : 0; + int pos = Utilities.getPositionBelow(t, t.getCaretPosition(), x); + + if (pos > -1) + t.moveCaretPosition(pos); + } + } + catch(BadLocationException ble) + { + // FIXME: Swallowing allowed? } } }, + new TextAction(selectionBeginLineAction) + { + public void actionPerformed(ActionEvent event) + { + JTextComponent t = getTextComponent(event); + + try + { + // TODO: There is a more efficent solution, but + // viewToModel doesn't work properly. + Point p = t.modelToView(t.getCaret().getDot()).getLocation(); + + int cur = t.getCaretPosition(); + int y = p.y; + + while (y == p.y && cur > 0) + y = t.modelToView(--cur).getLocation().y; + if (cur != 0) + cur++; + + Caret c = t.getCaret(); + c.moveDot(cur); + c.setMagicCaretPosition(t.modelToView(cur).getLocation()); + } + catch (BadLocationException ble) + { + // Do nothing here. + } + } + }, + new TextAction(selectionEndLineAction) + { + public void actionPerformed(ActionEvent event) + { + JTextComponent t = getTextComponent(event); + try + { + Point p = t.modelToView(t.getCaret().getDot()).getLocation(); + int cur = t.getCaretPosition(); + int y = p.y; + int length = t.getDocument().getLength(); + while (y == p.y && cur < length) + y = t.modelToView(++cur).getLocation().y; + if (cur != length) + cur--; + + Caret c = t.getCaret(); + c.moveDot(cur); + c.setMagicCaretPosition(t.modelToView(cur).getLocation()); + } + catch (BadLocationException ble) + { + // Nothing to do here + } + } + }, + new TextAction(selectionEndAction) + { + public void actionPerformed(ActionEvent event) + { + JTextComponent t = getTextComponent(event); + int offs = t.getDocument().getLength(); + Caret c = t.getCaret(); + c.moveDot(offs); + try + { + c.setMagicCaretPosition(t.modelToView(offs).getLocation()); + } + catch(BadLocationException ble) + { + // Can't happen. + } + } + }, + new TextAction(selectionBeginAction) + { + public void actionPerformed(ActionEvent event) + { + JTextComponent t = getTextComponent(event); + Caret c = t.getCaret(); + c.moveDot(0); + try + { + c.setMagicCaretPosition(t.modelToView(0).getLocation()); + } + catch(BadLocationException ble) + { + // Can't happen. + } + } + } }; /** diff --git a/libjava/classpath/javax/swing/text/DefaultFormatter.java b/libjava/classpath/javax/swing/text/DefaultFormatter.java index 493699dacba..e42b1698af8 100644 --- a/libjava/classpath/javax/swing/text/DefaultFormatter.java +++ b/libjava/classpath/javax/swing/text/DefaultFormatter.java @@ -219,7 +219,6 @@ public class DefaultFormatter extends JFormattedTextField.AbstractFormatter commitsOnValidEdit = true; overwriteMode = true; allowsInvalid = true; - valueClass = Object.class; } /** @@ -368,7 +367,11 @@ public class DefaultFormatter extends JFormattedTextField.AbstractFormatter Object value = string; Class valueClass = getValueClass(); if (valueClass == null) - valueClass = getFormattedTextField().getValue().getClass(); + { + JFormattedTextField jft = getFormattedTextField(); + if (jft != null) + valueClass = jft.getValue().getClass(); + } if (valueClass != null) try { diff --git a/libjava/classpath/javax/swing/text/DefaultHighlighter.java b/libjava/classpath/javax/swing/text/DefaultHighlighter.java index 40ea4f80aab..33b5fcab8bf 100644 --- a/libjava/classpath/javax/swing/text/DefaultHighlighter.java +++ b/libjava/classpath/javax/swing/text/DefaultHighlighter.java @@ -1,5 +1,5 @@ /* DefaultHighlighter.java -- - Copyright (C) 2004 Free Software Foundation, Inc. + Copyright (C) 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -40,9 +40,12 @@ package javax.swing.text; import java.awt.Color; import java.awt.Graphics; +import java.awt.Insets; import java.awt.Rectangle; import java.awt.Shape; -import java.util.Vector; +import java.util.ArrayList; + +import javax.swing.plaf.TextUI; public class DefaultHighlighter extends LayeredHighlighter { @@ -84,7 +87,7 @@ public class DefaultHighlighter extends LayeredHighlighter // This should never occur. return; } - + if (r0 == null || r1 == null) return; @@ -100,7 +103,7 @@ public class DefaultHighlighter extends LayeredHighlighter paintHighlight(g, r0); return; } - + // First line, from p0 to end-of-line. r0.width = rect.x + rect.width - r0.x; paintHighlight(g, r0); @@ -109,15 +112,19 @@ public class DefaultHighlighter extends LayeredHighlighter // have the same height -- not a good assumption with JEditorPane/JTextPane). r0.y += r0.height; r0.x = rect.x; - + r0.width = rect.width; + while (r0.y < r1.y) { paintHighlight(g, r0); r0.y += r0.height; } - // Last line, from beginnin-of-line to p1. - paintHighlight(g, r1); + // Last line, from beginning-of-line to p1. + // The "-1" is neccessary else we would paint one pixel column more + // than in the case where the selection is only on one line. + r0.width = r1.x + r1.width - 1; + paintHighlight(g, r0); } public Shape paintLayer(Graphics g, int p0, int p1, Shape bounds, @@ -127,7 +134,7 @@ public class DefaultHighlighter extends LayeredHighlighter } } - private class HighlightEntry + private class HighlightEntry implements Highlighter.Highlight { int p0; int p1; @@ -140,12 +147,12 @@ public class DefaultHighlighter extends LayeredHighlighter this.painter = painter; } - public int getStartPosition() + public int getStartOffset() { return p0; } - public int getEndPosition() + public int getEndOffset() { return p1; } @@ -163,7 +170,7 @@ public class DefaultHighlighter extends LayeredHighlighter new DefaultHighlightPainter(null); private JTextComponent textComponent; - private Vector highlights = new Vector(); + private ArrayList highlights = new ArrayList(); private boolean drawsLayeredHighlights = true; public DefaultHighlighter() @@ -208,12 +215,20 @@ public class DefaultHighlighter extends LayeredHighlighter checkPositions(p0, p1); HighlightEntry entry = new HighlightEntry(p0, p1, painter); highlights.add(entry); + + textComponent.getUI().damageRange(textComponent, p0, p1); + return entry; } public void removeHighlight(Object tag) { highlights.remove(tag); + + HighlightEntry entry = (HighlightEntry) tag; + textComponent.getUI().damageRange(textComponent, + entry.p0, + entry.p1); } public void removeAllHighlights() @@ -223,16 +238,93 @@ public class DefaultHighlighter extends LayeredHighlighter public Highlighter.Highlight[] getHighlights() { - return null; + return (Highlighter.Highlight[]) + highlights.toArray(new Highlighter.Highlight[highlights.size()]); } - public void changeHighlight(Object tag, int p0, int p1) + public void changeHighlight(Object tag, int n0, int n1) throws BadLocationException { - checkPositions(p0, p1); + int o0, o1; + + checkPositions(n0, n1); HighlightEntry entry = (HighlightEntry) tag; - entry.p0 = p0; - entry.p1 = p1; + o0 = entry.p0; + o1 = entry.p1; + + // Prevent useless write & repaint operations. + if (o0 == n0 && o1 == n1) + return; + + entry.p0 = n0; + entry.p1 = n1; + + TextUI ui = textComponent.getUI(); + + // Special situation where the old area has to be cleared simply. + if (n0 == n1) + ui.damageRange(textComponent, o0, o1); + // Calculates the areas where a change is really neccessary + else if ((o1 > n0 && o1 <= n1) + || (n1 > o0 && n1 <= o1)) + { + // [fds, fde) - the first damage region + // [sds, sde] - the second damage region + int fds, sds; + int fde, sde; + + // Calculate first damaged region. + if(o0 < n0) + { + // Damaged region will be cleared as + // the old highlight region starts first. + fds = o0; + fde = n0; + } + else + { + // Damaged region will be painted as + // the new highlight region starts first. + fds = n0; + fde = o0; + } + + if (o1 < n1) + { + // Final region will be painted as the + // old highlight region finishes first + sds = o1; + sde = n1; + } + else + { + // Final region will be cleared as the + // new highlight region finishes first. + sds = n1; + sde = o1; + } + + // If there is no undamaged region in between + // call damageRange only once. + if (fde == sds) + ui.damageRange(textComponent, fds, sde); + else + { + if (fds != fde) + ui.damageRange(textComponent, fds, fde); + + if (sds != sde) + ui.damageRange(textComponent, sds, sde); + } + } + else + { + // The two regions do not overlap. So mark + // both areas as damaged. + ui.damageRange(textComponent, o0, o1); + ui.damageRange(textComponent, n0, n1); + } + } public void paintLayeredHighlights(Graphics g, int p0, int p1, @@ -244,13 +336,21 @@ public class DefaultHighlighter extends LayeredHighlighter public void paint(Graphics g) { + int size = highlights.size(); + // Check if there are any highlights. - if (highlights.size() == 0) + if (size == 0) return; + + // Prepares the rectangle of the inner drawing area. + Insets insets = textComponent.getInsets(); + Shape bounds = + new Rectangle(insets.left, + insets.top, + textComponent.getWidth() - insets.left - insets.right, + textComponent.getHeight() - insets.top - insets.bottom); - Shape bounds = textComponent.getBounds(); - - for (int index = 0; index < highlights.size(); ++index) + for (int index = 0; index < size; ++index) { HighlightEntry entry = (HighlightEntry) highlights.get(index); entry.painter.paint(g, entry.p0, entry.p1, bounds, textComponent); diff --git a/libjava/classpath/javax/swing/text/DefaultStyledDocument.java b/libjava/classpath/javax/swing/text/DefaultStyledDocument.java index 46b82259b65..625ba4c3dcc 100644 --- a/libjava/classpath/javax/swing/text/DefaultStyledDocument.java +++ b/libjava/classpath/javax/swing/text/DefaultStyledDocument.java @@ -53,27 +53,25 @@ import javax.swing.undo.AbstractUndoableEdit; import javax.swing.undo.UndoableEdit; /** - * The default implementation of {@link StyledDocument}. - * - * The document is modeled as an {@link Element} tree, which has - * a {@link SectionElement} as single root, which has one or more - * {@link AbstractDocument.BranchElement}s as paragraph nodes - * and each paragraph node having one or more + * The default implementation of {@link StyledDocument}. The document is + * modeled as an {@link Element} tree, which has a {@link SectionElement} as + * single root, which has one or more {@link AbstractDocument.BranchElement}s + * as paragraph nodes and each paragraph node having one or more * {@link AbstractDocument.LeafElement}s as content nodes. - * + * * @author Michael Koch (konqueror@gmx.de) * @author Roman Kennke (roman@kennke.org) */ -public class DefaultStyledDocument extends AbstractDocument - implements StyledDocument +public class DefaultStyledDocument extends AbstractDocument implements + StyledDocument { + /** * An {@link UndoableEdit} that can undo attribute changes to an element. - * + * * @author Roman Kennke (kennke@aicas.com) */ - public static class AttributeUndoableEdit - extends AbstractUndoableEdit + public static class AttributeUndoableEdit extends AbstractUndoableEdit { /** * A copy of the old attributes. @@ -98,11 +96,13 @@ public class DefaultStyledDocument extends AbstractDocument /** * Creates a new <code>AttributeUndoableEdit</code>. - * - * @param el the element that changes attributes - * @param newAtts the new attributes - * @param replacing if the new attributes replace the old or only append to - * them + * + * @param el + * the element that changes attributes + * @param newAtts + * the new attributes + * @param replacing + * if the new attributes replace the old or only append to them */ public AttributeUndoableEdit(Element el, AttributeSet newAtts, boolean replacing) @@ -149,21 +149,19 @@ public class DefaultStyledDocument extends AbstractDocument } /** - * Carries specification information for new {@link Element}s that should - * be created in {@link ElementBuffer}. This allows the parsing process - * to be decoupled from the <code>Element</code> creation process. + * Carries specification information for new {@link Element}s that should be + * created in {@link ElementBuffer}. This allows the parsing process to be + * decoupled from the <code>Element</code> creation process. */ public static class ElementSpec { /** - * This indicates a start tag. This is a possible value for - * {@link #getType}. + * This indicates a start tag. This is a possible value for {@link #getType}. */ public static final short StartTagType = 1; /** - * This indicates an end tag. This is a possible value for - * {@link #getType}. + * This indicates an end tag. This is a possible value for {@link #getType}. */ public static final short EndTagType = 2; @@ -175,22 +173,19 @@ public class DefaultStyledDocument extends AbstractDocument /** * This indicates that the data associated with this spec should be joined - * with what precedes it. This is a possible value for - * {@link #getDirection}. + * with what precedes it. This is a possible value for {@link #getDirection}. */ public static final short JoinPreviousDirection = 4; /** * This indicates that the data associated with this spec should be joined - * with what follows it. This is a possible value for - * {@link #getDirection}. + * with what follows it. This is a possible value for {@link #getDirection}. */ public static final short JoinNextDirection = 5; /** - * This indicates that the data associated with this spec should be used - * to create a new element. This is a possible value for - * {@link #getDirection}. + * This indicates that the data associated with this spec should be used to + * create a new element. This is a possible value for {@link #getDirection}. */ public static final short OriginateDirection = 6; @@ -234,9 +229,11 @@ public class DefaultStyledDocument extends AbstractDocument /** * Creates a new <code>ElementSpec</code> with no content, length or * offset. This is most useful for start and end tags. - * - * @param a the attributes for the element to be created - * @param type the type of the tag + * + * @param a + * the attributes for the element to be created + * @param type + * the type of the tag */ public ElementSpec(AttributeSet a, short type) { @@ -247,27 +244,34 @@ public class DefaultStyledDocument extends AbstractDocument * Creates a new <code>ElementSpec</code> that specifies the length but * not the offset of an element. Such <code>ElementSpec</code>s are * processed sequentially from a known starting point. - * - * @param a the attributes for the element to be created - * @param type the type of the tag - * @param len the length of the element + * + * @param a + * the attributes for the element to be created + * @param type + * the type of the tag + * @param len + * the length of the element */ public ElementSpec(AttributeSet a, short type, int len) { this(a, type, null, 0, len); } - + /** * Creates a new <code>ElementSpec</code> with document content. - * - * @param a the attributes for the element to be created - * @param type the type of the tag - * @param txt the actual content - * @param offs the offset into the <code>txt</code> array - * @param len the length of the element + * + * @param a + * the attributes for the element to be created + * @param type + * the type of the tag + * @param txt + * the actual content + * @param offs + * the offset into the <code>txt</code> array + * @param len + * the length of the element */ - public ElementSpec(AttributeSet a, short type, char[] txt, int offs, - int len) + public ElementSpec(AttributeSet a, short type, char[] txt, int offs, int len) { attributes = a; this.type = type; @@ -279,8 +283,9 @@ public class DefaultStyledDocument extends AbstractDocument /** * Sets the type of the element. - * - * @param type the type of the element to be set + * + * @param type + * the type of the element to be set */ public void setType(short type) { @@ -289,7 +294,7 @@ public class DefaultStyledDocument extends AbstractDocument /** * Returns the type of the element. - * + * * @return the type of the element */ public short getType() @@ -299,8 +304,9 @@ public class DefaultStyledDocument extends AbstractDocument /** * Sets the direction of the element. - * - * @param dir the direction of the element to be set + * + * @param dir + * the direction of the element to be set */ public void setDirection(short dir) { @@ -309,7 +315,7 @@ public class DefaultStyledDocument extends AbstractDocument /** * Returns the direction of the element. - * + * * @return the direction of the element */ public short getDirection() @@ -319,7 +325,7 @@ public class DefaultStyledDocument extends AbstractDocument /** * Returns the attributes of the element. - * + * * @return the attributes of the element */ public AttributeSet getAttributes() @@ -329,7 +335,7 @@ public class DefaultStyledDocument extends AbstractDocument /** * Returns the actual content of the element. - * + * * @return the actual content of the element */ public char[] getArray() @@ -339,7 +345,7 @@ public class DefaultStyledDocument extends AbstractDocument /** * Returns the offset of the content. - * + * * @return the offset of the content */ public int getOffset() @@ -349,7 +355,7 @@ public class DefaultStyledDocument extends AbstractDocument /** * Returns the length of the content. - * + * * @return the length of the content */ public int getLength() @@ -361,7 +367,7 @@ public class DefaultStyledDocument extends AbstractDocument * Returns a String representation of this <code>ElementSpec</code> * describing the type, direction and length of this * <code>ElementSpec</code>. - * + * * @return a String representation of this <code>ElementSpec</code> */ public String toString() @@ -413,7 +419,8 @@ public class DefaultStyledDocument extends AbstractDocument /** * Performs all <em>structural</code> changes to the <code>Element</code> - * hierarchy. + * hierarchy. This class was implemented with much help from the document: + * http://java.sun.com/products/jfc/tsc/articles/text/element_buffer/index.html. */ public class ElementBuffer implements Serializable { @@ -426,25 +433,20 @@ public class DefaultStyledDocument extends AbstractDocument /** Holds the offset for structural changes. */ private int offset; + /** Holds the end offset for structural changes. */ + private int endOffset; + /** Holds the length of structural changes. */ private int length; - - /** Holds the end offset for structural changes. **/ - private int endOffset; - /** - * The number of inserted end tags. This is a counter which always gets - * incremented when an end tag is inserted. This is evaluated before - * content insertion to go up the element stack. - */ - private int numEndTags; + /** Holds the position of the change. */ + private int pos; - /** - * The number of inserted start tags. This is a counter which always gets - * incremented when an end tag is inserted. This is evaluated before - * content insertion to go up the element stack. - */ - private int numStartTags; + /** Holds the element that was last fractured. */ + private Element lastFractured; + + /** True if a fracture was not created during a insertFracture call. */ + private boolean fracNotCreated; /** * The current position in the element tree. This is used for bulk inserts @@ -453,14 +455,6 @@ public class DefaultStyledDocument extends AbstractDocument private Stack elementStack; /** - * Holds fractured elements during insertion of end and start tags. - * Inserting an end tag may lead to fracturing of the current paragraph - * element. The elements that have been cut off may be added to the - * next paragraph that is created in the next start tag. - */ - Element[] fracture; - - /** * The ElementChange that describes the latest changes. */ DefaultDocumentEvent documentEvent; @@ -468,8 +462,9 @@ public class DefaultStyledDocument extends AbstractDocument /** * Creates a new <code>ElementBuffer</code> for the specified * <code>root</code> element. - * - * @param root the root element for this <code>ElementBuffer</code> + * + * @param root + * the root element for this <code>ElementBuffer</code> */ public ElementBuffer(Element root) { @@ -479,7 +474,7 @@ public class DefaultStyledDocument extends AbstractDocument /** * Returns the root element of this <code>ElementBuffer</code>. - * + * * @return the root element of this <code>ElementBuffer</code> */ public Element getRootElement() @@ -488,21 +483,23 @@ public class DefaultStyledDocument extends AbstractDocument } /** - * Updates the element structure of the document in response to removal of - * content. It removes the affected {@link Element}s from the document - * structure. - * - * This method sets some internal parameters and delegates the work - * to {@link #removeUpdate}. - * - * @param offs the offset from which content is remove - * @param len the length of the removed content - * @param ev the document event that records the changes + * Removes the content. This method sets some internal parameters and + * delegates the work to {@link #removeUpdate}. + * + * @param offs + * the offset from which content is remove + * @param len + * the length of the removed content + * @param ev + * the document event that records the changes */ public void remove(int offs, int len, DefaultDocumentEvent ev) { + if (len == 0) + return; offset = offs; length = len; + pos = offset; documentEvent = ev; removeUpdate(); } @@ -519,9 +516,9 @@ public class DefaultStyledDocument extends AbstractDocument Element[] empty = new Element[0]; int removeStart = -1; int removeEnd = -1; - for (int i = startParagraph; i < endParagraph; i++) + for (int i = startParagraph; i < endParagraph; i++) { - Element paragraph = root.getElement(i); + BranchElement paragraph = (BranchElement) root.getElement(i); int contentStart = paragraph.getElementIndex(offset); int contentEnd = paragraph.getElementIndex(offset + length); if (contentStart == paragraph.getStartOffset() @@ -546,10 +543,8 @@ public class DefaultStyledDocument extends AbstractDocument Element[] removed = new Element[removeLen]; for (int j = contentStart; j < contentEnd; j++) removed[j] = paragraph.getElement(j); - ((BranchElement) paragraph).replace(contentStart, removeLen, - empty); - documentEvent.addEdit(new ElementEdit(paragraph, contentStart, - removed, empty)); + Edit edit = getEditForParagraphAndIndex(paragraph, contentStart); + edit.addRemovedElements(removed); } } // Now we remove paragraphs from the root that have been tagged for @@ -560,265 +555,234 @@ public class DefaultStyledDocument extends AbstractDocument Element[] removed = new Element[removeLen]; for (int i = removeStart; i < removeEnd; i++) removed[i] = root.getElement(i); - ((BranchElement) root).replace(removeStart, removeLen, empty); - documentEvent.addEdit(new ElementEdit(root, removeStart, removed, - empty)); + Edit edit = getEditForParagraphAndIndex((BranchElement) root, + removeStart); + edit.addRemovedElements(removed); } } /** - * Modifies the element structure so that the specified interval starts - * and ends at an element boundary. Content and paragraph elements - * are split and created as necessary. - * - * This also updates the <code>DefaultDocumentEvent</code> to reflect the - * structural changes. - * - * The bulk work is delegated to {@link #changeUpdate()}. - * - * @param offset the start index of the interval to be changed - * @param length the length of the interval to be changed - * @param ev the <code>DefaultDocumentEvent</code> describing the change - */ - public void change(int offset, int length, DefaultDocumentEvent ev) - { - this.offset = offset; - this.length = length; - documentEvent = ev; - changeUpdate(); - } - - /** - * Performs the actual work for {@link #change}. - * The elements at the interval boundaries are split up (if necessary) - * so that the interval boundaries are located at element boundaries. + * Performs the actual work for {@link #change}. The elements at the + * interval boundaries are split up (if necessary) so that the interval + * boundaries are located at element boundaries. */ protected void changeUpdate() { // Split up the element at the start offset if necessary. Element el = getCharacterElement(offset); - Element[] res = split(el, offset, 0); + Element[] res = split(el, offset, 0, el.getElementIndex(offset)); BranchElement par = (BranchElement) el.getParentElement(); + int index = par.getElementIndex(offset); + Edit edit = getEditForParagraphAndIndex(par, index); if (res[1] != null) { - int index = par.getElementIndex(offset); Element[] removed; Element[] added; if (res[0] == null) { removed = new Element[0]; - added = new Element[]{ res[1] }; + added = new Element[] { res[1] }; index++; } else { - removed = new Element[]{ el }; - added = new Element[]{ res[0], res[1] }; + removed = new Element[] { el }; + added = new Element[] { res[0], res[1] }; } - par.replace(index, removed.length, added); - addEdit(par, index, removed, added); + edit.addRemovedElements(removed); + + edit.addAddedElements(added); } int endOffset = offset + length; el = getCharacterElement(endOffset); - res = split(el, endOffset, 0); + res = split(el, endOffset, 0, el.getElementIndex(endOffset)); par = (BranchElement) el.getParentElement(); - if (res[1] != null) + if (res[0] != null) { - int index = par.getElementIndex(offset); Element[] removed; Element[] added; if (res[1] == null) { removed = new Element[0]; - added = new Element[]{ res[1] }; + added = new Element[] { res[1] }; } else { - removed = new Element[]{ el }; - added = new Element[]{ res[0], res[1] }; + removed = new Element[] { el }; + added = new Element[] { res[0], res[1] }; } - par.replace(index, removed.length, added); - addEdit(par, index, removed, added); + edit.addRemovedElements(removed); + edit.addAddedElements(added); } } /** - * Splits an element if <code>offset</code> is not alread at its boundary. + * Modifies the element structure so that the specified interval starts and + * ends at an element boundary. Content and paragraph elements are split and + * created as necessary. This also updates the + * <code>DefaultDocumentEvent</code> to reflect the structural changes. + * The bulk work is delegated to {@link #changeUpdate()}. + * + * @param offset + * the start index of the interval to be changed + * @param length + * the length of the interval to be changed + * @param ev + * the <code>DefaultDocumentEvent</code> describing the change + */ + public void change(int offset, int length, DefaultDocumentEvent ev) + { + if (length == 0) + return; + this.offset = offset; + this.pos = offset; + this.length = length; + documentEvent = ev; + changeUpdate(); + } + + /** + * Creates and returns a deep clone of the specified <code>clonee</code> + * with the specified parent as new parent. * - * @param el the Element to possibly split - * @param offset the offset at which to possibly split - * @param space the amount of space to create between the splitted parts + * This method can only clone direct instances of {@link BranchElement} + * or {@link LeafElement}. * - * @return An array of elements which represent the split result. This - * array has two elements, the two parts of the split. The first - * element might be null, which means that the element which should - * be splitted can remain in place. The second element might also - * be null, which means that the offset is already at an element - * boundary and the element doesn't need to be splitted. - * + * @param parent the new parent + * @param clonee the element to be cloned + * + * @return the cloned element with the new parent */ - private Element[] split(Element el, int offset, int space) + public Element clone(Element parent, Element clonee) { - // If we are at an element boundary, then return an empty array. - if ((offset == el.getStartOffset() || offset == el.getEndOffset()) - && space == 0 && el.isLeaf()) - return new Element[2]; - - // If the element is an instance of BranchElement, then we recursivly - // call this method to perform the split. - Element[] res = new Element[2]; - if (el instanceof BranchElement) + Element clone = clonee; + // We can only handle AbstractElements here. + if (clonee instanceof BranchElement) { - int index = el.getElementIndex(offset); - Element child = el.getElement(index); - Element[] result = split(child, offset, space); - Element[] removed; - Element[] added; - Element[] newAdded; - - int count = el.getElementCount(); - if (!(result[1] == null)) - { - // This is the case when we can keep the first element. - if (result[0] == null) - { - removed = new Element[count - index - 1]; - newAdded = new Element[count - index - 1]; - added = new Element[]{}; - } - // This is the case when we may not keep the first element. - else - { - removed = new Element[count - index]; - newAdded = new Element[count - index]; - added = new Element[]{result[0]}; - } - newAdded[0] = result[1]; - for (int i = index; i < count; i++) - { - Element el2 = el.getElement(i); - int ind = i - count + removed.length; - removed[ind] = el2; - if (ind != 0) - newAdded[ind] = el2; - } - - ((BranchElement) el).replace(index, removed.length, added); - addEdit(el, index, removed, added); - BranchElement newPar = - (BranchElement) createBranchElement(el.getParentElement(), - el.getAttributes()); - newPar.replace(0, 0, newAdded); - res = new Element[]{ null, newPar }; - } - else + BranchElement branchEl = (BranchElement) clonee; + BranchElement branchClone = + new BranchElement(parent, branchEl.getAttributes()); + // Also clone all of the children. + int numChildren = branchClone.getElementCount(); + Element[] cloneChildren = new Element[numChildren]; + for (int i = 0; i < numChildren; ++i) { - removed = new Element[count - index]; - for (int i = index; i < count; ++i) - removed[i - index] = el.getElement(i); - added = new Element[0]; - ((BranchElement) el).replace(index, removed.length, - added); - addEdit(el, index, removed, added); - BranchElement newPar = - (BranchElement) createBranchElement(el.getParentElement(), - el.getAttributes()); - newPar.replace(0, 0, removed); - res = new Element[]{ null, newPar }; + cloneChildren[i] = clone(branchClone, + branchClone.getElement(i)); } + branchClone.replace(0, 0, cloneChildren); + clone = branchClone; } - else if (el instanceof LeafElement) + else if (clonee instanceof LeafElement) { - BranchElement par = (BranchElement) el.getParentElement(); - Element el1 = createLeafElement(par, el.getAttributes(), - el.getStartOffset(), offset); - Element el2 = createLeafElement(par, el.getAttributes(), - offset + space, el.getEndOffset()); - res = new Element[]{ el1, el2 }; + clone = new LeafElement(parent, clonee.getAttributes(), + clonee.getStartOffset(), + clonee.getEndOffset()); } - return res; + return clone; } /** * Inserts new <code>Element</code> in the document at the specified - * position. - * - * Most of the work is done by {@link #insertUpdate}, after some fields - * have been prepared for it. - * - * @param offset the location in the document at which the content is - * inserted - * @param length the length of the inserted content - * @param data the element specifications for the content to be inserted - * @param ev the document event that is updated to reflect the structural - * changes + * position. Most of the work is done by {@link #insertUpdate}, after some + * fields have been prepared for it. + * + * @param offset + * the location in the document at which the content is inserted + * @param length + * the length of the inserted content + * @param data + * the element specifications for the content to be inserted + * @param ev + * the document event that is updated to reflect the structural + * changes */ public void insert(int offset, int length, ElementSpec[] data, DefaultDocumentEvent ev) { if (length == 0) return; + this.offset = offset; - this.length = length; + this.pos = offset; this.endOffset = offset + length; + this.length = length; documentEvent = ev; - // Push the root and the paragraph at offset onto the element stack. - elementStack.clear(); - elementStack.push(root); - elementStack.push(root.getElement(root.getElementIndex(offset))); - numEndTags = 0; - numStartTags = 0; + + edits.removeAllElements(); + elementStack.removeAllElements(); + lastFractured = null; + fracNotCreated = false; insertUpdate(data); + // This for loop applies all the changes that were made and updates the + // DocumentEvent. + int size = edits.size(); + for (int i = 0; i < size; i++) + { + Edit curr = (Edit) edits.get(i); + BranchElement e = (BranchElement) curr.e; + Element[] removed = curr.getRemovedElements(); + Element[] added = curr.getAddedElements(); + // FIXME: We probably shouldn't create the empty Element[] in the + // first place. + if (removed.length > 0 || added.length > 0) + { + if (curr.index + removed.length <= e.getElementCount()) + { + e.replace(curr.index, removed.length, added); + ElementEdit ee = new ElementEdit(e, curr.index, removed, added); + ev.addEdit(ee); + } + else + { + System.err.println("WARNING: Tried to replace elements "); + System.err.print("beyond boundaries: elementCount: "); + System.err.println(e.getElementCount()); + System.err.print("index: " + curr.index); + System.err.println(", removed.length: " + removed.length); + } + } + } } /** - * Performs the actual structural change for {@link #insert}. This - * creates a bunch of {@link Element}s as specified by <code>data</code> - * and inserts it into the document as specified in the arguments to - * {@link #insert}. - * - * @param data the element specifications for the elements to be inserte - */ + * Inserts new content + * + * @param data + * the element specifications for the elements to be inserted + */ protected void insertUpdate(ElementSpec[] data) { - if (data[0].getType() == ElementSpec.EndTagType) + // Push the root and the paragraph at offset onto the element stack. + Element current = root; + int index; + while (!current.isLeaf()) { - // fracture deepest child here - BranchElement paragraph = (BranchElement) elementStack.peek(); - Element curr = paragraph.getParentElement(); - int index = curr.getElementIndex(offset); - while (!curr.isLeaf()) - { - index = curr.getElementIndex(offset); - curr = curr.getElement(index); - } - Element parent = curr.getParentElement(); - Element newEl1 = createLeafElement(parent, - curr.getAttributes(), - curr.getStartOffset(), offset); - Element grandParent = parent.getParentElement(); - BranchElement nextBranch = - (BranchElement) grandParent.getElement - (grandParent.getElementIndex(parent.getEndOffset())); - Element firstLeaf = nextBranch.getElement(0); - while (!firstLeaf.isLeaf()) - { - firstLeaf = firstLeaf.getElement(0); - } - BranchElement parent2 = (BranchElement) firstLeaf.getParentElement(); - Element newEl2 = - createLeafElement(parent2, - firstLeaf.getAttributes(), - offset, firstLeaf.getEndOffset()); - parent2.replace(0, 1, new Element[] { newEl2 }); - - - ((BranchElement) parent). - replace(index, 1, new Element[] { newEl1 }); + index = current.getElementIndex(offset); + elementStack.push(current); + current = current.getElement(index); } - for (int i = 0; i < data.length; i++) + int i = 0; + int type = data[0].getType(); + if (type == ElementSpec.ContentType) + { + // If the first tag is content we must treat it separately to allow + // for joining properly to previous Elements and to ensure that + // no extra LeafElements are erroneously inserted. + insertFirstContentTag(data); + pos += data[0].length; + i = 1; + } + else + { + createFracture(data); + i = 0; + } + + // Handle each ElementSpec individually. + for (; i < data.length; i++) { BranchElement paragraph = (BranchElement) elementStack.peek(); switch (data[i].getType()) @@ -827,19 +791,41 @@ public class DefaultStyledDocument extends AbstractDocument switch (data[i].getDirection()) { case ElementSpec.JoinFractureDirection: + // Fracture the tree and ensure the appropriate element + // is on top of the stack. + fracNotCreated = false; insertFracture(data[i]); + if (fracNotCreated) + { + if (lastFractured != null) + elementStack.push(lastFractured.getParentElement()); + else + elementStack.push(paragraph.getElement(0)); + } break; case ElementSpec.JoinNextDirection: - int index = paragraph.getElementIndex(offset); - elementStack.push(paragraph.getElement(index)); - break; - case ElementSpec.OriginateDirection: - Element current = (Element) elementStack.peek(); - Element newParagraph = - insertParagraph((BranchElement) current, offset); - elementStack.push(newParagraph); + // Push the next paragraph element onto the stack so + // future insertions are added to it. + int ix = paragraph.getElementIndex(pos) + 1; + elementStack.push(paragraph.getElement(ix)); break; default: + Element br = null; + if (data.length > i + 1) + { + // leaves will be added to paragraph later + int x = 0; + if (paragraph.getElementCount() > 0) + x = paragraph.getElementIndex(pos) + 1; + Edit e = getEditForParagraphAndIndex(paragraph, x); + br = (BranchElement) createBranchElement(paragraph, + data[i].getAttributes()); + e.added.add(br); + elementStack.push(br); + } + else + // need to add leaves to paragraph now + br = insertParagraph(paragraph, pos); break; } break; @@ -848,50 +834,27 @@ public class DefaultStyledDocument extends AbstractDocument break; case ElementSpec.ContentType: insertContentTag(data[i]); + offset = pos; break; } } - endEdit(); } - - /** - * Finishes an insertion by possibly evaluating the outstanding start and - * end tags. However, this is only performed if the event has received any - * modifications. - */ - private void endEdit() - { - if (documentEvent.modified) - prepareContentInsertion(); - } - + /** - * Evaluates the number of inserted end tags and performs the corresponding - * structural changes. + * Inserts a new paragraph. + * + * @param par - + * the parent + * @param offset - + * the offset + * @return the new paragraph */ - private void prepareContentInsertion() - { - while (numEndTags > 0) - { - elementStack.pop(); - numEndTags--; - } - - while (numStartTags > 0) - { - Element current = (Element) elementStack.peek(); - Element newParagraph = - insertParagraph((BranchElement) current, offset); - elementStack.push(newParagraph); - numStartTags--; - } - } - private Element insertParagraph(BranchElement par, int offset) { - Element current = par.getElement(par.getElementIndex(offset)); - Element[] res = split(current, offset, 0); int index = par.getElementIndex(offset); + Element current = par.getElement(index); + Element[] res = split(current, offset, 0, 0); + Edit e = getEditForParagraphAndIndex(par, index + 1); Element ret; if (res[1] != null) { @@ -902,334 +865,757 @@ public class DefaultStyledDocument extends AbstractDocument removed = new Element[0]; if (res[1] instanceof BranchElement) { - added = new Element[]{ res[1] }; + added = new Element[] { res[1] }; ret = res[1]; } else { ret = createBranchElement(par, null); - added = new Element[]{ ret, res[1] }; + added = new Element[] { ret, res[1] }; } index++; } else { - removed = new Element[]{ current }; + removed = new Element[] { current }; if (res[1] instanceof BranchElement) { ret = res[1]; - added = new Element[]{ res[0], res[1] }; + added = new Element[] { res[0], res[1] }; } else { ret = createBranchElement(par, null); - added = new Element[]{ res[0], ret, res[1] }; + added = new Element[] { res[0], ret, res[1] }; } } - par.replace(index, removed.length, added); - addEdit(par, index, removed, added); + + e.addAddedElements(added); + e.addRemovedElements(removed); } else { ret = createBranchElement(par, null); - Element[] added = new Element[]{ ret }; - par.replace(index, 0, added); - addEdit(par, index, new Element[0], added); + e.addAddedElement(ret); } return ret; } /** - * Inserts a fracture into the document structure. + * Inserts the first tag into the document. * - * @param tag - the element spec. + * @param data - + * the data to be inserted. */ - private void insertFracture(ElementSpec tag) + private void insertFirstContentTag(ElementSpec[] data) { - // This is the parent of the paragraph about to be fractured. We will - // create a new child of this parent. - BranchElement parent = (BranchElement) elementStack.peek(); - int parentIndex = parent.getElementIndex(offset); - - // This is the old paragraph. We must remove all its children that - // occur after offset and move them to a new paragraph. We must - // also recreate its child that occurs at offset to have the proper - // end offset. The remainder of this child will also go in the new - // paragraph. - BranchElement previous = (BranchElement) parent.getElement(parentIndex); - - // This is the new paragraph. - BranchElement newBranch = - (BranchElement) createBranchElement(parent, previous.getAttributes()); - - - // The steps we must take to properly fracture are: - // 1. Recreate the LeafElement at offset to have the correct end offset. - // 2. Create a new LeafElement with the remainder of the LeafElement in - // #1 ==> this is whatever was in that LeafElement to the right of the - // inserted newline. - // 3. Find the paragraph at offset and remove all its children that - // occur _after_ offset. These will be moved to the newly created - // paragraph. - // 4. Move the LeafElement created in #2 and all the LeafElements removed - // in #3 to the newly created paragraph. - // 5. Add the new paragraph to the parent. - int previousIndex = previous.getElementIndex(offset); - int numReplaced = previous.getElementCount() - previousIndex; - Element previousLeaf = previous.getElement(previousIndex); - AttributeSet prevLeafAtts = previous.getAttributes(); - - // This recreates the child at offset to have the proper end offset. - // (Step 1). - Element newPreviousLeaf = - createLeafElement(previous, - prevLeafAtts, previousLeaf.getStartOffset(), - offset); - // This creates the new child, which is the remainder of the old child. - // (Step 2). - - Element firstLeafInNewBranch = - createLeafElement(newBranch, prevLeafAtts, - offset, previousLeaf.getEndOffset()); - - // Now we move the new LeafElement and all the old children that occurred - // after the offset to the new paragraph. (Step 4). - Element[] newLeaves = new Element[numReplaced]; - newLeaves[0] = firstLeafInNewBranch; - for (int i = 1; i < numReplaced; i++) - newLeaves[i] = previous.getElement(previousIndex + i); - newBranch.replace(0, 0, newLeaves); - addEdit(newBranch, 0, null, newLeaves); - - // Now we remove the children after the offset from the previous - // paragraph. (Step 3). - int removeSize = previous.getElementCount() - previousIndex; - Element[] add = new Element[] { newPreviousLeaf }; - Element[] remove = new Element[removeSize]; - for (int j = 0; j < removeSize; j++) - remove[j] = previous.getElement(previousIndex + j); - previous.replace(previousIndex, removeSize, add); - addEdit(previous, previousIndex, remove, add); - - // Finally we add the new paragraph to the parent. (Step 5). - Element[] nb = new Element[] { newBranch }; - int index = parentIndex + 1; - parent.replace(index, 0, nb); - addEdit(parent, index, null, nb); + ElementSpec first = data[0]; + BranchElement paragraph = (BranchElement) elementStack.peek(); + int index = paragraph.getElementIndex(pos); + Element current = paragraph.getElement(index); + int newEndOffset = pos + first.length; + boolean onlyContent = data.length == 1; + Edit edit = getEditForParagraphAndIndex(paragraph, index); + switch (first.getDirection()) + { + case ElementSpec.JoinPreviousDirection: + if (current.getEndOffset() != newEndOffset && !onlyContent) + { + Element newEl1 = createLeafElement(paragraph, + current.getAttributes(), + current.getStartOffset(), + newEndOffset); + edit.addAddedElement(newEl1); + edit.addRemovedElement(current); + offset = newEndOffset; + } + break; + case ElementSpec.JoinNextDirection: + if (pos != 0) + { + Element newEl1 = createLeafElement(paragraph, + current.getAttributes(), + current.getStartOffset(), + pos); + edit.addAddedElement(newEl1); + Element next = paragraph.getElement(index + 1); + + if (onlyContent) + newEl1 = createLeafElement(paragraph, next.getAttributes(), + pos, next.getEndOffset()); + else + { + newEl1 = createLeafElement(paragraph, next.getAttributes(), + pos, newEndOffset); + pos = newEndOffset; + } + edit.addAddedElement(newEl1); + edit.addRemovedElement(current); + edit.addRemovedElement(next); + } + break; + default: + if (current.getStartOffset() != pos) + { + Element newEl = createLeafElement(paragraph, + current.getAttributes(), + current.getStartOffset(), + pos); + edit.addAddedElement(newEl); + } + edit.addRemovedElement(current); + Element newEl1 = createLeafElement(paragraph, first.getAttributes(), + pos, newEndOffset); + edit.addAddedElement(newEl1); + if (current.getEndOffset() != endOffset) + recreateLeaves(newEndOffset, paragraph, onlyContent); + else + offset = newEndOffset; + break; + } } - + /** * Inserts a content element into the document structure. * - * @param tag the element spec + * @param tag - + * the element spec */ private void insertContentTag(ElementSpec tag) { - prepareContentInsertion(); + BranchElement paragraph = (BranchElement) elementStack.peek(); int len = tag.getLength(); int dir = tag.getDirection(); AttributeSet tagAtts = tag.getAttributes(); - if (dir == ElementSpec.JoinPreviousDirection) - { - // The mauve tests to this class show that a JoinPrevious insertion - // does not add any edits to the document event. To me this means - // that nothing is done here. The previous element naturally should - // expand so that it covers the new characters. - } - else if (dir == ElementSpec.JoinNextDirection) + + if (dir == ElementSpec.JoinNextDirection) { - // FIXME: - // Have to handle JoinNext differently depending on whether - // or not it comes after a fracture. If comes after a fracture, - // the insertFracture method takes care of everything and nothing - // needs to be done here. Otherwise, we need to adjust the - // Element structure. For now, I check if the elementStack's - // top Element is the immediate parent of the LeafElement at - // offset - if so, we did not come immediately after a - // fracture. This seems awkward and should probably be improved. - // We may be doing too much in insertFracture because we are - // adjusting the offsets, the correct thing to do may be to - // create a new branch element and push it on to element stack - // and then this method here can be more general. - - BranchElement paragraph = (BranchElement) elementStack.peek(); - int index = paragraph.getElementIndex(offset); + int index = paragraph.getElementIndex(pos); Element target = paragraph.getElement(index); - if (target.isLeaf() && paragraph.getElementCount() > (index + 1)) + Edit edit = getEditForParagraphAndIndex(paragraph, index); + + if (paragraph.getStartOffset() > pos) + { + Element first = paragraph.getElement(0); + Element newEl = createLeafElement(paragraph, + first.getAttributes(), pos, + first.getEndOffset()); + edit.addAddedElement(newEl); + edit.addRemovedElement(first); + } + else if (paragraph.getElementCount() > (index + 1) + && (pos == target.getStartOffset() && !target.equals(lastFractured))) { Element next = paragraph.getElement(index + 1); - Element newEl1 = createLeafElement(paragraph, - target.getAttributes(), - target.getStartOffset(), - offset); - Element newEl2 = createLeafElement(paragraph, - next.getAttributes(), offset, - next.getEndOffset()); - Element[] add = new Element[] { newEl1, newEl2 }; - paragraph.replace (index, 2, add); - addEdit(paragraph, index, new Element[] { target, next }, add); + Element newEl = createLeafElement(paragraph, + next.getAttributes(), pos, + next.getEndOffset()); + edit.addAddedElement(newEl); + edit.addRemovedElement(next); + edit.addRemovedElement(target); + } + else + { + BranchElement parent = (BranchElement) paragraph.getParentElement(); + int i = parent.getElementIndex(pos); + BranchElement next = (BranchElement) parent.getElement(i + 1); + AttributeSet atts = tag.getAttributes(); + + if (next != null) + { + Element nextLeaf = next.getElement(0); + Edit e = getEditForParagraphAndIndex(next, 0); + Element newEl2 = createLeafElement(next, atts, pos, nextLeaf.getEndOffset()); + e.addAddedElement(newEl2); + e.addRemovedElement(nextLeaf); + } } } - else if (dir == ElementSpec.OriginateDirection) + else { - BranchElement paragraph = (BranchElement) elementStack.peek(); - int index = paragraph.getElementIndex(offset); - Element current = paragraph.getElement(index); + int end = pos + len; + Element leaf = createLeafElement(paragraph, tag.getAttributes(), pos, end); - Element[] added; - Element[] removed = new Element[] {current}; - Element[] splitRes = split(current, offset, length); - if (splitRes[0] == null) + // Check for overlap with other leaves/branches + if (paragraph.getElementCount() > 0) { - added = new Element[2]; - added[0] = createLeafElement(paragraph, tagAtts, - offset, endOffset); - added[1] = splitRes[1]; - removed = new Element[0]; - index++; - } - else if (current.getStartOffset() == offset) - { - // This is if the new insertion happens immediately before - // the <code>current</code> Element. In this case there are 2 - // resulting Elements. - added = new Element[2]; - added[0] = createLeafElement(paragraph, tagAtts, offset, - endOffset); - added[1] = splitRes[1]; - } - else if (current.getEndOffset() == endOffset) - { - // This is if the new insertion happens right at the end of - // the <code>current</code> Element. In this case there are - // 2 resulting Elements. - added = new Element[2]; - added[0] = splitRes[0]; - added[1] = createLeafElement(paragraph, tagAtts, offset, - endOffset); + int index = paragraph.getElementIndex(pos); + Element target = paragraph.getElement(index); + boolean onlyContent = target.isLeaf(); + + BranchElement toRec = paragraph; + if (!onlyContent) + toRec = (BranchElement) target; + + // Check if we should place the leaf before or after target + if (pos > target.getStartOffset()) + index++; + + Edit edit = getEditForParagraphAndIndex(paragraph, index); + edit.addAddedElement(leaf); + + if (end != toRec.getEndOffset()) + { + recreateLeaves(end, toRec, onlyContent); + + if (onlyContent) + edit.addRemovedElement(target); + } } else - { - // This is if the new insertion is in the middle of the - // <code>current</code> Element. In this case - // there will be 3 resulting Elements. - added = new Element[3]; - added[0] = splitRes[0]; - added[1] = createLeafElement(paragraph, tagAtts, offset, - endOffset); - added[2] = splitRes[1]; - } - paragraph.replace(index, removed.length, added); - addEdit(paragraph, index, removed, added); + paragraph.replace(0, 0, new Element[] { leaf }); } - offset += len; + + pos += len; } - + /** - * Creates a copy of the element <code>clonee</code> that has the parent - * <code>parent</code>. - * @param parent the parent of the newly created Element - * @param clonee the Element to clone - * @return the cloned Element + * This method fractures the child at offset. + * + * @param data + * the ElementSpecs used for the entire insertion */ - public Element clone (Element parent, Element clonee) + private void createFracture(ElementSpec[] data) { - // If the Element we want to clone is a leaf, then simply copy it - if (clonee.isLeaf()) - return createLeafElement(parent, clonee.getAttributes(), - clonee.getStartOffset(), clonee.getEndOffset()); + BranchElement paragraph = (BranchElement) elementStack.peek(); + int index = paragraph.getElementIndex(offset); + Element child = paragraph.getElement(index); + Edit edit = getEditForParagraphAndIndex(paragraph, index); + AttributeSet atts = child.getAttributes(); - // Otherwise create a new BranchElement with the desired parent and - // the clonee's attributes - BranchElement result = (BranchElement) createBranchElement(parent, clonee.getAttributes()); - - // And clone all the of clonee's children - Element[] children = new Element[clonee.getElementCount()]; - for (int i = 0; i < children.length; i++) - children[i] = clone(result, clonee.getElement(i)); - - // Make the cloned children the children of the BranchElement - result.replace(0, 0, children); - return result; + if (offset != 0) + { + Element newEl1 = createLeafElement(paragraph, atts, + child.getStartOffset(), offset); + edit.addAddedElement(newEl1); + edit.addRemovedElement(child); + } } /** - * Adds an ElementChange for a given element modification to the document - * event. If there already is an ElementChange registered for this element, - * this method tries to merge the ElementChanges together. However, this - * is only possible if the indices of the new and old ElementChange are - * equal. - * - * @param e the element - * @param i the index of the change - * @param removed the removed elements, or <code>null</code> - * @param added the added elements, or <code>null</code> + * Recreates a specified part of a the tree after a new leaf + * has been inserted. + * + * @param start - where to start recreating from + * @param paragraph - the paragraph to recreate + * @param onlyContent - true if this is the only content + */ + private void recreateLeaves(int start, BranchElement paragraph, boolean onlyContent) + { + int index = paragraph.getElementIndex(start); + Element child = paragraph.getElement(index); + AttributeSet atts = child.getAttributes(); + + if (!onlyContent) + { + BranchElement newBranch = (BranchElement) createBranchElement(paragraph, + atts); + Element newLeaf = createLeafElement(newBranch, atts, start, + child.getEndOffset()); + newBranch.replace(0, 0, new Element[] { newLeaf }); + + BranchElement parent = (BranchElement) paragraph.getParentElement(); + int parSize = parent.getElementCount(); + Edit edit = getEditForParagraphAndIndex(parent, parSize); + edit.addAddedElement(newBranch); + + int paragraphSize = paragraph.getElementCount(); + Element[] removed = new Element[paragraphSize - (index + 1)]; + int s = 0; + for (int j = index + 1; j < paragraphSize; j++) + removed[s++] = paragraph.getElement(j); + + edit = getEditForParagraphAndIndex(paragraph, index); + edit.addRemovedElements(removed); + Element[] added = recreateAfterFracture(removed, newBranch, 0, child.getEndOffset()); + newBranch.replace(1, 0, added); + + lastFractured = newLeaf; + pos = newBranch.getEndOffset(); + } + else + { + Element newLeaf = createLeafElement(paragraph, atts, start, + child.getEndOffset()); + Edit edit = getEditForParagraphAndIndex(paragraph, index); + edit.addAddedElement(newLeaf); + } + } + + /** + * Splits an element if <code>offset</code> is not already at its + * boundary. + * + * @param el + * the Element to possibly split + * @param offset + * the offset at which to possibly split + * @param space + * the amount of space to create between the splitted parts + * @param editIndex + * the index of the edit to use + * @return An array of elements which represent the split result. This array + * has two elements, the two parts of the split. The first element + * might be null, which means that the element which should be + * splitted can remain in place. The second element might also be + * null, which means that the offset is already at an element + * boundary and the element doesn't need to be splitted. */ - private void addEdit(Element e, int i, Element[] removed, Element[] added) + private Element[] split(Element el, int offset, int space, int editIndex) { - // Perform sanity check first. - DocumentEvent.ElementChange ec = documentEvent.getChange(e); + // If we are at an element boundary, then return an empty array. + if ((offset == el.getStartOffset() || offset == el.getEndOffset()) + && space == 0 && el.isLeaf()) + return new Element[2]; - // Merge the existing stuff with the new stuff. - Element[] oldAdded = ec == null ? null: ec.getChildrenAdded(); - Element[] newAdded; - if (oldAdded != null && added != null) + // If the element is an instance of BranchElement, then we + // recursivly + // call this method to perform the split. + Element[] res = new Element[2]; + if (el instanceof BranchElement) { - if (ec.getIndex() <= i) + int index = el.getElementIndex(offset); + Element child = el.getElement(index); + Element[] result = split(child, offset, space, editIndex); + Element[] removed; + Element[] added; + Element[] newAdded; + + int count = el.getElementCount(); + if (result[1] != null) { - int index = i - ec.getIndex(); - // Merge adds together. - newAdded = new Element[oldAdded.length + added.length]; - System.arraycopy(oldAdded, 0, newAdded, 0, index); - System.arraycopy(added, 0, newAdded, index, added.length); - System.arraycopy(oldAdded, index, newAdded, index + added.length, - oldAdded.length - index); - i = ec.getIndex(); + // This is the case when we can keep the first element. + if (result[0] == null) + { + removed = new Element[count - index - 1]; + newAdded = new Element[count - index - 1]; + added = new Element[] {}; + + } + // This is the case when we may not keep the first + // element. + else + { + removed = new Element[count - index]; + newAdded = new Element[count - index]; + added = new Element[] { result[0] }; + } + newAdded[0] = result[1]; + for (int i = index; i < count; i++) + { + Element el2 = el.getElement(i); + int ind = i - count + removed.length; + removed[ind] = el2; + if (ind != 0) + newAdded[ind] = el2; + } + + Edit edit = getEditForParagraphAndIndex((BranchElement) el, editIndex); + edit.addRemovedElements(removed); + edit.addAddedElements(added); + + BranchElement newPar = + (BranchElement) createBranchElement(el.getParentElement(), + el.getAttributes()); + newPar.replace(0, 0, newAdded); + res = new Element[] { null, newPar }; } else - throw new AssertionError("Not yet implemented case."); + { + removed = new Element[count - index]; + for (int i = index; i < count; ++i) + removed[i - index] = el.getElement(i); + + Edit edit = getEditForParagraphAndIndex((BranchElement) el, editIndex); + edit.addRemovedElements(removed); + + BranchElement newPar = (BranchElement) createBranchElement(el.getParentElement(), + el.getAttributes()); + newPar.replace(0, 0, removed); + res = new Element[] { null, newPar }; + } } - else if (added != null) - newAdded = added; - else if (oldAdded != null) - newAdded = oldAdded; - else - newAdded = new Element[0]; + else if (el instanceof LeafElement) + { + BranchElement par = (BranchElement) el.getParentElement(); + Element el1 = createLeafElement(par, el.getAttributes(), + el.getStartOffset(), offset); + + Element el2 = createLeafElement(par, el.getAttributes(), + offset + space, + el.getEndOffset()); + res = new Element[] { el1, el2 }; + } + return res; + } - Element[] oldRemoved = ec == null ? null: ec.getChildrenRemoved(); - Element[] newRemoved; - if (oldRemoved != null && removed != null) + /** + * Inserts a fracture into the document structure. + * + * @param tag - + * the element spec. + */ + private void insertFracture(ElementSpec tag) + { + // insert the fracture at offset. + BranchElement parent = (BranchElement) elementStack.peek(); + int parentIndex = parent.getElementIndex(pos); + AttributeSet parentAtts = parent.getAttributes(); + Element toFracture = parent.getElement(parentIndex); + int parSize = parent.getElementCount(); + Edit edit = getEditForParagraphAndIndex(parent, parentIndex); + Element frac = toFracture; + int leftIns = 0; + int indexOfFrac = toFracture.getElementIndex(pos); + int size = toFracture.getElementCount(); + + // gets the leaf that falls along the fracture + frac = toFracture.getElement(indexOfFrac); + while (!frac.isLeaf()) + frac = frac.getElement(frac.getElementIndex(pos)); + + AttributeSet atts = frac.getAttributes(); + int fracStart = frac.getStartOffset(); + int fracEnd = frac.getEndOffset(); + if (pos >= fracStart && pos < fracEnd) { - if (ec.getIndex() <= i) + // recreate left-side of branch and all its children before offset + // add the fractured leaves to the right branch + BranchElement rightBranch = + (BranchElement) createBranchElement(parent, parentAtts); + + // Check if left branch has already been edited. If so, we only + // need to create the right branch. + BranchElement leftBranch = null; + Element[] added = null; + if (edit.added.size() > 0 || edit.removed.size() > 0) { - int index = i - ec.getIndex(); - // Merge removes together. - newRemoved = new Element[oldRemoved.length + removed.length]; - System.arraycopy(oldAdded, 0, newRemoved, 0, index); - System.arraycopy(removed, 0, newRemoved, index, removed.length); - System.arraycopy(oldRemoved, index, newRemoved, - index + removed.length, - oldRemoved.length - index); - i = ec.getIndex(); + added = new Element[] { rightBranch }; + + // don't try to remove left part of tree + parentIndex++; } else - throw new AssertionError("Not yet implemented case."); + { + leftBranch = + (BranchElement) createBranchElement(parent, parentAtts); + added = new Element[] { leftBranch, rightBranch }; + + // add fracture to leftBranch + if (fracStart != pos) + { + Element leftFracturedLeaf = + createLeafElement(leftBranch, atts, fracStart, pos); + leftBranch.replace(leftIns, 0, + new Element[] { leftFracturedLeaf }); + } + } + + if (!toFracture.isLeaf()) + { + // add all non-fracture elements to the branches + if (indexOfFrac > 0 && leftBranch != null) + { + Element[] add = new Element[indexOfFrac]; + for (int i = 0; i < indexOfFrac; i++) + add[i] = toFracture.getElement(i); + leftIns = add.length; + leftBranch.replace(0, 0, add); + } + + int count = size - indexOfFrac - 1; + if (count > 0) + { + Element[] add = new Element[count]; + int j = 0; + int i = indexOfFrac + 1; + while (j < count) + add[j++] = toFracture.getElement(i++); + rightBranch.replace(0, 0, add); + } + } + + // add to fracture to rightBranch + // Check if we can join the right frac leaf with the next leaf + int rm = 0; + int end = fracEnd; + Element next = rightBranch.getElement(0); + if (next != null && next.isLeaf() + && next.getAttributes().isEqual(atts)) + { + end = next.getEndOffset(); + rm = 1; + } + + Element rightFracturedLeaf = createLeafElement(rightBranch, atts, + pos, end); + rightBranch.replace(0, rm, new Element[] { rightFracturedLeaf }); + + // recreate those elements after parentIndex and add/remove all + // new/old elements to parent + int remove = parSize - parentIndex; + Element[] removed = new Element[0]; + Element[] added2 = new Element[0]; + if (remove > 0) + { + removed = new Element[remove]; + int s = 0; + for (int j = parentIndex; j < parSize; j++) + removed[s++] = parent.getElement(j); + edit.addRemovedElements(removed); + added2 = recreateAfterFracture(removed, parent, 1, + rightBranch.getEndOffset()); + } + + edit.addAddedElements(added); + edit.addAddedElements(added2); + elementStack.push(rightBranch); + lastFractured = rightFracturedLeaf; } - else if (removed != null) - newRemoved = removed; - else if (oldRemoved != null) - newRemoved = oldRemoved; else - newRemoved = new Element[0]; + fracNotCreated = true; + } + + /** + * Recreates all the elements from the parent to the element on the top of + * the stack, starting from startFrom with the starting offset of + * startOffset. + * + * @param recreate - + * the elements to recreate + * @param parent - + * the element to add the new elements to + * @param startFrom - + * where to start recreating from + * @param startOffset - + * the offset of the first element + * @return the array of added elements + */ + private Element[] recreateAfterFracture(Element[] recreate, + BranchElement parent, int startFrom, + int startOffset) + { + Element[] added = new Element[recreate.length - startFrom]; + int j = 0; + for (int i = startFrom; i < recreate.length; i++) + { + Element curr = recreate[i]; + int len = curr.getEndOffset() - curr.getStartOffset(); + if (curr instanceof LeafElement) + added[j] = createLeafElement(parent, curr.getAttributes(), + startOffset, startOffset + len); + else + { + BranchElement br = + (BranchElement) createBranchElement(parent, + curr.getAttributes()); + int bSize = curr.getElementCount(); + for (int k = 0; k < bSize; k++) + { + Element bCurr = curr.getElement(k); + Element[] add = recreateAfterFracture(new Element[] { bCurr }, br, 0, + startOffset); + br.replace(0, 0, add); + + } + added[j] = br; + } + startOffset += len; + j++; + } + + return added; + } + } + + /** + * This method looks through the Vector of Edits to see if there is already an + * Edit object associated with the given paragraph. If there is, then we + * return it. Otherwise we create a new Edit object, add it to the vector, and + * return it. Note: this method is package private to avoid accessors. + * + * @param index + * the index associated with the Edit we want to create + * @param para + * the paragraph associated with the Edit we want + * @return the found or created Edit object + */ + Edit getEditForParagraphAndIndex(BranchElement para, int index) + { + Edit curr; + int size = edits.size(); + for (int i = 0; i < size; i++) + { + curr = (Edit) edits.elementAt(i); + if (curr.e.equals(para)) + return curr; + } + curr = new Edit(para, index, null, null); + edits.add(curr); + + return curr; + } + /** + * Instance of all editing information for an object in the Vector. This class + * is used to add information to the DocumentEvent associated with an + * insertion/removal/change as well as to store the changes that need to be + * made so they can be made all at the same (appropriate) time. + */ + class Edit + { + /** The element to edit . */ + Element e; + + /** The index of the change. */ + int index; + + /** The removed elements. */ + Vector removed = new Vector(); + + /** The added elements. */ + Vector added = new Vector(); + + /** + * Return an array containing the Elements that have been removed from the + * paragraph associated with this Edit. + * + * @return an array of removed Elements + */ + public Element[] getRemovedElements() + { + int size = removed.size(); + Element[] removedElements = new Element[size]; + for (int i = 0; i < size; i++) + removedElements[i] = (Element) removed.elementAt(i); + return removedElements; + } + + /** + * Return an array containing the Elements that have been added to the + * paragraph associated with this Edit. + * + * @return an array of added Elements + */ + public Element[] getAddedElements() + { + int size = added.size(); + Element[] addedElements = new Element[size]; + for (int i = 0; i < size; i++) + addedElements[i] = (Element) added.elementAt(i); + return addedElements; + } + + /** + * Checks if e is already in the vector. + * + * @param e - the Element to look for + * @param v - the vector to search + * @return true if e is in v. + */ + private boolean contains(Vector v, Element e) + { + if (e == null) + return false; + + int i = v.size(); + for (int j = 0; j < i; j++) + { + Element e1 = (Element) v.get(j); + if ((e1 != null) && (e1.getAttributes().isEqual(e.getAttributes())) + && (e1.getName().equals(e.getName())) + && (e1.getStartOffset() == e.getStartOffset()) + && (e1.getEndOffset() == e.getEndOffset()) + && (e1.getParentElement().equals(e.getParentElement())) + && (e1.getElementCount() == e.getElementCount())) + return true; + } + return false; + } + + /** + * Adds one Element to the vector of removed Elements. + * + * @param e + * the Element to add + */ + public void addRemovedElement(Element e) + { + if (!contains(removed, e)) + removed.add(e); + } + + /** + * Adds each Element in the given array to the vector of removed Elements + * + * @param e + * the array containing the Elements to be added + */ + public void addRemovedElements(Element[] e) + { + if (e == null || e.length == 0) + return; + for (int i = 0; i < e.length; i++) + { + if (!contains(removed, e[i])) + removed.add(e[i]); + } + } - // Replace the existing edit for the element with the merged. - documentEvent.addEdit(new ElementEdit(e, i, newRemoved, newAdded)); + /** + * Adds one Element to the vector of added Elements. + * + * @param e + * the Element to add + */ + public void addAddedElement(Element e) + { + if (!contains(added, e)) + added.add(e); + } + + /** + * Adds each Element in the given array to the vector of added Elements. + * + * @param e + * the array containing the Elements to be added + */ + public void addAddedElements(Element[] e) + { + if (e == null || e.length == 0) + return; + for (int i = 0; i < e.length; i++) + { + if (!contains(added, e[i])) + added.add(e[i]); + } + } + + /** + * Creates a new Edit object with the given parameters + * + * @param e + * the paragraph Element associated with this Edit + * @param i + * the index within the paragraph where changes are started + * @param removed + * an array containing Elements that should be removed from the + * paragraph Element + * @param added + * an array containing Elements that should be added to the + * paragraph Element + */ + public Edit(Element e, int i, Element[] removed, Element[] added) + { + this.e = e; + this.index = i; + addRemovedElements(removed); + addAddedElements(added); } } /** - * An element type for sections. This is a simple BranchElement with - * a unique name. + * An element type for sections. This is a simple BranchElement with a unique + * name. */ protected class SectionElement extends BranchElement { @@ -1244,7 +1630,7 @@ public class DefaultStyledDocument extends AbstractDocument /** * Returns the name of the element. This method always returns * "section". - * + * * @return the name of the element */ public String getName() @@ -1256,18 +1642,18 @@ public class DefaultStyledDocument extends AbstractDocument /** * Receives notification when any of the document's style changes and calls * {@link DefaultStyledDocument#styleChanged(Style)}. - * + * * @author Roman Kennke (kennke@aicas.com) */ - private class StyleChangeListener - implements ChangeListener + private class StyleChangeListener implements ChangeListener { /** * Receives notification when any of the document's style changes and calls * {@link DefaultStyledDocument#styleChanged(Style)}. - * - * @param event the change event + * + * @param event + * the change event */ public void stateChanged(ChangeEvent event) { @@ -1296,6 +1682,11 @@ public class DefaultStyledDocument extends AbstractDocument private StyleChangeListener styleChangeListener; /** + * Vector that contains all the edits. Maybe replace by a HashMap. + */ + Vector edits = new Vector(); + + /** * Creates a new <code>DefaultStyledDocument</code>. */ public DefaultStyledDocument() @@ -1304,10 +1695,11 @@ public class DefaultStyledDocument extends AbstractDocument } /** - * Creates a new <code>DefaultStyledDocument</code> that uses the - * specified {@link StyleContext}. - * - * @param context the <code>StyleContext</code> to use + * Creates a new <code>DefaultStyledDocument</code> that uses the specified + * {@link StyleContext}. + * + * @param context + * the <code>StyleContext</code> to use */ public DefaultStyledDocument(StyleContext context) { @@ -1315,14 +1707,16 @@ public class DefaultStyledDocument extends AbstractDocument } /** - * Creates a new <code>DefaultStyledDocument</code> that uses the - * specified {@link StyleContext} and {@link Content} buffer. - * - * @param content the <code>Content</code> buffer to use - * @param context the <code>StyleContext</code> to use + * Creates a new <code>DefaultStyledDocument</code> that uses the specified + * {@link StyleContext} and {@link Content} buffer. + * + * @param content + * the <code>Content</code> buffer to use + * @param context + * the <code>StyleContext</code> to use */ public DefaultStyledDocument(AbstractDocument.Content content, - StyleContext context) + StyleContext context) { super(content, context); buffer = new ElementBuffer(createDefaultRoot()); @@ -1330,10 +1724,9 @@ public class DefaultStyledDocument extends AbstractDocument } /** - * Adds a style into the style hierarchy. Unspecified style attributes - * can be resolved in the <code>parent</code> style, if one is specified. - * - * While it is legal to add nameless styles (<code>nm == null</code), + * Adds a style into the style hierarchy. Unspecified style attributes can be + * resolved in the <code>parent</code> style, if one is specified. While it + * is legal to add nameless styles (<code>nm == null</code), * you must be aware that the client application is then responsible * for managing the style hierarchy, since unnamed styles cannot be * looked up by their name. @@ -1360,14 +1753,12 @@ public class DefaultStyledDocument extends AbstractDocument /** * Create the default root element for this kind of <code>Document</code>. - * + * * @return the default root element for this kind of <code>Document</code> */ protected AbstractDocument.AbstractElement createDefaultRoot() { Element[] tmp; - // FIXME: Create a SecionElement here instead of a BranchElement. - // Use createBranchElement() and createLeafElement instead. SectionElement section = new SectionElement(); BranchElement paragraph = new BranchElement(section, null); @@ -1375,7 +1766,7 @@ public class DefaultStyledDocument extends AbstractDocument tmp[0] = paragraph; section.replace(0, 0, tmp); - LeafElement leaf = new LeafElement(paragraph, null, 0, 1); + Element leaf = new LeafElement(paragraph, null, 0, 1); tmp = new Element[1]; tmp[0] = leaf; paragraph.replace(0, 0, tmp); @@ -1384,14 +1775,14 @@ public class DefaultStyledDocument extends AbstractDocument } /** - * Returns the <code>Element</code> that corresponds to the character - * at the specified position. - * - * @param position the position of which we query the corresponding - * <code>Element</code> - * - * @return the <code>Element</code> that corresponds to the character - * at the specified position + * Returns the <code>Element</code> that corresponds to the character at the + * specified position. + * + * @param position + * the position of which we query the corresponding + * <code>Element</code> + * @return the <code>Element</code> that corresponds to the character at the + * specified position */ public Element getCharacterElement(int position) { @@ -1402,15 +1793,15 @@ public class DefaultStyledDocument extends AbstractDocument int index = element.getElementIndex(position); element = element.getElement(index); } - + return element; } /** * Extracts a background color from a set of attributes. - * - * @param attributes the attributes from which to get a background color - * + * + * @param attributes + * the attributes from which to get a background color * @return the background color that correspond to the attributes */ public Color getBackground(AttributeSet attributes) @@ -1421,7 +1812,7 @@ public class DefaultStyledDocument extends AbstractDocument /** * Returns the default root element. - * + * * @return the default root element */ public Element getDefaultRootElement() @@ -1431,9 +1822,9 @@ public class DefaultStyledDocument extends AbstractDocument /** * Extracts a font from a set of attributes. - * - * @param attributes the attributes from which to get a font - * + * + * @param attributes + * the attributes from which to get a font * @return the font that correspond to the attributes */ public Font getFont(AttributeSet attributes) @@ -1441,12 +1832,12 @@ public class DefaultStyledDocument extends AbstractDocument StyleContext context = (StyleContext) getAttributeContext(); return context.getFont(attributes); } - + /** * Extracts a foreground color from a set of attributes. - * - * @param attributes the attributes from which to get a foreground color - * + * + * @param attributes + * the attributes from which to get a foreground color * @return the foreground color that correspond to the attributes */ public Color getForeground(AttributeSet attributes) @@ -1457,9 +1848,9 @@ public class DefaultStyledDocument extends AbstractDocument /** * Returns the logical <code>Style</code> for the specified position. - * - * @param position the position from which to query to logical style - * + * + * @param position + * the position from which to query to logical style * @return the logical <code>Style</code> for the specified position */ public Style getLogicalStyle(int position) @@ -1474,37 +1865,32 @@ public class DefaultStyledDocument extends AbstractDocument } /** - * Returns the paragraph element for the specified position. - * If the position is outside the bounds of the document's root element, - * then the closest element is returned. That is the last paragraph if + * Returns the paragraph element for the specified position. If the position + * is outside the bounds of the document's root element, then the closest + * element is returned. That is the last paragraph if * <code>position >= endIndex</code> or the first paragraph if * <code>position < startIndex</code>. - * - * @param position the position for which to query the paragraph element - * + * + * @param position + * the position for which to query the paragraph element * @return the paragraph element for the specified position */ public Element getParagraphElement(int position) { - BranchElement root = (BranchElement) getDefaultRootElement(); - int start = root.getStartOffset(); - int end = root.getEndOffset(); - if (position >= end) - position = end - 1; - else if (position < start) - position = start; - - Element par = root.positionToElement(position); - - assert par != null : "The paragraph element must not be null"; - return par; + Element e = getDefaultRootElement(); + while (!e.isLeaf()) + e = e.getElement(e.getElementIndex(position)); + + if (e != null) + return e.getParentElement(); + return e; } /** * Looks up and returns a named <code>Style</code>. - * - * @param nm the name of the <code>Style</code> - * + * + * @param nm + * the name of the <code>Style</code> * @return the found <code>Style</code> of <code>null</code> if no such * <code>Style</code> exists */ @@ -1516,8 +1902,9 @@ public class DefaultStyledDocument extends AbstractDocument /** * Removes a named <code>Style</code> from the style hierarchy. - * - * @param nm the name of the <code>Style</code> to be removed + * + * @param nm + * the name of the <code>Style</code> to be removed */ public void removeStyle(String nm) { @@ -1528,31 +1915,32 @@ public class DefaultStyledDocument extends AbstractDocument /** * Sets text attributes for the fragment specified by <code>offset</code> * and <code>length</code>. - * - * @param offset the start offset of the fragment - * @param length the length of the fragment - * @param attributes the text attributes to set - * @param replace if <code>true</code>, the attributes of the current - * selection are overridden, otherwise they are merged + * + * @param offset + * the start offset of the fragment + * @param length + * the length of the fragment + * @param attributes + * the text attributes to set + * @param replace + * if <code>true</code>, the attributes of the current selection + * are overridden, otherwise they are merged */ public void setCharacterAttributes(int offset, int length, - AttributeSet attributes, - boolean replace) + AttributeSet attributes, boolean replace) { // Exit early if length is 0, so no DocumentEvent is created or fired. if (length == 0) return; try { - // Must obtain a write lock for this method. writeLock() and + // Must obtain a write lock for this method. writeLock() and // writeUnlock() should always be in try/finally block to make // sure that locking happens in a balanced manner. writeLock(); - DefaultDocumentEvent ev = - new DefaultDocumentEvent( - offset, - length, - DocumentEvent.EventType.CHANGE); + DefaultDocumentEvent ev = new DefaultDocumentEvent(offset, + length, + DocumentEvent.EventType.CHANGE); // Modify the element structure so that the interval begins at an // element @@ -1563,13 +1951,13 @@ public class DefaultStyledDocument extends AbstractDocument // Visit all paragraph elements within the specified interval int end = offset + length; Element curr; - for (int pos = offset; pos < end; ) + for (int pos = offset; pos < end;) { // Get the CharacterElement at offset pos. curr = getCharacterElement(pos); if (pos == curr.getEndOffset()) break; - + MutableAttributeSet a = (MutableAttributeSet) curr.getAttributes(); ev.addEdit(new AttributeUndoableEdit(curr, attributes, replace)); // If replace is true, remove all the old attributes. @@ -1588,12 +1976,14 @@ public class DefaultStyledDocument extends AbstractDocument writeUnlock(); } } - + /** * Sets the logical style for the paragraph at the specified position. - * - * @param position the position at which the logical style is added - * @param style the style to set for the current paragraph + * + * @param position + * the position at which the logical style is added + * @param style + * the style to set for the current paragraph */ public void setLogicalStyle(int position, Style style) { @@ -1603,60 +1993,59 @@ public class DefaultStyledDocument extends AbstractDocument if (el == null) return; try - { - writeLock(); - if (el instanceof AbstractElement) - { - AbstractElement ael = (AbstractElement) el; - ael.setResolveParent(style); - int start = el.getStartOffset(); - int end = el.getEndOffset(); - DefaultDocumentEvent ev = - new DefaultDocumentEvent ( - start, - end - start, - DocumentEvent.EventType.CHANGE); - // FIXME: Add an UndoableEdit to this event and fire it. - fireChangedUpdate(ev); - } - else - throw new - AssertionError("paragraph elements are expected to be" - + "instances of AbstractDocument.AbstractElement"); - } + { + writeLock(); + if (el instanceof AbstractElement) + { + AbstractElement ael = (AbstractElement) el; + ael.setResolveParent(style); + int start = el.getStartOffset(); + int end = el.getEndOffset(); + DefaultDocumentEvent ev = new DefaultDocumentEvent(start, + end - start, + DocumentEvent.EventType.CHANGE); + fireChangedUpdate(ev); + fireUndoableEditUpdate(new UndoableEditEvent(this, ev)); + } + else + throw new AssertionError( + "paragraph elements are expected to be" + + "instances of AbstractDocument.AbstractElement"); + } finally - { - writeUnlock(); - } + { + writeUnlock(); + } } /** * Sets text attributes for the paragraph at the specified fragment. - * - * @param offset the beginning of the fragment - * @param length the length of the fragment - * @param attributes the text attributes to set - * @param replace if <code>true</code>, the attributes of the current - * selection are overridden, otherwise they are merged + * + * @param offset + * the beginning of the fragment + * @param length + * the length of the fragment + * @param attributes + * the text attributes to set + * @param replace + * if <code>true</code>, the attributes of the current selection + * are overridden, otherwise they are merged */ public void setParagraphAttributes(int offset, int length, - AttributeSet attributes, - boolean replace) + AttributeSet attributes, boolean replace) { try { - // Must obtain a write lock for this method. writeLock() and + // Must obtain a write lock for this method. writeLock() and // writeUnlock() should always be in try/finally blocks to make // sure that locking occurs in a balanced manner. writeLock(); - + // Create a DocumentEvent to use for changedUpdate(). - DefaultDocumentEvent ev = - new DefaultDocumentEvent ( - offset, - length, - DocumentEvent.EventType.CHANGE); - + DefaultDocumentEvent ev = new DefaultDocumentEvent(offset, + length, + DocumentEvent.EventType.CHANGE); + // Have to iterate through all the _paragraph_ elements that are // contained or partially contained in the interval // (offset, offset + length). @@ -1665,7 +2054,7 @@ public class DefaultStyledDocument extends AbstractDocument int endElement = rootElement.getElementIndex(offset + length - 1); if (endElement < startElement) endElement = startElement; - + for (int i = startElement; i <= endElement; i++) { Element par = rootElement.getElement(i); @@ -1688,11 +2077,13 @@ public class DefaultStyledDocument extends AbstractDocument } /** - * Called in response to content insert actions. This is used to - * update the element structure. - * - * @param ev the <code>DocumentEvent</code> describing the change - * @param attr the attributes for the change + * Called in response to content insert actions. This is used to update the + * element structure. + * + * @param ev + * the <code>DocumentEvent</code> describing the change + * @param attr + * the attributes for the change */ protected void insertUpdate(DefaultDocumentEvent ev, AttributeSet attr) { @@ -1703,8 +2094,7 @@ public class DefaultStyledDocument extends AbstractDocument int offset = ev.getOffset(); int length = ev.getLength(); int endOffset = offset + length; - AttributeSet paragraphAttributes = - getParagraphElement(endOffset).getAttributes(); + AttributeSet paragraphAttributes = getParagraphElement(endOffset).getAttributes(); Segment txt = new Segment(); try { @@ -1723,91 +2113,75 @@ public class DefaultStyledDocument extends AbstractDocument short finalStartDirection = ElementSpec.OriginateDirection; boolean prevCharWasNewline = false; Element prev = getCharacterElement(offset); - Element next = getCharacterElement(endOffset); + Element next = getCharacterElement(endOffset); Element prevParagraph = getParagraphElement(offset); Element paragraph = getParagraphElement(endOffset); - + int segmentEnd = txt.offset + txt.count; - + // Check to see if we're inserting immediately after a newline. if (offset > 0) { try - { - String s = getText(offset - 1, 1); - if (s.equals("\n")) - { - finalStartDirection = - handleInsertAfterNewline(specs, offset, endOffset, - prevParagraph, - paragraph, - paragraphAttributes); - - prevCharWasNewline = true; - // Find the final start tag from the ones just created. - for (int i = 0; i < specs.size(); i++) - if (((ElementSpec) specs.get(i)).getType() - == ElementSpec.StartTagType) - finalStartTag = (ElementSpec)specs.get(i); - } - } + { + String s = getText(offset - 1, 1); + if (s.equals("\n")) + { + finalStartDirection = handleInsertAfterNewline(specs, offset, + endOffset, + prevParagraph, + paragraph, + paragraphAttributes); + + prevCharWasNewline = true; + // Find the final start tag from the ones just created. + for (int i = 0; i < specs.size(); i++) + if (((ElementSpec) specs.get(i)).getType() == ElementSpec.StartTagType) + finalStartTag = (ElementSpec) specs.get(i); + } + } catch (BadLocationException ble) - { - // This shouldn't happen. - AssertionError ae = new AssertionError(); - ae.initCause(ble); - throw ae; - } + { + // This shouldn't happen. + AssertionError ae = new AssertionError(); + ae.initCause(ble); + throw ae; + } } - for (int i = txt.offset; i < segmentEnd; ++i) { len++; if (txt.array[i] == '\n') { // Add the ElementSpec for the content. - specs.add(new ElementSpec(attr, ElementSpec.ContentType, len)); + specs.add(new ElementSpec(attr, ElementSpec.ContentType, len)); // Add ElementSpecs for the newline. specs.add(new ElementSpec(null, ElementSpec.EndTagType)); finalStartTag = new ElementSpec(paragraphAttributes, - ElementSpec.StartTagType); + ElementSpec.StartTagType); specs.add(finalStartTag); len = 0; } } // Create last element if last character hasn't been a newline. - if (len > 0) + if (len > 0) specs.add(new ElementSpec(attr, ElementSpec.ContentType, len)); - // Set the direction of the last spec of type StartTagType. - // If we are inserting after a newline then this value comes from + // Set the direction of the last spec of type StartTagType. + // If we are inserting after a newline then this value comes from // handleInsertAfterNewline. if (finalStartTag != null) - { + { if (prevCharWasNewline) finalStartTag.setDirection(finalStartDirection); else if (prevParagraph.getEndOffset() != endOffset) - { - try - { - String last = getText(endOffset - 1, 1); - if (!last.equals("\n")) - finalStartTag.setDirection(ElementSpec.JoinFractureDirection); - } - catch (BadLocationException ble) - { - // This shouldn't happen. - AssertionError ae = new AssertionError(); - ae.initCause(ble); - throw ae; - } - } + finalStartTag.setDirection(ElementSpec.JoinFractureDirection); else { - // If there is an element AFTER this one, then set the + // If there is an element AFTER this one, then set the // direction to JoinNextDirection. Element parent = prevParagraph.getParentElement(); int index = parent.getElementIndex(offset); @@ -1816,19 +2190,18 @@ public class DefaultStyledDocument extends AbstractDocument finalStartTag.setDirection(ElementSpec.JoinNextDirection); } } - + // If we are at the last index, then check if we could probably be // joined with the next element. // This means: - // - we must be a ContentTag - // - if there is a next Element, we must have the same attributes - // - if there is no next Element, but one will be created, - // we must have the same attributes as the higher-level run. + // - we must be a ContentTag + // - if there is a next Element, we must have the same attributes + // - if there is no next Element, but one will be created, + // we must have the same attributes as the higher-level run. ElementSpec last = (ElementSpec) specs.lastElement(); if (last.getType() == ElementSpec.ContentType) { - Element currentRun = - prevParagraph.getElement(prevParagraph.getElementIndex(offset)); + Element currentRun = prevParagraph.getElement(prevParagraph.getElementIndex(offset)); if (currentRun.getEndOffset() == endOffset) { if (endOffset < getLength() && next.getAttributes().isEqual(attr) @@ -1838,62 +2211,58 @@ public class DefaultStyledDocument extends AbstractDocument else { if (finalStartTag != null - && finalStartTag.getDirection() == - ElementSpec.JoinFractureDirection + && finalStartTag.getDirection() == ElementSpec.JoinFractureDirection && currentRun.getAttributes().isEqual(attr)) { last.setDirection(ElementSpec.JoinNextDirection); } } } - + // If we are at the first new element, then check if it could be // joined with the previous element. ElementSpec first = (ElementSpec) specs.firstElement(); if (prev.getAttributes().isEqual(attr) && first.getType() == ElementSpec.ContentType) first.setDirection(ElementSpec.JoinPreviousDirection); - - ElementSpec[] elSpecs = - (ElementSpec[]) specs.toArray(new ElementSpec[specs.size()]); + ElementSpec[] elSpecs = (ElementSpec[]) specs.toArray(new ElementSpec[specs.size()]); buffer.insert(offset, length, elSpecs, ev); } /** - * A helper method to set up the ElementSpec buffer for the special - * case of an insertion occurring immediately after a newline. - * @param specs the ElementSpec buffer to initialize. + * A helper method to set up the ElementSpec buffer for the special case of an + * insertion occurring immediately after a newline. + * + * @param specs + * the ElementSpec buffer to initialize. */ short handleInsertAfterNewline(Vector specs, int offset, int endOffset, - Element prevParagraph, Element paragraph, - AttributeSet a) + Element prevParagraph, Element paragraph, + AttributeSet a) { if (prevParagraph.getParentElement() == paragraph.getParentElement()) { specs.add(new ElementSpec(a, ElementSpec.EndTagType)); specs.add(new ElementSpec(a, ElementSpec.StartTagType)); - if (prevParagraph.getEndOffset() != endOffset) + if (paragraph.getStartOffset() != endOffset) return ElementSpec.JoinFractureDirection; // If there is an Element after this one, use JoinNextDirection. Element parent = paragraph.getParentElement(); - if (parent.getElementCount() > parent.getElementIndex(offset) + 1) + if (parent.getElementCount() > (parent.getElementIndex(offset) + 1)) return ElementSpec.JoinNextDirection; } - else - { - // TODO: What to do here? - } return ElementSpec.OriginateDirection; } - + /** * Updates the document structure in response to text removal. This is - * forwarded to the {@link ElementBuffer} of this document. Any changes to - * the document structure are added to the specified document event and - * sent to registered listeners. - * - * @param ev the document event that records the changes to the document + * forwarded to the {@link ElementBuffer} of this document. Any changes to the + * document structure are added to the specified document event and sent to + * registered listeners. + * + * @param ev + * the document event that records the changes to the document */ protected void removeUpdate(DefaultDocumentEvent ev) { @@ -1903,7 +2272,7 @@ public class DefaultStyledDocument extends AbstractDocument /** * Returns an enumeration of all style names. - * + * * @return an enumeration of all style names */ public Enumeration getStyleNames() @@ -1914,61 +2283,35 @@ public class DefaultStyledDocument extends AbstractDocument /** * Called when any of this document's styles changes. - * - * @param style the style that changed + * + * @param style + * the style that changed */ protected void styleChanged(Style style) { // Nothing to do here. This is intended to be overridden by subclasses. } - void printElements (Element start, int pad) - { - for (int i = 0; i < pad; i++) - System.out.print(" "); - if (pad == 0) - System.out.println ("ROOT ELEMENT ("+start.getStartOffset()+", "+start.getEndOffset()+")"); - else if (start instanceof AbstractDocument.BranchElement) - System.out.println ("BranchElement ("+start.getStartOffset()+", "+start.getEndOffset()+")"); - else - { - { - try - { - System.out.println ("LeafElement ("+start.getStartOffset()+", " - + start.getEndOffset()+"): "+ - start.getDocument(). - getText(start.getStartOffset(), - start.getEndOffset() - - start.getStartOffset())); - } - catch (BadLocationException ble) - { - } - } - } - for (int i = 0; i < start.getElementCount(); i ++) - printElements (start.getElement(i), pad+3); - } - /** * Inserts a bulk of structured content at once. - * - * @param offset the offset at which the content should be inserted - * @param data the actual content spec to be inserted + * + * @param offset + * the offset at which the content should be inserted + * @param data + * the actual content spec to be inserted */ protected void insert(int offset, ElementSpec[] data) - throws BadLocationException + throws BadLocationException { if (data == null || data.length == 0) return; try { // writeLock() and writeUnlock() should always be in a try/finally - // block so that locking balance is guaranteed even if some + // block so that locking balance is guaranteed even if some // exception is thrown. writeLock(); - + // First we collect the content to be inserted. StringBuffer contentBuffer = new StringBuffer(); for (int i = 0; i < data.length; i++) @@ -1986,15 +2329,14 @@ public class DefaultStyledDocument extends AbstractDocument // If there was no content inserted then exit early. if (length == 0) return; - + UndoableEdit edit = content.insertString(offset, contentBuffer.toString()); // Create the DocumentEvent with the ElementEdit added - DefaultDocumentEvent ev = - new DefaultDocumentEvent(offset, - length, - DocumentEvent.EventType.INSERT); + DefaultDocumentEvent ev = new DefaultDocumentEvent(offset, + length, + DocumentEvent.EventType.INSERT); ev.addEdit(edit); // Finally we must update the document structure and fire the insert @@ -2012,20 +2354,66 @@ public class DefaultStyledDocument extends AbstractDocument /** * Initializes the <code>DefaultStyledDocument</code> with the specified * data. - * - * @param data the specification of the content with which the document is - * initialized + * + * @param data + * the specification of the content with which the document is + * initialized */ protected void create(ElementSpec[] data) { + writeLock(); try { - // Clear content. - content.remove(0, content.length()); - // Clear buffer and root element. - buffer = new ElementBuffer(createDefaultRoot()); - // Insert the data. - insert(0, data); + // Clear content if there is some. + int len = getLength(); + if (len > 0) + remove(0, len); + + // Now we insert the content. + StringBuilder b = new StringBuilder(); + for (int i = 0; i < data.length; ++i) + { + ElementSpec el = data[i]; + if (el.getArray() != null && el.getLength() > 0) + b.append(el.getArray(), el.getOffset(), el.getLength()); + } + Content content = getContent(); + UndoableEdit cEdit = content.insertString(0, b.toString()); + + DefaultDocumentEvent ev = + new DefaultDocumentEvent(0, b.length(), + DocumentEvent.EventType.INSERT); + ev.addEdit(cEdit); + + // We do a little trick here to get the new structure: We instantiate + // a new ElementBuffer with a new root element, insert into that root + // and then reparent the newly created elements to the old root + // element. + BranchElement createRoot = + (BranchElement) createBranchElement(null, null); + Element dummyLeaf = createLeafElement(createRoot, null, 0, 1); + createRoot.replace(0, 0, new Element[]{ dummyLeaf }); + ElementBuffer createBuffer = new ElementBuffer(createRoot); + createBuffer.insert(0, b.length(), data, new DefaultDocumentEvent(0, b.length(), DocumentEvent.EventType.INSERT)); + // Now the new root is the first child of the createRoot. + Element newRoot = createRoot.getElement(0); + BranchElement root = (BranchElement) getDefaultRootElement(); + Element[] added = new Element[newRoot.getElementCount()]; + for (int i = 0; i < added.length; ++i) + { + added[i] = newRoot.getElement(i); + ((AbstractElement) added[i]).element_parent = root; + } + Element[] removed = new Element[root.getElementCount()]; + for (int i = 0; i < removed.length; ++i) + removed[i] = root.getElement(i); + + // Replace the old elements in root with the new and update the event. + root.replace(0, removed.length, added); + ev.addEdit(new ElementEdit(root, 0, removed, added)); + + fireInsertUpdate(ev); + fireUndoableEditUpdate(new UndoableEditEvent(this, ev)); } catch (BadLocationException ex) { @@ -2033,10 +2421,9 @@ public class DefaultStyledDocument extends AbstractDocument err.initCause(ex); throw err; } - } - - static boolean attributeSetsAreSame (AttributeSet a, AttributeSet b) - { - return (a == null && b == null) || (a != null && a.isEqual(b)); + finally + { + writeUnlock(); + } } } diff --git a/libjava/classpath/javax/swing/text/DefaultTextUI.java b/libjava/classpath/javax/swing/text/DefaultTextUI.java index e7ff01845e6..c347668b996 100644 --- a/libjava/classpath/javax/swing/text/DefaultTextUI.java +++ b/libjava/classpath/javax/swing/text/DefaultTextUI.java @@ -45,6 +45,7 @@ import javax.swing.plaf.basic.BasicTextUI; * all text components is now {@link BasicTextUI}. * * @author Roman Kennke (kennke@aicas.com) + * @deprecated as of 1.5 use {@link BasicTextUI} instead */ public abstract class DefaultTextUI extends BasicTextUI { diff --git a/libjava/classpath/javax/swing/text/FlowView.java b/libjava/classpath/javax/swing/text/FlowView.java index 6d4b9cd3174..8be8f41e939 100644 --- a/libjava/classpath/javax/swing/text/FlowView.java +++ b/libjava/classpath/javax/swing/text/FlowView.java @@ -38,14 +38,10 @@ exception statement from your version. */ package javax.swing.text; -import java.awt.Container; -import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; -import java.util.Iterator; -import java.util.Vector; -import javax.swing.SwingConstants; +import javax.swing.SizeRequirements; import javax.swing.event.DocumentEvent; /** @@ -89,7 +85,7 @@ public abstract class FlowView extends BoxView */ public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) { - layout(fv); + // The default implementation does nothing. } /** @@ -105,7 +101,7 @@ public abstract class FlowView extends BoxView */ public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) { - layout(fv); + // The default implementation does nothing. } /** @@ -121,7 +117,7 @@ public abstract class FlowView extends BoxView */ public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) { - layout(fv); + // The default implementation does nothing. } /** @@ -131,7 +127,7 @@ public abstract class FlowView extends BoxView * * @return the logical view of the managed <code>FlowView</code> */ - public View getLogicalView(FlowView fv) + protected View getLogicalView(FlowView fv) { return fv.layoutPool; } @@ -166,43 +162,60 @@ 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. + * * @param fv the flow view for which we perform the layout * @param rowIndex the index of the row - * @param pos the start position for 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) { - int spanLeft = fv.getFlowSpan(rowIndex); - if (spanLeft <= 0) - return -1; - - int offset = pos; View row = fv.getView(rowIndex); - int flowAxis = fv.getFlowAxis(); + int axis = fv.getFlowAxis(); + int span = fv.getFlowSpan(rowIndex); + int x = fv.getFlowStart(rowIndex); + int offset = pos; + View logicalView = getLogicalView(fv); + // Special case when span == 0. We need to layout the row as if it had + // a span of Integer.MAX_VALUE. + if (span == 0) + span = Integer.MAX_VALUE; - while (spanLeft > 0) + while (span > 0) { - View child = createView(fv, offset, spanLeft, rowIndex); - if (child == null) - { - offset = -1; - break; - } - - int span = (int) child.getPreferredSpan(flowAxis); - if (span > spanLeft) - { - offset = -1; - break; - } - - row.append(child); - spanLeft -= span; - offset = child.getEndOffset(); + 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; + x += viewSpan; + span -= viewSpan; + offset += (view.getEndOffset() - view.getStartOffset()); } - return offset; + 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; + } + return offset != pos ? offset : -1; } /** @@ -212,189 +225,106 @@ public abstract class FlowView extends BoxView * available span and can be broken down) or <code>null</code> (if it does * not fit in the available span and also cannot be broken down). * + * The default implementation fetches the logical view at the specified + * <code>startOffset</code>. If that view has a different startOffset than + * specified in the argument, a fragment is created using + * {@link View#createFragment(int, int)} that has the correct startOffset + * and the logical view's endOffset. + * * @param fv the flow view - * @param offset the start offset for the view to be created + * @param startOffset the start offset for the view to be created * @param spanLeft the available span * @param rowIndex the index of the row * * @return a view to fill the row with, or <code>null</code> if there * is no view or view fragment that fits in the available span */ - protected View createView(FlowView fv, int offset, int spanLeft, + protected View createView(FlowView fv, int startOffset, int spanLeft, int rowIndex) { - // Find the logical element for the given offset. - View logicalView = getLogicalView(fv); - - int viewIndex = logicalView.getViewIndex(offset, Position.Bias.Forward); - if (viewIndex == -1) - return null; - - View child = logicalView.getView(viewIndex); - int flowAxis = fv.getFlowAxis(); - int span = (int) child.getPreferredSpan(flowAxis); - - if (span <= spanLeft) - return child; - else if (child.getBreakWeight(flowAxis, offset, spanLeft) - > BadBreakWeight) - // FIXME: What to do with the pos parameter here? - return child.breakView(flowAxis, offset, 0, spanLeft); - else - return null; - } - } - - /** - * This special subclass of <code>View</code> is used to represent - * the logical representation of this view. It does not support any - * visual representation, this is handled by the physical view implemented - * in the <code>FlowView</code>. - */ - class LogicalView extends View - { - /** - * The child views of this logical view. - */ - Vector children; - - /** - * Creates a new LogicalView instance. - */ - LogicalView(Element el) - { - super(el); - children = new Vector(); - } - - /** - * Returns the container that holds this view. The logical view returns - * the enclosing FlowView's container here. - * - * @return the container that holds this view - */ - public Container getContainer() - { - return FlowView.this.getContainer(); - } - - /** - * Returns the number of child views of this logical view. - * - * @return the number of child views of this logical view - */ - public int getViewCount() - { - return children.size(); - } - - /** - * Returns the child view at the specified index. - * - * @param index the index - * - * @return the child view at the specified index - */ - public View getView(int index) - { - return (View) children.get(index); - } - - /** - * Replaces some child views with other child views. - * - * @param offset the offset at which to replace child views - * @param length the number of children to remove - * @param views the views to be inserted - */ - public void replace(int offset, int length, View[] views) - { - if (length > 0) - { - for (int count = 0; count < length; ++count) - children.remove(offset); - } - - int endOffset = offset + views.length; - for (int i = offset; i < endOffset; ++i) - { - children.add(i, views[i - offset]); - // Set the parent of the child views to the flow view itself so - // it has something to resolve. - views[i - offset].setParent(FlowView.this); - } + View logicalView = getLogicalView(fv); + // FIXME: Handle the bias thing correctly. + int index = logicalView.getViewIndex(startOffset, Position.Bias.Forward); + View retVal = null; + if (index >= 0) + { + retVal = logicalView.getView(index); + if (retVal.getStartOffset() != startOffset) + retVal = retVal.createFragment(startOffset, retVal.getEndOffset()); + } + return retVal; } /** - * Returns the index of the child view that contains the specified - * position in the document model. + * Tries to adjust the specified row to fit within the desired span. The + * default implementation iterates through the children of the specified + * row to find the view that has the highest break weight and - if there + * is more than one view with such a break weight - which is nearest to + * the end of the row. If there is such a view that has a break weight > + * {@link View#BadBreakWeight}, this view is broken using the + * {@link View#breakView(int, int, float, float)} method and this view and + * all views after the now broken view are replaced by the broken view. * - * @param pos the position for which we are searching the child view - * @param b the bias - * - * @return the index of the child view that contains the specified - * position in the document model + * @param fv the flow view + * @param rowIndex the index of the row to be adjusted + * @param desiredSpan the layout span + * @param x the X location at which the row starts */ - public int getViewIndex(int pos, Position.Bias b) - { - int index = -1; - int i = 0; - for (Iterator it = children.iterator(); it.hasNext(); i++) + protected void adjustRow(FlowView fv, int rowIndex, int desiredSpan, int x) { + // Determine the last view that has the highest break weight. + int axis = fv.getFlowAxis(); + View row = fv.getView(rowIndex); + int count = row.getViewCount(); + int breakIndex = -1; + int maxBreakWeight = View.BadBreakWeight; + int breakX = x; + int breakSpan = desiredSpan; + int currentX = x; + int currentSpan = desiredSpan; + for (int i = 0; i < count; ++i) { - View child = (View) it.next(); - if (child.getStartOffset() >= pos - && child.getEndOffset() < pos) + View view = row.getView(i); + int weight = view.getBreakWeight(axis, currentX, currentSpan); + if (weight >= maxBreakWeight) { - index = i; - break; + breakIndex = i; + breakX = currentX; + breakSpan = currentSpan; + maxBreakWeight = weight; } + int size = (int) view.getPreferredSpan(axis); + currentX += size; + currentSpan -= size; } - return index; - } - /** - * Throws an AssertionError because it must never be called. LogicalView - * only serves as a holder for child views and has no visual - * representation. - */ - public float getPreferredSpan(int axis) - { - throw new AssertionError("This method must not be called in " - + "LogicalView."); - } - - /** - * Throws an AssertionError because it must never be called. LogicalView - * only serves as a holder for child views and has no visual - * representation. - */ - public Shape modelToView(int pos, Shape a, Position.Bias b) - throws BadLocationException - { - throw new AssertionError("This method must not be called in " - + "LogicalView."); - } - - /** - * Throws an AssertionError because it must never be called. LogicalView - * only serves as a holder for child views and has no visual - * representation. - */ - public void paint(Graphics g, Shape s) - { - throw new AssertionError("This method must not be called in " - + "LogicalView."); + // If there is a potential break location found, break the row at + // this location. + if (breakIndex > -1) + { + View toBeBroken = row.getView(breakIndex); + View brokenView = toBeBroken.breakView(axis, + toBeBroken.getStartOffset(), + breakX, breakSpan); + row.replace(breakIndex, count - breakIndex, + new View[]{brokenView}); + } } + } + /** + * This special subclass of <code>View</code> is used to represent + * the logical representation of this view. It does not support any + * visual representation, this is handled by the physical view implemented + * in the <code>FlowView</code>. + */ + class LogicalView extends BoxView + { /** - * Throws an AssertionError because it must never be called. LogicalView - * only serves as a holder for child views and has no visual - * representation. + * Creates a new LogicalView instance. */ - public int viewToModel(float x, float y, Shape a, Position.Bias[] b) + LogicalView(Element el, int axis) { - throw new AssertionError("This method must not be called in " - + "LogicalView."); + super(el, axis); } } @@ -424,6 +354,11 @@ public abstract class FlowView extends BoxView protected FlowStrategy strategy; /** + * Indicates if the flow should be rebuild during the next layout. + */ + private boolean layoutDirty; + + /** * Creates a new <code>FlowView</code> for the given * <code>Element</code> and <code>axis</code>. * @@ -436,6 +371,7 @@ public abstract class FlowView extends BoxView { super(element, axis); strategy = sharedStrategy; + layoutDirty = true; } /** @@ -510,16 +446,8 @@ public abstract class FlowView extends BoxView { if (layoutPool == null) { - layoutPool = new LogicalView(getElement()); - - Element el = getElement(); - int count = el.getElementCount(); - for (int i = 0; i < count; ++i) - { - Element childEl = el.getElement(i); - View childView = vf.create(childEl); - layoutPool.append(childView); - } + layoutPool = new LogicalView(getElement(), getAxis()); + layoutPool.setParent(this); } } @@ -534,27 +462,32 @@ public abstract class FlowView extends BoxView */ protected void layout(int width, int height) { - boolean rebuild = false; - int flowAxis = getFlowAxis(); if (flowAxis == X_AXIS) { - rebuild = !(width == layoutSpan); - layoutSpan = width; + if (layoutSpan != width) + { + layoutChanged(Y_AXIS); + layoutSpan = width; + } } else { - rebuild = !(height == layoutSpan); - layoutSpan = height; + if (layoutSpan != height) + { + layoutChanged(X_AXIS); + layoutSpan = height; + } } - if (rebuild) - strategy.layout(this); + if (layoutDirty) + { + strategy.layout(this); + layoutDirty = false; + } - // TODO: If the span along the box axis has changed in the process of - // relayouting the rows (that is, if rows have been added or removed), - // call preferenceChanged in order to throw away cached layout information - // of the surrounding BoxView. + if (getPreferredSpan(getAxis()) != height) + preferenceChanged(this, false, true); super.layout(width, height); } @@ -574,6 +507,7 @@ public abstract class FlowView extends BoxView // be updated accordingly. layoutPool.insertUpdate(changes, a, vf); strategy.insertUpdate(this, changes, getInsideAllocation(a)); + layoutDirty = true; } /** @@ -588,6 +522,7 @@ public abstract class FlowView extends BoxView public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory vf) { strategy.removeUpdate(this, changes, getInsideAllocation(a)); + layoutDirty = true; } /** @@ -602,6 +537,7 @@ public abstract class FlowView extends BoxView public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory vf) { strategy.changedUpdate(this, changes, getInsideAllocation(a)); + layoutDirty = true; } /** @@ -640,4 +576,30 @@ public abstract class FlowView extends BoxView } return result; } + + /** + * Calculates the size requirements of this <code>BoxView</code> along + * its minor axis, that is the axis opposite to the axis specified in the + * constructor. + * + * This is overridden and forwards the request to the logical view. + * + * @param axis the axis that is examined + * @param r the <code>SizeRequirements</code> object to hold the result, + * if <code>null</code>, a new one is created + * + * @return the size requirements for this <code>BoxView</code> along + * the specified axis + */ + 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); + res.minimum = (int) layoutPool.getMinimumSpan(axis); + res.preferred = (int) layoutPool.getPreferredSpan(axis); + res.maximum = (int) layoutPool.getMaximumSpan(axis); + return res; + } } diff --git a/libjava/classpath/javax/swing/text/GapContent.java b/libjava/classpath/javax/swing/text/GapContent.java index 80dcfa56e06..28d1d6ee01f 100644 --- a/libjava/classpath/javax/swing/text/GapContent.java +++ b/libjava/classpath/javax/swing/text/GapContent.java @@ -1,5 +1,5 @@ /* GapContent.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. @@ -39,8 +39,10 @@ exception statement from your version. */ package javax.swing.text; import java.io.Serializable; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; import java.util.ListIterator; import java.util.Vector; @@ -68,8 +70,8 @@ public class GapContent /** * A {@link Position} implementation for <code>GapContent</code>. */ - class GapContentPosition - implements Position, Comparable + private class GapContentPosition + implements Position, Comparable { /** The index within the buffer array. */ @@ -130,7 +132,7 @@ public class GapContent } } - class InsertUndo extends AbstractUndoableEdit + private class InsertUndo extends AbstractUndoableEdit { public int where, length; String text; @@ -169,7 +171,7 @@ public class GapContent } - class UndoRemove extends AbstractUndoableEdit + private class UndoRemove extends AbstractUndoableEdit { public int where; String text; @@ -206,7 +208,41 @@ public class GapContent } } - + + /** + * Compares WeakReference objects in a List by comparing the referenced + * objects instead. + * + * @author Roman Kennke (kennke@aicas.com) + */ + private class WeakPositionComparator + implements Comparator + { + + /** + * Compares two objects of type WeakReference. The objects are compared + * using the referenced objects compareTo() method. + */ + public int compare(Object o1, Object o2) + { + // Unwrap references. + if (o1 instanceof WeakReference) + o1 = ((WeakReference) o1).get(); + if (o2 instanceof WeakReference) + o2 = ((WeakReference) o2).get(); + + GapContentPosition p1 = (GapContentPosition) o1; + GapContentPosition p2 = (GapContentPosition) o2; + + int retVal; + if (p1 == null || p2 == null) + retVal = -1; + else + retVal = p1.compareTo(p2); + return retVal; + } + } + /** The serialization UID (compatible with JDK1.5). */ private static final long serialVersionUID = -6226052713477823730L; @@ -233,9 +269,10 @@ public class GapContent /** * The positions generated by this GapContent. They are kept in an ordered - * fashion, so they can be looked up easily. + * fashion, so they can be looked up easily. The value objects will be + * WeakReference objects that in turn hold GapContentPosition objects. */ - ArrayList positions; + private ArrayList positions; /** * Creates a new GapContent object. @@ -310,8 +347,12 @@ public class GapContent int length = length(); int strLen = str.length(); + if (where < 0) + throw new BadLocationException("The where argument cannot be smaller" + + " than the zero", where); + if (where >= length) - throw new BadLocationException("the where argument cannot be greater" + throw new BadLocationException("The where argument cannot be greater" + " than the content length", where); replace(where, 0, str.toCharArray(), strLen); @@ -446,18 +487,22 @@ public class GapContent throw new BadLocationException("The offset was out of the bounds of this" + " buffer", offset); + clearPositionReferences(); + // We store the actual array index in the GapContentPosition. The real // offset is then calculated in the GapContentPosition. int mark = offset; if (offset >= gapStart) mark += gapEnd - gapStart; GapContentPosition pos = new GapContentPosition(mark); + WeakReference r = new WeakReference(pos); // Add this into our list in a sorted fashion. - int index = Collections.binarySearch(positions, pos); + int index = Collections.binarySearch(positions, r, + new WeakPositionComparator()); if (index < 0) index = -(index + 1); - positions.add(index, pos); + positions.add(index, r); return pos; } @@ -557,7 +602,7 @@ public class GapContent assert newGapEnd > gapEnd : "The new gap end must be greater than the " + "old gap end."; - setPositionsInRange(gapEnd, newGapEnd - gapEnd, newGapEnd + 1); + setPositionsInRange(gapEnd, newGapEnd - gapEnd, newGapEnd); gapEnd = newGapEnd; } @@ -566,7 +611,7 @@ public class GapContent * * @return the allocated buffer array */ - protected Object getArray() + protected final Object getArray() { return buffer; } @@ -642,19 +687,30 @@ public class GapContent int endOffset = offset + length; int index1 = Collections.binarySearch(positions, - new GapContentPosition(offset)); + new GapContentPosition(offset), + new WeakPositionComparator()); if (index1 < 0) index1 = -(index1 + 1); // Search the first index with the specified offset. The binarySearch does // not necessarily find the first one. - while (index1 > 0 - && ((GapContentPosition) positions.get(index1 - 1)).mark == offset) - index1--; + while (index1 > 0) + { + WeakReference r = (WeakReference) positions.get(index1 - 1); + GapContentPosition p = (GapContentPosition) r.get(); + if (p != null && p.mark == offset || p == null) + index1--; + else + break; + } for (ListIterator i = positions.listIterator(index1); i.hasNext();) { - GapContentPosition p = (GapContentPosition) i.next(); + WeakReference r = (WeakReference) i.next(); + GapContentPosition p = (GapContentPosition) r.get(); + if (p == null) + continue; + if (p.mark > endOffset) break; if (p.mark >= offset && p.mark <= endOffset) @@ -672,24 +728,35 @@ public class GapContent * @param length the length of the range to search * @param value the new value for each mark */ - void setPositionsInRange(int offset, int length, int value) + private void setPositionsInRange(int offset, int length, int value) { int endOffset = offset + length; int index1 = Collections.binarySearch(positions, - new GapContentPosition(offset)); + new GapContentPosition(offset), + new WeakPositionComparator()); if (index1 < 0) index1 = -(index1 + 1); // Search the first index with the specified offset. The binarySearch does // not necessarily find the first one. - while (index1 > 0 - && ((GapContentPosition) positions.get(index1 - 1)).mark == offset) - index1--; + while (index1 > 0) + { + WeakReference r = (WeakReference) positions.get(index1 - 1); + GapContentPosition p = (GapContentPosition) r.get(); + if (p != null && p.mark == offset || p == null) + index1--; + else + break; + } for (ListIterator i = positions.listIterator(index1); i.hasNext();) { - GapContentPosition p = (GapContentPosition) i.next(); + WeakReference r = (WeakReference) i.next(); + GapContentPosition p = (GapContentPosition) r.get(); + if (p == null) + continue; + if (p.mark > endOffset) break; @@ -707,23 +774,35 @@ public class GapContent * @param length the length of the range to search * @param incr the increment */ - void adjustPositionsInRange(int offset, int length, int incr) + private void adjustPositionsInRange(int offset, int length, int incr) { int endOffset = offset + length; int index1 = Collections.binarySearch(positions, - new GapContentPosition(offset)); + new GapContentPosition(offset), + new WeakPositionComparator()); if (index1 < 0) index1 = -(index1 + 1); // Search the first index with the specified offset. The binarySearch does // not necessarily find the first one. - while (index1 > 0 - && ((GapContentPosition) positions.get(index1 - 1)).mark == offset) - index1--; + while (index1 > 0) + { + WeakReference r = (WeakReference) positions.get(index1 - 1); + GapContentPosition p = (GapContentPosition) r.get(); + if (p != null && p.mark == offset || p == null) + index1--; + else + break; + } + for (ListIterator i = positions.listIterator(index1); i.hasNext();) { - GapContentPosition p = (GapContentPosition) i.next(); + WeakReference r = (WeakReference) i.next(); + GapContentPosition p = (GapContentPosition) r.get(); + if (p == null) + continue; + if (p.mark > endOffset) break; @@ -747,6 +826,17 @@ public class GapContent } /** + * @specnote This method is not very well specified and the positions vector + * is implementation specific. The undo positions are managed + * differently in this implementation, this method is only here + * for binary compatibility. + */ + protected void updateUndoPositions(Vector positions, int offset, int length) + { + // We do nothing here. + } + + /** * Outputs debugging info to System.err. It prints out the buffer array, * the gapStart is marked by a < sign, the gapEnd is marked by a > * sign and each position is marked by a # sign. @@ -776,8 +866,23 @@ public class GapContent { for (Iterator i = positions.iterator(); i.hasNext();) { - GapContentPosition pos = (GapContentPosition) i.next(); + WeakReference r = (WeakReference) i.next(); + GapContentPosition pos = (GapContentPosition) r.get(); System.err.println("position at: " + pos.mark); } } + + /** + * Clears all GC'ed references in the positions array. + */ + private void clearPositionReferences() + { + Iterator i = positions.iterator(); + while (i.hasNext()) + { + WeakReference r = (WeakReference) i.next(); + if (r.get() == null) + i.remove(); + } + } } diff --git a/libjava/classpath/javax/swing/text/GlyphView.java b/libjava/classpath/javax/swing/text/GlyphView.java index 47deb50d03a..d505274c91f 100644 --- a/libjava/classpath/javax/swing/text/GlyphView.java +++ b/libjava/classpath/javax/swing/text/GlyphView.java @@ -277,38 +277,41 @@ public class GlyphView extends View implements TabableView, Cloneable public void paint(GlyphView view, Graphics g, Shape a, int p0, int p1) { + Color oldColor = g.getColor(); int height = (int) getHeight(view); Segment txt = view.getText(p0, p1); Rectangle bounds = a.getBounds(); - TabExpander tabEx = null; View parent = view.getParent(); if (parent instanceof TabExpander) tabEx = (TabExpander) parent; - // Fill the background of the text run. - Color background = view.getBackground(); - g.setColor(background); int width = Utilities.getTabbedTextWidth(txt, g.getFontMetrics(), bounds.x, tabEx, txt.offset); - g.fillRect(bounds.x, bounds.y, width, height); - + // Fill the background of the text run. + Color background = view.getBackground(); + if (background != null) + { + g.setColor(background); + g.fillRect(bounds.x, bounds.y, width, height); + } // Draw the actual text. g.setColor(view.getForeground()); g.setFont(view.getFont()); + int ascent = g.getFontMetrics().getAscent(); if (view.isSuperscript()) // TODO: Adjust font for superscripting. - Utilities.drawTabbedText(txt, bounds.x, bounds.y - height / 2, g, tabEx, - txt.offset); + Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent - height / 2, + g, tabEx, txt.offset); else if (view.isSubscript()) // TODO: Adjust font for subscripting. - Utilities.drawTabbedText(txt, bounds.x, bounds.y + height / 2, g, tabEx, - txt.offset); + Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent + height / 2, + g, tabEx, txt.offset); else - Utilities.drawTabbedText(txt, bounds.x, bounds.y, g, tabEx, + Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent, g, tabEx, txt.offset); - if (view.isStikeThrough()) + if (view.isStrikeThrough()) { int strikeHeight = (int) (getAscent(view) / 2); g.drawLine(bounds.x, bounds.y + strikeHeight, bounds.height + width, @@ -320,6 +323,7 @@ public class GlyphView extends View implements TabableView, Cloneable g.drawLine(bounds.x, bounds.y + lineHeight, bounds.height + width, bounds.y + lineHeight); } + g.setColor(oldColor); } /** @@ -470,12 +474,12 @@ public class GlyphView extends View implements TabableView, Cloneable /** * The start offset within the document for this view. */ - int startOffset; + private int startOffset; /** * The end offset within the document for this view. */ - int endOffset; + private int endOffset; /** * Creates a new <code>GlyphView</code> for the given <code>Element</code>. @@ -485,8 +489,8 @@ public class GlyphView extends View implements TabableView, Cloneable public GlyphView(Element element) { super(element); - startOffset = element.getStartOffset(); - endOffset = element.getEndOffset(); + startOffset = -1; + endOffset = -1; } /** @@ -534,8 +538,7 @@ public class GlyphView extends View implements TabableView, Cloneable { Element el = getElement(); checkPainter(); - getGlyphPainter().paint(this, g, a, el.getStartOffset(), - el.getEndOffset()); + getGlyphPainter().paint(this, g, a, getStartOffset(), getEndOffset()); } @@ -563,7 +566,8 @@ public class GlyphView extends View implements TabableView, Cloneable tabEx, 0.F); } else - span = painter.getHeight(this); + span = painter.getHeight(this); + return span; } @@ -682,7 +686,10 @@ public class GlyphView extends View implements TabableView, Cloneable */ public int getStartOffset() { - return startOffset; + int start = startOffset; + if (start < 0) + start = super.getStartOffset(); + return start; } /** @@ -694,7 +701,10 @@ public class GlyphView extends View implements TabableView, Cloneable */ public int getEndOffset() { - return endOffset; + int end = endOffset; + if (end < 0) + end = super.getEndOffset(); + return end; } /** @@ -771,7 +781,11 @@ public class GlyphView extends View implements TabableView, Cloneable { Element el = getElement(); AttributeSet atts = el.getAttributes(); - return StyleConstants.getBackground(atts); + // We cannot use StyleConstants.getBackground() here, because that returns + // BLACK as default (when background == null). What we need is the + // background setting of the text component instead, which is what we get + // when background == null anyway. + return (Color) atts.getAttribute(StyleConstants.Background); } /** @@ -782,7 +796,7 @@ public class GlyphView extends View implements TabableView, Cloneable * * @return whether the text should be rendered strike-through or not */ - public boolean isStikeThrough() + public boolean isStrikeThrough() { Element el = getElement(); AttributeSet atts = el.getAttributes(); @@ -876,13 +890,15 @@ public class GlyphView extends View implements TabableView, Cloneable checkPainter(); GlyphPainter painter = getGlyphPainter(); - int breakLocation = painter.getBoundedPosition(this, p0, pos, len); + // Try to find a suitable line break. BreakIterator lineBreaker = BreakIterator.getLineInstance(); Segment txt = new Segment(); try { - getDocument().getText(getStartOffset(), getEndOffset(), txt); + int start = getStartOffset(); + int length = getEndOffset() - start; + getDocument().getText(start, length, txt); } catch (BadLocationException ex) { @@ -891,11 +907,10 @@ public class GlyphView extends View implements TabableView, Cloneable err.initCause(ex); throw err; } - lineBreaker.setText(txt); - int goodBreakLocation = lineBreaker.previous(); - if (goodBreakLocation != BreakIterator.DONE) - breakLocation = goodBreakLocation; - + int breakLocation = + Utilities.getBreakLocation(txt, getContainer().getFontMetrics(getFont()), + (int) pos, (int) (pos + len), + getTabExpander(), p0); View brokenView = createFragment(p0, breakLocation); return brokenView; } @@ -922,23 +937,24 @@ public class GlyphView extends View implements TabableView, Cloneable weight = super.getBreakWeight(axis, pos, len); else { - // Determine the model locations at pos and pos + len. - int spanX = (int) getPreferredSpan(X_AXIS); - int spanY = (int) getPreferredSpan(Y_AXIS); - Rectangle dummyAlloc = new Rectangle(0, 0, spanX, spanY); - Position.Bias[] biasRet = new Position.Bias[1]; - int offset1 = viewToModel(pos, spanY / 2, dummyAlloc, biasRet); - int offset2 = viewToModel(pos, spanY / 2, dummyAlloc, biasRet); - Segment txt = getText(offset1, offset2); - BreakIterator lineBreaker = BreakIterator.getLineInstance(); - lineBreaker.setText(txt); - int breakLoc = lineBreaker.previous(); - if (breakLoc == offset1) - weight = View.BadBreakWeight; - else if(breakLoc == BreakIterator.DONE) - weight = View.GoodBreakWeight; - else - weight = View.ExcellentBreakWeight; + // FIXME: Commented out because the Utilities.getBreakLocation method + // is still buggy. The GoodBreakWeight is a reasonable workaround for + // now. +// int startOffset = getStartOffset(); +// int endOffset = getEndOffset() - 1; +// Segment s = getText(startOffset, endOffset); +// Container c = getContainer(); +// FontMetrics fm = c.getFontMetrics(c.getFont()); +// int x0 = (int) pos; +// int x = (int) (pos + len); +// int breakLoc = Utilities.getBreakLocation(s, fm, x0, x, +// getTabExpander(), +// startOffset); +// if (breakLoc == startOffset || breakLoc == endOffset) +// weight = GoodBreakWeight; +// else +// weight = ExcellentBreakWeight; + weight = GoodBreakWeight; } return weight; } @@ -955,14 +971,14 @@ public class GlyphView extends View implements TabableView, Cloneable */ public void changedUpdate(DocumentEvent e, Shape a, ViewFactory vf) { - getParent().preferenceChanged(this, true, true); + preferenceChanged(this, true, true); } /** * Receives notification that some text has been inserted within the * text fragment that this view is responsible for. This calls - * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for - * width. + * {@link View#preferenceChanged(View, boolean, boolean)} for the + * direction in which the glyphs are rendered. * * @param e the document event describing the change; not used here * @param a the view allocation on screen; not used here @@ -970,7 +986,7 @@ public class GlyphView extends View implements TabableView, Cloneable */ public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf) { - getParent().preferenceChanged(this, true, false); + preferenceChanged(this, true, false); } /** @@ -985,7 +1001,7 @@ public class GlyphView extends View implements TabableView, Cloneable */ public void removeUpdate(DocumentEvent e, Shape a, ViewFactory vf) { - getParent().preferenceChanged(this, true, false); + preferenceChanged(this, true, false); } /** @@ -1000,8 +1016,10 @@ public class GlyphView extends View implements TabableView, Cloneable public View createFragment(int p0, int p1) { GlyphView fragment = (GlyphView) clone(); - fragment.startOffset = p0; - fragment.endOffset = p1; + if (p0 != getStartOffset()) + fragment.startOffset = p0; + if (p1 != getEndOffset()) + fragment.endOffset = p1; return fragment; } diff --git a/libjava/classpath/javax/swing/text/IconView.java b/libjava/classpath/javax/swing/text/IconView.java index af2581a8831..699cda90eba 100644 --- a/libjava/classpath/javax/swing/text/IconView.java +++ b/libjava/classpath/javax/swing/text/IconView.java @@ -130,8 +130,6 @@ public class IconView throws BadLocationException { Element el = getElement(); - if (pos < el.getStartOffset() || pos >= el.getEndOffset()) - throw new BadLocationException("Illegal offset for this view", pos); Rectangle r = a.getBounds(); Icon icon = StyleConstants.getIcon(el.getAttributes()); return new Rectangle(r.x, r.y, icon.getIconWidth(), icon.getIconHeight()); diff --git a/libjava/classpath/javax/swing/text/JTextComponent.java b/libjava/classpath/javax/swing/text/JTextComponent.java index afa1f24a6a4..6b8348ceaf5 100644 --- a/libjava/classpath/javax/swing/text/JTextComponent.java +++ b/libjava/classpath/javax/swing/text/JTextComponent.java @@ -60,7 +60,9 @@ import java.util.Enumeration; import java.util.Hashtable; import javax.accessibility.Accessible; +import javax.accessibility.AccessibleAction; import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleEditableText; import javax.accessibility.AccessibleRole; import javax.accessibility.AccessibleStateSet; import javax.accessibility.AccessibleText; @@ -86,200 +88,415 @@ public abstract class JTextComponent extends JComponent implements Scrollable, Accessible { /** - * AccessibleJTextComponent + * This class implements accessibility support for the JTextComponent class. + * It provides an implementation of the Java Accessibility API appropriate + * to menu user-interface elements. */ - // FIXME: This inner class is a complete stub and needs to be implemented. - public class AccessibleJTextComponent extends AccessibleJComponent - implements AccessibleText, CaretListener, DocumentListener + public class AccessibleJTextComponent extends AccessibleJComponent implements + AccessibleText, CaretListener, DocumentListener, AccessibleAction, + AccessibleEditableText { private static final long serialVersionUID = 7664188944091413696L; + /** The caret's offset. */ + int dot = 0; + + /** The current JTextComponent. */ + JTextComponent textComp = JTextComponent.this; + /** - * Constructor AccessibleJTextComponent + * Constructs an AccessibleJTextComponent. + * Adds a listener to track caret change. */ public AccessibleJTextComponent() { - // Nothing to do here. + super(); + textComp.addCaretListener(this); } /** - * getCaretPosition - * @return int + * 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 */ public int getCaretPosition() { - return 0; // TODO + dot = textComp.getCaretPosition(); + return dot; } /** - * getSelectedText - * @return String + * Returns the portion of the text that is selected. + * + * @return null if no text is selected. */ public String getSelectedText() { - return null; // TODO + return textComp.getSelectedText(); } /** - * getSelectionStart - * @return int + * 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. */ public int getSelectionStart() { - return 0; // TODO + if (getSelectedText() == null || (textComp.getText().equals(""))) + return 0; + return textComp.getSelectionStart(); } /** - * getSelectionEnd - * @return int + * 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. */ public int getSelectionEnd() { - return 0; // TODO + if (getSelectedText() == null || (textComp.getText().equals(""))) + return 0; + return textComp.getSelectionEnd(); } /** - * caretUpdate - * @param value0 TODO + * 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 */ - public void caretUpdate(CaretEvent value0) + public void caretUpdate(CaretEvent e) { - // TODO + // TODO: fire appropriate event. + dot = e.getDot(); } /** - * getAccessibleStateSet - * @return AccessibleStateSet + * Returns the accessible state set of this component. + * + * @return the accessible state set of this component */ public AccessibleStateSet getAccessibleStateSet() { - return null; // TODO + AccessibleStateSet state = super.getAccessibleStateSet(); + // TODO: Figure out what state must be added here to the super's state. + return state; } /** - * getAccessibleRole - * @return AccessibleRole + * Returns the accessible role of this component. + * + * @return the accessible role of this component + * + * @see AccessibleRole */ public AccessibleRole getAccessibleRole() { - return null; // TODO + return AccessibleRole.TEXT; } /** - * getAccessibleText - * @return AccessibleText + * Returns the AccessibleEditableText interface for 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. + * + * @return this + * + * @see AccessibleText */ public AccessibleText getAccessibleText() { - return null; // TODO + return this; } - + /** - * insertUpdate - * @param value0 TODO + * Insert update. Fire appropriate property change event which + * is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY. + * + * @param e - document event */ - public void insertUpdate(DocumentEvent value0) + public void insertUpdate(DocumentEvent e) { // TODO } /** - * removeUpdate - * @param value0 TODO + * Remove update. Fire appropriate property change event which + * is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY. + * + * @param e - document event */ - public void removeUpdate(DocumentEvent value0) + public void removeUpdate(DocumentEvent e) { // TODO } /** - * changedUpdate - * @param value0 TODO + * Changed update. Fire appropriate property change event which + * is AccessibleContext.ACCESSIBLE_TEXT_PROPERTY. + * + * @param e - document event */ - public void changedUpdate(DocumentEvent value0) + public void changedUpdate(DocumentEvent e) { // TODO } /** - * getIndexAtPoint - * @param value0 TODO - * @return int + * 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. + * + * @param p the point to look at + * @return the character index, or -1 */ - public int getIndexAtPoint(Point value0) + public int getIndexAtPoint(Point p) { return 0; // TODO } /** - * getRootEditorRect - * @return Rectangle + * 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. + * + * @param index the 0-based character index + * @return the bounding box, may be empty or null. */ - Rectangle getRootEditorRect() + public Rectangle getCharacterBounds(int index) { - return null; + return null; // TODO } /** - * getCharacterBounds - * @param value0 TODO - * @return Rectangle + * Return the number of characters. + * + * @return the character count */ - public Rectangle getCharacterBounds(int value0) + public int getCharCount() + { + return textComp.getText().length(); + } + + /** + * Returns the attributes of a character at an index, or null if the index + * is out of bounds. + * + * @param index the 0-based character index + * @return the character's attributes + */ + public AttributeSet getCharacterAttribute(int index) { return null; // TODO } /** - * getCharCount - * @return int + * 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 */ - public int getCharCount() + public String getAtIndex(int part, int index) { - return 0; // TODO + return null; // TODO } /** - * getCharacterAttribute - * @param value0 TODO - * @return AttributeSet + * 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 */ - public AttributeSet getCharacterAttribute(int value0) + public String getAfterIndex(int part, int index) { return null; // TODO } /** - * getAtIndex - * @param value0 TODO - * @param value1 TODO - * @return String + * 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 */ - public String getAtIndex(int value0, int value1) + public String getBeforeIndex(int part, int index) { return null; // TODO } + + /** + * Get the number possible actions for this object, with the zeroth + * representing the default action. + * + * @return the 0-based number of actions + */ + public int getAccessibleActionCount() + { + return 0; // TODO + } + + /** + * Get a description for the specified action. Returns null if out of + * bounds. + * + * @param i + * the action to describe, 0-based + * @return description of the action + */ + public String getAccessibleActionDescription(int i) + { + // TODO: Not implemented fully + return super.getAccessibleDescription(); + } + + /** + * Perform the specified action. Does nothing if out of bounds. + * + * @param i the action to perform, 0-based + * @return true if the action was performed + */ + public boolean doAccessibleAction(int i) + { + return false; // TODO + } + + /** + * Set the text contents to the given string. + * + * @param s the new text + */ + public void setTextContents(String s) + { + // TODO + } /** - * getAfterIndex - * @param value0 TODO - * @param value1 TODO - * @return String + * Inserts the given string at the specified location. + * + * @param index the index for insertion + * @param s the new text */ - public String getAfterIndex(int value0, int value1) + public void insertTextAtIndex(int index, String s) { - return null; // TODO + replaceText(index, index, s); } /** - * getBeforeIndex - * @param value0 TODO - * @param value1 TODO - * @return String + * Return the text between two points. + * + * @param start the start position, inclusive + * @param end the end position, exclusive */ - public String getBeforeIndex(int value0, int value1) + public String getTextRange(int start, int end) { - return null; // TODO + try + { + return textComp.getText(start, end - start); + } + catch (BadLocationException ble) + { + return ""; + } + } + + /** + * Delete the text between two points. + * + * @param start the start position, inclusive + * @param end the end position, exclusive + */ + public void delete(int start, int end) + { + replaceText(start, end, ""); + } + + /** + * Cut the text between two points to the system clipboard. + * + * @param start the start position, inclusive + * @param end the end position, exclusive + */ + public void cut(int start, int end) + { + textComp.select(start, end); + textComp.cut(); + } + + /** + * Paste the text from the system clipboard at the given index. + * + * @param start the start position + */ + public void paste(int start) + { + textComp.setCaretPosition(start); + textComp.paste(); + } + + /** + * Replace the text between two points with the given string. + * + * @param start the start position, inclusive + * @param end the end position, exclusive + * @param s the string to paste + */ + public void replaceText(int start, int end, String s) + { + textComp.select(start, end); + textComp.replaceSelection(s); + } + + /** + * Select the text between two points. + * + * @param start the start position, inclusive + * @param end the end position, exclusive + */ + public void selectText(int start, int end) + { + textComp.select(start, end); + } + + /** + * Set the attributes of text between two points. + * + * @param start the start position, inclusive + * @param end the end position, exclusive + * @param s the new attribute set for the range + */ + public void setAttributes(int start, int end, AttributeSet s) + { + // TODO } } @@ -945,7 +1162,7 @@ public abstract class JTextComponent extends JComponent */ public AccessibleContext getAccessibleContext() { - return null; + return new AccessibleJTextComponent(); } public void setMargin(Insets m) @@ -1024,9 +1241,15 @@ public abstract class JTextComponent extends JComponent */ public String getSelectedText() { + int start = getSelectionStart(); + int offset = getSelectionEnd() - start; + + if (offset <= 0) + return null; + try { - return doc.getText(getSelectionStart(), getSelectionEnd()); + return doc.getText(start, offset); } catch (BadLocationException e) { @@ -1375,8 +1598,12 @@ public abstract class JTextComponent extends JComponent // Insert new text. doc.insertString(start, content, null); - // Set dot to new position. - setCaretPosition(start + content.length()); + // Set dot to new position, + dot = start + content.length(); + setCaretPosition(dot); + + // and update it's magic position. + caret.setMagicCaretPosition(modelToView(dot).getLocation()); } catch (BadLocationException e) { @@ -1387,7 +1614,7 @@ public abstract class JTextComponent extends JComponent public boolean getScrollableTracksViewportHeight() { if (getParent() instanceof JViewport) - return ((JViewport) getParent()).getHeight() > getPreferredSize().height; + return getParent().getHeight() > getPreferredSize().height; return false; } @@ -1395,7 +1622,7 @@ public abstract class JTextComponent extends JComponent public boolean getScrollableTracksViewportWidth() { if (getParent() instanceof JViewport) - return ((JViewport) getParent()).getWidth() > getPreferredSize().width; + return getParent().getWidth() > getPreferredSize().width; return false; } diff --git a/libjava/classpath/javax/swing/text/LabelView.java b/libjava/classpath/javax/swing/text/LabelView.java index 4890735b925..03279c4b2b5 100644 --- a/libjava/classpath/javax/swing/text/LabelView.java +++ b/libjava/classpath/javax/swing/text/LabelView.java @@ -109,7 +109,11 @@ public class LabelView extends GlyphView { Element el = getElement(); AttributeSet atts = el.getAttributes(); - background = StyleConstants.getBackground(atts); + // We cannot use StyleConstants.getBackground() here, because that returns + // BLACK as default (when background == null). What we need is the + // background setting of the text component instead, which is what we get + // when background == null anyway. + background = (Color) atts.getAttribute(StyleConstants.Background); foreground = StyleConstants.getForeground(atts); strikeThrough = StyleConstants.isStrikeThrough(atts); subscript = StyleConstants.isSubscript(atts); diff --git a/libjava/classpath/javax/swing/text/MutableAttributeSet.java b/libjava/classpath/javax/swing/text/MutableAttributeSet.java index 2fe9ad50f67..3728b9ce126 100644 --- a/libjava/classpath/javax/swing/text/MutableAttributeSet.java +++ b/libjava/classpath/javax/swing/text/MutableAttributeSet.java @@ -1,5 +1,5 @@ /* MutableAttributeSet.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. @@ -40,46 +40,78 @@ package javax.swing.text; import java.util.Enumeration; /** - * MutableAttributeSet + * An {@link AttributeSet} that supports modification of the stored + * attributes. + * * @author Andrew Selkirk - * @version 1.0 + * @since 1.2 */ public interface MutableAttributeSet extends AttributeSet { /** - * addAttribute - * @param name TODO - * @param value TODO + * Adds an attribute with the given <code>name</code> and <code>value</code> + * to the set. If the set already contains an attribute with the given + * <code>name</code>, the attribute value is updated. + * + * @param name the attribute name (<code>null</code> not permitted). + * @param value the value (<code>null</code> not permitted). + * + * @throws NullPointerException if either argument is <code>null</code>. */ void addAttribute(Object name, Object value); /** - * addAttributes - * @param attributes TODO + * Adds all the attributes from <code>attributes</code> to this set. + * + * @param attributes the set of attributes to add (<code>null</code> not + * permitted). + * + * @throws NullPointerException if <code>attributes</code> is + * <code>null</code>. */ void addAttributes(AttributeSet attributes); /** - * removeAttribute - * @param name TODO + * Removes the attribute with the specified <code>name</code>, if this + * attribute is defined. This method will only remove an attribute from + * this set, not from the resolving parent. + * + * @param name the attribute name (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>name</code> is <code>null</code>. */ void removeAttribute(Object name); /** - * removeAttributes - * @param names TODO + * Removes the attributes listed in <code>names</code>. + * + * @param names the attribute names (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>names</code> is <code>null</code> + * or contains any <code>null</code> values. */ void removeAttributes(Enumeration names); /** - * removeAttributes - * @param attributes TODO + * Removes attributes from this set if they are found in the + * given set. Only attributes whose key AND value are removed. + * Removes attributes only from this set, not from the resolving parent. + * Since the resolving parent is stored as an attribute, if + * <code>attributes</code> has the same resolving parent as this set, the + * parent will be removed from this set. + * + * @param attributes the attributes (<code>null</code> not permitted). */ void removeAttributes(AttributeSet attributes); /** - * setResolveParent - * @param parent TODO + * Sets the reolving parent for this set. When looking up an attribute, if + * it is not found in this set, then the resolving parent is also used for + * the lookup. + * + * @param parent the parent attribute set (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>parent</code> is <code>null</code>. */ void setResolveParent(AttributeSet parent); } diff --git a/libjava/classpath/javax/swing/text/NavigationFilter.java b/libjava/classpath/javax/swing/text/NavigationFilter.java index 45f58f9e229..ea9b29db837 100644 --- a/libjava/classpath/javax/swing/text/NavigationFilter.java +++ b/libjava/classpath/javax/swing/text/NavigationFilter.java @@ -68,4 +68,31 @@ public class NavigationFilter { fb.setDot(dot, bias); } + + /** + * Returns the next visual position in the specified direction at which one + * would place a caret. The default implementation forwards to the text + * component's root view. Subclasses may wish to restrict that more. + * + * @param c the text component + * @param pos the current model position + * @param bias the bias of <code>pos</code> + * @param dir the direction, one of {@link javax.swing.SwingConstants#NORTH}, + * {@link javax.swing.SwingConstants#SOUTH}, + * {@link javax.swing.SwingConstants#WEST} or + * {@link javax.swing.SwingConstants#EAST} + * @param retBias the bias of the returned position + * + * @return the next model location to place the caret + * + * @throws BadLocationException when <code>pos</code> is not a valid model + * position + */ + public int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias bias, int dir, + Position.Bias[] retBias) + throws BadLocationException + { + return c.getUI().getNextVisualPositionFrom(c, pos, bias, dir, retBias); + } } diff --git a/libjava/classpath/javax/swing/text/ParagraphView.java b/libjava/classpath/javax/swing/text/ParagraphView.java index c4864503187..15bed781825 100644 --- a/libjava/classpath/javax/swing/text/ParagraphView.java +++ b/libjava/classpath/javax/swing/text/ParagraphView.java @@ -63,10 +63,20 @@ public class ParagraphView extends FlowView implements TabExpander { super(el, X_AXIS); } + public float getAlignment(int axis) { - // FIXME: This is very likely not 100% correct. Work this out. - return 0.0F; + float align; + if (axis == X_AXIS) + align = 0.0F; // TODO: Implement according to justification + else + align = super.getAlignment(axis); + return align; + } + + protected void loadChildren(ViewFactory vf) + { + // Do nothing here. The children are added while layouting. } } @@ -128,17 +138,18 @@ public class ParagraphView extends FlowView implements TabExpander */ public float getAlignment(int axis) { + float align; if (axis == X_AXIS) - return 0.0F; + align = super.getAlignment(axis); else if (getViewCount() > 0) { - float prefHeight = getPreferredSpan(Y_AXIS); float firstRowHeight = getView(0).getPreferredSpan(Y_AXIS); - return (firstRowHeight / 2.F) / prefHeight; + align = (firstRowHeight / 2.F) / prefHeight; } else - return 0.0F; + align = 0.0F; + return align; } /** @@ -229,4 +240,184 @@ public class ParagraphView extends FlowView implements TabExpander { return tabSet; } + + /** + * Finds the next offset in the document that has one of the characters + * specified in <code>string</code>. If there is no such character found, + * this returns -1. + * + * @param string the characters to search for + * @param start the start offset + * + * @return the next offset in the document that has one of the characters + * specified in <code>string</code> + */ + protected int findOffsetToCharactersInString(char[] string, int start) + { + int offset = -1; + Document doc = getDocument(); + Segment text = new Segment(); + try + { + doc.getText(start, doc.getLength() - start, text); + int index = start; + + searchLoop: + while (true) + { + char ch = text.next(); + if (ch == Segment.DONE) + break; + + for (int j = 0; j < string.length; ++j) + { + if (string[j] == ch) + { + offset = index; + break searchLoop; + } + } + index++; + } + } + catch (BadLocationException ex) + { + // Ignore this and return -1. + } + return offset; + } + + protected int getClosestPositionTo(int pos, Position.Bias bias, Shape a, + int direction, Position.Bias[] biasRet, + int rowIndex, int x) + throws BadLocationException + { + // FIXME: Implement this properly. However, this looks like it might + // have been replaced by viewToModel. + return pos; + } + + /** + * Returns the size that is used by this view (or it's child views) between + * <code>startOffset</code> and <code>endOffset</code>. If the child views + * implement the {@link TabableView} interface, then this is used to + * determine the span, otherwise we use the preferred span of the child + * views. + * + * @param startOffset the start offset + * @param endOffset the end offset + * + * @return the span used by the view between <code>startOffset</code> and + * <code>endOffset</cod> + */ + protected float getPartialSize(int startOffset, int endOffset) + { + int startIndex = getViewIndex(startOffset, Position.Bias.Backward); + int endIndex = getViewIndex(endOffset, Position.Bias.Forward); + float span; + if (startIndex == endIndex) + { + View child = getView(startIndex); + if (child instanceof TabableView) + { + TabableView tabable = (TabableView) child; + span = tabable.getPartialSpan(startOffset, endOffset); + } + else + span = child.getPreferredSpan(X_AXIS); + } + else if (endIndex - startIndex == 1) + { + View child1 = getView(startIndex); + if (child1 instanceof TabableView) + { + TabableView tabable = (TabableView) child1; + span = tabable.getPartialSpan(startOffset, child1.getEndOffset()); + } + else + span = child1.getPreferredSpan(X_AXIS); + View child2 = getView(endIndex); + if (child2 instanceof TabableView) + { + TabableView tabable = (TabableView) child2; + span += tabable.getPartialSpan(child2.getStartOffset(), endOffset); + } + else + span += child2.getPreferredSpan(X_AXIS); + } + else + { + // Start with the first view. + View child1 = getView(startIndex); + if (child1 instanceof TabableView) + { + TabableView tabable = (TabableView) child1; + span = tabable.getPartialSpan(startOffset, child1.getEndOffset()); + } + else + span = child1.getPreferredSpan(X_AXIS); + + // Add up the view spans between the start and the end view. + for (int i = startIndex + 1; i < endIndex; i++) + { + View child = getView(i); + span += child.getPreferredSpan(X_AXIS); + } + + // Add the span of the last view. + View child2 = getView(endIndex); + if (child2 instanceof TabableView) + { + TabableView tabable = (TabableView) child2; + span += tabable.getPartialSpan(child2.getStartOffset(), endOffset); + } + else + span += child2.getPreferredSpan(X_AXIS); + } + return span; + } + + /** + * Returns the location where the tabs are calculated from. This returns + * <code>0.0F</code> by default. + * + * @return the location where the tabs are calculated from + */ + protected float getTabBase() + { + return 0.0F; + } + + /** + * @specnote This method is specified to take a Row parameter, which is a + * private inner class of that class, which makes it unusable from + * application code. Also, this method seems to be replaced by + * {@link FlowStrategy#adjustRow(FlowView, int, int, int)}. + * + */ + protected void adjustRow(Row r, int desiredSpan, int x) + { + } + + /** + * @specnote This method's signature differs from the one defined in + * {@link View} and is therefore never called. It is probably there + * for historical reasons. + */ + public View breakView(int axis, float len, Shape a) + { + // This method is not used. + return null; + } + + /** + * @specnote This method's signature differs from the one defined in + * {@link View} and is therefore never called. It is probably there + * for historical reasons. + */ + public int getBreakWeight(int axis, float len) + { + // This method is not used. + return 0; + } } diff --git a/libjava/classpath/javax/swing/text/PasswordView.java b/libjava/classpath/javax/swing/text/PasswordView.java index e54331c5a8e..9d4c86a8388 100644 --- a/libjava/classpath/javax/swing/text/PasswordView.java +++ b/libjava/classpath/javax/swing/text/PasswordView.java @@ -107,8 +107,6 @@ public class PasswordView protected int drawSelectedText(Graphics g, int x, int y, int p0, int p1) throws BadLocationException { - // FIXME: Throw BadLocationException somehow. - // Update font metrics. updateMetrics(); @@ -119,25 +117,18 @@ public class PasswordView g.setColor(selectedColor); g.setColor(Color.BLACK); - // Initialize buffer for faster drawing of all characters. - int len = p1 - p0; - char[] buffer = new char[len]; - for (int index = 0; index < len; ++index) - buffer[index] = ch; - - // Draw echo charaters. - g.drawChars(buffer, 0, len, x, y); - - // Return new x position right of all drawn characters. - return x + len * metrics.charWidth(ch); + // Draw echo character using drawEchoCharacter() method. + for (int index = p0; index < p1; ++index) + x = drawEchoCharacter(g, x, y, ch); + return x; } /** * Draws unselected text at a given position. * * @param g the <code>Graphics</code> object to draw to - * @param x the x-position - * @param y the y-position + * @param x the x-position of the start of the baseline + * @param y the y-position of the start of the baseline * @param p0 the position of the first character to draw * @param p1 the position of the first character not to draw * @@ -146,35 +137,20 @@ public class PasswordView protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1) throws BadLocationException { - // FIXME: Throw BadLocationException somehow. - // Update font metrics. updateMetrics(); // Get echo character. char ch = getEchoChar(); - Segment segment = new Segment(); // Set color for unselected text. g.setColor(unselectedColor); g.setColor(Color.BLACK); - // Initialize buffer for faster drawing of all characters. - p1--; - getDocument().getText(p0, p1 - p0, segment); - int len = segment.toString().length(); - - char[] buffer = new char[len]; - for (int index = 0; index < len; ++index) - buffer[index] = ch; - - y += getPreferredSpan(Y_AXIS)/2; - - // Draw echo charaters. - g.drawChars(buffer, 0, len, x, y); - - // Return new x position right of all drawn characters. - return x + (len * metrics.charWidth(ch)); + // Draw echo character using drawEchoCharacter() method. + for (int index = p0; index < p1; ++index) + x = drawEchoCharacter(g, x, y, ch); + return x; } /** diff --git a/libjava/classpath/javax/swing/text/PlainDocument.java b/libjava/classpath/javax/swing/text/PlainDocument.java index 2200cae80a0..c699dcad2aa 100644 --- a/libjava/classpath/javax/swing/text/PlainDocument.java +++ b/libjava/classpath/javax/swing/text/PlainDocument.java @@ -1,5 +1,5 @@ /* PlainDocument.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. @@ -40,6 +40,15 @@ package javax.swing.text; import java.util.ArrayList; +/** + * A simple document class which maps lines to {@link Element}s. + * + * @author Anthony Balkissoon (abalkiss@redhat.com) + * @author Graydon Hoare (graydon@redhat.com) + * @author Roman Kennke (roman@kennke.org) + * @author Michael Koch (konqueror@gmx.de) + * @author Robert Schuster (robertschuster@fsfe.org) + */ public class PlainDocument extends AbstractDocument { private static final long serialVersionUID = 4758290289196893664L; @@ -109,18 +118,21 @@ public class PlainDocument extends AbstractDocument AttributeSet attributes) { int offset = event.getOffset(); + int eventLength = event.getLength(); int end = offset + event.getLength(); - int elementIndex = rootElement.getElementIndex(offset); + 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. - if (offset > 0) + // Element structure (but only if we are dealing with a line which + // has not existed as Element before). + if (offset > 0 && firstElement.getStartOffset() != offset) { try { String s = getText(offset - 1, 1); - if (s.equals("\n")) + if (s.equals("\n") ) { int newEl2EndOffset = end; boolean replaceNext = false; @@ -159,33 +171,43 @@ public class PlainDocument extends AbstractDocument Element[] added; try { - String str = content.getString(0, content.length()); + String str = content.getString(offset, eventLength); ArrayList elts = new ArrayList(); // 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', offset); - while (i != -1 && i <= end) + 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, i + 1)); - j = i + 1; - if (j >= str.length()) - break; - i = str.indexOf('\n', j); + j, offset + i + 1)); + + j = offset + i + 1; + if (j >= contentLength) + break; + i = str.indexOf('\n', i + 1); } + // 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 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()]; - for (int k = 0; k < elts.size(); ++k) - added[k] = (Element) elts.get(k); + elts.toArray(added); + removed[0] = firstElement; // Now create and add the ElementEdit @@ -204,6 +226,7 @@ public class PlainDocument extends AbstractDocument ae.initCause(e); throw ae; } + super.insertUpdate(event, attributes); } diff --git a/libjava/classpath/javax/swing/text/PlainView.java b/libjava/classpath/javax/swing/text/PlainView.java index a318ee71ae0..4bb3a8eda33 100644 --- a/libjava/classpath/javax/swing/text/PlainView.java +++ b/libjava/classpath/javax/swing/text/PlainView.java @@ -1,5 +1,5 @@ /* PlainView.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. @@ -46,7 +46,7 @@ import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; -import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent.ElementChange; @@ -137,22 +137,32 @@ public class PlainView extends View implements TabExpander return rect; } + /** + * Draws a line of text. The X and Y coordinates specify the start of + * the <em>baseline</em> of the line. + * + * @param lineIndex the index of the line + * @param g the graphics to use for drawing the text + * @param x the X coordinate of the baseline + * @param y the Y coordinate of the baseline + */ protected void drawLine(int lineIndex, Graphics g, int x, int y) { try { - metrics = g.getFontMetrics(); - // FIXME: Selected text are not drawn yet. - Element line = getElement().getElement(lineIndex); - drawUnselectedText(g, x, y, line.getStartOffset(), line.getEndOffset()); - //drawSelectedText(g, , , , ); + metrics = g.getFontMetrics(); + // FIXME: Selected text are not drawn yet. + Element line = getElement().getElement(lineIndex); + drawUnselectedText(g, x, y, line.getStartOffset(), + line.getEndOffset() - 1); + //drawSelectedText(g, , , , ); } catch (BadLocationException e) - { - AssertionError ae = new AssertionError("Unexpected bad location"); - ae.initCause(e); - throw ae; - } + { + AssertionError ae = new AssertionError("Unexpected bad location"); + ae.initCause(e); + throw ae; + } } protected int drawSelectedText(Graphics g, int x, int y, int p0, int p1) @@ -164,6 +174,20 @@ public class PlainView extends View implements TabExpander return Utilities.drawTabbedText(segment, x, y, g, this, 0); } + /** + * Draws a chunk of unselected text. + * + * @param g the graphics to use for drawing the text + * @param x the X coordinate of the baseline + * @param y the Y coordinate of the baseline + * @param p0 the start position in the text model + * @param p1 the end position in the text model + * + * @return the X location of the end of the range + * + * @throws BadLocationException if <code>p0</code> or <code>p1</code> are + * invalid + */ protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1) throws BadLocationException { @@ -194,12 +218,12 @@ public class PlainView extends View implements TabExpander // FIXME: Text may be scrolled. Document document = textComponent.getDocument(); Element root = document.getDefaultRootElement(); - int y = rect.y; + int y = rect.y + metrics.getAscent(); for (int i = 0; i < root.getElementCount(); i++) { - drawLine(i, g, rect.x, y); - y += metrics.getHeight(); + drawLine(i, g, rect.x, y); + y += metrics.getHeight(); } } @@ -284,18 +308,20 @@ public class PlainView extends View implements TabExpander // make sure we have the metrics updateMetrics(); - float span = 0; Element el = getElement(); + float span; switch (axis) { case X_AXIS: span = determineMaxLineLength(); + break; case Y_AXIS: default: span = metrics.getHeight() * el.getElementCount(); break; } + return span; } @@ -318,12 +344,19 @@ public class PlainView extends View implements TabExpander Element root = doc.getDefaultRootElement(); // PlainView doesn't support line-wrapping so we can find out which - // Element was clicked on just by the y-position - int lineClicked = (int) (y - rec.y) / metrics.getHeight(); - if (lineClicked >= root.getElementCount()) - return getEndOffset() - 1; + // Element was clicked on just by the y-position. + // Since the coordinates may be outside of the coordinate space + // of the allocation area (e.g. user dragged mouse outside + // the component) we have to limit the values. + // This has the nice effect that the user can drag the + // mouse above or below the component and it will still + // react to the x values (e.g. when selecting). + int lineClicked + = Math.min(Math.max((int) (y - rec.y) / metrics.getHeight(), 0), + root.getElementCount() - 1); Element line = root.getElement(lineClicked); + Segment s = getLineBuffer(); int start = line.getStartOffset(); // We don't want the \n at the end of the line. @@ -353,6 +386,8 @@ public class PlainView extends View implements TabExpander */ protected void updateDamage(DocumentEvent changes, Shape a, ViewFactory f) { + float oldMaxLineLength = maxLineLength; + Rectangle alloc = a.getBounds(); Element el = getElement(); ElementChange ec = changes.getChange(el); @@ -360,7 +395,19 @@ public class PlainView extends View implements TabExpander // repaint the changed line if (ec == null) { - int line = getElement().getElementIndex(changes.getOffset()); + int line = el.getElementIndex(changes.getOffset()); + + // If characters have been removed from the current longest line + // we have to find out which one is the longest now otherwise + // the preferred x-axis span will not shrink. + if (changes.getType() == DocumentEvent.EventType.REMOVE + && el.getElement(line) == longestLine) + { + maxLineLength = -1; + if (determineMaxLineLength() != alloc.width) + preferenceChanged(this, true, false); + } + damageLineRange(line, line, a, getContainer()); return; } @@ -373,12 +420,13 @@ public class PlainView extends View implements TabExpander if (removed == null && newElements == null) { int line = getElement().getElementIndex(changes.getOffset()); + damageLineRange(line, line, a, getContainer()); return; } // Check to see if we removed the longest line, if so we have to - // search through all lines and find the longest one again + // search through all lines and find the longest one again. if (removed != null) { for (int i = 0; i < removed.length; i++) @@ -386,8 +434,11 @@ public class PlainView extends View implements TabExpander { // reset maxLineLength and search through all lines for longest one maxLineLength = -1; - determineMaxLineLength(); + if (determineMaxLineLength() != alloc.width) + preferenceChanged(this, true, removed.length != newElements.length); + ((JTextComponent)getContainer()).repaint(); + return; } } @@ -397,6 +448,7 @@ public class PlainView extends View implements TabExpander { // No lines were added, just repaint the container and exit ((JTextComponent)getContainer()).repaint(); + return; } @@ -445,6 +497,14 @@ public class PlainView extends View implements TabExpander maxLineLength = longestNewLength; longestLine = longestNewLine; } + + // Report any changes to the preferred sizes of the view + // which may cause the underlying component to be revalidated. + boolean widthChanged = oldMaxLineLength != maxLineLength; + boolean heightChanged = removed.length != newElements.length; + if (widthChanged || heightChanged) + preferenceChanged(this, widthChanged, heightChanged); + // Repaint the container ((JTextComponent)getContainer()).repaint(); } @@ -511,7 +571,9 @@ public class PlainView extends View implements TabExpander host.repaint(); else { - Rectangle repaintRec = rec0.union(rec1); + Rectangle repaintRec = SwingUtilities.computeUnion(rec0.x, rec0.y, + rec0.width, + rec0.height, rec1); host.repaint(repaintRec.x, repaintRec.y, repaintRec.width, repaintRec.height); } diff --git a/libjava/classpath/javax/swing/text/Segment.java b/libjava/classpath/javax/swing/text/Segment.java index 84e0e700f2e..875d9966c1f 100644 --- a/libjava/classpath/javax/swing/text/Segment.java +++ b/libjava/classpath/javax/swing/text/Segment.java @@ -1,5 +1,5 @@ /* Segment.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. @@ -39,20 +39,40 @@ package javax.swing.text; import java.text.CharacterIterator; +/** + * A text fragment represented by a sequence of characters stored in an array. + */ public class Segment implements Cloneable, CharacterIterator { private boolean partialReturn; + + /** The current index. */ private int current; + /** Storage for the characters (may contain additional characters). */ public char[] array; + + /** The number of characters in the segment. */ public int count; + + /** The offset of the first character in the segment. */ public int offset; + /** + * Creates a new <code>Segment</code>. + */ public Segment() { // Nothing to do here. } + /** + * Creates a new <code>Segment</code>. + * + * @param array the underlying character data. + * @param offset the offset of the first character in the segment. + * @param count the number of characters in the segment. + */ public Segment(char[] array, int offset, int count) { this.array = array; @@ -60,6 +80,12 @@ public class Segment implements Cloneable, CharacterIterator this.count = count; } + /** + * Clones the segment (note that the underlying character array is not cloned, + * just the reference to it). + * + * @return A clone of the segment. + */ public Object clone() { try @@ -72,6 +98,13 @@ public class Segment implements Cloneable, CharacterIterator } } + /** + * Returns the character at the current index. If the segment consists of + * zero characters, or the current index has passed the end of the + * characters in the segment, this method returns {@link #DONE}. + * + * @return The character at the current index. + */ public char current() { if (count == 0 @@ -81,6 +114,14 @@ public class Segment implements Cloneable, CharacterIterator return array[current]; } + /** + * Sets the current index to the first character in the segment and returns + * that character. If the segment contains zero characters, this method + * returns {@link #DONE}. + * + * @return The first character in the segment, or {@link #DONE} if the + * segment contains zero characters. + */ public char first() { if (count == 0) @@ -90,21 +131,46 @@ public class Segment implements Cloneable, CharacterIterator return array[current]; } + /** + * Returns the index of the first character in the segment. + * + * @return The index of the first character. + */ public int getBeginIndex() { return offset; } + /** + * Returns the end index for the segment (one position beyond the last + * character in the segment - note that this can be outside the range of the + * underlying character array). + * + * @return The end index for the segment. + */ public int getEndIndex() { return offset + count; } + /** + * Returns the index of the current character in the segment. + * + * @return The index of the current character. + */ public int getIndex() { return current; } + /** + * 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}. + * + * @return The last character in the segment, or {@link #DONE} if the + * segment contains zero characters. + */ public char last() { if (count == 0) @@ -114,6 +180,17 @@ public class Segment implements Cloneable, CharacterIterator return array[current]; } + /** + * Sets the current index to point to the next character in the segment and + * returns that character. If the next character position is past the end of + * the segment, the index is set to {@link #getEndIndex()} and the method + * returns {@link #DONE}. If the segment contains zero characters, this + * method returns {@link #DONE}. + * + * @return The next character in the segment or {@link #DONE} (if the next + * character position is past the end of the segment or if the + * segment contains zero characters). + */ public char next() { if (count == 0) @@ -129,6 +206,16 @@ public class Segment implements Cloneable, CharacterIterator return array[current]; } + /** + * Sets the current index to point to the previous character in the segment + * and returns that character. If the current index is equal to + * {@link #getBeginIndex()}, or if the segment contains zero characters, this + * method returns {@link #DONE}. + * + * @return The previous character in the segment or {@link #DONE} (if the + * current character position is at the beginning of the segment or + * if the segment contains zero characters). + */ public char previous() { if (count == 0 @@ -139,11 +226,26 @@ public class Segment implements Cloneable, CharacterIterator return array[current]; } + /** + * Sets the current index and returns the character at that position (or + * {@link #DONE} if the index is equal to {@link #getEndIndex()}. + * + * @param position the current position. + * + * @return The character at the specified <code>position</code>, or + * {@link #DONE} if <code>position</code> is equal to + * {@link #getEndIndex()}. + * + * @throws IllegalArgumentException if <code>position</code> is not in the + * range {@link #getBeginIndex()} to {@link #getEndIndex()}. + */ public char setIndex(int position) { if (position < getBeginIndex() || position > getEndIndex()) - throw new IllegalArgumentException(); + throw new IllegalArgumentException("position: " + position + + ", beginIndex: " + getBeginIndex() + + ", endIndex: " + getEndIndex()); current = position; @@ -153,12 +255,23 @@ public class Segment implements Cloneable, CharacterIterator return array[current]; } + /** + * Returns a <code>String</code> containing the same characters as this + * <code>Segment</code>. + * + * @return A <code>String</code> containing the same characters as this + * <code>Segment</code>. + */ public String toString() { return new String(array, offset, count); } /** + * Sets the partial return flag. + * + * @param p the new value of the flag. + * * @since 1.4 */ public void setPartialReturn(boolean p) @@ -167,6 +280,9 @@ public class Segment implements Cloneable, CharacterIterator } /** + * Returns the partial return flag. + * + * @return The partial return flag. * @since 1.4 */ public boolean isPartialReturn() diff --git a/libjava/classpath/javax/swing/text/SimpleAttributeSet.java b/libjava/classpath/javax/swing/text/SimpleAttributeSet.java index 0c9f607b196..8dbcb0c6a14 100644 --- a/libjava/classpath/javax/swing/text/SimpleAttributeSet.java +++ b/libjava/classpath/javax/swing/text/SimpleAttributeSet.java @@ -1,5 +1,5 @@ /* SimpleAttributeSet.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,33 +42,67 @@ import java.io.Serializable; import java.util.Enumeration; import java.util.Hashtable; +/** + * A set of attributes. + */ public class SimpleAttributeSet implements MutableAttributeSet, Serializable, Cloneable { /** The serialization UID (compatible with JDK1.5). */ private static final long serialVersionUID = 8267656273837665219L; + /** An empty attribute set. */ public static final AttributeSet EMPTY = new SimpleAttributeSet(); + /** Storage for the attributes. */ Hashtable tab; + /** + * Creates a new attribute set that is initially empty. + */ public SimpleAttributeSet() { - this(null); + tab = new Hashtable(); } + /** + * Creates a new <code>SimpleAttributeSet</code> with the same attributes + * and resolve parent as the specified set. + * + * @param a the attributes (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>a</code> is <code>null</code>. + */ public SimpleAttributeSet(AttributeSet a) { tab = new Hashtable(); - if (a != null) - addAttributes(a); + addAttributes(a); } + /** + * Adds an attribute with the given <code>name</code> and <code>value</code> + * to the set. If the set already contains an attribute with the given + * <code>name</code>, the attribute value is updated. + * + * @param name the attribute name (<code>null</code> not permitted). + * @param value the value (<code>null</code> not permitted). + * + * @throws NullPointerException if either argument is <code>null</code>. + */ public void addAttribute(Object name, Object value) { tab.put(name, value); } + /** + * Adds all the attributes from <code>attributes</code> to this set. + * + * @param attributes the set of attributes to add (<code>null</code> not + * permitted). + * + * @throws NullPointerException if <code>attributes</code> is + * <code>null</code>. + */ public void addAttributes(AttributeSet attributes) { Enumeration e = attributes.getAttributeNames(); @@ -80,6 +114,11 @@ public class SimpleAttributeSet } } + /** + * Returns a clone of the attribute set. + * + * @return A clone of the attribute set. + */ public Object clone() { SimpleAttributeSet s = new SimpleAttributeSet(); @@ -97,9 +136,18 @@ public class SimpleAttributeSet */ public boolean containsAttribute(Object name, Object value) { - return (tab.containsKey(name) && tab.get(name).equals(value)) || - (getResolveParent() != null && getResolveParent(). - containsAttribute(name, value)); + if (value == null) + throw new NullPointerException("Null 'value' argument."); + if (tab.containsKey(name)) + return tab.get(name).equals(value); + else + { + AttributeSet p = getResolveParent(); + if (p != null) + return p.containsAttribute(name, value); + else + return false; + } } /** @@ -115,6 +163,15 @@ public class SimpleAttributeSet && tab.get(name).equals(value); } + /** + * Returns <code>true</code> of this <code>AttributeSet</code> contains all + * of the specified <code>attributes</code>. + * + * @param attributes the requested attributes + * + * @return <code>true</code> of this <code>AttributeSet</code> contains all + * of the specified <code>attributes</code> + */ public boolean containsAttributes(AttributeSet attributes) { Enumeration e = attributes.getAttributeNames(); @@ -128,11 +185,24 @@ public class SimpleAttributeSet return true; } + /** + * Creates and returns a copy of this <code>AttributeSet</code>. + * + * @return a copy of this <code>AttributeSet</code> + */ public AttributeSet copyAttributes() { return (AttributeSet) clone(); } + /** + * Checks this set for equality with an arbitrary object. + * + * @param obj the object (<code>null</code> permitted). + * + * @return <code>true</code> if this set is equal to <code>obj</code>, and + * <code>false</code> otherwise. + */ public boolean equals(Object obj) { return @@ -140,44 +210,95 @@ public class SimpleAttributeSet && this.isEqual((AttributeSet) obj); } + /** + * Returns the value of the specified attribute, or <code>null</code> if + * there is no attribute with that name. If the attribute is not defined + * directly in this set, the parent hierarchy (if there is one) will be + * used. + * + * @param name the attribute (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>name</code> is <code>null</code>. + */ public Object getAttribute(Object name) { Object val = tab.get(name); if (val != null) return val; - Object p = getResolveParent(); - if (p != null && p instanceof AttributeSet) - return (((AttributeSet)p).getAttribute(name)); + AttributeSet p = getResolveParent(); + if (p != null) + return p.getAttribute(name); return null; } + /** + * Returns the number of attributes stored in this set, plus 1 if a parent + * has been specified (the reference to the parent is stored as a special + * attribute). The attributes stored in the parent do NOT contribute + * to the count. + * + * @return The attribute count. + */ public int getAttributeCount() { return tab.size(); } + /** + * Returns an enumeration of the attribute names. + * + * @return An enumeration of the attribute names. + */ public Enumeration getAttributeNames() { return tab.keys(); } + /** + * Returns the resolving parent. + * + * @return The resolving parent (possibly <code>null</code>). + * + * @see #setResolveParent(AttributeSet) + */ public AttributeSet getResolveParent() { return (AttributeSet) tab.get(ResolveAttribute); } + /** + * Returns a hash code for this instance. + * + * @return A hash code. + */ public int hashCode() { return tab.hashCode(); } + /** + * Returns <code>true</code> if the given attribute is defined in this set, + * and <code>false</code> otherwise. The parent attribute set is not + * checked. + * + * @param attrName the attribute name (<code>null</code> not permitted). + */ public boolean isDefined(Object attrName) { return tab.containsKey(attrName); } + /** + * Returns <code>true</code> if the set contains no attributes, and + * <code>false</code> otherwise. Note that the resolving parent is + * stored as an attribute, so this method will return <code>false</code> if + * a resolving parent is set. + * + * @return <code>true</code> if the set contains no attributes, and + * <code>false</code> otherwise. + */ public boolean isEmpty() { return tab.isEmpty(); @@ -186,7 +307,13 @@ public class SimpleAttributeSet /** * Returns true if the given set has the same number of attributes * as this set and <code>containsAttributes(attr)</code> returns - * true. + * <code>true</code>. + * + * @param attr the attribute set (<code>null</code> not permitted). + * + * @return A boolean. + * + * @throws NullPointerException if <code>attr</code> is <code>null</code>. */ public boolean isEqual(AttributeSet attr) { @@ -194,6 +321,15 @@ public class SimpleAttributeSet && this.containsAttributes(attr); } + /** + * Removes the attribute with the specified <code>name</code>, if this + * attribute is defined. This method will only remove an attribute from + * this set, not from the resolving parent. + * + * @param name the attribute name (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>name</code> is <code>null</code>. + */ public void removeAttribute(Object name) { tab.remove(name); @@ -202,7 +338,12 @@ public class SimpleAttributeSet /** * Removes attributes from this set if they are found in the * given set. Only attributes whose key AND value are removed. - * Removes attributes only from this set, not from the resolving parent. + * Removes attributes only from this set, not from the resolving parent. + * Since the resolving parent is stored as an attribute, if + * <code>attributes</code> has the same resolving parent as this set, the + * parent will be removed from this set. + * + * @param attributes the attributes (<code>null</code> not permitted). */ public void removeAttributes(AttributeSet attributes) { @@ -216,6 +357,14 @@ public class SimpleAttributeSet } } + /** + * Removes the attributes listed in <code>names</code>. + * + * @param names the attribute names (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>names</code> is <code>null</code> + * or contains any <code>null</code> values. + */ public void removeAttributes(Enumeration names) { while (names.hasMoreElements()) @@ -224,11 +373,31 @@ public class SimpleAttributeSet } } + /** + * Sets the reolving parent for this set. When looking up an attribute, if + * it is not found in this set, then the resolving parent is also used for + * the lookup. + * <p> + * Note that the parent is stored as an attribute, and will contribute 1 to + * the count returned by {@link #getAttributeCount()}. + * + * @param parent the parent attribute set (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>parent</code> is <code>null</code>. + * + * @see #setResolveParent(AttributeSet) + */ public void setResolveParent(AttributeSet parent) { addAttribute(ResolveAttribute, parent); } - + + /** + * Returns a string representation of this instance, typically used for + * debugging purposes. + * + * @return A string representation of this instance. + */ public String toString() { return tab.toString(); diff --git a/libjava/classpath/javax/swing/text/StringContent.java b/libjava/classpath/javax/swing/text/StringContent.java index 7db377a1c9b..0a31505f3a6 100644 --- a/libjava/classpath/javax/swing/text/StringContent.java +++ b/libjava/classpath/javax/swing/text/StringContent.java @@ -1,5 +1,5 @@ /* StringContent.java -- - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -54,7 +54,8 @@ import javax.swing.undo.UndoableEdit; * * <p>Do not use this class for large size.</p> */ -public final class StringContent implements AbstractDocument.Content, Serializable +public final class StringContent + implements AbstractDocument.Content, Serializable { /** The serialization UID (compatible with JDK1.5). */ private static final long serialVersionUID = 4755994433709540381L; @@ -87,7 +88,8 @@ public final class StringContent implements AbstractDocument.Content, Serializab try { StringContent.this.checkLocation(this.start, this.length); - this.redoContent = new String(StringContent.this.content, this.start, this.length); + this.redoContent = new String(StringContent.this.content, this.start, + this.length); StringContent.this.remove(this.start, this.length); } catch (BadLocationException b) @@ -175,11 +177,20 @@ public final class StringContent implements AbstractDocument.Content, Serializab } } + /** + * Creates a new instance containing the string "\n". + */ public StringContent() { this(1); } + /** + * Creates a new instance containing the string "\n". + * + * @param initialLength the initial length of the underlying character + * array used to store the content. + */ public StringContent(int initialLength) { super(); @@ -198,7 +209,7 @@ public final class StringContent implements AbstractDocument.Content, Serializab Iterator iter = this.positions.iterator(); while(iter.hasNext()) { - Position p = (Position)iter.next(); + Position p = (Position) iter.next(); if ((offset <= p.getOffset()) && (p.getOffset() <= (offset + length))) refPos.add(p); @@ -206,6 +217,16 @@ public final class StringContent implements AbstractDocument.Content, Serializab return refPos; } + /** + * Creates a position reference for the character at the given offset. The + * position offset will be automatically updated when new characters are + * inserted into or removed from the content. + * + * @param offset the character offset. + * + * @throws BadLocationException if offset is outside the bounds of the + * content. + */ public Position createPosition(int offset) throws BadLocationException { if (offset < this.count || offset > this.count) @@ -215,11 +236,27 @@ public final class StringContent implements AbstractDocument.Content, Serializab return sp; } + /** + * Returns the length of the string content, including the '\n' character at + * the end. + * + * @return The length of the string content. + */ public int length() { return this.count; } + /** + * Inserts <code>str</code> at the given position and returns an + * {@link UndoableEdit} that enables undo/redo support. + * + * @param where the insertion point (must be less than + * <code>length()</code>). + * @param str the string to insert (<code>null</code> not permitted). + * + * @return An object that can undo the insertion. + */ public UndoableEdit insertString(int where, String str) throws BadLocationException { @@ -235,13 +272,15 @@ public final class StringContent implements AbstractDocument.Content, Serializab if (where > 0) System.arraycopy(this.content, 0, temp, 0, where); System.arraycopy(insert, 0, temp, where, insert.length); - System.arraycopy(this.content, where, temp, (where + insert.length), (temp.length - where - insert.length)); + System.arraycopy(this.content, where, temp, (where + insert.length), + (temp.length - where - insert.length)); if (this.content.length < temp.length) this.content = new char[temp.length]; // Copy the result in the original char array. System.arraycopy(temp, 0, this.content, 0, temp.length); // Move all the positions. - Vector refPos = getPositionsInRange(this.positions, where, temp.length - where); + Vector refPos = getPositionsInRange(this.positions, where, + temp.length - where); Iterator iter = refPos.iterator(); while (iter.hasNext()) { @@ -252,20 +291,35 @@ public final class StringContent implements AbstractDocument.Content, Serializab return iundo; } + /** + * Removes the specified range of characters and returns an + * {@link UndoableEdit} that enables undo/redo support. + * + * @param where the starting index. + * @param nitems the number of characters. + * + * @return An object that can undo the removal. + * + * @throws BadLocationException if the character range extends outside the + * bounds of the content OR includes the last character. + */ public UndoableEdit remove(int where, int nitems) throws BadLocationException { - checkLocation(where, nitems); + checkLocation(where, nitems + 1); char[] temp = new char[(this.content.length - nitems)]; this.count = this.count - nitems; - RemoveUndo rundo = new RemoveUndo(where, new String(this.content, where, nitems)); + RemoveUndo rundo = new RemoveUndo(where, new String(this.content, where, + nitems)); // Copy array. System.arraycopy(this.content, 0, temp, 0, where); - System.arraycopy(this.content, where + nitems, temp, where, this.content.length - where - nitems); + System.arraycopy(this.content, where + nitems, temp, where, + this.content.length - where - nitems); this.content = new char[temp.length]; // Then copy the result in the original char array. System.arraycopy(temp, 0, this.content, 0, this.content.length); // Move all the positions. - Vector refPos = getPositionsInRange(this.positions, where, this.content.length + nitems - where); + Vector refPos = getPositionsInRange(this.positions, where, + this.content.length + nitems - where); Iterator iter = refPos.iterator(); while (iter.hasNext()) { @@ -278,31 +332,75 @@ public final class StringContent implements AbstractDocument.Content, Serializab return rundo; } + /** + * Returns a new <code>String</code> containing the characters in the + * specified range. + * + * @param where the start index. + * @param len the number of characters. + * + * @return A string. + * + * @throws BadLocationException if the requested range of characters extends + * outside the bounds of the content. + */ public String getString(int where, int len) throws BadLocationException { checkLocation(where, len); - return new String (this.content, where, len); + return new String(this.content, where, len); } - public void getChars(int where, int len, Segment txt) throws BadLocationException + /** + * Updates <code>txt</code> to contain a direct reference to the underlying + * character array. + * + * @param where the index of the first character. + * @param len the number of characters. + * @param txt a carrier for the return result (<code>null</code> not + * permitted). + * + * @throws BadLocationException if the requested character range is not + * within the bounds of the content. + * @throws NullPointerException if <code>txt</code> is <code>null</code>. + */ + public void getChars(int where, int len, Segment txt) + throws BadLocationException { checkLocation(where, len); - if (txt != null) - { - txt.array = this.content; - txt.offset = where; - txt.count = len; - } + txt.array = this.content; + txt.offset = where; + txt.count = len; } - // This is package-private to avoid an accessor method. + + /** + * @specnote This method is not very well specified and the positions vector + * is implementation specific. The undo positions are managed + * differently in this implementation, this method is only here + * for binary compatibility. + */ + protected void updateUndoPositions(Vector positions) + { + // We do nothing here. + } + + /** + * A utility method that checks the validity of the specified character + * range. + * + * @param where the first character in the range. + * @param len the number of characters in the range. + * + * @throws BadLocationException if the specified range is not within the + * bounds of the content. + */ void checkLocation(int where, int len) throws BadLocationException { if (where < 0) throw new BadLocationException("Invalid location", 1); else if (where > this.count) throw new BadLocationException("Invalid location", this.count); - else if ((where + len)>this.count) + else if ((where + len) > this.count) throw new BadLocationException("Invalid range", this.count); } diff --git a/libjava/classpath/javax/swing/text/StyleConstants.java b/libjava/classpath/javax/swing/text/StyleConstants.java index 598eaf621bc..c7906b8ad32 100644 --- a/libjava/classpath/javax/swing/text/StyleConstants.java +++ b/libjava/classpath/javax/swing/text/StyleConstants.java @@ -1,5 +1,5 @@ /* StyleConstants.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. @@ -43,45 +43,124 @@ import java.awt.Component; import javax.swing.Icon; +/** + * Represents standard attribute keys. This class also contains a set of + * useful static utility methods for querying and populating an + * {@link AttributeSet}. + * + * @since 1.2 + */ public class StyleConstants { + /** + * A value representing left alignment for the + * {@link ParagraphConstants#Alignment} attribute. + */ public static final int ALIGN_LEFT = 0; + + /** + * A value representing center alignment for the + * {@link ParagraphConstants#Alignment} attribute. + */ public static final int ALIGN_CENTER = 1; + + /** + * A value representing right alignment for the + * {@link ParagraphConstants#Alignment} attribute. + */ public static final int ALIGN_RIGHT = 2; + + /** + * A value representing ful justification for the + * {@link ParagraphConstants#Alignment} attribute. + */ public static final int ALIGN_JUSTIFIED = 3; + /** An alias for {@link CharacterConstants#Background}. */ public static final Object Background = CharacterConstants.Background; + + /** An alias for {@link CharacterConstants#BidiLevel}. */ public static final Object BidiLevel = CharacterConstants.BidiLevel; + + /** An alias for {@link CharacterConstants#Bold}. */ public static final Object Bold = CharacterConstants.Bold; - public static final Object ComponentAttribute = CharacterConstants.ComponentAttribute; + + /** An alias for {@link CharacterConstants#ComponentAttribute}. */ + public static final Object ComponentAttribute + = CharacterConstants.ComponentAttribute; + + /** An alias for {@link CharacterConstants#Family}. */ public static final Object Family = CharacterConstants.Family; + + /** An alias for {@link CharacterConstants#Family}. */ public static final Object FontFamily = CharacterConstants.Family; + + /** An alias for {@link CharacterConstants#Size}. */ public static final Object FontSize = CharacterConstants.Size; + + /** An alias for {@link CharacterConstants#Foreground}. */ public static final Object Foreground = CharacterConstants.Foreground; + + /** An alias for {@link CharacterConstants#IconAttribute}. */ public static final Object IconAttribute = CharacterConstants.IconAttribute; + + /** An alias for {@link CharacterConstants#Italic}. */ public static final Object Italic = CharacterConstants.Italic; + + /** An alias for {@link CharacterConstants#Size}. */ public static final Object Size = CharacterConstants.Size; + + /** An alias for {@link CharacterConstants#StrikeThrough}. */ public static final Object StrikeThrough = CharacterConstants.StrikeThrough; + + /** An alias for {@link CharacterConstants#Subscript}. */ public static final Object Subscript = CharacterConstants.Subscript; + + /** An alias for {@link CharacterConstants#Superscript}. */ public static final Object Superscript = CharacterConstants.Superscript; + + /** An alias for {@link CharacterConstants#Underline}. */ public static final Object Underline = CharacterConstants.Underline; + /** An alias for {@link ParagraphConstants#Alignment}. */ public static final Object Alignment = ParagraphConstants.Alignment; - public static final Object FirstLineIndent = ParagraphConstants.FirstLineIndent; + + /** An alias for {@link ParagraphConstants#FirstLineIndent}. */ + public static final Object FirstLineIndent + = ParagraphConstants.FirstLineIndent; + + /** An alias for {@link ParagraphConstants#LeftIndent}. */ public static final Object LeftIndent = ParagraphConstants.LeftIndent; + + /** An alias for {@link ParagraphConstants#LineSpacing}. */ public static final Object LineSpacing = ParagraphConstants.LineSpacing; + + /** An alias for {@link ParagraphConstants#Orientation}. */ public static final Object Orientation = ParagraphConstants.Orientation; + + /** An alias for {@link ParagraphConstants#RightIndent}. */ public static final Object RightIndent = ParagraphConstants.RightIndent; + + /** An alias for {@link ParagraphConstants#SpaceAbove}. */ public static final Object SpaceAbove = ParagraphConstants.SpaceAbove; + + /** An alias for {@link ParagraphConstants#SpaceBelow}. */ public static final Object SpaceBelow = ParagraphConstants.SpaceBelow; + + /** An alias for {@link ParagraphConstants#TabSet}. */ public static final Object TabSet = ParagraphConstants.TabSet; public static final String ComponentElementName = "component"; + public static final String IconElementName = "icon"; - public static final Object ComposedTextAttribute = new StyleConstants("composed text"); + public static final Object ComposedTextAttribute + = new StyleConstants("composed text"); + public static final Object ModelAttribute = new StyleConstants("model"); + public static final Object NameAttribute = new StyleConstants("name"); + public static final Object ResolveAttribute = new StyleConstants("resolver"); String keyname; @@ -93,279 +172,727 @@ public class StyleConstants keyname = k; } + /** + * Returns a string representation of the attribute key. + * + * @return A string representation of the attribute key. + */ public String toString() { return keyname; } + /** + * Returns the alignment specified in the given attributes, or + * {@link #ALIGN_LEFT} if no alignment is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The alignment (typically one of {@link #ALIGN_LEFT}, + * {@link #ALIGN_RIGHT}, {@link #ALIGN_CENTER} or + * {@link #ALIGN_JUSTIFIED}). + * + * @see #setAlignment(MutableAttributeSet, int) + */ public static int getAlignment(AttributeSet a) { - if (a.isDefined(Alignment)) - return ((Integer)a.getAttribute(Alignment)).intValue(); + Integer i = (Integer) a.getAttribute(Alignment); + if (i != null) + return i.intValue(); else return ALIGN_LEFT; } + /** + * Returns the background color specified in the given attributes, or + * {@link Color#BLACK} if no background color is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The background color. + * + * @see #setBackground(MutableAttributeSet, Color) + */ public static Color getBackground(AttributeSet a) { - if (a.isDefined(Background)) - return (Color) a.getAttribute(Background); + Color c = (Color) a.getAttribute(Background); + if (c != null) + return c; else - return Color.WHITE; + return Color.BLACK; } - + + /** + * Returns the bidi level specified in the given attributes, or + * <code>0</code> if no bidi level is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The bidi level. + * + * @see #setBidiLevel(MutableAttributeSet, int) + */ public static int getBidiLevel(AttributeSet a) { - if (a.isDefined(BidiLevel)) - return ((Integer)a.getAttribute(BidiLevel)).intValue(); + Integer i = (Integer) a.getAttribute(BidiLevel); + if (i != null) + return i.intValue(); else return 0; } + /** + * Returns the component specified in the given attributes, or + * <code>null</code> if no component is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The component (possibly <code>null</code>). + * + * @see #setComponent(MutableAttributeSet, Component) + */ public static Component getComponent(AttributeSet a) { - if (a.isDefined(ComponentAttribute)) - return (Component) a.getAttribute(ComponentAttribute); + Component c = (Component) a.getAttribute(ComponentAttribute); + if (c != null) + return c; else - return (Component) null; + return null; } + /** + * Returns the indentation specified in the given attributes, or + * <code>0.0f</code> if no indentation is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The indentation. + * + * @see #setFirstLineIndent(MutableAttributeSet, float) + */ public static float getFirstLineIndent(AttributeSet a) { - if (a.isDefined(FirstLineIndent)) - return ((Float)a.getAttribute(FirstLineIndent)).floatValue(); + Float f = (Float) a.getAttribute(FirstLineIndent); + if (f != null) + return f.floatValue(); else - return 0.f; + return 0.0f; } + /** + * Returns the font family specified in the given attributes, or + * <code>Monospaced</code> if no font family is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The font family. + * + * @see #setFontFamily(MutableAttributeSet, String) + */ public static String getFontFamily(AttributeSet a) { - if (a.isDefined(FontFamily)) - return (String) a.getAttribute(FontFamily); + String ff = (String) a.getAttribute(FontFamily); + if (ff != null) + return ff; else return "Monospaced"; } + /** + * Returns the font size specified in the given attributes, or + * <code>12</code> if no font size is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The font size. + * + * @see #setFontSize(MutableAttributeSet, int) + */ public static int getFontSize(AttributeSet a) { - if (a.isDefined(FontSize)) - return ((Integer)a.getAttribute(FontSize)).intValue(); + Integer i = (Integer) a.getAttribute(FontSize); + if (i != null) + return i.intValue(); else return 12; } + /** + * Returns the foreground color specified in the given attributes, or + * {@link Color#BLACK} if no foreground color is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The foreground color. + * + * @see #setForeground(MutableAttributeSet, Color) + */ public static Color getForeground(AttributeSet a) { - if (a.isDefined(Foreground)) - return (Color) a.getAttribute(Foreground); + Color c = (Color) a.getAttribute(Foreground); + if (c != null) + return c; else return Color.BLACK; } + /** + * Returns the icon specified in the given attributes, or + * <code>null</code> if no icon is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The icon (possibly <code>null</code>). + * + * @see #setIcon(MutableAttributeSet, Icon) + */ public static Icon getIcon(AttributeSet a) { - if (a.isDefined(IconAttribute)) - return (Icon) a.getAttribute(IconAttribute); - else - return (Icon) null; + return (Icon) a.getAttribute(IconAttribute); } + /** + * Returns the left indentation specified in the given attributes, or + * <code>0.0f</code> if no left indentation is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The left indentation. + * + * @see #setLeftIndent(MutableAttributeSet, float) + */ public static float getLeftIndent(AttributeSet a) { - if (a.isDefined(LeftIndent)) - return ((Float)a.getAttribute(LeftIndent)).floatValue(); + Float f = (Float) a.getAttribute(LeftIndent); + if (f != null) + return f.floatValue(); else - return 0.f; + return 0.0f; } + /** + * Returns the line spacing specified in the given attributes, or + * <code>0.0f</code> if no line spacing is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The line spacing. + * + * @see #setLineSpacing(MutableAttributeSet, float) + */ public static float getLineSpacing(AttributeSet a) { - if (a.isDefined(LineSpacing)) - return ((Float)a.getAttribute(LineSpacing)).floatValue(); + Float f = (Float) a.getAttribute(LineSpacing); + if (f != null) + return f.floatValue(); else - return 0.f; + return 0.0f; } + /** + * Returns the right indentation specified in the given attributes, or + * <code>0.0f</code> if no right indentation is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The right indentation. + * + * @see #setRightIndent(MutableAttributeSet, float) + */ public static float getRightIndent(AttributeSet a) { - if (a.isDefined(RightIndent)) - return ((Float)a.getAttribute(RightIndent)).floatValue(); + Float f = (Float) a.getAttribute(RightIndent); + if (f != null) + return f.floatValue(); else - return 0.f; + return 0.0f; } + /** + * Returns the 'space above' specified in the given attributes, or + * <code>0.0f</code> if no 'space above' is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The 'space above'. + * + * @see #setSpaceAbove(MutableAttributeSet, float) + */ public static float getSpaceAbove(AttributeSet a) { - if (a.isDefined(SpaceAbove)) - return ((Float)a.getAttribute(SpaceAbove)).floatValue(); - else - return 0.f; + Float f = (Float) a.getAttribute(SpaceAbove); + if (f != null) + return f.floatValue(); + else + return 0.0f; } + /** + * Returns the 'space below' specified in the given attributes, or + * <code>0.0f</code> if no 'space below' is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The 'space below'. + * + * @see #setSpaceBelow(MutableAttributeSet, float) + */ public static float getSpaceBelow(AttributeSet a) { - if (a.isDefined(SpaceBelow)) - return ((Float)a.getAttribute(SpaceBelow)).floatValue(); + Float f = (Float) a.getAttribute(SpaceBelow); + if (f != null) + return f.floatValue(); else - return 0.f; + return 0.0f; } + /** + * Returns the tab set specified in the given attributes, or + * <code>null</code> if no tab set is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The tab set. + * + * @see #setTabSet(MutableAttributeSet, javax.swing.text.TabSet) + */ public static javax.swing.text.TabSet getTabSet(AttributeSet a) { - if (a.isDefined(StyleConstants.TabSet)) - return (javax.swing.text.TabSet) a.getAttribute(StyleConstants.TabSet); - else - return (javax.swing.text.TabSet) null; + // I'm guessing that the fully qualified class name is to differentiate + // between the TabSet class and the TabSet (attribute) instance on some + // compiler... + return (javax.swing.text.TabSet) a.getAttribute(StyleConstants.TabSet); } + /** + * Returns the value of the bold flag in the given attributes, or + * <code>false</code> if no bold flag is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The bold flag. + * + * @see #setBold(MutableAttributeSet, boolean) + */ public static boolean isBold(AttributeSet a) { - if (a.isDefined(Bold)) - return ((Boolean) a.getAttribute(Bold)).booleanValue(); + Boolean b = (Boolean) a.getAttribute(Bold); + if (b != null) + return b.booleanValue(); else - return false; + return false; } + /** + * Returns the value of the italic flag in the given attributes, or + * <code>false</code> if no italic flag is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The italic flag. + * + * @see #setItalic(MutableAttributeSet, boolean) + */ public static boolean isItalic(AttributeSet a) { - if (a.isDefined(Italic)) - return ((Boolean) a.getAttribute(Italic)).booleanValue(); + Boolean b = (Boolean) a.getAttribute(Italic); + if (b != null) + return b.booleanValue(); else - return false; + return false; } + /** + * Returns the value of the strike-through flag in the given attributes, or + * <code>false</code> if no strike-through flag is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The strike-through flag. + * + * @see #setStrikeThrough(MutableAttributeSet, boolean) + */ public static boolean isStrikeThrough(AttributeSet a) { - if (a.isDefined(StrikeThrough)) - return ((Boolean) a.getAttribute(StrikeThrough)).booleanValue(); + Boolean b = (Boolean) a.getAttribute(StrikeThrough); + if (b != null) + return b.booleanValue(); else - return false; + return false; } + /** + * Returns the value of the subscript flag in the given attributes, or + * <code>false</code> if no subscript flag is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The subscript flag. + * + * @see #setSubscript(MutableAttributeSet, boolean) + */ public static boolean isSubscript(AttributeSet a) { - if (a.isDefined(Subscript)) - return ((Boolean) a.getAttribute(Subscript)).booleanValue(); + Boolean b = (Boolean) a.getAttribute(Subscript); + if (b != null) + return b.booleanValue(); else - return false; + return false; } + /** + * Returns the value of the superscript flag in the given attributes, or + * <code>false</code> if no superscript flag is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The superscript flag. + * + * @see #setSuperscript(MutableAttributeSet, boolean) + */ public static boolean isSuperscript(AttributeSet a) { - if (a.isDefined(Superscript)) - return ((Boolean) a.getAttribute(Superscript)).booleanValue(); - else - return false; + Boolean b = (Boolean) a.getAttribute(Superscript); + if (b != null) + return b.booleanValue(); + else + return false; } + /** + * Returns the value of the underline flag in the given attributes, or + * <code>false</code> if no underline flag is specified. + * + * @param a the attribute set (<code>null</code> not permitted). + * + * @return The underline flag. + * + * @see #setUnderline(MutableAttributeSet, boolean) + */ public static boolean isUnderline(AttributeSet a) { - if (a.isDefined(Underline)) - return ((Boolean) a.getAttribute(Underline)).booleanValue(); + Boolean b = (Boolean) a.getAttribute(Underline); + if (b != null) + return b.booleanValue(); else - return false; + return false; } + /** + * Adds an alignment attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param align the alignment (typically one of + * {@link StyleConstants#ALIGN_LEFT}, + * {@link StyleConstants#ALIGN_RIGHT}, + * {@link StyleConstants#ALIGN_CENTER} or + * {@link StyleConstants#ALIGN_JUSTIFIED}). + * + * @throws NullPointerException if <code>a</code> is <code>null</code>. + * + * @see #getAlignment(AttributeSet) + */ public static void setAlignment(MutableAttributeSet a, int align) { a.addAttribute(Alignment, new Integer(align)); } - public static void setBackground(MutableAttributeSet a, Color fg) + /** + * Adds a background attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param bg the background (<code>null</code> not permitted). + * + * @throws NullPointerException if either argument is <code>null</code>. + * + * @see #getBackground(AttributeSet) + */ + public static void setBackground(MutableAttributeSet a, Color bg) { - a.addAttribute(Background, fg); + a.addAttribute(Background, bg); } + /** + * Adds a bidi-level attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param lev the level. + * + * @throws NullPointerException if <code>a</code> is <code>null</code>. + * + * @see #getBidiLevel(AttributeSet) + */ public static void setBidiLevel(MutableAttributeSet a, int lev) { a.addAttribute(BidiLevel, new Integer(lev)); } + /** + * Adds a bold attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param b the new value of the bold attribute. + * + * @throws NullPointerException if <code>a</code> is <code>null</code>. + * + * @see #isBold(AttributeSet) + */ public static void setBold(MutableAttributeSet a, boolean b) { a.addAttribute(Bold, Boolean.valueOf(b)); } + /** + * Adds a component attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param c the component (<code>null</code> not permitted). + * + * @throws NullPointerException if either argument is <code>null</code>. + * + * @see #getComponent(AttributeSet) + */ public static void setComponent(MutableAttributeSet a, Component c) { a.addAttribute(ComponentAttribute, c); } + /** + * Adds a first line indentation attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param i the indentation. + * + * @throws NullPointerException if <code>a</code> is <code>null</code>. + * + * @see #getFirstLineIndent(AttributeSet) + */ public static void setFirstLineIndent(MutableAttributeSet a, float i) { a.addAttribute(FirstLineIndent, new Float(i)); } + /** + * Adds a font family attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param fam the font family name (<code>null</code> not permitted). + * + * @throws NullPointerException if either argument is <code>null</code>. + * + * @see #getFontFamily(AttributeSet) + */ public static void setFontFamily(MutableAttributeSet a, String fam) { a.addAttribute(FontFamily, fam); } + /** + * Adds a font size attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param s the font size (in points). + * + * @throws NullPointerException if <code>a</code> is <code>null</code>. + * + * @see #getFontSize(AttributeSet) + */ public static void setFontSize(MutableAttributeSet a, int s) { a.addAttribute(FontSize, new Integer(s)); } + /** + * Adds a foreground color attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param fg the foreground color (<code>null</code> not permitted). + * + * @throws NullPointerException if either argument is <code>null</code>. + * + * @see #getForeground(AttributeSet) + */ public static void setForeground(MutableAttributeSet a, Color fg) { a.addAttribute(Foreground, fg); } + /** + * Adds an icon attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param c the icon (<code>null</code> not permitted). + * + * @throws NullPointerException if either argument is <code>null</code>. + * + * @see #getIcon(AttributeSet) + */ public static void setIcon(MutableAttributeSet a, Icon c) { a.addAttribute(IconAttribute, c); } + /** + * Adds an italic attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param b the new value of the italic attribute. + * + * @throws NullPointerException if <code>a</code> is <code>null</code>. + * + * @see #isItalic(AttributeSet) + */ public static void setItalic(MutableAttributeSet a, boolean b) { a.addAttribute(Italic, Boolean.valueOf(b)); } + /** + * Adds a left indentation attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param i the indentation. + * + * @throws NullPointerException if <code>a</code> is <code>null</code>. + * + * @see #getLeftIndent(AttributeSet) + */ public static void setLeftIndent(MutableAttributeSet a, float i) { a.addAttribute(LeftIndent, new Float(i)); } + /** + * Adds a line spacing attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param i the line spacing. + * + * @throws NullPointerException if <code>a</code> is <code>null</code>. + * + * @see #getLineSpacing(AttributeSet) + */ public static void setLineSpacing(MutableAttributeSet a, float i) { a.addAttribute(LineSpacing, new Float(i)); } + /** + * Adds a right indentation attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param i the right indentation. + * + * @throws NullPointerException if <code>a</code> is <code>null</code>. + * + * @see #getRightIndent(AttributeSet) + */ public static void setRightIndent(MutableAttributeSet a, float i) { a.addAttribute(RightIndent, new Float(i)); } + /** + * Adds a 'space above' attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param i the space above attribute value. + * + * @throws NullPointerException if <code>a</code> is <code>null</code>. + * + * @see #getSpaceAbove(AttributeSet) + */ public static void setSpaceAbove(MutableAttributeSet a, float i) { a.addAttribute(SpaceAbove, new Float(i)); } + /** + * Adds a 'space below' attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param i the space below attribute value. + * + * @throws NullPointerException if <code>a</code> is <code>null</code>. + * + * @see #getSpaceBelow(AttributeSet) + */ public static void setSpaceBelow(MutableAttributeSet a, float i) { a.addAttribute(SpaceBelow, new Float(i)); } + /** + * Adds a strike-through attribue to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param b the strike-through attribute value. + * + * @throws NullPointerException if <code>a</code> is <code>null</code>. + * + * @see #isStrikeThrough(AttributeSet) + */ public static void setStrikeThrough(MutableAttributeSet a, boolean b) { a.addAttribute(StrikeThrough, Boolean.valueOf(b)); } + /** + * Adds a subscript attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param b the subscript attribute value. + * + * @throws NullPointerException if <code>a</code> is <code>null</code>. + * + * @see #isSubscript(AttributeSet) + */ public static void setSubscript(MutableAttributeSet a, boolean b) { a.addAttribute(Subscript, Boolean.valueOf(b)); } + /** + * Adds a superscript attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param b the superscript attribute value. + * + * @throws NullPointerException if <code>a</code> is <code>null</code>. + * + * @see #isSuperscript(AttributeSet) + */ public static void setSuperscript(MutableAttributeSet a, boolean b) { a.addAttribute(Superscript, Boolean.valueOf(b)); } - public static void setTabSet(MutableAttributeSet a, javax.swing.text.TabSet tabs) + /** + * Adds a {@link TabSet} attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param tabs the tab set (<code>null</code> not permitted). + * + * @throws NullPointerException if either argument is <code>null</code>. + * + * @see #getTabSet(AttributeSet) + */ + public static void setTabSet(MutableAttributeSet a, + javax.swing.text.TabSet tabs) { a.addAttribute(StyleConstants.TabSet, tabs); } + /** + * Adds an underline attribute to the specified set. + * + * @param a the attribute set (<code>null</code> not permitted). + * @param b the underline attribute value. + * + * @throws NullPointerException if <code>a</code> is <code>null</code>. + * + * @see #isUnderline(AttributeSet) + */ public static void setUnderline(MutableAttributeSet a, boolean b) { a.addAttribute(Underline, Boolean.valueOf(b)); @@ -373,73 +900,173 @@ public class StyleConstants // The remainder are so-called "typesafe enumerations" which // alias subsets of the above constants. + + /** + * A set of keys for attributes that apply to characters. + */ public static class CharacterConstants extends StyleConstants implements AttributeSet.CharacterAttribute { + /** + * Private constructor prevents new instances being created. + * + * @param k the key name. + */ private CharacterConstants(String k) { super(k); } - public static Object Background = ColorConstants.Background; - public static Object BidiLevel = new CharacterConstants("bidiLevel"); - public static Object Bold = FontConstants.Bold; - public static Object ComponentAttribute = new CharacterConstants("component"); - public static Object Family = FontConstants.Family; - public static Object Size = FontConstants.Size; - public static Object Foreground = ColorConstants.Foreground; - public static Object IconAttribute = new CharacterConstants("icon"); - public static Object Italic = FontConstants.Italic; - public static Object StrikeThrough = new CharacterConstants("strikethrough"); - public static Object Subscript = new CharacterConstants("subscript"); - public static Object Superscript = new CharacterConstants("superscript"); - public static Object Underline = new CharacterConstants("underline"); + /** An alias for {@link ColorConstants#Background}. */ + public static final Object Background = ColorConstants.Background; + + /** A key for the bidi level character attribute. */ + public static final Object BidiLevel = new CharacterConstants("bidiLevel"); + + /** An alias for {@link FontConstants#Bold}. */ + public static final Object Bold = FontConstants.Bold; + + /** A key for the component character attribute. */ + public static final Object ComponentAttribute + = new CharacterConstants("component"); + + /** An alias for {@link FontConstants#Family}. */ + public static final Object Family = FontConstants.Family; + + /** An alias for {@link FontConstants#Size}. */ + public static final Object Size = FontConstants.Size; + + /** An alias for {@link ColorConstants#Foreground}. */ + public static final Object Foreground = ColorConstants.Foreground; + + /** A key for the icon character attribute. */ + public static final Object IconAttribute = new CharacterConstants("icon"); + + /** A key for the italic character attribute. */ + public static final Object Italic = FontConstants.Italic; + + /** A key for the strike through character attribute. */ + public static final Object StrikeThrough + = new CharacterConstants("strikethrough"); + + /** A key for the subscript character attribute. */ + public static final Object Subscript = new CharacterConstants("subscript"); + + /** A key for the superscript character attribute. */ + public static final Object Superscript + = new CharacterConstants("superscript"); + + /** A key for the underline character attribute. */ + public static final Object Underline = new CharacterConstants("underline"); + } + /** + * A set of keys for attributes that relate to colors. + */ public static class ColorConstants extends StyleConstants implements AttributeSet.ColorAttribute, AttributeSet.CharacterAttribute { + /** + * Private constructor prevents new instances being created. + * + * @param k the key name. + */ private ColorConstants(String k) { super(k); } - public static Object Foreground = new ColorConstants("foreground"); - public static Object Background = new ColorConstants("background"); + + /** A key for the foreground color attribute. */ + public static final Object Foreground = new ColorConstants("foreground"); + + /** A key for the background color attribute. */ + public static final Object Background = new ColorConstants("background"); } + /** + * A set of keys for attributes that apply to fonts. + */ public static class FontConstants extends StyleConstants implements AttributeSet.FontAttribute, AttributeSet.CharacterAttribute { + /** + * Private constructor prevents new instances being created. + * + * @param k the key name. + */ private FontConstants(String k) { super(k); } - public static Object Bold = new FontConstants("bold"); - public static Object Family = new FontConstants("family"); - public static Object Italic = new FontConstants("italic"); - public static Object Size = new FontConstants("size"); + + /** A key for the bold font attribute. */ + public static final Object Bold = new FontConstants("bold"); + + /** A key for the family font attribute. */ + public static final Object Family = new FontConstants("family"); + + /** A key for the italic font attribute. */ + public static final Object Italic = new FontConstants("italic"); + + /** A key for the size font attribute. */ + public static final Object Size = new FontConstants("size"); } + /** + * A set of keys for attributes that apply to paragraphs. + */ public static class ParagraphConstants extends StyleConstants implements AttributeSet.ParagraphAttribute { + /** + * Private constructor prevents new instances being created. + * + * @param k the key name. + */ private ParagraphConstants(String k) { super(k); } - public static Object Alignment = new ParagraphConstants("Alignment"); - public static Object FirstLineIndent = new ParagraphConstants("FirstLineIndent"); - public static Object LeftIndent = new ParagraphConstants("LeftIndent"); - public static Object LineSpacing = new ParagraphConstants("LineSpacing"); - public static Object Orientation = new ParagraphConstants("Orientation"); - public static Object RightIndent = new ParagraphConstants("RightIndent"); - public static Object SpaceAbove = new ParagraphConstants("SpaceAbove"); - public static Object SpaceBelow = new ParagraphConstants("SpaceBelow"); - public static Object TabSet = new ParagraphConstants("TabSet"); + + /** A key for the alignment paragraph attribute. */ + public static final Object Alignment = new ParagraphConstants("Alignment"); + + /** A key for the first line indentation paragraph attribute. */ + public static final Object FirstLineIndent + = new ParagraphConstants("FirstLineIndent"); + + /** A key for the left indentation paragraph attribute. */ + public static final Object LeftIndent + = new ParagraphConstants("LeftIndent"); + + /** A key for the line spacing paragraph attribute. */ + public static final Object LineSpacing + = new ParagraphConstants("LineSpacing"); + + /** A key for the orientation paragraph attribute. */ + public static final Object Orientation + = new ParagraphConstants("Orientation"); + + /** A key for the right indentation paragraph attribute. */ + public static final Object RightIndent + = new ParagraphConstants("RightIndent"); + + /** A key for the 'space above' paragraph attribute. */ + public static final Object SpaceAbove + = new ParagraphConstants("SpaceAbove"); + + /** A key for the 'space below' paragraph attribute. */ + public static final Object SpaceBelow + = new ParagraphConstants("SpaceBelow"); + + /** A key for the tabset paragraph attribute. */ + public static final Object TabSet = new ParagraphConstants("TabSet"); + } } diff --git a/libjava/classpath/javax/swing/text/StyleContext.java b/libjava/classpath/javax/swing/text/StyleContext.java index dabc0ba9cd0..e2643a2aacd 100644 --- a/libjava/classpath/javax/swing/text/StyleContext.java +++ b/libjava/classpath/javax/swing/text/StyleContext.java @@ -48,6 +48,7 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Enumeration; import java.util.EventListener; +import java.util.HashSet; import java.util.Hashtable; import javax.swing.event.ChangeEvent; @@ -370,7 +371,7 @@ public class StyleContext { StringBuffer sb = new StringBuffer(); sb.append("[StyleContext.SmallattributeSet:"); - for (int i = 0; i < attrs.length; ++i) + for (int i = 0; i < attrs.length - 1; ++i) { sb.append(" ("); sb.append(attrs[i].toString()); @@ -406,7 +407,12 @@ public class StyleContext static StyleContext defaultStyleContext = new StyleContext(); static final int compressionThreshold = 9; - + + /** + * These attribute keys are handled specially in serialization. + */ + private static HashSet staticAttributeKeys = new HashSet(); + EventListenerList listenerList; Hashtable styleTable; @@ -737,4 +743,19 @@ public class StyleContext { throw new InternalError("not implemented"); } + + /** + * Registers an attribute key as a well-known keys. When an attribute with + * such a key is written to a stream,, a special syntax is used so that it + * can be recognized when it is read back in. All attribute keys defined + * in <code>StyleContext</code> are registered as static keys. If you define + * additional attribute keys that you want to exist as nonreplicated objects, + * then you should register them using this method. + * + * @param key the key to register as static attribute key + */ + public static void registerStaticAttributeKey(Object key) + { + staticAttributeKeys.add(key); + } } diff --git a/libjava/classpath/javax/swing/text/TableView.java b/libjava/classpath/javax/swing/text/TableView.java index d3113b82be2..2dcb9ebf7b3 100644 --- a/libjava/classpath/javax/swing/text/TableView.java +++ b/libjava/classpath/javax/swing/text/TableView.java @@ -54,7 +54,7 @@ import javax.swing.event.DocumentEvent; * * @author Roman Kennke (kennke@aicas.com) */ -public class TableView +public abstract class TableView extends BoxView { @@ -90,6 +90,18 @@ public class TableView public void replace(int offset, int length, View[] views) { super.replace(offset, length, views); + int viewCount = getViewCount(); + if (columnRequirements == null + || viewCount > columnRequirements.length) + { + columnRequirements = new SizeRequirements[viewCount]; + for (int i = 0; i < columnRequirements.length; i++) + columnRequirements[i] = new SizeRequirements(); + } + if (columnOffsets == null || columnOffsets.length < viewCount) + columnOffsets = new int[viewCount]; + if (columnSpans == null || columnSpans.length < viewCount) + columnSpans = new int[viewCount]; layoutChanged(X_AXIS); } @@ -108,8 +120,6 @@ public class TableView protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { - // TODO: Maybe prepare columnSpans and columnOffsets. - // Some sanity checks. If these preconditions are not met, then the // following code will not work. Also, there must be something // seriously wrong then. @@ -140,7 +150,7 @@ public class TableView { // FIXME: Figure out how to fetch the row heights from the TableView's // element. - super.layoutMajorAxis(targetSpan, axis, offsets, spans); + super.layoutMinorAxis(targetSpan, axis, offsets, spans); } /** @@ -303,7 +313,7 @@ public class TableView /** * The size requirements of the columns. */ - private SizeRequirements[] columnRequirements; + SizeRequirements[] columnRequirements = new SizeRequirements[0]; /** * Creates a new instance of <code>TableView</code>. @@ -313,15 +323,6 @@ public class TableView public TableView(Element el) { super(el, Y_AXIS); - int numChildren = el.getElementCount(); - View[] rows = new View[numChildren]; - for (int i = 0; i < numChildren; ++i) - { - Element rowEl = el.getElement(i); - TableRow rowView = createTableRow(rowEl); - rows[i] = rowView; - } - replace(0, 0, rows); } /** @@ -385,7 +386,10 @@ public class TableView protected void layoutColumns(int targetSpan, int[] offsets, int spans[], SizeRequirements[] reqs) { - // TODO: Figure out what exactly to do here. + updateColumnRequirements(); + SizeRequirements r = calculateMinorAxisRequirements(X_AXIS, null); + SizeRequirements.calculateTiledPositions(targetSpan, r, columnRequirements, + offsets, spans); } /** @@ -462,4 +466,26 @@ public class TableView // and look for a range that contains the given position. return super.getViewAtPosition(pos, a); } + + /** + * Updates the column requirements. + */ + private void updateColumnRequirements() + { + int rowCount = getViewCount(); + for (int r = 0; r < rowCount; ++r) + { + TableRow row = (TableRow) getView(r); + int columnCount = row.getViewCount(); + for (int c = 0; c < columnCount; ++c) + { + View cell = row.getView(c); + SizeRequirements cr = columnRequirements[c]; + cr.minimum = Math.max(cr.minimum, (int) cell.getMinimumSpan(X_AXIS)); + cr.preferred = Math.max(cr.preferred, + (int) cell.getPreferredSpan(X_AXIS)); + cr.maximum = Math.max(cr.maximum, (int) cell.getMaximumSpan(X_AXIS)); + } + } + } } diff --git a/libjava/classpath/javax/swing/text/Utilities.java b/libjava/classpath/javax/swing/text/Utilities.java index 1adc8ff87e9..d109a4a950f 100644 --- a/libjava/classpath/javax/swing/text/Utilities.java +++ b/libjava/classpath/javax/swing/text/Utilities.java @@ -41,12 +41,8 @@ package javax.swing.text; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Point; -import java.awt.Rectangle; import java.text.BreakIterator; -import javax.swing.SwingConstants; -import javax.swing.SwingUtilities; - /** * A set of utilities to deal with text. This is used by several other classes * inside this package. @@ -73,6 +69,10 @@ public class Utilities * are taken into account. Tabs are expanded using the * specified {@link TabExpander}. * + * + * The X and Y coordinates denote the start of the <em>baseline</em> where + * the text should be drawn. + * * @param s the text fragment to be drawn. * @param x the x position for drawing. * @param y the y position for drawing. @@ -88,15 +88,14 @@ public class Utilities // This buffers the chars to be drawn. char[] buffer = s.array; - - // The current x and y pixel coordinates. - int pixelX = x; - int pixelY = y; - // The font metrics of the current selected font. FontMetrics metrics = g.getFontMetrics(); int ascent = metrics.getAscent(); + // The current x and y pixel coordinates. + int pixelX = x; + int pixelY = y - ascent; + int pixelWidth = 0; int pos = s.offset; int len = 0; @@ -238,9 +237,10 @@ public class Utilities int pos; int currentX = x0; - for (pos = p0; pos < s.count; pos++) + for (pos = 0; pos < s.count; pos++) { char nextChar = s.array[s.offset+pos]; + if (nextChar == 0) { if (! round) @@ -256,6 +256,7 @@ public class Utilities else currentX = (int) te.nextTabStop(currentX, pos); } + if (currentX > x) { if (! round) @@ -263,7 +264,8 @@ public class Utilities break; } } - return pos; + + return pos + p0; } /** @@ -510,10 +512,10 @@ public class Utilities { int mark = Utilities.getTabbedTextOffset(s, metrics, x0, x, e, startOffset); BreakIterator breaker = BreakIterator.getWordInstance(); - breaker.setText(s.toString()); - + breaker.setText(s); + // If mark is equal to the end of the string, just use that position - if (mark == s.count) + if (mark == s.count + s.offset) return mark; // Try to find a word boundary previous to the mark at which we @@ -571,15 +573,29 @@ public class Utilities public static final int getPositionAbove(JTextComponent c, int offset, int x) throws BadLocationException { - View rootView = c.getUI().getRootView(c); - Rectangle r = c.modelToView(offset); - int offs = c.viewToModel(new Point(x, r.y)); - int pos = rootView.getNextVisualPositionFrom(offs, - Position.Bias.Forward, - SwingUtilities.calculateInnerArea(c, null), - SwingConstants.NORTH, - new Position.Bias[1]); - return pos; + int offs = getRowStart(c, offset); + + if(offs == -1) + return -1; + + // Effectively calculates the y value of the previous line. + Point pt = c.modelToView(offs-1).getLocation(); + + pt.x = x; + + // Calculate a simple fitting offset. + offs = c.viewToModel(pt); + + // Find out the real x positions of the calculated character and its + // neighbour. + int offsX = c.modelToView(offs).getLocation().x; + int offsXNext = c.modelToView(offs+1).getLocation().x; + + // Chose the one which is nearer to us and return its offset. + if (Math.abs(offsX-x) <= Math.abs(offsXNext-x)) + return offs; + else + return offs+1; } /** @@ -598,14 +614,31 @@ public class Utilities public static final int getPositionBelow(JTextComponent c, int offset, int x) throws BadLocationException { - View rootView = c.getUI().getRootView(c); - Rectangle r = c.modelToView(offset); - int offs = c.viewToModel(new Point(x, r.y)); - int pos = rootView.getNextVisualPositionFrom(offs, - Position.Bias.Forward, - SwingUtilities.calculateInnerArea(c, null), - SwingConstants.SOUTH, - new Position.Bias[1]); - return pos; - } + int offs = getRowEnd(c, offset); + + if(offs == -1) + return -1; + + // Effectively calculates the y value of the previous line. + Point pt = c.modelToView(offs+1).getLocation(); + + pt.x = x; + + // Calculate a simple fitting offset. + offs = c.viewToModel(pt); + + if (offs == c.getDocument().getLength()) + return offs; + + // Find out the real x positions of the calculated character and its + // neighbour. + int offsX = c.modelToView(offs).getLocation().x; + int offsXNext = c.modelToView(offs+1).getLocation().x; + + // Chose the one which is nearer to us and return its offset. + if (Math.abs(offsX-x) <= Math.abs(offsXNext-x)) + return offs; + else + return offs+1; + } } diff --git a/libjava/classpath/javax/swing/text/View.java b/libjava/classpath/javax/swing/text/View.java index b835842bc0e..2feaf29a4f9 100644 --- a/libjava/classpath/javax/swing/text/View.java +++ b/libjava/classpath/javax/swing/text/View.java @@ -44,6 +44,7 @@ import java.awt.Rectangle; import java.awt.Shape; import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; import javax.swing.event.DocumentEvent; public abstract class View implements SwingConstants @@ -72,8 +73,29 @@ public abstract class View implements SwingConstants public abstract void paint(Graphics g, Shape s); + /** + * Sets the parent for this view. This is the first method that is beeing + * called on a view to setup the view hierarchy. This is also the last method + * beeing called when the view is disconnected from the view hierarchy, in + * this case <code>parent</code> is null. + * + * If <code>parent</code> is <code>null</code>, a call to this method also + * calls <code>setParent</code> on the children, thus disconnecting them from + * the view hierarchy. That means that super must be called when this method + * is overridden. + * + * @param parent the parent to set, <code>null</code> when this view is + * beeing disconnected from the view hierarchy + */ public void setParent(View parent) { + if (parent == null) + { + int numChildren = getViewCount(); + for (int i = 0; i < numChildren; i++) + getView(i).setParent(null); + } + this.parent = parent; } @@ -101,27 +123,65 @@ public abstract class View implements SwingConstants return elt; } + /** + * Returns the preferred span along the specified axis. Normally the view is + * rendered with the span returned here if that is possible. + * + * @param axis the axis + * + * @return the preferred span along the specified axis + */ public abstract float getPreferredSpan(int axis); + /** + * Returns the resize weight of this view. A value of <code>0</code> or less + * means this view is not resizeable. Positive values make the view + * resizeable. The default implementation returns <code>0</code> + * unconditionally. + * + * @param axis the axis + * + * @return the resizability of this view along the specified axis + */ public int getResizeWeight(int axis) { return 0; } + /** + * Returns the maximum span along the specified axis. The default + * implementation will forward to + * {@link #getPreferredSpan(int)} unless {@link #getResizeWeight(int)} + * returns a value > 0, in which case this returns {@link Integer#MIN_VALUE}. + * + * @param axis the axis + * + * @return the maximum span along the specified axis + */ public float getMaximumSpan(int axis) { + float max = Integer.MAX_VALUE; if (getResizeWeight(axis) <= 0) - return getPreferredSpan(axis); - - return Integer.MAX_VALUE; + max = getPreferredSpan(axis); + return max; } + /** + * Returns the minimum span along the specified axis. The default + * implementation will forward to + * {@link #getPreferredSpan(int)} unless {@link #getResizeWeight(int)} + * returns a value > 0, in which case this returns <code>0</code>. + * + * @param axis the axis + * + * @return the minimum span along the specified axis + */ public float getMinimumSpan(int axis) { + float min = 0; if (getResizeWeight(axis) <= 0) - return getPreferredSpan(axis); - - return Integer.MAX_VALUE; + min = getPreferredSpan(axis); + return min; } public void setSize(float width, float height) @@ -129,6 +189,20 @@ public abstract class View implements SwingConstants // The default implementation does nothing. } + /** + * Returns the alignment of this view along the baseline of the parent view. + * An alignment of <code>0.0</code> will align this view with the left edge + * along the baseline, an alignment of <code>0.5</code> will align it + * centered to the baseline, an alignment of <code>1.0</code> will align + * the right edge along the baseline. + * + * The default implementation returns 0.5 unconditionally. + * + * @param axis the axis + * + * @return the alignment of this view along the parents baseline for the + * specified axis + */ public float getAlignment(int axis) { return 0.5f; @@ -160,6 +234,15 @@ public abstract class View implements SwingConstants return parent != null ? parent.getViewFactory() : null; } + /** + * Replaces a couple of child views with new child views. If + * <code>length == 0</code> then this is a simple insertion, if + * <code>views == null</code> this only removes some child views. + * + * @param offset the offset at which to replace + * @param length the number of child views to be removed + * @param views the new views to be inserted, may be <code>null</code> + */ public void replace(int offset, int length, View[] views) { // Default implementation does nothing. @@ -392,6 +475,10 @@ public abstract class View implements SwingConstants * of the change to the model. This calles {@link #forwardUpdateToView} * for each View that must be forwarded to. * + * If <code>ec</code> is not <code>null</code> (this means there have been + * structural changes to the element that this view is responsible for) this + * method should recognize this and don't notify newly added child views. + * * @param ec the ElementChange describing the element changes (may be * <code>null</code> if there were no changes) * @param ev the DocumentEvent describing the changes to the model @@ -404,10 +491,31 @@ public abstract class View implements SwingConstants DocumentEvent ev, Shape shape, ViewFactory vf) { int count = getViewCount(); - for (int i = 0; i < count; i++) + if (count > 0) { - View child = getView(i); - forwardUpdateToView(child, ev, shape, vf); + 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) + { + index = ec.getIndex(); + addLength = ec.getChildrenAdded().length; + } + + if (startIndex >= 0 && endIndex >= 0) + { + for (int i = startIndex; i <= endIndex; i++) + { + // Skip newly added child views. + if (index >= 0 && i >= index && i < (index+addLength)) + continue; + View child = getView(i); + forwardUpdateToView(child, ev, shape, vf); + } + } } } @@ -503,9 +611,9 @@ 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"); - Shape s1 = modelToView(p1, a, b1); - Shape s2 = modelToView(p2, a, b2); - return s1.getBounds().union(s2.getBounds()); + 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); } /** @@ -570,7 +678,7 @@ public abstract class View implements SwingConstants * Dumps the complete View hierarchy. This method can be used for debugging * purposes. */ - void dump() + protected void dump() { // Climb up the hierarchy to the parent. View parent = getParent(); @@ -590,7 +698,7 @@ public abstract class View implements SwingConstants { for (int i = 0; i < indent; ++i) System.out.print('.'); - System.out.println(this); + System.out.println(this + "(" + getStartOffset() + "," + getEndOffset() + ": " + getElement()); int count = getViewCount(); for (int i = 0; i < count; ++i) diff --git a/libjava/classpath/javax/swing/text/WrappedPlainView.java b/libjava/classpath/javax/swing/text/WrappedPlainView.java index baba343c5bf..e2790a05ca0 100644 --- a/libjava/classpath/javax/swing/text/WrappedPlainView.java +++ b/libjava/classpath/javax/swing/text/WrappedPlainView.java @@ -270,8 +270,7 @@ public class WrappedPlainView extends BoxView implements TabExpander protected int calculateBreakPosition(int p0, int p1) { Container c = getContainer(); - Rectangle alloc = c.isValid() ? c.getBounds() - : new Rectangle(c.getPreferredSize()); + Rectangle alloc = new Rectangle(0, 0, getWidth(), getHeight()); updateMetrics(); try { diff --git a/libjava/classpath/javax/swing/text/html/FormView.java b/libjava/classpath/javax/swing/text/html/FormView.java new file mode 100644 index 00000000000..b85c6943404 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/FormView.java @@ -0,0 +1,230 @@ +/* FormView.java -- A view for a variety of HTML form elements + 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.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JPasswordField; +import javax.swing.JRadioButton; +import javax.swing.JTextField; +import javax.swing.UIManager; +import javax.swing.text.AttributeSet; +import javax.swing.text.ComponentView; +import javax.swing.text.Element; +import javax.swing.text.StyleConstants; + +/** + * A View that renders HTML form elements like buttons and input fields. + * This is implemented as a {@link ComponentView} that creates different Swing + * component depending on the type and setting of the different form elements. + * + * Namely, this view creates the following components: + * <table> + * <tr><th>Element type</th><th>Swing component</th></tr> + * <tr><td>input, button</td><td>JButton</td></tr> + * <tr><td>input, checkbox</td><td>JButton</td></tr> + * <tr><td>input, image</td><td>JButton</td></tr> + * <tr><td>input, password</td><td>JButton</td></tr> + * <tr><td>input, radio</td><td>JButton</td></tr> + * <tr><td>input, reset</td><td>JButton</td></tr> + * <tr><td>input, submit</td><td>JButton</td></tr> + * <tr><td>input, text</td><td>JButton</td></tr> + * <tr><td>select, size > 1 or with multiple attribute</td> + * <td>JList in JScrollPane</td></tr> + * <tr><td>select, size unspecified or == 1</td><td>JComboBox</td></tr> + * <tr><td>textarea, text</td><td>JTextArea in JScrollPane</td></tr> + * <tr><td>input, file</td><td>JTextField</td></tr> + * </table> + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class FormView + extends ComponentView + implements ActionListener +{ + + /** + * If the value attribute of an <code><input type="submit">> + * tag is not specified, then this string is used. + * + * @deprecated As of JDK1.3 the value is fetched from the UIManager property + * <code>FormView.submitButtonText</code>. + */ + public static final String SUBMIT = + UIManager.getString("FormView.submitButtonText"); + + /** + * If the value attribute of an <code><input type="reset">> + * tag is not specified, then this string is used. + * + * @deprecated As of JDK1.3 the value is fetched from the UIManager property + * <code>FormView.resetButtonText</code>. + */ + public static final String RESET = + UIManager.getString("FormView.resetButtonText"); + + /** + * Creates a new <code>FormView</code>. + * + * @param el the element that is displayed by this view. + */ + public FormView(Element el) + { + super(el); + } + + /** + * Creates the correct AWT component for rendering the form element. + */ + protected Component createComponent() + { + Component comp = null; + Element el = getElement(); + Object tag = el.getAttributes().getAttribute(StyleConstants.NameAttribute); + if (tag.equals(HTML.Tag.INPUT)) + { + AttributeSet atts = el.getAttributes(); + String type = (String) atts.getAttribute(HTML.Attribute.TYPE); + String value = (String) atts.getAttribute(HTML.Attribute.VALUE); + if (type.equals("button")) + comp = new JButton(value); + else if (type.equals("checkbox")) + comp = new JCheckBox(value); + else if (type.equals("image")) + comp = new JButton(value); // FIXME: Find out how to fetch the image. + else if (type.equals("password")) + comp = new JPasswordField(value); + else if (type.equals("radio")) + comp = new JRadioButton(value); + else if (type.equals("reset")) + { + if (value == null || value.equals("")) + value = RESET; + comp = new JButton(value); + } + else if (type.equals("submit")) + { + if (value == null || value.equals("")) + value = SUBMIT; + comp = new JButton(value); + } + else if (type.equals("text")) + comp = new JTextField(value); + + } + // FIXME: Implement the remaining components. + return comp; + } + + /** + * Determines the maximum span for this view on the specified axis. + * + * @param axis the axis along which to determine the span + * + * @return the maximum span for this view on the specified axis + * + * @throws IllegalArgumentException if the axis is invalid + */ + public float getMaximumSpan(int axis) + { + // FIXME: The specs say that for some components the maximum span == the + // preferred span of the component. This should be figured out and + // implemented accordingly. + float span; + if (axis == X_AXIS) + span = getComponent().getMaximumSize().width; + else if (axis == Y_AXIS) + span = getComponent().getMaximumSize().height; + else + throw new IllegalArgumentException("Invalid axis parameter"); + return span; + } + + /** + * Processes an action from the Swing component. + * + * If the action comes from a submit button, the form is submitted by calling + * {@link #submitData}. In the case of a reset button, the form is reset to + * the original state. If the action comes from a password or text field, + * then the input focus is transferred to the next input element in the form, + * unless this text/password field is the last one, in which case the form + * is submitted. + * + * @param ev the action event + */ + public void actionPerformed(ActionEvent ev) + { + Element el = getElement(); + Object tag = el.getAttributes().getAttribute(StyleConstants.NameAttribute); + if (tag.equals(HTML.Tag.INPUT)) + { + AttributeSet atts = el.getAttributes(); + String type = (String) atts.getAttribute(HTML.Attribute.TYPE); + if (type.equals("submit")) + submitData(""); // FIXME: How to fetch the actual form data? + } + // FIXME: Implement the remaining actions. + } + + /** + * Submits the form data. A separate thread is created to do the + * transmission. + * + * @param data the form data + */ + protected void submitData(String data) + { + // FIXME: Implement this. + } + + /** + * Submits the form data in response to a click on a + * <code><input type="image"></code> element. + * + * @param imageData the mouse click coordinates + */ + protected void imageSubmit(String imageData) + { + // FIXME: Implement this. + } +} diff --git a/libjava/classpath/javax/swing/text/html/HTML.java b/libjava/classpath/javax/swing/text/html/HTML.java index 0b758d2b873..2b521cd22b4 100644 --- a/libjava/classpath/javax/swing/text/html/HTML.java +++ b/libjava/classpath/javax/swing/text/html/HTML.java @@ -57,8 +57,7 @@ public class HTML /** * Represents a HTML attribute. */ - public static class Attribute - implements Serializable + public static final class Attribute { /** * The action attribute @@ -464,47 +463,18 @@ public class HTML * The width attribute */ public static final Attribute WIDTH = new Attribute("width"); - private final String name; - - /** - * Creates the attribute with the given name. - */ - protected Attribute(String a_name) - { - name = a_name; - } - - /** - * Calls compareTo on the tag names (Strings) - */ - public int compareTo(Object other) - { - return name.compareTo(((Attribute) other).name); - } /** - * The attributes are equal if the names are equal - * (ignoring case) + * The attribute name. */ - public boolean equals(Object other) - { - if (other == this) - return true; - - if (!(other instanceof Attribute)) - return false; - - Attribute that = (Attribute) other; - - return that.name.equalsIgnoreCase(name); - } + private final String name; /** - * Returns the hash code which corresponds to the string for this tag. + * Creates the attribute with the given name. */ - public int hashCode() + private Attribute(String a_name) { - return name == null ? 0 : name.hashCode(); + name = a_name; } /** @@ -559,7 +529,6 @@ public class HTML * Represents a HTML tag. */ public static class Tag - implements Comparable, Serializable { /** * The <a> tag @@ -1047,42 +1016,6 @@ public class HTML } /** - * Calls compareTo on the tag names (Strings) - */ - public int compareTo(Object other) - { - return name.compareTo(((Tag) other).name); - } - - /** - * The tags are equal if the names are equal (ignoring case). - */ - public boolean equals(Object other) - { - if (other == this) - { - return true; - } - - if (!(other instanceof Tag)) - { - return false; - } - - Tag that = (Tag) other; - - return that.name.equalsIgnoreCase(name); - } - - /** - * Returns the hash code which corresponds to the string for this tag. - */ - public int hashCode() - { - return name == null ? 0 : name.hashCode(); - } - - /** * Returns the tag name. The names of the built-in tags are always * returned in lowercase. */ diff --git a/libjava/classpath/javax/swing/text/html/HTMLDocument.java b/libjava/classpath/javax/swing/text/html/HTMLDocument.java index 5b2452b32f6..2a96953ee91 100644 --- a/libjava/classpath/javax/swing/text/html/HTMLDocument.java +++ b/libjava/classpath/javax/swing/text/html/HTMLDocument.java @@ -38,10 +38,8 @@ exception statement from your version. */ package javax.swing.text.html; -import java.net.URL; - import java.io.IOException; - +import java.net.URL; import java.util.HashMap; import java.util.Stack; import java.util.Vector; @@ -131,16 +129,17 @@ public class HTMLDocument extends DefaultStyledDocument } /** - * Replaces the contents of the document with the given element specifications. - * This is called before insert if the loading is done in bursts. This is the - * only method called if loading the document entirely in one burst. + * Replaces the contents of the document with the given element + * specifications. This is called before insert if the loading is done + * in bursts. This is the only method called if loading the document + * entirely in one burst. * * @param data - the date that replaces the content of the document */ protected void create(DefaultStyledDocument.ElementSpec[] data) { - // FIXME: Not implemented - System.out.println("create not implemented"); + // Once the super behaviour is properly implemented it should be sufficient + // to simply call super.create(data). super.create(data); } @@ -149,11 +148,35 @@ public class HTMLDocument extends DefaultStyledDocument * * @return the new default root */ - protected AbstractDocument.AbstractElement createDefaultRoot() + protected AbstractElement createDefaultRoot() { - // FIXME: Not implemented - System.out.println("createDefaultRoot not implemented"); - return super.createDefaultRoot(); + AbstractDocument.AttributeContext ctx = getAttributeContext(); + + // Create html element. + AttributeSet atts = ctx.getEmptySet(); + atts = ctx.addAttribute(atts, StyleConstants.NameAttribute, HTML.Tag.HTML); + BranchElement html = (BranchElement) createBranchElement(null, atts); + + // Create body element. + atts = ctx.getEmptySet(); + atts = ctx.addAttribute(atts, StyleConstants.NameAttribute, HTML.Tag.BODY); + BranchElement body = (BranchElement) createBranchElement(html, atts); + html.replace(0, 0, new Element[] { body }); + + // Create p element. + atts = ctx.getEmptySet(); + atts = ctx.addAttribute(atts, StyleConstants.NameAttribute, HTML.Tag.P); + BranchElement p = (BranchElement) createBranchElement(body, atts); + body.replace(0, 0, new Element[] { p }); + + // Create an empty leaf element. + atts = ctx.getEmptySet(); + atts = ctx.addAttribute(atts, StyleConstants.NameAttribute, + HTML.Tag.CONTENT); + Element leaf = createLeafElement(p, atts, 0, 1); + p.replace(0, 0, new Element[]{ leaf }); + + return html; } /** @@ -165,28 +188,29 @@ public class HTMLDocument extends DefaultStyledDocument * @param a - the attributes for the element * @param p0 - the beginning of the range >= 0 * @param p1 - the end of the range >= p0 + * * @return the new element */ protected Element createLeafElement(Element parent, AttributeSet a, int p0, int p1) { - // FIXME: Not implemented - System.out.println("createLeafElement not implemented"); - return super.createLeafElement(parent, a, p0, p1); + RunElement el = new RunElement(parent, a, p0, p1); + el.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT); + return new RunElement(parent, a, p0, p1); } - /** This method returns an HTMLDocument.BlockElement object representing the + /** + * This method returns an HTMLDocument.BlockElement object representing the * attribute set a and attached to parent. * * @param parent - the parent element * @param a - the attributes for the element + * * @return the new element */ protected Element createBranchElement(Element parent, AttributeSet a) { - // FIXME: Not implemented - System.out.println("createBranchElement not implemented"); - return super.createBranchElement(parent, a); + return new BlockElement(parent, a); } /** @@ -204,9 +228,9 @@ public class HTMLDocument extends DefaultStyledDocument */ protected void insert(int offset, DefaultStyledDocument.ElementSpec[] data) throws BadLocationException - { - super.insert(offset, data); - } + { + super.insert(offset, data); + } /** * Updates document structure as a result of text insertion. This will happen @@ -451,7 +475,7 @@ public class HTMLDocument extends DefaultStyledDocument { public BlockElement (Element parent, AttributeSet a) { - super (parent, a); + super(parent, a); } /** @@ -470,10 +494,14 @@ public class HTMLDocument extends DefaultStyledDocument */ public String getName() { - return (String) getAttribute(StyleConstants.NameAttribute); + Object tag = getAttribute(StyleConstants.NameAttribute); + String name = null; + if (tag != null) + name = tag.toString(); + return name; } } - + /** * RunElement represents a section of text that has a set of * HTML character level attributes assigned to it. @@ -502,7 +530,11 @@ public class HTMLDocument extends DefaultStyledDocument */ public String getName() { - return (String) getAttribute(StyleConstants.NameAttribute); + Object tag = getAttribute(StyleConstants.NameAttribute); + String name = null; + if (tag != null) + name = tag.toString(); + return name; } /** @@ -531,7 +563,13 @@ public class HTMLDocument extends DefaultStyledDocument /** 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(); + /** A mapping between HTML.Tag objects and the actions that handle them **/ HashMap tagToAction; @@ -699,8 +737,8 @@ public class HTMLDocument extends DefaultStyledDocument */ public void start(HTML.Tag t, MutableAttributeSet a) { - // FIXME: Implement. - print ("ParagraphAction.start not implemented"); + // FIXME: What else must be done here? + blockOpen(t, a); } /** @@ -709,8 +747,8 @@ public class HTMLDocument extends DefaultStyledDocument */ public void end(HTML.Tag t) { - // FIXME: Implement. - print ("ParagraphAction.end not implemented"); + // FIXME: What else must be done here? + blockClose(t); } } @@ -1102,7 +1140,11 @@ public class HTMLDocument extends DefaultStyledDocument elements = new DefaultStyledDocument.ElementSpec[parseBuffer.size()]; parseBuffer.copyInto(elements); parseBuffer.removeAllElements(); - insert(offset, elements); + if (offset == 0) + create(elements); + else + insert(offset, elements); + offset += HTMLDocument.this.getLength() - offset; } @@ -1250,12 +1292,27 @@ public class HTMLDocument extends DefaultStyledDocument { printBuffer(); DefaultStyledDocument.ElementSpec element; - element = new DefaultStyledDocument.ElementSpec(attr.copyAttributes(), - DefaultStyledDocument.ElementSpec.StartTagType); + + // 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(); + copy = ctx.addAttribute(copy, StyleConstants.NameAttribute, t); + element = new DefaultStyledDocument.ElementSpec(copy, + DefaultStyledDocument.ElementSpec.StartTagType); parseBuffer.addElement(element); printBuffer(); } - + /** * Instructs the parse buffer to close the block element associated with * the given HTML.Tag @@ -1266,10 +1323,40 @@ public class HTMLDocument extends DefaultStyledDocument { printBuffer(); DefaultStyledDocument.ElementSpec element; + + // If the previous tag is a start tag then we insert a synthetic + // content tag. + DefaultStyledDocument.ElementSpec prev; + prev = (DefaultStyledDocument.ElementSpec) + parseBuffer.get(parseBuffer.size() - 1); + if (prev.getType() == DefaultStyledDocument.ElementSpec.StartTagType) + { + AbstractDocument.AttributeContext ctx = getAttributeContext(); + AttributeSet attributes = ctx.getEmptySet(); + attributes = ctx.addAttribute(attributes, StyleConstants.NameAttribute, + HTML.Tag.CONTENT); + element = new DefaultStyledDocument.ElementSpec(attributes, + DefaultStyledDocument.ElementSpec.ContentType, + 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); parseBuffer.addElement(element); printBuffer(); + if (parseStack.size() > 0) + parseStack.pop(); } /** @@ -1298,16 +1385,42 @@ public class HTMLDocument extends DefaultStyledDocument protected void addContent(char[] data, int offs, int length, boolean generateImpliedPIfNecessary) { + AbstractDocument.AttributeContext ctx = getAttributeContext(); + 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 - AttributeSet attributes = null; if (charAttr != null) attributes = charAttr.copyAttributes(); - - DefaultStyledDocument.ElementSpec element; + else + attributes = ctx.getEmptySet(); + attributes = ctx.addAttribute(attributes, StyleConstants.NameAttribute, + HTML.Tag.CONTENT); element = new DefaultStyledDocument.ElementSpec(attributes, - DefaultStyledDocument.ElementSpec.ContentType, - data, offs, length); + DefaultStyledDocument.ElementSpec.ContentType, + data, offs, length); printBuffer(); // Add the element to the buffer diff --git a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java index 1ef9768c923..2d5d1eb79da 100644 --- a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java +++ b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java @@ -56,17 +56,11 @@ import javax.accessibility.AccessibleContext; import javax.swing.Action; import javax.swing.JEditorPane; -import javax.swing.text.AbstractDocument; import javax.swing.text.BadLocationException; -import javax.swing.text.BoxView; -import javax.swing.text.ComponentView; import javax.swing.text.Document; import javax.swing.text.EditorKit; import javax.swing.text.Element; -import javax.swing.text.IconView; -import javax.swing.text.LabelView; import javax.swing.text.MutableAttributeSet; -import javax.swing.text.ParagraphView; import javax.swing.text.StyleConstants; import javax.swing.text.StyleContext; import javax.swing.text.StyledEditorKit; @@ -532,8 +526,8 @@ public class HTMLEditorKit public View create(Element element) { View view = null; - Object attr = element.getAttributes().getAttribute( - StyleConstants.NameAttribute); + Object attr = + element.getAttributes().getAttribute(StyleConstants.NameAttribute); if (attr instanceof HTML.Tag) { HTML.Tag tag = (HTML.Tag) attr; @@ -553,8 +547,16 @@ public class HTMLEditorKit view = new BlockView(element, View.Y_AXIS); // FIXME: Uncomment when the views have been implemented - /* else if (tag.equals(HTML.Tag.CONTENT)) - view = new InlineView(element); + else if (tag.equals(HTML.Tag.CONTENT)) + view = new InlineView(element); + else if (tag == HTML.Tag.HEAD) + view = new NullView(element); + else if (tag.equals(HTML.Tag.TABLE)) + view = new HTMLTableView(element); + else if (tag.equals(HTML.Tag.TD)) + view = new ParagraphView(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); @@ -564,8 +566,6 @@ public class HTMLEditorKit view = new HRuleView(element); else if (tag.equals(HTML.Tag.BR)) view = new BRView(element); - else if (tag.equals(HTML.Tag.TABLE)) - view = new TableView(element); else if (tag.equals(HTML.Tag.INPUT) || tag.equals(HTML.Tag.SELECT) || tag.equals(HTML.Tag.TEXTAREA)) view = new FormView(element); @@ -575,21 +575,11 @@ public class HTMLEditorKit view = new FrameSetView(element); else if (tag.equals(HTML.Tag.FRAME)) view = new FrameView(element); */ - } - + } if (view == null) { - String name = element.getName(); - if (name.equals(AbstractDocument.ContentElementName)) - view = new LabelView(element); - else if (name.equals(AbstractDocument.ParagraphElementName)) - view = new ParagraphView(element); - else if (name.equals(AbstractDocument.SectionElementName)) - view = new BoxView(element, View.Y_AXIS); - else if (name.equals(StyleConstants.ComponentElementName)) - view = new ComponentView(element); - else if (name.equals(StyleConstants.IconElementName)) - view = new IconView(element); + System.err.println("missing tag->view mapping for: " + element); + view = new NullView(element); } return view; } @@ -958,7 +948,8 @@ public class HTMLEditorKit throw new IOException("Parser is null."); HTMLDocument hd = ((HTMLDocument) doc); - hd.setBase(editorPane.getPage()); + if (editorPane != null) + hd.setBase(editorPane.getPage()); ParserCallback pc = hd.getReader(pos); // FIXME: What should ignoreCharSet be set to? diff --git a/libjava/classpath/javax/swing/text/html/HTMLTableView.java b/libjava/classpath/javax/swing/text/html/HTMLTableView.java new file mode 100644 index 00000000000..cac44d8dc27 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/HTMLTableView.java @@ -0,0 +1,82 @@ +/* HTMLTableView.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.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) + */ +class HTMLTableView + extends TableView +{ + + /** + * Creates a new HTMLTableView for the specified element. + * + * @param el the element for the table view + */ + public HTMLTableView(Element el) + { + super(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/InlineView.java b/libjava/classpath/javax/swing/text/html/InlineView.java new file mode 100644 index 00000000000..77ec86e8263 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/InlineView.java @@ -0,0 +1,166 @@ +/* InlineView.java -- Renders HTML content + 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.Shape; + +import javax.swing.event.DocumentEvent; +import javax.swing.text.AttributeSet; +import javax.swing.text.Document; +import javax.swing.text.Element; +import javax.swing.text.LabelView; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; + +/** + * Renders HTML content (identified by {@link HTML.Tag#CONTENT}). This is + * basically a {@link LabelView} that is adjusted to understand styles defined + * by stylesheets. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class InlineView + extends LabelView +{ + + /** + * Creates a new <code>InlineView</code> that renders the specified element. + * + * @param element the element for this view + */ + public InlineView(Element element) + { + super(element); + } + + /** + * Receives notification that something was inserted into the document in + * a location that this view is responsible for. + * + * @param e the document event + * @param a the current allocation of this view + * @param f the view factory for creating new views + * + * @since 1.5 + */ + public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) + { + // FIXME: What to do here? + super.insertUpdate(e, a, f); + } + + /** + * Receives notification that something was removed from the document in + * a location that this view is responsible for. + * + * @param e the document event + * @param a the current allocation of this view + * @param f the view factory for creating new views + * + * @since 1.5 + */ + public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) + { + // FIXME: What to do here? + super.removeUpdate(e, a, f); + } + + /** + * Receives notification that attributes have changed in the document in + * a location that this view is responsible for. This calls + * {@link #setPropertiesFromAttributes}. + * + * @param e the document event + * @param a the current allocation of this view + * @param f the view factory for creating new views + * + * @since 1.5 + */ + public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) + { + super.changedUpdate(e, a, f); + setPropertiesFromAttributes(); + } + + /** + * Returns the attributes that are used for rendering. This is implemented + * to multiplex the attributes specified in the model with a stylesheet. + * + * @return the attributes that are used for rendering + */ + public AttributeSet getAttributes() + { + // FIXME: Implement this. + return super.getAttributes(); + } + + + public int getBreakWeight(int axis, float pos, float len) + { + // FIXME: Implement this. + return super.getBreakWeight(axis, pos, len); + } + + public View breakView(int axis, int offset, float pos, float len) + { + // FIXME: Implement this. + return super.breakView(axis, offset, pos, len); + } + + protected void setPropertiesFromAttributes() + { + // FIXME: Implement this. + super.setPropertiesFromAttributes(); + } + + /** + * Returns the stylesheet used by this view. This returns the stylesheet + * of the <code>HTMLDocument</code> that is rendered by this view. + * + * @return the stylesheet used by this view + */ + protected StyleSheet getStyleSheet() + { + Document doc = getDocument(); + StyleSheet styleSheet = null; + if (doc instanceof HTMLDocument) + styleSheet = ((HTMLDocument) doc).getStyleSheet(); + return styleSheet; + } +} diff --git a/libjava/classpath/javax/swing/text/html/NullView.java b/libjava/classpath/javax/swing/text/html/NullView.java new file mode 100644 index 00000000000..4b66c5ad87e --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/NullView.java @@ -0,0 +1,102 @@ +/* NullView.java -- A dummy view that renders nothing + 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.Graphics; +import java.awt.Shape; + +import javax.swing.text.BadLocationException; +import javax.swing.text.Element; +import javax.swing.text.View; +import javax.swing.text.Position.Bias; + +/** + * A dummy view that renders nothing. This is used for invisible HTML elements + * like <head>. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class NullView + extends View +{ + + /** + * Creates a new NullView. + * + * @param elem the element + */ + public NullView(Element elem) + { + super(elem); + } + + /** + * Does nothing. + */ + public void paint(Graphics g, Shape s) + { + // Nothing to do here. + } + + /** + * Returns zero for both directions. + */ + public float getPreferredSpan(int axis) + { + return 0; + } + + /** + * Returns the allocation of this view, which should be empty anyway. + */ + public Shape modelToView(int pos, Shape a, Bias b) + throws BadLocationException + { + return a; + } + + /** + * Returns the start offset of the element. + */ + public int viewToModel(float x, float y, Shape a, Bias[] b) + { + return getElement().getStartOffset(); + } + +} diff --git a/libjava/classpath/javax/swing/text/html/ObjectView.java b/libjava/classpath/javax/swing/text/html/ObjectView.java new file mode 100644 index 00000000000..d6a77c06aad --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/ObjectView.java @@ -0,0 +1,110 @@ +/* ObjectView.java -- A view for HTML object tags + 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.Component; + +import javax.swing.text.AttributeSet; +import javax.swing.text.ComponentView; +import javax.swing.text.Element; + +/** + * A view for HTML <code><object></code> tags. + * + * This is a {@link ComponentView} that creates special components depending + * on the object specification. If the object tag has a classid attribute, then + * this view will try to load the class specified by this attribute using the + * classloader that loaded the associated document. If the class could be + * loaded, an instance is created and the type narrowed to {@link Component}. + * + * It is also possible to set bean properties on the created component using + * nested <code><param></code> tags. For example: + * <pre> + * <object classid="javax.swing.JLabel"> + * <param name="text" value="sample text"> + * </object> + * </pre> + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class ObjectView extends ComponentView +{ + + /** + * Creates a new <code>ObjectView</code>. + * + * @param el the element for which to create a view + */ + public ObjectView(Element el) + { + super(el); + } + + /** + * Creates a component based on the specification in the element of this + * view. See the class description for details. + */ + protected Component createComponent() + { + Component comp = null; + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + String classId = (String) atts.getAttribute("classid"); + try + { + Class objectClass = Class.forName(classId); + Object instance = objectClass.newInstance(); + comp = (Component) instance; + } + catch (ClassNotFoundException ex) + { + // Ignored. + } + catch (IllegalAccessException ex) + { + // Ignored. + } + catch (InstantiationException ex) + { + // Ignored. + } + // FIXME: Handle param tags and set bean properties accordingly. + return comp; + } +} diff --git a/libjava/classpath/javax/swing/text/html/Option.java b/libjava/classpath/javax/swing/text/html/Option.java new file mode 100644 index 00000000000..1def51b2f59 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/Option.java @@ -0,0 +1,157 @@ +/* Option.java -- Value class for <option> list model elements + 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.AttributeSet; + +/** + * Value class for the combobox model that renders <code><option></code> + * elements. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class Option +{ + + /** + * The attributes of the <option> tag. + */ + private AttributeSet attributes; + + /** + * The label. + */ + private String label; + + /** + * The selected state of this label. + */ + private boolean selected; + + /** + * Creates a new <code>Option</code> instance that uses the specified + * tag attributes. + * + * @param attr the attributes to use + */ + public Option(AttributeSet attr) + { + attributes = attr; + label = null; + selected = false; + // FIXME: Probably initialize something using the attributes. + } + + /** + * Sets the label to use for this <code><option></code> tag. + * + * @param l the label to set + */ + public void setLabel(String l) + { + label = l; + } + + /** + * Returns the label of this <code><option></code> tag. + * + * @return the label of this <code><option></code> tag + */ + public String getLabel() + { + return label; + } + + /** + * Returns the attributes used to render this <code><option></code> + * tag. + * + * @return the attributes used to render this <code><option></code> tag + */ + public AttributeSet getAttributes() + { + return attributes; + } + + /** + * Returns a string representation of this <code><option></code> tag. + * This returns the <code>label</code> property. + * + * @return a string representation of this <code><option></code> tag + */ + public String toString() + { + return label; + } + + /** + * Sets the selected state of this <code><option></code> tag. + * + * @param s the selected state to set + */ + protected void setSelection(boolean s) + { + selected = s; + } + + /** + * Returns <code>true</code> when this option is selected, <code>false</code> + * otherwise. + * + * @return <code>true</code> when this option is selected, <code>false</code> + * otherwise + */ + public boolean isSelected() + { + return selected; + } + + /** + * Returns the string associated with the <code>value</code> attribute or + * the label, if no such attribute is specified. + * + * @return the string associated with the <code>value</code> attribute or + * the label, if no such attribute is specified + */ + public String getValue() + { + // FIXME: Return some attribute here if specified. + return label; + } +} diff --git a/libjava/classpath/javax/swing/text/html/ParagraphView.java b/libjava/classpath/javax/swing/text/html/ParagraphView.java new file mode 100644 index 00000000000..2339f4e661d --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/ParagraphView.java @@ -0,0 +1,209 @@ +/* ParagraphView.java -- Renders a paragraph in HTML + 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.Graphics; +import java.awt.Shape; + +import javax.swing.SizeRequirements; +import javax.swing.text.AttributeSet; +import javax.swing.text.Document; +import javax.swing.text.Element; +import javax.swing.text.View; + +/** + * Renders a paragraph in HTML. This is a subclass of + * {@link javax.swing.text.ParagraphView} with some adjustments for + * understanding stylesheets. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class ParagraphView + extends javax.swing.text.ParagraphView +{ + + /** + * Creates a new ParagraphView for the specified element. + * + * @param element the element + */ + public ParagraphView(Element element) + { + super(element); + } + + /** + * Sets the parent of this view. This is implemented to call the parent + * functionality and then trigger {@link #setPropertiesFromAttributes} in + * order to load the stylesheet attributes. + * + * @param parent the parent view to set + */ + public void setParent(View parent) + { + super.setParent(parent); + if (parent != null) + setPropertiesFromAttributes(); + } + + /** + * Returns the attributes used by this view. This is implemented to multiplex + * the attributes of the model with the attributes of the stylesheet. + */ + public AttributeSet getAttributes() + { + // FIXME: Implement this multiplexing thing. + return super.getAttributes(); + } + + /** + * Loads the visual properties of the ParagraphView from the element's + * attributes and the stylesheet of the HTML document. + */ + protected void setPropertiesFromAttributes() + { + // FIXME: Implement this. + } + + /** + * Returns the stylesheet used by this view. + * + * @return the stylesheet used by this view + */ + protected StyleSheet getStyleSheet() + { + Document doc = getDocument(); + StyleSheet styleSheet = null; + if (doc instanceof HTMLDocument) + styleSheet = ((HTMLDocument) doc).getStyleSheet(); + return styleSheet; + } + + /** + * Calculates the minor axis requirements of this view. This is implemented + * to return the super class'es requirements and modifies the minimumSpan + * slightly so that it is not smaller than the length of the longest word. + * + * @param axis the axis + * @param r the SizeRequirements object to be used as return parameter; + * if <code>null</code> a new one will be created + * + * @return the requirements along the minor layout axis + */ + protected SizeRequirements calculateMinorAxisRequirements(int axis, + SizeRequirements r) + { + // FIXME: Implement the above specified behaviour. + return super.calculateMinorAxisRequirements(axis, r); + } + + /** + * Determines if this view is visible or not. If none of the children is + * visible and the only visible child is the break that ends the paragraph, + * this paragraph is not considered to be visible. + * + * @return the visibility of this paragraph + */ + public boolean isVisible() + { + // FIXME: Implement the above specified behaviour. + return super.isVisible(); + } + + /** + * Paints this view. This delegates to the superclass after the coordinates + * have been updated for tab calculations. + * + * @param g the graphics object + * @param a the current allocation of this view + */ + public void paint(Graphics g, Shape a) + { + // FIXME: Implement the above specified behaviour. + super.paint(g, a); + } + + /** + * Returns the preferred span of this view. If this view is not visible, + * we return <code>0</code>, otherwise the super class is called. + * + * @param axis the axis + * + * @return the preferred span of this view + */ + public float getPreferredSpan(int axis) + { + float span = 0; + if (isVisible()) + span = super.getPreferredSpan(axis); + return span; + } + + /** + * Returns the minimum span of this view. If this view is not visible, + * we return <code>0</code>, otherwise the super class is called. + * + * @param axis the axis + * + * @return the minimum span of this view + */ + public float getMinimumSpan(int axis) + { + float span = 0; + if (isVisible()) + span = super.getMinimumSpan(axis); + return span; + } + + /** + * Returns the maximum span of this view. If this view is not visible, + * we return <code>0</code>, otherwise the super class is called. + * + * @param axis the axis + * + * @return the maximum span of this view + */ + public float getMaximumSpan(int axis) + { + float span = 0; + if (isVisible()) + span = super.getMaximumSpan(axis); + return span; + } +} diff --git a/libjava/classpath/javax/swing/text/package.html b/libjava/classpath/javax/swing/text/package.html index 50043b6c4e8..5db555d8898 100644 --- a/libjava/classpath/javax/swing/text/package.html +++ b/libjava/classpath/javax/swing/text/package.html @@ -40,7 +40,7 @@ exception statement from your version. --> <head><title>GNU Classpath - javax.swing.text</title></head> <body> -<p></p> - +<p>Provides core text classes and interfaces representing models and views +used by the text components for display and editing of text.</p> </body> </html> diff --git a/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java b/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java index ae8b99c2fe5..e28c9261bab 100644 --- a/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java +++ b/libjava/classpath/javax/swing/tree/DefaultTreeCellEditor.java @@ -45,7 +45,6 @@ import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; -import java.awt.Insets; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -60,26 +59,52 @@ import javax.swing.Icon; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.SwingUtilities; -import javax.swing.UIDefaults; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.event.CellEditorListener; +import javax.swing.event.ChangeEvent; import javax.swing.event.EventListenerList; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; /** - * DefaultTreeCellEditor + * Participates in the tree cell editing. + * * @author Andrew Selkirk + * @author Audrius Meskauskas */ public class DefaultTreeCellEditor implements ActionListener, TreeCellEditor, TreeSelectionListener { /** - * EditorContainer + * The gap between the icon and editing component during editing. + */ + static int ICON_TEXT_GAP = 3; + + /** + * The left margin of the editing container (the gap between the tree and + * the editing component of the editing icon. + */ + static int TREE_ICON_GAP = ICON_TEXT_GAP; + + /** + * The number of the fast mouse clicks, required to start the editing + * session. + */ + static int CLICK_COUNT_TO_START = 3; + + /** + * This container that appears on the tree during editing session. + * It contains the editing component displays various other editor - + * specific parts like editing icon. */ public class EditorContainer extends Container { + /** + * Use v 1.5 serial version UID for interoperability. + */ + static final long serialVersionUID = 6470339600449699810L; + /** * Creates an <code>EditorContainer</code> object. */ @@ -96,17 +121,11 @@ public class DefaultTreeCellEditor { // Do nothing here. } - - /** - * Returns the preferred size for the Container. - * - * @return Dimension of EditorContainer - */ - public Dimension getPreferredSize() + + public void setBounds(Rectangle bounds) { - Dimension containerSize = super.getPreferredSize(); - containerSize.width += DefaultTreeCellEditor.this.offset; - return containerSize; + super.setBounds(bounds); + doLayout(); } /** @@ -118,63 +137,68 @@ public class DefaultTreeCellEditor */ public void paint(Graphics g) { - Rectangle tr = tree.getPathBounds(lastPath); - if (tr != null) + if (editingIcon != null) { - Insets i = ((DefaultTextField) editingComponent).getBorder() - .getBorderInsets(this); - int textIconGap = 3; - tr.x -= i.left; - - // paints icon - if (editingIcon != null) - { - editingIcon.paintIcon(this, g, tr.x - editingIcon. - getIconWidth()/2, tr.y + i.top + i.bottom); - tr.x += editingIcon.getIconWidth()/2 + textIconGap; - } - - tr.width += offset; - - // paint background - g.translate(tr.x, tr.y); - editingComponent.setSize(new Dimension(tr.width, tr.height)); - editingComponent.paint(g); - g.translate(-tr.x, -tr.y); + // From the previous version, the left margin is taken as half + // of the icon width. + editingIcon.paintIcon(this, g, TREE_ICON_GAP, 0); } super.paint(g); } /** - * Lays out this Container. If editing, the editor will be placed at offset - * in the x direction and 0 for y. + * Lays out this Container, moving the editor component to the left + * (leaving place for the icon). */ public void doLayout() { - if (DefaultTreeCellEditor.this.tree.isEditing()) - setLocation(offset, 0); - super.doLayout(); + // The offset of the editing component. + int eOffset; + + // Move the component to the left, leaving room for the editing icon: + if (editingIcon != null) + eOffset = TREE_ICON_GAP + editingIcon.getIconWidth() + ICON_TEXT_GAP; + else + eOffset = 0; + + Rectangle bounds = getBounds(); + Component c = getComponent(0); + c.setLocation(eOffset, 0); + + // Span the editing component near over all window width. + c.setSize(bounds.width - eOffset - TREE_ICON_GAP, bounds.height); + /* + * @specnote the Sun sets some more narrow editing component width (it is + * not documented how does it is calculated). However as our text field is + * still not able to auto - scroll horizontally, replicating such strategy + * would prevent adding extra characters to the text being edited. + */ } } /** - * DefaultTextField + * The default text field, used in the editing sessions. */ public class DefaultTextField extends JTextField { + /** + * Use v 1.5 serial version UID for interoperability. + */ + static final long serialVersionUID = -6629304544265300143L; + /** - * border + * The border of the text field. */ protected Border border; /** * Creates a <code>DefaultTextField</code> object. * - * @param border the border to use + * @param aBorder the border to use */ - public DefaultTextField(Border border) + public DefaultTextField(Border aBorder) { - this.border = border; + border = aBorder; } /** @@ -228,6 +252,31 @@ public class DefaultTreeCellEditor return renderer.getPreferredSize(); } } + + /** + * Listens for the events from the realEditor. + */ + class RealEditorListener implements CellEditorListener + { + /** + * The method is called when the editing has been cancelled. + * @param event unused + */ + public void editingCanceled(ChangeEvent event) + { + cancelCellEditing(); + } + + /** + * The method is called after completing the editing session. + * + * @param event unused + */ + public void editingStopped(ChangeEvent event) + { + stopCellEditing(); + } + } private EventListenerList listenerList = new EventListenerList(); @@ -334,6 +383,9 @@ public class DefaultTreeCellEditor if (editor == null) editor = createTreeCellEditor(); + else + editor.addCellEditorListener(new RealEditorListener()); + realEditor = editor; lastPath = tree.getLeadSelectionPath(); @@ -342,7 +394,6 @@ public class DefaultTreeCellEditor setFont(UIManager.getFont("Tree.font")); setBorderSelectionColor(UIManager.getColor("Tree.selectionBorderColor")); editingIcon = renderer.getIcon(); - timer = new javax.swing.Timer(1200, this); } /** @@ -371,7 +422,7 @@ public class DefaultTreeCellEditor else renderer.setIcon(renderer.getClosedIcon()); editingIcon = renderer.getIcon(); - + editingComponent = getTreeCellEditorComponent(tree, val, true, expanded, isLeaf, lastRow); } @@ -470,20 +521,21 @@ public class DefaultTreeCellEditor boolean leaf, int row) { if (realEditor == null) - createTreeCellEditor(); + realEditor = createTreeCellEditor(); return realEditor.getTreeCellEditorComponent(tree, value, isSelected, expanded, leaf, row); } /** - * Returns the value currently being edited. + * Returns the value currently being edited (requests it from the + * {@link realEditor}. * * @return the value currently being edited */ public Object getCellEditorValue() { - return editingComponent; + return realEditor.getCellEditorValue(); } /** @@ -503,12 +555,6 @@ public class DefaultTreeCellEditor prepareForEditing(); return true; } - - // Cell may not be currently editable, but may need to start timer. - if (shouldStartEditingTimer(event)) - startEditingTimer(); - else if (timer.isRunning()) - timer.stop(); return false; } @@ -532,9 +578,11 @@ public class DefaultTreeCellEditor */ public boolean stopCellEditing() { - if (editingComponent != null && realEditor.stopCellEditing()) + if (editingComponent != null) { - timer.stop(); + stopEditingTimer(); + tree.stopEditing(); + editingComponent = null; return true; } return false; @@ -548,15 +596,26 @@ public class DefaultTreeCellEditor { if (editingComponent != null) { - timer.stop(); - realEditor.cancelCellEditing(); + tree.cancelEditing(); + editingComponent = null; } + stopEditingTimer(); + } + + /** + * Stop the editing timer, if it is installed and running. + */ + private void stopEditingTimer() + { + if (timer != null && timer.isRunning()) + timer.stop(); } /** * Adds a <code>CellEditorListener</code> object to this editor. - * - * @param listener the listener to add + * + * @param listener + * the listener to add */ public void addCellEditorListener(CellEditorListener listener) { @@ -595,21 +654,16 @@ public class DefaultTreeCellEditor tPath = lastPath; lastPath = e.getNewLeadSelectionPath(); lastRow = tree.getRowForPath(lastPath); - configureEditingComponent(tree, renderer, realEditor); + stopCellEditing(); } /** - * Messaged when the timer fires, this will start the editing session. + * Messaged when the timer fires. * * @param e the event that characterizes the action. */ public void actionPerformed(ActionEvent e) { - if (lastPath != null && tPath != null && tPath.equals(lastPath)) - { - tree.startEditingAtPath(lastPath); - timer.stop(); - } } /** @@ -639,13 +693,11 @@ public class DefaultTreeCellEditor } /** - * Starts the editing timer. + * Starts the editing timer (if one installed). */ protected void startEditingTimer() { - if (timer == null) - timer = new javax.swing.Timer(1200, this); - if (!timer.isRunning()) + if (timer != null) timer.start(); } @@ -713,6 +765,7 @@ public class DefaultTreeCellEditor */ protected void prepareForEditing() { + editingContainer.removeAll(); editingContainer.add(editingComponent); } @@ -734,8 +787,11 @@ public class DefaultTreeCellEditor */ protected TreeCellEditor createTreeCellEditor() { - realEditor = new DefaultCellEditor(new DefaultTreeCellEditor.DefaultTextField( + DefaultCellEditor editor = new DefaultCellEditor(new DefaultTreeCellEditor.DefaultTextField( UIManager.getBorder("Tree.selectionBorder"))); - return realEditor; + editor.addCellEditorListener(new RealEditorListener()); + editor.setClickCountToStart(CLICK_COUNT_TO_START); + realEditor = editor; + return editor; } } diff --git a/libjava/classpath/javax/swing/undo/StateEdit.java b/libjava/classpath/javax/swing/undo/StateEdit.java index 80e4e33ec29..326abea1f4e 100644 --- a/libjava/classpath/javax/swing/undo/StateEdit.java +++ b/libjava/classpath/javax/swing/undo/StateEdit.java @@ -104,9 +104,11 @@ public class StateEdit * System (RCS). This certainly should not be part of the API * specification. But in order to be API-compatible with * Sun’s reference implementation, GNU Classpath also has to - * provide this field. However, we do not try to match its value. + * provide this field and match its value. The value used here has + * been in every JDK release at least from 1.2 to 1.5. */ - protected static final String RCSID = ""; + protected static final String RCSID = "$" + + "Id: StateEdit.java,v 1.6 1997/10/01 20:05:51 sandipc Exp $"; /** diff --git a/libjava/classpath/javax/swing/undo/StateEditable.java b/libjava/classpath/javax/swing/undo/StateEditable.java index 9a7fb09545d..bec396e1e20 100644 --- a/libjava/classpath/javax/swing/undo/StateEditable.java +++ b/libjava/classpath/javax/swing/undo/StateEditable.java @@ -78,9 +78,11 @@ public interface StateEditable * System (RCS). This certainly should not be part of the API * specification. But in order to be API-compatible with * Sun’s reference implementation, GNU Classpath also has to - * provide this field. However, we do not try to match its value. + * provide this field and match its value. The value used here has + * been in every JDK release at least from 1.2 to 1.5. */ - String RCSID = ""; + String RCSID = "$" + + "Id: StateEditable.java,v 1.2 1997/09/08 19:39:08 marklin Exp $"; /** |