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 }