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