Mercurial > projects > dwt-mac
diff dwt/graphics/TextLayout.d @ 45:d8635bb48c7c
Merge with SWT 3.5
author | Jacob Carlborg <doob@me.com> |
---|---|
date | Mon, 01 Dec 2008 17:07:00 +0100 |
parents | 642f460a0908 |
children | cfa563df4fdd |
line wrap: on
line diff
--- a/dwt/graphics/TextLayout.d Tue Oct 21 15:20:04 2008 +0200 +++ b/dwt/graphics/TextLayout.d Mon Dec 01 17:07:00 2008 +0100 @@ -1,5 +1,5 @@ -/******************************************************************************* - * Copyright (c) 2000, 2007 IBM Corporation and others. +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -9,12 +9,16 @@ * IBM Corporation - initial API and implementation * * Port to the D programming language: - * Jacob Carlborg <jacob.carlborg@gmail.com> + * Jacob Carlborg <doob@me.com> *******************************************************************************/ module dwt.graphics.TextLayout; import dwt.DWT; import dwt.DWTException; +import dwt.internal.Compatibility; +import dwt.internal.cocoa.NSArray; +import dwt.internal.cocoa.NSAutoreleasePool; +import dwt.internal.cocoa.NSBezierPath; import dwt.internal.cocoa.NSColor; import dwt.internal.cocoa.NSFont; import dwt.internal.cocoa.NSLayoutManager; @@ -27,6 +31,8 @@ import dwt.internal.cocoa.NSString; import dwt.internal.cocoa.NSTextContainer; import dwt.internal.cocoa.NSTextStorage; +import dwt.internal.cocoa.NSTextTab; +import dwt.internal.cocoa.NSThread; import dwt.internal.cocoa.OS; import tango.text.convert.Format; @@ -61,7 +67,11 @@ * when those instances are no longer required. * </p> * - * @since 3.0 + * @see <a href="http://www.eclipse.org/swt/snippets/#textlayout">TextLayout, TextStyle snippets</a> + * @see <a href="http://www.eclipse.org/swt/examples.php">DWT Example: CustomControlExample, StyledText tab</a> + * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> + * + * @since 3.0 */ public final class TextLayout : Resource { @@ -83,6 +93,8 @@ int[] lineOffsets; NSRect[] lineBounds; + + static final int UNDERLINE_THICK = 1 << 16; static class StyleItem { TextStyle style; @@ -130,11 +142,34 @@ if (isDisposed()) DWT.error(DWT.ERROR_GRAPHIC_DISPOSED); } +float[] 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 float[0]; + + float[] coordinates = new float[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 computeRuns() { if (textStorage !is null) return; NSString str = NSString.stringWith(text); textStorage = (cast(NSTextStorage)(new NSTextStorage()).alloc()); - textStorage.initWithString_(str); + textStorage.initWithString(str); layoutManager = cast(NSLayoutManager)(new NSLayoutManager()).alloc().init(); textContainer = cast(NSTextContainer)(new NSTextContainer()).alloc(); NSSize size = NSSize(); @@ -148,7 +183,7 @@ Font defaultFont = font !is null ? font : device.systemFont; NSRange range = NSRange(); range.length = str.length(); - textStorage.addAttribute(OS.NSFontAttributeName(), defaultFont.handle, range); + textStorage.addAttribute(OS.NSFontAttributeName, defaultFont.handle, range); NSMutableParagraphStyle paragraph = cast(NSMutableParagraphStyle)(new NSMutableParagraphStyle()).alloc().init(); NSTextAlignment align_ = NSLeftTextAlignment; @@ -166,13 +201,27 @@ paragraph.setAlignment(align_); paragraph.setLineSpacing(spacing); paragraph.setFirstLineHeadIndent(indent); + paragraph.setLineBreakMode(wrapWidth !is -1 ? OS.NSLineBreakByWordWrapping : OS.NSLineBreakByClipping); + paragraph.setTabStops(NSArray.array()); + if (tabs !is null) { + int count = tabs.length; + for (int i = 0, pos = 0; i < count; i++) { + pos += tabs[i]; + NSTextTab tab = (NSTextTab)new NSTextTab().alloc(); + tab = tab.initWithType(OS.NSLeftTabStopType, pos); + paragraph.addTabStop(tab); + tab.release(); + } + int width = count - 2 >= 0 ? tabs[count - 1] - tabs[count - 2] : tabs[count - 1]; + paragraph.setDefaultTabInterval(width); + } - //TODO tabs ascend descent wrap + //TODO ascend descent wrap - textStorage.addAttribute(OS.NSParagraphStyleAttributeName(), paragraph, range); + textStorage.addAttribute(OS.NSParagraphStyleAttributeName, paragraph, range); paragraph.release(); - int textLength = str.length(); + int /*long*/ textLength = str.length(); for (int i = 0; i < styles.length - 1; i++) { StyleItem run = styles[i]; if (run.style is null) continue; @@ -181,12 +230,12 @@ range.length = translateOffset(styles[i + 1].start) - range.location; Font font = style.font; if (font !is null) { - textStorage.addAttribute(OS.NSFontAttributeName(), font.handle, range); + textStorage.addAttribute(OS.NSFontAttributeName, font.handle, range); } Color foreground = style.foreground; if (foreground !is null) { NSColor color = NSColor.colorWithDeviceRed(foreground.handle[0], foreground.handle[1], foreground.handle[2], 1); - textStorage.addAttribute(OS.NSForegroundColorAttributeName(), color, range); + textStorage.addAttribute(OS.NSForegroundColorAttributeName, color, range); } Color background = style.background; if (background !is null) { @@ -194,15 +243,14 @@ textStorage.addAttribute(OS.NSBackgroundColorAttributeName, color, range); } if (style.strikeout) { - textStorage.addAttribute(OS.NSStrikethroughStyleAttributeName(), NSNumber.numberWithInt(OS.NSUnderlineStyleSingle), range); + textStorage.addAttribute(OS.NSStrikethroughStyleAttributeName, NSNumber.numberWithInt(OS.NSUnderlineStyleSingle), range); Color strikeColor = style.strikeoutColor; if (strikeColor !is null) { NSColor color = NSColor.colorWithDeviceRed(strikeColor.handle[0], strikeColor.handle[1], strikeColor.handle[2], 1); - textStorage.addAttribute(OS.NSStrikethroughColorAttributeName(), color, range); + textStorage.addAttribute(OS.NSStrikethroughColorAttributeName, color, range); } } if (style.underline) { - //TODO - IME - thick int underlineStyle = 0; switch (style.underlineStyle) { case DWT.UNDERLINE_SINGLE: @@ -211,18 +259,21 @@ case DWT.UNDERLINE_DOUBLE: underlineStyle = OS.NSUnderlineStyleDouble; break; + case UNDERLINE_THICK: + underlineStyle = OS.NSUnderlineStyleThick; + break; } if (underlineStyle !is 0) { - textStorage.addAttribute(OS.NSUnderlineStyleAttributeName(), NSNumber.numberWithInt(underlineStyle), range); + textStorage.addAttribute(OS.NSUnderlineStyleAttributeName, NSNumber.numberWithInt(underlineStyle), range); Color underlineColor = style.underlineColor; if (underlineColor !is null) { NSColor color = NSColor.colorWithDeviceRed(underlineColor.handle[0], underlineColor.handle[1], underlineColor.handle[2], 1); - textStorage.addAttribute(OS.NSUnderlineColorAttributeName(), color, range); + textStorage.addAttribute(OS.NSUnderlineColorAttributeName, color, range); } } } if (style.rise !is 0) { - textStorage.addAttribute(OS.NSBaselineOffsetAttributeName(), NSNumber.numberWithInt(style.rise), range); + textStorage.addAttribute(OS.NSBaselineOffsetAttributeName, NSNumber.numberWithInt(style.rise), range); } if (style.metrics !is null) { //TODO @@ -233,11 +284,11 @@ textContainer.setLineFragmentPadding(0); layoutManager.glyphRangeForTextContainer(textContainer); - int numberOfLines, index, numberOfGlyphs = layoutManager.numberOfGlyphs(); + int numberOfLines; + NSUInteger numberOfGlyphs = layoutManager.numberOfGlyphs(), index; NSRangePointer rangePtr = cast(NSRangePointer) OS.malloc(NSRange.sizeof); - NSRange lineRange = NSRange(); for (numberOfLines = 0, index = 0; index < numberOfGlyphs; numberOfLines++){ - layoutManager.lineFragmentUsedRectForGlyphAtIndex_effectiveRange_withoutAdditionalLayout_(index, rangePtr, true); + layoutManager.lineFragmentUsedRectForGlyphAtIndex(index, rangePtr, true); OS.memmove(&lineRange, rangePtr, NSRange.sizeof); index = lineRange.location + lineRange.length; } @@ -245,9 +296,10 @@ int[] offsets = new int[numberOfLines + 1]; NSRect[] bounds = new NSRect[numberOfLines]; for (numberOfLines = 0, index = 0; index < numberOfGlyphs; numberOfLines++){ - bounds[numberOfLines] = layoutManager.lineFragmentUsedRectForGlyphAtIndex_effectiveRange_withoutAdditionalLayout_(index, rangePtr, true); + bounds[numberOfLines] = layoutManager.lineFragmentUsedRectForGlyphAtIndex(index, rangePtr, true); + if (numberOfLines < bounds.length - 1) bounds[numberOfLines].height -= spacing; OS.memmove(&lineRange, rangePtr, NSRange.sizeof); - offsets[numberOfLines] = lineRange.location; + offsets[numberOfLines] = (int)/*64*/lineRange.location; index = lineRange.location + lineRange.length; } if (numberOfLines is 0) { @@ -257,7 +309,7 @@ bounds[0].height = Math.max(layoutManager.defaultLineHeightForFont(nsFont), ascent + descent); } OS.free(rangePtr); - offsets[numberOfLines] = textStorage.length(); + offsets[numberOfLines] = (int)/*64*/textStorage.length(); this.lineOffsets = offsets; this.lineBounds = bounds; } @@ -345,32 +397,181 @@ if (gc.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT); if (selectionForeground !is null && selectionForeground.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT); if (selectionBackground !is null && selectionBackground.isDisposed()) DWT.error(DWT.ERROR_INVALID_ARGUMENT); - gc.checkGC(GC.CLIPPING | GC.TRANSFORM | GC.FOREGROUND); -// float[] foreground = gc.data.foreground; -// NSColor color = NSColor.colorWithDeviceRed(foreground[0], foreground[1], foreground[2], foreground[3]); -// textStorage.setForegroundColor(color); - NSPoint pt = NSPoint(); - pt.x = x; - pt.y = y; - NSRange range = NSRange(); - range.length = layoutManager.numberOfGlyphs(); - bool hasSelection = selectionStart <= selectionEnd && selectionStart !is -1 && selectionEnd !is -1; + int length = translateOffset(text.length()); + if (length is 0 && flags is 0) return; + NSAutoreleasePool pool = gc.checkGC(GC.CLIPPING | GC.TRANSFORM | GC.FOREGROUND); + try { + gc.handle.saveGraphicsState(); + float[] fg = gc.data.foreground; + NSColor foreground = NSColor.colorWithDeviceRed(fg[0], fg[1], fg[2], fg[3]); + NSPoint pt = NSPoint(); + pt.x = x; + pt.y = y; + bool hasSelection = selectionStart <= selectionEnd && selectionStart !is -1 && selectionEnd !is -1; NSRange* selectionRange = null; - if (hasSelection) { - selectionRange = new NSRange(); - selectionRange.location = selectionStart; - selectionRange.length = selectionEnd - selectionStart + 1; - if (selectionBackground is null) selectionBackground = device.getSystemColor(DWT.COLOR_LIST_SELECTION); - NSColor selectionColor = NSColor.colorWithDeviceRed(selectionBackground.handle[0], selectionBackground.handle[1], selectionBackground.handle[2], selectionBackground.handle[3]); + NSColor selectionColor = null; + if (hasSelection || (flags & DWT.LAST_LINE_SELECTION) !is 0) { + if (selectionBackground is null) selectionBackground = device.getSystemColor(DWT.COLOR_LIST_SELECTION); + selectionColor = NSColor.colorWithDeviceRed(selectionBackground.handle[0], selectionBackground.handle[1], selectionBackground.handle[2], selectionBackground.handle[3]); + } + if (hasSelection) { + selectionRange = new NSRange(); + selectionRange.location = selectionStart; + selectionRange.length = selectionEnd - selectionStart + 1; layoutManager.addTemporaryAttribute(OS.NSBackgroundColorAttributeName, selectionColor, *selectionRange); - } - //TODO draw selection for flags (LAST_LINE_SELECTION and FULL_SELECTION) - if (range.length > 0) { - layoutManager.drawBackgroundForGlyphRange(range, pt); - layoutManager.drawGlyphsForGlyphRange(range, pt); - } - if (selectionRange !is null) { + } + //TODO draw selection for flags (DELIMITER_SELECTION) + int numberOfGlyphs = layoutManager.numberOfGlyphs(); + if (numberOfGlyphs > 0) { + NSRange range = new NSRange(); + for (int i = 0; i < styles.length - 1; i++) { + StyleItem run = styles[i]; + if (run.style !is null && run.style.foreground !is null) continue; + range.location = length !is 0 ? translateOffset(run.start) : 0; + range.length = translateOffset(styles[i + 1].start) - range.location; + layoutManager.addTemporaryAttribute(OS.NSForegroundColorAttributeName, foreground, range); + } + range.location = 0; + range.length = numberOfGlyphs; + layoutManager.drawBackgroundForGlyphRange(range, pt); + layoutManager.drawGlyphsForGlyphRange(range, pt); + range.length = length; + layoutManager.removeTemporaryAttribute(OS.NSForegroundColorAttributeName, range); + NSPoint point = new NSPoint(); + for (int j = 0; j < styles.length; j++) { + StyleItem run = styles[j]; + TextStyle style = run.style; + if (style is null) continue; + bool drawUnderline = style.underline && style.underlineStyle !is DWT.UNDERLINE_SINGLE && style.underlineStyle !is DWT.UNDERLINE_DOUBLE; + drawUnderline = drawUnderline && (j + 1 is styles.length || !style.isAdherentUnderline(styles[j + 1].style)); + bool drawBorder = style.borderStyle !is DWT.NONE; + drawBorder = drawBorder && (j + 1 is styles.length || !style.isAdherentBorder(styles[j + 1].style)); + if (!drawUnderline && !drawBorder) continue; + int end = j + 1 < styles.length ? translateOffset(styles[j + 1].start - 1) : length; + for (int i = 0; i < lineOffsets.length - 1; i++) { + int lineStart = untranslateOffset(lineOffsets[i]); + int lineEnd = untranslateOffset(lineOffsets[i + 1] - 1); + if (drawUnderline) { + int start = run.start; + for (int k = j; k > 0 && style.isAdherentUnderline(styles[k - 1].style); k--) { + start = styles[k - 1].start; + } + start = translateOffset(start); + if (!(start > lineEnd || end < lineStart)) { + range.location = layoutManager.glyphIndexForCharacterAtIndex(Math.max(lineStart, start)); + range.length = layoutManager.glyphIndexForCharacterAtIndex(Math.min(lineEnd, end) + 1) - range.location; + if (range.length > 0) { + gc.handle.saveGraphicsState(); + NSRect rect = layoutManager.boundingRectForGlyphRange(range, textContainer); + float baseline = layoutManager.typesetter().baselineOffsetInLayoutManager(layoutManager, lineStart); + float underlineX = pt.x + rect.x; + float underlineY = pt.y + rect.y + rect.height - baseline; + float[] color = null; + if (style.underlineColor !is null) color = style.underlineColor.handle; + if (color is null && style.foreground !is null) color = style.foreground.handle; + if (color !is null) { + NSColor.colorWithDeviceRed(color[0], color[1], color[2], color[3]).setStroke(); + } + NSBezierPath path = NSBezierPath.bezierPath(); + switch (style.underlineStyle) { + case DWT.UNDERLINE_ERROR: { + path.setLineWidth(2f); + path.setLineCapStyle(OS.NSRoundLineCapStyle); + path.setLineJoinStyle(OS.NSRoundLineJoinStyle); + path.setLineDash(new float[]{1, 3f}, 2, 0); + point.x = underlineX; + point.y = underlineY + 0.5f; + path.moveToPoint(point); + point.x = underlineX + rect.width; + point.y = underlineY + 0.5f; + path.lineToPoint(point); + break; + } + case DWT.UNDERLINE_SQUIGGLE: { + gc.handle.setShouldAntialias(false); + path.setLineWidth(1.0f); + path.setLineCapStyle(OS.NSButtLineCapStyle); + path.setLineJoinStyle(OS.NSMiterLineJoinStyle); + float lineBottom = pt.y + rect.y + rect.height; + float squigglyThickness = 1; + float squigglyHeight = 2 * squigglyThickness; + float squigglyY = Math.min(underlineY - squigglyHeight / 2, lineBottom - squigglyHeight - 1); + float[] points = computePolyline((int)underlineX, (int)squigglyY, (int)(underlineX + rect.width), (int)(squigglyY + squigglyHeight)); + point.x = points[0] + 0.5f; + point.y = points[1] + 0.5f; + path.moveToPoint(point); + for (int p = 2; p < points.length; p+=2) { + point.x = points[p] + 0.5f; + point.y = points[p+1] + 0.5f; + path.lineToPoint(point); + } + break; + } + } + path.stroke(); + gc.handle.restoreGraphicsState(); + } + } + } + if (drawBorder) { + int start = run.start; + for (int k = j; k > 0 && style.isAdherentBorder(styles[k - 1].style); k--) { + start = styles[k - 1].start; + } + start = translateOffset(start); + if (!(start > lineEnd || end < lineStart)) { + range.location = layoutManager.glyphIndexForCharacterAtIndex(Math.max(lineStart, start)); + range.length = layoutManager.glyphIndexForCharacterAtIndex(Math.min(lineEnd, end) + 1) - range.location; + if (range.length > 0) { + gc.handle.saveGraphicsState(); + NSRect rect = layoutManager.boundingRectForGlyphRange(range, textContainer); + rect.x += pt.x + 0.5f; + rect.y += pt.y + 0.5f; + rect.width -= 0.5f; + rect.height -= 0.5f; + float[] color = null; + if (style.borderColor !is null) color = style.borderColor.handle; + if (color is null && style.foreground !is null) color = style.foreground.handle; + if (color !is null) { + NSColor.colorWithDeviceRed(color[0], color[1], color[2], color[3]).setStroke(); + } + int width = 1; + float[] dashes = null; + switch (style.borderStyle) { + case DWT.BORDER_SOLID: break; + case DWT.BORDER_DASH: dashes = width !is 0 ? GC.LINE_DASH : GC.LINE_DASH_ZERO; break; + case DWT.BORDER_DOT: dashes = width !is 0 ? GC.LINE_DOT : GC.LINE_DOT_ZERO; break; + } + NSBezierPath path = NSBezierPath.bezierPath(); + path.setLineDash(dashes, dashes !is null ? dashes.length : 0, 0); + path.appendBezierPathWithRect(rect); + path.stroke(); + gc.handle.restoreGraphicsState(); + } + } + } + } + + } + } + if ((flags & DWT.LAST_LINE_SELECTION) !is 0) { + NSRect bounds = lineBounds[lineBounds.length - 1]; + NSRect rect = new NSRect(); + rect.x = pt.x + bounds.x + bounds.width; + rect.y = y + bounds.y; + rect.width = (flags & DWT.FULL_SELECTION) !is 0 ? 0x7fffffff : bounds.height / 3; + rect.height = bounds.height; + selectionColor.setFill(); + NSBezierPath path = NSBezierPath.bezierPath(); + path.appendBezierPathWithRect(rect); + path.fill(); + } + if (selectionRange !is null) { layoutManager.removeTemporaryAttribute(OS.NSBackgroundColorAttributeName, *selectionRange); + } + gc.handle.restoreGraphicsState(); + } finally { + gc.uncheckGC(pool); } } @@ -426,26 +627,37 @@ } /** - * 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(); - computeRuns(); - NSRect rect = layoutManager.usedRectForTextContainer(textContainer); - if (wrapWidth !is -1) rect.width = wrapWidth; - if (text.length() is 0) { - Font font = this.font !is null ? this.font : device.systemFont; - NSFont nsFont = font.handle; - rect.height = Math.max(rect.height, layoutManager.defaultLineHeightForFont(nsFont)); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + NSRect rect = layoutManager.usedRectForTextContainer(textContainer); + if (wrapWidth !is -1) rect.width = wrapWidth; + if (text.length() is 0) { + Font font = this.font !is null ? this.font : device.systemFont; + NSFont nsFont = font.handle; + rect.height = Math.max(rect.height, layoutManager.defaultLineHeightForFont(nsFont)); + } + rect.height = Math.max(rect.height, ascent + descent) + spacing; + return new Rectangle(0, 0, cast(int)rect.width, cast(int)rect.height); + } finally { + if (pool !is null) pool.release(); } - rect.height = Math.max(rect.height, ascent + descent); - return new Rectangle(0, 0, cast(int)rect.width, cast(int)rect.height); } /** @@ -464,19 +676,25 @@ */ public Rectangle getBounds(int start, int end) { checkLayout(); - computeRuns(); - 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 = translateOffset(start); - end = translateOffset(end); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + 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 = translateOffset(start); + end = translateOffset(end); NSRange range = NSRange(); - range.location = layoutManager.glyphIndexForCharacterAtIndex(start); - range.length = layoutManager.glyphIndexForCharacterAtIndex(end + 1) - range.location; - NSRect rect = layoutManager.boundingRectForGlyphRange(range, textContainer); + range.location = layoutManager.glyphIndexForCharacterAtIndex(start); + range.length = layoutManager.glyphIndexForCharacterAtIndex(end + 1) - range.location; + NSRect rect = layoutManager.boundingRectForGlyphRange(range, textContainer); return new Rectangle(cast(int)rect.x, cast(int)rect.y, cast(int)Math.ceil(rect.width), cast(int)Math.ceil(rect.height)); + } finally { + if (pool !is null) pool.release(); + } } /** @@ -561,13 +779,19 @@ */ public int getLevel(int offset) { checkLayout(); - computeRuns(); - int length = text.length(); - if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); - offset = translateOffset(offset); - int level = 0; - //TODO - return level; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + int length = text.length(); + if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); + offset = translateOffset(offset); + int level = 0; + //TODO + return level; + } finally { + if (pool !is null) pool.release(); + } } /** @@ -583,12 +807,18 @@ */ public int[] getLineOffsets() { checkLayout (); - computeRuns(); - int[] offsets = new int[lineOffsets.length]; - for (int i = 0; i < offsets.length; i++) { - offsets[i] = untranslateOffset(lineOffsets[i]); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + int[] offsets = new int[lineOffsets.length]; + for (int i = 0; i < offsets.length; i++) { + offsets[i] = untranslateOffset(lineOffsets[i]); + } + return offsets; + } finally { + if (pool !is null) pool.release(); } - return offsets; } /** @@ -607,16 +837,22 @@ */ public int getLineIndex(int offset) { checkLayout (); - computeRuns(); - int length = text.length(); - if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); - offset = translateOffset(offset); - for (int line=0; line<lineOffsets.length - 1; line++) { - if (lineOffsets[line + 1] > offset) { - return line; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + int length = text.length(); + if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); + offset = translateOffset(offset); + for (int line=0; line<lineOffsets.length - 1; line++) { + if (lineOffsets[line + 1] > offset) { + return line; + } } + return lineBounds.length - 1; + } finally { + if (pool !is null) pool.release(); } - return lineBounds.length - 1; } /** @@ -634,10 +870,16 @@ */ public Rectangle getLineBounds(int lineIndex) { checkLayout(); - computeRuns(); - if (!(0 <= lineIndex && lineIndex < lineBounds.length)) DWT.error(DWT.ERROR_INVALID_RANGE); - NSRect rect = lineBounds[lineIndex]; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + if (!(0 <= lineIndex && lineIndex < lineBounds.length)) DWT.error(DWT.ERROR_INVALID_RANGE); + NSRect rect = lineBounds[lineIndex]; return new Rectangle(cast(int)rect.x, cast(int)rect.y, cast(int)rect.width, cast(int)rect.height); + } finally { + if (pool !is null) pool.release(); + } } /** @@ -652,8 +894,14 @@ */ public int getLineCount() { checkLayout (); - computeRuns(); - return lineOffsets.length - 1; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + return lineOffsets.length - 1; + } finally { + if (pool !is null) pool.release(); + } } /** @@ -671,22 +919,28 @@ */ public FontMetrics getLineMetrics (int lineIndex) { checkLayout (); - computeRuns(); - int lineCount = getLineCount(); - if (!(0 <= lineIndex && lineIndex < lineCount)) DWT.error(DWT.ERROR_INVALID_RANGE); - int length = text.length(); - if (length is 0) { - Font font = this.font !is null ? this.font : device.systemFont; - NSFont nsFont = font.handle; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + int lineCount = getLineCount(); + if (!(0 <= lineIndex && lineIndex < lineCount)) DWT.error(DWT.ERROR_INVALID_RANGE); + int length = text.length(); + if (length is 0) { + Font font = this.font !is null ? this.font : device.systemFont; + NSFont nsFont = font.handle; int ascent = cast(int)(0.5f + nsFont.ascender()); int descent = cast(int)(0.5f + (-nsFont.descender() + nsFont.leading())); - ascent = Math.max(ascent, this.ascent); - descent = Math.max(descent, this.descent); - return FontMetrics.cocoa_new(ascent, descent, 0, 0, ascent + descent); + ascent = Math.max(ascent, this.ascent); + descent = Math.max(descent, this.descent); + return FontMetrics.cocoa_new(ascent, descent, 0, 0, ascent + descent); + } + Rectangle rect = getLineBounds(lineIndex); + int baseline = cast(int)layoutManager.typesetter().baselineOffsetInLayoutManager(layoutManager, getLineOffsets()[lineIndex]); + return FontMetrics.cocoa_new(rect.height - baseline, baseline, 0, 0, rect.height); + } finally { + if (pool !is null) pool.release(); } - Rectangle rect = getLineBounds(lineIndex); - int baseline = cast(int)layoutManager.typesetter().baselineOffsetInLayoutManager(layoutManager, getLineOffsets()[lineIndex]); - return FontMetrics.cocoa_new(rect.height - baseline, baseline, 0, 0, rect.height); } /** @@ -707,22 +961,28 @@ */ public Point getLocation(int offset, bool trailing) { checkLayout(); - computeRuns(); - int length = text.length(); - if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); - if (length is 0) return new Point(0, 0); - offset = translateOffset(offset); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + int length = text.length(); + if (!(0 <= offset && offset <= length)) DWT.error(DWT.ERROR_INVALID_RANGE); + if (length is 0) return new Point(0, 0); + offset = translateOffset(offset); NSUInteger glyphIndex = layoutManager.glyphIndexForCharacterAtIndex(offset); NSRect rect = layoutManager.lineFragmentUsedRectForGlyphAtIndex_effectiveRange_(glyphIndex, null); - NSPoint point = layoutManager.locationForGlyphAtIndex(glyphIndex); - if (trailing) { + NSPoint point = layoutManager.locationForGlyphAtIndex(glyphIndex); + if (trailing) { NSRange range = NSRange(); - range.location = glyphIndex; - range.length = 1; - NSRect bounds = layoutManager.boundingRectForGlyphRange(range, textContainer); - point.x += bounds.width; + range.location = glyphIndex; + range.length = 1; + NSRect bounds = layoutManager.boundingRectForGlyphRange(range, textContainer); + point.x += bounds.width; + } + return new Point(cast(int)point.x, cast(int)rect.y); + } finally { + if (pool !is null) pool.release(); } - return new Point(cast(int)point.x, cast(int)rect.y); } /** @@ -745,7 +1005,13 @@ * @see #getPreviousOffset(int, int) */ public int getNextOffset (int offset, int movement) { - return _getOffset(offset, movement, true); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + return _getOffset(offset, movement, true); + } finally { + if (pool !is null) pool.release(); + } } int _getOffset (int offset, int movement, bool forward) { @@ -763,18 +1029,18 @@ } else { offset--; } - return untranslateOffset(offset); + return Math.max(0, Math.min(length, untranslateOffset(offset))); } case DWT.MOVEMENT_WORD: { - return untranslateOffset(textStorage.nextWordFromIndex(offset, forward)); + return untranslateOffset((int)/*64*/textStorage.nextWordFromIndex(offset, forward)); } case DWT.MOVEMENT_WORD_END: { NSRange range = textStorage.doubleClickAtIndex(length is offset ? length - 1 : offset); - return untranslateOffset(range.location + range.length); + return untranslateOffset((int)/*64*/(range.location + range.length)); } case DWT.MOVEMENT_WORD_START: { NSRange range = textStorage.doubleClickAtIndex(length is offset ? length - 1 : offset); - return untranslateOffset(range.location); + return untranslateOffset((int)/*64*/range.location); } default: break; @@ -807,9 +1073,15 @@ */ public int getOffset(Point point, int[] trailing) { checkLayout(); - computeRuns(); - if (point is null) DWT.error(DWT.ERROR_NULL_ARGUMENT); - return getOffset(point.x, point.y, trailing); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + if (point is null) DWT.error(DWT.ERROR_NULL_ARGUMENT); + return getOffset(point.x, point.y, trailing); + } finally { + if (pool !is null) pool.release(); + } } /** @@ -837,18 +1109,24 @@ */ public int getOffset(int x, int y, int[] trailing) { checkLayout(); - computeRuns(); - if (trailing !is null && trailing.length < 1) DWT.error(DWT.ERROR_INVALID_ARGUMENT); - int length = text.length(); - if (length is 0) return 0; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + computeRuns(); + if (trailing !is null && trailing.length < 1) DWT.error(DWT.ERROR_INVALID_ARGUMENT); + int length = text.length(); + if (length is 0) return 0; NSPoint pt = NSPoint(); - pt.x = x; - pt.y = y; + pt.x = x; + pt.y = y; CGFloat partialFration; - NSUInteger glyphIndex = layoutManager.glyphIndexForPoint_inTextContainer_fractionOfDistanceThroughGlyph_(pt, textContainer, &partialFration); - int offset = layoutManager.characterIndexForGlyphAtIndex(glyphIndex); + NSUInteger glyphIndex = layoutManager.glyphIndexForPoint(pt, textContainer, &partialFration); + int /*long*/ offset = layoutManager.characterIndexForGlyphAtIndex(glyphIndex); if (trailing !is null) trailing[0] = cast(int) Math.round(partialFration); - return Math.min(untranslateOffset(offset), length - 1); + return Math.min(untranslateOffset((int)/*64*/offset), length - 1); + } finally { + if (pool !is null) pool.release(); + } } /** @@ -885,7 +1163,13 @@ * @see #getNextOffset(int, int) */ public int getPreviousOffset (int index, int movement) { - return _getOffset(index, movement, false); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + return _getOffset(index, movement, false); + } finally { + if (pool !is null) pool.release(); + } } /** @@ -1088,8 +1372,14 @@ if ((alignment & DWT.LEFT) !is 0) alignment = DWT.LEFT; if ((alignment & DWT.RIGHT) !is 0) alignment = DWT.RIGHT; if (this.alignment is alignment) return; - freeRuns(); - this.alignment = alignment; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.alignment = alignment; + } finally { + if (pool !is null) pool.release(); + } } /** @@ -1114,8 +1404,14 @@ checkLayout (); if (ascent < -1) DWT.error(DWT.ERROR_INVALID_ARGUMENT); if (this.ascent is ascent) return; - freeRuns(); - this.ascent = ascent; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.ascent = ascent; + } finally { + if (pool !is null) pool.release(); + } } /** @@ -1140,8 +1436,14 @@ checkLayout (); if (descent < -1) DWT.error(DWT.ERROR_INVALID_ARGUMENT); if (this.descent is descent) return; - freeRuns(); - this.descent = descent; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.descent = descent; + } finally { + if (pool !is null) pool.release(); + } } /** @@ -1167,7 +1469,13 @@ if (oldFont is font) return; this.font = font; if (oldFont !is null && oldFont.equals(font)) return; - freeRuns(); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + } finally { + if (pool !is null) pool.release(); + } } /** @@ -1186,8 +1494,14 @@ checkLayout (); if (indent < 0) return; if (this.indent is indent) return; - freeRuns(); - this.indent = indent; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.indent = indent; + } finally { + if (pool !is null) pool.release(); + } } /** @@ -1205,8 +1519,14 @@ public void setJustify (bool justify) { checkLayout (); if (justify is this.justify) return; - freeRuns(); - this.justify = justify; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.justify = justify; + } finally { + if (pool !is null) pool.release(); + } } /** @@ -1227,7 +1547,13 @@ if ((orientation & DWT.LEFT_TO_RIGHT) !is 0) orientation = DWT.LEFT_TO_RIGHT; if (this.orientation is orientation) return; this.orientation = orientation; - freeRuns(); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + } finally { + if (pool !is null) pool.release(); + } } /** @@ -1260,8 +1586,14 @@ if (i is segments.length) return; } } - freeRuns(); - this.segments = segments; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.segments = segments; + } finally { + if (pool !is null) pool.release(); + } } /** @@ -1281,8 +1613,14 @@ checkLayout(); if (spacing < 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT); if (this.spacing is spacing) return; - freeRuns(); - this.spacing = spacing; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.spacing = spacing; + } finally { + if (pool !is null) pool.release(); + } } /** @@ -1300,73 +1638,79 @@ */ public void setStyle (TextStyle style, int start, int end) { checkLayout(); - int length = text.length(); - if (length is 0) return; - if (start > end) return; - 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; - while (high - low > 1) { - int index = (high + low) / 2; - if (styles[index + 1].start > start) { - high = index; - } else { - low = index; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + int length = text.length(); + if (length is 0) return; + if (start > end) return; + 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; + while (high - low > 1) { + int index = (high + low) / 2; + if (styles[index + 1].start > start) { + high = index; + } else { + low = index; + } } - } - if (0 <= high && high < styles.length) { - StyleItem item = styles[high]; - if (item.start is start && styles[high + 1].start - 1 is end) { - if (style is null) { - if (item.style is null) return; - } else { - if (style.equals(item.style)) return; + if (0 <= high && high < styles.length) { + StyleItem item = styles[high]; + if (item.start is start && styles[high + 1].start - 1 is end) { + if (style is null) { + if (item.style is null) return; + } else { + if (style.equals(item.style)) return; + } } } - } - freeRuns(); - int modifyStart = high; - int modifyEnd = modifyStart; - while (modifyEnd < styles.length) { - if (styles[modifyEnd + 1].start > end) break; - modifyEnd++; - } - if (modifyStart is modifyEnd) { - int styleStart = styles[modifyStart].start; - int styleEnd = styles[modifyEnd + 1].start - 1; - if (styleStart is start && styleEnd is end) { - styles[modifyStart].style = style; - return; + freeRuns(); + int modifyStart = high; + int modifyEnd = modifyStart; + while (modifyEnd < styles.length) { + if (styles[modifyEnd + 1].start > end) break; + modifyEnd++; } - if (styleStart !is start && styleEnd !is end) { - StyleItem[] newStyles = new StyleItem[styles.length + 2]; - System.arraycopy(styles, 0, newStyles, 0, modifyStart + 1); - StyleItem item = new StyleItem(); - item.start = start; - item.style = style; - newStyles[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; - return; + if (modifyStart is modifyEnd) { + int styleStart = styles[modifyStart].start; + int styleEnd = styles[modifyEnd + 1].start - 1; + if (styleStart is start && styleEnd is end) { + styles[modifyStart].style = style; + return; + } + if (styleStart !is start && styleEnd !is end) { + StyleItem[] newStyles = new StyleItem[styles.length + 2]; + System.arraycopy(styles, 0, newStyles, 0, modifyStart + 1); + StyleItem item = new StyleItem(); + item.start = start; + item.style = style; + newStyles[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; + 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); + 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; + } finally { + if (pool !is null) pool.release(); } - 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); - 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; } /** @@ -1392,8 +1736,14 @@ if (i is tabs.length) return; } } - freeRuns(); - this.tabs = tabs; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.tabs = tabs; + } finally { + if (pool !is null) pool.release(); + } } /** @@ -1412,12 +1762,18 @@ checkLayout (); if (text is null) DWT.error(DWT.ERROR_NULL_ARGUMENT); if (text.equals(this.text)) return; - freeRuns(); - this.text = text; - styles = new StyleItem[2]; - styles[0] = new StyleItem(); - styles[1] = new StyleItem(); - styles[styles.length - 1].start = text.length(); + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.text = text; + styles = new StyleItem[2]; + styles[0] = new StyleItem(); + styles[1] = new StyleItem(); + styles[styles.length - 1].start = text.length(); + } finally { + if (pool !is null) pool.release(); + } } /** @@ -1440,8 +1796,14 @@ checkLayout(); if (width < -1 || width is 0) DWT.error(DWT.ERROR_INVALID_ARGUMENT); if (this.wrapWidth is width) return; - freeRuns(); - this.wrapWidth = width; + NSAutoreleasePool pool = null; + if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init(); + try { + freeRuns(); + this.wrapWidth = width; + } finally { + if (pool !is null) pool.release(); + } } /**