blob: 7b31c90f842799a41f8b935e2b55c4ea566ea4e1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006 Sybase, Inc. and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Sybase, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.jst.pagedesigner.css2.layout;
import java.util.List;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.jst.jsf.common.ui.internal.logging.Logger;
import org.eclipse.jst.pagedesigner.PDPlugin;
import org.eclipse.jst.pagedesigner.css2.property.TextDecorationMeta;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
/**
* @author mengbo
*/
public class TextLayoutSupport {
private static final Logger _log = PDPlugin
.getLogger(TextLayoutSupport.class);
private static final String[] DELIMITERS = { "\r\n", //$NON-NLS-1$
"\n", //$NON-NLS-1$
"\r" //$NON-NLS-1$
};
static private int delimeterLength;
/**
* Reuses an existing <code>TextFragmentBox</code>, or creates a new one.
*
* @param i
* the index
* @param fragments
* the original list of fragments
* @return a TextFragmentBox
*/
// copied from TextLayout
protected static TextFragmentBox getFragment(int i, List fragments) {
if (fragments.size() > i) {
return (TextFragmentBox) fragments.get(i);
}
TextFragmentBox box = new TextFragmentBox();
fragments.add(box);
return box;
}
/**
* Returns the average character width of given TextFragmentbox
*
* @param fragment
* the TextFragmentBox
* @return the average character width
*/
public static float getAverageCharWidth(TextFragmentBox fragment) {
if (fragment._width != 0 && fragment._length != 0) {
return fragment._width / (float) fragment._length;
}
return 0.0f;
}
// ----------------------------------------------------------------------------------------
/**
* this method will create a set of TextFragment. Each fragment will offset
* to the original text (whole text for the text figure).
* @param context
* @param text
* @param fragments
* @param font
* @param wrappingStyle
* @param trimLeading
*/
public static void layoutNormal(FlowContext context, String text,
List fragments, Font font, int wrappingStyle, boolean trimLeading) {
int i = 0; // The index of the current fragment;
int offset = 0;
if (trimLeading) {
offset = 1;
text = text.substring(1);
}
int length = 0; // The length of the current fragment
float prevAvgCharWidth;
LineBox currentLine;
TextFragmentBox fragment;
while (text.length() > 0) {
fragment = null;
prevAvgCharWidth = 0f;
fragment = getFragment(i, fragments);
prevAvgCharWidth = getAverageCharWidth(fragment);
// Check for newline, if it exists, call context.endLine and skip
// over the newline
// Exccept for first time through, don't do this.
if (i != 0) {
boolean changed = false;
if (text.charAt(0) == '\r') {
text = text.substring(1);
changed = true;
offset += 1;
}
if (text.length() != 0 && text.charAt(0) == '\n') {
text = text.substring(1);
changed = true;
offset += 1;
}
if (changed) {
context.endLine();
}
}
fragment._offset = offset;
// This loop is done at most twice.
// The second time through, a context.endLine()
// was requested, and the loop will break.
while (true) {
currentLine = context.getCurrentLine();
length = FlowUtilities.setupFragmentBasedOnTextSpace(fragment,
text, font, currentLine.getAvailableWidth(),
prevAvgCharWidth, wrappingStyle);
if (fragment._width <= currentLine.getAvailableWidth()
|| !context.isCurrentLineOccupied()) {
break;
}
context.endLine();
}
// fragment.x = context.getCurrentX();
context.addToCurrentLine(fragment);
text = text.substring(length);
offset += length;
if (text.length() > 0) {
context.endLine();
}
i++;
}
// Remove the remaining unused fragments.
while (i < fragments.size()) {
fragments.remove(fragments.size() - 1);
}
}
/**
* @param context
* @param text
* @param fragments
* @param font
*/
public static void layoutNoWrap(FlowContext context, String text,
List fragments, Font font) {
TextFragmentBox fragment;
int i = 0;
int offset = 0;
while (offset < text.length()) {
int result = nextLineBreak(text, offset);
fragment = getFragment(i++, fragments);
fragment._length = result - offset;
fragment._offset = offset;
FlowUtilities.setupFragment(fragment, font, text.substring(offset));
context.getCurrentLine().add(fragment);
offset = result + delimeterLength;
if (delimeterLength != 0) {
// in nextLineBreak we fo
context.endLine();
}
}
// Remove the remaining unused fragments.
while (i < fragments.size()) {
fragments.remove(i++);
}
}
private static int nextLineBreak(String text, int offset) {
int result = text.length();
delimeterLength = 0;
int current;
for (int i = 0; i < DELIMITERS.length; i++) {
current = text.indexOf(DELIMITERS[i], offset);
if (current != -1 && current < result) {
result = current;
delimeterLength = DELIMITERS[i].length();
}
}
return result;
}
/**
* @param g
* @param fragments
* @param font
* @param textDecoration
*/
public static void paintTextFigure(Graphics g, List fragments, Font font,
int textDecoration) {
paintTextFigure(g, fragments, font, null, textDecoration);
}
/**
* @param g
* @param rect
* @param textDecoration
*/
public static void paintTextDecoration(Graphics g, Rectangle rect,
int textDecoration) {
if ((textDecoration & TextDecorationMeta.UNDERLINE) != 0) {
g.drawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width
- 1, rect.y + rect.height - 1);
}
if ((textDecoration & TextDecorationMeta.OVERLINE) != 0) {
g.drawLine(rect.x, rect.y + 1, rect.x + rect.width - 1, rect.y + 1);
}
if ((textDecoration & TextDecorationMeta.LINETHROUGH) != 0) {
g.drawLine(rect.x, rect.y + rect.height / 2, rect.x + rect.width
- 1, rect.y + rect.height / 2);
}
}
/**
* @param g
* @param fragments
* @param font
* @param color
* @param textDecoration
*/
public static void paintTextFigure(Graphics g, List fragments, Font font,
Color color, int textDecoration) {
// FIXME: It happens there is problem in this method's parameters. what
// exception should be catched?
try {
TextFragmentBox frag;
// XXX: adjust font. Here is not using setFont(), because that will
// result in revalidate
g.setFont(font);
for (int i = 0; i < fragments.size(); i++) {
frag = (TextFragmentBox) fragments.get(i);
// if (!g.getClip(Rectangle.SINGLETON).intersects(frag))
// continue;
String draw;
draw = frag.getTextData();
if (color != null) {
g.setForegroundColor(color);
}
g.drawText(draw, frag._x, frag._y);
if ((textDecoration & TextDecorationMeta.UNDERLINE) != 0) {
g.drawLine(frag._x, frag._y + frag.getHeight() - 1, frag._x
+ frag.getWidth(), frag._y + frag.getHeight() - 1);
}
if ((textDecoration & TextDecorationMeta.OVERLINE) != 0) {
g.drawLine(frag._x, frag._y, frag._x + frag.getWidth(),
frag._y);
}
if ((textDecoration & TextDecorationMeta.LINETHROUGH) != 0) {
g.drawLine(frag._x, frag._y + frag.getHeight() / 2, frag._x
+ frag.getWidth(), frag._y + frag.getHeight() / 2);
}
if (Debug.DEBUG_BASELINE) {
g.drawLine(frag._x, frag._y + frag.getAscent(), frag._x
+ frag.getWidth(), frag._y + frag.getAscent());
}
}
} catch (Exception e) {
// "Error in text painting:"
_log.info("TextLayoutSupport.Info.1", e); //$NON-NLS-1$
}
}
/**
*
* @param g
* @param fragments
* @param text
* all the text in the Text figure.
* @param font
* @param color
* @param textDecoration
* @param start
* @param end
* @param selectionForeColor
* @param selectionBackColor
*/
public static void paintTextFigureWithSelection(Graphics g, List fragments,
String text, Font font, Color color, int textDecoration, int start,
int end, Color selectionForeColor, Color selectionBackColor) {
// FIXME: It happens there is problem in this method's parameters. what
// exception should be catched?
try {
TextFragmentBox frag;
Color originalForeground = g.getForegroundColor();
Color originalBackgroud = g.getBackgroundColor();
// XXX: adjust font. Here is not using setFont(), because that will
// result in revalidate
g.setFont(font);
for (int i = 0, n = fragments.size(); i < n; i++) {
frag = (TextFragmentBox) fragments.get(i);
// to make things simpler, we always draw the line using default
// color
if (color != null) {
g.setForegroundColor(color);
}
// if (!g.getClip(Rectangle.SINGLETON).intersects(frag))
// continue;
String draw;
draw = frag.getTextData();
if (frag._offset >= end || frag._offset + frag._length <= start) {
// we are not in selection. no need to change color
g.drawText(draw, frag._x, frag._y);
paintTextDecoration(g, frag.getRectangle(), textDecoration);
} else if (frag._offset >= start
&& frag._offset + frag._length <= end) {
// we are fully in selection
g.setForegroundColor(selectionForeColor);
g.setBackgroundColor(selectionBackColor);
g
.fillRectangle(frag._x, frag._y, FlowUtilities
.getTextExtents(draw, font).width, frag
.getHeight());
g.drawText(draw, frag._x, frag._y);
paintTextDecoration(g, frag.getRectangle(), textDecoration);
} else {
// partial of the fragment's text is in selection.
// draw the original string first
g.drawText(draw, frag._x, frag._y);
// then override with the selected parts.
g.setForegroundColor(selectionForeColor);
g.setBackgroundColor(selectionBackColor);
int partialStart = frag._offset > start ? frag._offset
: start;
int partialEnd = (frag._offset + frag._length > end) ? end
: (frag._offset + frag._length);
int x = 0;
String skip = text.substring(frag._offset, partialStart);
x = FlowUtilities.getTextExtents(skip, font).width;
String todraw = text.substring(partialStart, partialEnd);
if (todraw.length() > 0) {
Dimension dimension = FlowUtilities.getTextExtents(skip
+ todraw, font);
g.fillRectangle(frag._x + x, frag._y, dimension.width
- x, dimension.height);
g.drawText(skip + todraw, frag._x, frag._y);
if (color != null) {
g.setForegroundColor(color);
} else {
g.setForegroundColor(originalForeground);
}
g.drawText(skip, frag._x, frag._y);
paintTextDecoration(g, frag.getRectangle(),
textDecoration);
g.setForegroundColor(selectionForeColor);
paintTextDecoration(g,
new Rectangle(frag._x + x, frag._y,
dimension.width - x, dimension.height),
textDecoration);
}
}
// we do this in each loop, to make sure we are using correct
// color
g.setForegroundColor(originalForeground);
g.setBackgroundColor(originalBackgroud);
}
} catch (Exception e) {
// "Error in text painting:"
_log.info("TextLayoutSupport.Info.1", e); //$NON-NLS-1$
}
}
/**
* @param textAlign
* @param rect
* @param textWidth
* @return the x value
*/
public static int getBeginX(Object textAlign, Rectangle rect, int textWidth) {
int x = rect.x;
if (textAlign != null) {
String align = textAlign.toString();
if ("left".equalsIgnoreCase(align)) //$NON-NLS-1$
{
x = rect.x + 1;
} else if ("right".equalsIgnoreCase(align)) //$NON-NLS-1$
{
x = rect.x + rect.width - textWidth - 1;
if (x < 1) {
x = 1;
}
} else if ("center".equalsIgnoreCase(align)) //$NON-NLS-1$
{
int offset = (rect.width - textWidth) / 2;
if (offset <= 0) {
offset = 0;
}
x = x + offset + 1;
}
}
return x;
}
private TextLayoutSupport()
{
// no instantiation
}
}