/*****************************************************************************
* Copyright (c) 2015 CEA LIST.
*
* 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:
* Dirk Fauth <dirk.fauth@googlemail.com> - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.nebula.widgets.richtext.painter;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.Point;
/**
* This class is used to keep track of the style setting regarding opened tags. It is needed to
* support styling ranges over several lines or rendering, because tags can be opened for rendering
* on one line, while it is closed on another line.
*/
public class TagProcessingState {
/**
* Enumeration to specify the text alignment in a paragraph.
*/
public enum TextAlignment {
LEFT, CENTER, RIGHT, JUSTIFY
}
/**
* The coordinates where the drawing operations should start from.
*/
private Point startingPoint = new Point(0, 0);
/**
* The coordinates where the drawing operations should be performed from.
*/
private Point pointer = new Point(0, 0);
/**
* The left margin in a paragraph.
*/
private int marginLeft;
/**
* The alignment of the current paragraph.
*/
private TextAlignment textAlignment = TextAlignment.LEFT;
/**
* Stack of previous set foreground colors. Usually not necessary as the CKEDITOR closes spans
* before opening new ones regarding colors, but since HTML supports nesting of spans to apply
* colors on the way, a stack is necessary to avoid exceptions then.
*/
private Deque<Color> colorStack = new LinkedList<>();
/**
* Stack of previous set background colors. Usually not necessary as the CKEDITOR closes spans
* before opening new ones regarding colors, but since HTML supports nesting of spans to apply
* colors on the way, a stack is necessary to avoid exceptions then.
*/
private Deque<Color> bgColorStack = new LinkedList<>();
/**
* Flag that indicates whether underline styling is active or not. Necessary because the
* underline tag can wrap several other font or styling tags.
*/
private boolean underlineActive = false;
/**
* Flag that indicates whether strikethrough styling is active or not. Necessary because the
* strikethrough tag can wrap several other font or styling tags.
*/
private boolean strikethroughActive = false;
/**
* Stack of used fonts. Necessary because font styling options can be nested (e.g. bold, size,
* type) and therefore need to be reset in the correct order on close.
*/
private Deque<Font> fontStack = new LinkedList<>();
/**
* Flag that indicates whether the current list is an ordered or unordered list. Necessary for
* rendering the list item bullet.
*/
private Deque<Boolean> orderedListStack = new LinkedList<>();
/**
* Current list item number. Necessary for rendering an ordered list.
*/
private Deque<Integer> listNumberStack = new LinkedList<>();
/**
* The additional margin that is used on rendering a list. Necessary to align the text part of
* lists.
*/
private Deque<Integer> listMarginStack = new LinkedList<>();
/**
* The {@link LinePainter} of the current rendered line.
*/
private LinePainter currentLine;
/**
* The {@link Iterator} used to iterate over the lines that are rendered.
*/
private Iterator<LinePainter> lineIterator;
/**
* Flag that indicates whether drawing operations should be performed or not. Necessary for
* embedded rendering to be able to calculate the dynamic dimensions without rendering directly.
*/
private boolean rendering = true;
/**
* The number of paragraphs that are found in a text. Needed to calculate the preferred height.
*/
private int paragraphCount = 0;
/**
* Add the given {@link Color} to the stack of previous set foreground colors.
*
* @param prevColor
* The {@link Color} to add to the previous foreground color stack.
*/
public void addPreviousColor(Color prevColor) {
this.colorStack.addLast(prevColor);
}
/**
* Removes and returns the last color from the previous foreground color stack. (LIFO)
*
* @return The last {@link Color} that was added to the previous foreground color stack.
*/
public Color pollPreviousColor() {
return this.colorStack.pollLast();
}
/**
* Add the given {@link Color} to the stack of previous set background colors.
*
* @param prevBgColor
* The {@link Color} to add to the previous background color stack.
*/
public void addPreviousBgColor(Color prevBgColor) {
this.bgColorStack.addLast(prevBgColor);
}
/**
* Removes and returns the last color from the previous background color stack. (LIFO)
*
* @return The last {@link Color} that was added to the previous background color stack.
*/
public Color pollPreviousBgColor() {
return this.bgColorStack.pollLast();
}
/**
*
* @return <code>true</code> if there is a previous background color registered on the stack,
* <code>false</code> if not.
*/
public boolean hasPreviousBgColor() {
return !this.bgColorStack.isEmpty();
}
/**
* Add the given {@link Font} to the stack of previous set fonts.
*
* @param font
* The {@link Font} to add to the previous font stack
*/
public void addPreviousFont(Font font) {
this.fontStack.addLast(font);
}
/**
* Removes and returns the last font from the previous font stack. (LIFO)
*
* @return The last {@link Font} that was added to the previous font stack.
*/
public Font pollPreviousFont() {
return this.fontStack.pollLast();
}
/**
* @return <code>true</code> if underline styling is active, <code>false</code> if not.
*/
public boolean isUnderlineActive() {
return underlineActive;
}
/**
* @param underlineActive
* <code>true</code> if underline styling should be active, <code>false</code> if
* not.
*/
public void setUnderlineActive(boolean underlineActive) {
this.underlineActive = underlineActive;
}
/**
* @return <code>true</code> if strikethrough styling is active, <code>false</code> if not.
*/
public boolean isStrikethroughActive() {
return strikethroughActive;
}
/**
* @param strikethroughActive
* <code>true</code> if strikethrough styling should be active, <code>false</code> if
* not.
*/
public void setStrikethroughActive(boolean strikethroughActive) {
this.strikethroughActive = strikethroughActive;
}
public void setStartingPoint(int startX, int startY) {
this.startingPoint.x = startX;
this.startingPoint.y = startY;
this.pointer.x = startX;
this.pointer.y = startY;
}
public Point getPointer() {
return pointer;
}
public void increaseX(int x) {
this.pointer.x += x;
}
public void increaseY(int y) {
this.pointer.y += y;
}
public void setX(int x) {
this.pointer.x = x;
}
public void setY(int y) {
this.pointer.y = y;
}
/**
* Reset the x coordinate of the pointer to the value of the left margin. This is used to start
* a new line.
*/
public void resetX() {
this.pointer.x = this.startingPoint.x + this.marginLeft + getListMargin();
}
public void calculateX(int areaWidth) {
if (textAlignment.equals(TextAlignment.LEFT)
|| textAlignment.equals(TextAlignment.JUSTIFY)) {
this.pointer.x = this.startingPoint.x + this.marginLeft + getListMargin();
} else if (textAlignment.equals(TextAlignment.RIGHT)) {
int space = areaWidth - (this.marginLeft + getListMargin());
this.pointer.x = this.startingPoint.x + this.marginLeft + getListMargin() + (space - getCurrentLine().getContentWidth());
} else if (textAlignment.equals(TextAlignment.CENTER)) {
int space = areaWidth - (this.marginLeft + getListMargin());
this.pointer.x = this.startingPoint.x + this.marginLeft + getListMargin() + ((space - getCurrentLine().getContentWidth()) / 2);
}
}
public int getMarginLeft() {
return marginLeft;
}
public void setMarginLeft(int marginLeft) {
this.marginLeft = marginLeft;
}
public TextAlignment getTextAlignment() {
return textAlignment;
}
public void setTextAlignment(TextAlignment textAlignment) {
this.textAlignment = textAlignment;
}
public LinePainter getCurrentLine() {
return currentLine;
}
public void setLineIterator(Iterator<LinePainter> lineIterator) {
this.lineIterator = lineIterator;
}
public void activateNextLine() {
currentLine = lineIterator.next();
}
public int getCurrentLineHeight() {
return this.currentLine.getLineHeight();
}
public FontMetrics getCurrentBiggestFontMetrics() {
return this.currentLine.getBiggestMetrics();
}
public boolean isRendering() {
return rendering;
}
public void setRendering(boolean render) {
this.rendering = render;
}
public boolean isOrderedList() {
return this.orderedListStack.peekLast();
}
public void setOrderedList(boolean orderedList) {
this.orderedListStack.add(orderedList);
}
public int getListMargin() {
int result = 0;
for (Integer margin : this.listMarginStack) {
result += margin;
}
return result;
}
public void setListMargin(int listMargin) {
this.listMarginStack.add(listMargin);
}
public Integer getCurrentListNumber() {
return this.listNumberStack.peekLast();
}
public void initCurrentListNumber() {
this.listNumberStack.add(1);
}
public void increaseCurrentListNumber() {
int current = this.listNumberStack.pollLast();
current++;
this.listNumberStack.add(current);
}
public int getListDepth() {
return this.listNumberStack.size();
}
public void resetListConfiguration() {
this.listNumberStack.removeLast();
this.listMarginStack.removeLast();
this.orderedListStack.removeLast();
}
public int getParagraphCount() {
return paragraphCount;
}
public void increaseParagraphCount() {
this.paragraphCount++;
}
}