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