Mercurial > projects > dwt-win
diff 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 |
line wrap: on
line diff
--- a/dwt/graphics/TextLayout.d Mon May 05 00:12:38 2008 +0200 +++ b/dwt/graphics/TextLayout.d Sat May 17 17:34:28 2008 +0200 @@ -16,6 +16,7 @@ import dwt.DWTException; import dwt.internal.Compatibility; import dwt.internal.gdip.Gdip; + import dwt.internal.win32.OS; import dwt.graphics.Color; @@ -51,6 +52,8 @@ * @since 3.0 */ public final class TextLayout : Resource { + alias Resource.init_ init_; + Font font; String text, segmentsText; int lineSpacing; @@ -63,6 +66,7 @@ int[] tabs; int[] segments; StyleItem[] styles; + int stylesCount; StyleItem[] allRuns; StyleItem[][] runs; @@ -89,6 +93,11 @@ } } + /* IME has a copy of these constants */ + static const int UNDERLINE_IME_DOT = 1 << 16; + static const int UNDERLINE_IME_DASH = 2 << 16; + static const int UNDERLINE_IME_THICK = 3 << 16; + class StyleItem { TextStyle style; int start, length; @@ -112,6 +121,8 @@ int descent; int leading; int x; + int underlinePos, underlineThickness; + int strikeoutPos, strikeoutThickness; /* Justify info (malloc during computeRuns) */ int* justify; @@ -191,22 +202,21 @@ */ public this (Device device) { static_this(); - if (device is null) device = Device.getDevice(); - if (device is null) DWT.error(DWT.ERROR_NULL_ARGUMENT); - this.device = device; + super(device); wrapWidth = ascent = descent = -1; lineSpacing = 0; orientation = DWT.LEFT_TO_RIGHT; styles = new StyleItem[2]; styles[0] = new StyleItem(); styles[1] = new StyleItem(); + stylesCount = 2; text = ""; //$NON-NLS-1$ void* ppv; OS.OleInitialize(null); if (OS.CoCreateInstance(CLSID_CMultiLanguage.ptr, null, OS.CLSCTX_INPROC_SERVER, IID_IMLangFontLink2.ptr, cast(void*)&ppv) is OS.S_OK) { mLangFontLink2 = ppv; } - if (device.tracking) device.new_Object(this); + init_(); } void breakRun(StyleItem run) { @@ -349,9 +359,15 @@ if (start is 0 && i !is lineStart && !run.tab) { run = allRuns[--i]; } else if (start <= 0 && i is lineStart) { - i = firstIndice; - run = allRuns[i]; - start = Math.max(1, firstStart); + if (lineWidth is wrapWidth && firstIndice > 0) { + i = firstIndice - 1; + run = allRuns[i]; + start = run.length; + } else { + i = firstIndice; + run = allRuns[i]; + start = Math.max(1, firstStart); + } } breakRun(run); while (start < run.length) { @@ -471,12 +487,7 @@ if (gc is null) device.internal_dispose_GC(hDC, null); } -/** - * Disposes of the operating system resources associated with - * the text layout. Applications must dispose of all allocated text layouts. - */ -override public void dispose () { - if (device is null) return; +void destroy () { freeRuns(); font = null; text = null; @@ -493,8 +504,6 @@ mLangFontLink2 = null; } OS.OleUninitialize(); - if (device.tracking) device.dispose_Object(this); - device = null; } /** @@ -595,7 +604,7 @@ Gdip.Matrix_delete(identity_); if (!Gdip.Matrix_IsIdentity(matrix)) { lpXform = new float[6]; - Gdip.Matrix_GetElements(matrix, lpXform); + Gdip.Matrix_GetElements(matrix, lpXform.ptr); } Gdip.Matrix_delete(matrix); if ((data.style & DWT.MIRRORED) !is 0 && lpXform !is null) { @@ -642,26 +651,26 @@ selectionEnd = translateOffset(selectionEnd); } RECT rect; - void* selBrush; - void* selPen; - void* selBrushFg; + Gdip.Brush selBrush; + Gdip.Pen selPen; + Gdip.Brush selBrushFg; if (hasSelection || (flags & DWT.LAST_LINE_SELECTION) !is 0) { if (gdip) { auto bg = selectionBackground.handle; auto argb = ((alpha & 0xFF) << 24) | ((bg >> 16) & 0xFF) | (bg & 0xFF00) | ((bg & 0xFF) << 16); auto color = Gdip.Color_new(argb); - selBrush = Gdip.SolidBrush_new(color); + selBrush = cast(Gdip.Brush)Gdip.SolidBrush_new(color); Gdip.Color_delete(color); auto fg = selectionForeground.handle; argb = ((alpha & 0xFF) << 24) | ((fg >> 16) & 0xFF) | (fg & 0xFF00) | ((fg & 0xFF) << 16); color = Gdip.Color_new(argb); - selBrushFg = Gdip.SolidBrush_new(color); - selPen = Gdip.Pen_new( cast(Gdip.Brush)selBrushFg, 1); + selBrushFg = cast(Gdip.Brush)Gdip.SolidBrush_new(color); + selPen = cast(Gdip.Pen) Gdip.Pen_new( cast(Gdip.Brush)selBrushFg, 1); Gdip.Color_delete(color); } else { - selBrush = OS.CreateSolidBrush(selectionBackground.handle); - selPen = OS.CreatePen(OS.PS_SOLID, 1, selectionForeground.handle); + selBrush = cast(Gdip.Brush)OS.CreateSolidBrush(selectionBackground.handle); + selPen = cast(Gdip.Pen)OS.CreatePen(OS.PS_SOLID, 1, selectionForeground.handle); } } int offset = (orientation & DWT.RIGHT_TO_LEFT) !is 0 ? -1 : 0; @@ -670,7 +679,7 @@ int drawX = x + getLineIndent(line); int drawY = y + lineY[line]; StyleItem[] lineRuns = runs[line]; - int lineHeight = lineY[line+1] - lineY[line]; + int lineHeight = lineY[line+1] - lineY[line] - lineSpacing; if (flags !is 0 && (hasSelection || (flags & DWT.LAST_LINE_SELECTION) !is 0)) { bool extents = false; if (line is runs.length - 1 && (flags & DWT.LAST_LINE_SELECTION) !is 0) { @@ -691,21 +700,23 @@ if ((flags & DWT.FULL_SELECTION) !is 0) { width = OS.IsWin95 ? 0x7FFF : 0x6FFFFFF; } else { - width = (lineHeight - lineSpacing) / 3; + width = lineHeight / 3; } if (gdip) { - Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)selBrush, drawX + lineWidth[line], drawY, width, lineHeight - lineSpacing); + Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)selBrush, drawX + lineWidth[line], drawY, width, lineHeight); } else { OS.SelectObject(hdc, selBrush); - OS.PatBlt(hdc, drawX + lineWidth[line], drawY, width, lineHeight - lineSpacing, OS.PATCOPY); + OS.PatBlt(hdc, drawX + lineWidth[line], drawY, width, lineHeight, OS.PATCOPY); } } } if (drawX > clip.x + clip.width) continue; if (drawX + lineWidth[line] < clip.x) continue; int baseline = Math.max(0, this.ascent); + int lineUnderlinePos = 0; for (int i = 0; i < lineRuns.length; i++) { baseline = Math.max(baseline, lineRuns[i].ascent); + lineUnderlinePos = Math.min(lineUnderlinePos, lineRuns[i].underlinePos); } int alignmentX = drawX; for (int i = 0; i < lineRuns.length; i++) { @@ -718,26 +729,25 @@ bool fullSelection = hasSelection && selectionStart <= run.start && selectionEnd >= end; if (fullSelection) { if (gdip) { - Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)selBrush, drawX, drawY, run.width, lineHeight - lineSpacing); + Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)selBrush, drawX, drawY, run.width, lineHeight); } else { OS.SelectObject(hdc, selBrush); - OS.PatBlt(hdc, drawX, drawY, run.width, lineHeight - lineSpacing, OS.PATCOPY); + OS.PatBlt(hdc, drawX, drawY, run.width, lineHeight, OS.PATCOPY); } } else { if (run.style !is null && run.style.background !is null) { auto bg = run.style.background.handle; - int drawRunY = drawY + (baseline - run.ascent); if (gdip) { int argb = ((alpha & 0xFF) << 24) | ((bg >> 16) & 0xFF) | (bg & 0xFF00) | ((bg & 0xFF) << 16); auto color = Gdip.Color_new(argb); auto brush = Gdip.SolidBrush_new(color); - Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)brush, drawX, drawRunY, run.width, run.ascent + run.descent); + Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)brush, drawX, drawY, run.width, lineHeight); Gdip.Color_delete(color); Gdip.SolidBrush_delete(brush); } else { auto hBrush = OS.CreateSolidBrush (bg); auto oldBrush = OS.SelectObject(hdc, hBrush); - OS.PatBlt(hdc, drawX, drawRunY, run.width, run.ascent + run.descent, OS.PATCOPY); + OS.PatBlt(hdc, drawX, drawY, run.width, lineHeight, OS.PATCOPY); OS.SelectObject(hdc, oldBrush); OS.DeleteObject(hBrush); } @@ -757,8 +767,13 @@ OS.ScriptCPtoX(selEnd, true, cChars, gGlyphs, run.clusters, run.visAttrs, advances, &run.analysis, &piX); runX = (orientation & DWT.RIGHT_TO_LEFT) !is 0 ? run.width - piX : piX; rect.right = drawX + runX; - rect.bottom = drawY + lineHeight - lineSpacing; + rect.bottom = drawY + lineHeight; if (gdip) { + if (rect.left > rect.right) { + int tmp = rect.left; + rect.left = rect.right; + rect.right = tmp; + } Gdip.Graphics_FillRectangle(gdipGraphics, cast(Gdip.Brush)selBrush, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); } else { OS.SelectObject(hdc, selBrush); @@ -770,6 +785,7 @@ } drawX += run.width; } + RECT* borderClip = null; drawX = alignmentX; for (int i = 0; i < lineRuns.length; i++) { StyleItem run = lineRuns[i]; @@ -819,7 +835,7 @@ if ((type & OS.PT_CLOSEFIGURE) !is 0) newType |= Gdip.PathPointTypeCloseSubpath; types[typeIndex] = cast(byte)newType; } - auto path = Gdip.GraphicsPath_new(cast(Gdip.Point[])points, types, count, Gdip.FillModeAlternate); + auto path = Gdip.GraphicsPath_new(cast(Gdip.Point*)points.ptr, types.ptr, count, Gdip.FillModeAlternate); if (path is null) DWT.error(DWT.ERROR_NO_HANDLES); auto brush = foregroundBrush; if (fullSelection) { @@ -865,46 +881,31 @@ Gdip.Graphics_Restore(gdipGraphics, gstate2); } Gdip.Graphics_SetSmoothingMode(gdipGraphics, antialias); - if (run.style !is null && (run.style.underline || run.style.strikeout)) { - auto newPen = hasSelection ? cast(Gdip.Pen)selPen : Gdip.Pen_new(brush, 1); - Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeNone); - if (run.style.underline) { - int underlineY = drawY + baseline + 1 - run.style.rise; - Gdip.Graphics_DrawLine(gdipGraphics, newPen, drawX, underlineY, drawX + run.width, underlineY); - } - if (run.style.strikeout) { - int strikeoutY = drawRunY + run.leading + (run.ascent - run.style.rise) / 2; - Gdip.Graphics_DrawLine(gdipGraphics, newPen, drawX, strikeoutY, drawX + run.width, strikeoutY); - } - if (cast(void*)newPen !is selPen) Gdip.Pen_delete(newPen); - Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf); - } + drawLines(gdip, gdipGraphics, x, drawY + baseline, lineUnderlinePos, drawY + lineHeight, lineRuns, i, brush, null, alpha); if (partialSelection) { Gdip.Graphics_Restore(gdipGraphics, gstate); gstate = Gdip.Graphics_Save(gdipGraphics); Gdip.Graphics_SetClip(gdipGraphics, &gdipRect, Gdip.CombineModeIntersect); Gdip.Graphics_SetSmoothingMode(gdipGraphics, textAntialias); - Gdip.Graphics_FillPath(gdipGraphics, cast(Gdip.Brush)selBrushFg, path); + if ((data.style & DWT.MIRRORED) !is 0) { + gstate2 = Gdip.Graphics_Save(gdipGraphics); + Gdip.Graphics_ScaleTransform(gdipGraphics, -1, 1, Gdip.MatrixOrderPrepend); + Gdip.Graphics_TranslateTransform(gdipGraphics, -2 * drawX - run.width, 0, Gdip.MatrixOrderPrepend); + } + Gdip.Graphics_FillPath(gdipGraphics, selBrushFg, path); + if ((data.style & DWT.MIRRORED) !is 0) { + Gdip.Graphics_Restore(gdipGraphics, gstate2); + } Gdip.Graphics_SetSmoothingMode(gdipGraphics, antialias); - if (run.style !is null && (run.style.underline || run.style.strikeout)) { - Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeNone); - if (run.style.underline) { - int underlineY = drawY + baseline + 1 - run.style.rise; - Gdip.Graphics_DrawLine(gdipGraphics, cast(Gdip.Pen)selPen, rect.left, underlineY, rect.right, underlineY); - } - if (run.style.strikeout) { - int strikeoutY = drawRunY + run.leading + (run.ascent - run.style.rise) / 2; - Gdip.Graphics_DrawLine(gdipGraphics, cast(Gdip.Pen)selPen, rect.left, strikeoutY, rect.right, strikeoutY); - } - Gdip.Graphics_SetPixelOffsetMode(gdipGraphics, Gdip.PixelOffsetModeHalf); - } + drawLines(gdip, gdipGraphics, x, drawY + baseline, lineUnderlinePos, drawY + lineHeight, lineRuns, i, selBrushFg, &rect, alpha); Gdip.Graphics_Restore(gdipGraphics, gstate); } + borderClip = drawBorder(gdip, gdipGraphics, x, drawY, lineHeight, foregroundBrush, selBrushFg, fullSelection, borderClip, partialSelection ? &rect : null, alpha, lineRuns, i, selectionStart, selectionEnd); Gdip.GraphicsPath_delete(path); if ( brush !is cast(Gdip.Brush)selBrushFg && brush !is cast(Gdip.Brush)foregroundBrush) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)brush); } else { - int fg = foreground; + auto fg = foreground; if (fullSelection) { fg = selectionForeground.handle; } else { @@ -912,40 +913,14 @@ } OS.SetTextColor(hdc, fg); OS.ScriptTextOut(hdc, run.psc, drawX + offset, drawRunY, 0, null, &run.analysis , null, 0, run.glyphs, run.glyphCount, run.advances, run.justify, run.goffsets); - if (run.style !is null && (run.style.underline || run.style.strikeout)) { - auto newPen = hasSelection && fg is selectionForeground.handle ? cast(HPEN)selPen : OS.CreatePen(OS.PS_SOLID, 1, fg); - auto oldPen = OS.SelectObject(hdc, newPen); - if (run.style.underline) { - int underlineY = drawY + baseline + 1 - run.style.rise; - OS.MoveToEx(hdc, drawX, underlineY, null); - OS.LineTo(hdc, drawX + run.width, underlineY); - } - if (run.style.strikeout) { - int strikeoutY = drawRunY + run.leading + (run.ascent - run.style.rise) / 2; - OS.MoveToEx(hdc, drawX, strikeoutY, null); - OS.LineTo(hdc, drawX + run.width, strikeoutY); - } - OS.SelectObject(hdc, oldPen); - if (!hasSelection || fg !is selectionForeground.handle) OS.DeleteObject(newPen); - } + drawLines(gdip, hdc, x, drawY + baseline, lineUnderlinePos, drawY + lineHeight, lineRuns, i, cast(void*)fg, null, alpha); if (partialSelection && fg !is selectionForeground.handle) { OS.SetTextColor(hdc, selectionForeground.handle); 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); - if (run.style !is null && (run.style.underline || run.style.strikeout)) { - auto oldPen = OS.SelectObject(hdc, selPen); - if (run.style.underline) { - int underlineY = drawY + baseline + 1 - run.style.rise; - OS.MoveToEx(hdc, rect.left, underlineY, null); - OS.LineTo(hdc, rect.right, underlineY); - } - if (run.style.strikeout) { - int strikeoutY = drawRunY + run.leading + (run.ascent - run.style.rise) / 2; - OS.MoveToEx(hdc, rect.left, strikeoutY, null); - OS.LineTo(hdc, rect.right, strikeoutY); - } - OS.SelectObject(hdc, oldPen); - } + drawLines(gdip, hdc, x, drawY + baseline, lineUnderlinePos, drawY + lineHeight, lineRuns, i, cast(void*)selectionForeground.handle, &rect, alpha); } + int selForeground = selectionForeground !is null ? selectionForeground.handle : 0; + borderClip = drawBorder(gdip, hdc, x, drawY, lineHeight, cast(void*)foreground, cast(void*)selForeground, fullSelection, borderClip, partialSelection ? &rect : null, alpha, lineRuns, i, selectionStart, selectionEnd); } } } @@ -955,7 +930,7 @@ if (gdip) { if (selBrush !is null) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)selBrush); if (selBrushFg !is null) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)selBrushFg); - if (selPen !is null) Gdip.Pen_delete(cast(Gdip.Pen)selPen); + if (selPen !is null) Gdip.Pen_delete(selPen); } else { OS.RestoreDC(hdc, state); if (gdipGraphics !is null) Gdip.Graphics_ReleaseHDC(gdipGraphics, hdc); @@ -964,6 +939,355 @@ } } +void drawLines(bool advance, void* graphics, int x, int lineBaseline, int lineUnderlinePos, int lineBottom, StyleItem[] line, int index, void* color, RECT* clipRect, int alpha) { + StyleItem run = line[index]; + TextStyle style = run.style; + if (style is null) return; + if (!style.underline && !style.strikeout) return; + int runX = x + run.x; + int underlineY = lineBaseline - lineUnderlinePos; + int strikeoutY = lineBaseline - run.strikeoutPos; + if (advance) { + Gdip.Graphics_SetPixelOffsetMode(cast(Gdip.Graphics)graphics, Gdip.PixelOffsetModeNone); + auto brush = color; + if (style.underline) { + if (style.underlineColor !is null) { + int fg = style.underlineColor.handle; + int argb = ((alpha & 0xFF) << 24) | ((fg >> 16) & 0xFF) | (fg & 0xFF00) | ((fg & 0xFF) << 16); + auto gdiColor = Gdip.Color_new(argb); + brush = Gdip.SolidBrush_new(gdiColor); + Gdip.Color_delete(gdiColor); + } + switch (style.underlineStyle) { + case DWT.UNDERLINE_SQUIGGLE: + case DWT.UNDERLINE_ERROR: { + int squigglyThickness = 1; + int squigglyHeight = 2 * squigglyThickness; + int squigglyY = Math.min(underlineY - squigglyHeight / 2, lineBottom - squigglyHeight - 1); + int squigglyX = runX; + for (int i = index; i > 0 && style.isAdherentUnderline(line[i - 1].style); i--) { + squigglyX = x + line[i - 1].x; + } + int gstate = 0; + if (clipRect is null) { + gstate = Gdip.Graphics_Save(cast(Gdip.Graphics)graphics); + Gdip.Rect gdipRect; + gdipRect.X = runX; + gdipRect.Y = squigglyY; + gdipRect.Width = run.width + 1; + gdipRect.Height = squigglyY + squigglyHeight + 1; + Gdip.Graphics_SetClip(cast(Gdip.Graphics)graphics, &gdipRect, Gdip.CombineModeIntersect); + } + int[] points = computePolyline(squigglyX, squigglyY, runX + run.width, squigglyY + squigglyHeight); + auto pen = Gdip.Pen_new(cast(Gdip.Brush)brush, squigglyThickness); + Gdip.Graphics_DrawLines(cast(Gdip.Graphics)graphics, pen, cast(Gdip.Point*)points.ptr, points.length / 2); + Gdip.Pen_delete(pen); + if (gstate !is 0) Gdip.Graphics_Restore(cast(Gdip.Graphics)graphics, gstate); + break; + } + case DWT.UNDERLINE_SINGLE: + Gdip.Graphics_FillRectangle(cast(Gdip.Graphics)graphics, cast(Gdip.Brush)brush, runX, underlineY, run.width, run.underlineThickness); + break; + case DWT.UNDERLINE_DOUBLE: + Gdip.Graphics_FillRectangle(cast(Gdip.Graphics)graphics, cast(Gdip.Brush)brush, runX, underlineY, run.width, run.underlineThickness); + Gdip.Graphics_FillRectangle(cast(Gdip.Graphics)graphics, cast(Gdip.Brush)brush, runX, underlineY + run.underlineThickness * 2, run.width, run.underlineThickness); + break; + case UNDERLINE_IME_THICK: + Gdip.Graphics_FillRectangle(cast(Gdip.Graphics)graphics, cast(Gdip.Brush)brush, runX - run.underlineThickness, underlineY, run.width, run.underlineThickness * 2); + break; + case UNDERLINE_IME_DOT: + case UNDERLINE_IME_DASH: { + auto pen = Gdip.Pen_new(cast(Gdip.Brush)brush, 1); + int dashStyle = style.underlineStyle is UNDERLINE_IME_DOT ? Gdip.DashStyleDot : Gdip.DashStyleDash; + Gdip.Pen_SetDashStyle(pen, dashStyle); + Gdip.Graphics_DrawLine(cast(Gdip.Graphics)graphics, pen, runX, underlineY, runX + run.width, underlineY); + Gdip.Pen_delete(pen); + break; + } + } + if (brush !is color) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)brush); + } + if (style.strikeout) { + if (style.strikeoutColor !is null) { + int fg = style.strikeoutColor.handle; + int argb = ((alpha & 0xFF) << 24) | ((fg >> 16) & 0xFF) | (fg & 0xFF00) | ((fg & 0xFF) << 16); + auto gdiColor = Gdip.Color_new(argb); + brush = Gdip.SolidBrush_new(gdiColor); + Gdip.Color_delete(gdiColor); + } + Gdip.Graphics_FillRectangle(cast(Gdip.Graphics)graphics, cast(Gdip.Brush)brush, runX, strikeoutY, run.width, run.strikeoutThickness); + if (brush !is color) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)brush); + } + Gdip.Graphics_SetPixelOffsetMode(cast(Gdip.Graphics)graphics, Gdip.PixelOffsetModeHalf); + } else { + uint colorRefUnderline = cast(uint)color; + uint colorRefStrikeout = cast(uint)color; + int /*long*/ brushUnderline = 0; + int /*long*/ brushStrikeout = 0; + RECT rect; + if (style.underline) { + if (style.underlineColor !is null) { + colorRefUnderline = style.underlineColor.handle; + } + switch (style.underlineStyle) { + case DWT.UNDERLINE_SQUIGGLE: + case DWT.UNDERLINE_ERROR: { + int squigglyThickness = 1; + int squigglyHeight = 2 * squigglyThickness; + int squigglyY = Math.min(underlineY - squigglyHeight / 2, lineBottom - squigglyHeight - 1); + int squigglyX = runX; + for (int i = index; i > 0 && style.isAdherentUnderline(line[i - 1].style); i--) { + squigglyX = x + line[i - 1].x; + } + int state = OS.SaveDC(graphics); + if (clipRect !is null) { + OS.IntersectClipRect(graphics, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); + } else { + OS.IntersectClipRect(graphics, runX, squigglyY, runX + run.width + 1, squigglyY + squigglyHeight + 1); + } + int[] points = computePolyline(squigglyX, squigglyY, runX + run.width, squigglyY + squigglyHeight); + auto pen = OS.CreatePen(OS.PS_SOLID, squigglyThickness, colorRefUnderline); + auto oldPen = OS.SelectObject(graphics, pen); + OS.Polyline(graphics, cast(POINT*)points.ptr, points.length / 2); + int length_ = points.length; + if (length_ >= 2 && squigglyThickness <= 1) { + OS.SetPixel (graphics, points[length_ - 2], points[length_ - 1], colorRefUnderline); + } + OS.RestoreDC(graphics, state); + OS.SelectObject(graphics, oldPen); + OS.DeleteObject(pen); + break; + } + case DWT.UNDERLINE_SINGLE: + brushUnderline = cast(uint) OS.CreateSolidBrush(colorRefUnderline); + OS.SetRect(&rect, runX, underlineY, runX + run.width, underlineY + run.underlineThickness); + if (clipRect !is null) { + rect.left = Math.max(rect.left, clipRect.left); + rect.right = Math.min(rect.right, clipRect.right); + } + OS.FillRect(graphics, &rect, cast(void*)brushUnderline); + break; + case DWT.UNDERLINE_DOUBLE: + brushUnderline = cast(uint)OS.CreateSolidBrush(colorRefUnderline); + OS.SetRect(&rect, runX, underlineY, runX + run.width, underlineY + run.underlineThickness); + if (clipRect !is null) { + rect.left = Math.max(rect.left, clipRect.left); + rect.right = Math.min(rect.right, clipRect.right); + } + OS.FillRect(graphics, &rect, cast(void*)brushUnderline); + OS.SetRect(&rect, runX, underlineY + run.underlineThickness * 2, runX + run.width, underlineY + run.underlineThickness * 3); + if (clipRect !is null) { + rect.left = Math.max(rect.left, clipRect.left); + rect.right = Math.min(rect.right, clipRect.right); + } + OS.FillRect(graphics, &rect, cast(void*)brushUnderline); + break; + case UNDERLINE_IME_THICK: + brushUnderline = cast(uint)OS.CreateSolidBrush(colorRefUnderline); + OS.SetRect(&rect, runX, underlineY - run.underlineThickness, runX + run.width, underlineY + run.underlineThickness); + if (clipRect !is null) { + rect.left = Math.max(rect.left, clipRect.left); + rect.right = Math.min(rect.right, clipRect.right); + } + OS.FillRect(graphics, &rect, cast(void*)brushUnderline); + break; + case UNDERLINE_IME_DASH: + case UNDERLINE_IME_DOT: { + underlineY = lineBaseline + run.descent; + int penStyle = style.underlineStyle is UNDERLINE_IME_DASH ? OS.PS_DASH : OS.PS_DOT; + auto pen = OS.CreatePen(penStyle, 1, colorRefUnderline); + auto oldPen = OS.SelectObject(graphics, pen); + OS.SetRect(&rect, runX, underlineY, runX + run.width, underlineY + run.underlineThickness); + if (clipRect !is null) { + rect.left = Math.max(rect.left, clipRect.left); + rect.right = Math.min(rect.right, clipRect.right); + } + OS.MoveToEx(graphics, rect.left, rect.top, null); + OS.LineTo(graphics, rect.right, rect.top); + OS.SelectObject(graphics, oldPen); + OS.DeleteObject(pen); + break; + } + } + } + if (style.strikeout) { + if (style.strikeoutColor !is null) { + colorRefStrikeout = style.strikeoutColor.handle; + } + if (brushUnderline !is 0 && colorRefStrikeout is colorRefUnderline) { + brushStrikeout = brushUnderline; + } else { + brushStrikeout = cast(int) OS.CreateSolidBrush(colorRefStrikeout); + } + OS.SetRect(&rect, runX, strikeoutY, runX + run.width, strikeoutY + run.strikeoutThickness); + if (clipRect !is null) { + rect.left = Math.max(rect.left, clipRect.left); + rect.right = Math.min(rect.right, clipRect.right); + } + OS.FillRect(graphics, &rect, cast(void*)brushStrikeout); + } + if (brushUnderline !is 0) OS.DeleteObject(cast(void*)brushUnderline); + if (brushStrikeout !is 0 && brushStrikeout !is brushUnderline) OS.DeleteObject(cast(void*)brushStrikeout); + } +} + +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) { + StyleItem run = line[index]; + TextStyle style = run.style; + if (style is null) return null; + if (style.borderStyle is DWT.NONE) return null; + if (rect !is null) { + if (clipRect is null) { + clipRect = new RECT (); + OS.SetRect(clipRect, -1, rect.top, -1, rect.bottom); + } + bool isRTL = (orientation & DWT.RIGHT_TO_LEFT) !is 0; + if (run.start <= selectionStart && selectionStart <= run.start + run.length) { + if (run.analysis.fRTL ^ isRTL) { + clipRect.right = rect.left; + } else { + clipRect.left = rect.left; + } + } + if (run.start <= selectionEnd && selectionEnd <= run.start + run.length) { + if (run.analysis.fRTL ^ isRTL) { + clipRect.left = rect.right; + } else { + clipRect.right = rect.right; + } + } + } + if (index + 1 >= line.length || !style.isAdherentBorder(line[index + 1].style)) { + int left = run.x; + for (int i = index; i > 0 && style.isAdherentBorder(line[i - 1].style); i--) { + left = line[i - 1].x; + } + if (advance) { + auto brush = color; + int customColor = -1; + if (style.borderColor !is null) { + customColor = style.borderColor.handle; + } else { + if (style.foreground !is null) { + customColor = style.foreground.handle; + } + if (fullSelection && clipRect is null) { + customColor = -1; + brush = selectionColor; + } + } + if (customColor !is -1) { + int argb = ((alpha & 0xFF) << 24) | ((customColor >> 16) & 0xFF) | (customColor & 0xFF00) | ((customColor & 0xFF) << 16); + auto gdiColor = Gdip.Color_new(argb); + brush = Gdip.SolidBrush_new(gdiColor); + Gdip.Color_delete(gdiColor); + } + int lineWidth = 1; + int lineStyle = Gdip.DashStyleSolid; + switch (style.borderStyle) { + case DWT.BORDER_SOLID: break; + case DWT.BORDER_DASH: lineStyle = Gdip.DashStyleDash; break; + case DWT.BORDER_DOT: lineStyle = Gdip.DashStyleDot; break; + } + auto pen = Gdip.Pen_new(cast(Gdip.Brush)brush, lineWidth); + Gdip.Pen_SetDashStyle(pen, lineStyle); + float gdipXOffset = 0.5f, gdipYOffset = 0.5f; + Gdip.Graphics_TranslateTransform(cast(Gdip.Graphics)graphics, gdipXOffset, gdipYOffset, Gdip.MatrixOrderPrepend); + if (style.borderColor is null && clipRect !is null) { + int gstate = Gdip.Graphics_Save(cast(Gdip.Graphics)graphics); + if (clipRect.left is -1) clipRect.left = 0; + if (clipRect.right is -1) clipRect.right = 0x7ffff; + Gdip.Rect gdipRect; + gdipRect.X = clipRect.left; + gdipRect.Y = clipRect.top; + gdipRect.Width = clipRect.right - clipRect.left; + gdipRect.Height = clipRect.bottom - clipRect.top; + Gdip.Graphics_SetClip(cast(Gdip.Graphics)graphics, &gdipRect, Gdip.CombineModeExclude); + Gdip.Graphics_DrawRectangle(cast(Gdip.Graphics)graphics, pen, x + left, y, run.x + run.width - left - 1, lineHeight - 1); + Gdip.Graphics_Restore(cast(Gdip.Graphics)graphics, gstate); + gstate = Gdip.Graphics_Save(cast(Gdip.Graphics)graphics); + Gdip.Graphics_SetClip(cast(Gdip.Graphics)graphics, &gdipRect, Gdip.CombineModeIntersect); + auto selPen = Gdip.Pen_new(cast(Gdip.Brush)selectionColor, lineWidth); + Gdip.Pen_SetDashStyle(pen, lineStyle); + Gdip.Graphics_DrawRectangle(cast(Gdip.Graphics)graphics, selPen, x + left, y, run.x + run.width - left - 1, lineHeight - 1); + Gdip.Pen_delete(selPen); + Gdip.Graphics_Restore(cast(Gdip.Graphics)graphics, gstate); + } else { + Gdip.Graphics_DrawRectangle(cast(Gdip.Graphics)graphics, pen, x + left, y, run.x + run.width - left - 1, lineHeight - 1); + } + Gdip.Graphics_TranslateTransform(cast(Gdip.Graphics)graphics, -gdipXOffset, -gdipYOffset, Gdip.MatrixOrderPrepend); + Gdip.Pen_delete(pen); + if (customColor !is -1) Gdip.SolidBrush_delete(cast(Gdip.SolidBrush)brush); + } else { + if (style.borderColor !is null) { + color = cast(void*)style.borderColor.handle; + } else { + if (style.foreground !is null) { + color = cast(void*)style.foreground.handle; + } + if (fullSelection && clipRect is null) { + color = selectionColor; + } + } + int lineWidth = 1; + int lineStyle = OS.PS_SOLID; + switch (style.borderStyle) { + case DWT.BORDER_SOLID: break; + case DWT.BORDER_DASH: lineStyle = OS.PS_DASH; break; + case DWT.BORDER_DOT: lineStyle = OS.PS_DOT; break; + } + LOGBRUSH logBrush; + logBrush.lbStyle = OS.BS_SOLID; + logBrush.lbColor = cast(uint)color; + auto newPen = OS.ExtCreatePen(lineStyle | OS.PS_GEOMETRIC, Math.max(1, lineWidth), &logBrush, 0, null); + auto oldPen = OS.SelectObject(graphics, newPen); + auto oldBrush = OS.SelectObject(graphics, OS.GetStockObject(OS.NULL_BRUSH)); + OS.Rectangle(graphics, x + left, y, x + run.x + run.width, y + lineHeight); + if (style.borderColor is null && clipRect !is null && color !is selectionColor) { + int state = OS.SaveDC(graphics); + if (clipRect.left is -1) clipRect.left = 0; + if (clipRect.right is -1) clipRect.right = 0x7ffff; + OS.IntersectClipRect(graphics, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); + logBrush.lbColor = cast(uint)selectionColor; + auto selPen = OS.ExtCreatePen (lineStyle | OS.PS_GEOMETRIC, Math.max(1, lineWidth), &logBrush, 0, null); + OS.SelectObject(graphics, selPen); + OS.Rectangle(graphics, x + left, y, x + run.x + run.width, y + lineHeight); + OS.RestoreDC(graphics, state); + OS.SelectObject(graphics, newPen); + OS.DeleteObject(selPen); + } + OS.SelectObject(graphics, oldBrush); + OS.SelectObject(graphics, oldPen); + OS.DeleteObject(newPen); + } + return null; + } + return clipRect; +} + +int[] computePolyline(int left, int top, int right, int bottom) { + int height = bottom - top; // can be any number + int width = 2 * height; // must be even + int peaks = Compatibility.ceil(right - left, width); + if (peaks is 0 && right - left > 2) { + peaks = 1; + } + int length_ = ((2 * peaks) + 1) * 2; + if (length_ < 0) return new int[0]; + + int[] coordinates = new int[length_]; + for (int i = 0; i < peaks; i++) { + int index = 4 * i; + coordinates[index] = left + (width * i); + coordinates[index+1] = bottom; + coordinates[index+2] = coordinates[index] + width / 2; + coordinates[index+3] = top; + } + coordinates[length_-2] = left + (width * peaks); + coordinates[length_-1] = bottom; + return coordinates; +} + void freeRuns () { if (allRuns is null) return; for (int i=0; i<allRuns.length; i++) { @@ -1011,13 +1335,18 @@ } /** - * Returns the bounds of the receiver. + * Returns the bounds of the receiver. The width returned is either the + * width of the longest line or the width set using {@link TextLayout#setWidth(int)}. + * To obtain the text bounds of a line use {@link TextLayout#getLineBounds(int)}. * * @return the bounds of the receiver * * @exception DWTException <ul> * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> * </ul> + * + * @see #setWidth(int) + * @see #getLineBounds(int) */ public Rectangle getBounds () { checkLayout(); @@ -1050,11 +1379,11 @@ public Rectangle getBounds (int start, int end) { checkLayout(); computeRuns(null); - int length = text.length; - if (length is 0) return new Rectangle(0, 0, 0, 0); + int length_ = text.length; + if (length_ is 0) return new Rectangle(0, 0, 0, 0); if (start > end) return new Rectangle(0, 0, 0, 0); - start = Math.min(Math.max(0, start), length - 1); - end = Math.min(Math.max(0, end), length - 1); + start = Math.min(Math.max(0, start), length_ - 1); + end = Math.min(Math.max(0, end), length_ - 1); start = translateOffset(start); end = translateOffset(end); int left = 0x7fffffff, right = 0; @@ -1187,7 +1516,7 @@ if (this.font !is null) { return this.font.handle; } - return device.systemFont; + return device.systemFont.handle; } /** @@ -1207,8 +1536,8 @@ public int getLevel (int offset) { checkLayout(); computeRuns(null); - int length = text.length; - if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); + int length_ = text.length; + if (!(0 <= offset && offset <= length_)) DWT.error(DWT.ERROR_INVALID_RANGE); offset = translateOffset(offset); for (int i=1; i<allRuns.length; i++) { if (allRuns[i].start > offset) { @@ -1306,8 +1635,8 @@ public int getLineIndex (int offset) { checkLayout(); computeRuns(null); - int length = text.length; - if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); + int length_ = text.length; + if (!(0 <= offset && offset <= length_)) DWT.error(DWT.ERROR_INVALID_RANGE); offset = translateOffset(offset); for (int line=0; line<runs.length; line++) { if (lineOffset[line + 1] > offset) { @@ -1337,7 +1666,7 @@ auto hDC = device.internal_new_GC(null); auto srcHdc = OS.CreateCompatibleDC(hDC); TEXTMETRIC lptm; - OS.SelectObject(srcHdc, font !is null ? font.handle : device.systemFont); + OS.SelectObject(srcHdc, font !is null ? font.handle : device.systemFont.handle); OS.GetTextMetrics(srcHdc, &lptm); OS.DeleteDC(srcHdc); device.internal_dispose_GC(hDC, null); @@ -1404,53 +1733,47 @@ public Point getLocation (int offset, bool trailing) { checkLayout(); computeRuns(null); - int length = text.length; - if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); - length = segmentsText.length; + int length_ = text.length; + if (!(0 <= offset && offset <= length_)) DWT.error(DWT.ERROR_INVALID_RANGE); + length_ = segmentsText.length; offset = translateOffset(offset); int line; for (line=0; line<runs.length; line++) { if (lineOffset[line + 1] > offset) break; } line = Math.min(line, runs.length - 1); - StyleItem[] lineRuns = runs[line]; - Point result = null; - if (offset is length) { - result = new Point(lineWidth[line], lineY[line]); - } else { - int width = 0; - for (int i=0; i<lineRuns.length; i++) { - StyleItem run = lineRuns[i]; - int end = run.start + run.length; - if (run.start <= offset && offset < end) { - if (run.style !is null && run.style.metrics !is null) { - GlyphMetrics metrics = run.style.metrics; - width += metrics.width * (offset - run.start + (trailing ? 1 : 0)); - result = new Point(width, lineY[line]); - } else if (run.tab) { - if (trailing || (offset is length)) width += run.width; - result = new Point(width, lineY[line]); - } else { - int runOffset = offset - run.start; - int cChars = run.length; - int gGlyphs = run.glyphCount; - int piX; - int* advances = run.justify !is null ? run.justify : run.advances; - OS.ScriptCPtoX(runOffset, trailing, cChars, gGlyphs, run.clusters, run.visAttrs, advances, &run.analysis, &piX); - if ((orientation & DWT.RIGHT_TO_LEFT) !is 0) { - result = new Point(width + (run.width - piX), lineY[line]); - } else { - result = new Point(width + piX, lineY[line]); - } - } - break; + if (offset is length_) { + return new Point(getLineIndent(line) + lineWidth[line], lineY[line]); + } + int low = -1; + int high = allRuns.length; + while (high - low > 1) { + int index = ((high + low) / 2); + StyleItem run = allRuns[index]; + if (run.start > offset) { + high = index; + } else if (run.start + run.length <= offset) { + low = index; + } else { + int width; + if (run.style !is null && run.style.metrics !is null) { + GlyphMetrics metrics = run.style.metrics; + width = metrics.width * (offset - run.start + (trailing ? 1 : 0)); + } else if (run.tab) { + width = (trailing || (offset is length_)) ? run.width : 0; + } else { + int runOffset = offset - run.start; + int cChars = run.length; + int gGlyphs = run.glyphCount; + int piX; + int* advances = run.justify !is null ? run.justify : run.advances; + OS.ScriptCPtoX(runOffset, trailing, cChars, gGlyphs, run.clusters, run.visAttrs, advances, &run.analysis, &piX); + width = (orientation & DWT.RIGHT_TO_LEFT) !is 0 ? run.width - piX : piX; } - width += run.width; + return new Point(run.x + width, lineY[line]); } } - if (result is null) result = new Point(0, 0); - result.x += getLineIndent(line); - return result; + return new Point(0, 0); } /** @@ -1479,13 +1802,13 @@ int _getOffset(int offset, int movement, bool forward) { computeRuns(null); - int length = text.length; - if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); - if (forward && offset is length) return length; + int length_ = text.length; + if (!(0 <= offset && offset <= length_)) DWT.error(DWT.ERROR_INVALID_RANGE); + if (forward && offset is length_) return length_; if (!forward && offset is 0) return 0; int step = forward ? 1 : -1; if ((movement & DWT.MOVEMENT_CHAR) !is 0) return offset + step; - length = segmentsText.length; + length_ = segmentsText.length; offset = translateOffset(offset); SCRIPT_LOGATTR* logAttr; SCRIPT_PROPERTIES* properties; @@ -1545,7 +1868,7 @@ } } i += step; - } while (0 <= i && i < allRuns.length - 1 && 0 <= offset && offset < length); + } while (0 <= i && i < allRuns.length - 1 && 0 <= offset && offset < length_); return forward ? text.length : 0; } @@ -1611,16 +1934,22 @@ if (lineY[line + 1] > y) break; } line = Math.min(line, runs.length - 1); - x -= getLineIndent(line); StyleItem[] lineRuns = runs[line]; - if (x >= lineWidth[line]) x = lineWidth[line] - 1; - if (x < 0) x = 0; - int width = 0; - for (int i = 0; i < lineRuns.length; i++) { - StyleItem run = lineRuns[i]; - if (run.lineBreak && !run.softBreak) return untranslateOffset(run.start); - if (width + run.width > x) { - int xRun = x - width; + int lineIndent = getLineIndent(line); + if (x >= lineIndent + lineWidth[line]) x = lineIndent + lineWidth[line] - 1; + if (x < lineIndent) x = lineIndent; + int low = -1; + int high = lineRuns.length; + while (high - low > 1) { + int index = ((high + low) / 2); + StyleItem run = lineRuns[index]; + if (run.x > x) { + high = index; + } else if (run.x + run.width <= x) { + low = index; + } else { + if (run.lineBreak && !run.softBreak) return untranslateOffset(run.start); + int xRun = x - run.x; if (run.style !is null && run.style.metrics !is null) { GlyphMetrics metrics = run.style.metrics; if (metrics.width > 0) { @@ -1631,7 +1960,7 @@ } } if (run.tab) { - if (trailing !is null) trailing[0] = x < (width + run.width / 2) ? 0 : 1; + if (trailing !is null) trailing[0] = x < (run.x + run.width / 2) ? 0 : 1; return untranslateOffset(run.start); } int cChars = run.length; @@ -1646,7 +1975,6 @@ if (trailing !is null) trailing[0] = piTrailing; return untranslateOffset(run.start + piCP); } - width += run.width; } if (trailing !is null) trailing[0] = 0; return untranslateOffset(lineOffset[line + 1]); @@ -1706,9 +2034,9 @@ */ public int[] getRanges () { checkLayout(); - int[] result = new int[styles.length * 2]; + int[] result = new int[stylesCount * 2]; int count = 0; - for (int i=0; i<styles.length - 1; i++) { + for (int i=0; i<stylesCount - 1; i++) { if (styles[i].style !is null) { result[count++] = styles[i].start; result[count++] = styles[i + 1].start - 1; @@ -1740,17 +2068,17 @@ if (segments is null) return text; int nSegments = segments.length; if (nSegments <= 1) return text; - int length = text.length; - if (length is 0) return text; + int length_ = text.length; + if (length_ is 0) return text; if (nSegments is 2) { - if (segments[0] is 0 && segments[1] is length) return text; + if (segments[0] is 0 && segments[1] is length_) return text; } - char[] oldChars = new char[length]; - text.getChars(0, length, oldChars, 0); - char[] newChars = new char[length + nSegments]; + char[] oldChars = new char[length_]; + text.getChars(0, length_, oldChars, 0); + char[] newChars = new char[length_ + nSegments]; int charCount = 0, segmentCount = 0; wchar separator = orientation is DWT.RIGHT_TO_LEFT ? RTL_MARK : LTR_MARK; - while (charCount < length) { + while (charCount < length_) { if (segmentCount < nSegments && charCount is segments[segmentCount]) { newChars[charCount + segmentCount++] = separator; } else { @@ -1793,9 +2121,9 @@ */ public TextStyle getStyle (int offset) { checkLayout(); - int length = text.length; - if (!(0 <= offset && offset < length)) DWT.error(DWT.ERROR_INVALID_RANGE); - for (int i=1; i<styles.length; i++) { + int length_ = text.length; + if (!(0 <= offset && offset < length_)) DWT.error(DWT.ERROR_INVALID_RANGE); + for (int i=1; i<stylesCount; i++) { if (styles[i].start > offset) { return styles[i - 1].style; } @@ -1818,9 +2146,9 @@ */ public TextStyle[] getStyles () { checkLayout(); - TextStyle[] result = new TextStyle[styles.length]; + TextStyle[] result = new TextStyle[stylesCount]; int count = 0; - for (int i=0; i<styles.length; i++) { + for (int i=0; i<stylesCount; i++) { if (styles[i].style !is null) { result[count++] = styles[i].style; } @@ -1896,10 +2224,10 @@ */ StyleItem[] itemize () { segmentsText = getSegmentsText(); - int length = segmentsText.length; + int length_ = segmentsText.length; SCRIPT_CONTROL scriptControl; SCRIPT_STATE scriptState; - final int MAX_ITEM = length + 1; + final int MAX_ITEM = length_ + 1; if ((orientation & DWT.RIGHT_TO_LEFT) !is 0) { scriptState.uBidiLevel = 1; @@ -1926,23 +2254,28 @@ * Merge styles ranges and script items */ StyleItem[] merge (SCRIPT_ITEM* items, int itemCount) { + if (styles.length > stylesCount) { + StyleItem[] newStyles = new StyleItem[stylesCount]; + System.arraycopy(styles, 0, newStyles, 0, stylesCount); + styles = newStyles; + } int count = 0, start = 0, end = segmentsText.length, itemIndex = 0, styleIndex = 0; - StyleItem[] runs = new StyleItem[itemCount + styles.length]; - SCRIPT_ITEM* scriptItem; + StyleItem[] runs = new StyleItem[itemCount + stylesCount]; + SCRIPT_ITEM* scriptItem = new SCRIPT_ITEM(); bool linkBefore = false; while (start < end) { StyleItem item = new StyleItem(); item.start = start; item.style = styles[styleIndex].style; runs[count++] = item; - scriptItem = items + itemIndex; + *scriptItem = items[itemIndex]; item.analysis = scriptItem.a; if (linkBefore) { item.analysis.fLinkBefore = true; linkBefore = false; } //scriptItem.a = new SCRIPT_ANALYSIS(); - scriptItem = items + (itemIndex + 1); + *scriptItem = items[ itemIndex + 1]; int itemLimit = scriptItem.iCharPos; int styleLimit = translateOffset(styles[styleIndex + 1].start); if (styleLimit <= itemLimit) { @@ -1951,7 +2284,7 @@ if (start < itemLimit && 0 < start && start < end) { char pChar = segmentsText.charAt(start - 1); char tChar = segmentsText.charAt(start); - if (!Compatibility.isWhitespace(pChar) && !Compatibility.isWhitespace(tChar)) { + if (Compatibility.isLetter(pChar) && Compatibility.isLetter(tChar)) { item.analysis.fLinkAfter = true; linkBefore = true; } @@ -1965,7 +2298,7 @@ } StyleItem item = new StyleItem(); item.start = end; - scriptItem = items + itemCount; + *scriptItem = items[ itemCount ]; item.analysis = scriptItem.a; runs[count++] = item; if (runs.length !is count) { @@ -2114,10 +2447,11 @@ public void setFont (Font font) { checkLayout(); if (font !is null && font.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT); - if (this.font is font) return; - if (font !is null && font ==/*eq*/ this.font) return; + Font oldFont = this.font; + if (oldFont is font) return; + this.font = font; + if (oldFont !is null && oldFont.opEquals(font)) return; freeRuns(); - this.font = font; } /** @@ -2256,7 +2590,7 @@ start = Math.min(Math.max(0, start), length_ - 1); end = Math.min(Math.max(0, end), length_ - 1); int low = -1; - int high = styles.length; + int high = stylesCount; while (high - low > 1) { int index = (high + low) / 2; if (styles[index + 1].start > start) { @@ -2265,7 +2599,7 @@ low = index; } } - if (0 <= high && high < styles.length) { + if (0 <= high && high < stylesCount) { StyleItem item = styles[high]; if (item.start is start && styles[high + 1].start - 1 is end) { if (style is null) { @@ -2278,7 +2612,7 @@ freeRuns(); int modifyStart = high; int modifyEnd = modifyStart; - while (modifyEnd < styles.length) { + while (modifyEnd < stylesCount) { if (styles[modifyEnd + 1].start > end) break; modifyEnd++; } @@ -2290,33 +2624,42 @@ return; } if (styleStart !is start && styleEnd !is end) { - StyleItem[] newStyles = new StyleItem[styles.length + 2]; - System.arraycopy(styles, 0, newStyles, 0, modifyStart + 1); + int newLength = stylesCount + 2; + if (newLength > styles.length) { + int newSize = Math.min(newLength + 1024, Math.max(64, newLength * 2)); + StyleItem[] newStyles = new StyleItem[newSize]; + System.arraycopy(styles, 0, newStyles, 0, stylesCount); + styles = newStyles; + } + System.arraycopy(styles, modifyEnd + 1, styles, modifyEnd + 3, stylesCount - modifyEnd - 1); StyleItem item = new StyleItem(); item.start = start; item.style = style; - newStyles[modifyStart + 1] = item; + styles[modifyStart + 1] = item; item = new StyleItem(); item.start = end + 1; item.style = styles[modifyStart].style; - newStyles[modifyStart + 2] = item; - System.arraycopy(styles, modifyEnd + 1, newStyles, modifyEnd + 3, styles.length - modifyEnd - 1); - styles = newStyles; + styles[modifyStart + 2] = item; + stylesCount = newLength; return; } } if (start is styles[modifyStart].start) modifyStart--; if (end is styles[modifyEnd + 1].start - 1) modifyEnd++; - int newLength = styles.length + 1 - (modifyEnd - modifyStart - 1); - StyleItem[] newStyles = new StyleItem[newLength]; - System.arraycopy(styles, 0, newStyles, 0, modifyStart + 1); + int newLength = stylesCount + 1 - (modifyEnd - modifyStart - 1); + if (newLength > styles.length) { + int newSize = Math.min(newLength + 1024, Math.max(64, newLength * 2)); + StyleItem[] newStyles = new StyleItem[newSize]; + System.arraycopy(styles, 0, newStyles, 0, stylesCount); + styles = newStyles; + } + System.arraycopy(styles, modifyEnd, styles, modifyStart + 2, stylesCount - modifyEnd); StyleItem item = new StyleItem(); item.start = start; item.style = style; - newStyles[modifyStart + 1] = item; - styles[modifyEnd].start = end + 1; - System.arraycopy(styles, modifyEnd, newStyles, modifyStart + 2, styles.length - modifyEnd); - styles = newStyles; + styles[modifyStart + 1] = item; + styles[modifyStart + 2].start = end + 1; + stylesCount = newLength; } /** @@ -2369,6 +2712,7 @@ styles[0] = new StyleItem(); styles[1] = new StyleItem(); styles[1].start = text.length; + stylesCount = 2; } /** @@ -2419,12 +2763,21 @@ return false; } +struct CallbackDataEnumFontFamExProc { + int delegate ( ENUMLOGFONTEX* lpelfe, NEWTEXTMETRICEX* lpntme, int FontType, int lParam) EnumFontFamExProc; + int lParam; +} +extern(Windows) private static int EnumFontFamExProcFunc( ENUMLOGFONTEX* lpelfe, NEWTEXTMETRICEX* lpntme, int FontType, int lParam) { + auto cb = cast(CallbackDataEnumFontFamExProc*)cast(void*)lParam; + return cb.EnumFontFamExProc( lpelfe, lpntme, FontType, cb.lParam ); +} + /* * Generate glyphs for one Run. */ void shape (HDC hdc, StyleItem run) { - int[] buffer = new int[1]; - char[] chars = new char[run.length]; + final int[] buffer = new int[1]; + final char[] chars = new char[run.length]; segmentsText.getChars(run.start, run.start + run.length, chars, 0); wchar[] wchars = StrToWCHARs( chars ); int maxGlyphs = (chars.length * 3 / 2) + 16; @@ -2435,9 +2788,29 @@ if (run.clusters is null) DWT.error(DWT.ERROR_NO_HANDLES); run.visAttrs = cast(SCRIPT_VISATTR*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, maxGlyphs * SCRIPT_VISATTR_SIZEOF); if (run.visAttrs is null) DWT.error(DWT.ERROR_NO_HANDLES); - run.psc = cast(SCRIPT_CACHE*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, 4); + run.psc = cast(SCRIPT_CACHE*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, (void*).sizeof); if (run.psc is null) DWT.error(DWT.ERROR_NO_HANDLES); - if (!shape(hdc, run, chars, buffer, maxGlyphs)) { + bool shapeSucceed = shape(hdc, run, chars, buffer, maxGlyphs); + final short script = run.analysis.eScript; + + if (!shapeSucceed) { + /* + * Shape failed. + * Try to shape with fNoGlyphIndex when the run is in the + * Private Use Area. This allows for end-user-defined character (EUDC). + */ + auto properties = device.scripts[script]; + if (properties.fPrivateUseArea) { + run.analysis.fNoGlyphIndex = true; + shapeSucceed = shape(hdc, run, chars, buffer, maxGlyphs); + } + } + + if (!shapeSucceed) { + /* + * Shape Failed. + * Try to use MLANG to find a suitable font to shape the run. + */ if (mLangFontLink2 !is null) { int dwCodePages; int cchCodePages; @@ -2447,52 +2820,152 @@ /* MapFont() */ if (OS.VtblCall(10, mLangFontLink2, cast(int)hdc, dwCodePages, cast(int)wchars[0], cast(int)hNewFont) is OS.S_OK) { auto hFont = OS.SelectObject(hdc, hNewFont); - if (shape(hdc, run, chars, buffer, maxGlyphs)) { + shapeSucceed = shape(hdc, run, chars, buffer, maxGlyphs); + if (shapeSucceed) { run.fallbackFont = hNewFont; } else { /* ReleaseFont() */ OS.VtblCall(8, mLangFontLink2, cast(int)hNewFont); OS.SelectObject(hdc, hFont); - SCRIPT_PROPERTIES* properties; - properties = device.scripts[run.analysis.eScript]; - if (properties.fPrivateUseArea) { - run.analysis.fNoGlyphIndex = true; - } - OS.ScriptShape(hdc, run.psc, wchars.ptr, wchars.length, maxGlyphs, &run.analysis, run.glyphs, run.clusters, run.visAttrs, buffer.ptr); - run.glyphCount = buffer[0]; } } } } - ABC abc; + + if (!shapeSucceed) { + /* + * Shape Failed. + * Try to shape the run using the LOGFONT in the cache. + */ + auto hFont = OS.GetCurrentObject(hdc, OS.OBJ_FONT); + LOGFONT logFont; + OS.GetObject(hFont, LOGFONT.sizeof, &logFont); + + LOGFONT* cachedLogFont = device.logFontsCache !is null ? device.logFontsCache[script] : null; + if (cachedLogFont !is null) { + cachedLogFont.lfHeight = logFont.lfHeight; + cachedLogFont.lfWeight = logFont.lfWeight; + cachedLogFont.lfItalic = logFont.lfItalic; + cachedLogFont.lfWidth = logFont.lfWidth; + auto newFont = OS.CreateFontIndirect(cachedLogFont); + OS.SelectObject(hdc, newFont); + shapeSucceed = shape(hdc, run, chars, buffer, maxGlyphs); + if (shapeSucceed) { + run.fallbackFont = newFont; + } else { + OS.SelectObject(hdc, hFont); + OS.DeleteObject(newFont); + } + } + if (!shapeSucceed) { + /* + * Shape Failed. + * Use EnumFontFamExProc to iterate over every font in the system that supports + * the charset of the run and try to shape it. + */ + if (device.logFontsCache is null) device.logFontsCache = new LOGFONT*[device.scripts.length]; + LOGFONT newLogFont; + + int EnumFontFamExProc( ENUMLOGFONTEX* lpelfe, NEWTEXTMETRICEX* lpntme, int FontType, int lParam) { + OS.MoveMemory(&newLogFont, cast(void*)lpelfe, LOGFONT.sizeof); + if (FontType is OS.RASTER_FONTTYPE) return 1; + newLogFont.lfHeight = logFont.lfHeight; + newLogFont.lfWeight = logFont.lfWeight; + newLogFont.lfItalic = logFont.lfItalic; + newLogFont.lfWidth = logFont.lfWidth; + auto newFont = OS.CreateFontIndirect(&newLogFont); + OS.SelectObject(hdc, newFont); + if (shape(hdc, run, chars, buffer, maxGlyphs)) { + run.fallbackFont = newFont; + LOGFONT* cacheLogFont = new LOGFONT(); + OS.MoveMemory(cacheLogFont, lpelfe, LOGFONT.sizeof); + device.logFontsCache[script] = cacheLogFont; + return 0; + } + OS.SelectObject(hdc, hFont); + OS.DeleteObject(newFont); + return 1; + } + CallbackDataEnumFontFamExProc cb; + cb.EnumFontFamExProc = &EnumFontFamExProc; + cb.lParam = 0; + auto properties = device.scripts[script]; + int charSet = properties.fAmbiguousCharSet ? OS.DEFAULT_CHARSET : properties.bCharSet; + newLogFont.lfCharSet = cast(byte)charSet; + OS.EnumFontFamiliesEx(hdc, &newLogFont, &EnumFontFamExProcFunc, cast(int) &cb, 0); + shapeSucceed = run.fallbackFont !is null; + } + } + + if (!shapeSucceed) { + /* + * Shape Failed. + * Give up and shape the run with the default font. + * Missing glyphs typically will be represent as black boxes in the text. + */ + auto wchars_ = StrToWCHARs(chars); + OS.ScriptShape(hdc, run.psc, wchars_.ptr, wchars_.length, maxGlyphs, &run.analysis, run.glyphs, run.clusters, run.visAttrs, buffer.ptr); + run.glyphCount = buffer[0]; + } + int[3] abc; run.advances = cast(int*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, run.glyphCount * 4); if (run.advances is null) DWT.error(DWT.ERROR_NO_HANDLES); run.goffsets = cast(GOFFSET*)OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, run.glyphCount * GOFFSET_SIZEOF); if (run.goffsets is null) DWT.error(DWT.ERROR_NO_HANDLES); - OS.ScriptPlace(hdc, run.psc, run.glyphs, run.glyphCount, run.visAttrs, &run.analysis, run.advances, run.goffsets, &abc); - if (run.style !is null && run.style.metrics !is null) { - GlyphMetrics metrics = run.style.metrics; - /* - * Bug in Windows, on a Japanese machine, Uniscribe returns glyphcount - * equals zero for FFFC (possibly other unicode code points), the fix - * is to make sure the glyph is at least one pixel wide. - */ - run.width = metrics.width * Math.max (1, run.glyphCount); - run.ascent = metrics.ascent; - run.descent = metrics.descent; - run.leading = 0; + OS.ScriptPlace(hdc, run.psc, run.glyphs, run.glyphCount, run.visAttrs, &run.analysis, run.advances, run.goffsets, cast(ABC*)abc.ptr); + run.width = abc[0] + abc[1] + abc[2]; + TextStyle style = run.style; + if (style !is null) { + OUTLINETEXTMETRIC* lotm = null; + if (style.underline || style.strikeout) { + lotm = new OUTLINETEXTMETRIC(); + if (OS.GetOutlineTextMetrics(hdc, OUTLINETEXTMETRIC.sizeof, lotm) is 0) { + lotm = null; + } + } + if (style.metrics !is null) { + GlyphMetrics metrics = style.metrics; + /* + * Bug in Windows, on a Japanese machine, Uniscribe returns glyphcount + * equals zero for FFFC (possibly other unicode code points), the fix + * is to make sure the glyph is at least one pixel wide. + */ + run.width = metrics.width * Math.max (1, run.glyphCount); + run.ascent = metrics.ascent; + run.descent = metrics.descent; + run.leading = 0; + } else { + TEXTMETRIC lptm; + if (lotm !is null) { + lptm = lotm.otmTextMetrics; + } else { + lptm = TEXTMETRIC.init; + OS.GetTextMetrics(hdc, &lptm); + } + run.ascent = lptm.tmAscent; + run.descent = lptm.tmDescent; + run.leading = lptm.tmInternalLeading; + } + if (lotm !is null) { + run.underlinePos = lotm.otmsUnderscorePosition; + run.underlineThickness = Math.max(1, lotm.otmsUnderscoreSize); + run.strikeoutPos = lotm.otmsStrikeoutPosition; + run.strikeoutThickness = Math.max(1, lotm.otmsStrikeoutSize); + } else { + run.underlinePos = 1; + run.underlineThickness = 1; + run.strikeoutPos = run.ascent / 2; + run.strikeoutThickness = 1; + } + run.ascent += style.rise; + run.descent -= style.rise; } else { - run.width = abc.abcA + abc.abcB + abc.abcC; TEXTMETRIC lptm; OS.GetTextMetrics(hdc, &lptm); run.ascent = lptm.tmAscent; run.descent = lptm.tmDescent; run.leading = lptm.tmInternalLeading; } - if (run.style !is null) { - run.ascent += run.style.rise; - run.descent -= +run.style.rise; - } } int validadeOffset(int offset, int step) {