aboutsummaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing/text
diff options
context:
space:
mode:
authorMark Wielaard <mark@gcc.gnu.org>2006-03-10 21:46:48 +0000
committerMark Wielaard <mark@gcc.gnu.org>2006-03-10 21:46:48 +0000
commit8aa540d2f783474d1d2e06f16744bf67b9c1facc (patch)
treeea38c56431c5d4528fb54254c3f8e50f517bede3 /libjava/classpath/javax/swing/text
parent27079765d00123f8e53d0e1ef7f9d46559266e6d (diff)
downloadgcc-8aa540d2f783474d1d2e06f16744bf67b9c1facc.zip
gcc-8aa540d2f783474d1d2e06f16744bf67b9c1facc.tar.gz
gcc-8aa540d2f783474d1d2e06f16744bf67b9c1facc.tar.bz2
Imported GNU Classpath 0.90
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. From-SVN: r111942
Diffstat (limited to 'libjava/classpath/javax/swing/text')
-rw-r--r--libjava/classpath/javax/swing/text/AbstractDocument.java167
-rw-r--r--libjava/classpath/javax/swing/text/AsyncBoxView.java1480
-rw-r--r--libjava/classpath/javax/swing/text/BoxView.java594
-rw-r--r--libjava/classpath/javax/swing/text/ComponentView.java3
-rw-r--r--libjava/classpath/javax/swing/text/CompositeView.java44
-rw-r--r--libjava/classpath/javax/swing/text/DefaultCaret.java123
-rw-r--r--libjava/classpath/javax/swing/text/DefaultEditorKit.java332
-rw-r--r--libjava/classpath/javax/swing/text/DefaultFormatter.java7
-rw-r--r--libjava/classpath/javax/swing/text/DefaultHighlighter.java140
-rw-r--r--libjava/classpath/javax/swing/text/DefaultStyledDocument.java2091
-rw-r--r--libjava/classpath/javax/swing/text/DefaultTextUI.java1
-rw-r--r--libjava/classpath/javax/swing/text/FlowView.java394
-rw-r--r--libjava/classpath/javax/swing/text/GapContent.java167
-rw-r--r--libjava/classpath/javax/swing/text/GlyphView.java126
-rw-r--r--libjava/classpath/javax/swing/text/IconView.java2
-rw-r--r--libjava/classpath/javax/swing/text/JTextComponent.java395
-rw-r--r--libjava/classpath/javax/swing/text/LabelView.java6
-rw-r--r--libjava/classpath/javax/swing/text/MutableAttributeSet.java64
-rw-r--r--libjava/classpath/javax/swing/text/NavigationFilter.java27
-rw-r--r--libjava/classpath/javax/swing/text/ParagraphView.java203
-rw-r--r--libjava/classpath/javax/swing/text/PasswordView.java44
-rw-r--r--libjava/classpath/javax/swing/text/PlainDocument.java55
-rw-r--r--libjava/classpath/javax/swing/text/PlainView.java110
-rw-r--r--libjava/classpath/javax/swing/text/Segment.java120
-rw-r--r--libjava/classpath/javax/swing/text/SimpleAttributeSet.java195
-rw-r--r--libjava/classpath/javax/swing/text/StringContent.java138
-rw-r--r--libjava/classpath/javax/swing/text/StyleConstants.java823
-rw-r--r--libjava/classpath/javax/swing/text/StyleContext.java25
-rw-r--r--libjava/classpath/javax/swing/text/TableView.java56
-rw-r--r--libjava/classpath/javax/swing/text/Utilities.java99
-rw-r--r--libjava/classpath/javax/swing/text/View.java136
-rw-r--r--libjava/classpath/javax/swing/text/WrappedPlainView.java3
-rw-r--r--libjava/classpath/javax/swing/text/html/FormView.java230
-rw-r--r--libjava/classpath/javax/swing/text/html/HTML.java79
-rw-r--r--libjava/classpath/javax/swing/text/html/HTMLDocument.java193
-rw-r--r--libjava/classpath/javax/swing/text/html/HTMLEditorKit.java43
-rw-r--r--libjava/classpath/javax/swing/text/html/HTMLTableView.java82
-rw-r--r--libjava/classpath/javax/swing/text/html/InlineView.java166
-rw-r--r--libjava/classpath/javax/swing/text/html/NullView.java102
-rw-r--r--libjava/classpath/javax/swing/text/html/ObjectView.java110
-rw-r--r--libjava/classpath/javax/swing/text/html/Option.java157
-rw-r--r--libjava/classpath/javax/swing/text/html/ParagraphView.java209
-rw-r--r--libjava/classpath/javax/swing/text/package.html4
43 files changed, 7534 insertions, 2011 deletions
diff --git a/libjava/classpath/javax/swing/text/AbstractDocument.java b/libjava/classpath/javax/swing/text/AbstractDocument.java
index c735388..a3e8c46 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 0000000..1988bba
--- /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 5c9587d..b5907dc 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 2846f8b..a7d237a 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 cd66452..a10aca7 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 776ef69..f2a68c0 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 88094b8..c005696 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 493699d..e42b169 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 40ea4f8..33b5fca 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 46b8225..625ba4c 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
* &quot;section&quot;.
- *
+ *
* @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 e7ff018..c347668 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 6d4b9cd..8be8f41 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 80dcfa5..28d1d6e 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 &lt; sign, the gapEnd is marked by a &gt;
* 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 47deb50..d505274 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 af2581a..699cda9 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 afa1f24..6b8348c 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 4890735..03279c4 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 2fe9ad5..3728b9c 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 45f58f9..ea9b29d 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 c486450..15bed78 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 e54331c5..9d4c86a 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 2200cae..c699dcad2 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 a318ee7..4bb3a8e 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 84e0e70..875d996 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 0c9f607..8dbcb0c 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 7db377a..0a31505 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 598eaf6..c7906b8 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 dabc0ba..e2643a2 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 d3113b8..2dcb9eb 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 1adc8ff..d109a4a 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 b835842..2feaf29 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 baba343..e2790a0 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 0000000..b85c694
--- /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>&lt;input type=&quot;submit&quot;&gt>
+ * 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>&lt;input type=&quot;reset&quot;&gt>
+ * 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>&lt;input type=&quot;image&quot;&gt;</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 0b758d2..2b521cd 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 &lt;a&gt; 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 5b2452b..2a96953 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 1ef9768..2d5d1eb 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 0000000..cac44d8
--- /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 0000000..77ec86e
--- /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 0000000..4b66c5a
--- /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 0000000..d6a77c0
--- /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>&lt;object&gt;</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>&lt;param&gt;</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 0000000..1def51b
--- /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>&lt;option&gt;</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>&lt;option&gt;</code> tag.
+ *
+ * @param l the label to set
+ */
+ public void setLabel(String l)
+ {
+ label = l;
+ }
+
+ /**
+ * Returns the label of this <code>&lt;option&gt;</code> tag.
+ *
+ * @return the label of this <code>&lt;option&gt;</code> tag
+ */
+ public String getLabel()
+ {
+ return label;
+ }
+
+ /**
+ * Returns the attributes used to render this <code>&lt;option&gt;</code>
+ * tag.
+ *
+ * @return the attributes used to render this <code>&lt;option&gt;</code> tag
+ */
+ public AttributeSet getAttributes()
+ {
+ return attributes;
+ }
+
+ /**
+ * Returns a string representation of this <code>&lt;option&gt;</code> tag.
+ * This returns the <code>label</code> property.
+ *
+ * @return a string representation of this <code>&lt;option&gt;</code> tag
+ */
+ public String toString()
+ {
+ return label;
+ }
+
+ /**
+ * Sets the selected state of this <code>&lt;option&gt;</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 0000000..2339f4e
--- /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 50043b6..5db555d 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>