summaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing/text
diff options
context:
space:
mode:
authormark <mark@138bc75d-0d04-0410-961f-82ee72b054a4>2005-11-15 23:20:01 +0000
committermark <mark@138bc75d-0d04-0410-961f-82ee72b054a4>2005-11-15 23:20:01 +0000
commit3b3101d8b5ae4f08a16c0b7111da6cad41bbd282 (patch)
treea5eb7cf42a51869cc8aa1fad7ad6a90cca47fdd8 /libjava/classpath/javax/swing/text
parent7e55c49d7d91ef9f09e93c1100119b1ab3652446 (diff)
downloadgcc-3b3101d8b5ae4f08a16c0b7111da6cad41bbd282.tar.gz
Imported GNU Classpath 0.19 + gcj-import-20051115.
* sources.am: Regenerated. * Makefile.in: Likewise. * scripts/makemake.tcl: Use glob -nocomplain. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@107049 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava/classpath/javax/swing/text')
-rw-r--r--libjava/classpath/javax/swing/text/AbstractDocument.java326
-rw-r--r--libjava/classpath/javax/swing/text/AttributeSet.java6
-rw-r--r--libjava/classpath/javax/swing/text/BoxView.java256
-rw-r--r--libjava/classpath/javax/swing/text/ComponentView.java200
-rw-r--r--libjava/classpath/javax/swing/text/CompositeView.java66
-rw-r--r--libjava/classpath/javax/swing/text/DefaultCaret.java409
-rw-r--r--libjava/classpath/javax/swing/text/DefaultEditorKit.java45
-rw-r--r--libjava/classpath/javax/swing/text/DefaultFormatter.java15
-rw-r--r--libjava/classpath/javax/swing/text/DefaultHighlighter.java2
-rw-r--r--libjava/classpath/javax/swing/text/DefaultStyledDocument.java1032
-rw-r--r--libjava/classpath/javax/swing/text/DefaultTextUI.java61
-rw-r--r--libjava/classpath/javax/swing/text/EditorKit.java10
-rw-r--r--libjava/classpath/javax/swing/text/FieldView.java17
-rw-r--r--libjava/classpath/javax/swing/text/FlowView.java70
-rw-r--r--libjava/classpath/javax/swing/text/GapContent.java382
-rw-r--r--libjava/classpath/javax/swing/text/GlyphView.java652
-rw-r--r--libjava/classpath/javax/swing/text/IconView.java32
-rw-r--r--libjava/classpath/javax/swing/text/InternationalFormatter.java5
-rw-r--r--libjava/classpath/javax/swing/text/JTextComponent.java211
-rw-r--r--libjava/classpath/javax/swing/text/LabelView.java242
-rw-r--r--libjava/classpath/javax/swing/text/LayoutQueue.java1
-rw-r--r--libjava/classpath/javax/swing/text/ParagraphView.java30
-rw-r--r--libjava/classpath/javax/swing/text/PlainDocument.java28
-rw-r--r--libjava/classpath/javax/swing/text/PlainView.java371
-rw-r--r--libjava/classpath/javax/swing/text/Segment.java4
-rw-r--r--libjava/classpath/javax/swing/text/SimpleAttributeSet.java57
-rw-r--r--libjava/classpath/javax/swing/text/StringContent.java3
-rw-r--r--libjava/classpath/javax/swing/text/StyleConstants.java6
-rw-r--r--libjava/classpath/javax/swing/text/StyleContext.java19
-rw-r--r--libjava/classpath/javax/swing/text/StyledDocument.java191
-rw-r--r--libjava/classpath/javax/swing/text/StyledEditorKit.java24
-rw-r--r--libjava/classpath/javax/swing/text/TabSet.java3
-rw-r--r--libjava/classpath/javax/swing/text/TabStop.java3
-rw-r--r--libjava/classpath/javax/swing/text/Utilities.java410
-rw-r--r--libjava/classpath/javax/swing/text/View.java76
-rw-r--r--libjava/classpath/javax/swing/text/WrappedPlainView.java700
-rw-r--r--libjava/classpath/javax/swing/text/html/CSS.java461
-rw-r--r--libjava/classpath/javax/swing/text/html/HTML.java13
-rw-r--r--libjava/classpath/javax/swing/text/html/HTMLDocument.java215
-rw-r--r--libjava/classpath/javax/swing/text/html/HTMLEditorKit.java38
-rw-r--r--libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java4
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/ContentModel.java5
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/DTD.java6
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/DocumentParser.java7
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/Element.java2
-rw-r--r--libjava/classpath/javax/swing/text/html/parser/Parser.java10
46 files changed, 5911 insertions, 815 deletions
diff --git a/libjava/classpath/javax/swing/text/AbstractDocument.java b/libjava/classpath/javax/swing/text/AbstractDocument.java
index 3c9a4d497a5..baf8608b888 100644
--- a/libjava/classpath/javax/swing/text/AbstractDocument.java
+++ b/libjava/classpath/javax/swing/text/AbstractDocument.java
@@ -65,11 +65,10 @@ import javax.swing.undo.UndoableEdit;
* @author original author unknown
* @author Roman Kennke (roman@kennke.org)
*/
-public abstract class AbstractDocument
- implements Document, Serializable
+public abstract class AbstractDocument implements Document, Serializable
{
- /** The serial version UID for this class as of JDK1.4. */
- private static final long serialVersionUID = -116069779446114664L;
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 6842927725919637215L;
/**
* Standard error message to indicate a bad location.
@@ -128,7 +127,28 @@ public abstract class AbstractDocument
* Manages event listeners for this <code>Document</code>.
*/
protected EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * Stores the current writer thread. Used for locking.
+ */
+ private Thread currentWriter = null;
+
+ /**
+ * The number of readers. Used for locking.
+ */
+ private int numReaders = 0;
+
+ /**
+ * Tells if there are one or more writers waiting.
+ */
+ private int numWritersWaiting = 0;
+
+ /**
+ * A condition variable that readers and writers wait on.
+ */
+ Object documentCV = new Object();
+
/**
* Creates a new <code>AbstractDocument</code> with the specified
* {@link Content} model.
@@ -332,7 +352,7 @@ public abstract class AbstractDocument
* @see GapContent
* @see StringContent
*/
- protected Content getContent()
+ protected final Content getContent()
{
return content;
}
@@ -348,8 +368,7 @@ public abstract class AbstractDocument
*/
protected Thread getCurrentWriter()
{
- // FIXME: Implement locking!
- return null;
+ return currentWriter;
}
/**
@@ -516,13 +535,18 @@ public abstract class AbstractDocument
// Just return when no text to insert was given.
if (text == null || text.length() == 0)
return;
-
DefaultDocumentEvent event =
new DefaultDocumentEvent(offset, text.length(),
DocumentEvent.EventType.INSERT);
- content.insertString(offset, text);
+
+ writeLock();
+ UndoableEdit undo = content.insertString(offset, text);
insertUpdate(event, attributes);
+ writeUnlock();
+
fireInsertUpdate(event);
+ if (undo != null)
+ fireUndoableEditUpdate(new UndoableEditEvent(this, undo));
}
/**
@@ -566,10 +590,28 @@ public abstract class AbstractDocument
}
/**
- * Blocks until a read lock can be obtained.
+ * Blocks until a read lock can be obtained. Must block if there is
+ * currently a writer modifying the <code>Document</code>.
*/
public void readLock()
{
+ if (currentWriter != null && currentWriter.equals(Thread.currentThread()))
+ return;
+ synchronized (documentCV)
+ {
+ while (currentWriter != null || numWritersWaiting > 0)
+ {
+ try
+ {
+ documentCV.wait();
+ }
+ catch (InterruptedException ie)
+ {
+ throw new Error("interrupted trying to get a readLock");
+ }
+ }
+ numReaders++;
+ }
}
/**
@@ -578,6 +620,40 @@ public abstract class AbstractDocument
*/
public void readUnlock()
{
+ // Note we could have a problem here if readUnlock was called without a
+ // prior call to readLock but the specs simply warn users to ensure that
+ // balance by using a finally block:
+ // readLock()
+ // try
+ // {
+ // doSomethingHere
+ // }
+ // finally
+ // {
+ // readUnlock();
+ // }
+
+ // All that the JDK seems to check for is that you don't call unlock
+ // more times than you've previously called lock, but it doesn't make
+ // sure that the threads calling unlock were the same ones that called lock
+
+ // FIXME: the reference implementation throws a
+ // javax.swing.text.StateInvariantError here
+ if (numReaders == 0)
+ throw new IllegalStateException("document lock failure");
+
+ synchronized (documentCV)
+ {
+ // If currentWriter is not null, the application code probably had a
+ // writeLock and then tried to obtain a readLock, in which case
+ // numReaders wasn't incremented
+ if (currentWriter == null)
+ {
+ numReaders --;
+ if (numReaders == 0 && numWritersWaiting != 0)
+ documentCV.notify();
+ }
+ }
}
/**
@@ -595,10 +671,42 @@ public abstract class AbstractDocument
DefaultDocumentEvent event =
new DefaultDocumentEvent(offset, length,
DocumentEvent.EventType.REMOVE);
+
+ // Here we set up the parameters for an ElementChange, if one
+ // needs to be added to the DocumentEvent later
+ Element root = getDefaultRootElement();
+ int start = root.getElementIndex(offset);
+ int end = root.getElementIndex(offset + length);
+
+ Element[] removed = new Element[end - start + 1];
+ for (int i = start; i <= end; i++)
+ removed[i - start] = root.getElement(i);
+
removeUpdate(event);
- content.remove(offset, length);
+
+ Element[] added = new Element[1];
+ added[0] = root.getElement(start);
+ boolean shouldFire = content.getString(offset, length).length() != 0;
+
+ writeLock();
+ UndoableEdit temp = content.remove(offset, length);
+ writeUnlock();
+
postRemoveUpdate(event);
- fireRemoveUpdate(event);
+
+ GapContent.UndoRemove changes = null;
+ if (content instanceof GapContent)
+ changes = (GapContent.UndoRemove) temp;
+
+ if (changes != null && !(start == end))
+ {
+ // We need to add an ElementChange to our DocumentEvent
+ ElementEdit edit = new ElementEdit (root, start, removed, added);
+ event.addEdit(edit);
+ }
+
+ if (shouldFire)
+ fireRemoveUpdate(event);
}
/**
@@ -713,7 +821,15 @@ public abstract class AbstractDocument
*/
public void render(Runnable runnable)
{
- // FIXME: Implement me!
+ readLock();
+ try
+ {
+ runnable.run();
+ }
+ finally
+ {
+ readUnlock();
+ }
}
/**
@@ -725,6 +841,7 @@ public abstract class AbstractDocument
*/
public void setAsynchronousLoadPriority(int p)
{
+ // TODO: Implement this properly.
}
/**
@@ -739,11 +856,30 @@ public abstract class AbstractDocument
}
/**
- * Blocks until a write lock can be obtained.
+ * Blocks until a write lock can be obtained. Must wait if there are
+ * readers currently reading or another thread is currently writing.
*/
protected void writeLock()
{
- // FIXME: Implement me.
+ if (currentWriter!= null && currentWriter.equals(Thread.currentThread()))
+ return;
+ synchronized (documentCV)
+ {
+ numWritersWaiting++;
+ while (numReaders > 0)
+ {
+ try
+ {
+ documentCV.wait();
+ }
+ catch (InterruptedException ie)
+ {
+ throw new Error("interruped while trying to obtain write lock");
+ }
+ }
+ numWritersWaiting --;
+ currentWriter = Thread.currentThread();
+ }
}
/**
@@ -752,7 +888,14 @@ public abstract class AbstractDocument
*/
protected void writeUnlock()
{
- // FIXME: Implement me.
+ synchronized (documentCV)
+ {
+ if (Thread.currentThread().equals(currentWriter))
+ {
+ currentWriter = null;
+ documentCV.notifyAll();
+ }
+ }
}
/**
@@ -970,8 +1113,8 @@ public abstract class AbstractDocument
public abstract class AbstractElement
implements Element, MutableAttributeSet, TreeNode, Serializable
{
- /** The serial version UID for AbstractElement. */
- private static final long serialVersionUID = 1265312733007397733L;
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 1712240033321461704L;
/** The number of characters that this Element spans. */
int count;
@@ -1231,6 +1374,9 @@ public abstract class AbstractDocument
/**
* Returns the resolve parent of this element.
+ * This is taken from the AttributeSet, but if this is null,
+ * this method instead returns the Element's parent's
+ * AttributeSet
*
* @return the resolve parent of this element
*
@@ -1238,7 +1384,9 @@ public abstract class AbstractDocument
*/
public AttributeSet getResolveParent()
{
- return attributes.getResolveParent();
+ if (attributes.getResolveParent() != null)
+ return attributes.getResolveParent();
+ return element_parent.getAttributes();
}
/**
@@ -1355,49 +1503,6 @@ public abstract class AbstractDocument
public abstract int getStartOffset();
/**
- * Prints diagnostic information to the specified stream.
- *
- * @param stream the stream to dump to
- * @param indent the indentation level
- * @param element the element to be dumped
- */
- private void dumpElement(PrintStream stream, String indent,
- Element element)
- {
- // FIXME: Should the method be removed?
- System.out.println(indent + "<" + element.getName() +">");
-
- if (element.isLeaf())
- {
- int start = element.getStartOffset();
- int end = element.getEndOffset();
- String text = "";
- try
- {
- text = getContent().getString(start, end - start);
- }
- catch (BadLocationException e)
- {
- AssertionError error =
- new AssertionError("BadLocationException should not be "
- + "thrown here. start = " + start
- + ", end = " + end);
- error.initCause(e);
- throw error;
- }
- System.out.println(indent + " ["
- + start + ","
- + end + "]["
- + text + "]");
- }
- else
- {
- for (int i = 0; i < element.getElementCount(); ++i)
- dumpElement(stream, indent + " ", element.getElement(i));
- }
- }
-
- /**
* Prints diagnostic output to the specified stream.
*
* @param stream the stream to write to
@@ -1405,10 +1510,66 @@ public abstract class AbstractDocument
*/
public void dump(PrintStream stream, int indent)
{
- String indentStr = "";
+ StringBuffer b = new StringBuffer();
for (int i = 0; i < indent; ++i)
- indentStr += " ";
- dumpElement(stream, indentStr, this);
+ b.append(' ');
+ b.append('<');
+ b.append(getName());
+ // Dump attributes if there are any.
+ if (getAttributeCount() > 0)
+ {
+ b.append('\n');
+ Enumeration attNames = getAttributeNames();
+ while (attNames.hasMoreElements())
+ {
+ for (int i = 0; i < indent + 2; ++i)
+ b.append(' ');
+ Object attName = attNames.nextElement();
+ b.append(attName);
+ b.append('=');
+ Object attribute = getAttribute(attName);
+ b.append(attribute);
+ b.append('\n');
+ }
+ }
+ b.append(">\n");
+
+ // Dump element content for leaf elements.
+ if (isLeaf())
+ {
+ for (int i = 0; i < indent + 2; ++i)
+ b.append(' ');
+ int start = getStartOffset();
+ int end = getEndOffset();
+ b.append('[');
+ b.append(start);
+ b.append(',');
+ b.append(end);
+ b.append("][");
+ try
+ {
+ b.append(getDocument().getText(start, end - start));
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError err = new AssertionError("BadLocationException "
+ + "must not be thrown "
+ + "here.");
+ err.initCause(ex);
+ throw err;
+ }
+ b.append("]\n");
+ }
+ stream.print(b.toString());
+
+ // Dump child elements if any.
+ int count = getElementCount();
+ for (int i = 0; i < count; ++i)
+ {
+ Element el = getElement(i);
+ if (el instanceof AbstractElement)
+ ((AbstractElement) el).dump(stream, indent + 2);
+ }
}
}
@@ -1418,8 +1579,8 @@ public abstract class AbstractDocument
*/
public class BranchElement extends AbstractElement
{
- /** The serial version UID for BranchElement. */
- private static final long serialVersionUID = -8595176318868717313L;
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = -6037216547466333183L;
/** The child elements of this BranchElement. */
private Element[] children = new Element[0];
@@ -1503,19 +1664,30 @@ public abstract class AbstractDocument
*/
public int getElementIndex(int offset)
{
- // If we have no children, return -1.
- if (getElementCount() == 0)
- return - 1;
-
+ // If offset is less than the start offset of our first child,
+ // return 0
+ if (offset < getStartOffset())
+ return 0;
+
// XXX: There is surely a better algorithm
// as beginning from first element each time.
- for (int index = 0; index < children.length; ++index)
+ for (int index = 0; index < children.length - 1; ++index)
{
Element elem = children[index];
if ((elem.getStartOffset() <= offset)
&& (offset < elem.getEndOffset()))
return index;
+ // If the next element's start offset is greater than offset
+ // then we have to return the closest Element, since no Elements
+ // will contain the offset
+ if (children[index + 1].getStartOffset() > offset)
+ {
+ if ((offset - elem.getEndOffset()) > (children[index + 1].getStartOffset() - offset))
+ return index + 1;
+ else
+ return index;
+ }
}
// If offset is greater than the index of the last element, return
@@ -1642,8 +1814,8 @@ public abstract class AbstractDocument
public class DefaultDocumentEvent extends CompoundEdit
implements DocumentEvent
{
- /** The serial version UID of DefaultDocumentEvent. */
- private static final long serialVersionUID = -7406103236022413522L;
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 5230037221564563284L;
/** The starting offset of the change. */
private int offset;
@@ -1748,7 +1920,7 @@ public abstract class AbstractDocument
return (DocumentEvent.ElementChange) changes.get(elem);
}
}
-
+
/**
* An implementation of {@link DocumentEvent.ElementChange} to be added
* to {@link DefaultDocumentEvent}s.
@@ -1843,8 +2015,8 @@ public abstract class AbstractDocument
*/
public class LeafElement extends AbstractElement
{
- /** The serial version UID of LeafElement. */
- private static final long serialVersionUID = 5115368706941283802L;
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = -8906306331347768017L;
/** Manages the start offset of this element. */
Position startPos;
diff --git a/libjava/classpath/javax/swing/text/AttributeSet.java b/libjava/classpath/javax/swing/text/AttributeSet.java
index 2f1f1890bae..01d148c067b 100644
--- a/libjava/classpath/javax/swing/text/AttributeSet.java
+++ b/libjava/classpath/javax/swing/text/AttributeSet.java
@@ -58,6 +58,7 @@ public interface AttributeSet
*/
static interface CharacterAttribute
{
+ // This interface is a marker interface and has no methods.
}
/**
@@ -65,6 +66,7 @@ public interface AttributeSet
*/
static interface ColorAttribute
{
+ // This interface is a marker interface and has no methods.
}
/**
@@ -72,6 +74,7 @@ public interface AttributeSet
*/
static interface FontAttribute
{
+ // This interface is a marker interface and has no methods.
}
/**
@@ -79,6 +82,7 @@ public interface AttributeSet
*/
static interface ParagraphAttribute
{
+ // This interface is a marker interface and has no methods.
}
/**
@@ -99,7 +103,7 @@ public interface AttributeSet
* <code>false</code> otherwise.
*
* @param name the name of the requested attribute
- * @param the value of the requested attribute
+ * @param value the value of the requested attribute
*
* @return <code>true</code> if this <code>AttributeSet</code> contains
* an attribute with the specified <code>name</code> and
diff --git a/libjava/classpath/javax/swing/text/BoxView.java b/libjava/classpath/javax/swing/text/BoxView.java
index 0f8ba1ce15e..f201045dbdb 100644
--- a/libjava/classpath/javax/swing/text/BoxView.java
+++ b/libjava/classpath/javax/swing/text/BoxView.java
@@ -155,8 +155,9 @@ public class BoxView
* automatically when any of the child view changes its preferences
* via {@link #preferenceChanged(View, boolean, boolean)}.
*
- * The layout will be updated the next time when {@link #setSize()} is
- * called, typically from within the {@link #paint()} method.
+ * The layout will be updated the next time when
+ * {@link #setSize(float, float)} is called, typically from within the
+ * {@link #paint(Graphics, Shape)} method.
*
* Valid values for the axis are {@link View#X_AXIS} and
* {@link View#Y_AXIS}.
@@ -216,12 +217,11 @@ public class BoxView
* @param alloc the allocated region for the child to paint into
* @param index the index of the child to be painted
*
- * @see {@link #childAllocation}
+ * @see #childAllocation(int, Rectangle)
*/
protected void paintChild(Graphics g, Rectangle alloc, int index)
{
View child = getView(index);
- childAllocation(index, alloc);
child.paint(g, alloc);
}
@@ -301,18 +301,15 @@ public class BoxView
setSize(bounds.width, bounds.height);
Rectangle inside = getInsideAllocation(a);
-
Rectangle copy = new Rectangle(inside);
int count = getViewCount();
for (int i = 0; i < count; ++i)
{
- // TODO: Figure out if the parameter to paintChild is meant to
- // be the child allocation or the allocation of this BoxView.
- // I assume the second option here.
- // We pass this method a copy of the inside rectangle here because
- // it modifies the actual values.
copy.setBounds(inside);
- paintChild(g, copy, i);
+ childAllocation(i, copy);
+ if (!copy.isEmpty()
+ && g.hitClip(copy.x, copy.y, copy.width, copy.height))
+ paintChild(g, copy, i);
}
}
@@ -362,6 +359,24 @@ public class BoxView
}
/**
+ * Calculates the layout of the children of this <code>BoxView</code> along
+ * the specified axis.
+ *
+ * @param span the target span
+ * @param axis the axis that is examined
+ * @param offsets an empty array, filled with the offsets of the children
+ * @param spans an empty array, filled with the spans of the children
+ */
+ protected void baselineLayout(int span, int axis, int[] offsets,
+ int[] spans)
+ {
+ if (axis == myAxis)
+ layoutMajorAxis(span, axis, offsets, spans);
+ else
+ layoutMinorAxis(span, axis, offsets, spans);
+ }
+
+ /**
* Calculates the size requirements of this <code>BoxView</code> along
* its major axis, that is the axis specified in the constructor.
*
@@ -375,27 +390,8 @@ public class BoxView
protected SizeRequirements calculateMajorAxisRequirements(int axis,
SizeRequirements sr)
{
- if (sr == null)
- sr = new SizeRequirements();
- else
- {
- sr.maximum = 0;
- sr.minimum = 0;
- sr.preferred = 0;
- sr.alignment = 0.5F;
- }
-
- int count = getViewCount();
-
- // Sum up the sizes of the children along the specified axis.
- for (int i = 0; i < count; ++i)
- {
- View child = getView(i);
- sr.minimum += child.getMinimumSpan(axis);
- sr.preferred += child.getPreferredSpan(axis);
- sr.maximum += child.getMaximumSpan(axis);
- }
- return sr;
+ SizeRequirements[] childReqs = getChildRequirements(axis);
+ return SizeRequirements.getTiledSizeRequirements(childReqs);
}
/**
@@ -413,48 +409,8 @@ public class BoxView
protected SizeRequirements calculateMinorAxisRequirements(int axis,
SizeRequirements sr)
{
- if (sr == null)
- sr = new SizeRequirements();
- else
- {
- sr.maximum = 0;
- sr.minimum = 0;
- sr.preferred = 0;
- sr.alignment = 0.5F;
- }
-
- int count = getViewCount();
-
- int aboveBaseline = 0;
- int belowBaseline = 0;
- int aboveBaselineMin = 0;
- int belowBaselineMin = 0;
- int aboveBaselineMax = 0;
- int belowBaselineMax = 0;
-
- for (int i = 0; i < count; ++i)
- {
- View child = getView(i);
- float align = child.getAlignment(axis);
- int pref = (int) child.getPreferredSpan(axis);
- int min = (int) child.getMinimumSpan(axis);
- int max = (int) child.getMaximumSpan(axis);
- aboveBaseline += (int) (align * pref);
- belowBaseline += (int) ((1.F - align) * pref);
- aboveBaselineMin += (int) (align * min);
- belowBaselineMin += (int) ((1.F - align) * min);
- aboveBaselineMax += (int) (align * max);
- belowBaselineMax += (int) ((1.F - align) * max);
- }
- sr.minimum = aboveBaselineMin + belowBaselineMin;
- sr.maximum = aboveBaselineMax + belowBaselineMax;
- sr.preferred = aboveBaseline + belowBaseline;
- if (aboveBaseline == 0)
- sr.alignment = 1.0F;
- else
- sr.alignment = (float) (sr.preferred / aboveBaseline);
-
- return sr;
+ SizeRequirements[] childReqs = getChildRequirements(axis);
+ return SizeRequirements.getAlignedSizeRequirements(childReqs);
}
/**
@@ -569,19 +525,8 @@ public class BoxView
*/
protected void layout(int width, int height)
{
- this.width = width;
- this.height = height;
-
- if (myAxis == X_AXIS)
- {
- layoutMajorAxis(width, X_AXIS, offsetsX, spansX);
- layoutMinorAxis(height, Y_AXIS, offsetsY, spansY);
- }
- else
- {
- layoutMajorAxis(height, Y_AXIS, offsetsY, spansY);
- layoutMinorAxis(width, X_AXIS, offsetsX, spansX);
- }
+ baselineLayout(width, X_AXIS, offsetsX, spansX);
+ baselineLayout(height, Y_AXIS, offsetsY, spansY);
}
/**
@@ -591,28 +536,16 @@ public class BoxView
* to layout the children
* @param axis the axis along which the layout is performed
* @param offsets the array that holds the offsets of the children on exit
- * @param offsets the array that holds the spans of the children on exit
+ * @param spans the array that holds the spans of the children on exit
*/
protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
int[] spans)
{
- // Allocate SizeRequirements for each child view.
- int count = getViewCount();
- SizeRequirements[] childReqs = new SizeRequirements[count];
- for (int i = 0; i < count; ++i)
- {
- View view = getView(i);
- childReqs[i] = new SizeRequirements((int) view.getMinimumSpan(axis),
- (int) view.getPreferredSpan(axis),
- (int) view.getMaximumSpan(axis),
- view.getAlignment(axis));
- }
-
+ SizeRequirements[] childReqs = getChildRequirements(axis);
// Calculate the spans and offsets using the SizeRequirements uility
// methods.
SizeRequirements.calculateTiledPositions(targetSpan, null, childReqs,
offsets, spans);
-
validateLayout(axis);
}
@@ -623,26 +556,21 @@ public class BoxView
* to layout the children
* @param axis the axis along which the layout is performed
* @param offsets the array that holds the offsets of the children on exit
- * @param offsets the array that holds the spans of the children on exit
+ * @param spans the array that holds the spans of the children on exit
*/
protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
int[] spans)
{
- // Allocate SizeRequirements for each child view.
- int count = getViewCount();
- SizeRequirements[] childReqs = new SizeRequirements[count];
- for (int i = 0; i < count; ++i)
- {
- View view = getView(i);
- childReqs[i] = new SizeRequirements((int) view.getMinimumSpan(axis),
- (int) view.getPreferredSpan(axis),
- (int) view.getMaximumSpan(axis),
- view.getAlignment(axis));
- }
-
+ SizeRequirements[] childReqs = getChildRequirements(axis);
// Calculate the spans and offsets using the SizeRequirements uility
// methods.
- SizeRequirements.calculateAlignedPositions(targetSpan, null, childReqs,
+ // TODO: This might be an opportunity for performance optimization. Here
+ // we could use a cached instance of SizeRequirements instead of passing
+ // null to baselineRequirements. However, this would involve rewriting
+ // the baselineRequirements() method to not use the SizeRequirements
+ // utility method, since they cannot reuse a cached instance.
+ SizeRequirements total = baselineRequirements(axis, null);
+ SizeRequirements.calculateAlignedPositions(targetSpan, total, childReqs,
offsets, spans);
validateLayout(axis);
}
@@ -692,6 +620,9 @@ public class BoxView
layoutChanged(X_AXIS);
if (this.height != (int) height)
layoutChanged(Y_AXIS);
+
+ this.width = (int) width;
+ this.height = (int) height;
Rectangle outside = new Rectangle(0, 0, this.width, this.height);
Rectangle inside = getInsideAllocation(outside);
@@ -711,4 +642,99 @@ public class BoxView
if (axis == Y_AXIS)
yLayoutValid = true;
}
+
+ /**
+ * Returns the size requirements of this view's children for the major
+ * axis.
+ *
+ * @return the size requirements of this view's children for the major
+ * axis
+ */
+ SizeRequirements[] getChildRequirements(int axis)
+ {
+ // Allocate SizeRequirements for each child view.
+ int count = getViewCount();
+ SizeRequirements[] childReqs = new SizeRequirements[count];
+ for (int i = 0; i < count; ++i)
+ {
+ View view = getView(i);
+ childReqs[i] = new SizeRequirements((int) view.getMinimumSpan(axis),
+ (int) view.getPreferredSpan(axis),
+ (int) view.getMaximumSpan(axis),
+ view.getAlignment(axis));
+ }
+ return childReqs;
+ }
+
+ /**
+ * Returns the span for the child view with the given index for the specified
+ * axis.
+ *
+ * @param axis the axis to examine, either <code>X_AXIS</code> or
+ * <code>Y_AXIS</code>
+ * @param childIndex the index of the child for for which to return the span
+ *
+ * @return the span for the child view with the given index for the specified
+ * axis
+ */
+ protected int getSpan(int axis, int childIndex)
+ {
+ if (axis == X_AXIS)
+ return spansX[childIndex];
+ else
+ return spansY[childIndex];
+ }
+
+ /**
+ * Returns the offset for the child view with the given index for the
+ * specified axis.
+ *
+ * @param axis the axis to examine, either <code>X_AXIS</code> or
+ * <code>Y_AXIS</code>
+ * @param childIndex the index of the child for for which to return the span
+ *
+ * @return the offset for the child view with the given index for the
+ * specified axis
+ */
+ protected int getOffset(int axis, int childIndex)
+ {
+ if (axis == X_AXIS)
+ return offsetsX[childIndex];
+ else
+ return offsetsY[childIndex];
+ }
+
+ /**
+ * Returns the alignment for this box view for the specified axis. The
+ * axis that is tiled (the major axis) will be requested to be aligned
+ * centered (0.5F). The minor axis alignment depends on the child view's
+ * total alignment.
+ *
+ * @param axis the axis which is examined
+ *
+ * @return the alignment for this box view for the specified axis
+ */
+ public float getAlignment(int axis)
+ {
+ if (axis == myAxis)
+ return 0.5F;
+ else
+ return baselineRequirements(axis, null).alignment;
+ }
+
+ /**
+ * Called by a child View when its preferred span has changed.
+ *
+ * @param width indicates that the preferred width of the child changed.
+ * @param height indicates that the preferred height of the child changed.
+ * @param child the child View.
+ */
+ public void preferenceChanged (View child, boolean width, boolean height)
+ {
+ if (width)
+ xLayoutValid = false;
+ if (height)
+ yLayoutValid = false;
+ super.preferenceChanged(child, width, height);
+ }
}
diff --git a/libjava/classpath/javax/swing/text/ComponentView.java b/libjava/classpath/javax/swing/text/ComponentView.java
index f6feda21513..16112c8f4de 100644
--- a/libjava/classpath/javax/swing/text/ComponentView.java
+++ b/libjava/classpath/javax/swing/text/ComponentView.java
@@ -1,5 +1,5 @@
/* ComponentView.java --
- Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -41,65 +41,125 @@ import java.awt.Component;
import java.awt.Graphics;
import java.awt.Shape;
+import javax.swing.SwingConstants;
+
+/**
+ * A {@link View} implementation that is able to render arbitrary
+ * {@link Component}s. This uses the attribute
+ * {@link StyleConstants#ComponentAttribute} to determine the
+ * <code>Component</code> that should be rendered. This <code>Component</code>
+ * becomes a direct child of the <code>JTextComponent</code> that contains
+ * this <code>ComponentView</code>, so this view must not be shared between
+ * multiple <code>JTextComponent</code>s.
+ *
+ * @author original author unknown
+ * @author Roman Kennke (roman@kennke.org)
+ */
+// FIXME: This class is a complete stub and needs to be implemented properly.
public class ComponentView extends View
{
- public ComponentView(Element elem)
- {
- super(elem);
- }
-
- protected Component createComponent()
- {
- return null;
- }
-
- public float getAlignment(int axis)
- {
- return 0;
- }
-
- public final Component getComponent()
- {
- return null;
- }
-
- public float getMaximumSpan(int axis)
- {
- return 0;
- }
-
- public float getMinimumSpan(int axis)
- {
- return 0;
- }
-
- public float getPreferredSpan(int axis)
- {
- return 0;
- }
-
- public Shape modelToView(int pos, Shape a, Position.Bias b)
- throws BadLocationException
- {
- return null;
- }
-
- public void paint(Graphics g, Shape a)
- {
- }
+ /**
+ * Creates a new instance of <code>ComponentView</code> for the specified
+ * <code>Element</code>.
+ *
+ * @param elem the element that this <code>View</code> is rendering
+ */
+ public ComponentView(Element elem)
+ {
+ super(elem);
+ }
+
+ /**
+ * Creates the <code>Component</code> that this <code>View</code> is
+ * rendering. The <code>Component</code> is determined using
+ * the {@link StyleConstants#ComponentAttribute} of the associated
+ * <code>Element</code>.
+ *
+ * @return the component that is rendered
+ */
+ protected Component createComponent()
+ {
+ return StyleConstants.getComponent(getElement().getAttributes());
+ }
+
+ /**
+ * Returns the alignment of this <code>View</code> along the specified axis.
+ *
+ * @param axis either {@link View#X_AXIS} or {@link View#Y_AXIS}
+ *
+ * @return the alignment of this <code>View</code> along the specified axis
+ */
+ public float getAlignment(int axis)
+ {
+ return 0;
+ }
+
+ /**
+ * Returns the <code>Component</code> that is rendered by this
+ * <code>ComponentView</code>.
+ *
+ * @return the <code>Component</code> that is rendered by this
+ * <code>ComponentView</code>
+ */
+ public final Component getComponent()
+ {
+ return null;
+ }
+
+ /**
+ * Returns the maximum span of this <code>View</code> along the specified
+ * axis.
+ *
+ * This will return {@link Component#getMaximumSize()} for the specified
+ * axis.
+ *
+ * @return the maximum span of this <code>View</code> along the specified
+ * axis
+ */
+ public float getMaximumSpan(int axis)
+ {
+ return 0;
+ }
+
+ public float getMinimumSpan(int axis)
+ {
+ // TODO: Implement this properly.
+ return 0;
+ }
+
+ public float getPreferredSpan(int axis)
+ {
+ // TODO: Implement this properly.
+ return 0;
+ }
+
+ public Shape modelToView(int pos, Shape a, Position.Bias b)
+ throws BadLocationException
+ {
+ // TODO: Implement this properly.
+ return null;
+ }
- public void setParent(View p)
- {
- }
+ public void paint(Graphics g, Shape a)
+ {
+ // TODO: Implement this properly.
+ }
+
+ public void setParent(View p)
+ {
+ // TODO: Implement this properly.
+ }
- public void setSize(float width, float height)
- {
- }
+ public void setSize(float width, float height)
+ {
+ // TODO: Implement this properly.
+ }
- public int viewToModel(float x, float y, Shape a, Position.Bias[] bias)
- {
- return 0;
- }
+ public int viewToModel(float x, float y, Shape a, Position.Bias[] bias)
+ {
+ // TODO: Implement this properly.
+ return 0;
+ }
/**
* Maps coordinates from the <code>View</code>'s space into a position
@@ -118,4 +178,34 @@ public class ComponentView extends View
// FIXME: Implement this properly.
return 0;
}
+
+ /**
+ * Returns the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction <code>d</code>.
+ *
+ * @param c the text component
+ * @param pos the document position
+ * @param b the bias for <code>pos</code>
+ * @param d the direction, must be either {@link SwingConstants#NORTH},
+ * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
+ * {@link SwingConstants#EAST}
+ * @param biasRet an array of {@link Position.Bias} that can hold at least
+ * one element, which is filled with the bias of the return position
+ * on method exit
+ *
+ * @return the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction
+ * <code>d</code>
+ *
+ * @throws BadLocationException if <code>pos</code> is not a valid offset in
+ * the document model
+ */
+ public int getNextVisualPositionFrom(JTextComponent c, int pos,
+ Position.Bias b, int d,
+ Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ // TODO: Implement this properly.
+ throw new AssertionError("Not implemented yet.");
+ }
}
diff --git a/libjava/classpath/javax/swing/text/CompositeView.java b/libjava/classpath/javax/swing/text/CompositeView.java
index 6776c95727a..bc626a40696 100644
--- a/libjava/classpath/javax/swing/text/CompositeView.java
+++ b/libjava/classpath/javax/swing/text/CompositeView.java
@@ -62,7 +62,7 @@ public abstract class CompositeView
/**
* The allocation of this <code>View</code> minus its insets. This is
* initialized in {@link #getInsideAllocation} and reused and modified in
- * {@link childAllocation}.
+ * {@link #childAllocation(int, Rectangle)}.
*/
Rectangle insideAllocation;
@@ -221,20 +221,17 @@ public abstract class CompositeView
if (childIndex != -1)
{
View child = getView(childIndex);
- Shape result = child.modelToView(pos, a, bias);
+ Rectangle r = a.getBounds();
+ childAllocation(childIndex, r);
+ Shape result = child.modelToView(pos, r, bias);
if (result == null)
throw new AssertionError("" + child.getClass().getName()
+ ".modelToView() must not return null");
return result;
}
else
- {
- // FIXME: Handle the case when we have no child view for the given
- // position.
- throw new AssertionError("No child views found where child views are "
- + "expected. pos = " + pos + ", bias = "
- + bias);
- }
+ throw new BadLocationException("No child view for the specified location",
+ pos);
}
/**
@@ -314,6 +311,7 @@ public abstract class CompositeView
*/
public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
int direction, Position.Bias[] biasRet)
+ throws BadLocationException
{
int retVal = -1;
switch (direction)
@@ -433,10 +431,16 @@ public abstract class CompositeView
*/
protected int getViewIndexAtPosition(int pos)
{
- // We have one child view allocated for each child element in
- // loadChildren(), so this should work.
- Element el = getElement();
- int index = el.getElementIndex(pos);
+ int index = -1;
+ for (int i = 0; i < children.length; i++)
+ {
+ if (children[i].getStartOffset() <= pos
+ && children[i].getEndOffset() > pos)
+ {
+ index = i;
+ break;
+ }
+ }
return index;
}
@@ -473,8 +477,8 @@ public abstract class CompositeView
insideAllocation = inside;
}
}
- inside.x = alloc.x - insets.left;
- inside.y = alloc.y - insets.top;
+ inside.x = alloc.x + insets.left;
+ inside.y = alloc.y + insets.top;
inside.width = alloc.width - insets.left - insets.right;
inside.height = alloc.height - insets.top - insets.bottom;
return inside;
@@ -594,6 +598,7 @@ public abstract class CompositeView
protected int getNextNorthSouthVisualPositionFrom(int pos, Position.Bias b,
Shape a, int direction,
Position.Bias[] biasRet)
+ throws BadLocationException
{
// FIXME: Implement this correctly.
return pos;
@@ -627,6 +632,7 @@ public abstract class CompositeView
protected int getNextEastWestVisualPositionFrom(int pos, Position.Bias b,
Shape a, int direction,
Position.Bias[] biasRet)
+ throws BadLocationException
{
// FIXME: Implement this correctly.
return pos;
@@ -649,4 +655,34 @@ public abstract class CompositeView
{
return false;
}
+
+ /**
+ * Returns the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction <code>d</code>.
+ *
+ * @param c the text component
+ * @param pos the document position
+ * @param b the bias for <code>pos</code>
+ * @param d the direction, must be either {@link SwingConstants#NORTH},
+ * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
+ * {@link SwingConstants#EAST}
+ * @param biasRet an array of {@link Position.Bias} that can hold at least
+ * one element, which is filled with the bias of the return position
+ * on method exit
+ *
+ * @return the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction
+ * <code>d</code>
+ *
+ * @throws BadLocationException if <code>pos</code> is not a valid offset in
+ * the document model
+ */
+ public int getNextVisualPositionFrom(JTextComponent c, int pos,
+ Position.Bias b, int d,
+ Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ // TODO: Implement this properly.
+ throw new AssertionError("Not implemented yet.");
+ }
}
diff --git a/libjava/classpath/javax/swing/text/DefaultCaret.java b/libjava/classpath/javax/swing/text/DefaultCaret.java
index 33c3ae3bf28..66e2f4723cf 100644
--- a/libjava/classpath/javax/swing/text/DefaultCaret.java
+++ b/libjava/classpath/javax/swing/text/DefaultCaret.java
@@ -40,15 +40,24 @@ package javax.swing.text;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import java.util.EventListener;
+import javax.swing.JComponent;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
import javax.swing.event.EventListenerList;
/**
@@ -60,12 +69,164 @@ import javax.swing.event.EventListenerList;
public class DefaultCaret extends Rectangle
implements Caret, FocusListener, MouseListener, MouseMotionListener
{
+
+ /**
+ * Controls the blinking of the caret.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
+ */
+ private class BlinkTimerListener implements ActionListener
+ {
+ /**
+ * Forces the next event to be ignored. The next event should be ignored
+ * if we force the caret to appear. We do not know how long will it take
+ * to fire the comming event; this may be near immediately. Better to leave
+ * the caret visible one iteration longer.
+ */
+ boolean ignoreNextEvent;
+
+ /**
+ * Receives notification when the blink timer fires and updates the visible
+ * state of the caret.
+ *
+ * @param event the action event
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ if (ignoreNextEvent)
+ ignoreNextEvent = false;
+ else
+ {
+ visible = !visible;
+ repaint();
+ }
+ }
+ }
+
+ /**
+ * Listens for changes in the text component's document and updates the
+ * caret accordingly.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class DocumentHandler implements DocumentListener
+ {
+ /**
+ * Receives notification that some text attributes have changed. No action
+ * is taken here.
+ *
+ * @param event the document event
+ */
+ public void changedUpdate(DocumentEvent event)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Receives notification that some text has been inserted from the text
+ * component. The caret is moved forward accordingly.
+ *
+ * @param event the document event
+ */
+ public void insertUpdate(DocumentEvent event)
+ {
+ if (policy == ALWAYS_UPDATE ||
+ (SwingUtilities.isEventDispatchThread() &&
+ policy == UPDATE_WHEN_ON_EDT))
+ {
+ int dot = getDot();
+ setDot(dot + event.getLength());
+ }
+ }
+
+ /**
+ * Receives notification that some text has been removed into the text
+ * component. The caret is moved backwards accordingly.
+ *
+ * @param event the document event
+ */
+ public void removeUpdate(DocumentEvent event)
+ {
+ if (policy == ALWAYS_UPDATE ||
+ (SwingUtilities.isEventDispatchThread() &&
+ policy == UPDATE_WHEN_ON_EDT))
+ {
+ int dot = getDot();
+ setDot(dot - event.getLength());
+ }
+ else if (policy == NEVER_UPDATE)
+ {
+ int docLength = event.getDocument().getLength();
+ if (getDot() > docLength)
+ setDot(docLength);
+ }
+ }
+ }
+
+ /**
+ * Listens for property changes on the text document. This is used to add and
+ * remove our document listener, if the document of the text component has
+ * changed.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class PropertyChangeHandler implements PropertyChangeListener
+ {
+
+ /**
+ * Receives notification when a property has changed on the text component.
+ * This adds/removes our document listener from the text component's
+ * document when the document changes.
+ *
+ * @param e the property change event
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals("document"))
+ {
+ Document oldDoc = (Document) e.getOldValue();
+ oldDoc.removeDocumentListener(documentListener);
+ Document newDoc = (Document) e.getNewValue();
+ newDoc.addDocumentListener(documentListener);
+ }
+ }
+
+ }
+
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 4325555698756477346L;
+
/**
- * The serial version UID for DefaultCaret.
+ * Indicates the Caret position should always be updated after Document
+ * changes even if the updates are not performed on the Event Dispatching
+ * thread.
+ *
+ * @since 1.5
*/
- private static final long serialVersionUID = 228155774675466193L;
+ public static final int ALWAYS_UPDATE = 2;
/**
+ * Indicates the Caret position should not be changed unless the Document
+ * length becomes less than the Caret position, in which case the Caret
+ * is moved to the end of the Document.
+ *
+ * @since 1.5
+ */
+ public static final int NEVER_UPDATE = 1;
+
+ /**
+ * Indicates the Caret position should be updated only if Document changes
+ * are made on the Event Dispatcher thread.
+ *
+ * @since 1.5
+ */
+ public static final int UPDATE_WHEN_ON_EDT = 0;
+
+ /** Keeps track of the current update policy **/
+ int policy = UPDATE_WHEN_ON_EDT;
+
+ /**
* The <code>ChangeEvent</code> that is fired by {@link #fireStateChanged()}.
*/
protected ChangeEvent changeEvent = new ChangeEvent(this);
@@ -76,6 +237,16 @@ public class DefaultCaret extends Rectangle
protected EventListenerList listenerList = new EventListenerList();
/**
+ * Our document listener.
+ */
+ DocumentListener documentListener;
+
+ /**
+ * Our property listener.
+ */
+ PropertyChangeListener propertyChangeListener;
+
+ /**
* The text component in which this caret is installed.
*/
private JTextComponent textComponent;
@@ -106,15 +277,65 @@ public class DefaultCaret extends Rectangle
private Point magicCaretPosition = null;
/**
- * Indicates if this <code>Caret</code> is currently visible or not.
+ * Indicates if this <code>Caret</code> is currently visible or not. This is
+ * package private to avoid an accessor method.
*/
- private boolean visible = true;
+ boolean visible = false;
/**
* The current highlight entry.
*/
private Object highlightEntry;
+ private Timer blinkTimer;
+
+ private BlinkTimerListener blinkListener;
+
+ /**
+ * Creates a new <code>DefaultCaret</code> instance.
+ */
+ public DefaultCaret()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Sets the Caret update policy.
+ *
+ * @param policy the new policy. Valid values are:
+ * ALWAYS_UPDATE: always update the Caret position, even when Document
+ * updates don't occur on the Event Dispatcher thread.
+ * NEVER_UPDATE: don't update the Caret position unless the Document
+ * length becomes less than the Caret position (then update the
+ * Caret to the end of the Document).
+ * UPDATE_WHEN_ON_EDT: update the Caret position when the
+ * Document updates occur on the Event Dispatcher thread. This is the
+ * default.
+ *
+ * @since 1.5
+ * @throws IllegalArgumentException if policy is not one of the above.
+ */
+ public void setUpdatePolicy (int policy)
+ {
+ if (policy != ALWAYS_UPDATE && policy != NEVER_UPDATE
+ && policy != UPDATE_WHEN_ON_EDT)
+ throw new
+ IllegalArgumentException
+ ("policy must be ALWAYS_UPDATE, NEVER__UPDATE, or UPDATE_WHEN_ON_EDT");
+ this.policy = policy;
+ }
+
+ /**
+ * Gets the caret update policy.
+ *
+ * @return the caret update policy.
+ * @since 1.5
+ */
+ public int getUpdatePolicy ()
+ {
+ return policy;
+ }
+
/**
* Moves the caret position when the mouse is dragged over the text
* component, modifying the selection accordingly.
@@ -123,7 +344,7 @@ public class DefaultCaret extends Rectangle
*/
public void mouseDragged(MouseEvent event)
{
- // FIXME: Implement this properly.
+ moveCaret(event);
}
/**
@@ -153,7 +374,7 @@ public class DefaultCaret extends Rectangle
*/
public void mouseClicked(MouseEvent event)
{
- // FIXME: Implement this properly.
+ // TODO: Implement double- and triple-click behaviour here.
}
/**
@@ -175,6 +396,7 @@ public class DefaultCaret extends Rectangle
*/
public void mouseExited(MouseEvent event)
{
+ // Nothing to do here.
}
/**
@@ -187,7 +409,7 @@ public class DefaultCaret extends Rectangle
*/
public void mousePressed(MouseEvent event)
{
- // FIXME: Implement this properly.
+ positionCaret(event);
}
/**
@@ -208,6 +430,7 @@ public class DefaultCaret extends Rectangle
*/
public void focusGained(FocusEvent event)
{
+ setVisible(true);
}
/**
@@ -217,6 +440,8 @@ public class DefaultCaret extends Rectangle
*/
public void focusLost(FocusEvent event)
{
+ if (event.isTemporary() == false)
+ setVisible(false);
}
/**
@@ -227,7 +452,8 @@ public class DefaultCaret extends Rectangle
*/
protected void moveCaret(MouseEvent event)
{
- // FIXME: Implement this properly.
+ int newDot = getComponent().viewToModel(event.getPoint());
+ moveDot(newDot);
}
/**
@@ -238,7 +464,8 @@ public class DefaultCaret extends Rectangle
*/
protected void positionCaret(MouseEvent event)
{
- // FIXME: Implement this properly.
+ int newDot = getComponent().viewToModel(event.getPoint());
+ setDot(newDot);
}
/**
@@ -253,7 +480,16 @@ public class DefaultCaret extends Rectangle
textComponent.removeFocusListener(this);
textComponent.removeMouseListener(this);
textComponent.removeMouseMotionListener(this);
+ textComponent.getDocument().removeDocumentListener(documentListener);
+ documentListener = null;
+ textComponent.removePropertyChangeListener(propertyChangeListener);
+ propertyChangeListener = null;
textComponent = null;
+
+ // Deinstall blink timer if present.
+ if (blinkTimer != null)
+ blinkTimer.stop();
+ blinkTimer = null;
}
/**
@@ -269,6 +505,11 @@ public class DefaultCaret extends Rectangle
textComponent.addFocusListener(this);
textComponent.addMouseListener(this);
textComponent.addMouseMotionListener(this);
+ propertyChangeListener = new PropertyChangeHandler();
+ textComponent.addPropertyChangeListener(propertyChangeListener);
+ documentListener = new DocumentHandler();
+ textComponent.getDocument().addDocumentListener(documentListener);
+
repaint();
}
@@ -376,10 +617,7 @@ public class DefaultCaret extends Rectangle
*/
protected final void repaint()
{
- // FIXME: Is this good? This possibly causes alot of the component
- // hierarchy to be repainted on every caret blink.
- if (textComponent != null)
- textComponent.repaint();
+ getComponent().repaint(x, y, width, height);
}
/**
@@ -390,7 +628,8 @@ public class DefaultCaret extends Rectangle
*/
public void paint(Graphics g)
{
- if (textComponent == null)
+ JTextComponent comp = getComponent();
+ if (comp == null)
return;
int dot = getDot();
@@ -398,25 +637,33 @@ public class DefaultCaret extends Rectangle
try
{
- rect = textComponent.modelToView(dot);
+ rect = textComponent.modelToView(dot);
}
catch (BadLocationException e)
{
- // This should never happen as dot should be always valid.
- return;
+ assert false : "Unexpected bad caret location: " + dot;
+ return;
}
if (rect == null)
return;
-
- // First we need to delete the old caret.
- // FIXME: Implement deleting of old caret.
-
+
+ // Check if paint has possibly been called directly, without a previous
+ // call to damage(). In this case we need to do some cleanup first.
+ if ((x != rect.x) || (y != rect.y))
+ {
+ repaint(); // Erase previous location of caret.
+ x = rect.x;
+ y = rect.y;
+ width = 1;
+ height = rect.height;
+ }
+
// Now draw the caret on the new position if visible.
if (visible)
{
- g.setColor(textComponent.getCaretColor());
- g.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
+ g.setColor(textComponent.getCaretColor());
+ g.drawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
}
}
@@ -507,6 +754,8 @@ public class DefaultCaret extends Rectangle
*/
public void setBlinkRate(int rate)
{
+ if (blinkTimer != null)
+ blinkTimer.setDelay(rate);
blinkRate = rate;
}
@@ -534,7 +783,8 @@ public class DefaultCaret extends Rectangle
{
this.dot = dot;
handleHighlight();
- repaint();
+ adjustVisibility(this);
+ appear();
}
/**
@@ -551,8 +801,45 @@ public class DefaultCaret extends Rectangle
this.dot = dot;
this.mark = dot;
handleHighlight();
- repaint();
+ adjustVisibility(this);
+ appear();
}
+
+ /**
+ * Show the caret (may be hidden due blinking) and adjust the timer not to
+ * hide it (possibly immediately).
+ *
+ * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
+ */
+ void appear()
+ {
+ // All machinery is only required if the carret is blinking.
+ if (blinkListener != null)
+ {
+ blinkListener.ignoreNextEvent = true;
+
+ // If the caret is visible, erase the current position by repainting
+ // over.
+ if (visible)
+ repaint();
+
+ // Draw the caret in the new position.
+ visible = true;
+
+ Rectangle area = null;
+ try
+ {
+ area = getComponent().modelToView(getDot());
+ }
+ catch (BadLocationException ex)
+ {
+ assert false : "Unexpected bad caret location: " + getDot();
+ }
+ if (area != null)
+ damage(area);
+ }
+ repaint();
+ }
/**
* Returns <code>true</code> if this <code>Caret</code> is currently visible,
@@ -574,8 +861,33 @@ public class DefaultCaret extends Rectangle
*/
public void setVisible(boolean v)
{
- visible = v;
- repaint();
+ if (v != visible)
+ {
+ visible = v;
+ if (visible)
+ if (textComponent.isEnabled() && textComponent.isEditable())
+ {
+ if (blinkTimer == null)
+ initBlinkTimer();
+ blinkTimer.start();
+ }
+ else
+ {
+ if (blinkTimer != null)
+ blinkTimer.stop();
+ }
+ Rectangle area = null;
+ try
+ {
+ area = getComponent().modelToView(getDot());
+ }
+ catch (BadLocationException ex)
+ {
+ assert false: "Unexpected bad caret location: " + getDot();
+ }
+ if (area != null)
+ damage(area);
+ }
}
/**
@@ -589,4 +901,47 @@ public class DefaultCaret extends Rectangle
{
return DefaultHighlighter.DefaultPainter;
}
+
+ /**
+ * Updates the carets rectangle properties to the specified rectangle and
+ * repaints the caret.
+ *
+ * @param r the rectangle to set as the caret rectangle
+ */
+ protected void damage(Rectangle r)
+ {
+ if (r == null)
+ return;
+ x = r.x;
+ y = r.y;
+ width = 1;
+ // height is normally set in paint and we leave it untouched. However, we
+ // must set a valid value here, since otherwise the painting mechanism
+ // sets a zero clip and never calls paint.
+ if (height <= 0)
+ height = getComponent().getHeight();
+ repaint();
+ }
+
+ /**
+ * Adjusts the text component so that the caret is visible. This default
+ * implementation simply calls
+ * {@link JComponent#scrollRectToVisible(Rectangle)} on the text component.
+ * Subclasses may wish to change this.
+ */
+ protected void adjustVisibility(Rectangle rect)
+ {
+ getComponent().scrollRectToVisible(rect);
+ }
+
+ /**
+ * Initializes the blink timer.
+ */
+ private void initBlinkTimer()
+ {
+ // Setup the blink timer.
+ blinkListener = new BlinkTimerListener();
+ blinkTimer = new Timer(getBlinkRate(), blinkListener);
+ blinkTimer.setRepeats(true);
+ }
}
diff --git a/libjava/classpath/javax/swing/text/DefaultEditorKit.java b/libjava/classpath/javax/swing/text/DefaultEditorKit.java
index a14f3ff4fe0..3b3fc1f7238 100644
--- a/libjava/classpath/javax/swing/text/DefaultEditorKit.java
+++ b/libjava/classpath/javax/swing/text/DefaultEditorKit.java
@@ -66,8 +66,7 @@ public class DefaultEditorKit extends EditorKit
*
* @see Toolkit#beep()
*/
- public static class BeepAction
- extends TextAction
+ public static class BeepAction extends TextAction
{
/**
* Creates a new <code>BeepAction</code>.
@@ -95,8 +94,7 @@ public class DefaultEditorKit extends EditorKit
* @see CutAction
* @see PasteAction
*/
- public static class CopyAction
- extends TextAction
+ public static class CopyAction extends TextAction
{
/**
@@ -128,8 +126,7 @@ public class DefaultEditorKit extends EditorKit
* @see CopyAction
* @see PasteAction
*/
- public static class CutAction
- extends TextAction
+ public static class CutAction extends TextAction
{
/**
@@ -159,8 +156,7 @@ public class DefaultEditorKit extends EditorKit
* @see CopyAction
* @see CutAction
*/
- public static class PasteAction
- extends TextAction
+ public static class PasteAction extends TextAction
{
/**
@@ -226,9 +222,6 @@ public class DefaultEditorKit extends EditorKit
{
t.getDocument().insertString(t.getCaret().getDot(),
event.getActionCommand(), null);
- t.getCaret().setDot(Math.min(t.getCaret().getDot() + 1,
- t.getDocument().getEndPosition()
- .getOffset()));
}
catch (BadLocationException be)
{
@@ -243,8 +236,7 @@ public class DefaultEditorKit extends EditorKit
* of the text component. This is typically triggered by hitting
* ENTER on the keyboard.
*/
- public static class InsertBreakAction
- extends TextAction
+ public static class InsertBreakAction extends TextAction
{
/**
@@ -273,8 +265,7 @@ public class DefaultEditorKit extends EditorKit
*/
// FIXME: Figure out what this Action is supposed to do. Obviously text
// that is entered by the user is inserted through DefaultKeyTypedAction.
- public static class InsertContentAction
- extends TextAction
+ public static class InsertContentAction extends TextAction
{
/**
@@ -292,14 +283,15 @@ public class DefaultEditorKit extends EditorKit
*/
public void actionPerformed(ActionEvent event)
{
+ // FIXME: Figure out what this Action is supposed to do. Obviously text
+ // that is entered by the user is inserted through DefaultKeyTypedAction.
}
}
/**
* Inserts a TAB character into the text editor.
*/
- public static class InsertTabAction
- extends TextAction
+ public static class InsertTabAction extends TextAction
{
/**
@@ -699,6 +691,7 @@ public class DefaultEditorKit extends EditorKit
*/
public DefaultEditorKit()
{
+ // Nothing to do here.
}
/**
@@ -954,15 +947,23 @@ public class DefaultEditorKit extends EditorKit
* @param offset the beginning offset from where to write
* @param len the length of the fragment to write
*
- * @throws BadLocationException if <code>offset</code> or
- * <code>offset + len</code>is an invalid location inside
- * <code>document</code>
+ * @throws BadLocationException if <code>offset</code> is an
+ * invalid location inside <code>document</code>.
* @throws IOException if something goes wrong while writing to
* <code>out</code>
*/
public void write(Writer out, Document document, int offset, int len)
- throws BadLocationException, IOException
+ throws BadLocationException, IOException
{
- // TODO: Implement this properly.
+ // Throw a BLE if offset is invalid
+ if (offset < 0 || offset > document.getLength())
+ throw new BadLocationException("Tried to write to invalid location",
+ offset);
+
+ // If they gave an overly large len, just adjust it
+ if (offset + len > document.getLength())
+ len = document.getLength() - offset;
+
+ out.write(document.getText(offset, len));
}
}
diff --git a/libjava/classpath/javax/swing/text/DefaultFormatter.java b/libjava/classpath/javax/swing/text/DefaultFormatter.java
index c97d90703c7..f9e0f10e654 100644
--- a/libjava/classpath/javax/swing/text/DefaultFormatter.java
+++ b/libjava/classpath/javax/swing/text/DefaultFormatter.java
@@ -57,8 +57,7 @@ import javax.swing.JFormattedTextField;
*
* @author Roman Kennke (roman@kennke.org)
*/
-public class DefaultFormatter
- extends JFormattedTextField.AbstractFormatter
+public class DefaultFormatter extends JFormattedTextField.AbstractFormatter
implements Cloneable, Serializable
{
@@ -156,9 +155,6 @@ public class DefaultFormatter
* Checks if the value in the input field is valid. If the
* property allowsInvalid is set to <code>false</code>, then
* the string in the input field is not allowed to be entered.
- *
- * @param doc the document of the input field
- * @param value the current (old) value of the input field
*/
private void checkValidInput()
{
@@ -179,15 +175,18 @@ public class DefaultFormatter
catch (ParseException pe)
{
// if that happens, something serious must be wrong
- throw new AssertionError("values must be parseable");
+ AssertionError ae;
+ ae = new AssertionError("values must be parseable");
+ ae.initCause(pe);
+ throw ae;
}
}
}
}
}
- /** The serialVersoinUID. */
- private static final long serialVersionUID = -7369196326612908900L;
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = -355018354457785329L;
/**
* Indicates if the value should be committed after every
diff --git a/libjava/classpath/javax/swing/text/DefaultHighlighter.java b/libjava/classpath/javax/swing/text/DefaultHighlighter.java
index c8d874caa51..40ea4f80aab 100644
--- a/libjava/classpath/javax/swing/text/DefaultHighlighter.java
+++ b/libjava/classpath/javax/swing/text/DefaultHighlighter.java
@@ -168,6 +168,7 @@ public class DefaultHighlighter extends LayeredHighlighter
public DefaultHighlighter()
{
+ // Nothing to do here.
}
public boolean getDrawsLayeredHighlights()
@@ -238,6 +239,7 @@ public class DefaultHighlighter extends LayeredHighlighter
Shape viewBounds, JTextComponent editor,
View view)
{
+ // TODO: Implement this properly.
}
public void paint(Graphics g)
diff --git a/libjava/classpath/javax/swing/text/DefaultStyledDocument.java b/libjava/classpath/javax/swing/text/DefaultStyledDocument.java
index 3545e52c453..eb56bb0f8e5 100644
--- a/libjava/classpath/javax/swing/text/DefaultStyledDocument.java
+++ b/libjava/classpath/javax/swing/text/DefaultStyledDocument.java
@@ -1,5 +1,5 @@
/* DefaultStyledDocument.java --
- Copyright (C) 2004 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -41,8 +41,14 @@ package javax.swing.text;
import java.awt.Color;
import java.awt.Font;
import java.io.Serializable;
+import java.util.Enumeration;
+import java.util.Vector;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
+import javax.swing.undo.AbstractUndoableEdit;
+import javax.swing.undo.UndoableEdit;
/**
* The default implementation of {@link StyledDocument}.
@@ -60,12 +66,359 @@ public class DefaultStyledDocument extends AbstractDocument
implements StyledDocument
{
/**
+ * An {@link UndoableEdit} that can undo attribute changes to an element.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public static class AttributeUndoableEdit
+ extends AbstractUndoableEdit
+ {
+ /**
+ * A copy of the old attributes.
+ */
+ protected AttributeSet copy;
+
+ /**
+ * The new attributes.
+ */
+ protected AttributeSet newAttributes;
+
+ /**
+ * If the new attributes replaced the old attributes or if they only were
+ * added to them.
+ */
+ protected boolean isReplacing;
+
+ /**
+ * The element that has changed.
+ */
+ protected Element element;
+
+ /**
+ * Creates a new <code>AttributeUndoableEdit</code>.
+ *
+ * @param el the element that changes attributes
+ * @param newAtts the new attributes
+ * @param replacing if the new attributes replace the old or only append to
+ * them
+ */
+ public AttributeUndoableEdit(Element el, AttributeSet newAtts,
+ boolean replacing)
+ {
+ element = el;
+ newAttributes = newAtts;
+ isReplacing = replacing;
+ copy = el.getAttributes().copyAttributes();
+ }
+
+ /**
+ * Undos the attribute change. The <code>copy</code> field is set as
+ * attributes on <code>element</code>.
+ */
+ public void undo()
+ {
+ super.undo();
+ AttributeSet atts = element.getAttributes();
+ if (atts instanceof MutableAttributeSet)
+ {
+ MutableAttributeSet mutable = (MutableAttributeSet) atts;
+ mutable.removeAttributes(atts);
+ mutable.addAttributes(copy);
+ }
+ }
+
+ /**
+ * Redos an attribute change. This adds <code>newAttributes</code> to the
+ * <code>element</code>'s attribute set, possibly clearing all attributes
+ * if <code>isReplacing</code> is true.
+ */
+ public void redo()
+ {
+ super.undo();
+ AttributeSet atts = element.getAttributes();
+ if (atts instanceof MutableAttributeSet)
+ {
+ MutableAttributeSet mutable = (MutableAttributeSet) atts;
+ if (isReplacing)
+ mutable.removeAttributes(atts);
+ mutable.addAttributes(newAttributes);
+ }
+ }
+ }
+
+ /**
+ * Carries specification information for new {@link Element}s that should
+ * be created in {@link ElementBuffer}. This allows the parsing process
+ * to be decoupled from the <code>Element</code> creation process.
+ */
+ public static class ElementSpec
+ {
+ /**
+ * This indicates a start tag. This is a possible value for
+ * {@link #getType}.
+ */
+ public static final short StartTagType = 1;
+
+ /**
+ * This indicates an end tag. This is a possible value for
+ * {@link #getType}.
+ */
+ public static final short EndTagType = 2;
+
+ /**
+ * This indicates a content element. This is a possible value for
+ * {@link #getType}.
+ */
+ public static final short ContentType = 3;
+
+ /**
+ * This indicates that the data associated with this spec should be joined
+ * with what precedes it. This is a possible value for
+ * {@link #getDirection}.
+ */
+ public static final short JoinPreviousDirection = 4;
+
+ /**
+ * This indicates that the data associated with this spec should be joined
+ * with what follows it. This is a possible value for
+ * {@link #getDirection}.
+ */
+ public static final short JoinNextDirection = 5;
+
+ /**
+ * This indicates that the data associated with this spec should be used
+ * to create a new element. This is a possible value for
+ * {@link #getDirection}.
+ */
+ public static final short OriginateDirection = 6;
+
+ /**
+ * This indicates that the data associated with this spec should be joined
+ * to the fractured element. This is a possible value for
+ * {@link #getDirection}.
+ */
+ public static final short JoinFractureDirection = 7;
+
+ /**
+ * The type of the tag.
+ */
+ short type;
+
+ /**
+ * The direction of the tag.
+ */
+ short direction;
+
+ /**
+ * The offset of the content.
+ */
+ int offset;
+
+ /**
+ * The length of the content.
+ */
+ int length;
+
+ /**
+ * The actual content.
+ */
+ char[] content;
+
+ /**
+ * The attributes for the tag.
+ */
+ AttributeSet attributes;
+
+ /**
+ * Creates a new <code>ElementSpec</code> with no content, length or
+ * offset. This is most useful for start and end tags.
+ *
+ * @param a the attributes for the element to be created
+ * @param type the type of the tag
+ */
+ public ElementSpec(AttributeSet a, short type)
+ {
+ this(a, type, 0);
+ }
+
+ /**
+ * Creates a new <code>ElementSpec</code> that specifies the length but
+ * not the offset of an element. Such <code>ElementSpec</code>s are
+ * processed sequentially from a known starting point.
+ *
+ * @param a the attributes for the element to be created
+ * @param type the type of the tag
+ * @param len the length of the element
+ */
+ public ElementSpec(AttributeSet a, short type, int len)
+ {
+ this(a, type, null, 0, len);
+ }
+
+ /**
+ * Creates a new <code>ElementSpec</code> with document content.
+ *
+ * @param a the attributes for the element to be created
+ * @param type the type of the tag
+ * @param txt the actual content
+ * @param offs the offset into the <code>txt</code> array
+ * @param len the length of the element
+ */
+ public ElementSpec(AttributeSet a, short type, char[] txt, int offs,
+ int len)
+ {
+ attributes = a;
+ this.type = type;
+ offset = offs;
+ length = len;
+ content = txt;
+ direction = OriginateDirection;
+ }
+
+ /**
+ * Sets the type of the element.
+ *
+ * @param type the type of the element to be set
+ */
+ public void setType(short type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * Returns the type of the element.
+ *
+ * @return the type of the element
+ */
+ public short getType()
+ {
+ return type;
+ }
+
+ /**
+ * Sets the direction of the element.
+ *
+ * @param dir the direction of the element to be set
+ */
+ public void setDirection(short dir)
+ {
+ direction = dir;
+ }
+
+ /**
+ * Returns the direction of the element.
+ *
+ * @return the direction of the element
+ */
+ public short getDirection()
+ {
+ return direction;
+ }
+
+ /**
+ * Returns the attributes of the element.
+ *
+ * @return the attributes of the element
+ */
+ public AttributeSet getAttributes()
+ {
+ return attributes;
+ }
+
+ /**
+ * Returns the actual content of the element.
+ *
+ * @return the actual content of the element
+ */
+ public char[] getArray()
+ {
+ return content;
+ }
+
+ /**
+ * Returns the offset of the content.
+ *
+ * @return the offset of the content
+ */
+ public int getOffset()
+ {
+ return offset;
+ }
+
+ /**
+ * Returns the length of the content.
+ *
+ * @return the length of the content
+ */
+ public int getLength()
+ {
+ return length;
+ }
+
+ /**
+ * Returns a String representation of this <code>ElementSpec</code>
+ * describing the type, direction and length of this
+ * <code>ElementSpec</code>.
+ *
+ * @return a String representation of this <code>ElementSpec</code>
+ */
+ public String toString()
+ {
+ StringBuilder b = new StringBuilder();
+ b.append('<');
+ switch (type)
+ {
+ case StartTagType:
+ b.append("StartTag");
+ break;
+ case EndTagType:
+ b.append("EndTag");
+ break;
+ case ContentType:
+ b.append("Content");
+ break;
+ default:
+ b.append("??");
+ break;
+ }
+
+ b.append(':');
+
+ switch (direction)
+ {
+ case JoinPreviousDirection:
+ b.append("JoinPrevious");
+ break;
+ case JoinNextDirection:
+ b.append("JoinNext");
+ break;
+ case OriginateDirection:
+ b.append("Originate");
+ break;
+ case JoinFractureDirection:
+ b.append("Fracture");
+ break;
+ default:
+ b.append("??");
+ break;
+ }
+
+ b.append(':');
+ b.append(length);
+
+ return b.toString();
+ }
+ }
+
+ /**
* Performs all <em>structural</code> changes to the <code>Element</code>
* hierarchy.
*/
- public class ElementBuffer
- implements Serializable
+ public class ElementBuffer implements Serializable
{
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 1688745877691146623L;
+
/** The root element of the hierarchy. */
private Element root;
@@ -76,6 +429,19 @@ public class DefaultStyledDocument extends AbstractDocument
private int length;
/**
+ * Holds fractured elements during insertion of end and start tags.
+ * Inserting an end tag may lead to fracturing of the current paragraph
+ * element. The elements that have been cut off may be added to the
+ * next paragraph that is created in the next start tag.
+ */
+ Element[] fracture;
+
+ /**
+ * The ElementChange that describes the latest changes.
+ */
+ DefaultDocumentEvent documentEvent;
+
+ /**
* Creates a new <code>ElementBuffer</code> for the specified
* <code>root</code> element.
*
@@ -114,6 +480,7 @@ public class DefaultStyledDocument extends AbstractDocument
{
this.offset = offset;
this.length = length;
+ documentEvent = ev;
changeUpdate();
}
@@ -142,38 +509,352 @@ public class DefaultStyledDocument extends AbstractDocument
void split(Element el, int offset)
{
if (el instanceof AbstractElement)
- {
- AbstractElement ael = (AbstractElement) el;
- int startOffset = ael.getStartOffset();
- int endOffset = ael.getEndOffset();
- int len = endOffset - startOffset;
- if (startOffset != offset && endOffset != offset)
- {
- Element paragraph = ael.getParentElement();
- if (paragraph instanceof BranchElement)
- {
- BranchElement par = (BranchElement) paragraph;
- Element child1 = createLeafElement(par, ael, startOffset,
- offset);
- Element child2 = createLeafElement(par, ael, offset,
- endOffset);
- int index = par.getElementIndex(startOffset);
- par.replace(index, 1, new Element[]{ child1, child2 });
- }
+ {
+ AbstractElement ael = (AbstractElement) el;
+ int startOffset = ael.getStartOffset();
+ int endOffset = ael.getEndOffset();
+ int len = endOffset - startOffset;
+ if (startOffset != offset && endOffset != offset)
+ {
+ Element paragraph = ael.getParentElement();
+ if (paragraph instanceof BranchElement)
+ {
+ BranchElement par = (BranchElement) paragraph;
+ Element child1 = createLeafElement(par, ael, startOffset,
+ offset);
+ Element child2 = createLeafElement(par, ael, offset,
+ endOffset);
+ int index = par.getElementIndex(startOffset);
+ Element[] add = new Element[]{ child1, child2 };
+ par.replace(index, 1, add);
+ documentEvent.addEdit(new ElementEdit(par, index,
+ new Element[]{ el },
+ add));
+ }
else
throw new AssertionError("paragraph elements are expected to "
+ "be instances of "
+ "javax.swing.text.AbstractDocument.BranchElement");
- }
- }
+ }
+ }
else
- throw new AssertionError("content elements are expected to be "
- + "instances of "
+ throw new AssertionError("content elements are expected to be "
+ + "instances of "
+ "javax.swing.text.AbstractDocument.AbstractElement");
}
+
+ /**
+ * Inserts new <code>Element</code> in the document at the specified
+ * position.
+ *
+ * Most of the work is done by {@link #insertUpdate}, after some fields
+ * have been prepared for it.
+ *
+ * @param offset the location in the document at which the content is
+ * inserted
+ * @param length the length of the inserted content
+ * @param data the element specifications for the content to be inserted
+ * @param ev the document event that is updated to reflect the structural
+ * changes
+ */
+ public void insert(int offset, int length, ElementSpec[] data,
+ DefaultDocumentEvent ev)
+ {
+ this.offset = offset;
+ this.length = length;
+ documentEvent = ev;
+ insertUpdate(data);
+ }
+
+ /**
+ * Performs the actual structural change for {@link #insert}. This
+ * creates a bunch of {@link Element}s as specified by <code>data</code>
+ * and inserts it into the document as specified in the arguments to
+ * {@link #insert}.
+ *
+ * @param data the element specifications for the elements to be inserte
+ */
+ protected void insertUpdate(ElementSpec[] data)
+ {
+ for (int i = 0; i < data.length; i++)
+ {
+ switch (data[i].getType())
+ {
+ case ElementSpec.StartTagType:
+ insertStartTag(data[i]);
+ break;
+ case ElementSpec.EndTagType:
+ insertEndTag(data[i]);
+ break;
+ default:
+ insertContentTag(data[i]);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Insert a new paragraph after the paragraph at the current position.
+ *
+ * @param tag the element spec that describes the element to be inserted
+ */
+ void insertStartTag(ElementSpec tag)
+ {
+ BranchElement root = (BranchElement) getDefaultRootElement();
+ int index = root.getElementIndex(offset);
+ if (index == -1)
+ index = 0;
+
+ BranchElement newParagraph =
+ (BranchElement) createBranchElement(root, tag.getAttributes());
+ newParagraph.setResolveParent(getStyle(StyleContext.DEFAULT_STYLE));
+
+ // Add new paragraph into document structure.
+ Element[] added = new Element[]{newParagraph};
+ root.replace(index + 1, 0, added);
+ ElementEdit edit = new ElementEdit(root, index + 1, new Element[0],
+ added);
+ documentEvent.addEdit(edit);
+
+ // Maybe add fractured elements.
+ if (tag.getDirection() == ElementSpec.JoinFractureDirection)
+ {
+ Element[] newFracture = new Element[fracture.length];
+ for (int i = 0; i < fracture.length; i++)
+ {
+ Element oldLeaf = fracture[i];
+ Element newLeaf = createLeafElement(newParagraph,
+ oldLeaf.getAttributes(),
+ oldLeaf.getStartOffset(),
+ oldLeaf.getEndOffset());
+ newFracture[i] = newLeaf;
+ }
+ newParagraph.replace(0, 0, newFracture);
+ edit = new ElementEdit(newParagraph, 0, new Element[0],
+ fracture);
+ documentEvent.addEdit(edit);
+ fracture = new Element[0];
+ }
+ }
+
+ /**
+ * Inserts an end tag into the document structure. This cuts of the
+ * current paragraph element, possibly fracturing it's child elements.
+ * The fractured elements are saved so that they can be joined later
+ * with a new paragraph element.
+ */
+ void insertEndTag(ElementSpec tag)
+ {
+ BranchElement root = (BranchElement) getDefaultRootElement();
+ int parIndex = root.getElementIndex(offset);
+ BranchElement paragraph = (BranchElement) root.getElement(parIndex);
+
+ int index = paragraph.getElementIndex(offset);
+ LeafElement content = (LeafElement) paragraph.getElement(index);
+ // We might have to split the element at offset.
+ split(content, offset);
+ index = paragraph.getElementIndex(offset);
+
+ int count = paragraph.getElementCount();
+ // Store fractured elements.
+ fracture = new Element[count - index];
+ for (int i = index; i < count; ++i)
+ fracture[i - index] = paragraph.getElement(i);
+
+ // Delete fractured elements.
+ paragraph.replace(index, count - index, new Element[0]);
+
+ // Add this action to the document event.
+ ElementEdit edit = new ElementEdit(paragraph, index, fracture,
+ new Element[0]);
+ documentEvent.addEdit(edit);
+ }
+
+ /**
+ * Inserts a content element into the document structure.
+ *
+ * @param tag the element spec
+ */
+ void insertContentTag(ElementSpec tag)
+ {
+ int len = tag.getLength();
+ int dir = tag.getDirection();
+ if (dir == ElementSpec.JoinPreviousDirection)
+ {
+ Element prev = getCharacterElement(offset);
+ BranchElement prevParent = (BranchElement) prev.getParentElement();
+ Element join = createLeafElement(prevParent, tag.getAttributes(),
+ prev.getStartOffset(),
+ Math.max(prev.getEndOffset(),
+ offset + len));
+ int ind = prevParent.getElementIndex(offset);
+ if (ind == -1)
+ ind = 0;
+ Element[] add = new Element[]{join};
+ prevParent.replace(ind, 1, add);
+
+ // Add this action to the document event.
+ ElementEdit edit = new ElementEdit(prevParent, ind,
+ new Element[]{prev}, add);
+ documentEvent.addEdit(edit);
+ }
+ else if (dir == ElementSpec.JoinNextDirection)
+ {
+ Element next = getCharacterElement(offset + len);
+ BranchElement nextParent = (BranchElement) next.getParentElement();
+ Element join = createLeafElement(nextParent, tag.getAttributes(),
+ offset,
+ next.getEndOffset());
+ int ind = nextParent.getElementIndex(offset + len);
+ if (ind == -1)
+ ind = 0;
+ Element[] add = new Element[]{join};
+ nextParent.replace(ind, 1, add);
+
+ // Add this action to the document event.
+ ElementEdit edit = new ElementEdit(nextParent, ind,
+ new Element[]{next}, add);
+ documentEvent.addEdit(edit);
+ }
+ else
+ {
+ BranchElement par = (BranchElement) getParagraphElement(offset);
+
+ int ind = par.getElementIndex(offset);
+
+ // Make room for the element.
+ // Cut previous element.
+ Element prev = par.getElement(ind);
+ if (prev != null && prev.getStartOffset() < offset)
+ {
+ Element cutPrev = createLeafElement(par, prev.getAttributes(),
+ prev.getStartOffset(),
+ offset);
+ Element[] remove = new Element[]{prev};
+ Element[] add = new Element[]{cutPrev};
+ if (prev.getEndOffset() > offset + len)
+ {
+ Element rem = createLeafElement(par, prev.getAttributes(),
+ offset + len,
+ prev.getEndOffset());
+ add = new Element[]{cutPrev, rem};
+ }
+
+ par.replace(ind, 1, add);
+ documentEvent.addEdit(new ElementEdit(par, ind, remove, add));
+ ind++;
+ }
+ // ind now points to the next element.
+
+ // Cut next element if necessary.
+ Element next = par.getElement(ind);
+ if (next != null && next.getStartOffset() < offset + len)
+ {
+ Element cutNext = createLeafElement(par, next.getAttributes(),
+ offset + len,
+ next.getEndOffset());
+ Element[] remove = new Element[]{next};
+ Element[] add = new Element[]{cutNext};
+ par.replace(ind, 1, add);
+ documentEvent.addEdit(new ElementEdit(par, ind, remove,
+ add));
+ }
+
+ // Insert new element.
+ Element newEl = createLeafElement(par, tag.getAttributes(),
+ offset, offset + len);
+ Element[] added = new Element[]{newEl};
+ par.replace(ind, 0, added);
+ // Add this action to the document event.
+ ElementEdit edit = new ElementEdit(par, ind, new Element[0],
+ added);
+ documentEvent.addEdit(edit);
+ }
+ offset += len;
+ }
+
+ /**
+ * Creates a copy of the element <code>clonee</code> that has the parent
+ * <code>parent</code>.
+ * @param parent the parent of the newly created Element
+ * @param clonee the Element to clone
+ * @return the cloned Element
+ */
+ public Element clone (Element parent, Element clonee)
+ {
+ // If the Element we want to clone is a leaf, then simply copy it
+ if (clonee.isLeaf())
+ return createLeafElement(parent, clonee.getAttributes(),
+ clonee.getStartOffset(), clonee.getEndOffset());
+
+ // Otherwise create a new BranchElement with the desired parent and
+ // the clonee's attributes
+ BranchElement result = (BranchElement) createBranchElement(parent, clonee.getAttributes());
+
+ // And clone all the of clonee's children
+ Element[] children = new Element[clonee.getElementCount()];
+ for (int i = 0; i < children.length; i++)
+ children[i] = clone(result, clonee.getElement(i));
+
+ // Make the cloned children the children of the BranchElement
+ result.replace(0, 0, children);
+ return result;
+ }
}
/**
+ * An element type for sections. This is a simple BranchElement with
+ * a unique name.
+ */
+ protected class SectionElement extends BranchElement
+ {
+ /**
+ * Creates a new SectionElement.
+ */
+ public SectionElement()
+ {
+ super(null, null);
+ }
+
+ /**
+ * Returns the name of the element. This method always returns
+ * &quot;section&quot;.
+ *
+ * @return the name of the element
+ */
+ public String getName()
+ {
+ return "section";
+ }
+ }
+
+ /**
+ * Receives notification when any of the document's style changes and calls
+ * {@link DefaultStyledDocument#styleChanged(Style)}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class StyleChangeListener
+ implements ChangeListener
+ {
+
+ /**
+ * Receives notification when any of the document's style changes and calls
+ * {@link DefaultStyledDocument#styleChanged(Style)}.
+ *
+ * @param event the change event
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ Style style = (Style) event.getSource();
+ styleChanged(style);
+ }
+ }
+
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 940485415728614849L;
+
+ /**
* The default size to use for new content buffers.
*/
public static final int BUFFER_SIZE_DEFAULT = 4096;
@@ -185,6 +866,11 @@ public class DefaultStyledDocument extends AbstractDocument
protected DefaultStyledDocument.ElementBuffer buffer;
/**
+ * Listens for changes on this document's styles and notifies styleChanged().
+ */
+ private StyleChangeListener styleChangeListener;
+
+ /**
* Creates a new <code>DefaultStyledDocument</code>.
*/
public DefaultStyledDocument()
@@ -237,7 +923,14 @@ public class DefaultStyledDocument extends AbstractDocument
public Style addStyle(String nm, Style parent)
{
StyleContext context = (StyleContext) getAttributeContext();
- return context.addStyle(nm, parent);
+ Style newStyle = context.addStyle(nm, parent);
+
+ // Register change listener.
+ if (styleChangeListener == null)
+ styleChangeListener = new StyleChangeListener();
+ newStyle.addChangeListener(styleChangeListener);
+
+ return newStyle;
}
/**
@@ -250,9 +943,11 @@ public class DefaultStyledDocument extends AbstractDocument
Element[] tmp;
// FIXME: Create a SecionElement here instead of a BranchElement.
// Use createBranchElement() and createLeafElement instead.
- BranchElement section = new BranchElement(null, null);
-
- BranchElement paragraph = new BranchElement(section, null);
+ SectionElement section = new SectionElement();
+
+ BranchElement paragraph =
+ (BranchElement) createBranchElement(section, null);
+ paragraph.setResolveParent(getStyle(StyleContext.DEFAULT_STYLE));
tmp = new Element[1];
tmp[0] = paragraph;
section.replace(0, 0, tmp);
@@ -261,7 +956,7 @@ public class DefaultStyledDocument extends AbstractDocument
tmp = new Element[1];
tmp[0] = leaf;
paragraph.replace(0, 0, tmp);
-
+
return section;
}
@@ -279,10 +974,10 @@ public class DefaultStyledDocument extends AbstractDocument
{
Element element = getDefaultRootElement();
- while (! element.isLeaf())
+ while (!element.isLeaf())
{
- int index = element.getElementIndex(position);
- element = element.getElement(index);
+ int index = element.getElementIndex(position);
+ element = element.getElement(index);
}
return element;
@@ -353,6 +1048,10 @@ public class DefaultStyledDocument extends AbstractDocument
/**
* Returns the paragraph element for the specified position.
+ * If the position is outside the bounds of the document's root element,
+ * then the closest element is returned. That is the last paragraph if
+ * <code>position >= endIndex</code> or the first paragraph if
+ * <code>position < startIndex</code>.
*
* @param position the position for which to query the paragraph element
*
@@ -360,8 +1059,18 @@ public class DefaultStyledDocument extends AbstractDocument
*/
public Element getParagraphElement(int position)
{
- Element element = getCharacterElement(position);
- return element.getParentElement();
+ BranchElement root = (BranchElement) getDefaultRootElement();
+ int start = root.getStartOffset();
+ int end = root.getEndOffset();
+ if (position >= end)
+ position = end - 1;
+ else if (position < start)
+ position = start;
+
+ Element par = root.positionToElement(position);
+
+ assert par != null : "The paragraph element must not be null";
+ return par;
}
/**
@@ -416,35 +1125,37 @@ public class DefaultStyledDocument extends AbstractDocument
int paragraphCount = root.getElementCount();
for (int pindex = 0; pindex < paragraphCount; pindex++)
{
- Element paragraph = root.getElement(pindex);
- // Skip paragraphs that lie outside the interval.
- if ((paragraph.getStartOffset() > offset + length)
- || (paragraph.getEndOffset() < offset))
- continue;
-
- // Visit content elements within this paragraph
- int contentCount = paragraph.getElementCount();
- for (int cindex = 0; cindex < contentCount; cindex++)
- {
- Element content = paragraph.getElement(cindex);
- // Skip content that lies outside the interval.
- if ((content.getStartOffset() > offset + length)
- || (content.getEndOffset() < offset))
- continue;
-
- if (content instanceof AbstractElement)
- {
- AbstractElement el = (AbstractElement) content;
- if (replace)
- el.removeAttributes(el);
- el.addAttributes(attributes);
- }
- else
- throw new AssertionError("content elements are expected to be"
- + "instances of "
+ Element paragraph = root.getElement(pindex);
+ // Skip paragraphs that lie outside the interval.
+ if ((paragraph.getStartOffset() > offset + length)
+ || (paragraph.getEndOffset() < offset))
+ continue;
+
+ // Visit content elements within this paragraph
+ int contentCount = paragraph.getElementCount();
+ for (int cindex = 0; cindex < contentCount; cindex++)
+ {
+ Element content = paragraph.getElement(cindex);
+ // Skip content that lies outside the interval.
+ if ((content.getStartOffset() > offset + length)
+ || (content.getEndOffset() < offset))
+ continue;
+
+ if (content instanceof AbstractElement)
+ {
+ AbstractElement el = (AbstractElement) content;
+ if (replace)
+ el.removeAttributes(el);
+ el.addAttributes(attributes);
+ }
+ else
+ throw new AssertionError("content elements are expected to be"
+ + "instances of "
+ "javax.swing.text.AbstractDocument.AbstractElement");
- }
+ }
}
+
+ fireChangedUpdate(ev);
}
/**
@@ -476,10 +1187,199 @@ public class DefaultStyledDocument extends AbstractDocument
* selection are overridden, otherwise they are merged
*/
public void setParagraphAttributes(int offset, int length,
- AttributeSet attributes,
- boolean replace)
+ AttributeSet attributes,
+ boolean replace)
+ {
+ int index = offset;
+ while (index < offset + length)
+ {
+ AbstractElement par = (AbstractElement) getParagraphElement(index);
+ AttributeContext ctx = getAttributeContext();
+ if (replace)
+ par.removeAttributes(par);
+ par.addAttributes(attributes);
+ index = par.getElementCount();
+ }
+ }
+
+ /**
+ * Called in response to content insert actions. This is used to
+ * update the element structure.
+ *
+ * @param ev the <code>DocumentEvent</code> describing the change
+ * @param attr the attributes for the change
+ */
+ protected void insertUpdate(DefaultDocumentEvent ev, AttributeSet attr)
+ {
+ super.insertUpdate(ev, attr);
+ int offset = ev.getOffset();
+ int length = ev.getLength();
+ int endOffset = offset + length;
+ Segment txt = new Segment();
+ try
+ {
+ getText(offset, length, txt);
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError ae = new AssertionError("Unexpected bad location");
+ ae.initCause(ex);
+ throw ae;
+ }
+
+ int len = 0;
+ Vector specs = new Vector();
+
+ Element prev = getCharacterElement(offset);
+ Element next = getCharacterElement(endOffset);
+
+ for (int i = offset; i < endOffset; ++i)
+ {
+ len++;
+ if (txt.array[i] == '\n')
+ {
+ ElementSpec spec = new ElementSpec(attr, ElementSpec.ContentType,
+ len);
+
+ // If we are at the last index, then check if we could probably be
+ // joined with the next element.
+ if (i == endOffset - 1)
+ {
+ if (next.getAttributes().isEqual(attr))
+ spec.setDirection(ElementSpec.JoinNextDirection);
+ }
+ // If we are at the first new element, then check if it could be
+ // joined with the previous element.
+ else if (specs.size() == 0)
+ {
+ if (prev.getAttributes().isEqual(attr))
+ spec.setDirection(ElementSpec.JoinPreviousDirection);
+ }
+
+ specs.add(spec);
+
+ // Add ElementSpecs for the newline.
+ ElementSpec endTag = new ElementSpec(null, ElementSpec.EndTagType);
+ specs.add(endTag);
+ ElementSpec startTag = new ElementSpec(null,
+ ElementSpec.StartTagType);
+ startTag.setDirection(ElementSpec.JoinFractureDirection);
+ specs.add(startTag);
+
+ len = 0;
+ offset += len;
+ }
+ }
+
+ // Create last element if last character hasn't been a newline.
+ if (len > 0)
+ {
+ ElementSpec spec = new ElementSpec(attr, ElementSpec.ContentType, len);
+ // If we are at the first new element, then check if it could be
+ // joined with the previous element.
+ if (specs.size() == 0)
+ {
+ if (prev.getAttributes().isEqual(attr))
+ spec.setDirection(ElementSpec.JoinPreviousDirection);
+ }
+ // Check if we could probably be joined with the next element.
+ else if (next.getAttributes().isEqual(attr))
+ spec.setDirection(ElementSpec.JoinNextDirection);
+
+ specs.add(spec);
+ }
+
+ ElementSpec[] elSpecs =
+ (ElementSpec[]) specs.toArray(new ElementSpec[specs.size()]);
+
+ buffer.insert(offset, length, elSpecs, ev);
+ }
+
+ /**
+ * Returns an enumeration of all style names.
+ *
+ * @return an enumeration of all style names
+ */
+ public Enumeration getStyleNames()
+ {
+ StyleContext context = (StyleContext) getAttributeContext();
+ return context.getStyleNames();
+ }
+
+ /**
+ * Called when any of this document's styles changes.
+ *
+ * @param style the style that changed
+ */
+ protected void styleChanged(Style style)
{
- // FIXME: Implement me.
- throw new Error("not implemented");
+ // Nothing to do here. This is intended to be overridden by subclasses.
+ }
+
+ /**
+ * Inserts a bulk of structured content at once.
+ *
+ * @param offset the offset at which the content should be inserted
+ * @param data the actual content spec to be inserted
+ */
+ protected void insert(int offset, ElementSpec[] data)
+ throws BadLocationException
+ {
+ writeLock();
+ // First we insert the content.
+ int index = offset;
+ for (int i = 0; i < data.length; i++)
+ {
+ ElementSpec spec = data[i];
+ if (spec.getArray() != null && spec.getLength() > 0)
+ {
+ String insertString = new String(spec.getArray(), spec.getOffset(),
+ spec.getLength());
+ content.insertString(index, insertString);
+ }
+ index += spec.getLength();
+ }
+ // Update the view structure.
+ DefaultDocumentEvent ev = new DefaultDocumentEvent(offset, index - offset,
+ DocumentEvent.EventType.INSERT);
+ for (int i = 0; i < data.length; i++)
+ {
+ ElementSpec spec = data[i];
+ AttributeSet atts = spec.getAttributes();
+ if (atts != null)
+ insertUpdate(ev, atts);
+ }
+
+ // Finally we must update the document structure and fire the insert update
+ // event.
+ buffer.insert(offset, index - offset, data, ev);
+ fireInsertUpdate(ev);
+ writeUnlock();
+ }
+
+ /**
+ * Initializes the <code>DefaultStyledDocument</code> with the specified
+ * data.
+ *
+ * @param data the specification of the content with which the document is
+ * initialized
+ */
+ protected void create(ElementSpec[] data)
+ {
+ try
+ {
+ // Clear content.
+ content.remove(0, content.length());
+ // Clear buffer and root element.
+ buffer = new ElementBuffer(createDefaultRoot());
+ // Insert the data.
+ insert(0, data);
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError err = new AssertionError("Unexpected bad location");
+ err.initCause(ex);
+ throw err;
+ }
}
}
diff --git a/libjava/classpath/javax/swing/text/DefaultTextUI.java b/libjava/classpath/javax/swing/text/DefaultTextUI.java
new file mode 100644
index 00000000000..e7ff01845e6
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/DefaultTextUI.java
@@ -0,0 +1,61 @@
+/* DefaultTextUI.java -- Deprecated base UI for text components
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text;
+
+import javax.swing.plaf.basic.BasicTextUI;
+
+/**
+ * This class is deprecated and should not be used anymore. The base UI for
+ * all text components is now {@link BasicTextUI}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public abstract class DefaultTextUI extends BasicTextUI
+{
+ /**
+ * This class is deprecated and should not be used anymore. The base UI for
+ * all text components is now {@link BasicTextUI}.
+ *
+ * @deprecated use {@link BasicTextUI} instead
+ */
+ public DefaultTextUI()
+ {
+ // Nothing to do here.
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/EditorKit.java b/libjava/classpath/javax/swing/text/EditorKit.java
index bd51a866f68..8719aee595f 100644
--- a/libjava/classpath/javax/swing/text/EditorKit.java
+++ b/libjava/classpath/javax/swing/text/EditorKit.java
@@ -48,24 +48,24 @@ import java.io.Writer;
import javax.swing.Action;
import javax.swing.JEditorPane;
-public abstract class EditorKit
- implements Cloneable, Serializable
+public abstract class EditorKit implements Cloneable, Serializable
{
private static final long serialVersionUID = -5044124649345887822L;
public EditorKit()
{
+ // Nothing to do here.
}
public Object clone()
{
try
{
- return super.clone();
+ return super.clone();
}
catch (CloneNotSupportedException e)
{
- return null;
+ return null;
}
}
@@ -74,10 +74,12 @@ public abstract class EditorKit
*/
public void deinstall(JEditorPane c)
{
+ // This default implementation does nothing.
}
public void install(JEditorPane c)
{
+ // This default implementation does nothing.
}
public abstract Caret createCaret();
diff --git a/libjava/classpath/javax/swing/text/FieldView.java b/libjava/classpath/javax/swing/text/FieldView.java
index e2e04d7c495..2496418444e 100644
--- a/libjava/classpath/javax/swing/text/FieldView.java
+++ b/libjava/classpath/javax/swing/text/FieldView.java
@@ -118,22 +118,24 @@ public class FieldView extends PlainView
FontMetrics fm = getFontMetrics();
if (axis == Y_AXIS)
- return fm.getHeight();
+ return super.getPreferredSpan(axis);
String text;
Element elem = getElement();
try
{
- text = elem.getDocument().getText(elem.getStartOffset(),
- elem.getEndOffset());
+ text = elem.getDocument().getText(elem.getStartOffset(),
+ elem.getEndOffset());
}
catch (BadLocationException e)
{
- // This should never happen.
- text = "";
+ // Should never happen
+ AssertionError ae = new AssertionError();
+ ae.initCause(e);
+ throw ae;
}
-
+
return fm.stringWidth(text);
}
@@ -159,18 +161,21 @@ public class FieldView extends PlainView
{
Shape newAlloc = adjustAllocation(shape);
super.insertUpdate(ev, newAlloc, vf);
+ getContainer().repaint();
}
public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
{
Shape newAlloc = adjustAllocation(shape);
super.removeUpdate(ev, newAlloc, vf);
+ getContainer().repaint();
}
public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
{
Shape newAlloc = adjustAllocation(shape);
super.removeUpdate(ev, newAlloc, vf);
+ getContainer().repaint();
}
public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias)
diff --git a/libjava/classpath/javax/swing/text/FlowView.java b/libjava/classpath/javax/swing/text/FlowView.java
index a6ef89efb78..fd6785b6f18 100644
--- a/libjava/classpath/javax/swing/text/FlowView.java
+++ b/libjava/classpath/javax/swing/text/FlowView.java
@@ -42,8 +42,10 @@ import java.awt.Container;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
+import java.util.Iterator;
import java.util.Vector;
+import javax.swing.SwingConstants;
import javax.swing.event.DocumentEvent;
/**
@@ -71,6 +73,7 @@ public abstract class FlowView extends BoxView
*/
public FlowStrategy()
{
+ // Nothing to do here.
}
/**
@@ -137,7 +140,7 @@ public abstract class FlowView extends BoxView
* Performs the layout for the whole view. By default this rebuilds
* all the physical views from the logical views of the managed FlowView.
*
- * This is called by {@link FlowLayout#layout} to update the layout of
+ * This is called by {@link FlowView#layout} to update the layout of
* the view.
*
* @param fv the flow view for which we perform the layout
@@ -183,11 +186,17 @@ public abstract class FlowView extends BoxView
{
View child = createView(fv, offset, spanLeft, rowIndex);
if (child == null)
- break;
+ {
+ offset = -1;
+ break;
+ }
int span = (int) child.getPreferredSpan(flowAxis);
if (span > spanLeft)
- break;
+ {
+ offset = -1;
+ break;
+ }
row.append(child);
spanLeft -= span;
@@ -204,7 +213,7 @@ public abstract class FlowView extends BoxView
* not fit in the available span and also cannot be broken down).
*
* @param fv the flow view
- * @param startOffset the start offset for the view to be created
+ * @param offset the start offset for the view to be created
* @param spanLeft the available span
* @param rowIndex the index of the row
*
@@ -218,13 +227,15 @@ public abstract class FlowView extends BoxView
View logicalView = getLogicalView(fv);
int viewIndex = logicalView.getViewIndex(offset, Position.Bias.Forward);
+ if (viewIndex == -1)
+ return null;
+
View child = logicalView.getView(viewIndex);
int flowAxis = fv.getFlowAxis();
int span = (int) child.getPreferredSpan(flowAxis);
if (span <= spanLeft)
return child;
-
else if (child.getBreakWeight(flowAxis, offset, spanLeft)
> BadBreakWeight)
// FIXME: What to do with the pos parameter here?
@@ -326,7 +337,19 @@ public abstract class FlowView extends BoxView
*/
public int getViewIndex(int pos, Position.Bias b)
{
- return getElement().getElementIndex(pos);
+ int index = -1;
+ int i = 0;
+ for (Iterator it = children.iterator(); it.hasNext(); i++)
+ {
+ View child = (View) it.next();
+ if (child.getStartOffset() >= pos
+ && child.getEndOffset() < pos)
+ {
+ index = i;
+ break;
+ }
+ }
+ return index;
}
/**
@@ -373,6 +396,37 @@ public abstract class FlowView extends BoxView
throw new AssertionError("This method must not be called in "
+ "LogicalView.");
}
+
+ /**
+ * Returns the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction <code>d</code>.
+ *
+ * @param c the text component
+ * @param pos the document position
+ * @param b the bias for <code>pos</code>
+ * @param d the direction, must be either {@link SwingConstants#NORTH},
+ * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
+ * {@link SwingConstants#EAST}
+ * @param biasRet an array of {@link Position.Bias} that can hold at least
+ * one element, which is filled with the bias of the return position
+ * on method exit
+ *
+ * @return the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction
+ * <code>d</code>
+ *
+ * @throws BadLocationException if <code>pos</code> is not a valid offset in
+ * the document model
+ */
+ public int getNextVisualPositionFrom(JTextComponent c, int pos,
+ Position.Bias b, int d,
+ Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ assert false : "getNextVisualPositionFrom() must not be called in "
+ + "LogicalView";
+ return 0;
+ }
}
/**
@@ -478,7 +532,7 @@ public abstract class FlowView extends BoxView
* The real children are created at layout time and each represent one
* row.
*
- * This method is called by {@link #setParent} in order to initialize
+ * This method is called by {@link View#setParent} in order to initialize
* the view.
*
* @param vf the view factory to use for creating the child views
@@ -502,7 +556,7 @@ public abstract class FlowView extends BoxView
/**
* Performs the layout of this view. If the span along the flow axis changed,
- * this first calls {@link FlowStrategy.layout} in order to rebuild the
+ * this first calls {@link FlowStrategy#layout} in order to rebuild the
* rows of this view. Then the superclass's behaviour is called to arrange
* the rows within the box.
*
diff --git a/libjava/classpath/javax/swing/text/GapContent.java b/libjava/classpath/javax/swing/text/GapContent.java
index 1dd46c4b0f4..4c65de0f5f4 100644
--- a/libjava/classpath/javax/swing/text/GapContent.java
+++ b/libjava/classpath/javax/swing/text/GapContent.java
@@ -39,10 +39,15 @@ exception statement from your version. */
package javax.swing.text;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.Collections;
-import java.util.LinkedList;
+import java.util.Iterator;
import java.util.ListIterator;
+import java.util.Vector;
+import javax.swing.undo.AbstractUndoableEdit;
+import javax.swing.undo.CannotRedoException;
+import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoableEdit;
/**
@@ -59,7 +64,6 @@ import javax.swing.undo.UndoableEdit;
public class GapContent
implements AbstractDocument.Content, Serializable
{
-
/**
* A {@link Position} implementation for <code>GapContent</code>.
*/
@@ -114,6 +118,11 @@ public class GapContent
*/
public int getOffset()
{
+ // Check precondition.
+ assert mark <= gapStart || mark >= gapEnd : "mark: " + mark
+ + ", gapStart: " + gapStart
+ + ", gapEnd: " + gapEnd;
+
if (mark <= gapStart)
return mark;
else
@@ -121,13 +130,91 @@ public class GapContent
}
}
- private static final long serialVersionUID = 8374645204155842629L;
+ class UndoInsertString extends AbstractUndoableEdit
+ {
+ public int where, length;
+ String text;
+ public UndoInsertString(int start, int len)
+ {
+ where = start;
+ length = len;
+ }
+
+ public void undo () throws CannotUndoException
+ {
+ super.undo();
+ try
+ {
+ text = getString(where, length);
+ remove(where, length);
+ }
+ catch (BadLocationException ble)
+ {
+ throw new CannotUndoException();
+ }
+ }
+
+ public void redo () throws CannotUndoException
+ {
+ super.redo();
+ try
+ {
+ insertString(where, text);
+ }
+ catch (BadLocationException ble)
+ {
+ throw new CannotRedoException();
+ }
+ }
+
+ }
+
+ class UndoRemove extends AbstractUndoableEdit
+ {
+ public int where;
+ String text;
+ public UndoRemove(int start, String removedText)
+ {
+ where = start;
+ text = removedText;
+ }
+
+ public void undo () throws CannotUndoException
+ {
+ super.undo();
+ try
+ {
+ insertString(where, text);
+ }
+ catch (BadLocationException ble)
+ {
+ throw new CannotUndoException();
+ }
+ }
+
+ public void redo () throws CannotUndoException
+ {
+ super.redo();
+ try
+ {
+ remove(where, text.length());
+ }
+ catch (BadLocationException ble)
+ {
+ throw new CannotRedoException();
+ }
+ }
+
+ }
+
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = -6226052713477823730L;
/**
* This is the default buffer size and the amount of bytes that a buffer is
* extended if it is full.
*/
- static final int DEFAULT_BUFSIZE = 64;
+ static final int DEFAULT_BUFSIZE = 10;
/**
* The text buffer.
@@ -148,7 +235,7 @@ public class GapContent
* The positions generated by this GapContent. They are kept in an ordered
* fashion, so they can be looked up easily.
*/
- LinkedList positions;
+ ArrayList positions;
/**
* Creates a new GapContent object.
@@ -166,10 +253,10 @@ public class GapContent
public GapContent(int size)
{
buffer = (char[]) allocateArray(size);
- gapStart = 0;
- gapEnd = size - 1;
- buffer[size - 1] = '\n';
- positions = new LinkedList();
+ gapStart = 1;
+ gapEnd = size;
+ buffer[0] = '\n';
+ positions = new ArrayList();
}
/**
@@ -211,8 +298,7 @@ public class GapContent
* @param where the position where the string is inserted
* @param str the string that is to be inserted
*
- * @return an UndoableEdit object (currently not supported, so
- * <code>null</code> is returned)
+ * @return an UndoableEdit object
*
* @throws BadLocationException if <code>where</code> is not a valid
* location in the buffer
@@ -228,9 +314,9 @@ public class GapContent
throw new BadLocationException("the where argument cannot be greater"
+ " than the content length", where);
- replace(where, 0, str.toCharArray(), str.length());
+ replace(where, 0, str.toCharArray(), strLen);
- return null;
+ return new UndoInsertString(where, strLen);
}
/**
@@ -239,8 +325,7 @@ public class GapContent
* @param where the position where the content is to be removed
* @param nitems number of characters to be removed
*
- * @return an UndoableEdit object (currently not supported, so
- * <code>null</code> is returned)
+ * @return an UndoableEdit object
*
* @throws BadLocationException if <code>where</code> is not a valid
* location in the buffer
@@ -257,9 +342,10 @@ public class GapContent
throw new BadLocationException("where + nitems cannot be greater"
+ " than the content length", where + nitems);
+ String removedText = getString(where, nitems);
replace(where, nitems, null, 0);
- return null;
+ return new UndoRemove(where, removedText);
}
/**
@@ -372,7 +458,6 @@ public class GapContent
if (index < 0)
index = -(index + 1);
positions.add(index, pos);
-
return pos;
}
@@ -386,7 +471,14 @@ public class GapContent
*/
protected void shiftEnd(int newSize)
{
- int delta = (gapEnd - gapStart) - newSize;
+ assert newSize > (gapEnd - gapStart) : "The new gap size must be greater "
+ + "than the old gap size";
+
+ int delta = newSize - gapEnd + gapStart;
+ // Update the marks after the gapEnd.
+ adjustPositionsInRange(gapEnd, buffer.length - gapEnd, delta);
+
+ // Copy the data around.
char[] newBuf = (char[]) allocateArray(length() + newSize);
System.arraycopy(buffer, 0, newBuf, 0, gapStart);
System.arraycopy(buffer, gapEnd, newBuf, gapStart + newSize, buffer.length
@@ -394,18 +486,6 @@ public class GapContent
gapEnd = gapStart + newSize;
buffer = newBuf;
- // Update the marks after the gapEnd.
- int index = Collections.binarySearch(positions, new GapContentPosition(
- gapEnd));
- if (index < 0)
- {
- index = -(index + 1);
- }
- for (ListIterator i = positions.listIterator(index); i.hasNext();)
- {
- GapContentPosition p = (GapContentPosition) i.next();
- p.mark += delta;
- }
}
/**
@@ -415,32 +495,15 @@ public class GapContent
*/
protected void shiftGap(int newGapStart)
{
- int newGapEnd = newGapStart + (gapEnd - gapStart);
-
- // Update the positions between newGapEnd and (old) gapEnd. The marks
- // must be shifted by (gapEnd - newGapEnd).
- int index1 = Collections.binarySearch(positions,
- new GapContentPosition(gapEnd));
- int index2 = Collections.binarySearch(positions,
- new GapContentPosition(newGapEnd));
- if (index1 > 0 && index2 > 0)
- {
- int i1 = Math.min(index1, index2);
- int i2 = Math.max(index1, index2);
- for (ListIterator i = positions.listIterator(i1); i.hasNext();)
- {
- if (i.nextIndex() > i2)
- break;
-
- GapContentPosition p = (GapContentPosition) i.next();
- p.mark += gapEnd - newGapEnd;
- }
- }
-
if (newGapStart == gapStart)
return;
- else if (newGapStart < gapStart)
+
+ int newGapEnd = newGapStart + gapEnd - gapStart;
+ if (newGapStart < gapStart)
{
+ // Update the positions between newGapStart and (old) gapStart. The marks
+ // must be shifted by (gapEnd - gapStart).
+ adjustPositionsInRange(newGapStart, gapStart - newGapStart, gapEnd - gapStart);
System.arraycopy(buffer, newGapStart, buffer, newGapEnd, gapStart
- newGapStart);
gapStart = newGapStart;
@@ -448,11 +511,54 @@ public class GapContent
}
else
{
+ // Update the positions between newGapEnd and (old) gapEnd. The marks
+ // must be shifted by (gapEnd - gapStart).
+ adjustPositionsInRange(gapEnd, newGapEnd - gapEnd, -(gapEnd - gapStart));
System.arraycopy(buffer, gapEnd, buffer, gapStart, newGapStart
- gapStart);
gapStart = newGapStart;
gapEnd = newGapEnd;
}
+ if (gapStart == 0)
+ resetMarksAtZero();
+ }
+
+ /**
+ * Shifts the gap start downwards. This does not affect the content of the
+ * buffer. This only updates the gap start and all the marks that are between
+ * the old gap start and the new gap start. They all are squeezed to the start
+ * of the gap, because their location has been removed.
+ *
+ * @param newGapStart the new gap start
+ */
+ protected void shiftGapStartDown(int newGapStart)
+ {
+ if (newGapStart == gapStart)
+ return;
+
+ assert newGapStart < gapStart : "The new gap start must be less than the "
+ + "old gap start.";
+ setPositionsInRange(newGapStart, gapStart - newGapStart, gapStart);
+ gapStart = newGapStart;
+ }
+
+ /**
+ * Shifts the gap end upwards. This does not affect the content of the
+ * buffer. This only updates the gap end and all the marks that are between
+ * the old gap end and the new end start. They all are squeezed to the end
+ * of the gap, because their location has been removed.
+ *
+ * @param newGapEnd the new gap start
+ */
+ protected void shiftGapEndUp(int newGapEnd)
+ {
+ if (newGapEnd == gapEnd)
+ return;
+
+ assert newGapEnd > gapEnd : "The new gap end must be greater than the "
+ + "old gap end.";
+ setPositionsInRange(gapEnd, newGapEnd - gapEnd, newGapEnd + 1);
+ gapEnd = newGapEnd;
}
/**
@@ -476,13 +582,15 @@ public class GapContent
protected void replace(int position, int rmSize, Object addItems,
int addSize)
{
+ if (gapStart != position)
+ shiftGap(position);
// Remove content
- shiftGap(position);
- gapEnd += rmSize;
+ if (rmSize > 0)
+ shiftGapEndUp(gapEnd + rmSize);
// If gap is too small, enlarge the gap.
- if ((gapEnd - gapStart) < addSize)
- shiftEnd(addSize);
+ if ((gapEnd - gapStart) <= addSize)
+ shiftEnd((addSize - gapEnd + gapStart + 1) * 2 + gapEnd + DEFAULT_BUFSIZE);
// Add new items to the buffer.
if (addItems != null)
@@ -491,4 +599,164 @@ public class GapContent
gapStart += addSize;
}
}
+
+ /**
+ * Returns the start index of the gap within the buffer array.
+ *
+ * @return the start index of the gap within the buffer array
+ */
+ protected final int getGapStart()
+ {
+ return gapStart;
+ }
+
+ /**
+ * Returns the end index of the gap within the buffer array.
+ *
+ * @return the end index of the gap within the buffer array
+ */
+ protected final int getGapEnd()
+ {
+ return gapEnd;
+ }
+
+ /**
+ * Returns all <code>Position</code>s that are in the range specified by
+ * <code>offset</code> and </code>length</code> within the buffer array.
+ *
+ * @param v the vector to use; if <code>null</code>, a new Vector is allocated
+ * @param offset the start offset of the range to search
+ * @param length the length of the range to search
+ *
+ * @return the positions within the specified range
+ */
+ protected Vector getPositionsInRange(Vector v, int offset, int length)
+ {
+ Vector res = v;
+ if (res == null)
+ res = new Vector();
+ else
+ res.clear();
+
+ int endOffset = offset + length;
+
+ int index1 = Collections.binarySearch(positions,
+ new GapContentPosition(offset));
+ if (index1 < 0)
+ index1 = -(index1 + 1);
+ for (ListIterator i = positions.listIterator(index1); i.hasNext();)
+ {
+ GapContentPosition p = (GapContentPosition) i.next();
+ if (p.mark > endOffset)
+ break;
+ if (p.mark >= offset && p.mark <= endOffset)
+ res.add(p);
+ }
+ return res;
+ }
+
+ /**
+ * Sets the mark of all <code>Position</code>s that are in the range
+ * specified by <code>offset</code> and </code>length</code> within
+ * the buffer array to <code>value</code>
+ *
+ * @param offset the start offset of the range to search
+ * @param length the length of the range to search
+ * @param value the new value for each mark
+ */
+ void setPositionsInRange(int offset, int length, int value)
+ {
+ int endOffset = offset + length;
+
+ int index1 = Collections.binarySearch(positions,
+ new GapContentPosition(offset));
+ if (index1 < 0)
+ index1 = -(index1 + 1);
+ for (ListIterator i = positions.listIterator(index1); i.hasNext();)
+ {
+ GapContentPosition p = (GapContentPosition) i.next();
+ if (p.mark > endOffset)
+ break;
+
+ if (p.mark >= offset && p.mark <= endOffset)
+ p.mark = value;
+ }
+ }
+
+ /**
+ * Adjusts the mark of all <code>Position</code>s that are in the range
+ * specified by <code>offset</code> and </code>length</code> within
+ * the buffer array by <code>increment</code>
+ *
+ * @param offset the start offset of the range to search
+ * @param length the length of the range to search
+ * @param incr the increment
+ */
+ void adjustPositionsInRange(int offset, int length, int incr)
+ {
+ int endOffset = offset + length;
+
+ int index1 = Collections.binarySearch(positions,
+ new GapContentPosition(offset));
+ if (index1 < 0)
+ index1 = -(index1 + 1);
+ for (ListIterator i = positions.listIterator(index1); i.hasNext();)
+ {
+ GapContentPosition p = (GapContentPosition) i.next();
+ if (p.mark > endOffset)
+ break;
+
+ if (p.mark >= offset && p.mark <= endOffset)
+ p.mark += incr;
+ }
+ }
+
+ /**
+ * Resets all <code>Position</code> that have an offset of <code>0</code>,
+ * to also have an array index of <code>0</code>. This might be necessary
+ * after a call to <code>shiftGap(0)</code>, since then the marks at offset
+ * <code>0</code> get shifted to <code>gapEnd</code>.
+ */
+ protected void resetMarksAtZero()
+ {
+ if (gapStart != 0)
+ return;
+
+ setPositionsInRange(gapEnd, 0, 0);
+ }
+
+ /**
+ * Outputs debugging info to System.err. It prints out the buffer array,
+ * the gapStart is marked by a &lt; sign, the gapEnd is marked by a &gt;
+ * sign and each position is marked by a # sign.
+ */
+ private void dump()
+ {
+ System.err.println("GapContent debug information");
+ System.err.println("buffer length: " + buffer.length);
+ System.err.println("gap start: " + gapStart);
+ System.err.println("gap end: " + gapEnd);
+ for (int i = 0; i < buffer.length; i++)
+ {
+ if (i == gapStart)
+ System.err.print('<');
+ if (i == gapEnd)
+ System.err.print('>');
+
+ if (!Character.isISOControl(buffer[i]))
+ System.err.print(buffer[i]);
+ else
+ System.err.print('.');
+ }
+ System.err.println();
+ }
+
+ private void dumpPositions()
+ {
+ for (Iterator i = positions.iterator(); i.hasNext();)
+ {
+ GapContentPosition pos = (GapContentPosition) i.next();
+ System.err.println("position at: " + pos.mark);
+ }
+ }
}
diff --git a/libjava/classpath/javax/swing/text/GlyphView.java b/libjava/classpath/javax/swing/text/GlyphView.java
index f9e60972d84..eb1fadd4f2d 100644
--- a/libjava/classpath/javax/swing/text/GlyphView.java
+++ b/libjava/classpath/javax/swing/text/GlyphView.java
@@ -44,6 +44,12 @@ import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
+import java.awt.Toolkit;
+import java.text.BreakIterator;
+
+import javax.swing.SwingConstants;
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.Position.Bias;
/**
* Renders a run of styled text. This {@link View} subclass paints the
@@ -52,9 +58,7 @@ import java.awt.Shape;
*
* @author Roman Kennke (roman@kennke.org)
*/
-public class GlyphView
- extends View
- implements TabableView, Cloneable
+public class GlyphView extends View implements TabableView, Cloneable
{
/**
@@ -68,15 +72,47 @@ public class GlyphView
*/
public GlyphPainter()
{
+ // Nothing to do here.
}
/**
+ * Returns the ascent of the font that is used by this glyph painter.
+ *
+ * @param v the glyph view
+ *
+ * @return the ascent of the font that is used by this glyph painter
+ */
+ public abstract float getAscent(GlyphView v);
+
+ /**
+ * Returns the descent of the font that is used by this glyph painter.
+ *
+ * @param v the glyph view
+ *
+ * @return the descent of the font that is used by this glyph painter
+ */
+ public abstract float getDescent(GlyphView v);
+
+ /**
* Returns the full height of the rendered text.
*
* @return the full height of the rendered text
*/
public abstract float getHeight(GlyphView view);
-
+
+ /**
+ * Determines the model offset, so that the text between <code>p0</code>
+ * and this offset fits within the span starting at <code>x</code> with
+ * the length of <code>len</code>.
+ *
+ * @param v the glyph view
+ * @param p0 the starting offset in the model
+ * @param x the start location in the view
+ * @param len the length of the span in the view
+ */
+ public abstract int getBoundedPosition(GlyphView v, int p0, float x,
+ float len);
+
/**
* Paints the glyphs.
*
@@ -97,8 +133,8 @@ public class GlyphView
* @param view the glyph view
* @param pos the position of the character in the model
* @param a the area that is occupied by the view
- * @param bias either {@link Position.Bias.Forward} or
- * {@link Position.Bias.Backward} depending on the preferred
+ * @param b either {@link Position.Bias#Forward} or
+ * {@link Position.Bias#Backward} depending on the preferred
* direction bias. If <code>null</code> this defaults to
* <code>Position.Bias.Forward</code>
*
@@ -114,6 +150,20 @@ public class GlyphView
throws BadLocationException;
/**
+ * Maps a visual position into a document location.
+ *
+ * @param v the glyph view
+ * @param x the X coordinate of the visual position
+ * @param y the Y coordinate of the visual position
+ * @param a the allocated region
+ * @param biasRet filled with the bias of the model location on method exit
+ *
+ * @return the model location that represents the specified view location
+ */
+ public abstract int viewToModel(GlyphView v, float x, float y, Shape a,
+ Position.Bias[] biasRet);
+
+ /**
* Determine the span of the glyphs from location <code>p0</code> to
* location <code>p1</code>. If <code>te</code> is not <code>null</code>,
* then TABs are expanded using this <code>TabExpander</code>.
@@ -122,7 +172,7 @@ public class GlyphView
*
* @param view the glyph view
* @param p0 the starting location in the document model
- * @param p0 the end location in the document model
+ * @param p1 the end location in the document model
* @param te the tab expander to use
* @param x the location at which the view is located
*
@@ -132,6 +182,69 @@ public class GlyphView
public abstract float getSpan(GlyphView view, int p0, int p1,
TabExpander te, float x);
+
+ /**
+ * Returns the model location that should be used to place a caret when
+ * moving the caret through the document.
+ *
+ * @param v the glyph view
+ * @param pos the current model location
+ * @param b the bias for <code>p</code>
+ * @param a the allocated region for the glyph view
+ * @param direction the direction from the current position; Must be one of
+ * {@link SwingConstants#EAST}, {@link SwingConstants#WEST},
+ * {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH}
+ * @param biasRet filled with the bias of the resulting location when method
+ * returns
+ *
+ * @return the location within the document that should be used to place the
+ * caret when moving the caret around the document
+ *
+ * @throws BadLocationException if <code>pos</code> is an invalid model
+ * location
+ * @throws IllegalArgumentException if <code>d</code> is invalid
+ */
+ public int getNextVisualPositionFrom(GlyphView v, int pos, Position.Bias b,
+ Shape a, int direction,
+ Position.Bias[] biasRet)
+ throws BadLocationException
+
+ {
+ int result = pos;
+ switch (direction)
+ {
+ case SwingConstants.EAST:
+ result = pos + 1;
+ break;
+ case SwingConstants.WEST:
+ result = pos - 1;
+ break;
+ case SwingConstants.NORTH:
+ case SwingConstants.SOUTH:
+ default:
+ // This should be handled in enclosing view, since the glyph view
+ // does not layout vertically.
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Returns a painter that can be used to render the specified glyph view.
+ * If this glyph painter is stateful, then it should return a new instance.
+ * However, if this painter is stateless it should return itself. The
+ * default behaviour is to return itself.
+ *
+ * @param v the glyph view for which to create a painter
+ * @param p0 the start offset of the rendered area
+ * @param p1 the end offset of the rendered area
+ *
+ * @return a painter that can be used to render the specified glyph view
+ */
+ public GlyphPainter getPainter(GlyphView v, int p0, int p1)
+ {
+ return this;
+ }
}
/**
@@ -147,7 +260,7 @@ public class GlyphView
public float getHeight(GlyphView view)
{
Font font = view.getFont();
- FontMetrics metrics = view.getContainer().getFontMetrics(font);
+ FontMetrics metrics = Toolkit.getDefaultToolkit().getFontMetrics(font);
float height = metrics.getHeight();
return height;
}
@@ -173,11 +286,40 @@ public class GlyphView
if (parent instanceof TabExpander)
tabEx = (TabExpander) parent;
- // FIXME: Set character attributes like font-family, font-size, colors.
- Color foreground = view.getForeground();
- g.setColor(foreground);
- Utilities.drawTabbedText(txt, bounds.x, bounds.y, g, tabEx,
- txt.offset);
+ // Fill the background of the text run.
+ Color background = view.getBackground();
+ g.setColor(background);
+ int width = Utilities.getTabbedTextWidth(txt, g.getFontMetrics(),
+ bounds.x, tabEx, txt.offset);
+ g.fillRect(bounds.x, bounds.y, width, height);
+
+ // Draw the actual text.
+ g.setColor(view.getForeground());
+ g.setFont(view.getFont());
+ if (view.isSuperscript())
+ // TODO: Adjust font for superscripting.
+ Utilities.drawTabbedText(txt, bounds.x, bounds.y - height / 2, g, tabEx,
+ txt.offset);
+ else if (view.isSubscript())
+ // TODO: Adjust font for subscripting.
+ Utilities.drawTabbedText(txt, bounds.x, bounds.y + height / 2, g, tabEx,
+ txt.offset);
+ else
+ Utilities.drawTabbedText(txt, bounds.x, bounds.y, g, tabEx,
+ txt.offset);
+
+ if (view.isStikeThrough())
+ {
+ int strikeHeight = (int) (getAscent(view) / 2);
+ g.drawLine(bounds.x, bounds.y + strikeHeight, bounds.height + width,
+ bounds.y + strikeHeight);
+ }
+ if (view.isUnderline())
+ {
+ int lineHeight = (int) getAscent(view);
+ g.drawLine(bounds.x, bounds.y + lineHeight, bounds.height + width,
+ bounds.y + lineHeight);
+ }
}
/**
@@ -188,8 +330,8 @@ public class GlyphView
* @param view the glyph view
* @param pos the position of the character in the model
* @param a the area that is occupied by the view
- * @param bias either {@link Position.Bias.Forward} or
- * {@link Position.Bias.Backward} depending on the preferred
+ * @param b either {@link Position.Bias#Forward} or
+ * {@link Position.Bias#Backward} depending on the preferred
* direction bias. If <code>null</code> this defaults to
* <code>Position.Bias.Forward</code>
*
@@ -225,7 +367,7 @@ public class GlyphView
*
* @param view the glyph view
* @param p0 the starting location in the document model
- * @param p0 the end location in the document model
+ * @param p1 the end location in the document model
* @param te the tab expander to use
* @param x the location at which the view is located
*
@@ -237,11 +379,90 @@ public class GlyphView
{
Element el = view.getElement();
Font font = view.getFont();
- FontMetrics fm = view.getContainer().getFontMetrics(font);
+ FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
Segment txt = view.getText(p0, p1);
int span = Utilities.getTabbedTextWidth(txt, fm, (int) x, te, p0);
return span;
}
+
+ /**
+ * Returns the ascent of the text run that is rendered by this
+ * <code>GlyphPainter</code>.
+ *
+ * @param v the glyph view
+ *
+ * @return the ascent of the text run that is rendered by this
+ * <code>GlyphPainter</code>
+ *
+ * @see FontMetrics#getAscent()
+ */
+ public float getAscent(GlyphView v)
+ {
+ Font font = v.getFont();
+ FontMetrics fm = v.getContainer().getFontMetrics(font);
+ return fm.getAscent();
+ }
+
+ /**
+ * Returns the descent of the text run that is rendered by this
+ * <code>GlyphPainter</code>.
+ *
+ * @param v the glyph view
+ *
+ * @return the descent of the text run that is rendered by this
+ * <code>GlyphPainter</code>
+ *
+ * @see FontMetrics#getDescent()
+ */
+ public float getDescent(GlyphView v)
+ {
+ Font font = v.getFont();
+ FontMetrics fm = v.getContainer().getFontMetrics(font);
+ return fm.getDescent();
+ }
+
+ /**
+ * Determines the model offset, so that the text between <code>p0</code>
+ * and this offset fits within the span starting at <code>x</code> with
+ * the length of <code>len</code>.
+ *
+ * @param v the glyph view
+ * @param p0 the starting offset in the model
+ * @param x the start location in the view
+ * @param len the length of the span in the view
+ */
+ public int getBoundedPosition(GlyphView v, int p0, float x, float len)
+ {
+ TabExpander te = v.getTabExpander();
+ Segment txt = v.getText(p0, v.getEndOffset());
+ Font font = v.getFont();
+ FontMetrics fm = v.getContainer().getFontMetrics(font);
+ int pos = Utilities.getTabbedTextOffset(txt, fm, (int) x,
+ (int) (x + len), te, p0, false);
+ return pos;
+ }
+
+ /**
+ * Maps a visual position into a document location.
+ *
+ * @param v the glyph view
+ * @param x the X coordinate of the visual position
+ * @param y the Y coordinate of the visual position
+ * @param a the allocated region
+ * @param biasRet filled with the bias of the model location on method exit
+ *
+ * @return the model location that represents the specified view location
+ */
+ public int viewToModel(GlyphView v, float x, float y, Shape a,
+ Bias[] biasRet)
+ {
+ Rectangle b = a.getBounds();
+ assert b.contains(x, y) : "The coordinates are expected to be within the "
+ + "view's bounds: x=" + x + ", y=" + y
+ + "a=" + a;
+ int pos = getBoundedPosition(v, v.getStartOffset(), b.x, x - b.x);
+ return pos;
+ }
}
/**
@@ -250,6 +471,16 @@ public class GlyphView
GlyphPainter glyphPainter;
/**
+ * The start offset within the document for this view.
+ */
+ int startOffset;
+
+ /**
+ * The end offset within the document for this view.
+ */
+ int endOffset;
+
+ /**
* Creates a new <code>GlyphView</code> for the given <code>Element</code>.
*
* @param element the element that is rendered by this GlyphView
@@ -257,6 +488,8 @@ public class GlyphView
public GlyphView(Element element)
{
super(element);
+ startOffset = element.getStartOffset();
+ endOffset = element.getEndOffset();
}
/**
@@ -319,16 +552,21 @@ public class GlyphView
*/
public float getPreferredSpan(int axis)
{
- Element el = getElement();
+ float span = 0;
checkPainter();
GlyphPainter painter = getGlyphPainter();
- TabExpander tabEx = null;
- View parent = getParent();
- if (parent instanceof TabExpander)
- tabEx = (TabExpander) parent;
- // FIXME: Figure out how to determine the x parameter.
- float span = painter.getSpan(this, el.getStartOffset(), el.getEndOffset(),
- tabEx, 0.F);
+ if (axis == X_AXIS)
+ {
+ Element el = getElement();
+ TabExpander tabEx = null;
+ View parent = getParent();
+ if (parent instanceof TabExpander)
+ tabEx = (TabExpander) parent;
+ span = painter.getSpan(this, getStartOffset(), getEndOffset(),
+ tabEx, 0.F);
+ }
+ else
+ span = painter.getHeight(this);
return span;
}
@@ -372,8 +610,9 @@ public class GlyphView
*/
public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
{
- // FIXME: not implemented
- return 0;
+ checkPainter();
+ GlyphPainter painter = getGlyphPainter();
+ return painter.viewToModel(this, x, y, a, b);
}
/**
@@ -383,12 +622,11 @@ public class GlyphView
*/
public TabExpander getTabExpander()
{
- // TODO: Figure out if this is correct.
TabExpander te = null;
View parent = getParent();
- if (parent instanceof ParagraphView)
- te = (ParagraphView) parent;
+ if (parent instanceof TabExpander)
+ te = (TabExpander) parent;
return te;
}
@@ -428,23 +666,26 @@ public class GlyphView
}
catch (BadLocationException ex)
{
- throw new AssertionError("BadLocationException must not be thrown "
- + "here");
+ AssertionError ae;
+ ae = new AssertionError("BadLocationException must not be thrown "
+ + "here");
+ ae.initCause(ex);
+ throw ae;
}
FontMetrics fm = null; // Fetch font metrics somewhere.
return Utilities.getTabbedTextWidth(seg, fm, 0, null, p0);
}
/**
- * Returns the starting offset in the document model of the portion
+ * Returns the start offset in the document model of the portion
* of text that this view is responsible for.
*
- * @return the starting offset in the document model of the portion
+ * @return the start offset in the document model of the portion
* of text that this view is responsible for
*/
- public int getBeginIndex()
+ public int getStartOffset()
{
- return getElement().getStartOffset();
+ return startOffset;
}
/**
@@ -454,9 +695,9 @@ public class GlyphView
* @return the end offset in the document model of the portion
* of text that this view is responsible for
*/
- public int getEndIndex()
+ public int getEndOffset()
{
- return getElement().getEndOffset();
+ return endOffset;
}
/**
@@ -476,8 +717,11 @@ public class GlyphView
}
catch (BadLocationException ex)
{
- throw new AssertionError("BadLocationException should not be "
- + "thrown here. p0 = " + p0 + ", p1 = " + p1);
+ AssertionError ae;
+ ae = new AssertionError("BadLocationException should not be "
+ + "thrown here. p0 = " + p0 + ", p1 = " + p1);
+ ae.initCause(ex);
+ throw ae;
}
return txt;
@@ -518,4 +762,332 @@ public class GlyphView
AttributeSet atts = el.getAttributes();
return StyleConstants.getForeground(atts);
}
+
+ /**
+ * Returns the background color which should be used to paint the text.
+ * This is fetched from the associated element's text attributes using
+ * {@link StyleConstants#getBackground}.
+ *
+ * @return the background color which should be used to paint the text
+ */
+ public Color getBackground()
+ {
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ return StyleConstants.getBackground(atts);
+ }
+
+ /**
+ * Determines whether the text should be rendered strike-through or not. This
+ * is determined using the method
+ * {@link StyleConstants#isStrikeThrough(AttributeSet)} on the element of
+ * this view.
+ *
+ * @return whether the text should be rendered strike-through or not
+ */
+ public boolean isStikeThrough()
+ {
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ return StyleConstants.isStrikeThrough(atts);
+ }
+
+ /**
+ * Determines whether the text should be rendered as subscript or not. This
+ * is determined using the method
+ * {@link StyleConstants#isSubscript(AttributeSet)} on the element of
+ * this view.
+ *
+ * @return whether the text should be rendered as subscript or not
+ */
+ public boolean isSubscript()
+ {
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ return StyleConstants.isSubscript(atts);
+ }
+
+ /**
+ * Determines whether the text should be rendered as superscript or not. This
+ * is determined using the method
+ * {@link StyleConstants#isSuperscript(AttributeSet)} on the element of
+ * this view.
+ *
+ * @return whether the text should be rendered as superscript or not
+ */
+ public boolean isSuperscript()
+ {
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ return StyleConstants.isSuperscript(atts);
+ }
+
+ /**
+ * Determines whether the text should be rendered as underlined or not. This
+ * is determined using the method
+ * {@link StyleConstants#isUnderline(AttributeSet)} on the element of
+ * this view.
+ *
+ * @return whether the text should be rendered as underlined or not
+ */
+ public boolean isUnderline()
+ {
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ return StyleConstants.isUnderline(atts);
+ }
+
+ /**
+ * Creates and returns a shallow clone of this GlyphView. This is used by
+ * the {@link #createFragment} and {@link #breakView} methods.
+ *
+ * @return a shallow clone of this GlyphView
+ */
+ protected final Object clone()
+ {
+ try
+ {
+ return super.clone();
+ }
+ catch (CloneNotSupportedException ex)
+ {
+ AssertionError err = new AssertionError("CloneNotSupportedException "
+ + "must not be thrown here");
+ err.initCause(ex);
+ throw err;
+ }
+ }
+
+ /**
+ * Tries to break the view near the specified view span <code>len</code>.
+ * The glyph view can only be broken in the X direction. For Y direction it
+ * returns itself.
+ *
+ * @param axis the axis for breaking, may be {@link View#X_AXIS} or
+ * {@link View#Y_AXIS}
+ * @param p0 the model location where the fragment should start
+ * @param pos the view position along the axis where the fragment starts
+ * @param len the desired length of the fragment view
+ *
+ * @return the fragment view, or <code>this</code> if breaking was not
+ * possible
+ */
+ public View breakView(int axis, int p0, float pos, float len)
+ {
+ if (axis == Y_AXIS)
+ return this;
+
+ checkPainter();
+ GlyphPainter painter = getGlyphPainter();
+ int breakLocation = painter.getBoundedPosition(this, p0, pos, len);
+ // Try to find a suitable line break.
+ BreakIterator lineBreaker = BreakIterator.getLineInstance();
+ Segment txt = new Segment();
+ try
+ {
+ getDocument().getText(getStartOffset(), getEndOffset(), txt);
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError err = new AssertionError("BadLocationException must not "
+ + "be thrown here.");
+ err.initCause(ex);
+ throw err;
+ }
+ lineBreaker.setText(txt);
+ int goodBreakLocation = lineBreaker.previous();
+ if (goodBreakLocation != BreakIterator.DONE)
+ breakLocation = goodBreakLocation;
+
+ View brokenView = createFragment(p0, breakLocation);
+ return brokenView;
+ }
+
+ /**
+ * Determines how well the specified view location is suitable for inserting
+ * a line break. If <code>axis</code> is <code>View.Y_AXIS</code>, then
+ * this method forwards to the superclass, if <code>axis</code> is
+ * <code>View.X_AXIS</code> then this method returns
+ * {@link View#ExcellentBreakWeight} if there is a suitable break location
+ * (usually whitespace) within the specified view span, or
+ * {@link View#GoodBreakWeight} if not.
+ *
+ * @param axis the axis along which the break weight is requested
+ * @param pos the starting view location
+ * @param len the length of the span at which the view should be broken
+ *
+ * @return the break weight
+ */
+ public int getBreakWeight(int axis, float pos, float len)
+ {
+ int weight;
+ if (axis == Y_AXIS)
+ weight = super.getBreakWeight(axis, pos, len);
+ else
+ {
+ // Determine the model locations at pos and pos + len.
+ int spanX = (int) getPreferredSpan(X_AXIS);
+ int spanY = (int) getPreferredSpan(Y_AXIS);
+ Rectangle dummyAlloc = new Rectangle(0, 0, spanX, spanY);
+ Position.Bias[] biasRet = new Position.Bias[1];
+ int offset1 = viewToModel(pos, spanY / 2, dummyAlloc, biasRet);
+ int offset2 = viewToModel(pos, spanY / 2, dummyAlloc, biasRet);
+ Segment txt = getText(offset1, offset2);
+ BreakIterator lineBreaker = BreakIterator.getLineInstance();
+ lineBreaker.setText(txt);
+ int breakLoc = lineBreaker.previous();
+ if (breakLoc == offset1)
+ weight = View.BadBreakWeight;
+ else if(breakLoc == BreakIterator.DONE)
+ weight = View.GoodBreakWeight;
+ else
+ weight = View.ExcellentBreakWeight;
+ }
+ return weight;
+ }
+
+ /**
+ * Receives notification that some text attributes have changed within the
+ * text fragment that this view is responsible for. This calls
+ * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for
+ * both width and height.
+ *
+ * @param e the document event describing the change; not used here
+ * @param a the view allocation on screen; not used here
+ * @param vf the view factory; not used here
+ */
+ public void changedUpdate(DocumentEvent e, Shape a, ViewFactory vf)
+ {
+ getParent().preferenceChanged(this, true, true);
+ }
+
+ /**
+ * Receives notification that some text has been inserted within the
+ * text fragment that this view is responsible for. This calls
+ * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for
+ * width.
+ *
+ * @param e the document event describing the change; not used here
+ * @param a the view allocation on screen; not used here
+ * @param vf the view factory; not used here
+ */
+ public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf)
+ {
+ getParent().preferenceChanged(this, true, false);
+ }
+
+ /**
+ * Receives notification that some text has been removed within the
+ * text fragment that this view is responsible for. This calls
+ * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for
+ * width.
+ *
+ * @param e the document event describing the change; not used here
+ * @param a the view allocation on screen; not used here
+ * @param vf the view factory; not used here
+ */
+ public void removeUpdate(DocumentEvent e, Shape a, ViewFactory vf)
+ {
+ getParent().preferenceChanged(this, true, false);
+ }
+
+ /**
+ * Creates a fragment view of this view that starts at <code>p0</code> and
+ * ends at <code>p1</code>.
+ *
+ * @param p0 the start location for the fragment view
+ * @param p1 the end location for the fragment view
+ *
+ * @return the fragment view
+ */
+ public View createFragment(int p0, int p1)
+ {
+ GlyphView fragment = (GlyphView) clone();
+ fragment.startOffset = p0;
+ fragment.endOffset = p1;
+ return fragment;
+ }
+
+ /**
+ * Returns the alignment of this view along the specified axis. For the Y
+ * axis this is <code>(height - descent) / height</code> for the used font,
+ * so that it is aligned along the baseline.
+ * For the X axis the superclass is called.
+ */
+ public float getAlignment(int axis)
+ {
+ float align;
+ if (axis == Y_AXIS)
+ {
+ checkPainter();
+ GlyphPainter painter = getGlyphPainter();
+ float height = painter.getHeight(this);
+ float descent = painter.getDescent(this);
+ align = (height - descent) / height;
+ }
+ else
+ align = super.getAlignment(axis);
+
+ return align;
+ }
+
+ /**
+ * Returns the model location that should be used to place a caret when
+ * moving the caret through the document.
+ *
+ * @param pos the current model location
+ * @param bias the bias for <code>p</code>
+ * @param a the allocated region for the glyph view
+ * @param direction the direction from the current position; Must be one of
+ * {@link SwingConstants#EAST}, {@link SwingConstants#WEST},
+ * {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH}
+ * @param biasRet filled with the bias of the resulting location when method
+ * returns
+ *
+ * @return the location within the document that should be used to place the
+ * caret when moving the caret around the document
+ *
+ * @throws BadLocationException if <code>pos</code> is an invalid model
+ * location
+ * @throws IllegalArgumentException if <code>d</code> is invalid
+ */
+ public int getNextVisualPositionFrom(int pos, Position.Bias bias, Shape a,
+ int direction, Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ checkPainter();
+ GlyphPainter painter = getGlyphPainter();
+ return painter.getNextVisualPositionFrom(this, pos, bias, a, direction,
+ biasRet);
+ }
+
+ /**
+ * Returns the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction <code>d</code>.
+ *
+ * @param c the text component
+ * @param pos the document position
+ * @param b the bias for <code>pos</code>
+ * @param d the direction, must be either {@link SwingConstants#NORTH},
+ * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
+ * {@link SwingConstants#EAST}
+ * @param biasRet an array of {@link Position.Bias} that can hold at least
+ * one element, which is filled with the bias of the return position
+ * on method exit
+ *
+ * @return the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction
+ * <code>d</code>
+ *
+ * @throws BadLocationException if <code>pos</code> is not a valid offset in
+ * the document model
+ */
+ public int getNextVisualPositionFrom(JTextComponent c, int pos,
+ Position.Bias b, int d,
+ Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ // TODO: Implement this properly.
+ throw new AssertionError("Not implemented yet.");
+ }
}
diff --git a/libjava/classpath/javax/swing/text/IconView.java b/libjava/classpath/javax/swing/text/IconView.java
index c7e22b6c3eb..6dd0f7ad34a 100644
--- a/libjava/classpath/javax/swing/text/IconView.java
+++ b/libjava/classpath/javax/swing/text/IconView.java
@@ -41,6 +41,8 @@ package javax.swing.text;
import java.awt.Graphics;
import java.awt.Shape;
+import javax.swing.SwingConstants;
+
// TODO: Implement this class.
public class IconView
extends View
@@ -125,4 +127,34 @@ public class IconView
// FIXME: not implemented
return 0;
}
+
+ /**
+ * Returns the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction <code>d</code>.
+ *
+ * @param c the text component
+ * @param pos the document position
+ * @param b the bias for <code>pos</code>
+ * @param d the direction, must be either {@link SwingConstants#NORTH},
+ * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
+ * {@link SwingConstants#EAST}
+ * @param biasRet an array of {@link Position.Bias} that can hold at least
+ * one element, which is filled with the bias of the return position
+ * on method exit
+ *
+ * @return the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction
+ * <code>d</code>
+ *
+ * @throws BadLocationException if <code>pos</code> is not a valid offset in
+ * the document model
+ */
+ public int getNextVisualPositionFrom(JTextComponent c, int pos,
+ Position.Bias b, int d,
+ Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ // TODO: Implement this properly.
+ throw new AssertionError("Not implemented yet.");
+ }
}
diff --git a/libjava/classpath/javax/swing/text/InternationalFormatter.java b/libjava/classpath/javax/swing/text/InternationalFormatter.java
index cedaf59feeb..86300a70d8e 100644
--- a/libjava/classpath/javax/swing/text/InternationalFormatter.java
+++ b/libjava/classpath/javax/swing/text/InternationalFormatter.java
@@ -57,9 +57,8 @@ import javax.swing.JFormattedTextField;
public class InternationalFormatter
extends DefaultFormatter
{
-
- /** The serialVersoinUID. */
- private static final long serialVersionUID = 6941977820906408656L;
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 2436068675711756856L;
/** The format that handles value to string conversion. */
Format format;
diff --git a/libjava/classpath/javax/swing/text/JTextComponent.java b/libjava/classpath/javax/swing/text/JTextComponent.java
index b3fad79124c..83966bbdf47 100644
--- a/libjava/classpath/javax/swing/text/JTextComponent.java
+++ b/libjava/classpath/javax/swing/text/JTextComponent.java
@@ -50,9 +50,9 @@ import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
import java.awt.event.InputMethodListener;
import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
@@ -72,7 +72,6 @@ import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
-import javax.swing.Timer;
import javax.swing.TransferHandler;
import javax.swing.UIManager;
import javax.swing.event.CaretEvent;
@@ -89,6 +88,7 @@ public abstract class JTextComponent extends JComponent
/**
* AccessibleJTextComponent
*/
+ // FIXME: This inner class is a complete stub and needs to be implemented.
public class AccessibleJTextComponent extends AccessibleJComponent
implements AccessibleText, CaretListener, DocumentListener
{
@@ -99,6 +99,7 @@ public abstract class JTextComponent extends JComponent
*/
public AccessibleJTextComponent()
{
+ // Nothing to do here.
}
/**
@@ -301,50 +302,6 @@ public abstract class JTextComponent extends JComponent
}
/**
- * The timer that lets the caret blink.
- */
- private class CaretBlinkTimer
- extends Timer
- implements ActionListener
- {
- /**
- * Creates a new CaretBlinkTimer object with a default delay of 1 second.
- */
- public CaretBlinkTimer()
- {
- super(1000, null);
- addActionListener(this);
- }
-
- /**
- * Lets the caret blink.
- */
- public void actionPerformed(ActionEvent ev)
- {
- Caret c = caret;
- if (c != null)
- c.setVisible(!c.isVisible());
- }
-
- /**
- * Updates the blink delay according to the current caret.
- */
- public void update()
- {
- stop();
- Caret c = caret;
- if (c != null)
- {
- setDelay(c.getBlinkRate());
- if (editable)
- start();
- else
- c.setVisible(false);
- }
- }
- }
-
- /**
* According to <a
* href="http://java.sun.com/products/jfc/tsc/special_report/kestrel/keybindings.html">this
* report</a>, a pair of private classes wraps a {@link
@@ -604,8 +561,7 @@ public abstract class JTextComponent extends JComponent
}
}
- class DefaultTransferHandler
- extends TransferHandler
+ class DefaultTransferHandler extends TransferHandler
{
public boolean canImport(JComponent component, DataFlavor[] flavors)
{
@@ -631,23 +587,23 @@ public abstract class JTextComponent extends JComponent
int end = textComponent.getSelectionEnd();
if (start == end)
- return;
+ return;
try
- {
- // Copy text to clipboard.
- String data = textComponent.getDocument().getText(start, end);
- StringSelection selection = new StringSelection(data);
- clipboard.setContents(selection, null);
-
- // Delete selected text on cut action.
- if (action == MOVE)
- doc.remove(start, end - start);
- }
+ {
+ // Copy text to clipboard.
+ String data = textComponent.getDocument().getText(start, end);
+ StringSelection selection = new StringSelection(data);
+ clipboard.setContents(selection, null);
+
+ // Delete selected text on cut action.
+ if (action == MOVE)
+ doc.remove(start, end - start);
+ }
catch (BadLocationException e)
- {
- // Ignore this and do nothing.
- }
+ {
+ // Ignore this and do nothing.
+ }
}
public int getSourceActions()
@@ -661,30 +617,30 @@ public abstract class JTextComponent extends JComponent
DataFlavor[] flavors = transferable.getTransferDataFlavors();
if (flavors == null)
- return false;
+ return false;
for (int i = 0; i < flavors.length; ++i)
- if (flavors[i].equals(DataFlavor.stringFlavor))
- flavor = flavors[i];
+ if (flavors[i].equals(DataFlavor.stringFlavor))
+ flavor = flavors[i];
if (flavor == null)
- return false;
+ return false;
try
- {
- JTextComponent textComponent = (JTextComponent) component;
- String data = (String) transferable.getTransferData(flavor);
- textComponent.replaceSelection(data);
- return true;
- }
+ {
+ JTextComponent textComponent = (JTextComponent) component;
+ String data = (String) transferable.getTransferData(flavor);
+ textComponent.replaceSelection(data);
+ return true;
+ }
catch (IOException e)
- {
- // Ignored.
- }
+ {
+ // Ignored.
+ }
catch (UnsupportedFlavorException e)
- {
- // Ignored.
- }
+ {
+ // Ignored.
+ }
return false;
}
@@ -701,8 +657,6 @@ public abstract class JTextComponent extends JComponent
private char focusAccelerator = '\0';
private NavigationFilter navigationFilter;
- private CaretBlinkTimer caretBlinkTimer;
-
/**
* Get a Keymap from the global keymap table, by name.
*
@@ -960,8 +914,6 @@ public abstract class JTextComponent extends JComponent
creatingKeymap = true;
}
- caretBlinkTimer = new CaretBlinkTimer();
-
setFocusable(true);
setEditable(true);
enableEvents(AWTEvent.KEY_EVENT_MASK);
@@ -1021,12 +973,17 @@ public abstract class JTextComponent extends JComponent
{
try
{
- doc.remove(0, doc.getLength());
- doc.insertString(0, text, null);
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).replace(0, doc.getLength(), text, null);
+ else
+ {
+ doc.remove(0, doc.getLength());
+ doc.insertString(0, text, null);
+ }
}
catch (BadLocationException e)
{
- // This can never happen.
+ // This can never happen.
}
}
@@ -1044,12 +1001,12 @@ public abstract class JTextComponent extends JComponent
try
{
- return doc.getText(0, doc.getLength());
+ return doc.getText(0, doc.getLength());
}
catch (BadLocationException e)
{
- // This should never happen.
- return "";
+ // This should never happen.
+ return "";
}
}
@@ -1080,12 +1037,12 @@ public abstract class JTextComponent extends JComponent
{
try
{
- return doc.getText(getSelectionStart(), getSelectionEnd());
+ return doc.getText(getSelectionStart(), getSelectionEnd());
}
catch (BadLocationException e)
{
- // This should never happen.
- return null;
+ // This should never happen.
+ return null;
}
}
@@ -1105,7 +1062,8 @@ public abstract class JTextComponent extends JComponent
*/
protected String paramString()
{
- return "JTextComponent";
+ // TODO: Do something useful here.
+ return super.paramString();
}
/**
@@ -1194,14 +1152,6 @@ public abstract class JTextComponent extends JComponent
if (editable == newValue)
return;
- if (newValue == true)
- caretBlinkTimer.start();
- else
- {
- caretBlinkTimer.stop();
- caret.setVisible(false);
- }
-
boolean oldValue = editable;
editable = newValue;
firePropertyChange("editable", oldValue, newValue);
@@ -1230,8 +1180,6 @@ public abstract class JTextComponent extends JComponent
Caret oldCaret = caret;
caret = newCaret;
- caretBlinkTimer.update();
-
if (caret != null)
caret.install(this);
@@ -1399,7 +1347,7 @@ public abstract class JTextComponent extends JComponent
start = Math.max(start, 0);
start = Math.min(start, length);
- end = Math.max(end, 0);
+ end = Math.max(end, start);
end = Math.min(end, length);
setCaretPosition(start);
@@ -1422,28 +1370,28 @@ public abstract class JTextComponent extends JComponent
// If content is empty delete selection.
if (content == null)
{
- caret.setDot(dot);
- return;
+ caret.setDot(dot);
+ return;
}
try
{
- int start = getSelectionStart();
- int end = getSelectionEnd();
-
- // Remove selected text.
- if (dot != mark)
- doc.remove(start, end - start);
-
- // Insert new text.
- doc.insertString(start, content, null);
-
- // Set dot to new position.
- setCaretPosition(start + content.length());
+ int start = getSelectionStart();
+ int end = getSelectionEnd();
+
+ // Remove selected text.
+ if (dot != mark)
+ doc.remove(start, end - start);
+
+ // Insert new text.
+ doc.insertString(start, content, null);
+
+ // Set dot to new position.
+ setCaretPosition(start + content.length());
}
catch (BadLocationException e)
{
- // This should never happen.
+ // This should never happen.
}
}
@@ -1577,15 +1525,15 @@ public abstract class JTextComponent extends JComponent
// Install default TransferHandler if none set.
if (getTransferHandler() == null)
{
- if (defaultTransferHandler == null)
- defaultTransferHandler = new DefaultTransferHandler();
-
- setTransferHandler(defaultTransferHandler);
+ if (defaultTransferHandler == null)
+ defaultTransferHandler = new DefaultTransferHandler();
+
+ setTransferHandler(defaultTransferHandler);
}
// Perform action.
ActionEvent event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
- action.getValue(Action.NAME).toString());
+ action.getValue(Action.NAME).toString());
action.actionPerformed(event);
}
@@ -1669,5 +1617,20 @@ public abstract class JTextComponent extends JComponent
throws IOException
{
output.write(getText());
- }
+ }
+
+ /**
+ * Returns the tooltip text for this text component for the given mouse
+ * event. This forwards the call to
+ * {@link TextUI#getToolTipText(JTextComponent, Point)}.
+ *
+ * @param ev the mouse event
+ *
+ * @return the tooltip text for this text component for the given mouse
+ * event
+ */
+ public String getToolTipText(MouseEvent ev)
+ {
+ return getUI().getToolTipText(this, ev.getPoint());
+ }
}
diff --git a/libjava/classpath/javax/swing/text/LabelView.java b/libjava/classpath/javax/swing/text/LabelView.java
index a10391613cd..4890735b925 100644
--- a/libjava/classpath/javax/swing/text/LabelView.java
+++ b/libjava/classpath/javax/swing/text/LabelView.java
@@ -38,10 +38,57 @@ exception statement from your version. */
package javax.swing.text;
-// TODO: Implement this class.
-public class LabelView
- extends GlyphView
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Shape;
+
+import javax.swing.event.DocumentEvent;
+
+/**
+ * A {@link GlyphView} that caches the textattributes for most effective
+ * rendering.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class LabelView extends GlyphView
{
+
+ /**
+ * The background color.
+ */
+ Color background;
+
+ /**
+ * The foreground color.
+ */
+ Color foreground;
+
+ /**
+ * The background color.
+ */
+ Font font;
+
+ /**
+ * The strikethrough flag.
+ */
+ boolean strikeThrough;
+
+ /**
+ * The underline flag.
+ */
+ boolean underline;
+
+ /**
+ * The subscript flag.
+ */
+ boolean subscript;
+
+ /**
+ * The superscript flag.
+ */
+ boolean superscript;
+
/**
* Creates a new <code>GlyphView</code> for the given <code>Element</code>.
*
@@ -50,5 +97,194 @@ public class LabelView
public LabelView(Element element)
{
super(element);
+ setPropertiesFromAttributes();
+ }
+
+ /**
+ * Loads the properties of this label view from the element's text
+ * attributes. This method is called from the constructor and the
+ * {@link #changedUpdate} method
+ */
+ protected void setPropertiesFromAttributes()
+ {
+ Element el = getElement();
+ AttributeSet atts = el.getAttributes();
+ background = StyleConstants.getBackground(atts);
+ foreground = StyleConstants.getForeground(atts);
+ strikeThrough = StyleConstants.isStrikeThrough(atts);
+ subscript = StyleConstants.isSubscript(atts);
+ superscript = StyleConstants.isSuperscript(atts);
+ underline = StyleConstants.isUnderline(atts);
+
+ // Determine the font.
+ String family = StyleConstants.getFontFamily(atts);
+ int size = StyleConstants.getFontSize(atts);
+ int style = Font.PLAIN;
+ if (StyleConstants.isBold(atts))
+ style |= Font.BOLD;
+ if (StyleConstants.isItalic(atts))
+ style |= Font.ITALIC;
+ font = new Font(family, style, size);
+ }
+
+ /**
+ * Receives notification when text attributes change in the chunk of
+ * text that this view is responsible for. This simply calls
+ * {@link #setPropertiesFromAttributes()}.
+ *
+ * @param e the document event
+ * @param a the allocation of this view
+ * @param vf the view factory to use for creating new views
+ */
+ public void changedUpdate(DocumentEvent e, Shape a, ViewFactory vf)
+ {
+ setPropertiesFromAttributes();
+ }
+
+ /**
+ * Returns the background color for the glyphs.
+ *
+ * @return the background color for the glyphs
+ */
+ public Color getBackground()
+ {
+ return background;
+ }
+
+ /**
+ * Sets the background color for the glyphs. A value of <code>null</code>
+ * means the background of the parent view should shine through.
+ *
+ * @param bg the background to set or <code>null</code>
+ *
+ * @since 1.5
+ */
+ protected void setBackground(Color bg)
+ {
+ background = bg;
+ }
+
+ /**
+ * Returns the foreground color for the glyphs.
+ *
+ * @return the foreground color for the glyphs
+ */
+ public Color getForeground()
+ {
+ return foreground;
+ }
+
+ /**
+ * Returns the font for the glyphs.
+ *
+ * @return the font for the glyphs
+ */
+ public Font getFont()
+ {
+ return font;
+ }
+
+ /**
+ * Returns the font metrics of the current font.
+ *
+ * @return the font metrics of the current font
+ *
+ * @deprecated this is not used anymore
+ */
+ protected FontMetrics getFontMetrics()
+ {
+ return getContainer().getGraphics().getFontMetrics(font);
+ }
+
+ /**
+ * Returns <code>true</code> if the glyphs are rendered underlined,
+ * <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if the glyphs are rendered underlined,
+ * <code>false</code> otherwise
+ */
+ public boolean isUnderline()
+ {
+ return underline;
+ }
+
+ /**
+ * Sets the underline flag.
+ *
+ * @param flag <code>true</code> if the glyphs are rendered underlined,
+ * <code>false</code> otherwise
+ */
+ protected void setUnderline(boolean flag)
+ {
+ underline = flag;
+ }
+
+ /**
+ * Returns <code>true</code> if the glyphs are rendered as subscript,
+ * <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if the glyphs are rendered as subscript,
+ * <code>false</code> otherwise
+ */
+ public boolean isSubscript()
+ {
+ return subscript;
+ }
+
+ /**
+ * Sets the subscript flag.
+ *
+ * @param flag <code>true</code> if the glyphs are rendered as subscript,
+ * <code>false</code> otherwise
+ */
+ protected void setSubscript(boolean flag)
+ {
+ subscript = flag;
+ }
+
+ /**
+ * Returns <code>true</code> if the glyphs are rendered as superscript,
+ * <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if the glyphs are rendered as superscript,
+ * <code>false</code> otherwise
+ */
+ public boolean isSuperscript()
+ {
+ return superscript;
+ }
+
+ /**
+ * Sets the superscript flag.
+ *
+ * @param flag <code>true</code> if the glyphs are rendered as superscript,
+ * <code>false</code> otherwise
+ */
+ protected void setSuperscript(boolean flag)
+ {
+ superscript = flag;
+ }
+
+ /**
+ * Returns <code>true</code> if the glyphs are rendered strike-through,
+ * <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if the glyphs are rendered strike-through,
+ * <code>false</code> otherwise
+ */
+ public boolean isStrikeThrough()
+ {
+ return strikeThrough;
+ }
+
+ /**
+ * Sets the strike-through flag.
+ *
+ * @param flag <code>true</code> if the glyphs are rendered strike-through,
+ * <code>false</code> otherwise
+ */
+ protected void setStrikeThrough(boolean flag)
+ {
+ strikeThrough = flag;
}
}
diff --git a/libjava/classpath/javax/swing/text/LayoutQueue.java b/libjava/classpath/javax/swing/text/LayoutQueue.java
index 83433b6eef5..b0c84b972b2 100644
--- a/libjava/classpath/javax/swing/text/LayoutQueue.java
+++ b/libjava/classpath/javax/swing/text/LayoutQueue.java
@@ -57,6 +57,7 @@ public class LayoutQueue
*/
public LayoutQueue()
{
+ // Nothing to do here.
}
/**
diff --git a/libjava/classpath/javax/swing/text/ParagraphView.java b/libjava/classpath/javax/swing/text/ParagraphView.java
index 6c6006a2a0f..6fb121f949e 100644
--- a/libjava/classpath/javax/swing/text/ParagraphView.java
+++ b/libjava/classpath/javax/swing/text/ParagraphView.java
@@ -59,6 +59,11 @@ public class ParagraphView extends FlowView implements TabExpander
{
super(el, X_AXIS);
}
+ public float getAlignment(int axis)
+ {
+ // FIXME: This is very likely not 100% correct. Work this out.
+ return 0.0F;
+ }
}
/**
@@ -86,4 +91,29 @@ public class ParagraphView extends FlowView implements TabExpander
{
return new Row(getElement());
}
+
+ /**
+ * Returns the alignment for this paragraph view for the specified axis.
+ * For the X_AXIS the paragraph view will be aligned at it's left edge
+ * (0.0F). For the Y_AXIS the paragraph view will be aligned at the
+ * center of it's first row.
+ *
+ * @param axis the axis which is examined
+ *
+ * @return the alignment for this paragraph view for the specified axis
+ */
+ public float getAlignment(int axis)
+ {
+ if (axis == X_AXIS)
+ return 0.0F;
+ else if (getViewCount() > 0)
+ {
+
+ float prefHeight = getPreferredSpan(Y_AXIS);
+ float firstRowHeight = getView(0).getPreferredSpan(Y_AXIS);
+ return (firstRowHeight / 2.F) / prefHeight;
+ }
+ else
+ return 0.0F;
+ }
}
diff --git a/libjava/classpath/javax/swing/text/PlainDocument.java b/libjava/classpath/javax/swing/text/PlainDocument.java
index 71070e92da7..9e600c4c908 100644
--- a/libjava/classpath/javax/swing/text/PlainDocument.java
+++ b/libjava/classpath/javax/swing/text/PlainDocument.java
@@ -132,8 +132,8 @@ public class PlainDocument extends AbstractDocument
// collapse elements if the removal spans more than 1 line
Element newEl = createLeafElement(rootElement,
SimpleAttributeSet.EMPTY,
- start, end - len);
- rootElement.replace(i1, i2 - i1, new Element[]{ newEl });
+ start, end);
+ rootElement.replace(i1, i2 - i1 + 1, new Element[]{ newEl });
}
}
@@ -147,4 +147,28 @@ public class PlainDocument extends AbstractDocument
Element root = getDefaultRootElement();
return root.getElement(root.getElementIndex(pos));
}
+
+ /**
+ * Inserts a string into the document. If the document property
+ * '<code>filterNewLines</code>' is set to <code>Boolean.TRUE</code>, then
+ * all newlines in the inserted string are replaced by space characters,
+ * otherwise the superclasses behaviour is executed.
+ *
+ * Inserting content causes a write lock to be acquired during this method
+ * call.
+ *
+ * @param offs the offset at which to insert the string
+ * @param str the string to be inserted
+ * @param atts the text attributes of the string to be inserted
+ *
+ * @throws BadLocationException
+ */
+ public void insertString(int offs, String str, AttributeSet atts)
+ throws BadLocationException
+ {
+ String string = str;
+ if (Boolean.TRUE.equals(getProperty("filterNewlines")))
+ string = str.replaceAll("\n", " ");
+ super.insertString(offs, string, atts);
+ }
}
diff --git a/libjava/classpath/javax/swing/text/PlainView.java b/libjava/classpath/javax/swing/text/PlainView.java
index 91d7547e77c..9f5ee8ad3c8 100644
--- a/libjava/classpath/javax/swing/text/PlainView.java
+++ b/libjava/classpath/javax/swing/text/PlainView.java
@@ -46,15 +46,35 @@ import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
-public class PlainView extends View
- implements TabExpander
+import javax.swing.SwingConstants;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentEvent.ElementChange;
+
+public class PlainView extends View implements TabExpander
{
Color selectedColor;
Color unselectedColor;
+
+ /**
+ * The color that is used to draw disabled text fields.
+ */
+ Color disabledColor;
+
Font font;
+ /** The length of the longest line in the Document **/
+ float maxLineLength = -1;
+
+ /** The longest line in the Document **/
+ Element longestLine = null;
+
protected FontMetrics metrics;
+ /**
+ * The instance returned by {@link #getLineBuffer()}.
+ */
+ private transient Segment lineBuffer;
+
public PlainView(Element elem)
{
super(elem);
@@ -104,7 +124,7 @@ public class PlainView extends View
// Get the rectangle for position.
Element line = getElement().getElement(lineIndex);
int lineStart = line.getStartOffset();
- Segment segment = new Segment();
+ Segment segment = getLineBuffer();
document.getText(lineStart, position - lineStart, segment);
int xoffset = Utilities.getTabbedTextWidth(segment, metrics, rect.x,
this, lineStart);
@@ -129,7 +149,9 @@ public class PlainView extends View
}
catch (BadLocationException e)
{
- // This should never happen.
+ AssertionError ae = new AssertionError("Unexpected bad location");
+ ae.initCause(e);
+ throw ae;
}
}
@@ -137,7 +159,7 @@ public class PlainView extends View
throws BadLocationException
{
g.setColor(selectedColor);
- Segment segment = new Segment();
+ Segment segment = getLineBuffer();
getDocument().getText(p0, p1 - p0, segment);
return Utilities.drawTabbedText(segment, x, y, g, this, 0);
}
@@ -145,8 +167,13 @@ public class PlainView extends View
protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1)
throws BadLocationException
{
- g.setColor(unselectedColor);
- Segment segment = new Segment();
+ JTextComponent textComponent = (JTextComponent) getContainer();
+ if (textComponent.isEnabled())
+ g.setColor(unselectedColor);
+ else
+ g.setColor(disabledColor);
+
+ Segment segment = getLineBuffer();
getDocument().getText(p0, p1 - p0, segment);
return Utilities.drawTabbedText(segment, x, y, g, this, segment.offset);
}
@@ -161,7 +188,8 @@ public class PlainView extends View
g.setFont(textComponent.getFont());
selectedColor = textComponent.getSelectedTextColor();
unselectedColor = textComponent.getForeground();
-
+ disabledColor = textComponent.getDisabledTextColor();
+
Rectangle rect = s.getBounds();
// FIXME: Text may be scrolled.
@@ -176,9 +204,19 @@ public class PlainView extends View
}
}
+ /**
+ * Returns the tab size of a tab. Checks the Document's
+ * properties for PlainDocument.tabSizeAttribute and returns it if it is
+ * defined, otherwise returns 8.
+ *
+ * @return the tab size.
+ */
protected int getTabSize()
{
- return 8;
+ Object tabSize = getDocument().getProperty(PlainDocument.tabSizeAttribute);
+ if (tabSize == null)
+ return 8;
+ return ((Integer)tabSize).intValue();
}
/**
@@ -191,10 +229,54 @@ public class PlainView extends View
*/
public float nextTabStop(float x, int tabStop)
{
- float tabSizePixels = getTabSize() + metrics.charWidth('m');
+ float tabSizePixels = getTabSize() * metrics.charWidth('m');
return (float) (Math.floor(x / tabSizePixels) + 1) * tabSizePixels;
}
+ /**
+ * Returns the length of the longest line, used for getting the span
+ * @return the length of the longest line
+ */
+ float determineMaxLineLength()
+ {
+ // if the longest line is cached, return the cached value
+ if (maxLineLength != -1)
+ return maxLineLength;
+
+ // otherwise we have to go through all the lines and find it
+ Element el = getElement();
+ Segment seg = getLineBuffer();
+ float span = 0;
+ for (int i = 0; i < el.getElementCount(); i++)
+ {
+ Element child = el.getElement(i);
+ int start = child.getStartOffset();
+ int end = child.getEndOffset();
+ try
+ {
+ el.getDocument().getText(start, end - start, seg);
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError ae = new AssertionError("Unexpected bad location");
+ ae.initCause(ex);
+ throw ae;
+ }
+
+ if (seg == null || seg.array == null || seg.count == 0)
+ continue;
+
+ int width = metrics.charsWidth(seg.array, seg.offset, seg.count);
+ if (width > span)
+ {
+ longestLine = child;
+ span = width;
+ }
+ }
+ maxLineLength = span;
+ return maxLineLength;
+ }
+
public float getPreferredSpan(int axis)
{
if (axis != X_AXIS && axis != Y_AXIS)
@@ -205,36 +287,16 @@ public class PlainView extends View
float span = 0;
Element el = getElement();
- Document doc = el.getDocument();
- Segment seg = new Segment();
switch (axis)
{
case X_AXIS:
- // calculate the maximum of the line's widths
- for (int i = 0; i < el.getElementCount(); i++)
- {
- Element child = el.getElement(i);
- int start = child.getStartOffset();
- int end = child.getEndOffset();
- try {
- doc.getText(start, start + end, seg);
- }
- catch (BadLocationException ex)
- {
- // throw new ClasspathAssertionError
- // ("no BadLocationException should be thrown here");
- }
- int width = metrics.charsWidth(seg.array, seg.offset, seg.count);
- span = Math.max(span, width);
- }
- break;
+ span = determineMaxLineLength();
case Y_AXIS:
default:
span = metrics.getHeight() * el.getElementCount();
break;
}
-
return span;
}
@@ -252,8 +314,251 @@ public class PlainView extends View
*/
public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
{
- // FIXME: not implemented
- return 0;
+ Rectangle rec = a.getBounds();
+ Document doc = getDocument();
+ Element root = doc.getDefaultRootElement();
+
+ // PlainView doesn't support line-wrapping so we can find out which
+ // Element was clicked on just by the y-position
+ int lineClicked = (int) (y - rec.y) / metrics.getHeight();
+ if (lineClicked >= root.getElementCount())
+ return getEndOffset() - 1;
+
+ Element line = root.getElement(lineClicked);
+ Segment s = getLineBuffer();
+ int start = line.getStartOffset();
+ // We don't want the \n at the end of the line.
+ int end = line.getEndOffset() - 1;
+ try
+ {
+ doc.getText(start, end - start, s);
+ }
+ catch (BadLocationException ble)
+ {
+ AssertionError ae = new AssertionError("Unexpected bad location");
+ ae.initCause(ble);
+ throw ae;
+ }
+
+ int pos = Utilities.getTabbedTextOffset(s, metrics, rec.x, (int)x, this, start);
+ return Math.max (0, pos);
+ }
+
+ /**
+ * Since insertUpdate and removeUpdate each deal with children
+ * Elements being both added and removed, they both have to perform
+ * the same checks. So they both simply call this method.
+ * @param changes the DocumentEvent for the changes to the Document.
+ * @param a the allocation of the View.
+ * @param f the ViewFactory to use for rebuilding.
+ */
+ protected void updateDamage(DocumentEvent changes, Shape a, ViewFactory f)
+ {
+ Element el = getElement();
+ ElementChange ec = changes.getChange(el);
+
+ // If ec is null then no lines were added or removed, just
+ // repaint the changed line
+ if (ec == null)
+ {
+ int line = getElement().getElementIndex(changes.getOffset());
+ damageLineRange(line, line, a, getContainer());
+ return;
+ }
+
+ Element[] removed = ec.getChildrenRemoved();
+ Element[] newElements = ec.getChildrenAdded();
+
+ // If no Elements were added or removed, we just want to repaint
+ // the area containing the line that was modified
+ if (removed == null && newElements == null)
+ {
+ int line = getElement().getElementIndex(changes.getOffset());
+ damageLineRange(line, line, a, getContainer());
+ return;
+ }
+
+ // Check to see if we removed the longest line, if so we have to
+ // search through all lines and find the longest one again
+ if (removed != null)
+ {
+ for (int i = 0; i < removed.length; i++)
+ if (removed[i].equals(longestLine))
+ {
+ // reset maxLineLength and search through all lines for longest one
+ maxLineLength = -1;
+ determineMaxLineLength();
+ ((JTextComponent)getContainer()).repaint();
+ return;
+ }
+ }
+
+ // If we've reached here, that means we haven't removed the longest line
+ if (newElements == null)
+ {
+ // No lines were added, just repaint the container and exit
+ ((JTextComponent)getContainer()).repaint();
+ return;
+ }
+
+ // Make sure we have the metrics
+ updateMetrics();
+
+ // If we've reached here, that means we haven't removed the longest line
+ // and we have added at least one line, so we have to check if added lines
+ // are longer than the previous longest line
+ Segment seg = getLineBuffer();
+ float longestNewLength = 0;
+ Element longestNewLine = null;
+
+ // Loop through the added lines to check their length
+ for (int i = 0; i < newElements.length; i++)
+ {
+ Element child = newElements[i];
+ int start = child.getStartOffset();
+ int end = child.getEndOffset();
+ try
+ {
+ el.getDocument().getText(start, end - start, seg);
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError ae = new AssertionError("Unexpected bad location");
+ ae.initCause(ex);
+ throw ae;
+ }
+
+ if (seg == null || seg.array == null || seg.count == 0)
+ continue;
+
+ int width = metrics.charsWidth(seg.array, seg.offset, seg.count);
+ if (width > longestNewLength)
+ {
+ longestNewLine = child;
+ longestNewLength = width;
+ }
+ }
+
+ // Check if the longest of the new lines is longer than our previous
+ // longest line, and if so update our values
+ if (longestNewLength > maxLineLength)
+ {
+ maxLineLength = longestNewLength;
+ longestLine = longestNewLine;
+ }
+ // Repaint the container
+ ((JTextComponent)getContainer()).repaint();
+ }
+
+ /**
+ * This method is called when something is inserted into the Document
+ * that this View is displaying.
+ *
+ * @param changes the DocumentEvent for the changes.
+ * @param a the allocation of the View
+ * @param f the ViewFactory used to rebuild
+ */
+ public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f)
+ {
+ updateDamage(changes, a, f);
+ }
+
+ /**
+ * This method is called when something is removed from the Document
+ * that this View is displaying.
+ *
+ * @param changes the DocumentEvent for the changes.
+ * @param a the allocation of the View
+ * @param f the ViewFactory used to rebuild
+ */
+ public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f)
+ {
+ updateDamage(changes, a, f);
+ }
+
+ /**
+ * This method is called when attributes were changed in the
+ * Document in a location that this view is responsible for.
+ */
+ public void changedUpdate (DocumentEvent changes, Shape a, ViewFactory f)
+ {
+ updateDamage(changes, a, f);
+ }
+
+ /**
+ * Repaint the given line range. This is called from insertUpdate,
+ * changedUpdate, and removeUpdate when no new lines were added
+ * and no lines were removed, to repaint the line that was
+ * modified.
+ *
+ * @param line0 the start of the range
+ * @param line1 the end of the range
+ * @param a the rendering region of the host
+ * @param host the Component that uses this View (used to call repaint
+ * on that Component)
+ *
+ * @since 1.4
+ */
+ protected void damageLineRange (int line0, int line1, Shape a, Component host)
+ {
+ if (a == null)
+ return;
+
+ Rectangle rec0 = lineToRect(a, line0);
+ Rectangle rec1 = lineToRect(a, line1);
+
+ if (rec0 == null || rec1 == null)
+ // something went wrong, repaint the entire host to be safe
+ host.repaint();
+ else
+ {
+ Rectangle repaintRec = rec0.union(rec1);
+ host.repaint();
+ }
+ }
+
+ /**
+ * Provides a {@link Segment} object, that can be used to fetch text from
+ * the document.
+ *
+ * @returna {@link Segment} object, that can be used to fetch text from
+ * the document
+ */
+ protected Segment getLineBuffer()
+ {
+ if (lineBuffer == null)
+ lineBuffer = new Segment();
+ return lineBuffer;
+ }
+
+ /**
+ * Returns the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction <code>d</code>.
+ *
+ * @param c the text component
+ * @param pos the document position
+ * @param b the bias for <code>pos</code>
+ * @param d the direction, must be either {@link SwingConstants#NORTH},
+ * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
+ * {@link SwingConstants#EAST}
+ * @param biasRet an array of {@link Position.Bias} that can hold at least
+ * one element, which is filled with the bias of the return position
+ * on method exit
+ *
+ * @return the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction
+ * <code>d</code>
+ *
+ * @throws BadLocationException if <code>pos</code> is not a valid offset in
+ * the document model
+ */
+ public int getNextVisualPositionFrom(JTextComponent c, int pos,
+ Position.Bias b, int d,
+ Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ // TODO: Implement this properly.
+ throw new AssertionError("Not implemented yet.");
}
}
diff --git a/libjava/classpath/javax/swing/text/Segment.java b/libjava/classpath/javax/swing/text/Segment.java
index 92d850016d9..84e0e700f2e 100644
--- a/libjava/classpath/javax/swing/text/Segment.java
+++ b/libjava/classpath/javax/swing/text/Segment.java
@@ -39,8 +39,7 @@ package javax.swing.text;
import java.text.CharacterIterator;
-public class Segment
- implements Cloneable, CharacterIterator
+public class Segment implements Cloneable, CharacterIterator
{
private boolean partialReturn;
private int current;
@@ -51,6 +50,7 @@ public class Segment
public Segment()
{
+ // Nothing to do here.
}
public Segment(char[] array, int offset, int count)
diff --git a/libjava/classpath/javax/swing/text/SimpleAttributeSet.java b/libjava/classpath/javax/swing/text/SimpleAttributeSet.java
index 3ef5db61d43..0c9f607b196 100644
--- a/libjava/classpath/javax/swing/text/SimpleAttributeSet.java
+++ b/libjava/classpath/javax/swing/text/SimpleAttributeSet.java
@@ -45,6 +45,9 @@ import java.util.Hashtable;
public class SimpleAttributeSet
implements MutableAttributeSet, Serializable, Cloneable
{
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 8267656273837665219L;
+
public static final AttributeSet EMPTY = new SimpleAttributeSet();
Hashtable tab;
@@ -84,12 +87,34 @@ public class SimpleAttributeSet
return s;
}
+ /**
+ * Returns true if the given name and value represent an attribute
+ * found either in this AttributeSet or in its resolve parent hierarchy.
+ * @param name the key for the attribute
+ * @param value the value for the attribute
+ * @return true if the attribute is found here or in this set's resolve
+ * parent hierarchy
+ */
public boolean containsAttribute(Object name, Object value)
{
+ return (tab.containsKey(name) && tab.get(name).equals(value)) ||
+ (getResolveParent() != null && getResolveParent().
+ containsAttribute(name, value));
+ }
+
+ /**
+ * Returns true if the given name and value are found in this AttributeSet.
+ * Does not check the resolve parent.
+ * @param name the key for the attribute
+ * @param value the value for the attribute
+ * @return true if the attribute is found in this AttributeSet
+ */
+ boolean containsAttributeLocally(Object name, Object value)
+ {
return tab.containsKey(name)
&& tab.get(name).equals(value);
}
-
+
public boolean containsAttributes(AttributeSet attributes)
{
Enumeration e = attributes.getAttributeNames();
@@ -110,9 +135,9 @@ public class SimpleAttributeSet
public boolean equals(Object obj)
{
- return (obj != null)
- && (obj instanceof SimpleAttributeSet)
- && ((SimpleAttributeSet)obj).tab.equals(this.tab);
+ return
+ (obj instanceof AttributeSet)
+ && this.isEqual((AttributeSet) obj);
}
public Object getAttribute(Object name)
@@ -157,10 +182,16 @@ public class SimpleAttributeSet
{
return tab.isEmpty();
}
-
+
+ /**
+ * Returns true if the given set has the same number of attributes
+ * as this set and <code>containsAttributes(attr)</code> returns
+ * true.
+ */
public boolean isEqual(AttributeSet attr)
{
- return this.equals(attr);
+ return getAttributeCount() == attr.getAttributeCount()
+ && this.containsAttributes(attr);
}
public void removeAttribute(Object name)
@@ -168,9 +199,21 @@ public class SimpleAttributeSet
tab.remove(name);
}
+ /**
+ * Removes attributes from this set if they are found in the
+ * given set. Only attributes whose key AND value are removed.
+ * Removes attributes only from this set, not from the resolving parent.
+ */
public void removeAttributes(AttributeSet attributes)
{
- removeAttributes(attributes.getAttributeNames());
+ Enumeration e = attributes.getAttributeNames();
+ while (e.hasMoreElements())
+ {
+ Object name = e.nextElement();
+ Object val = attributes.getAttribute(name);
+ if (containsAttributeLocally(name, val))
+ removeAttribute(name);
+ }
}
public void removeAttributes(Enumeration names)
diff --git a/libjava/classpath/javax/swing/text/StringContent.java b/libjava/classpath/javax/swing/text/StringContent.java
index bedf480d4ec..7db377a1c9b 100644
--- a/libjava/classpath/javax/swing/text/StringContent.java
+++ b/libjava/classpath/javax/swing/text/StringContent.java
@@ -56,6 +56,9 @@ import javax.swing.undo.UndoableEdit;
*/
public final class StringContent implements AbstractDocument.Content, Serializable
{
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 4755994433709540381L;
+
// This is package-private to avoid an accessor method.
char[] content;
diff --git a/libjava/classpath/javax/swing/text/StyleConstants.java b/libjava/classpath/javax/swing/text/StyleConstants.java
index 3f973f22631..598eaf621bc 100644
--- a/libjava/classpath/javax/swing/text/StyleConstants.java
+++ b/libjava/classpath/javax/swing/text/StyleConstants.java
@@ -54,11 +54,13 @@ public class StyleConstants
public static final Object BidiLevel = CharacterConstants.BidiLevel;
public static final Object Bold = CharacterConstants.Bold;
public static final Object ComponentAttribute = CharacterConstants.ComponentAttribute;
- public static final Object FontFamily = CharacterConstants.Family;
+ public static final Object Family = CharacterConstants.Family;
+ public static final Object FontFamily = CharacterConstants.Family;
public static final Object FontSize = CharacterConstants.Size;
public static final Object Foreground = CharacterConstants.Foreground;
public static final Object IconAttribute = CharacterConstants.IconAttribute;
public static final Object Italic = CharacterConstants.Italic;
+ public static final Object Size = CharacterConstants.Size;
public static final Object StrikeThrough = CharacterConstants.StrikeThrough;
public static final Object Subscript = CharacterConstants.Subscript;
public static final Object Superscript = CharacterConstants.Superscript;
@@ -109,7 +111,7 @@ public class StyleConstants
if (a.isDefined(Background))
return (Color) a.getAttribute(Background);
else
- return Color.BLACK;
+ return Color.WHITE;
}
public static int getBidiLevel(AttributeSet a)
diff --git a/libjava/classpath/javax/swing/text/StyleContext.java b/libjava/classpath/javax/swing/text/StyleContext.java
index ae11622ffc6..6c4e299455f 100644
--- a/libjava/classpath/javax/swing/text/StyleContext.java
+++ b/libjava/classpath/javax/swing/text/StyleContext.java
@@ -57,9 +57,15 @@ import javax.swing.event.EventListenerList;
public class StyleContext
implements Serializable, AbstractDocument.AttributeContext
{
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 8042858831190784241L;
+
public class NamedStyle
implements Serializable, Style
{
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = -6690628971806226374L;
+
protected ChangeEvent changeEvent;
protected EventListenerList listenerList;
@@ -288,7 +294,7 @@ public class StyleContext
public boolean equals(Object obj)
{
return
- (obj instanceof SmallAttributeSet)
+ (obj instanceof AttributeSet)
&& this.isEqual((AttributeSet)obj);
}
@@ -300,9 +306,14 @@ public class StyleContext
return attrs[i+1];
}
- Object p = getResolveParent();
- if (p != null && p instanceof AttributeSet)
- return (((AttributeSet)p).getAttribute(key));
+ // Check the resolve parent, unless we're looking for the
+ // ResolveAttribute, which would cause an infinite loop
+ if (!(key.equals(ResolveAttribute)))
+ {
+ Object p = getResolveParent();
+ if (p != null && p instanceof AttributeSet)
+ return (((AttributeSet)p).getAttribute(key));
+ }
return null;
}
diff --git a/libjava/classpath/javax/swing/text/StyledDocument.java b/libjava/classpath/javax/swing/text/StyledDocument.java
index ea277540f23..168e1b116f3 100644
--- a/libjava/classpath/javax/swing/text/StyledDocument.java
+++ b/libjava/classpath/javax/swing/text/StyledDocument.java
@@ -45,101 +45,96 @@ import java.awt.Font;
* @author Andrew Selkirk
* @version 1.0
*/
-public interface StyledDocument extends Document {
-
- //-------------------------------------------------------------
- // Methods ----------------------------------------------------
- //-------------------------------------------------------------
-
- /**
- * addStyle
- * @param nm TODO
- * @param rent TODO
- * @returns Style
- */
- Style addStyle(String nm, Style parent);
-
- /**
- * removeStyle
- * @param nm TODO
- */
- void removeStyle(String nm);
-
- /**
- * getStyle
- * @param nm TODO
- * @returns Style
- */
- Style getStyle(String nm);
-
- /**
- * setCharacterAttributes
- * @param offset TODO
- * @param length TODO
- * @param set TODO
- * @param replace TODO
- */
- void setCharacterAttributes(int offset, int length,
- AttributeSet set, boolean replace);
-
- /**
- * setParagraphAttributes
- * @param offset TODO
- * @param length TODO
- * @param set TODO
- * @param replace TODO
- */
- void setParagraphAttributes(int offset, int length,
- AttributeSet set, boolean replace);
-
- /**
- * getLogicalStyle
- * @param position TODO
- * @returns Style
- */
- Style getLogicalStyle(int position);
-
- /**
- * setLogicalStyle
- * @param position TODO
- * @param style TODO
- */
- void setLogicalStyle(int position, Style style);
-
- /**
- * getParagraphElement
- * @param position TODO
- * @returns Element
- */
- Element getParagraphElement(int position);
-
- /**
- * getCharacterElement
- * @param position TODO
- * @returns Element
- */
- Element getCharacterElement(int position);
-
- /**
- * getForeground
- * @param set TODO
- * @returns Color
- */
- Color getForeground(AttributeSet set);
-
- /**
- * getBackground
- * @param set TODO
- * @returns Color
- */
- Color getBackground(AttributeSet set);
-
- /**
- * getFont
- * @param set TODO
- * @returns Font
- */
- Font getFont(AttributeSet set);
-
-
-} // StyledDocument
+public interface StyledDocument extends Document
+{
+ /**
+ * addStyle
+ * @param nm TODO
+ * @param parent TODO
+ * @returns Style
+ */
+ Style addStyle(String nm, Style parent);
+
+ /**
+ * removeStyle
+ * @param nm TODO
+ */
+ void removeStyle(String nm);
+
+ /**
+ * getStyle
+ * @param nm TODO
+ * @returns Style
+ */
+ Style getStyle(String nm);
+
+ /**
+ * setCharacterAttributes
+ * @param offset TODO
+ * @param length TODO
+ * @param set TODO
+ * @param replace TODO
+ */
+ void setCharacterAttributes(int offset, int length, AttributeSet set,
+ boolean replace);
+
+ /**
+ * setParagraphAttributes
+ * @param offset TODO
+ * @param length TODO
+ * @param set TODO
+ * @param replace TODO
+ */
+ void setParagraphAttributes(int offset, int length, AttributeSet set,
+ boolean replace);
+
+ /**
+ * getLogicalStyle
+ * @param position TODO
+ * @returns Style
+ */
+ Style getLogicalStyle(int position);
+
+ /**
+ * setLogicalStyle
+ * @param position TODO
+ * @param style TODO
+ */
+ void setLogicalStyle(int position, Style style);
+
+ /**
+ * getParagraphElement
+ * @param position TODO
+ * @returns Element
+ */
+ Element getParagraphElement(int position);
+
+ /**
+ * getCharacterElement
+ * @param position TODO
+ * @returns Element
+ */
+ Element getCharacterElement(int position);
+
+ /**
+ * getForeground
+ * @param set TODO
+ * @returns Color
+ */
+ Color getForeground(AttributeSet set);
+
+ /**
+ * getBackground
+ * @param set TODO
+ * @returns Color
+ */
+ Color getBackground(AttributeSet set);
+
+ /**
+ * getFont
+ * @param set TODO
+ * @returns Font
+ */
+ Font getFont(AttributeSet set);
+
+}
diff --git a/libjava/classpath/javax/swing/text/StyledEditorKit.java b/libjava/classpath/javax/swing/text/StyledEditorKit.java
index 89c4cf18ee4..e71f992b534 100644
--- a/libjava/classpath/javax/swing/text/StyledEditorKit.java
+++ b/libjava/classpath/javax/swing/text/StyledEditorKit.java
@@ -40,13 +40,9 @@ package javax.swing.text;
import java.awt.Color;
import java.awt.event.ActionEvent;
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-import java.io.Serializable;
import javax.swing.Action;
import javax.swing.JEditorPane;
-import javax.swing.JTextPane;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
@@ -460,11 +456,11 @@ public class StyledEditorKit extends DefaultEditorKit
* <code>StyledEditorKit</code>, namely the following types of Elements:
*
* <ul>
- * <li>{@link AbstractDocument.ContentElementName}</li>
- * <li>{@link AbstractDocument.ParagraphElementName}</li>
- * <li>{@link AbstractDocument.SectionElementName}</li>
- * <li>{@link StyleContext.ComponentElementName}</li>
- * <li>{@link StyleContext.IconElementName}</li>
+ * <li>{@link AbstractDocument#ContentElementName}</li>
+ * <li>{@link AbstractDocument#ParagraphElementName}</li>
+ * <li>{@link AbstractDocument#SectionElementName}</li>
+ * <li>{@link StyleConstants#ComponentElementName}</li>
+ * <li>{@link StyleConstants#IconElementName}</li>
* </ul>
*/
static class StyledViewFactory
@@ -667,11 +663,11 @@ public class StyledEditorKit extends DefaultEditorKit
* namely the following types of <code>Element</code>s:
*
* <ul>
- * <li>{@link AbstractDocument.ContentElementName}</li>
- * <li>{@link AbstractDocument.ParagraphElementName}</li>
- * <li>{@link AbstractDocument.SectionElementName}</li>
- * <li>{@link StyleContext.ComponentElementName}</li>
- * <li>{@link StyleContext.IconElementName}</li>
+ * <li>{@link AbstractDocument#ContentElementName}</li>
+ * <li>{@link AbstractDocument#ParagraphElementName}</li>
+ * <li>{@link AbstractDocument#SectionElementName}</li>
+ * <li>{@link StyleConstants#ComponentElementName}</li>
+ * <li>{@link StyleConstants#IconElementName}</li>
* </ul>
*
* @return a {@link ViewFactory} that is able to create {@link View}s
diff --git a/libjava/classpath/javax/swing/text/TabSet.java b/libjava/classpath/javax/swing/text/TabSet.java
index 146f545aac7..ecad9444ea5 100644
--- a/libjava/classpath/javax/swing/text/TabSet.java
+++ b/libjava/classpath/javax/swing/text/TabSet.java
@@ -41,6 +41,9 @@ import java.io.Serializable;
public class TabSet implements Serializable
{
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = 2367703481999080593L;
+
TabStop[] tabs;
public TabSet(TabStop[] t)
diff --git a/libjava/classpath/javax/swing/text/TabStop.java b/libjava/classpath/javax/swing/text/TabStop.java
index 032da8bca46..56f862fdae4 100644
--- a/libjava/classpath/javax/swing/text/TabStop.java
+++ b/libjava/classpath/javax/swing/text/TabStop.java
@@ -41,6 +41,9 @@ import java.io.Serializable;
public class TabStop implements Serializable
{
+ /** The serialization UID (compatible with JDK1.5). */
+ private static final long serialVersionUID = -5381995917363605058L;
+
public static final int ALIGN_LEFT = 0;
public static final int ALIGN_RIGHT = 1;
public static final int ALIGN_CENTER = 2;
diff --git a/libjava/classpath/javax/swing/text/Utilities.java b/libjava/classpath/javax/swing/text/Utilities.java
index d40408ddc3f..7830b2fca04 100644
--- a/libjava/classpath/javax/swing/text/Utilities.java
+++ b/libjava/classpath/javax/swing/text/Utilities.java
@@ -40,6 +40,11 @@ package javax.swing.text;
import java.awt.FontMetrics;
import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.text.BreakIterator;
+
+import javax.swing.SwingConstants;
/**
* A set of utilities to deal with text. This is used by several other classes
@@ -195,4 +200,409 @@ public class Utilities
return maxWidth;
}
+
+ /**
+ * Provides a facility to map screen coordinates into a model location. For a
+ * given text fragment and start location within this fragment, this method
+ * determines the model location so that the resulting fragment fits best
+ * into the span <code>[x0, x]</code>.
+ *
+ * The parameter <code>round</code> controls which model location is returned
+ * if the view coordinates are on a character: If <code>round</code> is
+ * <code>true</code>, then the result is rounded up to the next character, so
+ * that the resulting fragment is the smallest fragment that is larger than
+ * the specified span. If <code>round</code> is <code>false</code>, then the
+ * resulting fragment is the largest fragment that is smaller than the
+ * specified span.
+ *
+ * @param s the text segment
+ * @param fm the font metrics to use
+ * @param x0 the starting screen location
+ * @param x the target screen location at which the requested fragment should
+ * end
+ * @param te the tab expander to use; if this is <code>null</code>, TABs are
+ * expanded to one space character
+ * @param p0 the starting model location
+ * @param round if <code>true</code> round up to the next location, otherwise
+ * round down to the current location
+ *
+ * @return the model location, so that the resulting fragment fits within the
+ * specified span
+ */
+ public static final int getTabbedTextOffset(Segment s, FontMetrics fm, int x0,
+ int x, TabExpander te, int p0,
+ boolean round)
+ {
+ // At the end of the for loop, this holds the requested model location
+ int pos;
+ int currentX = x0;
+
+ for (pos = p0; pos < s.count; pos++)
+ {
+ char nextChar = s.array[s.offset+pos];
+ if (nextChar == 0)
+ {
+ if (! round)
+ pos--;
+ break;
+ }
+ if (nextChar != '\t')
+ currentX += fm.charWidth(nextChar);
+ else
+ {
+ if (te == null)
+ currentX += fm.charWidth(' ');
+ else
+ currentX = (int) te.nextTabStop(currentX, pos);
+ }
+ if (currentX > x)
+ {
+ if (! round)
+ pos--;
+ break;
+ }
+ }
+ return pos;
+ }
+
+ /**
+ * Provides a facility to map screen coordinates into a model location. For a
+ * given text fragment and start location within this fragment, this method
+ * determines the model location so that the resulting fragment fits best
+ * into the span <code>[x0, x]</code>.
+ *
+ * This method rounds up to the next location, so that the resulting fragment
+ * will be the smallest fragment of the text, that is greater than the
+ * specified span.
+ *
+ * @param s the text segment
+ * @param fm the font metrics to use
+ * @param x0 the starting screen location
+ * @param x the target screen location at which the requested fragment should
+ * end
+ * @param te the tab expander to use; if this is <code>null</code>, TABs are
+ * expanded to one space character
+ * @param p0 the starting model location
+ *
+ * @return the model location, so that the resulting fragment fits within the
+ * specified span
+ */
+ public static final int getTabbedTextOffset(Segment s, FontMetrics fm, int x0,
+ int x, TabExpander te, int p0)
+ {
+ return getTabbedTextOffset(s, fm, x0, x, te, p0, true);
+ }
+
+ /**
+ * Finds the start of the next word for the given offset.
+ *
+ * @param c
+ * the text component
+ * @param offs
+ * the offset in the document
+ * @return the location in the model of the start of the next word.
+ * @throws BadLocationException
+ * if the offset is invalid.
+ */
+ public static final int getNextWord(JTextComponent c, int offs)
+ throws BadLocationException
+ {
+ if (offs < 0 || offs > (c.getText().length() - 1))
+ throw new BadLocationException("invalid offset specified", offs);
+ String text = c.getText();
+ BreakIterator wb = BreakIterator.getWordInstance();
+ wb.setText(text);
+ int last = wb.following(offs);
+ int current = wb.next();
+ while (current != BreakIterator.DONE)
+ {
+ for (int i = last; i < current; i++)
+ {
+ // FIXME: Should use isLetter(int) and text.codePointAt(int)
+ // instead, but isLetter(int) isn't implemented yet
+ if (Character.isLetter(text.charAt(i)))
+ return last;
+ }
+ last = current;
+ current = wb.next();
+ }
+ return BreakIterator.DONE;
+ }
+
+ /**
+ * Finds the start of the previous word for the given offset.
+ *
+ * @param c
+ * the text component
+ * @param offs
+ * the offset in the document
+ * @return the location in the model of the start of the previous word.
+ * @throws BadLocationException
+ * if the offset is invalid.
+ */
+ public static final int getPreviousWord(JTextComponent c, int offs)
+ throws BadLocationException
+ {
+ if (offs < 0 || offs > (c.getText().length() - 1))
+ throw new BadLocationException("invalid offset specified", offs);
+ String text = c.getText();
+ BreakIterator wb = BreakIterator.getWordInstance();
+ wb.setText(text);
+ int last = wb.preceding(offs);
+ int current = wb.previous();
+
+ while (current != BreakIterator.DONE)
+ {
+ for (int i = last; i < offs; i++)
+ {
+ // FIXME: Should use isLetter(int) and text.codePointAt(int)
+ // instead, but isLetter(int) isn't implemented yet
+ if (Character.isLetter(text.charAt(i)))
+ return last;
+ }
+ last = current;
+ current = wb.previous();
+ }
+ return 0;
+ }
+
+ /**
+ * Finds the start of a word for the given location.
+ * @param c the text component
+ * @param offs the offset location
+ * @return the location of the word beginning
+ * @throws BadLocationException if the offset location is invalid
+ */
+ public static final int getWordStart(JTextComponent c, int offs)
+ throws BadLocationException
+ {
+ if (offs < 0 || offs >= c.getText().length())
+ throw new BadLocationException("invalid offset specified", offs);
+
+ String text = c.getText();
+ BreakIterator wb = BreakIterator.getWordInstance();
+ wb.setText(text);
+ if (wb.isBoundary(offs))
+ return offs;
+ return wb.preceding(offs);
+ }
+
+ /**
+ * Finds the end of a word for the given location.
+ * @param c the text component
+ * @param offs the offset location
+ * @return the location of the word end
+ * @throws BadLocationException if the offset location is invalid
+ */
+ public static final int getWordEnd(JTextComponent c, int offs)
+ throws BadLocationException
+ {
+ if (offs < 0 || offs >= c.getText().length())
+ throw new BadLocationException("invalid offset specified", offs);
+
+ String text = c.getText();
+ BreakIterator wb = BreakIterator.getWordInstance();
+ wb.setText(text);
+ return wb.following(offs);
+ }
+
+ /**
+ * Get the model position of the end of the row that contains the
+ * specified model position. Return null if the given JTextComponent
+ * does not have a size.
+ * @param c the JTextComponent
+ * @param offs the model position
+ * @return the model position of the end of the row containing the given
+ * offset
+ * @throws BadLocationException if the offset is invalid
+ */
+ public static final int getRowEnd(JTextComponent c, int offs)
+ throws BadLocationException
+ {
+ String text = c.getText();
+ if (text == null)
+ return -1;
+
+ // Do a binary search for the smallest position X > offs
+ // such that that character at positino X is not on the same
+ // line as the character at position offs
+ int high = offs + ((text.length() - 1 - offs) / 2);
+ int low = offs;
+ int oldHigh = text.length() + 1;
+ while (true)
+ {
+ if (c.modelToView(high).y != c.modelToView(offs).y)
+ {
+ oldHigh = high;
+ high = low + ((high + 1 - low) / 2);
+ if (oldHigh == high)
+ return high - 1;
+ }
+ else
+ {
+ low = high;
+ high += ((oldHigh - high) / 2);
+ if (low == high)
+ return low;
+ }
+ }
+ }
+
+ /**
+ * Get the model position of the start of the row that contains the specified
+ * model position. Return null if the given JTextComponent does not have a
+ * size.
+ *
+ * @param c the JTextComponent
+ * @param offs the model position
+ * @return the model position of the start of the row containing the given
+ * offset
+ * @throws BadLocationException if the offset is invalid
+ */
+ public static final int getRowStart(JTextComponent c, int offs)
+ throws BadLocationException
+ {
+ String text = c.getText();
+ if (text == null)
+ return -1;
+
+ // Do a binary search for the greatest position X < offs
+ // such that the character at position X is not on the same
+ // row as the character at position offs
+ int high = offs;
+ int low = 0;
+ int oldLow = 0;
+ while (true)
+ {
+ if (c.modelToView(low).y != c.modelToView(offs).y)
+ {
+ oldLow = low;
+ low = high - ((high + 1 - low) / 2);
+ if (oldLow == low)
+ return low + 1;
+ }
+ else
+ {
+ high = low;
+ low -= ((low - oldLow) / 2);
+ if (low == high)
+ return low;
+ }
+ }
+ }
+
+ /**
+ * Determine where to break the text in the given Segment, attempting to find
+ * a word boundary.
+ * @param s the Segment that holds the text
+ * @param metrics the font metrics used for calculating the break point
+ * @param x0 starting view location representing the start of the text
+ * @param x the target view location
+ * @param e the TabExpander used for expanding tabs (if this is null tabs
+ * are expanded to 1 space)
+ * @param startOffset the offset in the Document of the start of the text
+ * @return the offset at which we should break the text
+ */
+ public static final int getBreakLocation(Segment s, FontMetrics metrics,
+ int x0, int x, TabExpander e,
+ int startOffset)
+ {
+ int mark = Utilities.getTabbedTextOffset(s, metrics, x0, x, e, startOffset);
+ BreakIterator breaker = BreakIterator.getWordInstance();
+ breaker.setText(s.toString());
+
+ // If mark is equal to the end of the string, just use that position
+ if (mark == s.count)
+ return mark;
+
+ // Try to find a word boundary previous to the mark at which we
+ // can break the text
+ int preceding = breaker.preceding(mark + 1);
+
+ if (preceding != 0)
+ return preceding;
+ else
+ // If preceding is 0 we couldn't find a suitable word-boundary so
+ // just break it on the character boundary
+ return mark;
+ }
+
+ /**
+ * Returns the paragraph element in the text component <code>c</code> at
+ * the specified location <code>offset</code>.
+ *
+ * @param c the text component
+ * @param offset the offset of the paragraph element to return
+ *
+ * @return the paragraph element at <code>offset</code>
+ */
+ public static final Element getParagraphElement(JTextComponent c, int offset)
+ {
+ Document doc = c.getDocument();
+ Element par = null;
+ if (doc instanceof StyledDocument)
+ {
+ StyledDocument styledDoc = (StyledDocument) doc;
+ par = styledDoc.getParagraphElement(offset);
+ }
+ else
+ {
+ Element root = c.getDocument().getDefaultRootElement();
+ int parIndex = root.getElementIndex(offset);
+ par = root.getElement(parIndex);
+ }
+ return par;
+ }
+
+ /**
+ * Returns the document position that is closest above to the specified x
+ * coordinate in the row containing <code>offset</code>.
+ *
+ * @param c the text component
+ * @param offset the offset
+ * @param x the x coordinate
+ *
+ * @return the document position that is closest above to the specified x
+ * coordinate in the row containing <code>offset</code>
+ *
+ * @throws BadLocationException if <code>offset</code> is not a valid offset
+ */
+ public static final int getPositionAbove(JTextComponent c, int offset, int x)
+ throws BadLocationException
+ {
+ View rootView = c.getUI().getRootView(c);
+ Rectangle r = c.modelToView(offset);
+ int offs = c.viewToModel(new Point(x, r.y));
+ int pos = rootView.getNextVisualPositionFrom(c, offs,
+ Position.Bias.Forward,
+ SwingConstants.NORTH,
+ new Position.Bias[1]);
+ return pos;
+ }
+
+ /**
+ * Returns the document position that is closest below to the specified x
+ * coordinate in the row containing <code>offset</code>.
+ *
+ * @param c the text component
+ * @param offset the offset
+ * @param x the x coordinate
+ *
+ * @return the document position that is closest above to the specified x
+ * coordinate in the row containing <code>offset</code>
+ *
+ * @throws BadLocationException if <code>offset</code> is not a valid offset
+ */
+ public static final int getPositionBelow(JTextComponent c, int offset, int x)
+ throws BadLocationException
+ {
+ View rootView = c.getUI().getRootView(c);
+ Rectangle r = c.modelToView(offset);
+ int offs = c.viewToModel(new Point(x, r.y));
+ int pos = rootView.getNextVisualPositionFrom(c, offs,
+ Position.Bias.Forward,
+ SwingConstants.SOUTH,
+ new Position.Bias[1]);
+ return pos;
+ }
}
diff --git a/libjava/classpath/javax/swing/text/View.java b/libjava/classpath/javax/swing/text/View.java
index 24efba9a1bc..daab347d731 100644
--- a/libjava/classpath/javax/swing/text/View.java
+++ b/libjava/classpath/javax/swing/text/View.java
@@ -43,7 +43,6 @@ import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
-import javax.swing.JComponent;
import javax.swing.SwingConstants;
import javax.swing.event.DocumentEvent;
@@ -87,9 +86,9 @@ public abstract class View implements SwingConstants
{
View parent = getParent();
if (parent == null)
- throw new AssertionError("The parent of a View must not be null.");
-
- return parent.getContainer();
+ return null;
+ else
+ return parent.getContainer();
}
public Document getDocument()
@@ -508,6 +507,30 @@ public abstract class View implements SwingConstants
}
/**
+ * Maps a position in the document into the coordinate space of the View.
+ * The output rectangle usually reflects the font height but has a width
+ * of zero.
+ *
+ * This method is deprecated and calls
+ * {@link #modelToView(int, Position.Bias, int, Position.Bias, Shape)} with
+ * a bias of {@link Position.Bias#Forward}.
+ *
+ * @param pos the position of the character in the model
+ * @param a the area that is occupied by the view
+ *
+ * @return a rectangle that gives the location of the document position
+ * inside the view coordinate space
+ *
+ * @throws BadLocationException if <code>pos</code> is invalid
+ *
+ * @deprecated Use {@link #modelToView(int, Shape, Position.Bias)} instead.
+ */
+ public Shape modelToView(int pos, Shape a) throws BadLocationException
+ {
+ return modelToView(pos, a, Position.Bias.Forward);
+ }
+
+ /**
* Maps coordinates from the <code>View</code>'s space into a position
* in the document model.
*
@@ -521,6 +544,25 @@ public abstract class View implements SwingConstants
*/
public abstract int viewToModel(float x, float y, Shape a, Position.Bias[] b);
+ /**
+ * Maps coordinates from the <code>View</code>'s space into a position
+ * in the document model. This method is deprecated and only there for
+ * compatibility.
+ *
+ * @param x the x coordinate in the view space
+ * @param y the y coordinate in the view space
+ * @param a the allocation of this <code>View</code>
+ *
+ * @return the position in the document that corresponds to the screen
+ * coordinates <code>x, y</code>
+ *
+ * @deprecated Use {@link #viewToModel(float, float, Shape, Position.Bias[])}
+ * instead.
+ */
+ public int viewToModel(float x, float y, Shape a)
+ {
+ return viewToModel(x, y, a, new Position.Bias[0]);
+ }
/**
* Dumps the complete View hierarchy. This method can be used for debugging
@@ -552,4 +594,30 @@ public abstract class View implements SwingConstants
for (int i = 0; i < count; ++i)
getView(i).dump(indent + 1);
}
+
+ /**
+ * Returns the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction <code>d</code>.
+ *
+ * @param c the text component
+ * @param pos the document position
+ * @param b the bias for <code>pos</code>
+ * @param d the direction, must be either {@link SwingConstants#NORTH},
+ * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
+ * {@link SwingConstants#EAST}
+ * @param biasRet an array of {@link Position.Bias} that can hold at least
+ * one element, which is filled with the bias of the return position
+ * on method exit
+ *
+ * @return the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction
+ * <code>d</code>
+ *
+ * @throws BadLocationException if <code>pos</code> is not a valid offset in
+ * the document model
+ */
+ public abstract int getNextVisualPositionFrom(JTextComponent c, int pos,
+ Position.Bias b, int d,
+ Position.Bias[] biasRet)
+ throws BadLocationException;
}
diff --git a/libjava/classpath/javax/swing/text/WrappedPlainView.java b/libjava/classpath/javax/swing/text/WrappedPlainView.java
new file mode 100644
index 00000000000..b90519046ae
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/WrappedPlainView.java
@@ -0,0 +1,700 @@
+/* WrappedPlainView.java --
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text;
+
+import java.awt.Color;
+import java.awt.Container;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.SwingConstants;
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.Position.Bias;
+
+/**
+ * @author abalkiss
+ *
+ */
+public class WrappedPlainView extends BoxView implements TabExpander
+{
+ /** The color for selected text **/
+ Color selectedColor;
+
+ /** The color for unselected text **/
+ Color unselectedColor;
+
+ /** The color for disabled components **/
+ Color disabledColor;
+
+ /** Stores the font metrics **/
+ protected FontMetrics metrics;
+
+ /** Whether or not to wrap on word boundaries **/
+ boolean wordWrap;
+
+ /** A ViewFactory that creates WrappedLines **/
+ ViewFactory viewFactory = new WrappedLineCreator();
+
+ /** The start of the selected text **/
+ int selectionStart;
+
+ /** The end of the selected text **/
+ int selectionEnd;
+
+ /**
+ * The instance returned by {@link #getLineBuffer()}.
+ */
+ private transient Segment lineBuffer;
+
+ public WrappedPlainView (Element elem)
+ {
+ this (elem, false);
+ }
+
+ public WrappedPlainView (Element elem, boolean wordWrap)
+ {
+ super (elem, Y_AXIS);
+ this.wordWrap = wordWrap;
+ }
+
+ /**
+ * Provides access to the Segment used for retrievals from the Document.
+ * @return the Segment.
+ */
+ protected final Segment getLineBuffer()
+ {
+ if (lineBuffer == null)
+ lineBuffer = new Segment();
+ return lineBuffer;
+ }
+
+ /**
+ * Returns the next tab stop position after a given reference position.
+ *
+ * This implementation ignores the <code>tabStop</code> argument.
+ *
+ * @param x the current x position in pixels
+ * @param tabStop the position within the text stream that the tab occured at
+ */
+ public float nextTabStop(float x, int tabStop)
+ {
+ JTextComponent host = (JTextComponent)getContainer();
+ float tabSizePixels = getTabSize()
+ * host.getFontMetrics(host.getFont()).charWidth('m');
+ return (float) (Math.floor(x / tabSizePixels) + 1) * tabSizePixels;
+ }
+
+ /**
+ * Returns the tab size for the Document based on
+ * PlainDocument.tabSizeAttribute, defaulting to 8 if this property is
+ * not defined
+ *
+ * @return the tab size.
+ */
+ protected int getTabSize()
+ {
+ Object tabSize = getDocument().getProperty(PlainDocument.tabSizeAttribute);
+ if (tabSize == null)
+ return 8;
+ return ((Integer)tabSize).intValue();
+ }
+
+ /**
+ * Draws a line of text, suppressing white space at the end and expanding
+ * tabs. Calls drawSelectedText and drawUnselectedText.
+ * @param p0 starting document position to use
+ * @param p1 ending document position to use
+ * @param g graphics context
+ * @param x starting x position
+ * @param y starting y position
+ */
+ protected void drawLine(int p0, int p1, Graphics g, int x, int y)
+ {
+ try
+ {
+ // We have to draw both selected and unselected text. There are
+ // several cases:
+ // - entire range is unselected
+ // - entire range is selected
+ // - start of range is selected, end of range is unselected
+ // - start of range is unselected, end of range is selected
+ // - middle of range is selected, start and end of range is unselected
+
+ // entire range unselected:
+ if ((selectionStart == selectionEnd) ||
+ (p0 > selectionEnd || p1 < selectionStart))
+ drawUnselectedText(g, x, y, p0, p1);
+
+ // entire range selected
+ else if (p0 >= selectionStart && p1 <= selectionEnd)
+ drawSelectedText(g, x, y, p0, p1);
+
+ // start of range selected, end of range unselected
+ else if (p0 >= selectionStart)
+ {
+ x = drawSelectedText(g, x, y, p0, selectionEnd);
+ drawUnselectedText(g, x, y, selectionEnd, p1);
+ }
+
+ // start of range unselected, end of range selected
+ else if (selectionStart > p0 && selectionEnd > p1)
+ {
+ x = drawUnselectedText(g, x, y, p0, selectionStart);
+ drawSelectedText(g, x, y, selectionStart, p1);
+ }
+
+ // middle of range selected
+ else if (selectionStart > p0)
+ {
+ x = drawUnselectedText(g, x, y, p0, selectionStart);
+ x = drawSelectedText(g, x, y, selectionStart, selectionEnd);
+ drawUnselectedText(g, x, y, selectionEnd, p1);
+ }
+ }
+ catch (BadLocationException ble)
+ {
+ // shouldn't happen
+ }
+ }
+
+ /**
+ * Renders the range of text as selected text. Just paints the text
+ * in the color specified by the host component. Assumes the highlighter
+ * will render the selected background.
+ * @param g the graphics context
+ * @param x the starting X coordinate
+ * @param y the starting Y coordinate
+ * @param p0 the starting model location
+ * @param p1 the ending model location
+ * @return the X coordinate of the end of the text
+ * @throws BadLocationException if the given range is invalid
+ */
+ protected int drawSelectedText(Graphics g, int x, int y, int p0, int p1)
+ throws BadLocationException
+ {
+ g.setColor(selectedColor);
+ Segment segment = getLineBuffer();
+ getDocument().getText(p0, p1 - p0, segment);
+ return Utilities.drawTabbedText(segment, x, y, g, this, p0);
+ }
+
+ /**
+ * Renders the range of text as normal unhighlighted text.
+ * @param g the graphics context
+ * @param x the starting X coordinate
+ * @param y the starting Y coordinate
+ * @param p0 the starting model location
+ * @param p1 the end model location
+ * @return the X location of the end off the range
+ * @throws BadLocationException if the range given is invalid
+ */
+ protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1)
+ throws BadLocationException
+ {
+ JTextComponent textComponent = (JTextComponent) getContainer();
+ if (textComponent.isEnabled())
+ g.setColor(unselectedColor);
+ else
+ g.setColor(disabledColor);
+
+ Segment segment = getLineBuffer();
+ getDocument().getText(p0, p1 - p0, segment);
+ return Utilities.drawTabbedText(segment, x, y, g, this, p0);
+ }
+
+ /**
+ * Loads the children to initiate the view. Called by setParent.
+ * Creates a WrappedLine for each child Element.
+ */
+ protected void loadChildren (ViewFactory f)
+ {
+ Element root = getElement();
+ int numChildren = root.getElementCount();
+ if (numChildren == 0)
+ return;
+
+ View[] children = new View[numChildren];
+ for (int i = 0; i < numChildren; i++)
+ children[i] = new WrappedLine(root.getElement(i));
+ replace(0, 0, children);
+ }
+
+ /**
+ * Calculates the break position for the text between model positions
+ * p0 and p1. Will break on word boundaries or character boundaries
+ * depending on the break argument given in construction of this
+ * WrappedPlainView. Used by the nested WrappedLine class to determine
+ * when to start the next logical line.
+ * @param p0 the start model position
+ * @param p1 the end model position
+ * @return the model position at which to break the text
+ */
+ protected int calculateBreakPosition(int p0, int p1)
+ {
+ Container c = getContainer();
+ Rectangle alloc = c.isValid() ? c.getBounds()
+ : new Rectangle(c.getPreferredSize());
+ updateMetrics();
+ try
+ {
+ getDocument().getText(p0, p1 - p0, getLineBuffer());
+ }
+ catch (BadLocationException ble)
+ {
+ // this shouldn't happen
+ }
+ // FIXME: Should we account for the insets of the container?
+ if (wordWrap)
+ return p0
+ + Utilities.getBreakLocation(lineBuffer, metrics, alloc.x,
+ alloc.x + alloc.width, this, 0);
+ else
+ {
+ return p0
+ + Utilities.getTabbedTextOffset(lineBuffer, metrics, alloc.x,
+ alloc.x + alloc.width, this, 0);
+ }
+ }
+
+ void updateMetrics()
+ {
+ Container component = getContainer();
+ metrics = component.getFontMetrics(component.getFont());
+ }
+
+ /**
+ * Determines the preferred span along the given axis. Implemented to
+ * cache the font metrics and then call the super classes method.
+ */
+ public float getPreferredSpan (int axis)
+ {
+ updateMetrics();
+ return super.getPreferredSpan(axis);
+ }
+
+ /**
+ * Determines the minimum span along the given axis. Implemented to
+ * cache the font metrics and then call the super classes method.
+ */
+ public float getMinimumSpan (int axis)
+ {
+ updateMetrics();
+ return super.getMinimumSpan(axis);
+ }
+
+ /**
+ * Determines the maximum span along the given axis. Implemented to
+ * cache the font metrics and then call the super classes method.
+ */
+ public float getMaximumSpan (int axis)
+ {
+ updateMetrics();
+ return super.getMaximumSpan(axis);
+ }
+
+ /**
+ * Called when something was inserted. Overridden so that
+ * the view factory creates WrappedLine views.
+ */
+ public void insertUpdate (DocumentEvent e, Shape a, ViewFactory f)
+ {
+ super.insertUpdate(e, a, viewFactory);
+ // FIXME: could improve performance by repainting only the necessary area
+ getContainer().repaint();
+ }
+
+ /**
+ * Called when something is removed. Overridden so that
+ * the view factory creates WrappedLine views.
+ */
+ public void removeUpdate (DocumentEvent e, Shape a, ViewFactory f)
+ {
+ super.removeUpdate(e, a, viewFactory);
+ // FIXME: could improve performance by repainting only the necessary area
+ getContainer().repaint();
+ }
+
+ /**
+ * Called when the portion of the Document that this View is responsible
+ * for changes. Overridden so that the view factory creates
+ * WrappedLine views.
+ */
+ public void changedUpdate (DocumentEvent e, Shape a, ViewFactory f)
+ {
+ super.changedUpdate(e, a, viewFactory);
+ // FIXME: could improve performance by repainting only the necessary area
+ getContainer().repaint();
+ }
+
+ class WrappedLineCreator implements ViewFactory
+ {
+ // Creates a new WrappedLine
+ public View create(Element elem)
+ {
+ return new WrappedLine(elem);
+ }
+ }
+
+ /**
+ * Renders the <code>Element</code> that is associated with this
+ * <code>View</code>. Caches the metrics and then calls
+ * super.paint to paint all the child views.
+ *
+ * @param g the <code>Graphics</code> context to render to
+ * @param a the allocated region for the <code>Element</code>
+ */
+ public void paint(Graphics g, Shape a)
+ {
+ JTextComponent comp = (JTextComponent)getContainer();
+ selectionStart = comp.getSelectionStart();
+ selectionEnd = comp.getSelectionEnd();
+ updateMetrics();
+ super.paint(g, a);
+ }
+
+ /**
+ * Sets the size of the View. Implemented to update the metrics
+ * and then call super method.
+ */
+ public void setSize (float width, float height)
+ {
+ updateMetrics();
+ if (width != getWidth())
+ preferenceChanged(null, true, true);
+ super.setSize(width, height);
+ }
+
+ class WrappedLine extends View
+ {
+ /** Used to cache the number of lines for this View **/
+ int numLines;
+
+ public WrappedLine(Element elem)
+ {
+ super(elem);
+ determineNumLines();
+ }
+
+ /**
+ * Renders this (possibly wrapped) line using the given Graphics object
+ * and on the given rendering surface.
+ */
+ public void paint(Graphics g, Shape s)
+ {
+ // Ensure metrics are up-to-date.
+ updateMetrics();
+ JTextComponent textComponent = (JTextComponent) getContainer();
+
+ g.setFont(textComponent.getFont());
+ selectedColor = textComponent.getSelectedTextColor();
+ unselectedColor = textComponent.getForeground();
+ disabledColor = textComponent.getDisabledTextColor();
+
+ // FIXME: this is a hack, for some reason textComponent.getSelectedColor
+ // was returning black, which is not visible against a black background
+ selectedColor = Color.WHITE;
+
+ Rectangle rect = s.getBounds();
+ int lineHeight = metrics.getHeight();
+
+ int end = getEndOffset();
+ int currStart = getStartOffset();
+ int currEnd;
+ while (currStart < end)
+ {
+ currEnd = calculateBreakPosition(currStart, end);
+ drawLine(currStart, currEnd, g, rect.x, rect.y);
+ rect.y += lineHeight;
+ if (currEnd == currStart)
+ currStart ++;
+ else
+ currStart = currEnd;
+ }
+ }
+
+ /**
+ * Determines the number of logical lines that the Element
+ * needs to be displayed
+ * @return the number of lines needed to display the Element
+ */
+ int determineNumLines()
+ {
+ numLines = 0;
+ int end = getEndOffset();
+ if (end == 0)
+ return 0;
+
+ int breakPoint;
+ for (int i = getStartOffset(); i < end;)
+ {
+ numLines ++;
+ // careful: check that there's no off-by-one problem here
+ // depending on which position calculateBreakPosition returns
+ breakPoint = calculateBreakPosition(i, end);
+ if (breakPoint == i)
+ i ++;
+ else
+ i = breakPoint;
+ }
+ return numLines;
+ }
+
+ /**
+ * Determines the preferred span for this view along the given axis.
+ *
+ * @param axis the axis (either X_AXIS or Y_AXIS)
+ *
+ * @return the preferred span along the given axis.
+ * @throws IllegalArgumentException if axis is not X_AXIS or Y_AXIS
+ */
+ public float getPreferredSpan(int axis)
+ {
+ if (axis == X_AXIS)
+ return getWidth();
+ else if (axis == Y_AXIS)
+ return numLines * metrics.getHeight();
+
+ throw new IllegalArgumentException("Invalid axis for getPreferredSpan: "
+ + axis);
+ }
+
+ /**
+ * Provides a mapping from model space to view space.
+ *
+ * @param pos the position in the model
+ * @param a the region into which the view is rendered
+ * @param b the position bias (forward or backward)
+ *
+ * @return a box in view space that represents the given position
+ * in model space
+ * @throws BadLocationException if the given model position is invalid
+ */
+ public Shape modelToView(int pos, Shape a, Bias b)
+ throws BadLocationException
+ {
+ Segment s = getLineBuffer();
+ int lineHeight = metrics.getHeight();
+ Rectangle rect = a.getBounds();
+
+ // Return a rectangle with width 1 and height equal to the height
+ // of the text
+ rect.height = lineHeight;
+ rect.width = 1;
+
+ int currLineStart = getStartOffset();
+ int end = getEndOffset();
+
+ if (pos < currLineStart || pos >= end)
+ throw new BadLocationException("invalid offset", pos);
+
+ while (true)
+ {
+ int currLineEnd = calculateBreakPosition(currLineStart, end);
+ // If pos is between currLineStart and currLineEnd then just find
+ // the width of the text from currLineStart to pos and add that
+ // to rect.x
+ if (pos >= currLineStart && pos < currLineEnd || pos == end - 1)
+ {
+ try
+ {
+ getDocument().getText(currLineStart, pos - currLineStart, s);
+ }
+ catch (BadLocationException ble)
+ {
+ // Shouldn't happen
+ }
+ rect.x += Utilities.getTabbedTextWidth(s, metrics, rect.x,
+ WrappedPlainView.this,
+ currLineStart);
+ return rect;
+ }
+ // Increment rect.y so we're checking the next logical line
+ rect.y += lineHeight;
+
+ // Increment currLineStart to the model position of the start
+ // of the next logical line
+ if (currLineEnd == currLineStart)
+ currLineStart = end;
+ else
+ currLineStart = currLineEnd;
+ }
+
+ }
+
+ /**
+ * Provides a mapping from view space to model space.
+ *
+ * @param x the x coordinate in view space
+ * @param y the y coordinate in view space
+ * @param a the region into which the view is rendered
+ * @param b the position bias (forward or backward)
+ *
+ * @return the location in the model that best represents the
+ * given point in view space
+ */
+ public int viewToModel(float x, float y, Shape a, Bias[] b)
+ {
+ Segment s = getLineBuffer();
+ Rectangle rect = a.getBounds();
+ int currLineStart = getStartOffset();
+ int end = getEndOffset();
+ int lineHeight = metrics.getHeight();
+ if (y < rect.y)
+ return currLineStart;
+ if (y > rect.y + rect.height)
+ return end - 1;
+
+ while (true)
+ {
+ int currLineEnd = calculateBreakPosition(currLineStart, end);
+ // If we're at the right y-position that means we're on the right
+ // logical line and we should look for the character
+ if (y >= rect.y && y < rect.y + lineHeight)
+ {
+ // Check if the x position is to the left or right of the text
+ if (x < rect.x)
+ return currLineStart;
+ if (x > rect.x + rect.width)
+ return currLineEnd - 1;
+
+ try
+ {
+ getDocument().getText(currLineStart, end - currLineStart, s);
+ }
+ catch (BadLocationException ble)
+ {
+ // Shouldn't happen
+ }
+ int mark = Utilities.getTabbedTextOffset(s, metrics, rect.x,
+ (int) x,
+ WrappedPlainView.this,
+ currLineStart);
+ return currLineStart + mark;
+ }
+ // Increment rect.y so we're checking the next logical line
+ rect.y += lineHeight;
+
+ // Increment currLineStart to the model position of the start
+ // of the next logical line
+ if (currLineEnd == currLineStart)
+ currLineStart = end;
+ else
+ currLineStart = currLineEnd;
+ }
+ }
+
+ /**
+ * Returns the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction <code>d</code>.
+ *
+ * @param c the text component
+ * @param pos the document position
+ * @param b the bias for <code>pos</code>
+ * @param d the direction, must be either {@link SwingConstants#NORTH},
+ * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
+ * {@link SwingConstants#EAST}
+ * @param biasRet an array of {@link Position.Bias} that can hold at least
+ * one element, which is filled with the bias of the return position
+ * on method exit
+ *
+ * @return the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction
+ * <code>d</code>
+ *
+ * @throws BadLocationException if <code>pos</code> is not a valid offset
+ * in the document model
+ */
+ public int getNextVisualPositionFrom(JTextComponent c, int pos,
+ Position.Bias b, int d,
+ Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ // TODO: Implement this properly.
+ throw new AssertionError("Not implemented yet.");
+ }
+
+ /**
+ * This method is called from insertUpdate and removeUpdate.
+ * If the number of lines in the document has changed, just repaint
+ * the whole thing (note, could improve performance by not repainting
+ * anything above the changes). If the number of lines hasn't changed,
+ * just repaint the given Rectangle.
+ * @param a the Rectangle to repaint if the number of lines hasn't changed
+ */
+ void updateDamage (Rectangle a)
+ {
+ int newNumLines = determineNumLines();
+ if (numLines != newNumLines)
+ {
+ numLines = newNumLines;
+ getContainer().repaint();
+ }
+ else
+ getContainer().repaint(a.x, a.y, a.width, a.height);
+ }
+
+ /**
+ * This method is called when something is inserted into the Document
+ * that this View is displaying.
+ *
+ * @param changes the DocumentEvent for the changes.
+ * @param a the allocation of the View
+ * @param f the ViewFactory used to rebuild
+ */
+ public void insertUpdate (DocumentEvent changes, Shape a, ViewFactory f)
+ {
+ updateDamage((Rectangle)a);
+ }
+
+ /**
+ * This method is called when something is removed from the Document
+ * that this View is displaying.
+ *
+ * @param changes the DocumentEvent for the changes.
+ * @param a the allocation of the View
+ * @param f the ViewFactory used to rebuild
+ */
+ public void removeUpdate (DocumentEvent changes, Shape a, ViewFactory f)
+ {
+ updateDamage((Rectangle)a);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/CSS.java b/libjava/classpath/javax/swing/text/html/CSS.java
new file mode 100644
index 00000000000..029ad26f8a9
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/CSS.java
@@ -0,0 +1,461 @@
+/* CSS.java -- Provides CSS attributes
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package javax.swing.text.html;
+
+import java.util.HashMap;
+
+/**
+ * Provides CSS attributes to be used by the HTML view classes. The constants
+ * defined here are used as keys for text attributes for use in
+ * {@link javax.swing.text.AttributeSet}s of {@link javax.swing.text.Element}s.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class CSS
+{
+ /**
+ * Returns an array of all CSS attributes.
+ *
+ * @return All available CSS.Attribute objects.
+ */
+ public static CSS.Attribute[] getAllAttributeKeys()
+ {
+ Object[] src = Attribute.attributeMap.values().toArray();
+ CSS.Attribute[] dst = new CSS.Attribute[ src.length ];
+ System.arraycopy(src, 0, dst, 0, src.length);
+ return dst;
+ }
+
+ /**
+ * Returns an a given CSS attribute.
+ *
+ * @param name - The name of the attribute.
+ * @return The CSS attribute with the given name, or <code>null</code> if
+ * no attribute with that name exists.
+ */
+ public static CSS.Attribute getAttribute(String name)
+ {
+ return (CSS.Attribute)Attribute.attributeMap.get( name );
+ }
+
+ public static final class Attribute
+ {
+ /**
+ * The CSS attribute 'background'.
+ */
+ public static final Attribute BACKGROUND =
+ new Attribute("background", false, null);
+
+ /**
+ * The CSS attribute 'background-attachment'.
+ */
+ public static final Attribute BACKGROUND_ATTACHMENT =
+ new Attribute("background-attachment", false, "scroll");
+
+ /**
+ * The CSS attribute 'background-color'.
+ */
+ public static final Attribute BACKGROUND_COLOR =
+ new Attribute("background-color", false, "transparent");
+
+ /**
+ * The CSS attribute 'background-image'.
+ */
+ public static final Attribute BACKGROUND_IMAGE =
+ new Attribute("background-image", false, "none");
+
+ /**
+ * The CSS attribute 'background-position'.
+ */
+ public static final Attribute BACKGROUND_POSITION =
+ new Attribute("background-position", false, null);
+
+ /**
+ * The CSS attribute 'background-repeat'.
+ */
+ public static final Attribute BACKGROUND_REPEAT =
+ new Attribute("background-repeat", false, "repeat");
+
+ /**
+ * The CSS attribute 'border'.
+ */
+ public static final Attribute BORDER = new Attribute("border", false, null);
+
+ /**
+ * The CSS attribute 'border-bottom'.
+ */
+ public static final Attribute BORDER_BOTTOM =
+ new Attribute("border-bottom", false, null);
+
+ /**
+ * The CSS attribute 'border-bottom-width'.
+ */
+ public static final Attribute BORDER_BOTTOM_WIDTH =
+ new Attribute("border-bottom-width", false, "medium");
+
+ /**
+ * The CSS attribute 'border-color'.
+ */
+ public static final Attribute BORDER_COLOR =
+ new Attribute("border-color", false, "black");
+
+ /**
+ * The CSS attribute 'border-left'.
+ */
+ public static final Attribute BORDER_LEFT =
+ new Attribute("border-left", false, null);
+
+ /**
+ * The CSS attribute 'border-left-width'.
+ */
+ public static final Attribute BORDER_LEFT_WIDTH =
+ new Attribute("border-left-width", false, "medium");
+
+ /**
+ * The CSS attribute 'border-right'.
+ */
+ public static final Attribute BORDER_RIGHT =
+ new Attribute("border-right", false, null);
+
+ /**
+ * The CSS attribute 'border-right-width'.
+ */
+ public static final Attribute BORDER_RIGHT_WIDTH =
+ new Attribute("border-right-width", false, "medium");
+
+ /**
+ * The CSS attribute 'border-style'.
+ */
+ public static final Attribute BORDER_STYLE =
+ new Attribute("border-style", false, "none");
+
+ /**
+ * The CSS attribute 'border-top'.
+ */
+ public static final Attribute BORDER_TOP =
+ new Attribute("border-top", false, null);
+
+ /**
+ * The CSS attribute 'border-top-width'.
+ */
+ public static final Attribute BORDER_TOP_WIDTH =
+ new Attribute("border-top-width", false, "medium");
+
+ /**
+ * The CSS attribute 'border-width'.
+ */
+ public static final Attribute BORDER_WIDTH =
+ new Attribute("border-width", false, "medium");
+
+ /**
+ * The CSS attribute 'clear'.
+ */
+ public static final Attribute CLEAR = new Attribute("clear", false, "none");
+
+ /**
+ * The CSS attribute 'color'.
+ */
+ public static final Attribute COLOR = new Attribute("color", true, "black");
+
+ /**
+ * The CSS attribute 'display'.
+ */
+ public static final Attribute DISPLAY =
+ new Attribute("display", false, "block");
+
+ /**
+ * The CSS attribute 'float'.
+ */
+ public static final Attribute FLOAT = new Attribute("float", false, "none");
+
+ /**
+ * The CSS attribute 'font'.
+ */
+ public static final Attribute FONT = new Attribute("font", true, null);
+
+ /**
+ * The CSS attribute 'font-family'.
+ */
+ public static final Attribute FONT_FAMILY =
+ new Attribute("font-family", true, null);
+
+ /**
+ * The CSS attribute 'font-size'.
+ */
+ public static final Attribute FONT_SIZE =
+ new Attribute("font-size", true, "medium");
+
+ /**
+ * The CSS attribute 'font-style'.
+ */
+ public static final Attribute FONT_STYLE =
+ new Attribute("font-style", true, "normal");
+
+ /**
+ * The CSS attribute 'font-variant'.
+ */
+ public static final Attribute FONT_VARIANT =
+ new Attribute("font-variant", true, "normal");
+
+ /**
+ * The CSS attribute 'font-weight'.
+ */
+ public static final Attribute FONT_WEIGHT =
+ new Attribute("font-weight", true, "normal");
+
+ /**
+ * The CSS attribute 'height'.
+ */
+ public static final Attribute HEIGHT =
+ new Attribute("height", false, "auto");
+
+ /**
+ * The CSS attribute 'letter-spacing'.
+ */
+ public static final Attribute LETTER_SPACING =
+ new Attribute("letter-spacing", true, "normal");
+
+ /**
+ * The CSS attribute 'line-height'.
+ */
+ public static final Attribute LINE_HEIGHT =
+ new Attribute("line-height", true, "normal");
+
+ /**
+ * The CSS attribute 'list-style'.
+ */
+ public static final Attribute LIST_STYLE =
+ new Attribute("list-style", true, null);
+
+ /**
+ * The CSS attribute 'list-style-image'.
+ */
+ public static final Attribute LIST_STYLE_IMAGE =
+ new Attribute("list-style-image", true, "none");
+
+ /**
+ * The CSS attribute 'list-style-position'.
+ */
+ public static final Attribute LIST_STYLE_POSITION =
+ new Attribute("list-style-position", true, "outside");
+
+ /**
+ * The CSS attribute 'list-style-type'.
+ */
+ public static final Attribute LIST_STYLE_TYPE =
+ new Attribute("list-style-type", true, "disc");
+
+ /**
+ * The CSS attribute 'margin'.
+ */
+ public static final Attribute MARGIN = new Attribute("margin", false, null);
+
+ /**
+ * The CSS attribute 'margin-bottom'.
+ */
+ public static final Attribute MARGIN_BOTTOM =
+ new Attribute("margin-bottom", false, "0");
+
+ /**
+ * The CSS attribute 'margin-left'.
+ */
+ public static final Attribute MARGIN_LEFT =
+ new Attribute("margin-left", false, "0");
+
+ /**
+ * The CSS attribute 'margin-right'.
+ */
+ public static final Attribute MARGIN_RIGHT =
+ new Attribute("margin-right", false, "0");
+
+ /**
+ * The CSS attribute 'margin-top'.
+ */
+ public static final Attribute MARGIN_TOP =
+ new Attribute("margin-top", false, "0");
+
+ /**
+ * The CSS attribute 'padding'.
+ */
+ public static final Attribute PADDING =
+ new Attribute("padding", false, null);
+
+ /**
+ * The CSS attribute 'padding-bottom'.
+ */
+ public static final Attribute PADDING_BOTTOM =
+ new Attribute("padding-bottom", false, "0");
+
+ /**
+ * The CSS attribute 'padding-left'.
+ */
+ public static final Attribute PADDING_LEFT =
+ new Attribute("padding-left", false, "0");
+
+ /**
+ * The CSS attribute 'padding-right'.
+ */
+ public static final Attribute PADDING_RIGHT =
+ new Attribute("padding-right", false, "0");
+
+ /**
+ * The CSS attribute 'padding-top'.
+ */
+ public static final Attribute PADDING_TOP =
+ new Attribute("padding-top", false, "0");
+
+ /**
+ * The CSS attribute 'text-align'.
+ */
+ public static final Attribute TEXT_ALIGN =
+ new Attribute("text-align", true, null);
+
+ /**
+ * The CSS attribute 'text-decoration'.
+ */
+ public static final Attribute TEXT_DECORATION =
+ new Attribute("text-decoration", true, "none");
+
+ /**
+ * The CSS attribute 'text-indent'.
+ */
+ public static final Attribute TEXT_INDENT =
+ new Attribute("text-indent", true, "0");
+
+ /**
+ * The CSS attribute 'text-transform'.
+ */
+ public static final Attribute TEXT_TRANSFORM =
+ new Attribute("text-transform", true, "none");
+
+ /**
+ * The CSS attribute 'vertical-align'.
+ */
+ public static final Attribute VERTICAL_ALIGN =
+ new Attribute("vertical-align", false, "baseline");
+
+ /**
+ * The CSS attribute 'white-space'.
+ */
+ public static final Attribute WHITE_SPACE =
+ new Attribute("white-space", true, "normal");
+
+ /**
+ * The CSS attribute 'width'.
+ */
+ public static final Attribute WIDTH =
+ new Attribute("width", false, "auto");
+
+ /**
+ * The CSS attribute 'word-spacing'.
+ */
+ public static final Attribute WORD_SPACING =
+ new Attribute("word-spacing", true, "normal");
+
+ /**
+ * The attribute string.
+ */
+ String attStr;
+
+ /**
+ * Indicates if this attribute should be inherited from it's parent or
+ * not.
+ */
+ boolean isInherited;
+
+ /**
+ * A default value for this attribute if one exists, otherwise null.
+ */
+ String defaultValue;
+
+ /**
+ * A HashMap of all attributes.
+ */
+ static HashMap attributeMap;
+
+ /**
+ * Creates a new Attribute instance with the specified values.
+ *
+ * @param attr the attribute string
+ * @param inherited if the attribute should be inherited or not
+ * @param def a default value; may be <code>null</code>
+ */
+ Attribute(String attr, boolean inherited, String def)
+ {
+ attStr = attr;
+ isInherited = inherited;
+ defaultValue = def;
+ if( attributeMap == null)
+ attributeMap = new HashMap();
+ attributeMap.put( attr, this );
+ }
+
+ /**
+ * Returns the string representation of this attribute as specified
+ * in the CSS specification.
+ */
+ public String toString()
+ {
+ return attStr;
+ }
+
+ /**
+ * Returns <code>true</code> if the attribute should be inherited from
+ * the parent, <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if the attribute should be inherited from
+ * the parent, <code>false</code> otherwise
+ */
+ public boolean isInherited()
+ {
+ return isInherited;
+ }
+
+ /**
+ * Returns the default value of this attribute if one exists,
+ * <code>null</code> otherwise.
+ *
+ * @return the default value of this attribute if one exists,
+ * <code>null</code> otherwise
+ */
+ public String getDefaultValue()
+ {
+ return defaultValue;
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/text/html/HTML.java b/libjava/classpath/javax/swing/text/html/HTML.java
index 3c03a63a471..0b758d2b873 100644
--- a/libjava/classpath/javax/swing/text/html/HTML.java
+++ b/libjava/classpath/javax/swing/text/html/HTML.java
@@ -945,22 +945,22 @@ public class HTML
* This tag is not included into the array, returned by getAllTags().
* toString() returns 'comment'. HTML reader synthesizes this tag.
*/
- public static final Tag COMMENT = new Tag("comment", SYNTETIC);
+ public static final Tag COMMENT = new Tag("comment", SYNTHETIC);
/**
* All text content is labeled with this tag.
* This tag is not included into the array, returned by getAllTags().
* toString() returns 'content'. HTML reader synthesizes this tag.
*/
- public static final Tag CONTENT = new Tag("content", SYNTETIC);
+ public static final Tag CONTENT = new Tag("content", SYNTHETIC);
/**
* All text content must be in a paragraph element.
* If a paragraph didn't exist when content was encountered,
* a paragraph is manufactured.
- * toString() returns 'implied'. HTML reader synthesizes this tag.
+ * toString() returns 'p-implied'. HTML reader synthesizes this tag.
*/
- public static final Tag IMPLIED = new Tag("implied", SYNTETIC);
+ public static final Tag IMPLIED = new Tag("p-implied", SYNTHETIC);
final String name;
final int flags;
@@ -1144,7 +1144,7 @@ public class HTML
*/
boolean isSyntetic()
{
- return (flags & SYNTETIC) != 0;
+ return (flags & SYNTHETIC) != 0;
}
private static void unexpected(Exception ex)
@@ -1185,7 +1185,7 @@ public class HTML
static final int BREAKS = 1;
static final int BLOCK = 2;
static final int PREFORMATTED = 4;
- static final int SYNTETIC = 8;
+ static final int SYNTHETIC = 8;
private static Map tagMap;
private static Map attrMap;
@@ -1196,6 +1196,7 @@ public class HTML
*/
public HTML()
{
+ // Nothing to do here.
}
/**
diff --git a/libjava/classpath/javax/swing/text/html/HTMLDocument.java b/libjava/classpath/javax/swing/text/html/HTMLDocument.java
index a95e496ec4d..d048a04e614 100644
--- a/libjava/classpath/javax/swing/text/html/HTMLDocument.java
+++ b/libjava/classpath/javax/swing/text/html/HTMLDocument.java
@@ -38,7 +38,14 @@ exception statement from your version. */
package javax.swing.text.html;
+import java.net.URL;
+
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.AttributeSet;
import javax.swing.text.DefaultStyledDocument;
+import javax.swing.text.Element;
+import javax.swing.text.ElementIterator;
+import javax.swing.text.html.HTML.Tag;
/**
* TODO: This class is not yet completetely implemented.
@@ -47,7 +54,215 @@ import javax.swing.text.DefaultStyledDocument;
*/
public class HTMLDocument extends DefaultStyledDocument
{
+ /** A key for document properies. The value for the key is
+ * a Vector of Strings of comments not found in the body.
+ */
+ public static final String AdditionalComments = "AdditionalComments";
+ URL baseURL = null;
+ boolean preservesUnknownTags = true;
+
+ /**
+ * Returns the location against which to resolve relative URLs.
+ * This is the document's URL if the document was loaded from a URL.
+ * If a <code>base</code> tag is found, it will be used.
+ * @return the base URL
+ */
+ public URL getBase()
+ {
+ return baseURL;
+ }
+
+ /**
+ * Sets the location against which to resolve relative URLs.
+ * @param u the new base URL
+ */
+ public void setBase(URL u)
+ {
+ baseURL = u;
+ //TODO: also set the base of the StyleSheet
+ }
+
+ /**
+ * Returns whether or not the parser preserves unknown HTML tags.
+ * @return true if the parser preserves unknown tags
+ */
+ public boolean getPreservesUnknownTags()
+ {
+ return preservesUnknownTags;
+ }
+
+ /**
+ * Sets the behaviour of the parser when it encounters unknown HTML tags.
+ * @param preservesTags true if the parser should preserve unknown tags.
+ */
+ public void setPreservesUnknownTags(boolean preservesTags)
+ {
+ preservesUnknownTags = preservesTags;
+ }
+
+ /**
+ * An iterator to iterate through LeafElements in the document.
+ */
+ class LeafIterator extends Iterator
+ {
+ HTML.Tag tag;
+ HTMLDocument doc;
+ ElementIterator it;
+
+ public LeafIterator (HTML.Tag t, HTMLDocument d)
+ {
+ doc = d;
+ tag = t;
+ it = new ElementIterator(doc);
+ }
+
+ /**
+ * Return the attributes for the tag associated with this iteartor
+ * @return the AttributeSet
+ */
+ public AttributeSet getAttributes()
+ {
+ if (it.current() != null)
+ return it.current().getAttributes();
+ return null;
+ }
+
+ /**
+ * Get the end of the range for the current occurrence of the tag
+ * being defined and having the same attributes.
+ * @return the end of the range
+ */
+ public int getEndOffset()
+ {
+ if (it.current() != null)
+ return it.current().getEndOffset();
+ return -1;
+ }
+
+ /**
+ * Get the start of the range for the current occurrence of the tag
+ * being defined and having the same attributes.
+ * @return the start of the range (-1 if it can't be found).
+ */
+
+ public int getStartOffset()
+ {
+ if (it.current() != null)
+ return it.current().getStartOffset();
+ return -1;
+ }
+
+ /**
+ * Advance the iterator to the next LeafElement .
+ */
+ public void next()
+ {
+ it.next();
+ while (it.current()!= null && !it.current().isLeaf())
+ it.next();
+ }
+
+ /**
+ * Indicates whether or not the iterator currently represents an occurrence
+ * of the tag.
+ * @return true if the iterator currently represents an occurrence of the
+ * tag.
+ */
+ public boolean isValid()
+ {
+ return it.current() != null;
+ }
+
+ /**
+ * Type of tag for this iterator.
+ */
+ public Tag getTag()
+ {
+ return tag;
+ }
+
+ }
+
public void processHTMLFrameHyperlinkEvent(HTMLFrameHyperlinkEvent event)
{
+ // TODO: Implement this properly.
+ }
+
+ /**
+ * Gets an iterator for the given HTML.Tag.
+ * @param t the requested HTML.Tag
+ * @return the Iterator
+ */
+ public HTMLDocument.Iterator getIterator (HTML.Tag t)
+ {
+ return new HTMLDocument.LeafIterator(t, this);
+ }
+
+ /**
+ * An iterator over a particular type of tag.
+ */
+ public abstract static class Iterator
+ {
+ /**
+ * Return the attribute set for this tag.
+ * @return the <code>AttributeSet</code> (null if none found).
+ */
+ public abstract AttributeSet getAttributes();
+
+ /**
+ * Get the end of the range for the current occurrence of the tag
+ * being defined and having the same attributes.
+ * @return the end of the range
+ */
+ public abstract int getEndOffset();
+
+ /**
+ * Get the start of the range for the current occurrence of the tag
+ * being defined and having the same attributes.
+ * @return the start of the range (-1 if it can't be found).
+ */
+ public abstract int getStartOffset();
+
+ /**
+ * Move the iterator forward.
+ */
+ public abstract void next();
+
+ /**
+ * Indicates whether or not the iterator currently represents an occurrence
+ * of the tag.
+ * @return true if the iterator currently represents an occurrence of the
+ * tag.
+ */
+ public abstract boolean isValid();
+
+ /**
+ * Type of tag this iterator represents.
+ * @return the tag.
+ */
+ public abstract HTML.Tag getTag();
+ }
+
+ public class BlockElement extends AbstractDocument.BranchElement
+ {
+ public BlockElement (Element parent, AttributeSet a)
+ {
+ super (parent, a);
+ }
+
+ /**
+ * Gets the resolving parent. Since HTML attributes are not
+ * inherited at the model level, this returns null.
+ */
+ public AttributeSet getResolveParent()
+ {
+ return null;
+ }
+
+ public String getName()
+ {
+ //FIXME: this is supposed to do something different from the super class
+ return super.getName();
+ }
}
}
diff --git a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java
index c0182fe6ac9..5189c777539 100644
--- a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java
+++ b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java
@@ -43,8 +43,10 @@ import java.io.Reader;
import java.io.Serializable;
import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.StyledEditorKit;
+import javax.swing.text.html.parser.ParserDelegator;
/**
* This class is NOT implemented. This file currently holds only
@@ -96,9 +98,9 @@ public class HTMLEditorKit
/**
* The parser calls this method after it finishes parsing the document.
*/
- public void flush()
- throws BadLocationException
+ public void flush() throws BadLocationException
{
+ // TODO: What to do here, if anything?
}
/**
@@ -108,6 +110,7 @@ public class HTMLEditorKit
*/
public void handleComment(char[] comment, int position)
{
+ // TODO: What to do here, if anything?
}
/**
@@ -118,6 +121,7 @@ public class HTMLEditorKit
*/
public void handleEndOfLineString(String end_of_line)
{
+ // TODO: What to do here, if anything?
}
/**
@@ -129,6 +133,7 @@ public class HTMLEditorKit
*/
public void handleEndTag(HTML.Tag tag, int position)
{
+ // TODO: What to do here, if anything?
}
/**
@@ -139,6 +144,7 @@ public class HTMLEditorKit
*/
public void handleError(String message, int position)
{
+ // TODO: What to do here, if anything?
}
/**
@@ -149,9 +155,9 @@ public class HTMLEditorKit
* @param position The tag position in the text being parsed.
*/
public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet attributes,
- int position
- )
+ int position)
{
+ // TODO: What to do here, if anything?
}
/**
@@ -165,6 +171,7 @@ public class HTMLEditorKit
int position
)
{
+ // TODO: What to do here, if anything?
}
/**
@@ -174,6 +181,7 @@ public class HTMLEditorKit
*/
public void handleText(char[] text, int position)
{
+ // TODO: What to do here, if anything?
}
}
@@ -247,4 +255,26 @@ public class HTMLEditorKit
* The "ident paragraph right" action.
*/
public static final String PARA_INDENT_RIGHT = "html-para-indent-right";
+
+ /**
+ * Create a text storage model for this type of editor.
+ *
+ * @return the model
+ */
+ public Document createDefaultDocument()
+ {
+ HTMLDocument document = new HTMLDocument();
+ return document;
+ }
+
+ /**
+ * Get the parser that this editor kit uses for reading HTML streams. This
+ * method can be overridden to use the alternative parser.
+ *
+ * @return the HTML parser (by default, {@link ParserDelegator}).
+ */
+ protected Parser getParser()
+ {
+ return new ParserDelegator();
+ }
} \ No newline at end of file
diff --git a/libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java b/libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java
index dc0ab10a8f7..e146965d778 100644
--- a/libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java
+++ b/libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java
@@ -41,7 +41,6 @@ package javax.swing.text.html;
import java.net.URL;
import javax.swing.event.HyperlinkEvent;
-import javax.swing.event.HyperlinkEvent.EventType;
import javax.swing.text.Element;
/**
@@ -50,8 +49,7 @@ import javax.swing.text.Element;
*
* @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org)
*/
-public class HTMLFrameHyperlinkEvent
- extends HyperlinkEvent
+public class HTMLFrameHyperlinkEvent extends HyperlinkEvent
{
private final String target_frame;
diff --git a/libjava/classpath/javax/swing/text/html/parser/ContentModel.java b/libjava/classpath/javax/swing/text/html/parser/ContentModel.java
index deb7b1602bb..70e9c2acbff 100644
--- a/libjava/classpath/javax/swing/text/html/parser/ContentModel.java
+++ b/libjava/classpath/javax/swing/text/html/parser/ContentModel.java
@@ -95,9 +95,12 @@ public final class ContentModel
*/
public int type;
- /** Create a content model initializing all fields to default values. */
+ /**
+ * Create a content model initializing all fields to default values.
+ */
public ContentModel()
{
+ // Nothing to do here.
}
/**
diff --git a/libjava/classpath/javax/swing/text/html/parser/DTD.java b/libjava/classpath/javax/swing/text/html/parser/DTD.java
index f17ca011ea0..16bc5b0d6af 100644
--- a/libjava/classpath/javax/swing/text/html/parser/DTD.java
+++ b/libjava/classpath/javax/swing/text/html/parser/DTD.java
@@ -81,8 +81,9 @@ public class DTD
{
/**
* The version of the persistent data format.
+ * @specnote This was made <code>final</code> in 1.5.
*/
- public static int FILE_VERSION = 1;
+ public static final int FILE_VERSION = 1;
/**
* The table of existing available DTDs.
@@ -590,8 +591,7 @@ public class DTD
* @param name the name of the entity
* @param type the type of the entity, a bitwise combination
* of GENERAL, PARAMETER, SYSTEM and PUBLIC.
- * @throws an error if the parameter is both GENERAL and PARAMETER
- * of both PUBLIC and SYSTEM.
+ *
* @return the created entity
*/
private Entity newEntity(String name, int type)
diff --git a/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java b/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java
index 164297f1882..062606d17ba 100644
--- a/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java
+++ b/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java
@@ -168,6 +168,7 @@ public class DocumentParser
* specific packages, write your own DTD or obtain the working instance
* of parser in other way, for example, by calling
* {@link javax.swing.text.html.HTMLEditorKit#getParser()}.
+ *
* @param a_dtd a DTD to use.
*/
public DocumentParser(DTD a_dtd)
@@ -212,6 +213,7 @@ public class DocumentParser
*/
protected void handleComment(char[] comment)
{
+ // This default implementation does nothing.
}
/**
@@ -224,6 +226,7 @@ public class DocumentParser
protected void handleEmptyTag(TagElement tag)
throws javax.swing.text.ChangedCharSetException
{
+ // This default implementation does nothing.
}
/**
@@ -234,11 +237,13 @@ public class DocumentParser
*/
protected void handleEndTag(TagElement tag)
{
+ // This default implementation does nothing.
}
/* Handle error that has occured in the given line. */
protected void handleError(int line, String message)
{
+ // This default implementation does nothing.
}
/**
@@ -249,6 +254,7 @@ public class DocumentParser
*/
protected void handleStartTag(TagElement tag)
{
+ // This default implementation does nothing.
}
/**
@@ -257,5 +263,6 @@ public class DocumentParser
*/
protected void handleText(char[] text)
{
+ // This default implementation does nothing.
}
}
diff --git a/libjava/classpath/javax/swing/text/html/parser/Element.java b/libjava/classpath/javax/swing/text/html/parser/Element.java
index 098983c6923..c07c07f5426 100644
--- a/libjava/classpath/javax/swing/text/html/parser/Element.java
+++ b/libjava/classpath/javax/swing/text/html/parser/Element.java
@@ -148,10 +148,10 @@ public final class Element
/**
* The default constructor must have package level access in this
* class. Use DTD.defineElement(..) to create an element when required.
- * @todo MAKE THIS PACKAGE in the final version. Now the Parser needs it!
*/
Element()
{
+ // Nothing to do here.
}
/**
diff --git a/libjava/classpath/javax/swing/text/html/parser/Parser.java b/libjava/classpath/javax/swing/text/html/parser/Parser.java
index 7ff6853da82..a88e9ce1953 100644
--- a/libjava/classpath/javax/swing/text/html/parser/Parser.java
+++ b/libjava/classpath/javax/swing/text/html/parser/Parser.java
@@ -256,6 +256,7 @@ public class Parser
*/
protected void endTag(boolean omitted)
{
+ // This default implementation does nothing.
}
/**
@@ -310,6 +311,7 @@ public class Parser
*/
protected void handleComment(char[] comment)
{
+ // This default implementation does nothing.
}
/**
@@ -333,6 +335,7 @@ public class Parser
protected void handleEmptyTag(TagElement tag)
throws ChangedCharSetException
{
+ // This default implementation does nothing.
}
/**
@@ -343,11 +346,13 @@ public class Parser
*/
protected void handleEndTag(TagElement tag)
{
+ // This default implementation does nothing.
}
/* Handle error that has occured in the given line. */
protected void handleError(int line, String message)
{
+ // This default implementation does nothing.
}
/**
@@ -358,6 +363,7 @@ public class Parser
*/
protected void handleStartTag(TagElement tag)
{
+ // This default implementation does nothing.
}
/**
@@ -376,6 +382,7 @@ public class Parser
*/
protected void handleText(char[] text)
{
+ // This default implementation does nothing.
}
/**
@@ -387,6 +394,7 @@ public class Parser
*/
protected void handleTitle(char[] title)
{
+ // This default implementation does nothing.
}
/**
@@ -420,6 +428,7 @@ public class Parser
*/
protected void markFirstTime(Element element)
{
+ // This default implementation does nothing.
}
/**
@@ -432,5 +441,6 @@ public class Parser
protected void startTag(TagElement tag)
throws ChangedCharSetException
{
+ // This default implementation does nothing.
}
}