Mercurial > projects > dwt-mac
comparison dwt/custom/StyledTextRenderer.d @ 0:380af2bdd8e5
Upload of whole dwt tree
author | Jacob Carlborg <doob@me.com> <jacob.carlborg@gmail.com> |
---|---|
date | Sat, 09 Aug 2008 17:00:02 +0200 |
parents | |
children | 1a8b3cb347e0 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:380af2bdd8e5 |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2000, 2007 IBM Corporation and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
10 *******************************************************************************/ | |
11 module dwt.custom; | |
12 | |
13 | |
14 import dwt.DWT; | |
15 import dwt.graphics.*; | |
16 import dwt.widgets.*; | |
17 | |
18 /** | |
19 * A StyledTextRenderer renders the content of a StyledText widget. | |
20 * This class can be used to render to the display or to a printer. | |
21 */ | |
22 class StyledTextRenderer { | |
23 Device device; | |
24 StyledText styledText; | |
25 StyledTextContent content; | |
26 | |
27 /* Font info */ | |
28 Font regularFont, boldFont, italicFont, boldItalicFont; | |
29 int tabWidth; | |
30 int ascent, descent; | |
31 int averageCharWidth; | |
32 | |
33 /* Line data */ | |
34 int topIndex = -1; | |
35 TextLayout[] layouts; | |
36 int lineCount; | |
37 int[] lineWidth; | |
38 int[] lineHeight; | |
39 LineInfo[] lines; | |
40 int maxWidth; | |
41 int maxWidthLineIndex; | |
42 bool idleRunning; | |
43 | |
44 /* Bullet */ | |
45 Bullet[] bullets; | |
46 int[] bulletsIndices; | |
47 int[] redrawLines; | |
48 | |
49 /* Style data */ | |
50 int[] ranges; | |
51 int styleCount; | |
52 StyleRange[] styles; | |
53 StyleRange[] stylesSet; | |
54 int stylesSetCount = 0; | |
55 final static int BULLET_MARGIN = 8; | |
56 | |
57 final static bool COMPACT_STYLES = true; | |
58 final static bool MERGE_STYLES = true; | |
59 | |
60 final static int GROW = 32; | |
61 final static int IDLE_TIME = 50; | |
62 final static int CACHE_SIZE = 128; | |
63 | |
64 final static int BACKGROUND = 1 << 0; | |
65 final static int ALIGNMENT = 1 << 1; | |
66 final static int INDENT = 1 << 2; | |
67 final static int JUSTIFY = 1 << 3; | |
68 final static int SEGMENTS = 1 << 5; | |
69 | |
70 static class LineInfo { | |
71 int flags; | |
72 Color background; | |
73 int alignment; | |
74 int indent; | |
75 bool justify; | |
76 int[] segments; | |
77 | |
78 public LineInfo() { | |
79 } | |
80 public LineInfo(LineInfo info) { | |
81 if (info !is null) { | |
82 flags = info.flags; | |
83 background = info.background; | |
84 alignment = info.alignment; | |
85 indent = info.indent; | |
86 justify = info.justify; | |
87 segments = info.segments; | |
88 } | |
89 } | |
90 } | |
91 | |
92 StyledTextRenderer(Device device, StyledText styledText) { | |
93 this.device = device; | |
94 this.styledText = styledText; | |
95 } | |
96 int addMerge(int[] mergeRanges, StyleRange[] mergeStyles, int mergeCount, int modifyStart, int modifyEnd) { | |
97 int rangeCount = styleCount << 1; | |
98 StyleRange endStyle = null; | |
99 int endStart = 0, endLength = 0; | |
100 if (modifyEnd < rangeCount) { | |
101 endStyle = styles[modifyEnd >> 1]; | |
102 endStart = ranges[modifyEnd]; | |
103 endLength = ranges[modifyEnd + 1]; | |
104 } | |
105 int grow = mergeCount - (modifyEnd - modifyStart); | |
106 if (rangeCount + grow >= ranges.length) { | |
107 int[] tmpRanges = new int[ranges.length + grow + (GROW << 1)]; | |
108 System.arraycopy(ranges, 0, tmpRanges, 0, modifyStart); | |
109 StyleRange[] tmpStyles = new StyleRange[styles.length + (grow >> 1) + GROW]; | |
110 System.arraycopy(styles, 0, tmpStyles, 0, modifyStart >> 1); | |
111 if (rangeCount > modifyEnd) { | |
112 System.arraycopy(ranges, modifyEnd, tmpRanges, modifyStart + mergeCount, rangeCount - modifyEnd); | |
113 System.arraycopy(styles, modifyEnd >> 1, tmpStyles, (modifyStart + mergeCount) >> 1, styleCount - (modifyEnd >> 1)); | |
114 } | |
115 ranges = tmpRanges; | |
116 styles = tmpStyles; | |
117 } else { | |
118 if (rangeCount > modifyEnd) { | |
119 System.arraycopy(ranges, modifyEnd, ranges, modifyStart + mergeCount, rangeCount - modifyEnd); | |
120 System.arraycopy(styles, modifyEnd >> 1, styles, (modifyStart + mergeCount) >> 1, styleCount - (modifyEnd >> 1)); | |
121 } | |
122 } | |
123 if (MERGE_STYLES) { | |
124 int j = modifyStart; | |
125 for (int i = 0; i < mergeCount; i += 2) { | |
126 if (j > 0 && ranges[j - 2] + ranges[j - 1] is mergeRanges[i] && mergeStyles[i >> 1].similarTo(styles[(j - 2) >> 1])) { | |
127 ranges[j - 1] += mergeRanges[i + 1]; | |
128 } else { | |
129 styles[j >> 1] = mergeStyles[i >> 1]; | |
130 ranges[j++] = mergeRanges[i]; | |
131 ranges[j++] = mergeRanges[i + 1]; | |
132 } | |
133 } | |
134 if (endStyle !is null && ranges[j - 2] + ranges[j - 1] is endStart && endStyle.similarTo(styles[(j - 2) >> 1])) { | |
135 ranges[j - 1] += endLength; | |
136 modifyEnd += 2; | |
137 mergeCount += 2; | |
138 } | |
139 if (rangeCount > modifyEnd) { | |
140 System.arraycopy(ranges, modifyStart + mergeCount, ranges, j, rangeCount - modifyEnd); | |
141 System.arraycopy(styles, (modifyStart + mergeCount) >> 1, styles, j >> 1, styleCount - (modifyEnd >> 1)); | |
142 } | |
143 grow = (j - modifyStart) - (modifyEnd - modifyStart); | |
144 } else { | |
145 System.arraycopy(mergeRanges, 0, ranges, modifyStart, mergeCount); | |
146 System.arraycopy(mergeStyles, 0, styles, modifyStart >> 1, mergeCount >> 1); | |
147 } | |
148 styleCount += grow >> 1; | |
149 return grow; | |
150 } | |
151 int addMerge(StyleRange[] mergeStyles, int mergeCount, int modifyStart, int modifyEnd) { | |
152 int grow = mergeCount - (modifyEnd - modifyStart); | |
153 StyleRange endStyle = null; | |
154 if (modifyEnd < styleCount) endStyle = styles[modifyEnd]; | |
155 if (styleCount + grow >= styles.length) { | |
156 StyleRange[] tmpStyles = new StyleRange[styles.length + grow + GROW]; | |
157 System.arraycopy(styles, 0, tmpStyles, 0, modifyStart); | |
158 if (styleCount > modifyEnd) { | |
159 System.arraycopy(styles, modifyEnd, tmpStyles, modifyStart + mergeCount, styleCount - modifyEnd); | |
160 } | |
161 styles = tmpStyles; | |
162 } else { | |
163 if (styleCount > modifyEnd) { | |
164 System.arraycopy(styles, modifyEnd, styles, modifyStart + mergeCount, styleCount - modifyEnd); | |
165 } | |
166 } | |
167 if (MERGE_STYLES) { | |
168 int j = modifyStart; | |
169 for (int i = 0; i < mergeCount; i++) { | |
170 StyleRange newStyle = mergeStyles[i], style; | |
171 if (j > 0 && (style = styles[j - 1]).start + style.length is newStyle.start && newStyle.similarTo(style)) { | |
172 style.length += newStyle.length; | |
173 } else { | |
174 styles[j++] = newStyle; | |
175 } | |
176 } | |
177 StyleRange style = styles[j - 1]; | |
178 if (endStyle !is null && style.start + style.length is endStyle.start && endStyle.similarTo(style)) { | |
179 style.length += endStyle.length; | |
180 modifyEnd++; | |
181 mergeCount++; | |
182 } | |
183 if (styleCount > modifyEnd) { | |
184 System.arraycopy(styles, modifyStart + mergeCount, styles, j, styleCount - modifyEnd); | |
185 } | |
186 grow = (j - modifyStart) - (modifyEnd - modifyStart); | |
187 } else { | |
188 System.arraycopy(mergeStyles, 0, styles, modifyStart, mergeCount); | |
189 } | |
190 styleCount += grow; | |
191 return grow; | |
192 } | |
193 void calculate(int startLine, int lineCount) { | |
194 int endLine = startLine + lineCount; | |
195 if (startLine < 0 || endLine > lineWidth.length) { | |
196 return; | |
197 } | |
198 int hTrim = styledText.leftMargin + styledText.rightMargin + styledText.getCaretWidth(); | |
199 for (int i = startLine; i < endLine; i++) { | |
200 if (lineWidth[i] is -1 || lineHeight[i] is -1) { | |
201 TextLayout layout = getTextLayout(i); | |
202 Rectangle rect = layout.getBounds(); | |
203 lineWidth[i] = rect.width + hTrim; | |
204 lineHeight[i] = rect.height; | |
205 disposeTextLayout(layout); | |
206 } | |
207 if (lineWidth[i] > maxWidth) { | |
208 maxWidth = lineWidth[i]; | |
209 maxWidthLineIndex = i; | |
210 } | |
211 } | |
212 } | |
213 void calculateClientArea () { | |
214 int index = styledText.getTopIndex(); | |
215 int lineCount = content.getLineCount(); | |
216 int height = styledText.getClientArea().height; | |
217 int y = 0; | |
218 while (height > y && lineCount > index) { | |
219 calculate(index, 1); | |
220 y += lineHeight[index++]; | |
221 } | |
222 } | |
223 void calculateIdle () { | |
224 if (idleRunning) return; | |
225 Runnable runnable = new Runnable() { | |
226 public void run() { | |
227 if (styledText is null) return; | |
228 int i; | |
229 long start = System.currentTimeMillis(); | |
230 for (i = 0; i < lineCount; i++) { | |
231 if (lineHeight[i] is -1 || lineWidth[i] is -1) { | |
232 calculate(i, 1); | |
233 if (System.currentTimeMillis() - start > IDLE_TIME) break; | |
234 } | |
235 } | |
236 if (i < lineCount) { | |
237 Display display = styledText.getDisplay(); | |
238 display.asyncExec(this); | |
239 } else { | |
240 idleRunning = false; | |
241 styledText.setScrollBars(true); | |
242 ScrollBar bar = styledText.getVerticalBar(); | |
243 if (bar !is null) { | |
244 bar.setSelection(styledText.getVerticalScrollOffset()); | |
245 } | |
246 } | |
247 } | |
248 }; | |
249 Display display = styledText.getDisplay(); | |
250 display.asyncExec(runnable); | |
251 idleRunning = true; | |
252 } | |
253 void clearLineBackground(int startLine, int count) { | |
254 if (lines is null) return; | |
255 for (int i = startLine; i < startLine + count; i++) { | |
256 LineInfo info = lines[i]; | |
257 if (info !is null) { | |
258 info.flags &= ~BACKGROUND; | |
259 info.background = null; | |
260 if (info.flags is 0) lines[i] = null; | |
261 } | |
262 } | |
263 } | |
264 void clearLineStyle(int startLine, int count) { | |
265 if (lines is null) return; | |
266 for (int i = startLine; i < startLine + count; i++) { | |
267 LineInfo info = lines[i]; | |
268 if (info !is null) { | |
269 info.flags &= ~(ALIGNMENT | INDENT | JUSTIFY); | |
270 if (info.flags is 0) lines[i] = null; | |
271 } | |
272 } | |
273 } | |
274 void copyInto(StyledTextRenderer renderer) { | |
275 if (ranges !is null) { | |
276 int[] newRanges = renderer.ranges = new int[styleCount << 1]; | |
277 System.arraycopy(ranges, 0, newRanges, 0, newRanges.length); | |
278 } | |
279 if (styles !is null) { | |
280 StyleRange[] newStyles = renderer.styles = new StyleRange[styleCount]; | |
281 for (int i = 0; i < newStyles.length; i++) { | |
282 newStyles[i] = (StyleRange)styles[i].clone(); | |
283 } | |
284 renderer.styleCount = styleCount; | |
285 } | |
286 if (lines !is null) { | |
287 LineInfo[] newLines = renderer.lines = new LineInfo[lineCount]; | |
288 for (int i = 0; i < newLines.length; i++) { | |
289 newLines[i] = new LineInfo(lines[i]); | |
290 } | |
291 renderer.lineCount = lineCount; | |
292 } | |
293 } | |
294 void dispose() { | |
295 if (boldFont !is null) boldFont.dispose(); | |
296 if (italicFont !is null) italicFont.dispose(); | |
297 if (boldItalicFont !is null) boldItalicFont.dispose(); | |
298 boldFont = italicFont = boldItalicFont = null; | |
299 reset(); | |
300 content = null; | |
301 device = null; | |
302 styledText = null; | |
303 } | |
304 void disposeTextLayout (TextLayout layout) { | |
305 if (layouts !is null) { | |
306 for (int i = 0; i < layouts.length; i++) { | |
307 if (layouts[i] is layout) return; | |
308 } | |
309 } | |
310 layout.dispose(); | |
311 } | |
312 void drawBullet(Bullet bullet, GC gc, int paintX, int paintY, int index, int lineAscent, int lineDescent) { | |
313 StyleRange style = bullet.style; | |
314 GlyphMetrics metrics = style.metrics; | |
315 Color color = style.foreground; | |
316 if (color !is null) gc.setForeground(color); | |
317 if ((bullet.type & ST.BULLET_DOT) !is 0 && StyledText.IS_MOTIF) { | |
318 int size = Math.max(4, (lineAscent + lineDescent) / 4); | |
319 if ((size & 1) is 0) size++; | |
320 if (color is null) { | |
321 Display display = styledText.getDisplay(); | |
322 color = display.getSystemColor(DWT.COLOR_BLACK); | |
323 } | |
324 gc.setBackground(color); | |
325 int x = paintX + Math.max(0, metrics.width - size - BULLET_MARGIN); | |
326 gc.fillArc(x, paintY + size, size + 1, size + 1, 0, 360); | |
327 return; | |
328 } | |
329 Font font = style.font; | |
330 if (font !is null) gc.setFont(font); | |
331 String String = ""; | |
332 int type = bullet.type & (ST.BULLET_DOT|ST.BULLET_NUMBER|ST.BULLET_LETTER_LOWER|ST.BULLET_LETTER_UPPER); | |
333 switch (type) { | |
334 case ST.BULLET_DOT: String = "\u2022"; break; | |
335 case ST.BULLET_NUMBER: String = String.valueOf(index); break; | |
336 case ST.BULLET_LETTER_LOWER: String = String.valueOf((char) (index % 26 + 97)); break; | |
337 case ST.BULLET_LETTER_UPPER: String = String.valueOf((char) (index % 26 + 65)); break; | |
338 } | |
339 if ((bullet.type & ST.BULLET_TEXT) !is 0) String += bullet.text; | |
340 Display display = styledText.getDisplay(); | |
341 TextLayout layout = new TextLayout(display); | |
342 layout.setText(String); | |
343 layout.setAscent(lineAscent); | |
344 layout.setDescent(lineDescent); | |
345 style = (StyleRange)style.clone(); | |
346 style.metrics = null; | |
347 if (style.font is null) style.font = getFont(style.fontStyle); | |
348 layout.setStyle(style, 0, String.length()); | |
349 int x = paintX + Math.max(0, metrics.width - layout.getBounds().width - BULLET_MARGIN); | |
350 layout.draw(gc, x, paintY); | |
351 layout.dispose(); | |
352 } | |
353 int drawLine(int lineIndex, int paintX, int paintY, GC gc, Color widgetBackground, Color widgetForeground) { | |
354 TextLayout layout = getTextLayout(lineIndex); | |
355 String line = content.getLine(lineIndex); | |
356 int lineOffset = content.getOffsetAtLine(lineIndex); | |
357 int lineLength = line.length(); | |
358 Point selection = styledText.getSelection(); | |
359 int selectionStart = selection.x - lineOffset; | |
360 int selectionEnd = selection.y - lineOffset; | |
361 Rectangle client = styledText.getClientArea(); | |
362 Color lineBackground = getLineBackground(lineIndex, null); | |
363 StyledTextEvent event = styledText.getLineBackgroundData(lineOffset, line); | |
364 if (event !is null && event.lineBackground !is null) lineBackground = event.lineBackground; | |
365 int height = layout.getBounds().height; | |
366 if (lineBackground !is null) { | |
367 gc.setBackground(lineBackground); | |
368 gc.fillRectangle(client.x, paintY, client.width, height); | |
369 } else { | |
370 gc.setBackground(widgetBackground); | |
371 styledText.drawBackground(gc, client.x, paintY, client.width, height); | |
372 } | |
373 gc.setForeground(widgetForeground); | |
374 if (selectionStart is selectionEnd || (selectionEnd <= 0 && selectionStart > lineLength - 1)) { | |
375 layout.draw(gc, paintX, paintY); | |
376 } else { | |
377 int start = Math.max(0, selectionStart); | |
378 int end = Math.min(lineLength, selectionEnd); | |
379 Color selectionFg = styledText.getSelectionForeground(); | |
380 Color selectionBg = styledText.getSelectionBackground(); | |
381 int flags; | |
382 if ((styledText.getStyle() & DWT.FULL_SELECTION) !is 0) { | |
383 flags = DWT.FULL_SELECTION; | |
384 } else { | |
385 flags = DWT.DELIMITER_SELECTION; | |
386 } | |
387 if (selectionStart <= lineLength && lineLength < selectionEnd ) { | |
388 flags |= DWT.LAST_LINE_SELECTION; | |
389 } | |
390 layout.draw(gc, paintX, paintY, start, end - 1, selectionFg, selectionBg, flags); | |
391 } | |
392 | |
393 // draw objects | |
394 Bullet bullet = null; | |
395 int bulletIndex = -1; | |
396 if (bullets !is null) { | |
397 if (bulletsIndices !is null) { | |
398 int index = lineIndex - topIndex; | |
399 if (0 <= index && index < CACHE_SIZE) { | |
400 bullet = bullets[index]; | |
401 bulletIndex = bulletsIndices[index]; | |
402 } | |
403 } else { | |
404 for (int i = 0; i < bullets.length; i++) { | |
405 bullet = bullets[i]; | |
406 bulletIndex = bullet.indexOf(lineIndex); | |
407 if (bulletIndex !is -1) break; | |
408 } | |
409 } | |
410 } | |
411 if (bulletIndex !is -1 && bullet !is null) { | |
412 FontMetrics metrics = layout.getLineMetrics(0); | |
413 int lineAscent = metrics.getAscent() + metrics.getLeading(); | |
414 if (bullet.type is ST.BULLET_CUSTOM) { | |
415 bullet.style.start = lineOffset; | |
416 styledText.paintObject(gc, paintX, paintY, lineAscent, metrics.getDescent(), bullet.style, bullet, bulletIndex); | |
417 } else { | |
418 drawBullet(bullet, gc, paintX, paintY, bulletIndex, lineAscent, metrics.getDescent()); | |
419 } | |
420 } | |
421 TextStyle[] styles = layout.getStyles(); | |
422 int[] ranges = null; | |
423 for (int i = 0; i < styles.length; i++) { | |
424 if (styles[i].metrics !is null) { | |
425 if (ranges is null) ranges = layout.getRanges(); | |
426 int start = ranges[i << 1]; | |
427 int length = ranges[(i << 1) + 1] - start; | |
428 Point point = layout.getLocation(start, false); | |
429 FontMetrics metrics = layout.getLineMetrics(layout.getLineIndex(start)); | |
430 StyleRange style = (StyleRange)((StyleRange)styles[i]).clone(); | |
431 style.start = start + lineOffset; | |
432 style.length = length; | |
433 int lineAscent = metrics.getAscent() + metrics.getLeading(); | |
434 styledText.paintObject(gc, point.x + paintX, point.y + paintY, lineAscent, metrics.getDescent(), style, null, 0); | |
435 } | |
436 } | |
437 disposeTextLayout(layout); | |
438 return height; | |
439 } | |
440 int getBaseline() { | |
441 return ascent; | |
442 } | |
443 Font getFont(int style) { | |
444 switch (style) { | |
445 case DWT.BOLD: | |
446 if (boldFont !is null) return boldFont; | |
447 return boldFont = new Font(device, getFontData(style)); | |
448 case DWT.ITALIC: | |
449 if (italicFont !is null) return italicFont; | |
450 return italicFont = new Font(device, getFontData(style)); | |
451 case DWT.BOLD | DWT.ITALIC: | |
452 if (boldItalicFont !is null) return boldItalicFont; | |
453 return boldItalicFont = new Font(device, getFontData(style)); | |
454 default: | |
455 return regularFont; | |
456 } | |
457 } | |
458 FontData[] getFontData(int style) { | |
459 FontData[] fontDatas = regularFont.getFontData(); | |
460 for (int i = 0; i < fontDatas.length; i++) { | |
461 fontDatas[i].setStyle(style); | |
462 } | |
463 return fontDatas; | |
464 } | |
465 int getHeight () { | |
466 int defaultLineHeight = getLineHeight(); | |
467 if (styledText.isFixedLineHeight()) { | |
468 return lineCount * defaultLineHeight; | |
469 } | |
470 int totalHeight = 0; | |
471 int width = styledText.getWrapWidth(); | |
472 for (int i = 0; i < lineCount; i++) { | |
473 int height = lineHeight[i]; | |
474 if (height is -1) { | |
475 if (width > 0) { | |
476 int length = content.getLine(i).length(); | |
477 height = ((length * averageCharWidth / width) + 1) * defaultLineHeight; | |
478 } else { | |
479 height = defaultLineHeight; | |
480 } | |
481 } | |
482 totalHeight += height; | |
483 } | |
484 return totalHeight + styledText.topMargin + styledText.bottomMargin; | |
485 } | |
486 int getLineAlignment(int index, int defaultAlignment) { | |
487 if (lines is null) return defaultAlignment; | |
488 LineInfo info = lines[index]; | |
489 if (info !is null && (info.flags & ALIGNMENT) !is 0) { | |
490 return info.alignment; | |
491 } | |
492 return defaultAlignment; | |
493 } | |
494 Color getLineBackground(int index, Color defaultBackground) { | |
495 if (lines is null) return defaultBackground; | |
496 LineInfo info = lines[index]; | |
497 if (info !is null && (info.flags & BACKGROUND) !is 0) { | |
498 return info.background; | |
499 } | |
500 return defaultBackground; | |
501 } | |
502 Bullet getLineBullet (int index, Bullet defaultBullet) { | |
503 if (bullets is null) return defaultBullet; | |
504 if (bulletsIndices !is null) return defaultBullet; | |
505 for (int i = 0; i < bullets.length; i++) { | |
506 Bullet bullet = bullets[i]; | |
507 if (bullet.indexOf(index) !is -1) return bullet; | |
508 } | |
509 return defaultBullet; | |
510 } | |
511 int getLineHeight() { | |
512 return ascent + descent; | |
513 } | |
514 int getLineHeight(int lineIndex) { | |
515 if (lineHeight[lineIndex] is -1) { | |
516 calculate(lineIndex, 1); | |
517 } | |
518 return lineHeight[lineIndex]; | |
519 } | |
520 int getLineIndent(int index, int defaultIndent) { | |
521 if (lines is null) return defaultIndent; | |
522 LineInfo info = lines[index]; | |
523 if (info !is null && (info.flags & INDENT) !is 0) { | |
524 return info.indent; | |
525 } | |
526 return defaultIndent; | |
527 } | |
528 bool getLineJustify(int index, bool defaultJustify) { | |
529 if (lines is null) return defaultJustify; | |
530 LineInfo info = lines[index]; | |
531 if (info !is null && (info.flags & JUSTIFY) !is 0) { | |
532 return info.justify; | |
533 } | |
534 return defaultJustify; | |
535 } | |
536 int[] getLineSegments(int index, int[] defaultSegments) { | |
537 if (lines is null) return defaultSegments; | |
538 LineInfo info = lines[index]; | |
539 if (info !is null && (info.flags & SEGMENTS) !is 0) { | |
540 return info.segments; | |
541 } | |
542 return defaultSegments; | |
543 } | |
544 int getRangeIndex(int offset, int low, int high) { | |
545 if (styleCount is 0) return 0; | |
546 if (ranges !is null) { | |
547 while (high - low > 2) { | |
548 int index = ((high + low) / 2) / 2 * 2; | |
549 int end = ranges[index] + ranges[index + 1]; | |
550 if (end > offset) { | |
551 high = index; | |
552 } else { | |
553 low = index; | |
554 } | |
555 } | |
556 } else { | |
557 while (high - low > 1) { | |
558 int index = ((high + low) / 2); | |
559 int end = styles[index].start + styles[index].length; | |
560 if (end > offset) { | |
561 high = index; | |
562 } else { | |
563 low = index; | |
564 } | |
565 } | |
566 } | |
567 return high; | |
568 } | |
569 int[] getRanges(int start, int length) { | |
570 int[] newRanges; | |
571 int end = start + length - 1; | |
572 if (ranges !is null) { | |
573 int rangeCount = styleCount << 1; | |
574 int rangeStart = getRangeIndex(start, -1, rangeCount); | |
575 if (rangeStart >= rangeCount) return null; | |
576 if (ranges[rangeStart] > end) return null; | |
577 int rangeEnd = Math.min(rangeCount - 2, getRangeIndex(end, rangeStart - 1, rangeCount) + 1); | |
578 newRanges = new int[rangeEnd - rangeStart + 2]; | |
579 System.arraycopy(ranges, rangeStart, newRanges, 0, newRanges.length); | |
580 } else { | |
581 int rangeStart = getRangeIndex(start, -1, styleCount); | |
582 if (rangeStart >= styleCount) return null; | |
583 if (styles[rangeStart].start > end) return null; | |
584 int rangeEnd = Math.min(styleCount - 1, getRangeIndex(end, rangeStart - 1, styleCount)); | |
585 newRanges = new int[(rangeEnd - rangeStart + 1) << 1]; | |
586 for (int i = rangeStart, j = 0; i <= rangeEnd; i++, j += 2) { | |
587 StyleRange style = styles[i]; | |
588 newRanges[j] = style.start; | |
589 newRanges[j + 1] = style.length; | |
590 } | |
591 } | |
592 if (start > newRanges[0]) { | |
593 newRanges[1] = newRanges[0] + newRanges[1] - start; | |
594 newRanges[0] = start; | |
595 } | |
596 if (end < newRanges[newRanges.length - 2] + newRanges[newRanges.length - 1] - 1) { | |
597 newRanges[newRanges.length - 1] = end - newRanges[newRanges.length - 2] + 1; | |
598 } | |
599 return newRanges; | |
600 } | |
601 StyleRange[] getStyleRanges(int start, int length, bool includeRanges) { | |
602 StyleRange[] newStyles; | |
603 int end = start + length - 1; | |
604 if (ranges !is null) { | |
605 int rangeCount = styleCount << 1; | |
606 int rangeStart = getRangeIndex(start, -1, rangeCount); | |
607 if (rangeStart >= rangeCount) return null; | |
608 if (ranges[rangeStart] > end) return null; | |
609 int rangeEnd = Math.min(rangeCount - 2, getRangeIndex(end, rangeStart - 1, rangeCount) + 1); | |
610 newStyles = new StyleRange[((rangeEnd - rangeStart) >> 1) + 1]; | |
611 if (includeRanges) { | |
612 for (int i = rangeStart, j = 0; i <= rangeEnd; i += 2, j++) { | |
613 newStyles[j] = (StyleRange)styles[i >> 1].clone(); | |
614 newStyles[j].start = ranges[i]; | |
615 newStyles[j].length = ranges[i + 1]; | |
616 } | |
617 } else { | |
618 System.arraycopy(styles, rangeStart >> 1, newStyles, 0, newStyles.length); | |
619 } | |
620 } else { | |
621 int rangeStart = getRangeIndex(start, -1, styleCount); | |
622 if (rangeStart >= styleCount) return null; | |
623 if (styles[rangeStart].start > end) return null; | |
624 int rangeEnd = Math.min(styleCount - 1, getRangeIndex(end, rangeStart - 1, styleCount)); | |
625 newStyles = new StyleRange[rangeEnd - rangeStart + 1]; | |
626 System.arraycopy(styles, rangeStart, newStyles, 0, newStyles.length); | |
627 } | |
628 StyleRange style = newStyles[0]; | |
629 if (start > style.start) { | |
630 if (!includeRanges || ranges is null) newStyles[0] = style = (StyleRange)style.clone(); | |
631 style.length = style.start + style.length - start; | |
632 style.start = start; | |
633 } | |
634 style = newStyles[newStyles.length - 1]; | |
635 if (end < style.start + style.length - 1) { | |
636 if (end < style.start) { | |
637 StyleRange[] tmp = new StyleRange[newStyles.length - 1]; | |
638 System.arraycopy(newStyles, 0, tmp, 0, newStyles.length - 1); | |
639 newStyles = tmp; | |
640 } else { | |
641 if (!includeRanges || ranges is null) newStyles[newStyles.length - 1] = style = (StyleRange)style.clone(); | |
642 style.length = end - style.start + 1; | |
643 } | |
644 } | |
645 return newStyles; | |
646 } | |
647 StyleRange getStyleRange(StyleRange style) { | |
648 if (style.start is 0 && style.length is 0 && style.fontStyle is DWT.NORMAL) return style; | |
649 StyleRange clone = (StyleRange)style.clone(); | |
650 clone.start = clone.length = 0; | |
651 clone.fontStyle = DWT.NORMAL; | |
652 if (clone.font is null) clone.font = getFont(style.fontStyle); | |
653 return clone; | |
654 } | |
655 TextLayout getTextLayout(int lineIndex) { | |
656 return getTextLayout(lineIndex, styledText.getOrientation(), styledText.getWrapWidth(), styledText.lineSpacing); | |
657 } | |
658 TextLayout getTextLayout(int lineIndex, int orientation, int width, int lineSpacing) { | |
659 TextLayout layout = null; | |
660 if (styledText !is null) { | |
661 int topIndex = styledText.topIndex > 0 ? styledText.topIndex - 1 : 0; | |
662 if (layouts is null || topIndex !is this.topIndex) { | |
663 TextLayout[] newLayouts = new TextLayout[CACHE_SIZE]; | |
664 if (layouts !is null) { | |
665 for (int i = 0; i < layouts.length; i++) { | |
666 if (layouts[i] !is null) { | |
667 int layoutIndex = (i + this.topIndex) - topIndex; | |
668 if (0 <= layoutIndex && layoutIndex < newLayouts.length) { | |
669 newLayouts[layoutIndex] = layouts[i]; | |
670 } else { | |
671 layouts[i].dispose(); | |
672 } | |
673 } | |
674 } | |
675 } | |
676 if (bullets !is null && bulletsIndices !is null && topIndex !is this.topIndex) { | |
677 int delta = topIndex - this.topIndex; | |
678 if (delta > 0) { | |
679 if (delta < bullets.length) { | |
680 System.arraycopy(bullets, delta, bullets, 0, bullets.length - delta); | |
681 System.arraycopy(bulletsIndices, delta, bulletsIndices, 0, bulletsIndices.length - delta); | |
682 } | |
683 int startIndex = Math.max(0, bullets.length - delta); | |
684 for (int i = startIndex; i < bullets.length; i++) bullets[i] = null; | |
685 } else { | |
686 if (-delta < bullets.length) { | |
687 System.arraycopy(bullets, 0, bullets, -delta, bullets.length + delta); | |
688 System.arraycopy(bulletsIndices, 0, bulletsIndices, -delta, bulletsIndices.length + delta); | |
689 } | |
690 int endIndex = Math.min(bullets.length, -delta); | |
691 for (int i = 0; i < endIndex; i++) bullets[i] = null; | |
692 } | |
693 } | |
694 this.topIndex = topIndex; | |
695 layouts = newLayouts; | |
696 } | |
697 if (layouts !is null) { | |
698 int layoutIndex = lineIndex - topIndex; | |
699 if (0 <= layoutIndex && layoutIndex < layouts.length) { | |
700 layout = layouts[layoutIndex]; | |
701 if (layout !is null) { | |
702 if (lineWidth[lineIndex] !is -1) return layout; | |
703 } else { | |
704 layout = layouts[layoutIndex] = new TextLayout(device); | |
705 } | |
706 } | |
707 } | |
708 } | |
709 if (layout is null) layout = new TextLayout(device); | |
710 String line = content.getLine(lineIndex); | |
711 int lineOffset = content.getOffsetAtLine(lineIndex); | |
712 int[] segments = null; | |
713 int indent = 0; | |
714 int alignment = DWT.LEFT; | |
715 bool justify = false; | |
716 Bullet bullet = null; | |
717 int[] ranges = null; | |
718 StyleRange[] styles = null; | |
719 int rangeStart = 0, styleCount = 0; | |
720 StyledTextEvent event = null; | |
721 if (styledText !is null) { | |
722 event = styledText.getLineStyleData(lineOffset, line); | |
723 segments = styledText.getBidiSegments(lineOffset, line); | |
724 indent = styledText.indent; | |
725 alignment = styledText.alignment; | |
726 justify = styledText.justify; | |
727 } | |
728 if (event !is null) { | |
729 indent = event.indent; | |
730 alignment = event.alignment; | |
731 justify = event.justify; | |
732 bullet = event.bullet; | |
733 ranges = event.ranges; | |
734 styles = event.styles; | |
735 if (styles !is null) { | |
736 styleCount = styles.length; | |
737 if (styledText.isFixedLineHeight()) { | |
738 for (int i = 0; i < styleCount; i++) { | |
739 if (styles[i].isVariableHeight()) { | |
740 styledText.verticalScrollOffset = -1; | |
741 styledText.setVariableLineHeight(); | |
742 styledText.redraw(); | |
743 break; | |
744 } | |
745 } | |
746 } | |
747 } | |
748 if (bullets is null || bulletsIndices is null) { | |
749 bullets = new Bullet[CACHE_SIZE]; | |
750 bulletsIndices = new int[CACHE_SIZE]; | |
751 } | |
752 int index = lineIndex - topIndex; | |
753 if (0 <= index && index < CACHE_SIZE) { | |
754 bullets[index] = bullet; | |
755 bulletsIndices[index] = event.bulletIndex; | |
756 } | |
757 } else { | |
758 if (lines !is null) { | |
759 LineInfo info = lines[lineIndex]; | |
760 if (info !is null) { | |
761 if ((info.flags & INDENT) !is 0) indent = info.indent; | |
762 if ((info.flags & ALIGNMENT) !is 0) alignment = info.alignment; | |
763 if ((info.flags & JUSTIFY) !is 0) justify = info.justify; | |
764 if ((info.flags & SEGMENTS) !is 0) segments = info.segments; | |
765 } | |
766 } | |
767 if (bulletsIndices !is null) { | |
768 bullets = null; | |
769 bulletsIndices = null; | |
770 } | |
771 if (bullets !is null) { | |
772 for (int i = 0; i < bullets.length; i++) { | |
773 if (bullets[i].indexOf(lineIndex) !is -1) { | |
774 bullet = bullets[i]; | |
775 break; | |
776 } | |
777 } | |
778 } | |
779 ranges = this.ranges; | |
780 styles = this.styles; | |
781 styleCount = this.styleCount; | |
782 if (ranges !is null) { | |
783 rangeStart = getRangeIndex(lineOffset, -1, styleCount << 1); | |
784 } else { | |
785 rangeStart = getRangeIndex(lineOffset, -1, styleCount); | |
786 } | |
787 } | |
788 if (bullet !is null) { | |
789 StyleRange style = bullet.style; | |
790 GlyphMetrics metrics = style.metrics; | |
791 indent += metrics.width; | |
792 } | |
793 layout.setFont(regularFont); | |
794 layout.setAscent(ascent); | |
795 layout.setDescent(descent); | |
796 layout.setText(line); | |
797 layout.setOrientation(orientation); | |
798 layout.setSegments(segments); | |
799 layout.setWidth(width); | |
800 layout.setSpacing(lineSpacing); | |
801 layout.setTabs(new int[]{tabWidth}); | |
802 layout.setIndent(indent); | |
803 layout.setAlignment(alignment); | |
804 layout.setJustify(justify); | |
805 | |
806 int lastOffset = 0; | |
807 int length = line.length(); | |
808 if (styles !is null) { | |
809 if (ranges !is null) { | |
810 int rangeCount = styleCount << 1; | |
811 for (int i = rangeStart; i < rangeCount; i += 2) { | |
812 int start, end; | |
813 if (lineOffset > ranges[i]) { | |
814 start = 0; | |
815 end = Math.min (length, ranges[i + 1] - lineOffset + ranges[i]); | |
816 } else { | |
817 start = ranges[i] - lineOffset; | |
818 end = Math.min(length, start + ranges[i + 1]); | |
819 } | |
820 if (start >= length) break; | |
821 if (lastOffset < start) { | |
822 layout.setStyle(null, lastOffset, start - 1); | |
823 } | |
824 layout.setStyle(getStyleRange(styles[i >> 1]), start, end); | |
825 lastOffset = Math.max(lastOffset, end); | |
826 } | |
827 } else { | |
828 for (int i = rangeStart; i < styleCount; i++) { | |
829 int start, end; | |
830 if (lineOffset > styles[i].start) { | |
831 start = 0; | |
832 end = Math.min (length, styles[i].length - lineOffset + styles[i].start); | |
833 } else { | |
834 start = styles[i].start - lineOffset; | |
835 end = Math.min(length, start + styles[i].length); | |
836 } | |
837 if (start >= length) break; | |
838 if (lastOffset < start) { | |
839 layout.setStyle(null, lastOffset, start - 1); | |
840 } | |
841 layout.setStyle(getStyleRange(styles[i]), start, end); | |
842 lastOffset = Math.max(lastOffset, end); | |
843 } | |
844 } | |
845 } | |
846 if (lastOffset < length) layout.setStyle(null, lastOffset, length); | |
847 if (styledText !is null && styledText.ime !is null) { | |
848 IME ime = styledText.ime; | |
849 int compositionOffset = ime.getCompositionOffset(); | |
850 if (compositionOffset !is -1) { | |
851 int commitCount = ime.getCommitCount(); | |
852 int compositionLength = ime.getText().length(); | |
853 if (compositionLength !is commitCount) { | |
854 int compositionLine = content.getLineAtOffset(compositionOffset); | |
855 if (compositionLine is lineIndex) { | |
856 int[] imeRanges = ime.getRanges(); | |
857 TextStyle[] imeStyles = ime.getStyles(); | |
858 if (imeRanges.length > 0) { | |
859 for (int i = 0; i < imeStyles.length; i++) { | |
860 int start = imeRanges[i*2] - lineOffset; | |
861 int end = imeRanges[i*2+1] - lineOffset; | |
862 TextStyle imeStyle = imeStyles[i], userStyle; | |
863 for (int j = start; j <= end; j++) { | |
864 userStyle = layout.getStyle(j); | |
865 if (userStyle is null && j > 0) userStyle = layout.getStyle(j - 1); | |
866 if (userStyle is null && j + 1 < length) userStyle = layout.getStyle(j + 1); | |
867 if (userStyle is null) { | |
868 layout.setStyle(imeStyle, j, j); | |
869 } else { | |
870 TextStyle newStyle = new TextStyle(imeStyle); | |
871 if (newStyle.font is null) newStyle.font = userStyle.font; | |
872 if (newStyle.foreground is null) newStyle.foreground = userStyle.foreground; | |
873 if (newStyle.background is null) newStyle.background = userStyle.background; | |
874 layout.setStyle(newStyle, j, j); | |
875 } | |
876 } | |
877 } | |
878 } else { | |
879 int start = compositionOffset - lineOffset; | |
880 int end = start + compositionLength - 1; | |
881 TextStyle userStyle = layout.getStyle(start); | |
882 if (userStyle is null) { | |
883 if (start > 0) userStyle = layout.getStyle(start - 1); | |
884 if (userStyle is null && end + 1 < length) userStyle = layout.getStyle(end + 1); | |
885 if (userStyle !is null) { | |
886 TextStyle newStyle = new TextStyle(); | |
887 newStyle.font = userStyle.font; | |
888 newStyle.foreground = userStyle.foreground; | |
889 newStyle.background = userStyle.background; | |
890 layout.setStyle(newStyle, start, end); | |
891 } | |
892 } | |
893 } | |
894 } | |
895 } | |
896 } | |
897 } | |
898 | |
899 if (styledText !is null && styledText.isFixedLineHeight()) { | |
900 int index = -1; | |
901 int lineCount = layout.getLineCount(); | |
902 int height = getLineHeight(); | |
903 for (int i = 0; i < lineCount; i++) { | |
904 int lineHeight = layout.getLineBounds(i).height; | |
905 if (lineHeight > height) { | |
906 height = lineHeight; | |
907 index = i; | |
908 } | |
909 } | |
910 if (index !is -1) { | |
911 FontMetrics metrics = layout.getLineMetrics(index); | |
912 ascent = metrics.getAscent() + metrics.getLeading(); | |
913 descent = metrics.getDescent(); | |
914 if (layouts !is null) { | |
915 for (int i = 0; i < layouts.length; i++) { | |
916 if (layouts[i] !is null && layouts[i] !is layout) { | |
917 layouts[i].setAscent(ascent); | |
918 layouts[i].setDescent(descent); | |
919 } | |
920 } | |
921 } | |
922 if (styledText.verticalScrollOffset !is 0) { | |
923 int topIndex = styledText.topIndex; | |
924 int topIndexY = styledText.topIndexY; | |
925 int lineHeight = getLineHeight(); | |
926 if (topIndexY >= 0) { | |
927 styledText.verticalScrollOffset = (topIndex - 1) * lineHeight + lineHeight - topIndexY; | |
928 } else { | |
929 styledText.verticalScrollOffset = topIndex * lineHeight - topIndexY; | |
930 } | |
931 } | |
932 styledText.calculateScrollBars(); | |
933 if (styledText.isBidiCaret()) styledText.createCaretBitmaps(); | |
934 styledText.caretDirection = DWT.NULL; | |
935 styledText.setCaretLocation(); | |
936 styledText.redraw(); | |
937 } | |
938 } | |
939 return layout; | |
940 } | |
941 int getWidth() { | |
942 return maxWidth; | |
943 } | |
944 void reset() { | |
945 if (layouts !is null) { | |
946 for (int i = 0; i < layouts.length; i++) { | |
947 TextLayout layout = layouts[i]; | |
948 if (layout !is null) layout.dispose(); | |
949 } | |
950 layouts = null; | |
951 } | |
952 topIndex = -1; | |
953 stylesSetCount = styleCount = lineCount = 0; | |
954 ranges = null; | |
955 styles = null; | |
956 stylesSet = null; | |
957 lines = null; | |
958 lineWidth = null; | |
959 lineHeight = null; | |
960 bullets = null; | |
961 bulletsIndices = null; | |
962 redrawLines = null; | |
963 } | |
964 void reset(int startLine, int lineCount) { | |
965 int endLine = startLine + lineCount; | |
966 if (startLine < 0 || endLine > lineWidth.length) return; | |
967 for (int i = startLine; i < endLine; i++) { | |
968 lineWidth[i] = -1; | |
969 lineHeight[i] = -1; | |
970 } | |
971 if (startLine <= maxWidthLineIndex && maxWidthLineIndex < endLine) { | |
972 maxWidth = 0; | |
973 maxWidthLineIndex = -1; | |
974 if (lineCount !is this.lineCount) { | |
975 for (int i = 0; i < this.lineCount; i++) { | |
976 if (lineWidth[i] > maxWidth) { | |
977 maxWidth = lineWidth[i]; | |
978 maxWidthLineIndex = i; | |
979 } | |
980 } | |
981 } | |
982 } | |
983 } | |
984 void setContent(StyledTextContent content) { | |
985 reset(); | |
986 this.content = content; | |
987 lineCount = content.getLineCount(); | |
988 lineWidth = new int[lineCount]; | |
989 lineHeight = new int[lineCount]; | |
990 reset(0, lineCount); | |
991 } | |
992 void setFont(Font font, int tabs) { | |
993 TextLayout layout = new TextLayout(device); | |
994 layout.setFont(regularFont); | |
995 if (font !is null) { | |
996 if (boldFont !is null) boldFont.dispose(); | |
997 if (italicFont !is null) italicFont.dispose(); | |
998 if (boldItalicFont !is null) boldItalicFont.dispose(); | |
999 boldFont = italicFont = boldItalicFont = null; | |
1000 regularFont = font; | |
1001 layout.setText(" "); | |
1002 layout.setFont(font); | |
1003 layout.setStyle(new TextStyle(getFont(DWT.NORMAL), null, null), 0, 0); | |
1004 layout.setStyle(new TextStyle(getFont(DWT.BOLD), null, null), 1, 1); | |
1005 layout.setStyle(new TextStyle(getFont(DWT.ITALIC), null, null), 2, 2); | |
1006 layout.setStyle(new TextStyle(getFont(DWT.BOLD | DWT.ITALIC), null, null), 3, 3); | |
1007 FontMetrics metrics = layout.getLineMetrics(0); | |
1008 ascent = metrics.getAscent() + metrics.getLeading(); | |
1009 descent = metrics.getDescent(); | |
1010 boldFont.dispose(); | |
1011 italicFont.dispose(); | |
1012 boldItalicFont.dispose(); | |
1013 boldFont = italicFont = boldItalicFont = null; | |
1014 } | |
1015 layout.dispose(); | |
1016 layout = new TextLayout(device); | |
1017 layout.setFont(regularFont); | |
1018 StringBuffer tabBuffer = new StringBuffer(tabs); | |
1019 for (int i = 0; i < tabs; i++) { | |
1020 tabBuffer.append(' '); | |
1021 } | |
1022 layout.setText(tabBuffer.toString()); | |
1023 tabWidth = layout.getBounds().width; | |
1024 layout.dispose(); | |
1025 if (styledText !is null) { | |
1026 GC gc = new GC(styledText); | |
1027 averageCharWidth = gc.getFontMetrics().getAverageCharWidth(); | |
1028 gc.dispose(); | |
1029 } | |
1030 } | |
1031 void setLineAlignment(int startLine, int count, int alignment) { | |
1032 if (lines is null) lines = new LineInfo[lineCount]; | |
1033 for (int i = startLine; i < startLine + count; i++) { | |
1034 if (lines[i] is null) { | |
1035 lines[i] = new LineInfo(); | |
1036 } | |
1037 lines[i].flags |= ALIGNMENT; | |
1038 lines[i].alignment = alignment; | |
1039 } | |
1040 } | |
1041 void setLineBackground(int startLine, int count, Color background) { | |
1042 if (lines is null) lines = new LineInfo[lineCount]; | |
1043 for (int i = startLine; i < startLine + count; i++) { | |
1044 if (lines[i] is null) { | |
1045 lines[i] = new LineInfo(); | |
1046 } | |
1047 lines[i].flags |= BACKGROUND; | |
1048 lines[i].background = background; | |
1049 } | |
1050 } | |
1051 void setLineBullet(int startLine, int count, Bullet bullet) { | |
1052 if (bulletsIndices !is null) { | |
1053 bulletsIndices = null; | |
1054 bullets = null; | |
1055 } | |
1056 if (bullets is null) { | |
1057 if (bullet is null) return; | |
1058 bullets = new Bullet[1]; | |
1059 bullets[0] = bullet; | |
1060 } | |
1061 int index = 0; | |
1062 while (index < bullets.length) { | |
1063 if (bullet is bullets[index]) break; | |
1064 index++; | |
1065 } | |
1066 if (bullet !is null) { | |
1067 if (index is bullets.length) { | |
1068 Bullet[] newBulletsList = new Bullet[bullets.length + 1]; | |
1069 System.arraycopy(bullets, 0, newBulletsList, 0, bullets.length); | |
1070 newBulletsList[index] = bullet; | |
1071 bullets = newBulletsList; | |
1072 } | |
1073 bullet.addIndices(startLine, count); | |
1074 } else { | |
1075 updateBullets(startLine, count, 0, false); | |
1076 styledText.redrawLinesBullet(redrawLines); | |
1077 redrawLines = null; | |
1078 } | |
1079 } | |
1080 void setLineIndent(int startLine, int count, int indent) { | |
1081 if (lines is null) lines = new LineInfo[lineCount]; | |
1082 for (int i = startLine; i < startLine + count; i++) { | |
1083 if (lines[i] is null) { | |
1084 lines[i] = new LineInfo(); | |
1085 } | |
1086 lines[i].flags |= INDENT; | |
1087 lines[i].indent = indent; | |
1088 } | |
1089 } | |
1090 void setLineJustify(int startLine, int count, bool justify) { | |
1091 if (lines is null) lines = new LineInfo[lineCount]; | |
1092 for (int i = startLine; i < startLine + count; i++) { | |
1093 if (lines[i] is null) { | |
1094 lines[i] = new LineInfo(); | |
1095 } | |
1096 lines[i].flags |= JUSTIFY; | |
1097 lines[i].justify = justify; | |
1098 } | |
1099 } | |
1100 void setLineSegments(int startLine, int count, int[] segments) { | |
1101 if (lines is null) lines = new LineInfo[lineCount]; | |
1102 for (int i = startLine; i < startLine + count; i++) { | |
1103 if (lines[i] is null) { | |
1104 lines[i] = new LineInfo(); | |
1105 } | |
1106 lines[i].flags |= SEGMENTS; | |
1107 lines[i].segments = segments; | |
1108 } | |
1109 } | |
1110 void setStyleRanges (int[] newRanges, StyleRange[] newStyles) { | |
1111 if (newStyles is null) { | |
1112 stylesSetCount = styleCount = 0; | |
1113 ranges = null; | |
1114 styles = null; | |
1115 stylesSet = null; | |
1116 return; | |
1117 } | |
1118 if (newRanges is null && COMPACT_STYLES) { | |
1119 newRanges = new int[newStyles.length << 1]; | |
1120 StyleRange[] tmpStyles = new StyleRange[newStyles.length]; | |
1121 if (stylesSet is null) stylesSet = new StyleRange[4]; | |
1122 for (int i = 0, j = 0; i < newStyles.length; i++) { | |
1123 StyleRange newStyle = newStyles[i]; | |
1124 newRanges[j++] = newStyle.start; | |
1125 newRanges[j++] = newStyle.length; | |
1126 int index = 0; | |
1127 while (index < stylesSetCount) { | |
1128 if (stylesSet[index].similarTo(newStyle)) break; | |
1129 index++; | |
1130 } | |
1131 if (index is stylesSetCount) { | |
1132 if (stylesSetCount is stylesSet.length) { | |
1133 StyleRange[] tmpStylesSet = new StyleRange[stylesSetCount + 4]; | |
1134 System.arraycopy(stylesSet, 0, tmpStylesSet, 0, stylesSetCount); | |
1135 stylesSet = tmpStylesSet; | |
1136 } | |
1137 stylesSet[stylesSetCount++] = newStyle; | |
1138 } | |
1139 tmpStyles[i] = stylesSet[index]; | |
1140 } | |
1141 newStyles = tmpStyles; | |
1142 } | |
1143 | |
1144 if (styleCount is 0) { | |
1145 if (newRanges !is null) { | |
1146 ranges = new int[newRanges.length]; | |
1147 System.arraycopy(newRanges, 0, ranges, 0, ranges.length); | |
1148 } | |
1149 styles = new StyleRange[newStyles.length]; | |
1150 System.arraycopy(newStyles, 0, styles, 0, styles.length); | |
1151 styleCount = newStyles.length; | |
1152 return; | |
1153 } | |
1154 if (newRanges !is null && ranges is null) { | |
1155 ranges = new int[styles.length << 1]; | |
1156 for (int i = 0, j = 0; i < styleCount; i++) { | |
1157 ranges[j++] = styles[i].start; | |
1158 ranges[j++] = styles[i].length; | |
1159 } | |
1160 } | |
1161 if (newRanges is null && ranges !is null) { | |
1162 newRanges = new int[newStyles.length << 1]; | |
1163 for (int i = 0, j = 0; i < newStyles.length; i++) { | |
1164 newRanges[j++] = newStyles[i].start; | |
1165 newRanges[j++] = newStyles[i].length; | |
1166 } | |
1167 } | |
1168 if (ranges !is null) { | |
1169 int rangeCount = styleCount << 1; | |
1170 int start = newRanges[0]; | |
1171 int modifyStart = getRangeIndex(start, -1, rangeCount), modifyEnd; | |
1172 bool insert = modifyStart is rangeCount; | |
1173 if (!insert) { | |
1174 int end = newRanges[newRanges.length - 2] + newRanges[newRanges.length - 1]; | |
1175 modifyEnd = getRangeIndex(end, modifyStart - 1, rangeCount); | |
1176 insert = modifyStart is modifyEnd && ranges[modifyStart] >= end; | |
1177 } | |
1178 if (insert) { | |
1179 addMerge(newRanges, newStyles, newRanges.length, modifyStart, modifyStart); | |
1180 return; | |
1181 } | |
1182 modifyEnd = modifyStart; | |
1183 int[] mergeRanges = new int[6]; | |
1184 StyleRange[] mergeStyles = new StyleRange[3]; | |
1185 for (int i = 0; i < newRanges.length; i += 2) { | |
1186 int newStart = newRanges[i]; | |
1187 int newEnd = newStart + newRanges[i + 1]; | |
1188 if (newStart is newEnd) continue; | |
1189 int modifyLast = 0, mergeCount = 0; | |
1190 while (modifyEnd < rangeCount) { | |
1191 if (newStart >= ranges[modifyStart] + ranges[modifyStart + 1]) modifyStart += 2; | |
1192 if (ranges[modifyEnd] + ranges[modifyEnd + 1] > newEnd) break; | |
1193 modifyEnd += 2; | |
1194 } | |
1195 if (ranges[modifyStart] < newStart && newStart < ranges[modifyStart] + ranges[modifyStart + 1]) { | |
1196 mergeStyles[mergeCount >> 1] = styles[modifyStart >> 1]; | |
1197 mergeRanges[mergeCount] = ranges[modifyStart]; | |
1198 mergeRanges[mergeCount + 1] = newStart - ranges[modifyStart]; | |
1199 mergeCount += 2; | |
1200 } | |
1201 mergeStyles[mergeCount >> 1] = newStyles[i >> 1]; | |
1202 mergeRanges[mergeCount] = newStart; | |
1203 mergeRanges[mergeCount + 1] = newRanges[i + 1]; | |
1204 mergeCount += 2; | |
1205 if (modifyEnd < rangeCount && ranges[modifyEnd] < newEnd && newEnd < ranges[modifyEnd] + ranges[modifyEnd + 1]) { | |
1206 mergeStyles[mergeCount >> 1] = styles[modifyEnd >> 1]; | |
1207 mergeRanges[mergeCount] = newEnd; | |
1208 mergeRanges[mergeCount + 1] = ranges[modifyEnd] + ranges[modifyEnd + 1] - newEnd; | |
1209 mergeCount += 2; | |
1210 modifyLast = 2; | |
1211 } | |
1212 int grow = addMerge(mergeRanges, mergeStyles, mergeCount, modifyStart, modifyEnd + modifyLast); | |
1213 rangeCount += grow; | |
1214 modifyStart = modifyEnd += grow; | |
1215 } | |
1216 } else { | |
1217 int start = newStyles[0].start; | |
1218 int modifyStart = getRangeIndex(start, -1, styleCount), modifyEnd; | |
1219 bool insert = modifyStart is styleCount; | |
1220 if (!insert) { | |
1221 int end = newStyles[newStyles.length - 1].start + newStyles[newStyles.length - 1].length; | |
1222 modifyEnd = getRangeIndex(end, modifyStart - 1, styleCount); | |
1223 insert = modifyStart is modifyEnd && styles[modifyStart].start >= end; | |
1224 } | |
1225 if (insert) { | |
1226 addMerge(newStyles, newStyles.length, modifyStart, modifyStart); | |
1227 return; | |
1228 } | |
1229 modifyEnd = modifyStart; | |
1230 StyleRange[] mergeStyles = new StyleRange[3]; | |
1231 for (int i = 0; i < newStyles.length; i++) { | |
1232 StyleRange newStyle = newStyles[i], style; | |
1233 int newStart = newStyle.start; | |
1234 int newEnd = newStart + newStyle.length; | |
1235 if (newStart is newEnd) continue; | |
1236 int modifyLast = 0, mergeCount = 0; | |
1237 while (modifyEnd < styleCount) { | |
1238 if (newStart >= styles[modifyStart].start + styles[modifyStart].length) modifyStart++; | |
1239 if (styles[modifyEnd].start + styles[modifyEnd].length > newEnd) break; | |
1240 modifyEnd++; | |
1241 } | |
1242 style = styles[modifyStart]; | |
1243 if (style.start < newStart && newStart < style.start + style.length) { | |
1244 style = mergeStyles[mergeCount++] = (StyleRange)style.clone(); | |
1245 style.length = newStart - style.start; | |
1246 } | |
1247 mergeStyles[mergeCount++] = newStyle; | |
1248 if (modifyEnd < styleCount) { | |
1249 style = styles[modifyEnd]; | |
1250 if (style.start < newEnd && newEnd < style.start + style.length) { | |
1251 style = mergeStyles[mergeCount++] = (StyleRange)style.clone(); | |
1252 style.length += style.start - newEnd; | |
1253 style.start = newEnd; | |
1254 modifyLast = 1; | |
1255 } | |
1256 } | |
1257 int grow = addMerge(mergeStyles, mergeCount, modifyStart, modifyEnd + modifyLast); | |
1258 modifyStart = modifyEnd += grow; | |
1259 } | |
1260 } | |
1261 } | |
1262 void textChanging(TextChangingEvent event) { | |
1263 int start = event.start; | |
1264 int newCharCount = event.newCharCount, replaceCharCount = event.replaceCharCount; | |
1265 int newLineCount = event.newLineCount, replaceLineCount = event.replaceLineCount; | |
1266 | |
1267 updateRanges(start, replaceCharCount, newCharCount); | |
1268 | |
1269 int startLine = content.getLineAtOffset(start); | |
1270 if (replaceCharCount is content.getCharCount()) lines = null; | |
1271 if (replaceLineCount is lineCount) { | |
1272 lineCount = newLineCount; | |
1273 lineWidth = new int[lineCount]; | |
1274 lineHeight = new int[lineCount]; | |
1275 reset(0, lineCount); | |
1276 } else { | |
1277 int delta = newLineCount - replaceLineCount; | |
1278 if (lineCount + delta > lineWidth.length) { | |
1279 int[] newWidths = new int[lineCount + delta + GROW]; | |
1280 System.arraycopy(lineWidth, 0, newWidths, 0, lineCount); | |
1281 lineWidth = newWidths; | |
1282 int[] newHeights = new int[lineCount + delta + GROW]; | |
1283 System.arraycopy(lineHeight, 0, newHeights, 0, lineCount); | |
1284 lineHeight = newHeights; | |
1285 } | |
1286 if (lines !is null) { | |
1287 if (lineCount + delta > lines.length) { | |
1288 LineInfo[] newLines = new LineInfo[lineCount + delta + GROW]; | |
1289 System.arraycopy(lines, 0, newLines, 0, lineCount); | |
1290 lines = newLines; | |
1291 } | |
1292 } | |
1293 int startIndex = startLine + replaceLineCount + 1; | |
1294 int endIndex = startLine + newLineCount + 1; | |
1295 System.arraycopy(lineWidth, startIndex, lineWidth, endIndex, lineCount - startIndex); | |
1296 System.arraycopy(lineHeight, startIndex, lineHeight, endIndex, lineCount - startIndex); | |
1297 for (int i = startLine; i < endIndex; i++) { | |
1298 lineWidth[i] = lineHeight[i] = -1; | |
1299 } | |
1300 for (int i = lineCount + delta; i < lineCount; i++) { | |
1301 lineWidth[i] = lineHeight[i] = -1; | |
1302 } | |
1303 if (layouts !is null) { | |
1304 int layoutStartLine = startLine - topIndex; | |
1305 int layoutEndLine = layoutStartLine + replaceLineCount + 1; | |
1306 for (int i = layoutStartLine; i < layoutEndLine; i++) { | |
1307 if (0 <= i && i < layouts.length) { | |
1308 if (layouts[i] !is null) layouts[i].dispose(); | |
1309 layouts[i] = null; | |
1310 if (bullets !is null && bulletsIndices !is null) bullets[i] = null; | |
1311 } | |
1312 } | |
1313 if (delta > 0) { | |
1314 for (int i = layouts.length - 1; i >= layoutEndLine; i--) { | |
1315 if (0 <= i && i < layouts.length) { | |
1316 endIndex = i + delta; | |
1317 if (0 <= endIndex && endIndex < layouts.length) { | |
1318 layouts[endIndex] = layouts[i]; | |
1319 layouts[i] = null; | |
1320 if (bullets !is null && bulletsIndices !is null) { | |
1321 bullets[endIndex] = bullets[i]; | |
1322 bulletsIndices[endIndex] = bulletsIndices[i]; | |
1323 bullets[i] = null; | |
1324 } | |
1325 } else { | |
1326 if (layouts[i] !is null) layouts[i].dispose(); | |
1327 layouts[i] = null; | |
1328 if (bullets !is null && bulletsIndices !is null) bullets[i] = null; | |
1329 } | |
1330 } | |
1331 } | |
1332 } else if (delta < 0) { | |
1333 for (int i = layoutEndLine; i < layouts.length; i++) { | |
1334 if (0 <= i && i < layouts.length) { | |
1335 endIndex = i + delta; | |
1336 if (0 <= endIndex && endIndex < layouts.length) { | |
1337 layouts[endIndex] = layouts[i]; | |
1338 layouts[i] = null; | |
1339 if (bullets !is null && bulletsIndices !is null) { | |
1340 bullets[endIndex] = bullets[i]; | |
1341 bulletsIndices[endIndex] = bulletsIndices[i]; | |
1342 bullets[i] = null; | |
1343 } | |
1344 } else { | |
1345 if (layouts[i] !is null) layouts[i].dispose(); | |
1346 layouts[i] = null; | |
1347 if (bullets !is null && bulletsIndices !is null) bullets[i] = null; | |
1348 } | |
1349 } | |
1350 } | |
1351 } | |
1352 } | |
1353 if (replaceLineCount !is 0 || newLineCount !is 0) { | |
1354 int startLineOffset = content.getOffsetAtLine(startLine); | |
1355 if (startLineOffset !is start) startLine++; | |
1356 updateBullets(startLine, replaceLineCount, newLineCount, true); | |
1357 if (lines !is null) { | |
1358 startIndex = startLine + replaceLineCount; | |
1359 endIndex = startLine + newLineCount; | |
1360 System.arraycopy(lines, startIndex, lines, endIndex, lineCount - startIndex); | |
1361 for (int i = startLine; i < endIndex; i++) { | |
1362 lines[i] = null; | |
1363 } | |
1364 for (int i = lineCount + delta; i < lineCount; i++) { | |
1365 lines[i] = null; | |
1366 } | |
1367 } | |
1368 } | |
1369 lineCount += delta; | |
1370 if (maxWidthLineIndex !is -1 && startLine <= maxWidthLineIndex && maxWidthLineIndex <= startLine + replaceLineCount) { | |
1371 maxWidth = 0; | |
1372 maxWidthLineIndex = -1; | |
1373 for (int i = 0; i < lineCount; i++) { | |
1374 if (lineWidth[i] > maxWidth) { | |
1375 maxWidth = lineWidth[i]; | |
1376 maxWidthLineIndex = i; | |
1377 } | |
1378 } | |
1379 } | |
1380 } | |
1381 } | |
1382 void updateBullets(int startLine, int replaceLineCount, int newLineCount, bool update) { | |
1383 if (bullets is null) return; | |
1384 if (bulletsIndices !is null) return; | |
1385 for (int i = 0; i < bullets.length; i++) { | |
1386 Bullet bullet = bullets[i]; | |
1387 int[] lines = bullet.removeIndices(startLine, replaceLineCount, newLineCount, update); | |
1388 if (lines !is null) { | |
1389 if (redrawLines is null) { | |
1390 redrawLines = lines; | |
1391 } else { | |
1392 int[] newRedrawBullets = new int[redrawLines.length + lines.length]; | |
1393 System.arraycopy(redrawLines, 0, newRedrawBullets, 0, redrawLines.length); | |
1394 System.arraycopy(lines, 0, newRedrawBullets, redrawLines.length, lines.length); | |
1395 redrawLines = newRedrawBullets; | |
1396 } | |
1397 } | |
1398 } | |
1399 int removed = 0; | |
1400 for (int i = 0; i < bullets.length; i++) { | |
1401 if (bullets[i].size() is 0) removed++; | |
1402 } | |
1403 if (removed > 0) { | |
1404 if (removed is bullets.length) { | |
1405 bullets = null; | |
1406 } else { | |
1407 Bullet[] newBulletsList = new Bullet[bullets.length - removed]; | |
1408 for (int i = 0, j = 0; i < bullets.length; i++) { | |
1409 Bullet bullet = bullets[i]; | |
1410 if (bullet.size() > 0) newBulletsList[j++] = bullet; | |
1411 } | |
1412 bullets = newBulletsList; | |
1413 } | |
1414 } | |
1415 } | |
1416 void updateRanges(int start, int replaceCharCount, int newCharCount) { | |
1417 if (styleCount is 0 || (replaceCharCount is 0 && newCharCount is 0)) return; | |
1418 if (ranges !is null) { | |
1419 int rangeCount = styleCount << 1; | |
1420 int modifyStart = getRangeIndex(start, -1, rangeCount); | |
1421 if (modifyStart is rangeCount) return; | |
1422 int end = start + replaceCharCount; | |
1423 int modifyEnd = getRangeIndex(end, modifyStart - 1, rangeCount); | |
1424 int offset = newCharCount - replaceCharCount; | |
1425 if (modifyStart is modifyEnd && ranges[modifyStart] < start && end < ranges[modifyEnd] + ranges[modifyEnd + 1]) { | |
1426 if (newCharCount is 0) { | |
1427 ranges[modifyStart + 1] -= replaceCharCount; | |
1428 modifyEnd += 2; | |
1429 } else { | |
1430 if (rangeCount + 2 > ranges.length) { | |
1431 int[] newRanges = new int[ranges.length + (GROW << 1)]; | |
1432 System.arraycopy(ranges, 0, newRanges, 0, rangeCount); | |
1433 ranges = newRanges; | |
1434 StyleRange[] newStyles = new StyleRange[styles.length + GROW]; | |
1435 System.arraycopy(styles, 0, newStyles, 0, styleCount); | |
1436 styles = newStyles; | |
1437 } | |
1438 System.arraycopy(ranges, modifyStart + 2, ranges, modifyStart + 4, rangeCount - (modifyStart + 2)); | |
1439 System.arraycopy(styles, (modifyStart + 2) >> 1, styles, (modifyStart + 4) >> 1, styleCount - ((modifyStart + 2) >> 1)); | |
1440 ranges[modifyStart + 3] = ranges[modifyStart] + ranges[modifyStart + 1] - end; | |
1441 ranges[modifyStart + 2] = start + newCharCount; | |
1442 ranges[modifyStart + 1] = start - ranges[modifyStart]; | |
1443 styles[(modifyStart >> 1) + 1] = styles[modifyStart >> 1]; | |
1444 rangeCount += 2; | |
1445 styleCount++; | |
1446 modifyEnd += 4; | |
1447 } | |
1448 if (offset !is 0) { | |
1449 for (int i = modifyEnd; i < rangeCount; i += 2) { | |
1450 ranges[i] += offset; | |
1451 } | |
1452 } | |
1453 } else { | |
1454 if (ranges[modifyStart] < start && start < ranges[modifyStart] + ranges[modifyStart + 1]) { | |
1455 ranges[modifyStart + 1] = start - ranges[modifyStart]; | |
1456 modifyStart += 2; | |
1457 } | |
1458 if (modifyEnd < rangeCount && ranges[modifyEnd] < end && end < ranges[modifyEnd] + ranges[modifyEnd + 1]) { | |
1459 ranges[modifyEnd + 1] = ranges[modifyEnd] + ranges[modifyEnd + 1] - end; | |
1460 ranges[modifyEnd] = end; | |
1461 } | |
1462 if (offset !is 0) { | |
1463 for (int i = modifyEnd; i < rangeCount; i += 2) { | |
1464 ranges[i] += offset; | |
1465 } | |
1466 } | |
1467 System.arraycopy(ranges, modifyEnd, ranges, modifyStart, rangeCount - modifyEnd); | |
1468 System.arraycopy(styles, modifyEnd >> 1, styles, modifyStart >> 1, styleCount - (modifyEnd >> 1)); | |
1469 styleCount -= (modifyEnd - modifyStart) >> 1; | |
1470 } | |
1471 } else { | |
1472 int modifyStart = getRangeIndex(start, -1, styleCount); | |
1473 if (modifyStart is styleCount) return; | |
1474 int end = start + replaceCharCount; | |
1475 int modifyEnd = getRangeIndex(end, modifyStart - 1, styleCount); | |
1476 int offset = newCharCount - replaceCharCount; | |
1477 if (modifyStart is modifyEnd && styles[modifyStart].start < start && end < styles[modifyEnd].start + styles[modifyEnd].length) { | |
1478 if (newCharCount is 0) { | |
1479 styles[modifyStart].length -= replaceCharCount; | |
1480 modifyEnd++; | |
1481 } else { | |
1482 if (styleCount + 1 > styles.length) { | |
1483 StyleRange[] newStyles = new StyleRange[styles.length + GROW]; | |
1484 System.arraycopy(styles, 0, newStyles, 0, styleCount); | |
1485 styles = newStyles; | |
1486 } | |
1487 System.arraycopy(styles, modifyStart + 1, styles, modifyStart + 2, styleCount - (modifyStart + 1)); | |
1488 styles[modifyStart + 1] = (StyleRange)styles[modifyStart].clone(); | |
1489 styles[modifyStart + 1].length = styles[modifyStart].start + styles[modifyStart].length - end; | |
1490 styles[modifyStart + 1].start = start + newCharCount; | |
1491 styles[modifyStart].length = start - styles[modifyStart].start; | |
1492 styleCount++; | |
1493 modifyEnd += 2; | |
1494 } | |
1495 if (offset !is 0) { | |
1496 for (int i = modifyEnd; i < styleCount; i++) { | |
1497 styles[i].start += offset; | |
1498 } | |
1499 } | |
1500 } else { | |
1501 if (styles[modifyStart].start < start && start < styles[modifyStart].start + styles[modifyStart].length) { | |
1502 styles[modifyStart].length = start - styles[modifyStart].start; | |
1503 modifyStart++; | |
1504 } | |
1505 if (modifyEnd < styleCount && styles[modifyEnd].start < end && end < styles[modifyEnd].start + styles[modifyEnd].length) { | |
1506 styles[modifyEnd].length = styles[modifyEnd].start + styles[modifyEnd].length - end; | |
1507 styles[modifyEnd].start = end; | |
1508 } | |
1509 if (offset !is 0) { | |
1510 for (int i = modifyEnd; i < styleCount; i++) { | |
1511 styles[i].start += offset; | |
1512 } | |
1513 } | |
1514 System.arraycopy(styles, modifyEnd, styles, modifyStart, styleCount - modifyEnd); | |
1515 styleCount -= modifyEnd - modifyStart; | |
1516 } | |
1517 } | |
1518 } | |
1519 } |