Mercurial > projects > dwt-win
comparison dwt/graphics/TextLayout.d @ 213:36f5cb12e1a2
Update to SWT 3.4M7
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 17 May 2008 17:34:28 +0200 |
parents | ab60f3309436 |
children | a8fed3e56433 |
comparison
equal
deleted
inserted
replaced
212:ab60f3309436 | 213:36f5cb12e1a2 |
---|---|
14 | 14 |
15 import dwt.DWT; | 15 import dwt.DWT; |
16 import dwt.DWTException; | 16 import dwt.DWTException; |
17 import dwt.internal.Compatibility; | 17 import dwt.internal.Compatibility; |
18 import dwt.internal.gdip.Gdip; | 18 import dwt.internal.gdip.Gdip; |
19 | |
19 import dwt.internal.win32.OS; | 20 import dwt.internal.win32.OS; |
20 | 21 |
21 import dwt.graphics.Color; | 22 import dwt.graphics.Color; |
22 import dwt.graphics.Device; | 23 import dwt.graphics.Device; |
23 import dwt.graphics.Font; | 24 import dwt.graphics.Font; |
49 * </p> | 50 * </p> |
50 * | 51 * |
51 * @since 3.0 | 52 * @since 3.0 |
52 */ | 53 */ |
53 public final class TextLayout : Resource { | 54 public final class TextLayout : Resource { |
55 alias Resource.init_ init_; | |
56 | |
54 Font font; | 57 Font font; |
55 String text, segmentsText; | 58 String text, segmentsText; |
56 int lineSpacing; | 59 int lineSpacing; |
57 int ascent, descent; | 60 int ascent, descent; |
58 int alignment; | 61 int alignment; |
61 int indent; | 64 int indent; |
62 bool justify; | 65 bool justify; |
63 int[] tabs; | 66 int[] tabs; |
64 int[] segments; | 67 int[] segments; |
65 StyleItem[] styles; | 68 StyleItem[] styles; |
69 int stylesCount; | |
66 | 70 |
67 StyleItem[] allRuns; | 71 StyleItem[] allRuns; |
68 StyleItem[][] runs; | 72 StyleItem[][] runs; |
69 int[] lineOffset, lineY, lineWidth; | 73 int[] lineOffset, lineY, lineWidth; |
70 void* mLangFontLink2; | 74 void* mLangFontLink2; |
87 static_this_completed = true; | 91 static_this_completed = true; |
88 } | 92 } |
89 } | 93 } |
90 } | 94 } |
91 | 95 |
96 /* IME has a copy of these constants */ | |
97 static const int UNDERLINE_IME_DOT = 1 << 16; | |
98 static const int UNDERLINE_IME_DASH = 2 << 16; | |
99 static const int UNDERLINE_IME_THICK = 3 << 16; | |
100 | |
92 class StyleItem { | 101 class StyleItem { |
93 TextStyle style; | 102 TextStyle style; |
94 int start, length; | 103 int start, length; |
95 bool lineBreak, softBreak, tab; | 104 bool lineBreak, softBreak, tab; |
96 | 105 |
110 int width; | 119 int width; |
111 int ascent; | 120 int ascent; |
112 int descent; | 121 int descent; |
113 int leading; | 122 int leading; |
114 int x; | 123 int x; |
124 int underlinePos, underlineThickness; | |
125 int strikeoutPos, strikeoutThickness; | |
115 | 126 |
116 /* Justify info (malloc during computeRuns) */ | 127 /* Justify info (malloc during computeRuns) */ |
117 int* justify; | 128 int* justify; |
118 | 129 |
119 /* ScriptBreak */ | 130 /* ScriptBreak */ |
189 * | 200 * |
190 * @see #dispose() | 201 * @see #dispose() |
191 */ | 202 */ |
192 public this (Device device) { | 203 public this (Device device) { |
193 static_this(); | 204 static_this(); |
194 if (device is null) device = Device.getDevice(); | 205 super(device); |
195 if (device is null) DWT.error(DWT.ERROR_NULL_ARGUMENT); | |
196 this.device = device; | |
197 wrapWidth = ascent = descent = -1; | 206 wrapWidth = ascent = descent = -1; |
198 lineSpacing = 0; | 207 lineSpacing = 0; |
199 orientation = DWT.LEFT_TO_RIGHT; | 208 orientation = DWT.LEFT_TO_RIGHT; |
200 styles = new StyleItem[2]; | 209 styles = new StyleItem[2]; |
201 styles[0] = new StyleItem(); | 210 styles[0] = new StyleItem(); |
202 styles[1] = new StyleItem(); | 211 styles[1] = new StyleItem(); |
212 stylesCount = 2; | |
203 text = ""; //$NON-NLS-1$ | 213 text = ""; //$NON-NLS-1$ |
204 void* ppv; | 214 void* ppv; |
205 OS.OleInitialize(null); | 215 OS.OleInitialize(null); |
206 if (OS.CoCreateInstance(CLSID_CMultiLanguage.ptr, null, OS.CLSCTX_INPROC_SERVER, IID_IMLangFontLink2.ptr, cast(void*)&ppv) is OS.S_OK) { | 216 if (OS.CoCreateInstance(CLSID_CMultiLanguage.ptr, null, OS.CLSCTX_INPROC_SERVER, IID_IMLangFontLink2.ptr, cast(void*)&ppv) is OS.S_OK) { |
207 mLangFontLink2 = ppv; | 217 mLangFontLink2 = ppv; |
208 } | 218 } |
209 if (device.tracking) device.new_Object(this); | 219 init_(); |
210 } | 220 } |
211 | 221 |
212 void breakRun(StyleItem run) { | 222 void breakRun(StyleItem run) { |
213 if (run.psla !is null) return; | 223 if (run.psla !is null) return; |
214 wchar[] chars = StrToWCHARs( 0, segmentsText[ run.start .. run.start + run.length ] ); | 224 wchar[] chars = StrToWCHARs( 0, segmentsText[ run.start .. run.start + run.length ] ); |
347 start = run.length - 1; | 357 start = run.length - 1; |
348 } | 358 } |
349 if (start is 0 && i !is lineStart && !run.tab) { | 359 if (start is 0 && i !is lineStart && !run.tab) { |
350 run = allRuns[--i]; | 360 run = allRuns[--i]; |
351 } else if (start <= 0 && i is lineStart) { | 361 } else if (start <= 0 && i is lineStart) { |
352 i = firstIndice; | 362 if (lineWidth is wrapWidth && firstIndice > 0) { |
353 run = allRuns[i]; | 363 i = firstIndice - 1; |
354 start = Math.max(1, firstStart); | 364 run = allRuns[i]; |
365 start = run.length; | |
366 } else { | |
367 i = firstIndice; | |
368 run = allRuns[i]; | |
369 start = Math.max(1, firstStart); | |
370 } | |
355 } | 371 } |
356 breakRun(run); | 372 breakRun(run); |
357 while (start < run.length) { | 373 while (start < run.length) { |
358 logAttr = run.psla + start; | 374 logAttr = run.psla + start; |
359 //OS.MoveMemory(logAttr, run.psla + (start * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof); | 375 //OS.MoveMemory(logAttr, run.psla + (start * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof); |
469 } | 485 } |
470 if (srcHdc !is null) OS.DeleteDC(srcHdc); | 486 if (srcHdc !is null) OS.DeleteDC(srcHdc); |
471 if (gc is null) device.internal_dispose_GC(hDC, null); | 487 if (gc is null) device.internal_dispose_GC(hDC, null); |
472 } | 488 } |
473 | 489 |
474 /** | 490 void destroy () { |
475 * Disposes of the operating system resources associated with | |
476 * the text layout. Applications must dispose of all allocated text layouts. | |
477 */ | |
478 override public void dispose () { | |
479 if (device is null) return; | |
480 freeRuns(); | 491 freeRuns(); |
481 font = null; | 492 font = null; |
482 text = null; | 493 text = null; |
483 segmentsText = null; | 494 segmentsText = null; |
484 tabs = null; | 495 tabs = null; |
491 /* Release() */ | 502 /* Release() */ |
492 OS.VtblCall(2, mLangFontLink2); | 503 OS.VtblCall(2, mLangFontLink2); |
493 mLangFontLink2 = null; | 504 mLangFontLink2 = null; |
494 } | 505 } |
495 OS.OleUninitialize(); | 506 OS.OleUninitialize(); |
496 if (device.tracking) device.dispose_Object(this); | |
497 device = null; | |
498 } | 507 } |
499 | 508 |
500 /** | 509 /** |
501 * Draws the receiver's text using the specified GC at the specified | 510 * Draws the receiver's text using the specified GC at the specified |
502 * point. | 511 * point. |
593 Gdip.Matrix_Invert(identity_); | 602 Gdip.Matrix_Invert(identity_); |
594 Gdip.Matrix_Multiply(matrix, identity_, Gdip.MatrixOrderAppend); | 603 Gdip.Matrix_Multiply(matrix, identity_, Gdip.MatrixOrderAppend); |
595 Gdip.Matrix_delete(identity_); | 604 Gdip.Matrix_delete(identity_); |
596 if (!Gdip.Matrix_IsIdentity(matrix)) { | 605 if (!Gdip.Matrix_IsIdentity(matrix)) { |
597 lpXform = new float[6]; | 606 lpXform = new float[6]; |
598 Gdip.Matrix_GetElements(matrix, lpXform); | 607 Gdip.Matrix_GetElements(matrix, lpXform.ptr); |
599 } | 608 } |
600 Gdip.Matrix_delete(matrix); | 609 Gdip.Matrix_delete(matrix); |
601 if ((data.style & DWT.MIRRORED) !is 0 && lpXform !is null) { | 610 if ((data.style & DWT.MIRRORED) !is 0 && lpXform !is null) { |
602 gdip = true; | 611 gdip = true; |
603 lpXform = null; | 612 lpXform = null; |
640 if (selectionBackground is null) selectionBackground = device.getSystemColor(DWT.COLOR_LIST_SELECTION); | 649 if (selectionBackground is null) selectionBackground = device.getSystemColor(DWT.COLOR_LIST_SELECTION); |
641 selectionStart = translateOffset(selectionStart); | 650 selectionStart = translateOffset(selectionStart); |
642 selectionEnd = translateOffset(selectionEnd); | 651 selectionEnd = translateOffset(selectionEnd); |
643 } | 652 } |
644 RECT rect; | 653 RECT rect; |
645 void* selBrush; | 654 Gdip.Brush selBrush; |
646 void* selPen; | 655 Gdip.Pen selPen; |
647 void* selBrushFg; | 656 Gdip.Brush selBrushFg; |
648 | 657 |
649 if (hasSelection || (flags & DWT.LAST_LINE_SELECTION) !is 0) { | 658 if (hasSelection || (flags & DWT.LAST_LINE_SELECTION) !is 0) { |
650 if (gdip) { | 659 if (gdip) { |
651 auto bg = selectionBackground.handle; | 660 auto bg = selectionBackground.handle; |
652 auto argb = ((alpha & 0xFF) << 24) | ((bg >> 16) & 0xFF) | (bg & 0xFF00) | ((bg & 0xFF) << 16); | 661 auto argb = ((alpha & 0xFF) << 24) | ((bg >> 16) & 0xFF) | (bg & 0xFF00) | ((bg & 0xFF) << 16); |
653 auto color = Gdip.Color_new(argb); | 662 auto color = Gdip.Color_new(argb); |
654 selBrush = Gdip.SolidBrush_new(color); | 663 selBrush = cast(Gdip.Brush)Gdip.SolidBrush_new(color); |
655 Gdip.Color_delete(color); | 664 Gdip.Color_delete(color); |
656 auto fg = selectionForeground.handle; | 665 auto fg = selectionForeground.handle; |
657 argb = ((alpha & 0xFF) << 24) | ((fg >> 16) & 0xFF) | (fg & 0xFF00) | ((fg & 0xFF) << 16); | 666 argb = ((alpha & 0xFF) << 24) | ((fg >> 16) & 0xFF) | (fg & 0xFF00) | ((fg & 0xFF) << 16); |
658 color = Gdip.Color_new(argb); | 667 color = Gdip.Color_new(argb); |
659 selBrushFg = Gdip.SolidBrush_new(color); | 668 selBrushFg = cast(Gdip.Brush)Gdip.SolidBrush_new(color); |
660 selPen = Gdip.Pen_new( cast(Gdip.Brush)selBrushFg, 1); | 669 selPen = cast(Gdip.Pen) Gdip.Pen_new( cast(Gdip.Brush)selBrushFg, 1); |
661 Gdip.Color_delete(color); | 670 Gdip.Color_delete(color); |
662 } else { | 671 } else { |
663 selBrush = OS.CreateSolidBrush(selectionBackground.handle); | 672 selBrush = cast(Gdip.Brush)OS.CreateSolidBrush(selectionBackground.handle); |
664 selPen = OS.CreatePen(OS.PS_SOLID, 1, selectionForeground.handle); | 673 selPen = cast(Gdip.Pen)OS.CreatePen(OS.PS_SOLID, 1, selectionForeground.handle); |
665 } | 674 } |
666 } | 675 } |
667 int offset = (orientation & DWT.RIGHT_TO_LEFT) !is 0 ? -1 : 0; | 676 int offset = (orientation & DWT.RIGHT_TO_LEFT) !is 0 ? -1 : 0; |
668 OS.SetBkMode(hdc, OS.TRANSPARENT); | 677 OS.SetBkMode(hdc, OS.TRANSPARENT); |
669 for (int line=0; line<runs.length; line++) { | 678 for (int line=0; line<runs.length; line++) { |
670 int drawX = x + getLineIndent(line); | 679 int drawX = x + getLineIndent(line); |
671 int drawY = y + lineY[line]; | 680 int drawY = y + lineY[line]; |
672 StyleItem[] lineRuns = runs[line]; | 681 StyleItem[] lineRuns = runs[line]; |
673 int lineHeight = lineY[line+1] - lineY[line]; | 682 int lineHeight = lineY[line+1] - lineY[line] - lineSpacing; |
674 if (flags !is 0 && (hasSelection || (flags & DWT.LAST_LINE_SELECTION) !is 0)) { | 683 if (flags !is 0 && (hasSelection || (flags & DWT.LAST_LINE_SELECTION) !is 0)) { |
675 bool extents = false; | 684 bool extents = false; |
676 if (line is runs.length - 1 && (flags & DWT.LAST_LINE_SELECTION) !is 0) { | 685 if (line is runs.length - 1 && (flags & DWT.LAST_LINE_SELECTION) !is 0) { |
677 extents = true; | 686 extents = true; |
678 } else { | 687 } else { |
689 if (extents) { | 698 if (extents) { |
690 int width; | 699 int width; |
691 if ((flags & DWT.FULL_SELECTION) !is 0) { | 700 if ((flags & DWT.FULL_SELECTION) !is 0) { |
692 width = OS.IsWin95 ? 0x7FFF : 0x6FFFFFF; | 701 width = OS.IsWin95 ? 0x7FFF : 0x6FFFFFF; |
693 } else { | 702 } else { |
694 width = (lineHeight - lineSpacing) / 3; | 703 width = lineHeight / 3; |
695 } | 704 } |
696 if (gdip) { | 705 if (gdip) { |
697 Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)selBrush, drawX + lineWidth[line], drawY, width, lineHeight - lineSpacing); | 706 Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)selBrush, drawX + lineWidth[line], drawY, width, lineHeight); |
698 } else { | 707 } else { |
699 OS.SelectObject(hdc, selBrush); | 708 OS.SelectObject(hdc, selBrush); |
700 OS.PatBlt(hdc, drawX + lineWidth[line], drawY, width, lineHeight - lineSpacing, OS.PATCOPY); | 709 OS.PatBlt(hdc, drawX + lineWidth[line], drawY, width, lineHeight, OS.PATCOPY); |
701 } | 710 } |
702 } | 711 } |
703 } | 712 } |
704 if (drawX > clip.x + clip.width) continue; | 713 if (drawX > clip.x + clip.width) continue; |
705 if (drawX + lineWidth[line] < clip.x) continue; | 714 if (drawX + lineWidth[line] < clip.x) continue; |
706 int baseline = Math.max(0, this.ascent); | 715 int baseline = Math.max(0, this.ascent); |
716 int lineUnderlinePos = 0; | |
707 for (int i = 0; i < lineRuns.length; i++) { | 717 for (int i = 0; i < lineRuns.length; i++) { |
708 baseline = Math.max(baseline, lineRuns[i].ascent); | 718 baseline = Math.max(baseline, lineRuns[i].ascent); |
719 lineUnderlinePos = Math.min(lineUnderlinePos, lineRuns[i].underlinePos); | |
709 } | 720 } |
710 int alignmentX = drawX; | 721 int alignmentX = drawX; |
711 for (int i = 0; i < lineRuns.length; i++) { | 722 for (int i = 0; i < lineRuns.length; i++) { |
712 StyleItem run = lineRuns[i]; | 723 StyleItem run = lineRuns[i]; |
713 if (run.length is 0) continue; | 724 if (run.length is 0) continue; |
716 if (!run.lineBreak || run.softBreak) { | 727 if (!run.lineBreak || run.softBreak) { |
717 int end = run.start + run.length - 1; | 728 int end = run.start + run.length - 1; |
718 bool fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end; | 729 bool fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end; |
719 if (fullSelection) { | 730 if (fullSelection) { |
720 if (gdip) { | 731 if (gdip) { |
721 Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)selBrush, drawX, drawY, run.width, lineHeight - lineSpacing); | 732 Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)selBrush, drawX, drawY, run.width, lineHeight); |
722 } else { | 733 } else { |
723 OS.SelectObject(hdc, selBrush); | 734 OS.SelectObject(hdc, selBrush); |
724 OS.PatBlt(hdc, drawX, drawY, run.width, lineHeight - lineSpacing, OS.PATCOPY); | 735 OS.PatBlt(hdc, drawX, drawY, run.width, lineHeight, OS.PATCOPY); |
725 } | 736 } |
726 } else { | 737 } else { |
727 if (run.style !is null && run.style.background !is null) { | 738 if (run.style !is null && run.style.background !is null) { |
728 auto bg = run.style.background.handle; | 739 auto bg = run.style.background.handle; |
729 int drawRunY = drawY + (baseline - run.ascent); | |
730 if (gdip) { | 740 if (gdip) { |
731 int argb = ((alpha & 0xFF) << 24) | ((bg >> 16) & 0xFF) | (bg & 0xFF00) | ((bg & 0xFF) << 16); | 741 int argb = ((alpha & 0xFF) << 24) | ((bg >> 16) & 0xFF) | (bg & 0xFF00) | ((bg & 0xFF) << 16); |
732 auto color = Gdip.Color_new(argb); | 742 auto color = Gdip.Color_new(argb); |
733 auto brush = Gdip.SolidBrush_new(color); | 743 auto brush = Gdip.SolidBrush_new(color); |
734 Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)brush, drawX, drawRunY, run.width, run.ascent + run.descent); | 744 Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)brush, drawX, drawY, run.width, lineHeight); |
735 Gdip.Color_delete(color); | 745 Gdip.Color_delete(color); |
736 Gdip.SolidBrush_delete(brush); | 746 Gdip.SolidBrush_delete(brush); |
737 } else { | 747 } else { |
738 auto hBrush = OS.CreateSolidBrush (bg); | 748 auto hBrush = OS.CreateSolidBrush (bg); |
739 auto oldBrush = OS.SelectObject(hdc, hBrush); | 749 auto oldBrush = OS.SelectObject(hdc, hBrush); |
740 OS.PatBlt(hdc, drawX, drawRunY, run.width, run.ascent + run.descent, OS.PATCOPY); | 750 OS.PatBlt(hdc, drawX, drawY, run.width, lineHeight, OS.PATCOPY); |
741 OS.SelectObject(hdc, oldBrush); | 751 OS.SelectObject(hdc, oldBrush); |
742 OS.DeleteObject(hBrush); | 752 OS.DeleteObject(hBrush); |
743 } | 753 } |
744 } | 754 } |
745 bool partialSelection = hasSelection && !(selectionStart > end || run.start > selectionEnd); | 755 bool partialSelection = hasSelection && !(selectionStart > end || run.start > selectionEnd); |
755 rect.left = drawX + runX; | 765 rect.left = drawX + runX; |
756 rect.top = drawY; | 766 rect.top = drawY; |
757 OS.ScriptCPtoX(selEnd, true, cChars, gGlyphs, run.clusters, run.visAttrs, advances, &run.analysis, &piX); | 767 OS.ScriptCPtoX(selEnd, true, cChars, gGlyphs, run.clusters, run.visAttrs, advances, &run.analysis, &piX); |
758 runX = (orientation & DWT.RIGHT_TO_LEFT) !is 0 ? run.width - piX : piX; | 768 runX = (orientation & DWT.RIGHT_TO_LEFT) !is 0 ? run.width - piX : piX; |
759 rect.right = drawX + runX; | 769 rect.right = drawX + runX; |
760 rect.bottom = drawY + lineHeight - lineSpacing; | 770 rect.bottom = drawY + lineHeight; |
761 if (gdip) { | 771 if (gdip) { |
772 if (rect.left > rect.right) { | |
773 int tmp = rect.left; | |
774 rect.left = rect.right; | |
775 rect.right = tmp; | |
776 } | |
762 Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)selBrush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); | 777 Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)selBrush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); |
763 } else { | 778 } else { |
764 OS.SelectObject(hdc, selBrush); | 779 OS.SelectObject(hdc, selBrush); |
765 OS.PatBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY); | 780 OS.PatBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY); |
766 } | 781 } |
768 } | 783 } |
769 } | 784 } |
770 } | 785 } |
771 drawX += run.width; | 786 drawX += run.width; |
772 } | 787 } |
788 RECT* borderClip = null; | |
773 drawX = alignmentX; | 789 drawX = alignmentX; |
774 for (int i = 0; i < lineRuns.length; i++) { | 790 for (int i = 0; i < lineRuns.length; i++) { |
775 StyleItem run = lineRuns[i]; | 791 StyleItem run = lineRuns[i]; |
776 if (run.length is 0) continue; | 792 if (run.length is 0) continue; |
777 if (drawX > clip.x + clip.width) break; | 793 if (drawX > clip.x + clip.width) break; |
817 default: | 833 default: |
818 } | 834 } |
819 if ((type & OS.PT_CLOSEFIGURE) !is 0) newType |= Gdip.PathPointTypeCloseSubpath; | 835 if ((type & OS.PT_CLOSEFIGURE) !is 0) newType |= Gdip.PathPointTypeCloseSubpath; |
820 types[typeIndex] = cast(byte)newType; | 836 types[typeIndex] = cast(byte)newType; |
821 } | 837 } |
822 auto path = Gdip.GraphicsPath_new(cast(Gdip.Point[])points, types, count, Gdip.FillModeAlternate); | 838 auto path = Gdip.GraphicsPath_new(cast(Gdip.Point*)points.ptr, types.ptr, count, Gdip.FillModeAlternate); |
823 if (path is null) DWT.error(DWT.ERROR_NO_HANDLES); | 839 if (path is null) DWT.error(DWT.ERROR_NO_HANDLES); |
824 auto brush = foregroundBrush; | 840 auto brush = foregroundBrush; |
825 if (fullSelection) { | 841 if (fullSelection) { |
826 brush = cast(Gdip.Brush)selBrushFg; | 842 brush = cast(Gdip.Brush)selBrushFg; |
827 } else { | 843 } else { |
863 Gdip.Graphics_FillPath(gdipGraphics, brush, path); | 879 Gdip.Graphics_FillPath(gdipGraphics, brush, path); |
864 if ((data.style & DWT.MIRRORED) !is 0) { | 880 if ((data.style & DWT.MIRRORED) !is 0) { |
865 Gdip.Graphics_Restore(gdipGraphics, gstate2); | 881 Gdip.Graphics_Restore(gdipGraphics, gstate2); |
866 } | 882 } |
867 Gdip.Graphics_SetSmoothingMode(gdipGraphics, antialias); | 883 Gdip.Graphics_SetSmoothingMode(gdipGraphics, antialias); |
868 if (run.style !is null && (run.style.underline || run.style.strikeout)) { | 884 drawLines(gdip, gdipGraphics, x, drawY + baseline, lineUnderlinePos, drawY + lineHeight, lineRuns, i, brush, null, alpha); |
869 auto newPen = hasSelection ? cast(Gdip.Pen)selPen : Gdip.Pen_new(brush, 1); | |
870 Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeNone); | |
871 if (run.style.underline) { | |
872 int underlineY = drawY + baseline + 1 - run.style.rise; | |
873 Gdip.Graphics_DrawLine(gdipGraphics, newPen, drawX, underlineY, drawX + run.width, underlineY); | |
874 } | |
875 if (run.style.strikeout) { | |
876 int strikeoutY = drawRunY + run.leading + (run.ascent - run.style.rise) / 2; | |
877 Gdip.Graphics_DrawLine(gdipGraphics, newPen, drawX, strikeoutY, drawX + run.width, strikeoutY); | |
878 } | |
879 if (cast(void*)newPen !is selPen) Gdip.Pen_delete(newPen); | |
880 Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf); | |
881 } | |
882 if (partialSelection) { | 885 if (partialSelection) { |
883 Gdip.Graphics_Restore(gdipGraphics, gstate); | 886 Gdip.Graphics_Restore(gdipGraphics, gstate); |
884 gstate = Gdip.Graphics_Save(gdipGraphics); | 887 gstate = Gdip.Graphics_Save(gdipGraphics); |
885 Gdip.Graphics_SetClip(gdipGraphics, &gdipRect, Gdip.CombineModeIntersect); | 888 Gdip.Graphics_SetClip(gdipGraphics, &gdipRect, Gdip.CombineModeIntersect); |
886 Gdip.Graphics_SetSmoothingMode(gdipGraphics, textAntialias); | 889 Gdip.Graphics_SetSmoothingMode(gdipGraphics, textAntialias); |
887 Gdip.Graphics_FillPath(gdipGraphics, cast(Gdip.Brush)selBrushFg, path); | 890 if ((data.style & DWT.MIRRORED) !is 0) { |
891 gstate2 = Gdip.Graphics_Save(gdipGraphics); | |
892 Gdip.Graphics_ScaleTransform(gdipGraphics, -1, 1, Gdip.MatrixOrderPrepend); | |
893 Gdip.Graphics_TranslateTransform(gdipGraphics, -2 * drawX - run.width, 0, Gdip.MatrixOrderPrepend); | |
894 } | |
895 Gdip.Graphics_FillPath(gdipGraphics, selBrushFg, path); | |
896 if ((data.style & DWT.MIRRORED) !is 0) { | |
897 Gdip.Graphics_Restore(gdipGraphics, gstate2); | |
898 } | |
888 Gdip.Graphics_SetSmoothingMode(gdipGraphics, antialias); | 899 Gdip.Graphics_SetSmoothingMode(gdipGraphics, antialias); |
889 if (run.style !is null && (run.style.underline || run.style.strikeout)) { | 900 drawLines(gdip, gdipGraphics, x, drawY + baseline, lineUnderlinePos, drawY + lineHeight, lineRuns, i, selBrushFg, &rect, alpha); |
890 Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeNone); | |
891 if (run.style.underline) { | |
892 int underlineY = drawY + baseline + 1 - run.style.rise; | |
893 Gdip.Graphics_DrawLine(gdipGraphics, cast(Gdip.Pen)selPen, rect.left, underlineY, rect.right, underlineY); | |
894 } | |
895 if (run.style.strikeout) { | |
896 int strikeoutY = drawRunY + run.leading + (run.ascent - run.style.rise) / 2; | |
897 Gdip.Graphics_DrawLine(gdipGraphics, cast(Gdip.Pen)selPen, rect.left, strikeoutY, rect.right, strikeoutY); | |
898 } | |
899 Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf); | |
900 } | |
901 Gdip.Graphics_Restore(gdipGraphics, gstate); | 901 Gdip.Graphics_Restore(gdipGraphics, gstate); |
902 } | 902 } |
903 borderClip = drawBorder(gdip, gdipGraphics, x, drawY, lineHeight, foregroundBrush, selBrushFg, fullSelection, borderClip, partialSelection ? &rect : null, alpha, lineRuns, i, selectionStart, selectionEnd); | |
903 Gdip.GraphicsPath_delete(path); | 904 Gdip.GraphicsPath_delete(path); |
904 if ( brush !is cast(Gdip.Brush)selBrushFg && brush !is cast(Gdip.Brush)foregroundBrush) | 905 if ( brush !is cast(Gdip.Brush)selBrushFg && brush !is cast(Gdip.Brush)foregroundBrush) |
905 Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)brush); | 906 Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)brush); |
906 } else { | 907 } else { |
907 int fg = foreground; | 908 auto fg = foreground; |
908 if (fullSelection) { | 909 if (fullSelection) { |
909 fg = selectionForeground.handle; | 910 fg = selectionForeground.handle; |
910 } else { | 911 } else { |
911 if (run.style !is null && run.style.foreground !is null) fg = run.style.foreground.handle; | 912 if (run.style !is null && run.style.foreground !is null) fg = run.style.foreground.handle; |
912 } | 913 } |
913 OS.SetTextColor(hdc, fg); | 914 OS.SetTextColor(hdc, fg); |
914 OS.ScriptTextOut(hdc, run.psc, drawX + offset, drawRunY, 0, null, &run.analysis , null, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets); | 915 OS.ScriptTextOut(hdc, run.psc, drawX + offset, drawRunY, 0, null, &run.analysis , null, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets); |
915 if (run.style !is null && (run.style.underline || run.style.strikeout)) { | 916 drawLines(gdip, hdc, x, drawY + baseline, lineUnderlinePos, drawY + lineHeight, lineRuns, i, cast(void*)fg, null, alpha); |
916 auto newPen = hasSelection && fg is selectionForeground.handle ? cast(HPEN)selPen : OS.CreatePen(OS.PS_SOLID, 1, fg); | |
917 auto oldPen = OS.SelectObject(hdc, newPen); | |
918 if (run.style.underline) { | |
919 int underlineY = drawY + baseline + 1 - run.style.rise; | |
920 OS.MoveToEx(hdc, drawX, underlineY, null); | |
921 OS.LineTo(hdc, drawX + run.width, underlineY); | |
922 } | |
923 if (run.style.strikeout) { | |
924 int strikeoutY = drawRunY + run.leading + (run.ascent - run.style.rise) / 2; | |
925 OS.MoveToEx(hdc, drawX, strikeoutY, null); | |
926 OS.LineTo(hdc, drawX + run.width, strikeoutY); | |
927 } | |
928 OS.SelectObject(hdc, oldPen); | |
929 if (!hasSelection || fg !is selectionForeground.handle) OS.DeleteObject(newPen); | |
930 } | |
931 if (partialSelection && fg !is selectionForeground.handle) { | 917 if (partialSelection && fg !is selectionForeground.handle) { |
932 OS.SetTextColor(hdc, selectionForeground.handle); | 918 OS.SetTextColor(hdc, selectionForeground.handle); |
933 OS.ScriptTextOut(hdc, run.psc, drawX + offset, drawRunY, OS.ETO_CLIPPED, &rect, &run.analysis , null, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets); | 919 OS.ScriptTextOut(hdc, run.psc, drawX + offset, drawRunY, OS.ETO_CLIPPED, &rect, &run.analysis , null, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets); |
934 if (run.style !is null && (run.style.underline || run.style.strikeout)) { | 920 drawLines(gdip, hdc, x, drawY + baseline, lineUnderlinePos, drawY + lineHeight, lineRuns, i, cast(void*)selectionForeground.handle, &rect, alpha); |
935 auto oldPen = OS.SelectObject(hdc, selPen); | |
936 if (run.style.underline) { | |
937 int underlineY = drawY + baseline + 1 - run.style.rise; | |
938 OS.MoveToEx(hdc, rect.left, underlineY, null); | |
939 OS.LineTo(hdc, rect.right, underlineY); | |
940 } | |
941 if (run.style.strikeout) { | |
942 int strikeoutY = drawRunY + run.leading + (run.ascent - run.style.rise) / 2; | |
943 OS.MoveToEx(hdc, rect.left, strikeoutY, null); | |
944 OS.LineTo(hdc, rect.right, strikeoutY); | |
945 } | |
946 OS.SelectObject(hdc, oldPen); | |
947 } | |
948 } | 921 } |
922 int selForeground = selectionForeground !is null ? selectionForeground.handle : 0; | |
923 borderClip = drawBorder(gdip, hdc, x, drawY, lineHeight, cast(void*)foreground, cast(void*)selForeground, fullSelection, borderClip, partialSelection ? &rect : null, alpha, lineRuns, i, selectionStart, selectionEnd); | |
949 } | 924 } |
950 } | 925 } |
951 } | 926 } |
952 drawX += run.width; | 927 drawX += run.width; |
953 } | 928 } |
954 } | 929 } |
955 if (gdip) { | 930 if (gdip) { |
956 if (selBrush !is null) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)selBrush); | 931 if (selBrush !is null) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)selBrush); |
957 if (selBrushFg !is null) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)selBrushFg); | 932 if (selBrushFg !is null) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)selBrushFg); |
958 if (selPen !is null) Gdip.Pen_delete(cast(Gdip.Pen)selPen); | 933 if (selPen !is null) Gdip.Pen_delete(selPen); |
959 } else { | 934 } else { |
960 OS.RestoreDC(hdc, state); | 935 OS.RestoreDC(hdc, state); |
961 if (gdipGraphics !is null) Gdip.Graphics_ReleaseHDC(gdipGraphics, hdc); | 936 if (gdipGraphics !is null) Gdip.Graphics_ReleaseHDC(gdipGraphics, hdc); |
962 if (selBrush !is null) OS.DeleteObject (selBrush); | 937 if (selBrush !is null) OS.DeleteObject (selBrush); |
963 if (selPen !is null) OS.DeleteObject (selPen); | 938 if (selPen !is null) OS.DeleteObject (selPen); |
964 } | 939 } |
965 } | 940 } |
966 | 941 |
942 void drawLines(bool advance, void* graphics, int x, int lineBaseline, int lineUnderlinePos, int lineBottom, StyleItem[] line, int index, void* color, RECT* clipRect, int alpha) { | |
943 StyleItem run = line[index]; | |
944 TextStyle style = run.style; | |
945 if (style is null) return; | |
946 if (!style.underline && !style.strikeout) return; | |
947 int runX = x + run.x; | |
948 int underlineY = lineBaseline - lineUnderlinePos; | |
949 int strikeoutY = lineBaseline - run.strikeoutPos; | |
950 if (advance) { | |
951 Gdip.Graphics_SetPixelOffsetMode(cast(Gdip.Graphics)graphics, Gdip.PixelOffsetModeNone); | |
952 auto brush = color; | |
953 if (style.underline) { | |
954 if (style.underlineColor !is null) { | |
955 int fg = style.underlineColor.handle; | |
956 int argb = ((alpha & 0xFF) << 24) | ((fg >> 16) & 0xFF) | (fg & 0xFF00) | ((fg & 0xFF) << 16); | |
957 auto gdiColor = Gdip.Color_new(argb); | |
958 brush = Gdip.SolidBrush_new(gdiColor); | |
959 Gdip.Color_delete(gdiColor); | |
960 } | |
961 switch (style.underlineStyle) { | |
962 case DWT.UNDERLINE_SQUIGGLE: | |
963 case DWT.UNDERLINE_ERROR: { | |
964 int squigglyThickness = 1; | |
965 int squigglyHeight = 2 * squigglyThickness; | |
966 int squigglyY = Math.min(underlineY - squigglyHeight / 2, lineBottom - squigglyHeight - 1); | |
967 int squigglyX = runX; | |
968 for (int i = index; i > 0 && style.isAdherentUnderline(line[i - 1].style); i--) { | |
969 squigglyX = x + line[i - 1].x; | |
970 } | |
971 int gstate = 0; | |
972 if (clipRect is null) { | |
973 gstate = Gdip.Graphics_Save(cast(Gdip.Graphics)graphics); | |
974 Gdip.Rect gdipRect; | |
975 gdipRect.X = runX; | |
976 gdipRect.Y = squigglyY; | |
977 gdipRect.Width = run.width + 1; | |
978 gdipRect.Height = squigglyY + squigglyHeight + 1; | |
979 Gdip.Graphics_SetClip(cast(Gdip.Graphics)graphics, &gdipRect, Gdip.CombineModeIntersect); | |
980 } | |
981 int[] points = computePolyline(squigglyX, squigglyY, runX + run.width, squigglyY + squigglyHeight); | |
982 auto pen = Gdip.Pen_new(cast(Gdip.Brush)brush, squigglyThickness); | |
983 Gdip.Graphics_DrawLines(cast(Gdip.Graphics)graphics, pen, cast(Gdip.Point*)points.ptr, points.length / 2); | |
984 Gdip.Pen_delete(pen); | |
985 if (gstate !is 0) Gdip.Graphics_Restore(cast(Gdip.Graphics)graphics, gstate); | |
986 break; | |
987 } | |
988 case DWT.UNDERLINE_SINGLE: | |
989 Gdip.Graphics_FillRectangle(cast(Gdip.Graphics)graphics, cast(Gdip.Brush)brush, runX, underlineY, run.width, run.underlineThickness); | |
990 break; | |
991 case DWT.UNDERLINE_DOUBLE: | |
992 Gdip.Graphics_FillRectangle(cast(Gdip.Graphics)graphics, cast(Gdip.Brush)brush, runX, underlineY, run.width, run.underlineThickness); | |
993 Gdip.Graphics_FillRectangle(cast(Gdip.Graphics)graphics, cast(Gdip.Brush)brush, runX, underlineY + run.underlineThickness * 2, run.width, run.underlineThickness); | |
994 break; | |
995 case UNDERLINE_IME_THICK: | |
996 Gdip.Graphics_FillRectangle(cast(Gdip.Graphics)graphics, cast(Gdip.Brush)brush, runX - run.underlineThickness, underlineY, run.width, run.underlineThickness * 2); | |
997 break; | |
998 case UNDERLINE_IME_DOT: | |
999 case UNDERLINE_IME_DASH: { | |
1000 auto pen = Gdip.Pen_new(cast(Gdip.Brush)brush, 1); | |
1001 int dashStyle = style.underlineStyle is UNDERLINE_IME_DOT ? Gdip.DashStyleDot : Gdip.DashStyleDash; | |
1002 Gdip.Pen_SetDashStyle(pen, dashStyle); | |
1003 Gdip.Graphics_DrawLine(cast(Gdip.Graphics)graphics, pen, runX, underlineY, runX + run.width, underlineY); | |
1004 Gdip.Pen_delete(pen); | |
1005 break; | |
1006 } | |
1007 } | |
1008 if (brush !is color) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)brush); | |
1009 } | |
1010 if (style.strikeout) { | |
1011 if (style.strikeoutColor !is null) { | |
1012 int fg = style.strikeoutColor.handle; | |
1013 int argb = ((alpha & 0xFF) << 24) | ((fg >> 16) & 0xFF) | (fg & 0xFF00) | ((fg & 0xFF) << 16); | |
1014 auto gdiColor = Gdip.Color_new(argb); | |
1015 brush = Gdip.SolidBrush_new(gdiColor); | |
1016 Gdip.Color_delete(gdiColor); | |
1017 } | |
1018 Gdip.Graphics_FillRectangle(cast(Gdip.Graphics)graphics, cast(Gdip.Brush)brush, runX, strikeoutY, run.width, run.strikeoutThickness); | |
1019 if (brush !is color) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)brush); | |
1020 } | |
1021 Gdip.Graphics_SetPixelOffsetMode(cast(Gdip.Graphics)graphics, Gdip.PixelOffsetModeHalf); | |
1022 } else { | |
1023 uint colorRefUnderline = cast(uint)color; | |
1024 uint colorRefStrikeout = cast(uint)color; | |
1025 int /*long*/ brushUnderline = 0; | |
1026 int /*long*/ brushStrikeout = 0; | |
1027 RECT rect; | |
1028 if (style.underline) { | |
1029 if (style.underlineColor !is null) { | |
1030 colorRefUnderline = style.underlineColor.handle; | |
1031 } | |
1032 switch (style.underlineStyle) { | |
1033 case DWT.UNDERLINE_SQUIGGLE: | |
1034 case DWT.UNDERLINE_ERROR: { | |
1035 int squigglyThickness = 1; | |
1036 int squigglyHeight = 2 * squigglyThickness; | |
1037 int squigglyY = Math.min(underlineY - squigglyHeight / 2, lineBottom - squigglyHeight - 1); | |
1038 int squigglyX = runX; | |
1039 for (int i = index; i > 0 && style.isAdherentUnderline(line[i - 1].style); i--) { | |
1040 squigglyX = x + line[i - 1].x; | |
1041 } | |
1042 int state = OS.SaveDC(graphics); | |
1043 if (clipRect !is null) { | |
1044 OS.IntersectClipRect(graphics, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); | |
1045 } else { | |
1046 OS.IntersectClipRect(graphics, runX, squigglyY, runX + run.width + 1, squigglyY + squigglyHeight + 1); | |
1047 } | |
1048 int[] points = computePolyline(squigglyX, squigglyY, runX + run.width, squigglyY + squigglyHeight); | |
1049 auto pen = OS.CreatePen(OS.PS_SOLID, squigglyThickness, colorRefUnderline); | |
1050 auto oldPen = OS.SelectObject(graphics, pen); | |
1051 OS.Polyline(graphics, cast(POINT*)points.ptr, points.length / 2); | |
1052 int length_ = points.length; | |
1053 if (length_ >= 2 && squigglyThickness <= 1) { | |
1054 OS.SetPixel (graphics, points[length_ - 2], points[length_ - 1], colorRefUnderline); | |
1055 } | |
1056 OS.RestoreDC(graphics, state); | |
1057 OS.SelectObject(graphics, oldPen); | |
1058 OS.DeleteObject(pen); | |
1059 break; | |
1060 } | |
1061 case DWT.UNDERLINE_SINGLE: | |
1062 brushUnderline = cast(uint) OS.CreateSolidBrush(colorRefUnderline); | |
1063 OS.SetRect(&rect, runX, underlineY, runX + run.width, underlineY + run.underlineThickness); | |
1064 if (clipRect !is null) { | |
1065 rect.left = Math.max(rect.left, clipRect.left); | |
1066 rect.right = Math.min(rect.right, clipRect.right); | |
1067 } | |
1068 OS.FillRect(graphics, &rect, cast(void*)brushUnderline); | |
1069 break; | |
1070 case DWT.UNDERLINE_DOUBLE: | |
1071 brushUnderline = cast(uint)OS.CreateSolidBrush(colorRefUnderline); | |
1072 OS.SetRect(&rect, runX, underlineY, runX + run.width, underlineY + run.underlineThickness); | |
1073 if (clipRect !is null) { | |
1074 rect.left = Math.max(rect.left, clipRect.left); | |
1075 rect.right = Math.min(rect.right, clipRect.right); | |
1076 } | |
1077 OS.FillRect(graphics, &rect, cast(void*)brushUnderline); | |
1078 OS.SetRect(&rect, runX, underlineY + run.underlineThickness * 2, runX + run.width, underlineY + run.underlineThickness * 3); | |
1079 if (clipRect !is null) { | |
1080 rect.left = Math.max(rect.left, clipRect.left); | |
1081 rect.right = Math.min(rect.right, clipRect.right); | |
1082 } | |
1083 OS.FillRect(graphics, &rect, cast(void*)brushUnderline); | |
1084 break; | |
1085 case UNDERLINE_IME_THICK: | |
1086 brushUnderline = cast(uint)OS.CreateSolidBrush(colorRefUnderline); | |
1087 OS.SetRect(&rect, runX, underlineY - run.underlineThickness, runX + run.width, underlineY + run.underlineThickness); | |
1088 if (clipRect !is null) { | |
1089 rect.left = Math.max(rect.left, clipRect.left); | |
1090 rect.right = Math.min(rect.right, clipRect.right); | |
1091 } | |
1092 OS.FillRect(graphics, &rect, cast(void*)brushUnderline); | |
1093 break; | |
1094 case UNDERLINE_IME_DASH: | |
1095 case UNDERLINE_IME_DOT: { | |
1096 underlineY = lineBaseline + run.descent; | |
1097 int penStyle = style.underlineStyle is UNDERLINE_IME_DASH ? OS.PS_DASH : OS.PS_DOT; | |
1098 auto pen = OS.CreatePen(penStyle, 1, colorRefUnderline); | |
1099 auto oldPen = OS.SelectObject(graphics, pen); | |
1100 OS.SetRect(&rect, runX, underlineY, runX + run.width, underlineY + run.underlineThickness); | |
1101 if (clipRect !is null) { | |
1102 rect.left = Math.max(rect.left, clipRect.left); | |
1103 rect.right = Math.min(rect.right, clipRect.right); | |
1104 } | |
1105 OS.MoveToEx(graphics, rect.left, rect.top, null); | |
1106 OS.LineTo(graphics, rect.right, rect.top); | |
1107 OS.SelectObject(graphics, oldPen); | |
1108 OS.DeleteObject(pen); | |
1109 break; | |
1110 } | |
1111 } | |
1112 } | |
1113 if (style.strikeout) { | |
1114 if (style.strikeoutColor !is null) { | |
1115 colorRefStrikeout = style.strikeoutColor.handle; | |
1116 } | |
1117 if (brushUnderline !is 0 && colorRefStrikeout is colorRefUnderline) { | |
1118 brushStrikeout = brushUnderline; | |
1119 } else { | |
1120 brushStrikeout = cast(int) OS.CreateSolidBrush(colorRefStrikeout); | |
1121 } | |
1122 OS.SetRect(&rect, runX, strikeoutY, runX + run.width, strikeoutY + run.strikeoutThickness); | |
1123 if (clipRect !is null) { | |
1124 rect.left = Math.max(rect.left, clipRect.left); | |
1125 rect.right = Math.min(rect.right, clipRect.right); | |
1126 } | |
1127 OS.FillRect(graphics, &rect, cast(void*)brushStrikeout); | |
1128 } | |
1129 if (brushUnderline !is 0) OS.DeleteObject(cast(void*)brushUnderline); | |
1130 if (brushStrikeout !is 0 && brushStrikeout !is brushUnderline) OS.DeleteObject(cast(void*)brushStrikeout); | |
1131 } | |
1132 } | |
1133 | |
1134 RECT* drawBorder(bool advance, void* graphics, int x, int y, int lineHeight, void* color, void* selectionColor, bool fullSelection, RECT* clipRect, RECT* rect, int alpha, StyleItem[] line, int index, int selectionStart, int selectionEnd) { | |
1135 StyleItem run = line[index]; | |
1136 TextStyle style = run.style; | |
1137 if (style is null) return null; | |
1138 if (style.borderStyle is DWT.NONE) return null; | |
1139 if (rect !is null) { | |
1140 if (clipRect is null) { | |
1141 clipRect = new RECT (); | |
1142 OS.SetRect(clipRect, -1, rect.top, -1, rect.bottom); | |
1143 } | |
1144 bool isRTL = (orientation & DWT.RIGHT_TO_LEFT) !is 0; | |
1145 if (run.start <= selectionStart && selectionStart <= run.start + run.length) { | |
1146 if (run.analysis.fRTL ^ isRTL) { | |
1147 clipRect.right = rect.left; | |
1148 } else { | |
1149 clipRect.left = rect.left; | |
1150 } | |
1151 } | |
1152 if (run.start <= selectionEnd && selectionEnd <= run.start + run.length) { | |
1153 if (run.analysis.fRTL ^ isRTL) { | |
1154 clipRect.left = rect.right; | |
1155 } else { | |
1156 clipRect.right = rect.right; | |
1157 } | |
1158 } | |
1159 } | |
1160 if (index + 1 >= line.length || !style.isAdherentBorder(line[index + 1].style)) { | |
1161 int left = run.x; | |
1162 for (int i = index; i > 0 && style.isAdherentBorder(line[i - 1].style); i--) { | |
1163 left = line[i - 1].x; | |
1164 } | |
1165 if (advance) { | |
1166 auto brush = color; | |
1167 int customColor = -1; | |
1168 if (style.borderColor !is null) { | |
1169 customColor = style.borderColor.handle; | |
1170 } else { | |
1171 if (style.foreground !is null) { | |
1172 customColor = style.foreground.handle; | |
1173 } | |
1174 if (fullSelection && clipRect is null) { | |
1175 customColor = -1; | |
1176 brush = selectionColor; | |
1177 } | |
1178 } | |
1179 if (customColor !is -1) { | |
1180 int argb = ((alpha & 0xFF) << 24) | ((customColor >> 16) & 0xFF) | (customColor & 0xFF00) | ((customColor & 0xFF) << 16); | |
1181 auto gdiColor = Gdip.Color_new(argb); | |
1182 brush = Gdip.SolidBrush_new(gdiColor); | |
1183 Gdip.Color_delete(gdiColor); | |
1184 } | |
1185 int lineWidth = 1; | |
1186 int lineStyle = Gdip.DashStyleSolid; | |
1187 switch (style.borderStyle) { | |
1188 case DWT.BORDER_SOLID: break; | |
1189 case DWT.BORDER_DASH: lineStyle = Gdip.DashStyleDash; break; | |
1190 case DWT.BORDER_DOT: lineStyle = Gdip.DashStyleDot; break; | |
1191 } | |
1192 auto pen = Gdip.Pen_new(cast(Gdip.Brush)brush, lineWidth); | |
1193 Gdip.Pen_SetDashStyle(pen, lineStyle); | |
1194 float gdipXOffset = 0.5f, gdipYOffset = 0.5f; | |
1195 Gdip.Graphics_TranslateTransform(cast(Gdip.Graphics)graphics, gdipXOffset, gdipYOffset, Gdip.MatrixOrderPrepend); | |
1196 if (style.borderColor is null && clipRect !is null) { | |
1197 int gstate = Gdip.Graphics_Save(cast(Gdip.Graphics)graphics); | |
1198 if (clipRect.left is -1) clipRect.left = 0; | |
1199 if (clipRect.right is -1) clipRect.right = 0x7ffff; | |
1200 Gdip.Rect gdipRect; | |
1201 gdipRect.X = clipRect.left; | |
1202 gdipRect.Y = clipRect.top; | |
1203 gdipRect.Width = clipRect.right - clipRect.left; | |
1204 gdipRect.Height = clipRect.bottom - clipRect.top; | |
1205 Gdip.Graphics_SetClip(cast(Gdip.Graphics)graphics, &gdipRect, Gdip.CombineModeExclude); | |
1206 Gdip.Graphics_DrawRectangle(cast(Gdip.Graphics)graphics, pen, x + left, y, run.x + run.width - left - 1, lineHeight - 1); | |
1207 Gdip.Graphics_Restore(cast(Gdip.Graphics)graphics, gstate); | |
1208 gstate = Gdip.Graphics_Save(cast(Gdip.Graphics)graphics); | |
1209 Gdip.Graphics_SetClip(cast(Gdip.Graphics)graphics, &gdipRect, Gdip.CombineModeIntersect); | |
1210 auto selPen = Gdip.Pen_new(cast(Gdip.Brush)selectionColor, lineWidth); | |
1211 Gdip.Pen_SetDashStyle(pen, lineStyle); | |
1212 Gdip.Graphics_DrawRectangle(cast(Gdip.Graphics)graphics, selPen, x + left, y, run.x + run.width - left - 1, lineHeight - 1); | |
1213 Gdip.Pen_delete(selPen); | |
1214 Gdip.Graphics_Restore(cast(Gdip.Graphics)graphics, gstate); | |
1215 } else { | |
1216 Gdip.Graphics_DrawRectangle(cast(Gdip.Graphics)graphics, pen, x + left, y, run.x + run.width - left - 1, lineHeight - 1); | |
1217 } | |
1218 Gdip.Graphics_TranslateTransform(cast(Gdip.Graphics)graphics, -gdipXOffset, -gdipYOffset, Gdip.MatrixOrderPrepend); | |
1219 Gdip.Pen_delete(pen); | |
1220 if (customColor !is -1) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)brush); | |
1221 } else { | |
1222 if (style.borderColor !is null) { | |
1223 color = cast(void*)style.borderColor.handle; | |
1224 } else { | |
1225 if (style.foreground !is null) { | |
1226 color = cast(void*)style.foreground.handle; | |
1227 } | |
1228 if (fullSelection && clipRect is null) { | |
1229 color = selectionColor; | |
1230 } | |
1231 } | |
1232 int lineWidth = 1; | |
1233 int lineStyle = OS.PS_SOLID; | |
1234 switch (style.borderStyle) { | |
1235 case DWT.BORDER_SOLID: break; | |
1236 case DWT.BORDER_DASH: lineStyle = OS.PS_DASH; break; | |
1237 case DWT.BORDER_DOT: lineStyle = OS.PS_DOT; break; | |
1238 } | |
1239 LOGBRUSH logBrush; | |
1240 logBrush.lbStyle = OS.BS_SOLID; | |
1241 logBrush.lbColor = cast(uint)color; | |
1242 auto newPen = OS.ExtCreatePen(lineStyle | OS.PS_GEOMETRIC, Math.max(1, lineWidth), &logBrush, 0, null); | |
1243 auto oldPen = OS.SelectObject(graphics, newPen); | |
1244 auto oldBrush = OS.SelectObject(graphics, OS.GetStockObject(OS.NULL_BRUSH)); | |
1245 OS.Rectangle(graphics, x + left, y, x + run.x + run.width, y + lineHeight); | |
1246 if (style.borderColor is null && clipRect !is null && color !is selectionColor) { | |
1247 int state = OS.SaveDC(graphics); | |
1248 if (clipRect.left is -1) clipRect.left = 0; | |
1249 if (clipRect.right is -1) clipRect.right = 0x7ffff; | |
1250 OS.IntersectClipRect(graphics, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); | |
1251 logBrush.lbColor = cast(uint)selectionColor; | |
1252 auto selPen = OS.ExtCreatePen (lineStyle | OS.PS_GEOMETRIC, Math.max(1, lineWidth), &logBrush, 0, null); | |
1253 OS.SelectObject(graphics, selPen); | |
1254 OS.Rectangle(graphics, x + left, y, x + run.x + run.width, y + lineHeight); | |
1255 OS.RestoreDC(graphics, state); | |
1256 OS.SelectObject(graphics, newPen); | |
1257 OS.DeleteObject(selPen); | |
1258 } | |
1259 OS.SelectObject(graphics, oldBrush); | |
1260 OS.SelectObject(graphics, oldPen); | |
1261 OS.DeleteObject(newPen); | |
1262 } | |
1263 return null; | |
1264 } | |
1265 return clipRect; | |
1266 } | |
1267 | |
1268 int[] computePolyline(int left, int top, int right, int bottom) { | |
1269 int height = bottom - top; // can be any number | |
1270 int width = 2 * height; // must be even | |
1271 int peaks = Compatibility.ceil(right - left, width); | |
1272 if (peaks is 0 && right - left > 2) { | |
1273 peaks = 1; | |
1274 } | |
1275 int length_ = ((2 * peaks) + 1) * 2; | |
1276 if (length_ < 0) return new int[0]; | |
1277 | |
1278 int[] coordinates = new int[length_]; | |
1279 for (int i = 0; i < peaks; i++) { | |
1280 int index = 4 * i; | |
1281 coordinates[index] = left + (width * i); | |
1282 coordinates[index+1] = bottom; | |
1283 coordinates[index+2] = coordinates[index] + width / 2; | |
1284 coordinates[index+3] = top; | |
1285 } | |
1286 coordinates[length_-2] = left + (width * peaks); | |
1287 coordinates[length_-1] = bottom; | |
1288 return coordinates; | |
1289 } | |
1290 | |
967 void freeRuns () { | 1291 void freeRuns () { |
968 if (allRuns is null) return; | 1292 if (allRuns is null) return; |
969 for (int i=0; i<allRuns.length; i++) { | 1293 for (int i=0; i<allRuns.length; i++) { |
970 StyleItem run = allRuns[i]; | 1294 StyleItem run = allRuns[i]; |
971 run.free(); | 1295 run.free(); |
1009 checkLayout(); | 1333 checkLayout(); |
1010 return ascent; | 1334 return ascent; |
1011 } | 1335 } |
1012 | 1336 |
1013 /** | 1337 /** |
1014 * Returns the bounds of the receiver. | 1338 * Returns the bounds of the receiver. The width returned is either the |
1339 * width of the longest line or the width set using {@link TextLayout#setWidth(int)}. | |
1340 * To obtain the text bounds of a line use {@link TextLayout#getLineBounds(int)}. | |
1015 * | 1341 * |
1016 * @return the bounds of the receiver | 1342 * @return the bounds of the receiver |
1017 * | 1343 * |
1018 * @exception DWTException <ul> | 1344 * @exception DWTException <ul> |
1019 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | 1345 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> |
1020 * </ul> | 1346 * </ul> |
1347 * | |
1348 * @see #setWidth(int) | |
1349 * @see #getLineBounds(int) | |
1021 */ | 1350 */ |
1022 public Rectangle getBounds () { | 1351 public Rectangle getBounds () { |
1023 checkLayout(); | 1352 checkLayout(); |
1024 computeRuns(null); | 1353 computeRuns(null); |
1025 int width = 0; | 1354 int width = 0; |
1048 * </ul> | 1377 * </ul> |
1049 */ | 1378 */ |
1050 public Rectangle getBounds (int start, int end) { | 1379 public Rectangle getBounds (int start, int end) { |
1051 checkLayout(); | 1380 checkLayout(); |
1052 computeRuns(null); | 1381 computeRuns(null); |
1053 int length = text.length; | 1382 int length_ = text.length; |
1054 if (length is 0) return new Rectangle(0, 0, 0, 0); | 1383 if (length_ is 0) return new Rectangle(0, 0, 0, 0); |
1055 if (start > end) return new Rectangle(0, 0, 0, 0); | 1384 if (start > end) return new Rectangle(0, 0, 0, 0); |
1056 start = Math.min(Math.max(0, start), length - 1); | 1385 start = Math.min(Math.max(0, start), length_ - 1); |
1057 end = Math.min(Math.max(0, end), length - 1); | 1386 end = Math.min(Math.max(0, end), length_ - 1); |
1058 start = translateOffset(start); | 1387 start = translateOffset(start); |
1059 end = translateOffset(end); | 1388 end = translateOffset(end); |
1060 int left = 0x7fffffff, right = 0; | 1389 int left = 0x7fffffff, right = 0; |
1061 int top = 0x7fffffff, bottom = 0; | 1390 int top = 0x7fffffff, bottom = 0; |
1062 bool isRTL = (orientation & DWT.RIGHT_TO_LEFT) !is 0; | 1391 bool isRTL = (orientation & DWT.RIGHT_TO_LEFT) !is 0; |
1185 return item.style.font.handle; | 1514 return item.style.font.handle; |
1186 } | 1515 } |
1187 if (this.font !is null) { | 1516 if (this.font !is null) { |
1188 return this.font.handle; | 1517 return this.font.handle; |
1189 } | 1518 } |
1190 return device.systemFont; | 1519 return device.systemFont.handle; |
1191 } | 1520 } |
1192 | 1521 |
1193 /** | 1522 /** |
1194 * Returns the embedding level for the specified character offset. The | 1523 * Returns the embedding level for the specified character offset. The |
1195 * embedding level is usually used to determine the directionality of a | 1524 * embedding level is usually used to determine the directionality of a |
1205 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | 1534 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> |
1206 */ | 1535 */ |
1207 public int getLevel (int offset) { | 1536 public int getLevel (int offset) { |
1208 checkLayout(); | 1537 checkLayout(); |
1209 computeRuns(null); | 1538 computeRuns(null); |
1210 int length = text.length; | 1539 int length_ = text.length; |
1211 if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); | 1540 if (!(0 <= offset && offset <= length_)) DWT.error(DWT.ERROR_INVALID_RANGE); |
1212 offset = translateOffset(offset); | 1541 offset = translateOffset(offset); |
1213 for (int i=1; i<allRuns.length; i++) { | 1542 for (int i=1; i<allRuns.length; i++) { |
1214 if (allRuns[i].start > offset) { | 1543 if (allRuns[i].start > offset) { |
1215 return allRuns[i - 1].analysis.s.uBidiLevel; | 1544 return allRuns[i - 1].analysis.s.uBidiLevel; |
1216 } | 1545 } |
1304 * </ul> | 1633 * </ul> |
1305 */ | 1634 */ |
1306 public int getLineIndex (int offset) { | 1635 public int getLineIndex (int offset) { |
1307 checkLayout(); | 1636 checkLayout(); |
1308 computeRuns(null); | 1637 computeRuns(null); |
1309 int length = text.length; | 1638 int length_ = text.length; |
1310 if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); | 1639 if (!(0 <= offset && offset <= length_)) DWT.error(DWT.ERROR_INVALID_RANGE); |
1311 offset = translateOffset(offset); | 1640 offset = translateOffset(offset); |
1312 for (int line=0; line<runs.length; line++) { | 1641 for (int line=0; line<runs.length; line++) { |
1313 if (lineOffset[line + 1] > offset) { | 1642 if (lineOffset[line + 1] > offset) { |
1314 return line; | 1643 return line; |
1315 } | 1644 } |
1335 computeRuns(null); | 1664 computeRuns(null); |
1336 if (!(0 <= lineIndex && lineIndex < runs.length)) DWT.error(DWT.ERROR_INVALID_RANGE); | 1665 if (!(0 <= lineIndex && lineIndex < runs.length)) DWT.error(DWT.ERROR_INVALID_RANGE); |
1337 auto hDC = device.internal_new_GC(null); | 1666 auto hDC = device.internal_new_GC(null); |
1338 auto srcHdc = OS.CreateCompatibleDC(hDC); | 1667 auto srcHdc = OS.CreateCompatibleDC(hDC); |
1339 TEXTMETRIC lptm; | 1668 TEXTMETRIC lptm; |
1340 OS.SelectObject(srcHdc, font !is null ? font.handle : device.systemFont); | 1669 OS.SelectObject(srcHdc, font !is null ? font.handle : device.systemFont.handle); |
1341 OS.GetTextMetrics(srcHdc, &lptm); | 1670 OS.GetTextMetrics(srcHdc, &lptm); |
1342 OS.DeleteDC(srcHdc); | 1671 OS.DeleteDC(srcHdc); |
1343 device.internal_dispose_GC(hDC, null); | 1672 device.internal_dispose_GC(hDC, null); |
1344 | 1673 |
1345 int ascent = Math.max(lptm.tmAscent, this.ascent); | 1674 int ascent = Math.max(lptm.tmAscent, this.ascent); |
1402 * @see #getOffset(int, int, int[]) | 1731 * @see #getOffset(int, int, int[]) |
1403 */ | 1732 */ |
1404 public Point getLocation (int offset, bool trailing) { | 1733 public Point getLocation (int offset, bool trailing) { |
1405 checkLayout(); | 1734 checkLayout(); |
1406 computeRuns(null); | 1735 computeRuns(null); |
1407 int length = text.length; | 1736 int length_ = text.length; |
1408 if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); | 1737 if (!(0 <= offset && offset <= length_)) DWT.error(DWT.ERROR_INVALID_RANGE); |
1409 length = segmentsText.length; | 1738 length_ = segmentsText.length; |
1410 offset = translateOffset(offset); | 1739 offset = translateOffset(offset); |
1411 int line; | 1740 int line; |
1412 for (line=0; line<runs.length; line++) { | 1741 for (line=0; line<runs.length; line++) { |
1413 if (lineOffset[line + 1] > offset) break; | 1742 if (lineOffset[line + 1] > offset) break; |
1414 } | 1743 } |
1415 line = Math.min(line, runs.length - 1); | 1744 line = Math.min(line, runs.length - 1); |
1416 StyleItem[] lineRuns = runs[line]; | 1745 if (offset is length_) { |
1417 Point result = null; | 1746 return new Point(getLineIndent(line) + lineWidth[line], lineY[line]); |
1418 if (offset is length) { | 1747 } |
1419 result = new Point(lineWidth[line], lineY[line]); | 1748 int low = -1; |
1420 } else { | 1749 int high = allRuns.length; |
1421 int width = 0; | 1750 while (high - low > 1) { |
1422 for (int i=0; i<lineRuns.length; i++) { | 1751 int index = ((high + low) / 2); |
1423 StyleItem run = lineRuns[i]; | 1752 StyleItem run = allRuns[index]; |
1424 int end = run.start + run.length; | 1753 if (run.start > offset) { |
1425 if (run.start <= offset && offset < end) { | 1754 high = index; |
1426 if (run.style !is null && run.style.metrics !is null) { | 1755 } else if (run.start + run.length <= offset) { |
1427 GlyphMetrics metrics = run.style.metrics; | 1756 low = index; |
1428 width += metrics.width * (offset - run.start + (trailing ? 1 : 0)); | 1757 } else { |
1429 result = new Point(width, lineY[line]); | 1758 int width; |
1430 } else if (run.tab) { | 1759 if (run.style !is null && run.style.metrics !is null) { |
1431 if (trailing || (offset is length)) width += run.width; | 1760 GlyphMetrics metrics = run.style.metrics; |
1432 result = new Point(width, lineY[line]); | 1761 width = metrics.width * (offset - run.start + (trailing ? 1 : 0)); |
1433 } else { | 1762 } else if (run.tab) { |
1434 int runOffset = offset - run.start; | 1763 width = (trailing || (offset is length_)) ? run.width : 0; |
1435 int cChars = run.length; | 1764 } else { |
1436 int gGlyphs = run.glyphCount; | 1765 int runOffset = offset - run.start; |
1437 int piX; | 1766 int cChars = run.length; |
1438 int* advances = run.justify !is null ? run.justify : run.advances; | 1767 int gGlyphs = run.glyphCount; |
1439 OS.ScriptCPtoX(runOffset, trailing, cChars, gGlyphs, run.clusters, run.visAttrs, advances, &run.analysis, &piX); | 1768 int piX; |
1440 if ((orientation & DWT.RIGHT_TO_LEFT) !is 0) { | 1769 int* advances = run.justify !is null ? run.justify : run.advances; |
1441 result = new Point(width + (run.width - piX), lineY[line]); | 1770 OS.ScriptCPtoX(runOffset, trailing, cChars, gGlyphs, run.clusters, run.visAttrs, advances, &run.analysis, &piX); |
1442 } else { | 1771 width = (orientation & DWT.RIGHT_TO_LEFT) !is 0 ? run.width - piX : piX; |
1443 result = new Point(width + piX, lineY[line]); | 1772 } |
1444 } | 1773 return new Point(run.x + width, lineY[line]); |
1445 } | 1774 } |
1446 break; | 1775 } |
1447 } | 1776 return new Point(0, 0); |
1448 width += run.width; | |
1449 } | |
1450 } | |
1451 if (result is null) result = new Point(0, 0); | |
1452 result.x += getLineIndent(line); | |
1453 return result; | |
1454 } | 1777 } |
1455 | 1778 |
1456 /** | 1779 /** |
1457 * Returns the next offset for the specified offset and movement | 1780 * Returns the next offset for the specified offset and movement |
1458 * type. The movement is one of <code>DWT.MOVEMENT_CHAR</code>, | 1781 * type. The movement is one of <code>DWT.MOVEMENT_CHAR</code>, |
1477 return _getOffset (offset, movement, true); | 1800 return _getOffset (offset, movement, true); |
1478 } | 1801 } |
1479 | 1802 |
1480 int _getOffset(int offset, int movement, bool forward) { | 1803 int _getOffset(int offset, int movement, bool forward) { |
1481 computeRuns(null); | 1804 computeRuns(null); |
1482 int length = text.length; | 1805 int length_ = text.length; |
1483 if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); | 1806 if (!(0 <= offset && offset <= length_)) DWT.error(DWT.ERROR_INVALID_RANGE); |
1484 if (forward && offset is length) return length; | 1807 if (forward && offset is length_) return length_; |
1485 if (!forward && offset is 0) return 0; | 1808 if (!forward && offset is 0) return 0; |
1486 int step = forward ? 1 : -1; | 1809 int step = forward ? 1 : -1; |
1487 if ((movement & DWT.MOVEMENT_CHAR) !is 0) return offset + step; | 1810 if ((movement & DWT.MOVEMENT_CHAR) !is 0) return offset + step; |
1488 length = segmentsText.length; | 1811 length_ = segmentsText.length; |
1489 offset = translateOffset(offset); | 1812 offset = translateOffset(offset); |
1490 SCRIPT_LOGATTR* logAttr; | 1813 SCRIPT_LOGATTR* logAttr; |
1491 SCRIPT_PROPERTIES* properties; | 1814 SCRIPT_PROPERTIES* properties; |
1492 int i = forward ? 0 : allRuns.length - 1; | 1815 int i = forward ? 0 : allRuns.length - 1; |
1493 offset = validadeOffset(offset, step); | 1816 offset = validadeOffset(offset, step); |
1543 } | 1866 } |
1544 offset = validadeOffset(offset, step); | 1867 offset = validadeOffset(offset, step); |
1545 } | 1868 } |
1546 } | 1869 } |
1547 i += step; | 1870 i += step; |
1548 } while (0 <= i && i < allRuns.length - 1 && 0 <= offset && offset < length); | 1871 } while (0 <= i && i < allRuns.length - 1 && 0 <= offset && offset < length_); |
1549 return forward ? text.length : 0; | 1872 return forward ? text.length : 0; |
1550 } | 1873 } |
1551 | 1874 |
1552 /** | 1875 /** |
1553 * Returns the character offset for the specified point. | 1876 * Returns the character offset for the specified point. |
1609 int lineCount = runs.length; | 1932 int lineCount = runs.length; |
1610 for (line=0; line<lineCount; line++) { | 1933 for (line=0; line<lineCount; line++) { |
1611 if (lineY[line + 1] > y) break; | 1934 if (lineY[line + 1] > y) break; |
1612 } | 1935 } |
1613 line = Math.min(line, runs.length - 1); | 1936 line = Math.min(line, runs.length - 1); |
1614 x -= getLineIndent(line); | |
1615 StyleItem[] lineRuns = runs[line]; | 1937 StyleItem[] lineRuns = runs[line]; |
1616 if (x >= lineWidth[line]) x = lineWidth[line] - 1; | 1938 int lineIndent = getLineIndent(line); |
1617 if (x < 0) x = 0; | 1939 if (x >= lineIndent + lineWidth[line]) x = lineIndent + lineWidth[line] - 1; |
1618 int width = 0; | 1940 if (x < lineIndent) x = lineIndent; |
1619 for (int i = 0; i < lineRuns.length; i++) { | 1941 int low = -1; |
1620 StyleItem run = lineRuns[i]; | 1942 int high = lineRuns.length; |
1621 if (run.lineBreak && !run.softBreak) return untranslateOffset(run.start); | 1943 while (high - low > 1) { |
1622 if (width + run.width > x) { | 1944 int index = ((high + low) / 2); |
1623 int xRun = x - width; | 1945 StyleItem run = lineRuns[index]; |
1946 if (run.x > x) { | |
1947 high = index; | |
1948 } else if (run.x + run.width <= x) { | |
1949 low = index; | |
1950 } else { | |
1951 if (run.lineBreak && !run.softBreak) return untranslateOffset(run.start); | |
1952 int xRun = x - run.x; | |
1624 if (run.style !is null && run.style.metrics !is null) { | 1953 if (run.style !is null && run.style.metrics !is null) { |
1625 GlyphMetrics metrics = run.style.metrics; | 1954 GlyphMetrics metrics = run.style.metrics; |
1626 if (metrics.width > 0) { | 1955 if (metrics.width > 0) { |
1627 if (trailing !is null) { | 1956 if (trailing !is null) { |
1628 trailing[0] = (xRun % metrics.width < metrics.width / 2) ? 0 : 1; | 1957 trailing[0] = (xRun % metrics.width < metrics.width / 2) ? 0 : 1; |
1629 } | 1958 } |
1630 return untranslateOffset(run.start + xRun / metrics.width); | 1959 return untranslateOffset(run.start + xRun / metrics.width); |
1631 } | 1960 } |
1632 } | 1961 } |
1633 if (run.tab) { | 1962 if (run.tab) { |
1634 if (trailing !is null) trailing[0] = x < (width + run.width / 2) ? 0 : 1; | 1963 if (trailing !is null) trailing[0] = x < (run.x + run.width / 2) ? 0 : 1; |
1635 return untranslateOffset(run.start); | 1964 return untranslateOffset(run.start); |
1636 } | 1965 } |
1637 int cChars = run.length; | 1966 int cChars = run.length; |
1638 int cGlyphs = run.glyphCount; | 1967 int cGlyphs = run.glyphCount; |
1639 int piCP; | 1968 int piCP; |
1644 int* advances = run.justify !is null ? run.justify : run.advances; | 1973 int* advances = run.justify !is null ? run.justify : run.advances; |
1645 OS.ScriptXtoCP(xRun, cChars, cGlyphs, run.clusters, run.visAttrs, advances, &run.analysis, &piCP, &piTrailing); | 1974 OS.ScriptXtoCP(xRun, cChars, cGlyphs, run.clusters, run.visAttrs, advances, &run.analysis, &piCP, &piTrailing); |
1646 if (trailing !is null) trailing[0] = piTrailing; | 1975 if (trailing !is null) trailing[0] = piTrailing; |
1647 return untranslateOffset(run.start + piCP); | 1976 return untranslateOffset(run.start + piCP); |
1648 } | 1977 } |
1649 width += run.width; | |
1650 } | 1978 } |
1651 if (trailing !is null) trailing[0] = 0; | 1979 if (trailing !is null) trailing[0] = 0; |
1652 return untranslateOffset(lineOffset[line + 1]); | 1980 return untranslateOffset(lineOffset[line + 1]); |
1653 } | 1981 } |
1654 | 1982 |
1704 * | 2032 * |
1705 * @since 3.2 | 2033 * @since 3.2 |
1706 */ | 2034 */ |
1707 public int[] getRanges () { | 2035 public int[] getRanges () { |
1708 checkLayout(); | 2036 checkLayout(); |
1709 int[] result = new int[styles.length * 2]; | 2037 int[] result = new int[stylesCount * 2]; |
1710 int count = 0; | 2038 int count = 0; |
1711 for (int i=0; i<styles.length - 1; i++) { | 2039 for (int i=0; i<stylesCount - 1; i++) { |
1712 if (styles[i].style !is null) { | 2040 if (styles[i].style !is null) { |
1713 result[count++] = styles[i].start; | 2041 result[count++] = styles[i].start; |
1714 result[count++] = styles[i + 1].start - 1; | 2042 result[count++] = styles[i + 1].start - 1; |
1715 } | 2043 } |
1716 } | 2044 } |
1738 | 2066 |
1739 String getSegmentsText() { | 2067 String getSegmentsText() { |
1740 if (segments is null) return text; | 2068 if (segments is null) return text; |
1741 int nSegments = segments.length; | 2069 int nSegments = segments.length; |
1742 if (nSegments <= 1) return text; | 2070 if (nSegments <= 1) return text; |
1743 int length = text.length; | 2071 int length_ = text.length; |
1744 if (length is 0) return text; | 2072 if (length_ is 0) return text; |
1745 if (nSegments is 2) { | 2073 if (nSegments is 2) { |
1746 if (segments[0] is 0 && segments[1] is length) return text; | 2074 if (segments[0] is 0 && segments[1] is length_) return text; |
1747 } | 2075 } |
1748 char[] oldChars = new char[length]; | 2076 char[] oldChars = new char[length_]; |
1749 text.getChars(0, length, oldChars, 0); | 2077 text.getChars(0, length_, oldChars, 0); |
1750 char[] newChars = new char[length + nSegments]; | 2078 char[] newChars = new char[length_ + nSegments]; |
1751 int charCount = 0, segmentCount = 0; | 2079 int charCount = 0, segmentCount = 0; |
1752 wchar separator = orientation is DWT.RIGHT_TO_LEFT ? RTL_MARK : LTR_MARK; | 2080 wchar separator = orientation is DWT.RIGHT_TO_LEFT ? RTL_MARK : LTR_MARK; |
1753 while (charCount < length) { | 2081 while (charCount < length_) { |
1754 if (segmentCount < nSegments && charCount is segments[segmentCount]) { | 2082 if (segmentCount < nSegments && charCount is segments[segmentCount]) { |
1755 newChars[charCount + segmentCount++] = separator; | 2083 newChars[charCount + segmentCount++] = separator; |
1756 } else { | 2084 } else { |
1757 newChars[charCount + segmentCount] = oldChars[charCount++]; | 2085 newChars[charCount + segmentCount] = oldChars[charCount++]; |
1758 } | 2086 } |
1791 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> | 2119 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> |
1792 * </ul> | 2120 * </ul> |
1793 */ | 2121 */ |
1794 public TextStyle getStyle (int offset) { | 2122 public TextStyle getStyle (int offset) { |
1795 checkLayout(); | 2123 checkLayout(); |
1796 int length = text.length; | 2124 int length_ = text.length; |
1797 if (!(0 <= offset && offset < length)) DWT.error(DWT.ERROR_INVALID_RANGE); | 2125 if (!(0 <= offset && offset < length_)) DWT.error(DWT.ERROR_INVALID_RANGE); |
1798 for (int i=1; i<styles.length; i++) { | 2126 for (int i=1; i<stylesCount; i++) { |
1799 if (styles[i].start > offset) { | 2127 if (styles[i].start > offset) { |
1800 return styles[i - 1].style; | 2128 return styles[i - 1].style; |
1801 } | 2129 } |
1802 } | 2130 } |
1803 return null; | 2131 return null; |
1816 * | 2144 * |
1817 * @since 3.2 | 2145 * @since 3.2 |
1818 */ | 2146 */ |
1819 public TextStyle[] getStyles () { | 2147 public TextStyle[] getStyles () { |
1820 checkLayout(); | 2148 checkLayout(); |
1821 TextStyle[] result = new TextStyle[styles.length]; | 2149 TextStyle[] result = new TextStyle[stylesCount]; |
1822 int count = 0; | 2150 int count = 0; |
1823 for (int i=0; i<styles.length; i++) { | 2151 for (int i=0; i<stylesCount; i++) { |
1824 if (styles[i].style !is null) { | 2152 if (styles[i].style !is null) { |
1825 result[count++] = styles[i].style; | 2153 result[count++] = styles[i].style; |
1826 } | 2154 } |
1827 } | 2155 } |
1828 if (count !is result.length) { | 2156 if (count !is result.length) { |
1894 /* | 2222 /* |
1895 * Itemize the receiver text | 2223 * Itemize the receiver text |
1896 */ | 2224 */ |
1897 StyleItem[] itemize () { | 2225 StyleItem[] itemize () { |
1898 segmentsText = getSegmentsText(); | 2226 segmentsText = getSegmentsText(); |
1899 int length = segmentsText.length; | 2227 int length_ = segmentsText.length; |
1900 SCRIPT_CONTROL scriptControl; | 2228 SCRIPT_CONTROL scriptControl; |
1901 SCRIPT_STATE scriptState; | 2229 SCRIPT_STATE scriptState; |
1902 final int MAX_ITEM = length + 1; | 2230 final int MAX_ITEM = length_ + 1; |
1903 | 2231 |
1904 if ((orientation & DWT.RIGHT_TO_LEFT) !is 0) { | 2232 if ((orientation & DWT.RIGHT_TO_LEFT) !is 0) { |
1905 scriptState.uBidiLevel = 1; | 2233 scriptState.uBidiLevel = 1; |
1906 scriptState.fArabicNumContext = true; | 2234 scriptState.fArabicNumContext = true; |
1907 SCRIPT_DIGITSUBSTITUTE psds; | 2235 SCRIPT_DIGITSUBSTITUTE psds; |
1924 | 2252 |
1925 /* | 2253 /* |
1926 * Merge styles ranges and script items | 2254 * Merge styles ranges and script items |
1927 */ | 2255 */ |
1928 StyleItem[] merge (SCRIPT_ITEM* items, int itemCount) { | 2256 StyleItem[] merge (SCRIPT_ITEM* items, int itemCount) { |
2257 if (styles.length > stylesCount) { | |
2258 StyleItem[] newStyles = new StyleItem[stylesCount]; | |
2259 System.arraycopy(styles, 0, newStyles, 0, stylesCount); | |
2260 styles = newStyles; | |
2261 } | |
1929 int count = 0, start = 0, end = segmentsText.length, itemIndex = 0, styleIndex = 0; | 2262 int count = 0, start = 0, end = segmentsText.length, itemIndex = 0, styleIndex = 0; |
1930 StyleItem[] runs = new StyleItem[itemCount + styles.length]; | 2263 StyleItem[] runs = new StyleItem[itemCount + stylesCount]; |
1931 SCRIPT_ITEM* scriptItem; | 2264 SCRIPT_ITEM* scriptItem = new SCRIPT_ITEM(); |
1932 bool linkBefore = false; | 2265 bool linkBefore = false; |
1933 while (start < end) { | 2266 while (start < end) { |
1934 StyleItem item = new StyleItem(); | 2267 StyleItem item = new StyleItem(); |
1935 item.start = start; | 2268 item.start = start; |
1936 item.style = styles[styleIndex].style; | 2269 item.style = styles[styleIndex].style; |
1937 runs[count++] = item; | 2270 runs[count++] = item; |
1938 scriptItem = items + itemIndex; | 2271 *scriptItem = items[itemIndex]; |
1939 item.analysis = scriptItem.a; | 2272 item.analysis = scriptItem.a; |
1940 if (linkBefore) { | 2273 if (linkBefore) { |
1941 item.analysis.fLinkBefore = true; | 2274 item.analysis.fLinkBefore = true; |
1942 linkBefore = false; | 2275 linkBefore = false; |
1943 } | 2276 } |
1944 //scriptItem.a = new SCRIPT_ANALYSIS(); | 2277 //scriptItem.a = new SCRIPT_ANALYSIS(); |
1945 scriptItem = items + (itemIndex + 1); | 2278 *scriptItem = items[ itemIndex + 1]; |
1946 int itemLimit = scriptItem.iCharPos; | 2279 int itemLimit = scriptItem.iCharPos; |
1947 int styleLimit = translateOffset(styles[styleIndex + 1].start); | 2280 int styleLimit = translateOffset(styles[styleIndex + 1].start); |
1948 if (styleLimit <= itemLimit) { | 2281 if (styleLimit <= itemLimit) { |
1949 styleIndex++; | 2282 styleIndex++; |
1950 start = styleLimit; | 2283 start = styleLimit; |
1951 if (start < itemLimit && 0 < start && start < end) { | 2284 if (start < itemLimit && 0 < start && start < end) { |
1952 char pChar = segmentsText.charAt(start - 1); | 2285 char pChar = segmentsText.charAt(start - 1); |
1953 char tChar = segmentsText.charAt(start); | 2286 char tChar = segmentsText.charAt(start); |
1954 if (!Compatibility.isWhitespace(pChar) && !Compatibility.isWhitespace(tChar)) { | 2287 if (Compatibility.isLetter(pChar) && Compatibility.isLetter(tChar)) { |
1955 item.analysis.fLinkAfter = true; | 2288 item.analysis.fLinkAfter = true; |
1956 linkBefore = true; | 2289 linkBefore = true; |
1957 } | 2290 } |
1958 } | 2291 } |
1959 } | 2292 } |
1963 } | 2296 } |
1964 item.length = start - item.start; | 2297 item.length = start - item.start; |
1965 } | 2298 } |
1966 StyleItem item = new StyleItem(); | 2299 StyleItem item = new StyleItem(); |
1967 item.start = end; | 2300 item.start = end; |
1968 scriptItem = items + itemCount; | 2301 *scriptItem = items[ itemCount ]; |
1969 item.analysis = scriptItem.a; | 2302 item.analysis = scriptItem.a; |
1970 runs[count++] = item; | 2303 runs[count++] = item; |
1971 if (runs.length !is count) { | 2304 if (runs.length !is count) { |
1972 StyleItem[] result = new StyleItem[count]; | 2305 StyleItem[] result = new StyleItem[count]; |
1973 System.arraycopy(runs, 0, result, 0, count); | 2306 System.arraycopy(runs, 0, result, 0, count); |
2112 * </ul> | 2445 * </ul> |
2113 */ | 2446 */ |
2114 public void setFont (Font font) { | 2447 public void setFont (Font font) { |
2115 checkLayout(); | 2448 checkLayout(); |
2116 if (font !is null && font.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT); | 2449 if (font !is null && font.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT); |
2117 if (this.font is font) return; | 2450 Font oldFont = this.font; |
2118 if (font !is null && font ==/*eq*/ this.font) return; | 2451 if (oldFont is font) return; |
2452 this.font = font; | |
2453 if (oldFont !is null && oldFont.opEquals(font)) return; | |
2119 freeRuns(); | 2454 freeRuns(); |
2120 this.font = font; | |
2121 } | 2455 } |
2122 | 2456 |
2123 /** | 2457 /** |
2124 * Sets the indent of the receiver. This indent it applied of the first line of | 2458 * Sets the indent of the receiver. This indent it applied of the first line of |
2125 * each paragraph. | 2459 * each paragraph. |
2254 if (length_ is 0) return; | 2588 if (length_ is 0) return; |
2255 if (start > end) return; | 2589 if (start > end) return; |
2256 start = Math.min(Math.max(0, start), length_ - 1); | 2590 start = Math.min(Math.max(0, start), length_ - 1); |
2257 end = Math.min(Math.max(0, end), length_ - 1); | 2591 end = Math.min(Math.max(0, end), length_ - 1); |
2258 int low = -1; | 2592 int low = -1; |
2259 int high = styles.length; | 2593 int high = stylesCount; |
2260 while (high - low > 1) { | 2594 while (high - low > 1) { |
2261 int index = (high + low) / 2; | 2595 int index = (high + low) / 2; |
2262 if (styles[index + 1].start > start) { | 2596 if (styles[index + 1].start > start) { |
2263 high = index; | 2597 high = index; |
2264 } else { | 2598 } else { |
2265 low = index; | 2599 low = index; |
2266 } | 2600 } |
2267 } | 2601 } |
2268 if (0 <= high && high < styles.length) { | 2602 if (0 <= high && high < stylesCount) { |
2269 StyleItem item = styles[high]; | 2603 StyleItem item = styles[high]; |
2270 if (item.start is start && styles[high + 1].start - 1 is end) { | 2604 if (item.start is start && styles[high + 1].start - 1 is end) { |
2271 if (style is null) { | 2605 if (style is null) { |
2272 if (item.style is null) return; | 2606 if (item.style is null) return; |
2273 } else { | 2607 } else { |
2276 } | 2610 } |
2277 } | 2611 } |
2278 freeRuns(); | 2612 freeRuns(); |
2279 int modifyStart = high; | 2613 int modifyStart = high; |
2280 int modifyEnd = modifyStart; | 2614 int modifyEnd = modifyStart; |
2281 while (modifyEnd < styles.length) { | 2615 while (modifyEnd < stylesCount) { |
2282 if (styles[modifyEnd + 1].start > end) break; | 2616 if (styles[modifyEnd + 1].start > end) break; |
2283 modifyEnd++; | 2617 modifyEnd++; |
2284 } | 2618 } |
2285 if (modifyStart is modifyEnd) { | 2619 if (modifyStart is modifyEnd) { |
2286 int styleStart = styles[modifyStart].start; | 2620 int styleStart = styles[modifyStart].start; |
2288 if (styleStart is start && styleEnd is end) { | 2622 if (styleStart is start && styleEnd is end) { |
2289 styles[modifyStart].style = style; | 2623 styles[modifyStart].style = style; |
2290 return; | 2624 return; |
2291 } | 2625 } |
2292 if (styleStart !is start && styleEnd !is end) { | 2626 if (styleStart !is start && styleEnd !is end) { |
2293 StyleItem[] newStyles = new StyleItem[styles.length + 2]; | 2627 int newLength = stylesCount + 2; |
2294 System.arraycopy(styles, 0, newStyles, 0, modifyStart + 1); | 2628 if (newLength > styles.length) { |
2629 int newSize = Math.min(newLength + 1024, Math.max(64, newLength * 2)); | |
2630 StyleItem[] newStyles = new StyleItem[newSize]; | |
2631 System.arraycopy(styles, 0, newStyles, 0, stylesCount); | |
2632 styles = newStyles; | |
2633 } | |
2634 System.arraycopy(styles, modifyEnd + 1, styles, modifyEnd + 3, stylesCount - modifyEnd - 1); | |
2295 StyleItem item = new StyleItem(); | 2635 StyleItem item = new StyleItem(); |
2296 item.start = start; | 2636 item.start = start; |
2297 item.style = style; | 2637 item.style = style; |
2298 newStyles[modifyStart + 1] = item; | 2638 styles[modifyStart + 1] = item; |
2299 item = new StyleItem(); | 2639 item = new StyleItem(); |
2300 item.start = end + 1; | 2640 item.start = end + 1; |
2301 item.style = styles[modifyStart].style; | 2641 item.style = styles[modifyStart].style; |
2302 newStyles[modifyStart + 2] = item; | 2642 styles[modifyStart + 2] = item; |
2303 System.arraycopy(styles, modifyEnd + 1, newStyles, modifyEnd + 3, styles.length - modifyEnd - 1); | 2643 stylesCount = newLength; |
2304 styles = newStyles; | |
2305 return; | 2644 return; |
2306 } | 2645 } |
2307 } | 2646 } |
2308 if (start is styles[modifyStart].start) modifyStart--; | 2647 if (start is styles[modifyStart].start) modifyStart--; |
2309 if (end is styles[modifyEnd + 1].start - 1) modifyEnd++; | 2648 if (end is styles[modifyEnd + 1].start - 1) modifyEnd++; |
2310 int newLength = styles.length + 1 - (modifyEnd - modifyStart - 1); | 2649 int newLength = stylesCount + 1 - (modifyEnd - modifyStart - 1); |
2311 StyleItem[] newStyles = new StyleItem[newLength]; | 2650 if (newLength > styles.length) { |
2312 System.arraycopy(styles, 0, newStyles, 0, modifyStart + 1); | 2651 int newSize = Math.min(newLength + 1024, Math.max(64, newLength * 2)); |
2652 StyleItem[] newStyles = new StyleItem[newSize]; | |
2653 System.arraycopy(styles, 0, newStyles, 0, stylesCount); | |
2654 styles = newStyles; | |
2655 } | |
2656 System.arraycopy(styles, modifyEnd, styles, modifyStart + 2, stylesCount - modifyEnd); | |
2313 StyleItem item = new StyleItem(); | 2657 StyleItem item = new StyleItem(); |
2314 item.start = start; | 2658 item.start = start; |
2315 item.style = style; | 2659 item.style = style; |
2316 newStyles[modifyStart + 1] = item; | 2660 styles[modifyStart + 1] = item; |
2317 styles[modifyEnd].start = end + 1; | 2661 styles[modifyStart + 2].start = end + 1; |
2318 System.arraycopy(styles, modifyEnd, newStyles, modifyStart + 2, styles.length - modifyEnd); | 2662 stylesCount = newLength; |
2319 styles = newStyles; | |
2320 } | 2663 } |
2321 | 2664 |
2322 /** | 2665 /** |
2323 * Sets the receiver's tab list. Each value in the tab list specifies | 2666 * Sets the receiver's tab list. Each value in the tab list specifies |
2324 * the space in pixels from the origin of the text layout to the respective | 2667 * the space in pixels from the origin of the text layout to the respective |
2367 this.text = text; | 2710 this.text = text; |
2368 styles = new StyleItem[2]; | 2711 styles = new StyleItem[2]; |
2369 styles[0] = new StyleItem(); | 2712 styles[0] = new StyleItem(); |
2370 styles[1] = new StyleItem(); | 2713 styles[1] = new StyleItem(); |
2371 styles[1].start = text.length; | 2714 styles[1].start = text.length; |
2715 stylesCount = 2; | |
2372 } | 2716 } |
2373 | 2717 |
2374 /** | 2718 /** |
2375 * Sets the line width of the receiver, which determines how | 2719 * Sets the line width of the receiver, which determines how |
2376 * text should be wrapped and aligned. The default value is | 2720 * text should be wrapped and aligned. The default value is |
2417 } | 2761 } |
2418 run.glyphCount = 0; | 2762 run.glyphCount = 0; |
2419 return false; | 2763 return false; |
2420 } | 2764 } |
2421 | 2765 |
2766 struct CallbackDataEnumFontFamExProc { | |
2767 int delegate ( ENUMLOGFONTEX* lpelfe, NEWTEXTMETRICEX* lpntme, int FontType, int lParam) EnumFontFamExProc; | |
2768 int lParam; | |
2769 } | |
2770 extern(Windows) private static int EnumFontFamExProcFunc( ENUMLOGFONTEX* lpelfe, NEWTEXTMETRICEX* lpntme, int FontType, int lParam) { | |
2771 auto cb = cast(CallbackDataEnumFontFamExProc*)cast(void*)lParam; | |
2772 return cb.EnumFontFamExProc( lpelfe, lpntme, FontType, cb.lParam ); | |
2773 } | |
2774 | |
2422 /* | 2775 /* |
2423 * Generate glyphs for one Run. | 2776 * Generate glyphs for one Run. |
2424 */ | 2777 */ |
2425 void shape (HDC hdc, StyleItem run) { | 2778 void shape (HDC hdc, StyleItem run) { |
2426 int[] buffer = new int[1]; | 2779 final int[] buffer = new int[1]; |
2427 char[] chars = new char[run.length]; | 2780 final char[] chars = new char[run.length]; |
2428 segmentsText.getChars(run.start, run.start + run.length, chars, 0); | 2781 segmentsText.getChars(run.start, run.start + run.length, chars, 0); |
2429 wchar[] wchars = StrToWCHARs( chars ); | 2782 wchar[] wchars = StrToWCHARs( chars ); |
2430 int maxGlyphs = (chars.length * 3 / 2) + 16; | 2783 int maxGlyphs = (chars.length * 3 / 2) + 16; |
2431 auto hHeap = OS.GetProcessHeap(); | 2784 auto hHeap = OS.GetProcessHeap(); |
2432 run.glyphs = cast(ushort*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * 2); | 2785 run.glyphs = cast(ushort*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * 2); |
2433 if (run.glyphs is null) DWT.error(DWT.ERROR_NO_HANDLES); | 2786 if (run.glyphs is null) DWT.error(DWT.ERROR_NO_HANDLES); |
2434 run.clusters = cast(WORD*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * 2); | 2787 run.clusters = cast(WORD*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * 2); |
2435 if (run.clusters is null) DWT.error(DWT.ERROR_NO_HANDLES); | 2788 if (run.clusters is null) DWT.error(DWT.ERROR_NO_HANDLES); |
2436 run.visAttrs = cast(SCRIPT_VISATTR*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * SCRIPT_VISATTR_SIZEOF); | 2789 run.visAttrs = cast(SCRIPT_VISATTR*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * SCRIPT_VISATTR_SIZEOF); |
2437 if (run.visAttrs is null) DWT.error(DWT.ERROR_NO_HANDLES); | 2790 if (run.visAttrs is null) DWT.error(DWT.ERROR_NO_HANDLES); |
2438 run.psc = cast(SCRIPT_CACHE*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, 4); | 2791 run.psc = cast(SCRIPT_CACHE*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, (void*).sizeof); |
2439 if (run.psc is null) DWT.error(DWT.ERROR_NO_HANDLES); | 2792 if (run.psc is null) DWT.error(DWT.ERROR_NO_HANDLES); |
2440 if (!shape(hdc, run, chars, buffer, maxGlyphs)) { | 2793 bool shapeSucceed = shape(hdc, run, chars, buffer, maxGlyphs); |
2794 final short script = run.analysis.eScript; | |
2795 | |
2796 if (!shapeSucceed) { | |
2797 /* | |
2798 * Shape failed. | |
2799 * Try to shape with fNoGlyphIndex when the run is in the | |
2800 * Private Use Area. This allows for end-user-defined character (EUDC). | |
2801 */ | |
2802 auto properties = device.scripts[script]; | |
2803 if (properties.fPrivateUseArea) { | |
2804 run.analysis.fNoGlyphIndex = true; | |
2805 shapeSucceed = shape(hdc, run, chars, buffer, maxGlyphs); | |
2806 } | |
2807 } | |
2808 | |
2809 if (!shapeSucceed) { | |
2810 /* | |
2811 * Shape Failed. | |
2812 * Try to use MLANG to find a suitable font to shape the run. | |
2813 */ | |
2441 if (mLangFontLink2 !is null) { | 2814 if (mLangFontLink2 !is null) { |
2442 int dwCodePages; | 2815 int dwCodePages; |
2443 int cchCodePages; | 2816 int cchCodePages; |
2444 /* GetStrCodePages() */ | 2817 /* GetStrCodePages() */ |
2445 OS.VtblCall(4, mLangFontLink2, cast(int)wchars.ptr, wchars.length, 0, cast(int)&dwCodePages, cast(int)&cchCodePages); | 2818 OS.VtblCall(4, mLangFontLink2, cast(int)wchars.ptr, wchars.length, 0, cast(int)&dwCodePages, cast(int)&cchCodePages); |
2446 HFONT hNewFont; | 2819 HFONT hNewFont; |
2447 /* MapFont() */ | 2820 /* MapFont() */ |
2448 if (OS.VtblCall(10, mLangFontLink2, cast(int)hdc, dwCodePages, cast(int)wchars[0], cast(int)hNewFont) is OS.S_OK) { | 2821 if (OS.VtblCall(10, mLangFontLink2, cast(int)hdc, dwCodePages, cast(int)wchars[0], cast(int)hNewFont) is OS.S_OK) { |
2449 auto hFont = OS.SelectObject(hdc, hNewFont); | 2822 auto hFont = OS.SelectObject(hdc, hNewFont); |
2450 if (shape(hdc, run, chars, buffer, maxGlyphs)) { | 2823 shapeSucceed = shape(hdc, run, chars, buffer, maxGlyphs); |
2824 if (shapeSucceed) { | |
2451 run.fallbackFont = hNewFont; | 2825 run.fallbackFont = hNewFont; |
2452 } else { | 2826 } else { |
2453 /* ReleaseFont() */ | 2827 /* ReleaseFont() */ |
2454 OS.VtblCall(8, mLangFontLink2, cast(int)hNewFont); | 2828 OS.VtblCall(8, mLangFontLink2, cast(int)hNewFont); |
2455 OS.SelectObject(hdc, hFont); | 2829 OS.SelectObject(hdc, hFont); |
2456 SCRIPT_PROPERTIES* properties; | |
2457 properties = device.scripts[run.analysis.eScript]; | |
2458 if (properties.fPrivateUseArea) { | |
2459 run.analysis.fNoGlyphIndex = true; | |
2460 } | |
2461 OS.ScriptShape(hdc, run.psc, wchars.ptr, wchars.length, maxGlyphs, &run.analysis, run.glyphs, run.clusters, run.visAttrs, buffer.ptr); | |
2462 run.glyphCount = buffer[0]; | |
2463 } | 2830 } |
2464 } | 2831 } |
2465 } | 2832 } |
2466 } | 2833 } |
2467 ABC abc; | 2834 |
2835 if (!shapeSucceed) { | |
2836 /* | |
2837 * Shape Failed. | |
2838 * Try to shape the run using the LOGFONT in the cache. | |
2839 */ | |
2840 auto hFont = OS.GetCurrentObject(hdc, OS.OBJ_FONT); | |
2841 LOGFONT logFont; | |
2842 OS.GetObject(hFont, LOGFONT.sizeof, &logFont); | |
2843 | |
2844 LOGFONT* cachedLogFont = device.logFontsCache !is null ? device.logFontsCache[script] : null; | |
2845 if (cachedLogFont !is null) { | |
2846 cachedLogFont.lfHeight = logFont.lfHeight; | |
2847 cachedLogFont.lfWeight = logFont.lfWeight; | |
2848 cachedLogFont.lfItalic = logFont.lfItalic; | |
2849 cachedLogFont.lfWidth = logFont.lfWidth; | |
2850 auto newFont = OS.CreateFontIndirect(cachedLogFont); | |
2851 OS.SelectObject(hdc, newFont); | |
2852 shapeSucceed = shape(hdc, run, chars, buffer, maxGlyphs); | |
2853 if (shapeSucceed) { | |
2854 run.fallbackFont = newFont; | |
2855 } else { | |
2856 OS.SelectObject(hdc, hFont); | |
2857 OS.DeleteObject(newFont); | |
2858 } | |
2859 } | |
2860 if (!shapeSucceed) { | |
2861 /* | |
2862 * Shape Failed. | |
2863 * Use EnumFontFamExProc to iterate over every font in the system that supports | |
2864 * the charset of the run and try to shape it. | |
2865 */ | |
2866 if (device.logFontsCache is null) device.logFontsCache = new LOGFONT*[device.scripts.length]; | |
2867 LOGFONT newLogFont; | |
2868 | |
2869 int EnumFontFamExProc( ENUMLOGFONTEX* lpelfe, NEWTEXTMETRICEX* lpntme, int FontType, int lParam) { | |
2870 OS.MoveMemory(&newLogFont, cast(void*)lpelfe, LOGFONT.sizeof); | |
2871 if (FontType is OS.RASTER_FONTTYPE) return 1; | |
2872 newLogFont.lfHeight = logFont.lfHeight; | |
2873 newLogFont.lfWeight = logFont.lfWeight; | |
2874 newLogFont.lfItalic = logFont.lfItalic; | |
2875 newLogFont.lfWidth = logFont.lfWidth; | |
2876 auto newFont = OS.CreateFontIndirect(&newLogFont); | |
2877 OS.SelectObject(hdc, newFont); | |
2878 if (shape(hdc, run, chars, buffer, maxGlyphs)) { | |
2879 run.fallbackFont = newFont; | |
2880 LOGFONT* cacheLogFont = new LOGFONT(); | |
2881 OS.MoveMemory(cacheLogFont, lpelfe, LOGFONT.sizeof); | |
2882 device.logFontsCache[script] = cacheLogFont; | |
2883 return 0; | |
2884 } | |
2885 OS.SelectObject(hdc, hFont); | |
2886 OS.DeleteObject(newFont); | |
2887 return 1; | |
2888 } | |
2889 CallbackDataEnumFontFamExProc cb; | |
2890 cb.EnumFontFamExProc = &EnumFontFamExProc; | |
2891 cb.lParam = 0; | |
2892 auto properties = device.scripts[script]; | |
2893 int charSet = properties.fAmbiguousCharSet ? OS.DEFAULT_CHARSET : properties.bCharSet; | |
2894 newLogFont.lfCharSet = cast(byte)charSet; | |
2895 OS.EnumFontFamiliesEx(hdc, &newLogFont, &EnumFontFamExProcFunc, cast(int) &cb, 0); | |
2896 shapeSucceed = run.fallbackFont !is null; | |
2897 } | |
2898 } | |
2899 | |
2900 if (!shapeSucceed) { | |
2901 /* | |
2902 * Shape Failed. | |
2903 * Give up and shape the run with the default font. | |
2904 * Missing glyphs typically will be represent as black boxes in the text. | |
2905 */ | |
2906 auto wchars_ = StrToWCHARs(chars); | |
2907 OS.ScriptShape(hdc, run.psc, wchars_.ptr, wchars_.length, maxGlyphs, &run.analysis, run.glyphs, run.clusters, run.visAttrs, buffer.ptr); | |
2908 run.glyphCount = buffer[0]; | |
2909 } | |
2910 int[3] abc; | |
2468 run.advances = cast(int*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, run.glyphCount * 4); | 2911 run.advances = cast(int*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, run.glyphCount * 4); |
2469 if (run.advances is null) DWT.error(DWT.ERROR_NO_HANDLES); | 2912 if (run.advances is null) DWT.error(DWT.ERROR_NO_HANDLES); |
2470 run.goffsets = cast(GOFFSET*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, run.glyphCount * GOFFSET_SIZEOF); | 2913 run.goffsets = cast(GOFFSET*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, run.glyphCount * GOFFSET_SIZEOF); |
2471 if (run.goffsets is null) DWT.error(DWT.ERROR_NO_HANDLES); | 2914 if (run.goffsets is null) DWT.error(DWT.ERROR_NO_HANDLES); |
2472 OS.ScriptPlace(hdc, run.psc, run.glyphs, run.glyphCount, run.visAttrs, &run.analysis, run.advances, run.goffsets, &abc); | 2915 OS.ScriptPlace(hdc, run.psc, run.glyphs, run.glyphCount, run.visAttrs, &run.analysis, run.advances, run.goffsets, cast(ABC*)abc.ptr); |
2473 if (run.style !is null && run.style.metrics !is null) { | 2916 run.width = abc[0] + abc[1] + abc[2]; |
2474 GlyphMetrics metrics = run.style.metrics; | 2917 TextStyle style = run.style; |
2475 /* | 2918 if (style !is null) { |
2476 * Bug in Windows, on a Japanese machine, Uniscribe returns glyphcount | 2919 OUTLINETEXTMETRIC* lotm = null; |
2477 * equals zero for FFFC (possibly other unicode code points), the fix | 2920 if (style.underline || style.strikeout) { |
2478 * is to make sure the glyph is at least one pixel wide. | 2921 lotm = new OUTLINETEXTMETRIC(); |
2479 */ | 2922 if (OS.GetOutlineTextMetrics(hdc, OUTLINETEXTMETRIC.sizeof, lotm) is 0) { |
2480 run.width = metrics.width * Math.max (1, run.glyphCount); | 2923 lotm = null; |
2481 run.ascent = metrics.ascent; | 2924 } |
2482 run.descent = metrics.descent; | 2925 } |
2483 run.leading = 0; | 2926 if (style.metrics !is null) { |
2927 GlyphMetrics metrics = style.metrics; | |
2928 /* | |
2929 * Bug in Windows, on a Japanese machine, Uniscribe returns glyphcount | |
2930 * equals zero for FFFC (possibly other unicode code points), the fix | |
2931 * is to make sure the glyph is at least one pixel wide. | |
2932 */ | |
2933 run.width = metrics.width * Math.max (1, run.glyphCount); | |
2934 run.ascent = metrics.ascent; | |
2935 run.descent = metrics.descent; | |
2936 run.leading = 0; | |
2937 } else { | |
2938 TEXTMETRIC lptm; | |
2939 if (lotm !is null) { | |
2940 lptm = lotm.otmTextMetrics; | |
2941 } else { | |
2942 lptm = TEXTMETRIC.init; | |
2943 OS.GetTextMetrics(hdc, &lptm); | |
2944 } | |
2945 run.ascent = lptm.tmAscent; | |
2946 run.descent = lptm.tmDescent; | |
2947 run.leading = lptm.tmInternalLeading; | |
2948 } | |
2949 if (lotm !is null) { | |
2950 run.underlinePos = lotm.otmsUnderscorePosition; | |
2951 run.underlineThickness = Math.max(1, lotm.otmsUnderscoreSize); | |
2952 run.strikeoutPos = lotm.otmsStrikeoutPosition; | |
2953 run.strikeoutThickness = Math.max(1, lotm.otmsStrikeoutSize); | |
2954 } else { | |
2955 run.underlinePos = 1; | |
2956 run.underlineThickness = 1; | |
2957 run.strikeoutPos = run.ascent / 2; | |
2958 run.strikeoutThickness = 1; | |
2959 } | |
2960 run.ascent += style.rise; | |
2961 run.descent -= style.rise; | |
2484 } else { | 2962 } else { |
2485 run.width = abc.abcA + abc.abcB + abc.abcC; | |
2486 TEXTMETRIC lptm; | 2963 TEXTMETRIC lptm; |
2487 OS.GetTextMetrics(hdc, &lptm); | 2964 OS.GetTextMetrics(hdc, &lptm); |
2488 run.ascent = lptm.tmAscent; | 2965 run.ascent = lptm.tmAscent; |
2489 run.descent = lptm.tmDescent; | 2966 run.descent = lptm.tmDescent; |
2490 run.leading = lptm.tmInternalLeading; | 2967 run.leading = lptm.tmInternalLeading; |
2491 } | |
2492 if (run.style !is null) { | |
2493 run.ascent += run.style.rise; | |
2494 run.descent -= +run.style.rise; | |
2495 } | 2968 } |
2496 } | 2969 } |
2497 | 2970 |
2498 int validadeOffset(int offset, int step) { | 2971 int validadeOffset(int offset, int step) { |
2499 offset += step; | 2972 offset += step; |