RichEdit control formatting engine. This package uses the Java2D Complex Text API's.

X-SVN-Rev: 1190
This commit is contained in:
Alan Liu 2000-04-20 17:50:03 +00:00
parent d6ed5975e9
commit bf82494a6f
11 changed files with 4876 additions and 0 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,142 @@
/*
* @(#)$RCSfile: ClipWorkaround.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:50:03 $
*
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
*
* The program is provided "as is" without any warranty express or
* implied, including the warranty of non-infringement and the implied
* warranties of merchantibility and fitness for a particular purpose.
* IBM will not be liable for any damages suffered by you as a result
* of using the Program. In no event will IBM be liable for any
* special, indirect or consequential damages or lost profits even if
* IBM has been advised of the possibility of their occurrence. IBM
* will not be liable for any third party claims against you.
*/
// Requires Java2
package com.ibm.richtext.textformat;
import java.awt.Shape;
///*JDK12IMPORTS
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Line2D;
//JDK12IMPORTS*/
/*JDK11IMPORTS
import com.ibm.textlayout.Graphics2D;
import com.ibm.textlayout.Rectangle2D;
JDK11IMPORTS*/
/**
* This class exists to work around a clipping bug in JDK 1.2.
*/
final class ClipWorkaround {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
///*JDK12IMPORTS
private static final String excuse =
"Sorry, this method is a very limited workaround for a JDK 1.2 bug.";
//JDK12IMPORTS*/
static Object saveClipState(Graphics2D g) {
///*JDK12IMPORTS
return null;
//JDK12IMPORTS*/
/*JDK11IMPORTS
return g.getClip();
JDK11IMPORTS*/
}
static void restoreClipState(Graphics2D g, Object state) {
///*JDK12IMPORTS
if (state != null) {
throw new Error("Invalid clip state for this class.");
}
//JDK12IMPORTS*/
/*JDK11IMPORTS
g.setClip((Shape)state);
JDK11IMPORTS*/
}
/**
* Draw the given Shape into the Graphics, translated by (dx, dy)
* and clipped to clipRect.
*/
static void translateAndDrawShapeWithClip(Graphics2D g,
int dx,
int dy,
Rectangle2D clipRect,
Shape shape) {
///*JDK12IMPORTS
// really bogus implementation right now: basically only
// draws carets from a TextLayout.
// Oh yeah, it doesn't really clip correctly either...
PathIterator pathIter = shape.getPathIterator(null);
float[] points = new float[6];
int type = pathIter.currentSegment(points);
if (type != PathIterator.SEG_MOVETO) {
throw new Error(excuse);
}
float x1 = points[0] + dx;
float y1 = points[1] + dy;
if (pathIter.isDone()) {
throw new Error(excuse);
}
pathIter.next();
type = pathIter.currentSegment(points);
if (type != PathIterator.SEG_LINETO) {
throw new Error(excuse);
}
float x2 = points[0] + dx;
float y2 = points[1] + dy;
float minY = (float) clipRect.getY();
float maxY = (float) clipRect.getMaxY();
// Now clip within vertical limits in clipRect
if (y1 == y2) {
if (y1 < minY || y1 >= maxY) {
return;
}
}
else {
if (y1 > y2) {
float t = x1;
x1 = x2;
x2 = t;
t = y1;
y1 = y2;
y2 = t;
}
float invSlope = (x2-x1) / (y2-y1);
if (y1 < minY) {
x1 -= (minY-y1) * invSlope;
y1 = minY;
}
if (y2 >= maxY) {
x1 += (y2-maxY) * invSlope;
y2 = maxY;
}
}
g.draw(new Line2D.Float(x1, y1, x2, y2));
//JDK12IMPORTS*/
/*JDK11IMPORTS
g.setClip(clipRect);
g.translate(dx, dy);
try {
g.draw(shape);
}
finally {
g.translate(-dx, -dy);
}
JDK11IMPORTS*/
}
}

View file

@ -0,0 +1,95 @@
/*
* @(#)$RCSfile: DefaultCharacterMetric.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:50:03 $
*
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
*
* The program is provided "as is" without any warranty express or
* implied, including the warranty of non-infringement and the implied
* warranties of merchantibility and fitness for a particular purpose.
* IBM will not be liable for any damages suffered by you as a result
* of using the Program. In no event will IBM be liable for any
* special, indirect or consequential damages or lost profits even if
* IBM has been advised of the possibility of their occurrence. IBM
* will not be liable for any third party claims against you.
*/
// Requires Java2
package com.ibm.richtext.textformat;
import java.util.Hashtable;
import com.ibm.textlayout.attributes.AttributeMap;
///*JDK12IMPORTS
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
//JDK12IMPORTS*/
/*JDK11IMPORTS
import com.ibm.textlayout.FontRenderContext;
import com.ibm.textlayout.TextLayout;
JDK11IMPORTS*/
/**
* This class is used by the Formatter to estimate the height
* of characters in a particular style.
*/
final class DefaultCharacterMetric {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
final class Metric {
private float fAscent;
private float fDescent;
private float fLeading;
private Metric(float ascent, float descent, float leading) {
fAscent = ascent;
fDescent = descent;
fLeading = leading;
}
public int getAscent() {
return (int) Math.ceil(fAscent);
}
public int getDescent() {
return (int) Math.ceil(fDescent);
}
public int getLeading() {
return (int) Math.ceil(fLeading);
}
}
private final Hashtable fCache = new Hashtable();
private /*final*/ FontResolver fResolver;
private /*final*/ FontRenderContext fFrc;
public DefaultCharacterMetric(FontResolver resolver,
FontRenderContext frc) {
fResolver = resolver;
fFrc = frc;
}
/**
* Get a DefaultCharacterMetric instance for the given style. The
* style is first resolved with FontResolver.
*/
public Metric getMetricForStyle(AttributeMap style) {
style = fResolver.applyFont(style);
Metric metric = (Metric) fCache.get(style);
if (metric == null) {
TextLayout layout = new TextLayout(" ", style, fFrc);
metric = new Metric(layout.getAscent(),
layout.getDescent(),
layout.getLeading());
fCache.put(style, metric);
}
return metric;
}
}

View file

@ -0,0 +1,85 @@
/*
* @(#)$RCSfile: FontResolver.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:50:03 $
*
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
*
* The program is provided "as is" without any warranty express or
* implied, including the warranty of non-infringement and the implied
* warranties of merchantibility and fitness for a particular purpose.
* IBM will not be liable for any damages suffered by you as a result
* of using the Program. In no event will IBM be liable for any
* special, indirect or consequential damages or lost profits even if
* IBM has been advised of the possibility of their occurrence. IBM
* will not be liable for any third party claims against you.
*/
// Requires Java2
package com.ibm.richtext.textformat;
import com.ibm.textlayout.attributes.AttributeMap;
import com.ibm.textlayout.attributes.TextAttribute;
import com.ibm.textlayout.FontUtils;
import java.util.Hashtable;
import java.awt.Font;
final class FontResolver {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
static {
// Even though it violates the Prime Directive I'll conditionalize
// this anyway, since it is just a 1.2 workaround which I greatly
// resent.
///*JDK12IMPORTS
java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
//JDK12IMPORTS*/
}
private Hashtable styleMap;
private final AttributeMap fDefaultFontMap;
public FontResolver(AttributeMap defaults) {
styleMap = new Hashtable();
Hashtable tempMap = new Hashtable();
tempMap.put(TextAttribute.FAMILY, defaults.get(TextAttribute.FAMILY));
tempMap.put(TextAttribute.WEIGHT, defaults.get(TextAttribute.WEIGHT));
tempMap.put(TextAttribute.POSTURE, defaults.get(TextAttribute.POSTURE));
tempMap.put(TextAttribute.SIZE, defaults.get(TextAttribute.SIZE));
fDefaultFontMap = new AttributeMap(tempMap);
}
/**
* Fetch result of resolve(style) from cache, if present.
*/
public AttributeMap applyFont(AttributeMap style) {
Object cachedMap = styleMap.get(style);
if (cachedMap == null) {
AttributeMap resolvedMap = resolve(style);
styleMap.put(style, resolvedMap);
return resolvedMap;
}
else {
return (AttributeMap) cachedMap;
}
}
/**
* Return an AttributeMap containing a Font computed from the
* attributes in <tt>style</tt>.
*/
public AttributeMap resolve(AttributeMap style) {
if (style.get(TextAttribute.FONT) != null) {
return style;
}
Font font = FontUtils.getFont(fDefaultFontMap.addAttributes(style));
return style.addAttribute(TextAttribute.FONT, font);
}
}

View file

@ -0,0 +1,199 @@
/*
* @(#)$RCSfile: LayoutInfo.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:50:03 $
*
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
*
* The program is provided "as is" without any warranty express or
* implied, including the warranty of non-infringement and the implied
* warranties of merchantibility and fitness for a particular purpose.
* IBM will not be liable for any damages suffered by you as a result
* of using the Program. In no event will IBM be liable for any
* special, indirect or consequential damages or lost profits even if
* IBM has been advised of the possibility of their occurrence. IBM
* will not be liable for any third party claims against you.
*/
// Requires Java2
/** LayoutInfo
A line of text, possibly containing tab-segments.
*/
package com.ibm.richtext.textformat;
import java.awt.Color;
import java.awt.Rectangle;
///*JDK12IMPORTS
import java.awt.Graphics2D;
//JDK12IMPORTS*/
/*JDK11IMPORTS
import com.ibm.textlayout.Graphics2D;
JDK11IMPORTS*/
import com.ibm.richtext.styledtext.MConstText;
abstract class LayoutInfo
{
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
private int fCharStart; // offset in text to start of line (was fStart)
// neg. values indicate distance from end of text
private int fGraphicStart; // min pixel offset in fill direction
// negative values indicate distance from bottom of text view
/*
These methods are for storing Layouts in a gap-storage,
relative to either the start of end of text. See AsyncFormatter.
If you just want absolute (that is, start-relative) char and
graphic starts, don't make them end-relative.
*/
public final int getCharStart(int lengthBasis) {
if (fCharStart >= 0) {
return fCharStart;
}
else {
return lengthBasis + fCharStart;
}
}
public final int getGraphicStart(int graphicBasis) {
if (fGraphicStart >= 0) {
return fGraphicStart;
}
else {
return graphicBasis + fGraphicStart;
}
}
public final void setCharStart(int beginningRelativeStart) {
if (beginningRelativeStart < 0) {
throw new IllegalArgumentException("charStart must be nonnegavitve");
}
fCharStart = beginningRelativeStart;
}
public final void setGraphicStart(int beginningRelativeStart) {
if (beginningRelativeStart < 0) {
throw new IllegalArgumentException("charStart must be nonnegavitve");
}
fGraphicStart = beginningRelativeStart;
}
public final void makeRelativeToBeginning(int lengthBasis,
int graphicBasis) {
if (lengthBasis < 0 || graphicBasis < 0) {
throw new IllegalArgumentException("Bases must be positive.");
}
if (fCharStart >= 0 || fGraphicStart >= 0) {
throw new Error("Already start-relative.");
}
fCharStart += lengthBasis;
fGraphicStart += graphicBasis;
}
public final void makeRelativeToEnd(int lengthBasis,
int graphicBasis) {
if (lengthBasis < 0 || graphicBasis < 0) {
throw new IllegalArgumentException("Bases must be positive.");
}
if (fCharStart < 0 || fGraphicStart < 0) {
throw new Error("Already end-relative.");
}
fCharStart -= lengthBasis;
fGraphicStart -= graphicBasis;
}
public abstract int getCharLength();
public abstract int getAscent();
public abstract int getDescent();
public abstract int getLeading();
public abstract int getVisibleAdvance();
public abstract int getTotalAdvance();
public abstract int getLeadingMargin();
public abstract boolean isLeftToRight();
public int getHeight() {
return getAscent()+getDescent()+getLeading();
}
/**
* Draws text with highlighting.
*/
public void renderWithHighlight(int lengthBasis,
Graphics2D g,
int lineBound,
int x,
int y,
TextOffset selStart,
TextOffset selStop,
Color highlightColor)
{
}
/** Use layout information to render the line at x, y.*/
public void render(int lengthBasis,
Graphics2D g,
int lineBound,
int x,
int y)
{
}
public void renderCaret(MConstText text,
int lengthBasis,
Graphics2D g,
int lineBound,
int x,
int y,
int charOffset,
Color strongCaretColor,
Color weakCaretColor)
{
}
/**
* Given a point within this line, return the character offset corresponding to that point.
*
* @param result. This may be null, in which case a new TextOffset will be allocated.
* This object is modified in place, and also returned as the function result.
* @param text Text to inspect.
* @param lineX Position on this line relative to top left corner of this line.
* @param lineY Position on this line relative to top left corner of this line.
*/
public abstract TextOffset pixelToOffset(int lengthBasis,
TextOffset result,
int lineBound,
int x,
int y);
public abstract int strongCaretBaselinePosition(int lengthBasis,
int lineBound,
int charOffset);
public abstract Rectangle caretBounds(MConstText text,
int lengthBasis,
int lineBound,
int charOffset,
int x,
int y);
public abstract int getNextOffset(int lengthBasis,
int charOffset,
short dir);
}

View file

@ -0,0 +1,351 @@
/*
* @(#)$RCSfile: MFormatter.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:50:03 $
*
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
*
* The program is provided "as is" without any warranty express or
* implied, including the warranty of non-infringement and the implied
* warranties of merchantibility and fitness for a particular purpose.
* IBM will not be liable for any damages suffered by you as a result
* of using the Program. In no event will IBM be liable for any
* special, indirect or consequential damages or lost profits even if
* IBM has been advised of the possibility of their occurrence. IBM
* will not be liable for any third party claims against you.
*/
package com.ibm.richtext.textformat;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import com.ibm.richtext.styledtext.MConstText;
import com.ibm.textlayout.attributes.AttributeMap;
/**
*
* This class formats lines of text to a given length.
* It provides services needed for static text display,
* and also editable text, including: displaying text,
* reformatting text after an edit, converting between
* screen locations and offsets into the text, calculating
* areas of the screen for "highlighting," and computing
* offsets into the text resulting from arrow keys.
* <p>
* Text clients instantiate this class with an
* <tt>MConstText</tt> object and a format width. Text
* can be formatted such that all lines fit within the
* format length. Alternatively, text can be formatted
* such that lines end only at the end of paragraphs.
* <p>
* The format length is specified with the <tt>setLineBound()</tt>
* method.
* <p>
* Methods in the formatter which interact with the graphics
* system generally take as a paramter a <tt>Point</tt> object
* which represents the "origin" of the text display. The
* origin represents the location, in the graphics system used to display the text, of
* the top-left corner of the text.
* <p>
* To display the text, call <tt>draw()</tt>, passing the
* a rectangle in which to draw as a parameter. Only lines
* of text in the draw rectangle will be drawn.
* <p>
* When the formatter's text changes, it is important to first call
* <tt>stopBackgroundFormatting()</tt> to prevent the Formatter from
* accessing the text from a background thread. After modifications are
* complete,
* call the <tt>updateFormat()</tt> method before invoking any other
* methods of the formatter. <tt>updateFormat()</tt> reformats the
* new text, formatting no more text than is necessary.
* <p>
* The formatter provides services for responding to user input from the
* mouse and keyboard. The method <tt>pointToTextOffset()</tt> converts
* a screen location to an offset in the text. The method <tt>textOffsetToPoint</tt>
* converts an offset in the text to an array of two <tt>Point</tt> objects, which can be
* used to draw a verticle caret, denoting an insertion point. <tt>highlightArea</tt>
* accepts two offsets into the text as paramters, and returns an array of <tt>Polygon</tt>
* objects representing areas where visual highlighting should be applied.
* <p>
* Finally, for
* keyboard handling, the <tt>findNewInsertionOffset()</tt> method accepts an "initial"
* offset, a "previous" offset, as well as a direction, and returns a new offset. The direction
* can be up, down, left, or right. The previous offset is the insertion point location, before
* the arrow key is processed. The initial offset is the offset where an up or down arrow
* key sequence began. Using the initial offset allows for "intelligent" handling of up and down
* arrow keys.
* <p>
* Examples of using the MFormatter class
* are given in the <tt>AsyncFormatter</tt> class
* documentation.
* <p>
* @author John Raley
*
* @see MText
*/
public abstract class MFormatter {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
public abstract AttributeMap getDefaultValues();
/**
* Display text in drawArea, with highlighting.
* Does not reformat text
* @param g the Graphics object in which to draw
* @param drawArea the rectangle, in g's coordinate system, in which to draw
* @param origin the top-left corner of the text, in g's coordinate system
* @param selStart the offset where the current selection begins; pass <tt>null</tt> if no selection
* @param selStop the offset where the current selection ends
* @param highlight the color of the highlighting
*/
public abstract void draw(Graphics g, Rectangle drawArea, Point origin,
TextOffset selStart, TextOffset selStop, Color highlight);
public abstract void draw(Graphics g, Rectangle drawArea, Point origin);
/**
* Specify whether to wrap line at the edge of the destination area.
* <tt>true</tt> means wrap lines; <tt>false</tt> means to break lines
* only when an end-of-line character is reached.
* @param wrap <tt>true</tt> to break lines at the edge of the destination
* area; <tt>false</tt> otherwise.
*/
public abstract void setWrap(boolean wrap);
/**
* Return whether text is wrapped at the edge of the destination area.
* @see #setWrap
*/
public abstract boolean wrap();
/**
* Specify the number of pixels along the "line dimension".
* Lines are formatted to fit within the line dimension. The
* line dimension in Roman script is horizontal.
* @param lineWidth the length, in pixels, to which lines will be formatted
*/
public abstract void setLineBound(int lineBound);
/**
* Return the number of pixels along the line dimension.
*/
public abstract int lineBound();
/**
* Format text down to given height.
* @param height the height to which text will be formatted
*/
public abstract void formatToHeight(int height);
/**
* Reformat text after a change.
* After the formatter's text changes, call this method to reformat. Does
* not redraw.
* @param afStart the offset into the text where modification began; ie, the
* first character in the text which is "different" in some way. Does not
* have to be nonnegative.
* @param afLength the number of new or changed characters in the text. Should never
* be less than 0.
* @param viewRect the Rectangle in which the text will be displayed. This is needed for
* returning the "damaged" area - the area of the screen in which the text must be redrawn.
* @param origin the top-left corner of the text, in the display's coordinate system
* @returns a <tt>Rectangle</tt> which specifies the area in which text must be
* redrawn to reflect the change to the text.
*/
public abstract Rectangle updateFormat(int afStart,
int afLength,
Rectangle viewRect,
Point origin);
public abstract int minY();
/**
* Return the maximum vertical coordinate of the document area.
*/
public abstract int maxY();
public abstract int minX();
/**
* Return the maximum horizontal coordinate of the document area.
*/
public abstract int maxX();
/**
* Return the actual pixel length of the text which has been formatted.
*/
public abstract int formattedHeight();
public static final short eUp = -10, eDown = 10, eLeft = -1, eRight = 1;
/**
* Given a screen location p, return the offset of the character in the text nearest to p.
*
* The offset may or may not include a newline at the end of a line, determined by anchor and infiniteMode.
* The newline is not included if infiniteMode is true and the anchor is the position before the newline.
*
* @param result TextOffset to modify and return. If null, one will be allocated, modified, and returned.
* @param px the x component of the point.
* @param py the y component of the point.
* @param origin the top-left corner of the text, in the display's coordinate system
* @param anchor the previous offset. May be null. Used to determine whether newlines are included.
* @param infiniteMode if true, treat newlines at end of line as having infinite width.
*/
public abstract TextOffset pointToTextOffset(TextOffset result, int px, int py, Point origin, TextOffset anchor, boolean infiniteMode);
/**
* Given an offset, return the Rectangle bounding the caret at the offset.
* @param offset an offset into the text
* @param origin the top-left corner of the text, in the display's coordinate system
* @return a Rectangle bounding the caret.
*/
public abstract Rectangle getCaretRect(TextOffset offset, Point origin);
/**
* Draw the caret(s) associated with the given offset into the given Graphics.
* @param g the Graphics to draw into
* @param offset the offset in the text for which the caret is drawn
* @param origin the top-left corner of the text, in the display's coordinate system
* @param strongCaretColor the color of the strong caret
* @param weakCaretColor the color of the weak caret (if any)
*/
public abstract void drawCaret(Graphics g,
TextOffset offset,
Point origin,
Color strongCaretColor,
Color weakCaretColor);
/**
* @see #getBoundingRect
*/
public static final boolean LOOSE = false;
/**
* @see #getBoundingRect
*/
public static final boolean TIGHT = true;
/**
* Given two offsets in the text, return a rectangle which encloses the lines containing the offsets.
* Offsets do not need to be ordered or nonnegative.
* @param offset1,offset2 offsets into the text
* @param origin the top-left corner of the text, in the display's coordinate system
* @param tight if equal to TIGHT, the bounds is as small as possible. If LOOSE, the width
* of the bounds is allowed to be wider than necesary. Loose bounds are easier to compute.
* @returns a <tt>Rectangle</tt>, relative to <tt>origin</tt>, which encloses the lines containing the offsets
*/
public abstract Rectangle getBoundingRect(TextOffset offset1,
TextOffset offset2,
Point origin,
boolean tight);
public abstract void getBoundingRect(Rectangle boundingRect,
TextOffset offset1,
TextOffset offset2,
Point origin,
boolean tight);
/**
* Compute the offset resulting from moving from a previous offset in direction dir.
* For arrow keys.
* @param previousOffset the insertion offset prior to the arrow key press
* @param direction the direction of the arrow key (eUp, eDown, eLeft, or eRight)
* @returns new offset based on direction and previous offset.
*/
public abstract TextOffset findInsertionOffset(TextOffset result,
TextOffset previousOffset,
short direction);
/**
* Compute the offset resulting from moving from a previous offset, starting at an original offset, in direction dir.
* For arrow keys. Use this for "smart" up/down keys.
* @param result TextOffset to modify and return. If null, a new TextOffset is created, modified, and returned.
* @param initialOffset The offset at which an up-down arrow key sequence began.
* @param previousOffset The insertion offset prior to the arrow key press.
* @param direction The direction of the arrow key (eUp, eDown, eLeft, or eRight)
* @returns new offset based on direction and previous offset(s).
*/
public abstract TextOffset findNewInsertionOffset(TextOffset result,
TextOffset initialOffset,
TextOffset previousOffset,
short direction);
/**
* Return the index of the line containing the given character index.
* This method has complicated semantics, arising from not knowing
* which side of the index to check. The index will be given an
* implicit AFTER bias, unless the index is the last index in the text,
* the text length is non-zero, and there is not a paragraph separator
* at the end of the text.
*/
public abstract int lineContaining(int index);
/**
* Return the index of the line containing the given offset.
*/
public abstract int lineContaining(TextOffset offset);
/**
* Return the number of lines.
*/
public abstract int getLineCount();
/**
* Return the index of the first character on the given line.
*/
public abstract int lineRangeLow(int lineNumber);
/**
* Return the index of the first character following the given line.
*/
public abstract int lineRangeLimit(int lineNumber);
/**
* Tells the formatter to stop accessing the text until updateFormat is called.
*/
public abstract void stopBackgroundFormatting();
/**
* Return the line number at the given graphic height. If height is greater than
* the text height, maxLineNumber + 1 is returned.
*/
public abstract int lineAtHeight(int height);
/**
* Return the graphic height where the given line begins. If the lineNumber is
* maxLineNumber the entire text height is returned.
*/
public abstract int lineGraphicStart(int lineNumber);
/**
* Return true if the given line is left-to-right.
* @param lineNumber a valid line
* @return true if lineNumber is left-to-right
*/
public abstract boolean lineIsLeftToRight(int lineNumber);
/**
* Return a new <tt>MFormatter</tt>.
* @param text the text to format
* @param defaultValues values to use when certain attributes are not specified.
* <tt>defaultValues</tt> must contain values for the following attributes:
* <tt>FAMILY</tt>, <tt>WEIGHT</tt>, <tt>POSTURE</tt>, <tt>SIZE</tt>, <tt>SUPERSCRIPT</tt>,
* <tt>FOREGROUND</tt>, <tt>UNDERLINE</tt>, <tt>STRIKETHROUGH</tt>,
* <tt>EXTRA_LINE_SPACING</tt>, <tt>FIRST_LINE_INDENT</tt>,<tt>MIN_LINE_SPACING</tt>,
* <tt>LINE_FLUSH</tt>, <tt>LEADING_MARGIN</tt>, <tt>TRAILING_MARGIN</tt>, <tt>TAB_RULER</tt>
* @param lineBound length to which lines are formatted
* @param wrap <tt>true</tt> if text should be "line wrapped" (formatted to fit destination area)
*/
public static MFormatter createFormatter(MConstText text,
AttributeMap defaultValues,
int lineBound,
boolean wrap,
Graphics g) {
return new AsyncFormatter(text, defaultValues, lineBound, wrap, g);
}
}

View file

@ -0,0 +1,402 @@
/*
* @(#)$RCSfile: MTextIterator.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:50:03 $
*
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
*
* The program is provided "as is" without any warranty express or
* implied, including the warranty of non-infringement and the implied
* warranties of merchantibility and fitness for a particular purpose.
* IBM will not be liable for any damages suffered by you as a result
* of using the Program. In no event will IBM be liable for any
* special, indirect or consequential damages or lost profits even if
* IBM has been advised of the possibility of their occurrence. IBM
* will not be liable for any third party claims against you.
*/
// Requires Java2
package com.ibm.richtext.textformat;
import com.ibm.richtext.styledtext.MConstText;
import java.text.CharacterIterator;
import com.ibm.textlayout.attributes.AttributeMap;
///*JDK12IMPORTS
import java.text.AttributedCharacterIterator;
import java.text.AttributedCharacterIterator.Attribute;
import java.util.Map;
import java.util.Set;
//JDK12IMPORTS*/
/*JDK11IMPORTS
import com.ibm.textlayout.attributes.AttributedCharacterIterator;
import com.ibm.textlayout.attributes.AttributedCharacterIterator.Attribute;
import com.ibm.textlayout.attributes.Map;
JDK11IMPORTS*/
public final class MTextIterator implements AttributedCharacterIterator,
Cloneable {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
// memory leak, since this cache is never flushed
private static class Matcher {
boolean matches(Map lhs, Map rhs, Object query) {
Object lhsVal = lhs.get(query);
Object rhsVal = rhs.get(query);
if (lhsVal == null) {
return rhsVal == null;
}
else {
return lhsVal.equals(rhsVal);
}
}
}
private static final Matcher ATTR_MATCHER = new Matcher();
// Not quite optimal. Could have a matcher that would decompose
// a set once for repeated queries. Of course that would require
// allocation...
///*JDK12IMPORTS
private static final Matcher SET_MATCHER = new Matcher() {
boolean matches(Map lhs, Map rhs, Object query) {
// Not using Iterator to simplify 1.1 port.
Object[] elements = ((Set)query).toArray();
for (int i=0; i < elements.length; i++) {
if (!super.matches(lhs, rhs, elements[i])) {
return false;
}
}
return true;
}
};
//JDK12IMPORTS*/
private final class StyleCache {
private int fRunStart = 0;
private int fRunLimit = -1;
private int fRangeStart;
private int fRangeLimit;
private AttributeMap fStyle;
StyleCache(MConstText text, int start, int limit) {
fText = text;
fRangeStart = start;
fRangeLimit = limit;
update(start);
}
private void update(int pos) {
if (pos < fRunStart || pos >= fRunLimit) {
AttributeMap style = AttributeMap.EMPTY_ATTRIBUTE_MAP;
if (pos < fRangeStart) {
fRunLimit = fRangeStart;
fRunStart = Integer.MIN_VALUE;
}
else if (pos > fRangeLimit) {
fRunStart = fRangeLimit;
fRunLimit = Integer.MAX_VALUE;
}
else {
fRunStart = Math.max(fRangeStart, fText.characterStyleStart(pos));
fRunStart = Math.max(fRunStart, fText.paragraphStart(pos));
fRunLimit = Math.min(fRangeLimit, fText.characterStyleLimit(pos));
fRunLimit = Math.min(fRunLimit, fText.paragraphLimit(pos));
if (fRunStart < fRunLimit) {
style = fText.paragraphStyleAt(pos);
style = style.addAttributes(fText.characterStyleAt(pos));
}
}
fStyle = fFontResolver.applyFont(style);
}
}
int getRunStart(int pos) {
update(pos);
return fRunStart;
}
int getRunLimit(int pos) {
update(pos);
return fRunLimit;
}
Map getStyle(int pos) {
update(pos);
return fStyle;
}
}
private MConstText fText;
private CharacterIterator fCharIter;
private FontResolver fFontResolver;
private StyleCache fStyleCache;
/**
* Create an MTextIterator over the range [start, limit).
*/
public MTextIterator(MConstText text,
FontResolver resolver,
int start,
int limit) {
fText = text;
fFontResolver = resolver;
fCharIter = text.createCharacterIterator(start, limit);
fStyleCache = new StyleCache(text, start, limit);
}
/**
* Sets the position to getBeginIndex() and returns the character at that
* position.
* @return the first character in the text, or DONE if the text is empty
* @see getBeginIndex
*/
public char first() {
return fCharIter.first();
}
/**
* Sets the position to getEndIndex()-1 (getEndIndex() if the text is empty)
* and returns the character at that position.
* @return the last character in the text, or DONE if the text is empty
* @see getEndIndex
*/
public char last() {
return fCharIter.last();
}
/**
* Gets the character at the current position (as returned by getIndex()).
* @return the character at the current position or DONE if the current
* position is off the end of the text.
* @see getIndex
*/
public char current() {
return fCharIter.current();
}
/**
* Increments the iterator's index by one and returns the character
* at the new index. If the resulting index is greater or equal
* to getEndIndex(), the current index is reset to getEndIndex() and
* a value of DONE is returned.
* @return the character at the new position or DONE if the new
* position is off the end of the text range.
*/
public char next() {
return fCharIter.next();
}
/**
* Decrements the iterator's index by one and returns the character
* at the new index. If the current index is getBeginIndex(), the index
* remains at getBeginIndex() and a value of DONE is returned.
* @return the character at the new position or DONE if the current
* position is equal to getBeginIndex().
*/
public char previous() {
return fCharIter.previous();
}
/**
* Sets the position to the specified position in the text and returns that
* character.
* @param position the position within the text. Valid values range from
* getBeginIndex() to getEndIndex(). An IllegalArgumentException is thrown
* if an invalid value is supplied.
* @return the character at the specified position or DONE if the specified position is equal to getEndIndex()
*/
public char setIndex(int position) {
return fCharIter.setIndex(position);
}
/**
* Returns the start index of the text.
* @return the index at which the text begins.
*/
public int getBeginIndex() {
return fCharIter.getBeginIndex();;
}
/**
* Returns the end index of the text. This index is the index of the first
* character following the end of the text.
* @return the index after the last character in the text
*/
public int getEndIndex() {
return fCharIter.getEndIndex();
}
/**
* Returns the current index.
* @return the current index.
*/
public int getIndex() {
return fCharIter.getIndex();
}
/**
* Returns the index of the first character of the run
* with respect to all attributes containing the current character.
*/
public int getRunStart() {
return fStyleCache.getRunStart(fCharIter.getIndex());
}
/**
* Returns the index of the first character of the run
* with respect to the given attribute containing the current character.
*/
public int getRunStart(Object attribute) {
return getRunStart(attribute, ATTR_MATCHER);
}
/**
* Returns the index of the first character of the run
* with respect to the given attribute containing the current character.
*/
///*JDK12IMPORTS
public int getRunStart(Attribute attribute) {
return getRunStart(attribute, ATTR_MATCHER);
}
//JDK12IMPORTS*/
/**
* Returns the index of the first character of the run
* with respect to the given attributes containing the current character.
*/
///*JDK12IMPORTS
public int getRunStart(Set attributes) {
return getRunStart(attributes, SET_MATCHER);
}
//JDK12IMPORTS*/
private int getRunStart(Object query, Matcher matcher) {
int runStart = getRunStart();
int rangeStart = getBeginIndex();
Map initialStyle = getAttributes();
while (runStart > rangeStart) {
AttributeMap style = fText.characterStyleAt(runStart-1);
if (!matcher.matches(initialStyle, style, query)) {
return runStart;
}
runStart = fText.characterStyleStart(runStart-1);
}
return rangeStart;
}
/**
* Returns the index of the first character following the run
* with respect to all attributes containing the current character.
*/
public int getRunLimit() {
return fStyleCache.getRunLimit(fCharIter.getIndex());
}
/**
* Returns the index of the first character following the run
* with respect to the given attribute containing the current character.
*/
public int getRunLimit(Object attribute) {
return getRunLimit(attribute, ATTR_MATCHER);
}
/**
* Returns the index of the first character following the run
* with respect to the given attribute containing the current character.
*/
///*JDK12IMPORTS
public int getRunLimit(Attribute attribute) {
return getRunLimit(attribute, ATTR_MATCHER);
}
//JDK12IMPORTS*/
/**
* Returns the index of the first character following the run
* with respect to the given attributes containing the current character.
*/
///*JDK12IMPORTS
public int getRunLimit(Set attributes) {
return getRunLimit(attributes, SET_MATCHER);
}
//JDK12IMPORTS*/
private int getRunLimit(Object query, Matcher matcher) {
int runLimit = getRunLimit();
int rangeLimit = getEndIndex();
Map initialStyle = getAttributes();
while (runLimit < rangeLimit) {
AttributeMap style = fText.characterStyleAt(runLimit);
if (!matcher.matches(initialStyle, style, query)) {
return runLimit;
}
runLimit = fText.characterStyleLimit(runLimit);
}
return rangeLimit;
}
/**
* Returns a map with the attributes defined on the current
* character.
*/
public Map getAttributes() {
return fStyleCache.getStyle(fCharIter.getIndex());
}
/**
* Returns the value of the named attribute for the current character.
* Returns null if the attribute is not defined.
* @param attribute the key of the attribute whose value is requested.
*/
public Object getAttribute(Object attribute) {
return getAttributes().get(attribute);
}
/**
* Returns the value of the named attribute for the current character.
* Returns null if the attribute is not defined.
* @param attribute the key of the attribute whose value is requested.
*/
public Object getAttribute(Attribute attribute) {
return getAttributes().get(attribute);
}
/**
* Returns the keys of all attributes defined on the
* iterator's text range. The set is empty if no
* attributes are defined.
*/
///*JDK12IMPORTS
public Set getAllAttributeKeys() {
throw new Error("Implement this method!");
}
//JDK12IMPORTS*/
public Object clone() {
return new MTextIterator(fText,
fFontResolver,
getBeginIndex(),
getEndIndex());
}
}

View file

@ -0,0 +1,84 @@
/*
* @(#)$RCSfile: ParagraphRenderer.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:50:03 $
*
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
*
* The program is provided "as is" without any warranty express or
* implied, including the warranty of non-infringement and the implied
* warranties of merchantibility and fitness for a particular purpose.
* IBM will not be liable for any damages suffered by you as a result
* of using the Program. In no event will IBM be liable for any
* special, indirect or consequential damages or lost profits even if
* IBM has been advised of the possibility of their occurrence. IBM
* will not be liable for any third party claims against you.
*/
// Requires Java2
package com.ibm.richtext.textformat;
import com.ibm.textlayout.attributes.AttributeMap;
import com.ibm.textlayout.attributes.TextAttribute;
import com.ibm.richtext.styledtext.MConstText;
import java.awt.Color;
///*JDK12IMPORTS
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.Graphics2D;
//JDK12IMPORTS*/
/*JDK11IMPORTS
import com.ibm.textlayout.FontRenderContext;
import com.ibm.textlayout.LineBreakMeasurer;
import com.ibm.textlayout.Graphics2D;
JDK11IMPORTS*/
/**
* ParagraphRenderer is a factory for LayoutInfo objects.
*/
abstract class ParagraphRenderer {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
// If renderers can ever travel with their styles, then this attribute will denote a
// renderer. For now, renderers are not added as styles so this isn't needed.
//static final TextAttribute PARAGRAPH_RENDERER = new TextAttribute("Paragraph_Renderer") {};
/**
* Reset the renderer to use information from this style. Since renderers may be shared, you should
* make sure the renderer is initialized for the style you wish to render before you use it.
*/
public void initRenderer(AttributeMap pStyle) {
}
/**
* Return a LayoutInfo for the paragraph represented by
* measurer.
* @param text the text containing the paragraph
* @param layoutToReuse clients can pass in a LayoutInfo
* which the ParagraphRenderer may choose to reuse
* and return. If null, a new LayoutInfo will be
* created and returned.
* @param measurer the LineBreakMeasurer for this paragraph.
* Current position should be the first character on the line.
* If null, a 0-length line is generated. If measurer is null
* then paragraphStart and paragraphLimit should be equal.
* @param frc the FontRenderContext used for measurerment
* @param paragraphStart the index in the text where the
* current paragraph begins
* @param paragraphLimit the index of the first character
* after the current paragraph
* @param totalFormatWidth the width in which the line should fit
* @param lineBound where right-aligned lines are aligned
*/
public abstract LayoutInfo layout(MConstText text,
LayoutInfo layoutToReuse,
LineBreakMeasurer measurer,
FontRenderContext frc,
int paragraphStart,
int paragraphLimit,
int totalFormatWidth,
int lineBound);
}

View file

@ -0,0 +1,117 @@
/*
* @(#)$RCSfile: TestMTextIterator.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:50:03 $
*
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
*
* The program is provided "as is" without any warranty express or
* implied, including the warranty of non-infringement and the implied
* warranties of merchantibility and fitness for a particular purpose.
* IBM will not be liable for any damages suffered by you as a result
* of using the Program. In no event will IBM be liable for any
* special, indirect or consequential damages or lost profits even if
* IBM has been advised of the possibility of their occurrence. IBM
* will not be liable for any third party claims against you.
*/
// Requires Java2
package com.ibm.richtext.textformat;
import com.ibm.richtext.styledtext.StyledText;
import com.ibm.richtext.styledtext.MConstText;
import com.ibm.richtext.styledtext.MText;
import com.ibm.textlayout.attributes.TextAttribute;
import com.ibm.textlayout.attributes.AttributeMap;
import com.ibm.richtext.textpanel.TextPanel;
public class TestMTextIterator {
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
private static final FontResolver FONT_MAPPER;
static {
AttributeMap attrs = TextPanel.getDefaultSettings().getDefaultValues();
FONT_MAPPER = new FontResolver(attrs);
}
public static void compareIterToText(MTextIterator iter,
MConstText text) {
//System.out.println("Text: " + text);
final int beginIndex = iter.getBeginIndex();
final int endIndex = iter.getEndIndex();
char ch = iter.setIndex(beginIndex);
for (int i=beginIndex; i < endIndex; i++) {
//System.out.print(ch+ " ");
if (ch != text.at(i)) {
throw new Error("Characters are not the same.");
}
ch = iter.next();
}
if (ch != iter.DONE) {
throw new Error("Iterator is not done.");
}
for (int i=endIndex-1; i >= beginIndex; i--) {
ch = iter.previous();
//System.out.print(ch+ " ");
if (ch != text.at(i)) {
throw new Error("Backward iteration failed.");
}
}
iter.setIndex(beginIndex);
int runLimit;
for (int runStart = beginIndex; runStart < endIndex; runStart = runLimit) {
runLimit = Math.min(endIndex, text.characterStyleLimit(runStart));
if (iter.getRunStart() != runStart) {
System.out.println(iter.getRunStart() + "; " + runStart);
throw new Error("getRunStart is wrong.");
}
if (iter.getRunLimit() != runLimit) {
System.out.println(iter.getRunLimit() + "; " + runLimit);
throw new Error("getRunLimit is wrong.");
}
AttributeMap style = text.characterStyleAt(runStart);
while (iter.getIndex() < runLimit) {
AttributeMap resolved = FONT_MAPPER.applyFont(style);
if (!iter.getAttributes().equals(resolved)) {
throw new Error("Style is wrong.");
}
iter.next();
}
}
}
public void test() {
AttributeMap bold = new AttributeMap(TextAttribute.WEIGHT,
TextAttribute.WEIGHT_BOLD);
MText text = new StyledText("Hello there!", AttributeMap.EMPTY_ATTRIBUTE_MAP);
text.replace(2, 2, 'V', bold);
MTextIterator iter = new MTextIterator(text, FONT_MAPPER, 0, text.length());
compareIterToText(iter, text);
text.replace(6, 8, new StyledText("ALL_BOLD", bold), 0, 8);
iter = new MTextIterator(text, FONT_MAPPER, 1, text.length()-3);
compareIterToText(iter, text);
iter = new MTextIterator(text, FONT_MAPPER, 0, text.length());
compareIterToText(iter, text);
}
public static void main(String[] args) {
new TestMTextIterator().test();
System.out.println("PASSED");
}
}

View file

@ -0,0 +1,198 @@
/*
* @(#)$RCSfile: TextOffset.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:50:03 $
*
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
*
* The program is provided "as is" without any warranty express or
* implied, including the warranty of non-infringement and the implied
* warranties of merchantibility and fitness for a particular purpose.
* IBM will not be liable for any damages suffered by you as a result
* of using the Program. In no event will IBM be liable for any
* special, indirect or consequential damages or lost profits even if
* IBM has been advised of the possibility of their occurrence. IBM
* will not be liable for any third party claims against you.
*/
// Revision: 03 1.16 richtext/TextOffset.java, richtext, richtext
/*
9/5/96 {jbr} added set and equals methods
*/
package com.ibm.richtext.textformat;
/**
* A TextOffset indicates both an integer offset into text and a placement
* on one of the characters adjacent to the offset. An offset is a
* position between two characters; offset n
* is between character n-1 and character n. The placement specifies whether
* it is associated with the character
* after the offset
* (character n) or the character before the offset (character n-1).
* <p>
* Knowing which character the TextOffset is associated with is necessary
* when displaying carets. In bidirectional text, a single offset may
* have two distinct carets. Also, in multiline text, an offset at a line
* break has a possible caret on each line.
* <p>
* Most clients will not be interested in the placement, and will just use
* the offset.
*/
public final class TextOffset
{
static final String COPYRIGHT =
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
/**
* Indicates that the TextOffset is associated with character
* <code>fOffset - 1</code> - ie the character before its offset.
*/
public final static boolean BEFORE_OFFSET = true;
/**
* Indicates that the TextOffset is associated with character
* <code>fOffset</code> - ie the character after its offset.
*/
public final static boolean AFTER_OFFSET = false;
/**
* The offset into the text.
*/
public int fOffset = 0;
/**
* The placement - before or after.
*/
public boolean fPlacement = AFTER_OFFSET;
/**
* Constructs a new TextOffset
* @param offset the offset into the text to represent. Placement is implicitly AFTER_OFFSET.
*/
public TextOffset(int offset)
{
if (offset < 0)
throw new IllegalArgumentException("Offset is negative in TextOffset constructor.");
fOffset = offset;
fPlacement = AFTER_OFFSET;
}
/**
* Constructs a new TextOffset at 0, with placement AFTER_OFFSET.
*/
public TextOffset() {
this(0);
}
/**
* Constructs a new TextOffset with the given offset and placement.
* @param offset the offset into the text
* @param placement indicates the position of the caret; one of BEFORE_OFFSET or AFTER_OFFSET
*/
public TextOffset(int offset, boolean placement)
{
if (offset < 0)
throw new IllegalArgumentException("TextOffset constructor offset < 0: " + offset);
fOffset = offset;
fPlacement = placement;
}
/**
* Constructs a new TextOffset from an existing one.
* @param ths the TextOffset to copy
*/
public TextOffset(TextOffset rhs) {
this(rhs.fOffset, rhs.fPlacement);
}
/**
* Set the value of the TextOffset
* @param offset the offset into the text
* @param placement indicates the position of the caret; one of BEFORE_OFFSET or AFTER_OFFSET
*/
public void setOffset(int offset, boolean placement)
{
if (offset < 0)
throw new IllegalArgumentException("TextOffset setOffset offset < 0: " + offset);
fOffset = offset;
fPlacement = placement;
}
/**
* Compare this to another Object.
*/
public boolean equals(Object other) {
try {
return equals((TextOffset)other);
}
catch(ClassCastException e) {
return false;
}
}
/**
* Return true if offset and placement are the same.
*
* @param other offset to compare against
* @return true if both offsets are equal
*/
public boolean equals(TextOffset other) {
return fOffset == other.fOffset && fPlacement == other.fPlacement;
}
/**
* Return the hashCode for this object.
*/
public int hashCode() {
return fPlacement==AFTER_OFFSET? fOffset : -fOffset;
}
/**
* Return true if this offset is 'greaterThan' other. If the fOffset fields are equal, the
* placement field is considered, and AFTER_OFFSET is considered 'greaterThan' BEFORE_OFFSET.
*
* @param other the other offset
* @return true if this offset appears after other
*/
public boolean greaterThan(TextOffset other)
{
return fOffset > other.fOffset ||
(fOffset == other.fOffset && fPlacement == AFTER_OFFSET && other.fPlacement == BEFORE_OFFSET);
}
/**
* Return true if this offset is 'lessThan' other. If the fOffset fields are equal, the
* placement field is considered, and BEFORE_OFFSET is considered 'lessThan' AFTER_OFFSET.
*
* @param other the other offset
* @return true if this offset appears before other
*/
public boolean lessThan(TextOffset other) {
return fOffset < other.fOffset ||
(fOffset == other.fOffset && fPlacement == BEFORE_OFFSET && other.fPlacement == AFTER_OFFSET);
}
/**
* Copy the value of another TextOffset into this
* @param other the TextOffset to copy
*/
public void assign(TextOffset other) {
fOffset = other.fOffset;
fPlacement = other.fPlacement;
}
/**
* Return a string representation of this object.
*/
public String toString() {
return "[" + (fPlacement ? "before " : "after ") + fOffset + "]";
}
}