org.eclipse.swt.custom.StyledTextWriterBase Maven / Gradle / Ivy
Show all versions of org.eclipse.swt.gtk.linux.aarch64 Show documentation
/*******************************************************************************
* Copyright (c) 2000, 2022 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.custom;
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
/**
* The base class encapsulating the logic used to write styled content as
* a rich format (HTML, RTF, maybe more). It contains the common parts:
* iterates on lines, then on each line iterates on plain text and {@link StyleRange}(s).
*
* {@code toString()} is guaranteed to return a valid formatted string only after
* {@code close()} has been called.
*
* Whole and partial lines and line breaks can be written. Lines will be
* formatted using the styles queried from the {@link LineStyleListener},
* if set, or those set directly in the widget. All styles are applied to
* the stream like they are rendered by the widget. In addition, the
* widget font name and size is used for the whole text.
*/
abstract class StyledTextWriterBase extends TextWriter {
final StyledText styledText;
/**
* Creates a writer that processed content starting at offset "start"
* in the document. {@code start} and {@code length} can be set to specify
* partial lines.
*
* @param start start offset of content to write, 0 based from beginning of document
* @param length length of content to write
* @param styledText the widget to produce the RTF from
*/
public StyledTextWriterBase(StyledText styledText, int start, int length) {
super(start, length);
this.styledText = styledText;
}
/**
* Appends the specified segment of "string" to the output data.
* Copy from {@code start} up to, but excluding, {@code end}.
*
* @param string string to copy a segment from. Must not contain line breaks.
* Line breaks should be written using {@link #writeLineDelimiter()}
* @param start start offset of segment. 0 based.
* @param end end offset of segment
*/
void writeEscaped(String string, int start, int end) {
String textToWrite = string.substring(start, end);
write(escapeText(textToWrite));
}
/**
* Appends the specified line text to the output data. Lines will be formatted
* using the styles queried from the LineStyleListener, if set, or those set
* directly in the widget.
*
* @param line line text to write. Must not contain line breaks
* Line breaks should be written using {@link #writeLineDelimiter(String)}
* @param lineOffset offset of the line. 0 based from the start of the
* widget document. Any text occurring before the start offset or after the
* end offset specified during object creation is ignored.
*
* @throws SWTException {@code ERROR_IO} when the writer is closed.
*/
@Override
public void writeLine(String line, int lineOffset) {
if (isClosed()) {
SWT.error(SWT.ERROR_IO);
}
int lineIndex = styledText.content.getLineAtOffset(lineOffset);
int lineAlignment;
int lineIndent;
boolean lineJustify;
int verticalIndent;
int[] ranges;
StyleRange[] styles;
StyledTextEvent event = styledText.getLineStyleData(lineOffset, line);
if (event != null) {
verticalIndent = event.verticalIndent;
lineAlignment = event.alignment;
lineIndent = event.indent;
lineJustify = event.justify;
ranges = event.ranges;
styles = event.styles;
} else {
verticalIndent = styledText.renderer.getLineVerticalIndent(lineIndex);
lineAlignment = styledText.renderer.getLineAlignment(lineIndex, styledText.alignment);
lineIndent = styledText.renderer.getLineIndent(lineIndex, styledText.indent);
lineJustify = styledText.renderer.getLineJustify(lineIndex, styledText.justify);
ranges = styledText.renderer.getRanges(lineOffset, line.length());
styles = styledText.renderer.getStyleRanges(lineOffset, line.length(), false);
}
if (styles == null) {
styles = new StyleRange[0];
}
event = styledText.getLineBackgroundData(lineOffset, line);
Color lineBackground = (event != null && event.lineBackground != null)
? event.lineBackground
: styledText.renderer.getLineBackground(lineIndex, null);
writeStyledLine(line, lineOffset, ranges, styles, lineBackground, lineIndent, verticalIndent, lineAlignment, lineJustify);
}
/**
* Appends the specified line text to the output data.
*
* Use the colors and font styles specified in {@code styles} and {@code lineBackground}.
* Formatting is written to reflect the text rendering by the text widget.
* Style background colors take precedence over the line background color.
*
* @param line line text to write as RTF. Must not contain line breaks
* Line breaks should be written using writeLineDelimiter()
* @param lineOffset offset of the line. 0 based from the start of the
* widget document. Any text occurring before the start offset or after the
* end offset specified during object creation is ignored.
* @param styles styles to use for formatting. Must not be null.
* @param lineBackground line background color to use for formatting.
* May be null.
*/
void writeStyledLine(String line, int lineOffset, int ranges[], StyleRange[] styles,
Color lineBackground, int indent, int verticalIndent, int alignment, boolean justify) {
int lineLength = line.length();
int startOffset = getStart();
int writeOffset = startOffset - lineOffset;
if (writeOffset >= lineLength) {
return;
}
int lineIndex = Math.max(0, writeOffset);
String atLineEnd = writeLineStart(lineBackground, indent, verticalIndent, alignment, justify);
int endOffset = startOffset + super.getCharCount();
// We are already at the end, should not write an empty paragraph
if (lineOffset >= endOffset) {
return;
}
int lineEndOffset = Math.min(lineLength, endOffset - lineOffset);
int outTextLen = 0; // collect the length of the text we output (unescaped)
for (int i = 0; i < styles.length; i++) {
StyleRange style = styles[i];
int start, end;
if (ranges != null) {
start = ranges[i << 1] - lineOffset;
end = start + ranges[(i << 1) + 1];
} else {
start = style.start - lineOffset;
end = start + style.length;
}
// skip over partial first line
if (end < writeOffset) {
continue;
}
// style starts beyond line end or write end
if (start >= lineEndOffset) {
break;
}
// write any unstyled text
if (lineIndex < start) {
// copy to start of style
// style starting beyond end of write range or end of line
// is guarded against above.
writeEscaped(line, lineIndex, start);
outTextLen += start - lineIndex;
lineIndex = start;
}
// write styled text
String atSpanEnd = writeSpanStart(style);
// copy to end of style or end of write range or end of line
int copyEnd = Math.min(end, lineEndOffset);
// guard against invalid styles and let style processing continue
copyEnd = Math.max(copyEnd, lineIndex);
writeEscaped(line, lineIndex, copyEnd);
outTextLen += copyEnd - lineIndex;
writeSpanEnd(atSpanEnd);
lineIndex = copyEnd;
}
// write the unstyled text at the end of the line
if (lineIndex < lineEndOffset) {
writeEscaped(line, lineIndex, lineEndOffset);
outTextLen += lineEndOffset - lineIndex;
}
if (outTextLen == 0) {
writeEmptyLine();
}
writeLineEnd(atLineEnd);
}
/**
* Writes the part that only shows once, at the beginning of the output data.
*/
abstract void writeHeader();
/**
* Takes plain text and returns the same text escaped using the rules of the output format.
* @param text the text to escape
* @return the escaped text
*/
abstract String escapeText(String text);
/**
* Invoked at the beginning of each line in the original widget.
*
* It should output whatever tags are appropriate to start a new line in the output format.
* It will return the text that has to be output at the end of the line.
*
* @return the text to append at the end of the line
*/
abstract String writeLineStart(Color lineBackground, int indent, int verticalIndent, int alignment, boolean justify);
/**
* Invoked at the end of of each line in the original widget.
*
* It receives whatever text {@link #writeLineStart(Color,int,int,int,boolean)} returned
* and it will output it. A class might override it to do more.
*
* @param prepared the leftover text prepared by {@code writeLineStart}
*/
void writeLineEnd(String prepared) {
write(prepared);
}
/**
* Invoked at the end of each line, writeLineEnd, if there was no text output.
*
* This is needed for HTML, which does not render empty paragraphs.
*/
abstract void writeEmptyLine();
/**
* Invoked at the beginning of each styled span fragment in the original widget.
*
* It should output whatever tags are appropriate to start a formatted span in the output format.
* It will return the text that has to be output at the end of the span (to close it).
*
* @return the text to append at the end of the styled span
*/
abstract String writeSpanStart(StyleRange style);
/**
* Invoked at the end of of each styled span fragment in the original widget.
*
* It receives whatever text {@link #writeSpanStart(StyleRange)} returned
* and it will output it. A class might override it to do more.
*
* @param prepared the leftover text prepared by {@code writeSpanStart}
*/
void writeSpanEnd(String prepared) {
write(prepared);
}
}