summaryrefslogtreecommitdiffstats
path: root/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/PrintRenderer.java
blob: e88be545ddd39572a8be49bd5a596956754d56c0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/*******************************************************************************
 * 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 java.util.Hashtable;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.*;

/**
 * A PrintRenderer renders the content of a StyledText widget on 
 * a printer device.
 * Print rendering may occur in a non-UI thread. Therefore all 
 * requests for styles, content and any other information normally 
 * stored in the StyledText widget are served from cached data.
 * Caching also guarantees immutable data for threaded printing.
 */
class PrintRenderer extends StyledTextRenderer {
	StyledTextContent logicalContent;		// logical, unwrapped, content
	WrappedContent content;					// wrapped content
	Rectangle clientArea;					// printer client area
	GC gc;									// printer GC, there can be only one GC for each printer device
	Hashtable lineBackgrounds;				// line background colors used during rendering
	Hashtable lineStyles;					// line styles colors used during rendering
	Hashtable bidiSegments;			 		// bidi segments used during rendering on bidi platforms
	
/**
 * Creates an instance of <class>PrintRenderer</class>.
 * </p>
 * @param device Device to render on
 * @param regularFont Font to use for regular (non-bold) text.
 * @param gc printer GC to use for rendering. There can be only one GC for 
 * 	each printer device at any given time.
 * @param logicalContent StyledTextContent to print.
 * @param lineBackgrounds line background colors to use during rendering.
 * @param lineStyles line styles colors to use during rendering.
 * @param bidiSegments bidi segments to use during rendering on bidi platforms.
 * @param leftMargin margin to the left of the text.
 * @param tabLength length in characters of a tab character
 * @param clientArea the printer client area.
 */
PrintRenderer(
		Device device, Font regularFont, GC gc, 
		StyledTextContent logicalContent, Hashtable lineBackgrounds, 
		Hashtable lineStyles, Hashtable bidiSegments,
		int tabLength, Rectangle clientArea) {
	super(device, regularFont);
	this.logicalContent = logicalContent;
	this.lineBackgrounds = lineBackgrounds;
	this.lineStyles = lineStyles;
	this.bidiSegments = bidiSegments;	
	this.clientArea = clientArea;	
	this.gc = gc;
	calculateLineHeight();
	setTabLength(tabLength);
	content = new WrappedContent(this, logicalContent);
	// wrapLines requires tab width to be known	
	content.wrapLines();
}
/**
 * Disposes the resource created by the receiver.
 */
protected void dispose() {
	content = null;
	super.dispose();
}
/**
 * Do nothing. PrintRenderer does not create GCs.
 * @see StyledTextRenderer#disposeGC
 */
protected void disposeGC(GC gc) {
}
/** 
 * Do not print the selection.
 * @see StyledTextRenderer#drawLineSelectionBackground
 */
protected void drawLineBreakSelection(String line, int lineOffset, int paintX, int paintY, GC gc) {
}
/**
 * Returns from cache the text segments that should be treated as 
 * if they had a different direction than the surrounding text.
 * <p>
 * Use cached data.
 * </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) {
	int lineLength = lineText.length();
	int logicalLineOffset = getLogicalLineOffset(lineOffset);
	int[] segments = (int []) bidiSegments.get(new Integer(logicalLineOffset));
	
	if (segments == null) {
		segments = new int[] {0, lineLength};
	}
	else {
		// cached bidi segments are for logical lines.
		// make sure that returned segments match requested line since
		// line wrapping may require either entire or part of logical 
		// line bidi segments
		int logicalLineIndex = logicalContent.getLineAtOffset(lineOffset);
		int logicalLineLength = logicalContent.getLine(logicalLineIndex).length();
		
		if (lineOffset != logicalLineOffset || lineLength != logicalLineLength) {
			int lineOffsetDelta = lineOffset - logicalLineOffset;
			int newSegmentCount = 0;
			int[] newSegments = new int[segments.length];
			
			for (int i = 0; i < segments.length; i++) {
				newSegments[i] = Math.max(0, segments[i] - lineOffsetDelta);
				if (newSegments[i] > lineLength) {
					newSegments[i] = lineLength;
					newSegmentCount++;
					break;
				}
				if (i == 0 || newSegments[i] > 0) {
					newSegmentCount++;
				}
			}
			segments = new int[newSegmentCount];
			for (int i = 0, newIndex = 0; i < newSegments.length && newIndex < newSegmentCount; i++) {
				if (i == 0 || newSegments[i] > 0) {
					segments[newIndex++] = newSegments[i];
				}
			}
		}
	}
	return segments;
}
/**
 * Returns the printer client area.
 * </p>
 * @return the visible client area that can be used for rendering.
 * @see StyledTextRenderer#getClientArea
 */
protected Rectangle getClientArea() {
	return clientArea;
}
/**
 * Returns the <class>StyledTextContent</class> to use for line offset
 * calculations.
 * This is the wrapped content, calculated in the constructor from the 
 * logical printing content.
 * </p>
 * @return the <class>StyledTextContent</class> to use for line offset
 * calculations.
 */
protected StyledTextContent getContent() {
	return content;
}
/**
 * Returns the printer GC to use for rendering and measuring.
 * There can be only one GC for each printer device at any given
 * time.
 * </p>
 * @return the printer GC to use for rendering and measuring.
 */
protected GC getGC() {
	return gc;
}
/**
 * Returns 0. Scrolling does not affect printing. Text is wrapped
 * for printing.
 * </p>
 * @return 0
 * @see StyledTextRenderer#getHorizontalPixel
 */
protected int getHorizontalPixel() {
	return 0;
}
/**
 * Returns the start offset of the line at the given offset.
 * </p>
 * @param visualLineOffset an offset that may be anywhere within a 
 * 	line.
 * @return the start offset of the line at the given offset, 
 * 	relative to the start of the document.
 */
private int getLogicalLineOffset(int visualLineOffset) {
	int logicalLineIndex = logicalContent.getLineAtOffset(visualLineOffset);
	
	return logicalContent.getOffsetAtLine(logicalLineIndex);
}
protected  int getOrientation () {
	return SWT.LEFT_TO_RIGHT;
}
protected Color getSelectionBackground() {
	return null;
}
protected Color getSelectionForeground() {
	return null;
}
/**
 * Return cached line background data.
 * @see StyledTextRenderer#getLineBackgroundData
 */
protected StyledTextEvent getLineBackgroundData(int lineOffset, String line) {
	int logicalLineOffset = getLogicalLineOffset(lineOffset);
	
	return (StyledTextEvent) lineBackgrounds.get(new Integer(logicalLineOffset));
}
/**
 * Return cached line style background data.
 * @see StyledTextRenderer#getLineStyleData
 */
protected StyledTextEvent getLineStyleData(int lineOffset, String line) {
	int logicalLineOffset = getLogicalLineOffset(lineOffset);
	StyledTextEvent logicalLineEvent = (StyledTextEvent) lineStyles.get(new Integer(logicalLineOffset));
	
	if (logicalLineEvent != null) {
		StyledTextEvent clone = new StyledTextEvent((StyledTextContent) logicalLineEvent.data);
		clone.detail = logicalLineEvent.detail;
		clone.styles = logicalLineEvent.styles;
		clone.text = logicalLineEvent.text;
		logicalLineEvent = getLineStyleData(clone, lineOffset, line);
	}
	return logicalLineEvent;
}
/** 
 * Selection is not printed.
 * </p>
 * @return Point(0,0)
 * @see StyledTextRenderer#getSelection
 */
protected Point getSelection() {
	return new Point(0, 0);
}
/**
 * Printed content is always wrapped.
 * </p>
 * @return true
 * @see StyledTextRenderer#getWordWrap
 */
protected boolean getWordWrap() {
	return true;
}
/**
 * Selection is not printed. Returns false.
 * <p>
 * @return false
 * @see StyledTextRenderer#isFullLineSelection
 */
protected boolean isFullLineSelection() {
	return false;
}
}