blob: 2805c987f6271b3e81f0d865502533adab57dea9 [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.IFigure;
import org.eclipse.jst.pagedesigner.IHTMLConstants;
import org.eclipse.jst.pagedesigner.css2.ICSSStyle;
import org.eclipse.jst.pagedesigner.css2.property.ICSSPropertyID;
import org.eclipse.jst.pagedesigner.css2.style.AbstractStyle;
import org.eclipse.swt.graphics.Font;
import org.w3c.dom.Element;
/**
* @author mengbo
*/
// NOTE: CSSTextLayout does not extends CSSFlowLayout. Since text is a little
// special,
// we don't want to do things like "preLayout()" as in CSSFlowLayout.
public class CSSTextLayout extends FlowFigureLayout {
/**
* Wrapping will ONLY occur at valid line breaks
*/
public static final int WORD_WRAP_HARD = 0;
/**
* Wrapping will always occur at the end of the available space, breaking in
* the middle of a word.
*/
public static final int WORD_WRAP_SOFT = 1;
/**
* Wrapping will always occur at the end of available space, truncating a
* word if it doesn't fit.
*/
// don't support this flag
// public static final int WORD_WRAP_TRUNCATE = 2;
private int _wrappingStyle = WORD_WRAP_HARD;
/**
* @param textfigure
*/
public CSSTextLayout(CSSTextFigure textfigure) {
super(textfigure);
}
// --------------------------------------------------------------------------------------------------
FlowBox findLastNonLineBox(LineBox box) {
List fragments = box.getFragments();
for (int i = fragments.size() - 1; i >= 0; i--) {
FlowBox item = (FlowBox) fragments.get(i);
if (item instanceof LineBox) {
FlowBox found = findLastNonLineBox((LineBox) item);
if (found != null) {
return found;
}
} else {
return item;
}
}
return null;
}
// boolean isElementContentWhitespaceEnding()
// {
// if (!this._context.isCurrentLineOccupied())
// return true;
// LineBox line = this._context.getCurrentLine();
// FlowBox lastNoneLinebox = findLastNonLineBox(line);
// if (lastNoneLinebox instanceof TextFragmentBox)
// return ((TextFragmentBox) lastNoneLinebox)._isLastCharWhitespace;
// else
// return true;
// }
//
// String normalize(String text)
// {
// text = EntityMap.translateAndCompact(text);
// if (text.length() > 0 &&
// Character.isElementContentWhitespace(text.charAt(0)) &&
// isElementContentWhitespaceEnding())
// return text.substring(1);
// else
// return text;
// }
private void layoutEmptyString(List fragments, Font font) {
// empty node! we want to create a fake fragment, so things can be
// consistent
// that all the CSSTextFigure will have something inside, also in this
// way, even
// empty text node will have a position, thus we can support showing
// caret associated
// with this text figure.
fragments.clear();
TextFragmentBox box = TextLayoutSupport.getFragment(0, fragments);
box._length = 0;
box._offset = 0;
box._height = 0;
box._width = 0;
box.setTextData("");
// {following comments deprecated XXX: If is empty string, we only want
// to this figure to have a size, but don't
// want to it to be added into current line. Otherwise, a line with only
// a empty string
// will also take a line's space.}
// please reference LineBox.isOccupied()
// now we treat a line with only an empty text as not occupied.
getFlowContext().getCurrentLine().add(box);
}
/**
* @see org.eclipse.jst.pagedesigner.css2.layout.FlowFigureLayout#layout()
*/
protected void layout() {
CSSTextFigure flowFigure = (CSSTextFigure) getFlowFigure();
List fragments = flowFigure.getFragments();// Reuse the previous List
// of fragments
String text = flowFigure.getText();
Font font = flowFigure.getCSSStyle().getCSSFont().getSwtFont();
Object whitespace = flowFigure.getCSSStyle().getStyleProperty(
ICSSPropertyID.ATTR_WHITESPACE);
if (whitespace == ICSSPropertyID.VAL_PRE) {
if (text == null || text.length() == 0)
layoutEmptyString(fragments, font);
else
TextLayoutSupport.layoutNoWrap(getFlowContext(), text,
fragments, font);
} else if (whitespace == ICSSPropertyID.VAL_NOWRAP) {
if (text == null || text.length() == 0)
layoutEmptyString(fragments, font);
else
TextLayoutSupport.layoutNoWrap(getFlowContext(), text,
fragments, font);
} else {
if (text == null || text.length() == 0)
layoutEmptyString(fragments, font);
else {
//fix for bug #221629 - BEGIN
boolean useShouldTrimLeadingWSInlineMethod = false;
IFigure parentFigure = flowFigure.getParent();
if (parentFigure instanceof CSSFigure) {
ICSSStyle style = ((CSSFigure)parentFigure).getCSSStyle();
if (style instanceof AbstractStyle) {
Element element = ((AbstractStyle)style).getElement();
if (element != null &&
element.getNodeName().equals(IHTMLConstants.TAG_SPAN)) {
useShouldTrimLeadingWSInlineMethod = true;
}
}
}
boolean trimLeadingChar;
if (!useShouldTrimLeadingWSInlineMethod) {
trimLeadingChar = (text.charAt(0) == ' ' && shouldTrimLeadingWhitespace(getFlowContext()));
} else {
trimLeadingChar = (text.charAt(0) == ' ' && shouldTrimLeadingWhitespaceInline(getFlowContext()));
}
//fix for bug #221629 - END
TextLayoutSupport.layoutNormal(getFlowContext(), text,
fragments, font, _wrappingStyle, trimLeadingChar);
}
}
}
/**
* @param context
* @return true if should trim leading whitespace
*/
// XXX: maybe should move to TextSupport later.
public boolean shouldTrimLeadingWhitespace(FlowContext context) {
if (!context.isCurrentLineOccupied()) {
return true;
}
while (context instanceof CSSInlineFlowLayout) {
context = ((CSSInlineFlowLayout) context).getFlowContext();
}
LineBox line = context.getCurrentLine();
if (line == null || !line.isOccupied()) {
return true;
}
FlowBox lastNoneLinebox = findLastNonLineBox(line);
if (lastNoneLinebox == null || lastNoneLinebox.getWidth() == 0) {
return true;
} else if (lastNoneLinebox instanceof TextFragmentBox) {
return ((TextFragmentBox) lastNoneLinebox)._isLastCharWhitespace;
} else {
return false;
}
}
/**
* Used instead of shouldTrimLeadingWhitespace(FlowContext) if parent
* figure's style is for an appropriate in-line element, such as "span".
*
* @param context FlowContext instance.
* @return true if should trim leading whitespace, else false.
*/
private boolean shouldTrimLeadingWhitespaceInline(FlowContext context) {
if (!context.isCurrentLineOccupied()) {
return true;
}
LineBox line = context.getCurrentLine();
if (line == null || !line.isOccupied()) {
return true;
}
FlowBox lastNoneLinebox = findLastNonLineBox(line);
if (lastNoneLinebox == null || lastNoneLinebox.getWidth() == 0) {
return true;
} else if (lastNoneLinebox instanceof TextFragmentBox) {
return ((TextFragmentBox) lastNoneLinebox)._isLastCharWhitespace;
} else {
return false;
}
}
public void dispose() {
// TODO: anything to dispose?
}
}