home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 1995 November
/
PCWK1195.iso
/
inne
/
win95
/
sieciowe
/
hotja32.lzh
/
hotjava
/
classsrc
/
awt
/
formatter.java
< prev
next >
Wrap
Text File
|
1995-08-11
|
11KB
|
460 lines
/*
* @(#)Formatter.java 1.32 95/05/21 Jonathan Payne
*
* Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for NON-COMMERCIAL purposes and without
* fee is hereby granted provided that this copyright notice
* appears in all copies. Please refer to the file "copyright.html"
* for further important copyright and licensing information.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*/
package awt;
import java.util.*;
/**
* A class that knows how to layout a set of DisplayItems inside a
* window.
*
* @version 1.32 21 May 1995
* @author Jonathan Payne
*/
public class Formatter {
final boolean debug = false;
static Hashtable colors = new Hashtable();
/** Current formatting parameters. */
protected FormattingParameters currentParameters;
/** Current X coordinate, where next display item will
be added. */
protected int x;
/** Current X limit for the right margin. If a display item
moves beyond xlimit, we wrap. */
protected int xlimit;
/** Current Y coordinate. This is the value of Y for the top
of the current line we're building. */
protected int y;
/** Current lineAscent for this line. */
protected int lineAscent;
/** Current lineDescent for this line. */
protected int lineDescent;
/** Current maximum font ascent. */
protected int fontAscent;
/** Current font as determined by the current FormattingParameters. */
protected Font font;
/** Metrics of the current font. */
protected FontMetrics fontmetrics;
/** Current color as determined by the current FormattingParameters. */
protected Color color;
/** The display item index of the first item on this line. This is
used to go back to position all the items we accumulate on the
line we're laying out right now. */
protected int lineStartIndex;
/** Total number of items we think we've added to the window. */
protected int itemCount;
/** This is the stack of formatting parameters. */
protected Stack fpStack = new Stack();
/** This is true when we're in a section that doesn't render. */
protected int notRendering = 0;
/** This is used to combine requests to break a paragraph. This
is how much of a break we have accumulated so far since we last
wrapped. If a request to break is greater than this number, we
bump the new Y position by that much further. */
protected int breakSoFar;
/** This is the window we're laying objects out in. */
public TextWindow win;
private int heightChunk;
public Formatter(TextWindow w) {
win = w;
}
private Font newFont() {
return win.getFont(currentParameters.fontName,
currentParameters.fontAttr,
currentParameters.fontSize);
}
private Color newColor() {
Integer colorID = new Integer((currentParameters.r << 16) +
(currentParameters.g << 8) +
currentParameters.b);
Color color;
if ((color = (Color) colors.get(colorID)) == null) {
color = new Color(win.wServer,
currentParameters.r,
currentParameters.g,
currentParameters.b);
colors.put(colorID, color);
}
return color;
}
public void setParameters(FormattingParameters fp) {
if ((currentParameters = fp) != null) {
font = newFont();
fontmetrics = win.getFontMetrics(font);
color = newColor();
if (atBeginningOfLine()) {
x = currentParameters.leftMargin;
}
xlimit = win.width - currentParameters.rightMargin;
}
}
protected void finishCurrentLine() {
int limit = itemCount;
int offset;
switch (currentParameters.alignment) {
case FormattingParameters.ALIGN_LEFT:
default:
offset = 0;
break;
case FormattingParameters.ALIGN_RIGHT:
offset = xlimit - x;
break;
case FormattingParameters.ALIGN_CENTER:
offset = (xlimit - x) / 2;
break;
}
while (lineStartIndex < limit) {
DisplayItem di = win.nthItem(lineStartIndex++);
adjustItem(di, di.x + offset);
}
int height = lineAscent + lineDescent;
if (height > 0) {
y += height + 1;
} else if (fontmetrics != null) {
y += fontmetrics.height + 1;
}
((TextWindow) win).startNewLine(itemCount);
win.logicalHeight = y;
int hChunk = y >> 10;
if (hChunk > heightChunk) {
win.updateScrollbar();
heightChunk = hChunk;
}
}
protected void adjustItem(DisplayItem di, int x) {
di.move(x, di.y);
if (di instanceof TextDisplayItem) {
((TextDisplayItem) di).moveBaseline(y + lineAscent);
}
}
protected abstract int getLength();
protected abstract int charAt(int i);
protected abstract TextDisplayItem makeTextItem(int pos0, int pos1);
protected boolean outputString(int pos0, int pos1, int width) {
if (notRendering > 0) {
return true;
}
TextDisplayItem t = makeTextItem(pos0, pos1);
int ascent = fontmetrics.ascent;
int descent = fontmetrics.descent;
t.setup(font, color, x, y, width, fontmetrics.height);
if (ascent > lineAscent) {
lineAscent = ascent;
}
if (ascent > fontAscent) {
fontAscent = ascent;
}
if (descent > lineDescent) {
lineDescent = descent;
}
t.valid = true;
return addDisplayItem(t, false);
}
/* Process all the style refs at position pos, and then return
the next text position that we have to worry about style refs.
By default, assume no style refs. */
protected int processStyleRefs(int pos) {
return getLength();
}
protected void setBreakSoFar(int value) {
// System.out.println("breakSoFar: " + breakSoFar + " => " + value);
breakSoFar = value;
}
protected void reset() {
fpStack.setSize(0);
heightChunk = -1;
lineStartIndex = itemCount = win.count();
y = x = 0;
lineAscent = fontAscent = lineDescent = 0;
/* setting this high causes all leading breaks to be ignored. */
setBreakSoFar(10000);
}
public void layout() {
reset();
win.startNewLine(0);
pushParameters(new FormattingParameters(currentParameters));
int lastSpaceX;
int lastSpace;
int x = this.x;
int xlimit = this.xlimit;
int posLimit = getLength();
int pos = 0;
int pos0 = 0;
int stylePos = 0;
int wrap = 0;
int widths[] = null;
int lastWrapPos = -1;
stylePos = 0;
lastSpaceX = -1;
lastSpace = -1;
try {
while (pos < posLimit) {
if (pos >= stylePos) {
if (pos > pos0) {
outputString(pos0, pos, x - this.x);
x = this.x;
pos0 = pos;
}
this.x = x;
stylePos = processStyleRefs(pos);
x = this.x;
widths = fontmetrics.widths;
wrap = currentParameters.wrapStyle;
xlimit = this.xlimit;
lastSpace = pos;
if (win.formatter != this) {
System.out.println("Formatter aborting!");
return;
}
} else if (notRendering > 0) {
pos++;
continue;
}
int c = charAt(pos);
int newx;
if (c == '\t') {
int tabWidth = widths[' '] * 8;
if (pos > pos0) {
outputString(pos0, pos, x - this.x);
x = this.x;
}
pos0 = pos + 1;
newx = (x += tabWidth
- ((x - currentParameters.leftMargin) % tabWidth));
this.x = newx;
} else {
if (c == ' ') {
lastSpace = pos;
lastSpaceX = x;
}
newx = x + widths[c];
}
if ((newx >= xlimit && wrap != FormattingParameters.WRAP_NOT)
|| c == '\n') {
if (c != '\n' && wrap == FormattingParameters.WRAP_WORD
&& (lastSpace > pos0
|| this.x != currentParameters.leftMargin)) {
pos = lastSpace;
x = lastSpaceX;
}
if (pos > pos0) {
if (!outputString(pos0, pos, x - this.x)) {
System.out.println("Formatter aborting!");
return;
}
}
if (c != '\n') {
while (pos < posLimit && (c = charAt(pos)) == ' ')
pos += 1;
}
if (c == '\n') {
pos += 1;
}
wrapLine();
lastSpace = pos0 = pos;
x = this.x;
if (pos == lastWrapPos) {
System.out.println("Breaking: infinite loop");
break;
}
lastWrapPos = pos;
continue;
}
x = newx;
pos += 1;
}
if (pos > pos0) {
outputString(pos0, pos, x - this.x);
}
this.x = x;
stylePos = processStyleRefs(pos);
breakLine(12); /* add a newline to the end of the document */
popParameters();
} finally {
win.logicalHeight = y;
}
}
public void pushParameters(FormattingParameters p) {
if (p == null) {
p = new FormattingParameters(currentParameters);
}
fpStack.push(currentParameters);
setParameters(p);
}
public void popParameters() {
FormattingParameters fp;
fp = (FormattingParameters) fpStack.pop();
setParameters(fp);
}
public FormattingParameters getParameters() {
return currentParameters;
}
public boolean addDisplayItem(DisplayItem di, boolean checkWrap) {
if (win.formatter != this) {
return false;
}
win.addItem(di);
if (checkWrap && x + di.width > xlimit) {
wrapLine();
}
/* display are aligned to the text baseline by default */
lineAscent = Math.max(lineAscent, di.height - lineDescent);
di.move(x, y);
if (debug) {
System.out.println("Adding #" + itemCount + " = " + di);
}
x += di.width;
itemCount += 1;
setBreakSoFar(-1);
return true;
}
public void setLeftMargin(int lm) {
currentParameters.leftMargin = (short)lm;
setParameters(currentParameters);
}
public boolean atBeginningOfLine() {
return (lineStartIndex == itemCount
|| x == currentParameters.leftMargin);
}
public int getLeftMargin() {
return currentParameters.leftMargin;
}
public int getRightMargin() {
return win.width - currentParameters.rightMargin;
}
public void setXCoordinate(int x) {
this.x = x;
}
public int getXCoordinate() {
return x;
}
public int getYCoordinate() {
return y;
}
public void addCharacterSpacing(char c) {
int width = fontmetrics.widths[c];
if (x + width < xlimit) {
x += width;
}
}
public void wrapLine() {
if (debug) {
System.out.println("Wrapping");
}
setBreakSoFar(0);
finishCurrentLine();
x = currentParameters.leftMargin;
/* At some point I felt the need to change the line below
to the lines that are commented out above. That caused
other problems which I am fixing now. Hopefully next time
I won't have to remember why I did this! */
lineAscent = fontAscent = lineDescent = 0;
}
public void breakLine(int extra) {
if (breakSoFar == -1) {
wrapLine();
}
if (extra > breakSoFar) {
y += (extra - breakSoFar);
setBreakSoFar(extra);
}
}
public Font getFont() {
return font;
}
public Color getColor() {
return color;
}
public void stopRendering() {
notRendering += 1;
}
public void startRendering() {
notRendering -= 1;
}
}