blob: ef9841f6e73462463199b6d154c69ae948823cc9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.custom;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.*;
/**
* A DisplayRenderer renders the content of a StyledText widget on
* a display device.
*/
class DisplayRenderer extends StyledTextRenderer {
private StyledText parent; // used to get content and styles during rendering
private int topIndex = -1;
private TextLayout[] layouts;
/**
* Creates an instance of <class>DisplayRenderer</class>.
* </p>
* @param device Device to render on
* @param regularFont Font to use for regular (non-bold) text
* @param isBidi true=bidi platform, false=no bidi platform
* @param leftMargin margin to the left of the text
* @param parent <class>StyledText</class> widget to render
* @param tabLength length in characters of a tab character
*/
DisplayRenderer(Device device, Font regularFont, StyledText parent, int tabLength) {
super(device, regularFont);
this.parent = parent;
calculateLineHeight();
setTabLength(tabLength);
}
void dispose() {
super.dispose();
if (layouts != null) {
for (int i = 0; i < layouts.length; i++) {
TextLayout layout = layouts[i];
if (layout != null) super.disposeTextLayout(layout);
}
topIndex = -1;
layouts = null;
}
}
/**
* Dispose the specified GC.
* </p>
* @param gc GC to dispose.
*/
protected void disposeGC(GC gc) {
gc.dispose();
}
/**
* Draws the line delimiter selection if the selection extends beyond the given line.
* </p>
*
* @param line the line to draw
* @param lineOffset offset of the first character in the line.
* Relative to the start of the document.
* @param styles line styles
* @param paintY y location to draw at
* @param gc GC to draw on
*/
protected void drawLineBreakSelection(String line, int lineOffset, int paintX, int paintY, GC gc) {
Point selection = parent.internalGetSelection();
int lineLength = line.length();
int selectionStart = Math.max(0, selection.x - lineOffset);
int selectionEnd = selection.y - lineOffset;
int lineEndSpaceWidth = getLineEndSpaceWidth();
int lineHeight = getLineHeight();
if (selectionEnd == selectionStart || selectionEnd < 0 || selectionStart > lineLength || selectionEnd <= lineLength) {
return;
}
gc.setBackground(parent.getSelectionBackground());
gc.setForeground(parent.getSelectionForeground());
if ((parent.getStyle() & SWT.FULL_SELECTION) != 0) {
Rectangle rect = getClientArea();
gc.fillRectangle(paintX, paintY, rect.width - paintX, lineHeight);
} else {
boolean isWrappedLine = false;
if (parent.internalGetWordWrap()) {
StyledTextContent content = getContent();
int lineEnd = lineOffset + lineLength;
int lineIndex = content.getLineAtOffset(lineEnd);
// is the start offset of the next line the same as the end
// offset of this line?
if (lineIndex < content.getLineCount() - 1 &&
content.getOffsetAtLine(lineIndex + 1) == lineEnd) {
isWrappedLine = true;
}
}
if (isWrappedLine == false) {
// render the line break selection
gc.fillRectangle(paintX, paintY, lineEndSpaceWidth, lineHeight);
}
}
}
/**
* Returns the text segments that should be treated as if they
* had a different direction than the surrounding text.
* </p>
*
* @param lineOffset offset of the first character in the line.
* 0 based from the beginning of the document.
* @param line text of the line to specify bidi segments for
* @return text segments that should be treated as if they had a
* different direction than the surrounding text. Only the start
* index of a segment is specified, relative to the start of the
* line. Always starts with 0 and ends with the line length.
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_ARGUMENT - if the segment indices returned
* by the listener do not start with 0, are not in ascending order,
* exceed the line length or have duplicates</li>
* </ul>
*/
protected int[] getBidiSegments(int lineOffset, String lineText) {
if (!parent.isBidi()) return null;
return parent.getBidiSegments(lineOffset, lineText);
}
/**
* Returns the visible client area that can be used for rendering.
* </p>
* @return the visible client area that can be used for rendering.
*/
protected Rectangle getClientArea() {
return parent.getClientArea();
}
/**
* Returns the <class>StyledTextContent</class> to use for line offset
* calculations.
* </p>
* @return the <class>StyledTextContent</class> to use for line offset
* calculations.
*/
protected StyledTextContent getContent() {
return parent.internalGetContent();
}
/**
* Returns a new GC to use for rendering and measuring.
* When the GC is no longer used it needs to be disposed by
* calling disposeGC.
* </p>
* @return the GC to use for rendering and measuring.
* @see disposeGC
*/
protected GC getGC() {
return new GC(parent);
}
/**
* Returns the horizontal scroll position.
* </p>
* @return the horizontal scroll position.
*/
protected int getHorizontalPixel() {
return parent.internalGetHorizontalPixel();
}
protected int getLeftMargin() {
return parent.leftMargin;
}
/**
* @see StyledTextRenderer#getLineBackgroundData
*/
protected StyledTextEvent getLineBackgroundData(int lineOffset, String line) {
return parent.getLineBackgroundData(lineOffset, line);
}
/**
* @see StyledTextRenderer#getLineStyleData
*/
protected StyledTextEvent getLineStyleData(int lineOffset, String line) {
StyledTextEvent logicalLineEvent = parent.getLineStyleData(lineOffset, line);
if (logicalLineEvent != null) {
logicalLineEvent = getLineStyleData(logicalLineEvent, lineOffset, line);
}
return logicalLineEvent;
}
protected int getOrientation () {
return parent.getOrientation();
}
protected int getRightMargin() {
return parent.rightMargin;
}
protected Color getSelectionBackground() {
return parent.getSelectionBackground();
}
protected Color getSelectionForeground() {
return parent.getSelectionForeground();
}
/**
* @see StyledTextRenderer#getSelection
*/
protected Point getSelection() {
return parent.internalGetSelection();
}
/**
* @see StyledTextRenderer#getWordWrap
*/
protected boolean getWordWrap() {
return parent.getWordWrap();
}
/**
* @see StyledTextRenderer#isFullLineSelection
*/
protected boolean isFullLineSelection() {
return (parent.getStyle() & SWT.FULL_SELECTION) != 0;
}
TextLayout createTextLayout(int lineOffset) {
if (!parent.internalGetWordWrap()) {
int lineIndex = getContent().getLineAtOffset(lineOffset);
updateTopIndex();
if (layouts != null) {
int layoutIndex = lineIndex - topIndex;
if (0 <= layoutIndex && layoutIndex < layouts.length) {
TextLayout layout = layouts[layoutIndex];
if (layout != null) return layout;
return layouts[layoutIndex] = super.createTextLayout(lineIndex);
}
}
}
return super.createTextLayout(lineOffset);
}
void disposeTextLayout (TextLayout layout) {
if (layouts != null) {
for (int i=0; i<layouts.length; i++) {
if (layouts[i] == layout) return;
}
}
super.disposeTextLayout(layout);
}
void updateTopIndex() {
int verticalIncrement = parent.getVerticalIncrement();
int topIndex = verticalIncrement == 0 ? 0 : parent.verticalScrollOffset / verticalIncrement;
int newLength = Math.max(1 , parent.getPartialBottomIndex() - topIndex + 1);
if (layouts == null || topIndex != this.topIndex || newLength != layouts.length) {
TextLayout[] newLayouts = new TextLayout[newLength];
if (layouts != null) {
for(int i=0; i<layouts.length; i++) {
TextLayout layout = layouts[i];
if (layout != null) {
int layoutIndex = (i + this.topIndex) - topIndex;
if (0 <= layoutIndex && layoutIndex < newLayouts.length) {
newLayouts[layoutIndex] = layout;
} else {
super.disposeTextLayout(layout);
}
}
}
}
this.topIndex = topIndex;
layouts = newLayouts;
}
}
}