diff options
author | Andrew John Hughes <gnu_andrew@member.fsf.org> | 2005-09-20 18:46:20 +0000 |
---|---|---|
committer | Andrew John Hughes <gnu_andrew@member.fsf.org> | 2005-09-20 18:46:20 +0000 |
commit | 3baed5090a8b63218fd8a5808cd38caeb79a0f7d (patch) | |
tree | e3d5427d27e9ef1ebb9de69a3fd3639084d196d1 /javax/swing/text | |
parent | d30cf1c6c0de86bac88773529f7071497ce18916 (diff) | |
download | classpath-3baed5090a8b63218fd8a5808cd38caeb79a0f7d.tar.gz |
2005-09-20 Andrew John Hughes <gnu_andrew@member.fsf.org>
* Merge of generics-branch for 2005/09/07 - 2005/09/20 @ 10:05am GMT.
Diffstat (limited to 'javax/swing/text')
24 files changed, 2121 insertions, 242 deletions
diff --git a/javax/swing/text/AbstractDocument.java b/javax/swing/text/AbstractDocument.java index 3c9a4d497..660cd2b09 100644 --- a/javax/swing/text/AbstractDocument.java +++ b/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. @@ -332,7 +331,7 @@ public abstract class AbstractDocument * @see GapContent * @see StringContent */ - protected Content getContent() + protected final Content getContent() { return content; } @@ -970,8 +969,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; @@ -1355,49 +1354,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 +1361,65 @@ 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); + } + 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 +1429,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]; @@ -1642,8 +1653,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; @@ -1843,8 +1854,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/javax/swing/text/BoxView.java b/javax/swing/text/BoxView.java index 0f8ba1ce1..bc08626bd 100644 --- a/javax/swing/text/BoxView.java +++ b/javax/swing/text/BoxView.java @@ -221,7 +221,6 @@ public class BoxView protected void paintChild(Graphics g, Rectangle alloc, int index) { View child = getView(index); - childAllocation(index, alloc); child.paint(g, alloc); } @@ -306,12 +305,8 @@ public class BoxView 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); + childAllocation(i, copy); paintChild(g, copy, i); } } diff --git a/javax/swing/text/ComponentView.java b/javax/swing/text/ComponentView.java index f6feda215..e280e7c0c 100644 --- a/javax/swing/text/ComponentView.java +++ b/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,32 +41,82 @@ import java.awt.Component; import java.awt.Graphics; import java.awt.Shape; +/** + * A {@link View} implementation that is able to render arbitrary + * {@link Components}. 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) + */ public class ComponentView extends View { - public ComponentView(Element elem) - { - super(elem); - } - - protected Component createComponent() - { - return null; - } - - public float getAlignment(int axis) - { - return 0; - } + /** + * 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); + } - public final Component getComponent() - { - return null; - } - - public float getMaximumSpan(int axis) - { - return 0; - } + /** + * 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) { diff --git a/javax/swing/text/CompositeView.java b/javax/swing/text/CompositeView.java index 6776c9572..b3dd6123e 100644 --- a/javax/swing/text/CompositeView.java +++ b/javax/swing/text/CompositeView.java @@ -314,6 +314,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) @@ -594,6 +595,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 +629,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; diff --git a/javax/swing/text/DefaultCaret.java b/javax/swing/text/DefaultCaret.java index 33c3ae3bf..c247afcff 100644 --- a/javax/swing/text/DefaultCaret.java +++ b/javax/swing/text/DefaultCaret.java @@ -60,10 +60,8 @@ import javax.swing.event.EventListenerList; public class DefaultCaret extends Rectangle implements Caret, FocusListener, MouseListener, MouseMotionListener { - /** - * The serial version UID for DefaultCaret. - */ - private static final long serialVersionUID = 228155774675466193L; + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 4325555698756477346L; /** * The <code>ChangeEvent</code> that is fired by {@link #fireStateChanged()}. diff --git a/javax/swing/text/DefaultEditorKit.java b/javax/swing/text/DefaultEditorKit.java index a14f3ff4f..2f901947f 100644 --- a/javax/swing/text/DefaultEditorKit.java +++ b/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 { /** @@ -243,8 +239,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 +268,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 { /** @@ -298,8 +292,7 @@ public class DefaultEditorKit extends EditorKit /** * Inserts a TAB character into the text editor. */ - public static class InsertTabAction - extends TextAction + public static class InsertTabAction extends TextAction { /** diff --git a/javax/swing/text/DefaultFormatter.java b/javax/swing/text/DefaultFormatter.java index c97d90703..16d40c24b 100644 --- a/javax/swing/text/DefaultFormatter.java +++ b/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() { @@ -186,8 +182,8 @@ public class DefaultFormatter } } - /** 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/javax/swing/text/DefaultStyledDocument.java b/javax/swing/text/DefaultStyledDocument.java index 3545e52c4..765477507 100644 --- a/javax/swing/text/DefaultStyledDocument.java +++ b/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,6 +41,7 @@ package javax.swing.text; import java.awt.Color; import java.awt.Font; import java.io.Serializable; +import java.util.Vector; import javax.swing.event.DocumentEvent; @@ -60,12 +61,278 @@ public class DefaultStyledDocument extends AbstractDocument implements StyledDocument { /** + * 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 +343,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 +394,7 @@ public class DefaultStyledDocument extends AbstractDocument { this.offset = offset; this.length = length; + documentEvent = ev; changeUpdate(); } @@ -158,7 +439,11 @@ public class DefaultStyledDocument extends AbstractDocument Element child2 = createLeafElement(par, ael, offset, endOffset); int index = par.getElementIndex(startOffset); - par.replace(index, 1, new Element[]{ child1, child2 }); + 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 " @@ -171,8 +456,267 @@ public class DefaultStyledDocument extends AbstractDocument + "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; + } + } + + /** + * An element type for sections. This is a simple BranchElement with + * a unique name. + */ + protected class SectionElement extends BranchElement + { + /** + * Creates a new SectionElement. + */ + public SectionElement() + { + super(null, null); + } + + /** + * Returns the name of the element. This method always returns + * "section". + * + * @return the name of the element + */ + public String getName() + { + return "section"; + } } + /** The serialization UID (compatible with JDK1.5). */ + private static final long serialVersionUID = 940485415728614849L; + /** * The default size to use for new content buffers. */ @@ -250,9 +794,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 +807,7 @@ public class DefaultStyledDocument extends AbstractDocument tmp = new Element[1]; tmp[0] = leaf; paragraph.replace(0, 0, tmp); - + return section; } @@ -360,8 +906,9 @@ public class DefaultStyledDocument extends AbstractDocument */ public Element getParagraphElement(int position) { - Element element = getCharacterElement(position); - return element.getParentElement(); + BranchElement root = (BranchElement) getDefaultRootElement(); + Element par = root.positionToElement(position); + return par; } /** @@ -445,6 +992,8 @@ public class DefaultStyledDocument extends AbstractDocument + "javax.swing.text.AbstractDocument.AbstractElement"); } } + + fireChangedUpdate(ev); } /** @@ -476,10 +1025,110 @@ 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) { - // FIXME: Implement me. - throw new Error("not implemented"); + 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) + { + throw new AssertionError("BadLocationException must not be thrown " + + "here."); + } + + 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); + } } diff --git a/javax/swing/text/GapContent.java b/javax/swing/text/GapContent.java index 8e408f36c..5c8f81865 100644 --- a/javax/swing/text/GapContent.java +++ b/javax/swing/text/GapContent.java @@ -39,9 +39,11 @@ 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.UndoableEdit; @@ -59,7 +61,6 @@ import javax.swing.undo.UndoableEdit; public class GapContent implements AbstractDocument.Content, Serializable { - /** * A {@link Position} implementation for <code>GapContent</code>. */ @@ -114,6 +115,11 @@ public class GapContent */ public int getOffset() { + // Check precondition. + assert mark <= gapStart || mark > gapEnd : "mark: " + mark + + ", gapStart: " + gapStart + + ", gapEnd: " + gapEnd; + if (mark <= gapEnd) return mark; else @@ -121,13 +127,14 @@ public class GapContent } } - private static final long serialVersionUID = 8374645204155842629L; + /** 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 +155,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 +173,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(); } /** @@ -386,7 +393,19 @@ 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. + Vector v = getPositionsInRange(null, gapEnd, buffer.length - gapEnd); + for (Iterator i = v.iterator(); i.hasNext();) + { + GapContentPosition p = (GapContentPosition) i.next(); + p.mark += 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 +413,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 +422,22 @@ public class GapContent */ protected void shiftGap(int newGapStart) { - int newGapEnd = newGapStart + (gapEnd - gapStart); + if (newGapStart == gapStart) + return; - // 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 newGapEnd = newGapStart + gapEnd - gapStart; + + if (newGapStart < gapStart) { - int i1 = Math.min(index1, index2); - int i2 = Math.max(index1, index2); - for (ListIterator i = positions.listIterator(i1); i.hasNext();) + // Update the positions between newGapStart and (old) gapStart. The marks + // must be shifted by (gapEnd - gapStart). + Vector v = getPositionsInRange(null, newGapStart + 1, + gapStart - newGapStart + 1); + for (Iterator i = v.iterator(); i.hasNext();) { - if (i.nextIndex() > i2) - break; - GapContentPosition p = (GapContentPosition) i.next(); - p.mark += gapEnd - newGapEnd; + p.mark += gapEnd - gapStart; } - } - - if (newGapStart == gapStart) - return; - else if (newGapStart < gapStart) - { System.arraycopy(buffer, newGapStart, buffer, newGapEnd, gapStart - newGapStart); gapStart = newGapStart; @@ -448,6 +445,15 @@ public class GapContent } else { + // Update the positions between newGapEnd and (old) gapEnd. The marks + // must be shifted by (gapEnd - gapStart). + Vector v = getPositionsInRange(null, gapEnd, + newGapEnd - gapEnd); + for (Iterator i = v.iterator(); i.hasNext();) + { + GapContentPosition p = (GapContentPosition) i.next(); + p.mark -= gapEnd - gapStart; + } System.arraycopy(buffer, gapEnd, buffer, gapStart, newGapStart - gapStart); gapStart = newGapStart; @@ -456,6 +462,54 @@ public class GapContent } /** + * 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."; + Vector v = getPositionsInRange(null, newGapStart, gapStart - newGapStart); + for (Iterator i = v.iterator(); i.hasNext();) + { + GapContentPosition p = (GapContentPosition) i.next(); + p.mark = 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."; + Vector v = getPositionsInRange(null, gapEnd, newGapEnd - gapEnd); + for (Iterator i = v.iterator(); i.hasNext();) + { + GapContentPosition p = (GapContentPosition) i.next(); + p.mark = newGapEnd + 1; + } + gapEnd = newGapEnd; + } + + /** * Returns the allocated buffer array. * * @return the allocated buffer array @@ -478,11 +532,11 @@ public class GapContent { // Remove content shiftGap(position); - gapEnd += rmSize; + 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 +545,64 @@ 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)); + int index2 = Collections.binarySearch(positions, + new GapContentPosition(endOffset)); + if (index1 < 0) + index1 = -(index1 + 1); + if (index2 < 0) + index2 = -(index2 + 1); + for (ListIterator i = positions.listIterator(index1); i.hasNext();) + { + if (i.nextIndex() > index2) + break; + + GapContentPosition p = (GapContentPosition) i.next(); + if (p.mark >= offset && p.mark <= endOffset) + res.add(p); + } + return res; + } } diff --git a/javax/swing/text/GlyphView.java b/javax/swing/text/GlyphView.java index f9e60972d..6ffab23c4 100644 --- a/javax/swing/text/GlyphView.java +++ b/javax/swing/text/GlyphView.java @@ -44,6 +44,11 @@ import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; +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 +57,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 { /** @@ -71,12 +74,43 @@ public class GlyphView } /** + * 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 +131,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 +148,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 +170,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 +180,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; + } } /** @@ -173,11 +284,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 +328,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 +365,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 * @@ -242,6 +382,85 @@ public class GlyphView 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 +469,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 +486,8 @@ public class GlyphView public GlyphView(Element element) { super(element); + startOffset = element.getStartOffset(); + endOffset = element.getEndOffset(); } /** @@ -319,16 +550,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 +608,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 +620,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; } @@ -436,15 +672,15 @@ public class GlyphView } /** - * 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 +690,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; } /** @@ -518,4 +754,302 @@ 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); + } } diff --git a/javax/swing/text/InternationalFormatter.java b/javax/swing/text/InternationalFormatter.java index cedaf59fe..86300a70d 100644 --- a/javax/swing/text/InternationalFormatter.java +++ b/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/javax/swing/text/JTextComponent.java b/javax/swing/text/JTextComponent.java index b3fad7912..63dbf2a4b 100644 --- a/javax/swing/text/JTextComponent.java +++ b/javax/swing/text/JTextComponent.java @@ -303,9 +303,7 @@ public abstract class JTextComponent extends JComponent /** * The timer that lets the caret blink. */ - private class CaretBlinkTimer - extends Timer - implements ActionListener + private class CaretBlinkTimer extends Timer implements ActionListener { /** * Creates a new CaretBlinkTimer object with a default delay of 1 second. @@ -604,8 +602,7 @@ public abstract class JTextComponent extends JComponent } } - class DefaultTransferHandler - extends TransferHandler + class DefaultTransferHandler extends TransferHandler { public boolean canImport(JComponent component, DataFlavor[] flavors) { @@ -1105,7 +1102,8 @@ public abstract class JTextComponent extends JComponent */ protected String paramString() { - return "JTextComponent"; + // TODO: Do something useful here. + return super.paramString(); } /** diff --git a/javax/swing/text/PlainView.java b/javax/swing/text/PlainView.java index 91d7547e7..b93d9a4f1 100644 --- a/javax/swing/text/PlainView.java +++ b/javax/swing/text/PlainView.java @@ -51,6 +51,12 @@ public class PlainView extends View { Color selectedColor; Color unselectedColor; + + /** + * The color that is used to draw disabled text fields. + */ + Color disabledColor; + Font font; protected FontMetrics metrics; @@ -145,7 +151,12 @@ public class PlainView extends View protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1) throws BadLocationException { - g.setColor(unselectedColor); + JTextComponent textComponent = (JTextComponent) getContainer(); + if (textComponent.isEnabled()) + g.setColor(unselectedColor); + else + g.setColor(disabledColor); + Segment segment = new Segment(); getDocument().getText(p0, p1 - p0, segment); return Utilities.drawTabbedText(segment, x, y, g, this, segment.offset); @@ -161,7 +172,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. diff --git a/javax/swing/text/SimpleAttributeSet.java b/javax/swing/text/SimpleAttributeSet.java index 3ef5db61d..61cf5e97c 100644 --- a/javax/swing/text/SimpleAttributeSet.java +++ b/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; @@ -89,7 +92,7 @@ public class SimpleAttributeSet return tab.containsKey(name) && tab.get(name).equals(value); } - + public boolean containsAttributes(AttributeSet attributes) { Enumeration e = attributes.getAttributeNames(); @@ -110,9 +113,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) @@ -160,7 +163,9 @@ public class SimpleAttributeSet public boolean isEqual(AttributeSet attr) { - return this.equals(attr); + return attr != null + && attr.containsAttributes(this) + && this.containsAttributes(attr); } public void removeAttribute(Object name) diff --git a/javax/swing/text/StringContent.java b/javax/swing/text/StringContent.java index bedf480d4..7db377a1c 100644 --- a/javax/swing/text/StringContent.java +++ b/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/javax/swing/text/StyleConstants.java b/javax/swing/text/StyleConstants.java index 3f973f226..7d8f9bb63 100644 --- a/javax/swing/text/StyleConstants.java +++ b/javax/swing/text/StyleConstants.java @@ -109,7 +109,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/javax/swing/text/StyleContext.java b/javax/swing/text/StyleContext.java index ae11622ff..eda13e744 100644 --- a/javax/swing/text/StyleContext.java +++ b/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); } diff --git a/javax/swing/text/StyledEditorKit.java b/javax/swing/text/StyledEditorKit.java index 89c4cf18e..e71f992b5 100644 --- a/javax/swing/text/StyledEditorKit.java +++ b/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/javax/swing/text/TabSet.java b/javax/swing/text/TabSet.java index 146f545aa..ecad9444e 100644 --- a/javax/swing/text/TabSet.java +++ b/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/javax/swing/text/TabStop.java b/javax/swing/text/TabStop.java index 032da8bca..56f862fda 100644 --- a/javax/swing/text/TabStop.java +++ b/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/javax/swing/text/Utilities.java b/javax/swing/text/Utilities.java index d40408ddc..02d98c3b2 100644 --- a/javax/swing/text/Utilities.java +++ b/javax/swing/text/Utilities.java @@ -195,4 +195,89 @@ 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.getEndIndex(); pos++) + { + char nextChar = s.array[pos]; + if (nextChar != '\n') + 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); + } } diff --git a/javax/swing/text/View.java b/javax/swing/text/View.java index 24efba9a1..9cea4ecb0 100644 --- a/javax/swing/text/View.java +++ b/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; diff --git a/javax/swing/text/html/CSS.java b/javax/swing/text/html/CSS.java new file mode 100644 index 000000000..f02ceb89b --- /dev/null +++ b/javax/swing/text/html/CSS.java @@ -0,0 +1,426 @@ +/* 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; + +/** + * 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 +{ + + 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; + + /** + * 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; + } + + /** + * 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/javax/swing/text/html/parser/DTD.java b/javax/swing/text/html/parser/DTD.java index f17ca011e..1ede342de 100644 --- a/javax/swing/text/html/parser/DTD.java +++ b/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. |