
org.eclipse.swt.graphics.TextLayout Maven / Gradle / Ivy
/******************************************************************************* * Copyright (c) 2000, 2018 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.swt.graphics; import java.util.*; import org.eclipse.swt.*; import org.eclipse.swt.internal.*; import org.eclipse.swt.internal.cairo.*; import org.eclipse.swt.internal.gtk.*; /** *
,TextLayout
is a graphic object that represents * styled text. ** Instances of this class provide support for drawing, cursor * navigation, hit testing, text wrapping, alignment, tab expansion * line breaking, etc. These are aspects required for rendering internationalized text. *
* Application code must explicitly invoke the
* * @see TextLayout, TextStyle snippets * @see SWT Example: CustomControlExample, StyledText tab * @see Sample code and further information * * @since 3.0 */ public final class TextLayout extends Resource { static class StyleItem { TextStyle style; int start; @Override public String toString () { return "StyleItem {" + start + ", " + style + "}"; } } Font font; String text; int ascentInPoints, descentInPoints; int indent, wrapIndent, wrapWidth; int[] segments; char[] segmentsChars; int[] tabs; StyleItem[] styles; int stylesCount; int /*long*/ layout, context, attrList, selAttrList; int[] invalidOffsets; static final char LTR_MARK = '\u200E', RTL_MARK = '\u200F', ZWS = '\u200B', ZWNBS = '\uFEFF'; /** * Constructs a new instance of this class on the given device. *TextLayout#dispose()
* method to release the operating system resources managed by each instance * when those instances are no longer required. ** You must dispose the text layout when it is no longer required. *
* * @param device the device on which to allocate the text layout * * @exception IllegalArgumentException*
* * @see #dispose() */ public TextLayout (Device device) { super(device); device = this.device; context = GDK.gdk_pango_context_get(); if (context == 0) SWT.error(SWT.ERROR_NO_HANDLES); OS.pango_context_set_language(context, GTK.gtk_get_default_language()); OS.pango_context_set_base_dir(context, OS.PANGO_DIRECTION_LTR); layout = OS.pango_layout_new(context); if (layout == 0) SWT.error(SWT.ERROR_NO_HANDLES); OS.pango_layout_set_font_description(layout, device.systemFont.handle); OS.pango_layout_set_wrap(layout, OS.PANGO_WRAP_WORD_CHAR); OS.pango_layout_set_tabs(layout, device.emptyTab); OS.pango_layout_set_auto_dir(layout, false); text = ""; wrapWidth = ascentInPoints = descentInPoints = -1; styles = new StyleItem[2]; styles[0] = new StyleItem(); styles[1] = new StyleItem(); stylesCount = 2; init(); } void checkLayout() { if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED); } void computeRuns () { if (attrList != 0) return; String segmentsText = getSegmentsText(); byte[] buffer = Converter.wcsToMbcs(segmentsText, false); OS.pango_layout_set_text (layout, buffer, buffer.length); if (stylesCount == 2 && styles[0].style == null && ascentInPoints == -1 && descentInPoints == -1 && segments == null) return; int /*long*/ ptr = OS.pango_layout_get_text(layout); attrList = OS.pango_attr_list_new(); selAttrList = OS.pango_attr_list_new(); PangoAttribute attribute = new PangoAttribute(); char[] chars = null; int segementsLength = segmentsText.length(); int nSegments = segementsLength - text.length(); int offsetCount = nSegments; int[] lineOffsets = null; if ((ascentInPoints != -1 || descentInPoints != -1) && segementsLength > 0) { PangoRectangle rect = new PangoRectangle(); if (ascentInPoints != -1) rect.y = -(DPIUtil.autoScaleUp(getDevice(), ascentInPoints) * OS.PANGO_SCALE); rect.height = DPIUtil.autoScaleUp(getDevice(), (Math.max(0, ascentInPoints) + Math.max(0, descentInPoints))) * OS.PANGO_SCALE; int lineCount = OS.pango_layout_get_line_count(layout); chars = new char[segementsLength + lineCount * 2]; lineOffsets = new int [lineCount]; int oldPos = 0, lineIndex = 0; PangoLayoutLine line = new PangoLayoutLine(); while (lineIndex < lineCount) { int /*long*/ linePtr = OS.pango_layout_get_line(layout, lineIndex); OS.memmove(line, linePtr, PangoLayoutLine.sizeof); int bytePos = line.start_index; /* Note: The length in bytes of ZWS and ZWNBS are both equals to 3 */ int offset = lineIndex * 6; int /*long*/ attr = OS.pango_attr_shape_new (rect, rect); OS.memmove (attribute, attr, PangoAttribute.sizeof); attribute.start_index = bytePos + offset; attribute.end_index = bytePos + offset + 3; OS.memmove (attr, attribute, PangoAttribute.sizeof); OS.pango_attr_list_insert(attrList, attr); OS.pango_attr_list_insert(selAttrList, OS.pango_attribute_copy(attr)); attr = OS.pango_attr_shape_new (rect, rect); OS.memmove (attribute, attr, PangoAttribute.sizeof); attribute.start_index = bytePos + offset + 3; attribute.end_index = bytePos + offset + 6; OS.memmove (attr, attribute, PangoAttribute.sizeof); OS.pango_attr_list_insert(attrList, attr); OS.pango_attr_list_insert(selAttrList, OS.pango_attribute_copy(attr)); int pos = (int)/*64*/OS.g_utf16_pointer_to_offset(ptr, ptr + bytePos); chars[pos + lineIndex * 2] = ZWS; chars[pos + lineIndex * 2 + 1] = ZWNBS; segmentsText.getChars(oldPos, pos, chars, oldPos + lineIndex * 2); lineOffsets[lineIndex] = pos + lineIndex * 2; oldPos = pos; lineIndex++; } segmentsText.getChars(oldPos, segementsLength, chars, oldPos + lineIndex * 2); buffer = Converter.wcsToMbcs(chars, false); OS.pango_layout_set_text (layout, buffer, buffer.length); ptr = OS.pango_layout_get_text(layout); offsetCount += 2 * lineCount; } else { chars = new char[segementsLength]; segmentsText.getChars(0, segementsLength, chars, 0); } invalidOffsets = new int[offsetCount]; if (offsetCount > 0) { offsetCount = 0; int lineIndex = 0; int segmentCount = 0; for (int i = 0; i < chars.length; i++) { char c = chars[i]; if (c == ZWS && lineOffsets != null && lineIndex < lineOffsets.length && i == lineOffsets[lineIndex]) { invalidOffsets[offsetCount++] = i; //ZWS invalidOffsets[offsetCount++] = ++i; //ZWNBS lineIndex++; } else if (segmentCount < nSegments && i - offsetCount == segments[segmentCount]) { invalidOffsets[offsetCount++] = i; segmentCount++; } } } int strlen = C.strlen(ptr); Font defaultFont = font != null ? font : device.systemFont; for (int i = 0; i < stylesCount - 1; i++) { StyleItem styleItem = styles[i]; TextStyle style = styleItem.style; if (style == null) continue; int start = translateOffset(styleItem.start); int end = translateOffset(styles[i+1].start - 1); int byteStart = (int)/*64*/(OS.g_utf16_offset_to_pointer(ptr, start) - ptr); int byteEnd = (int)/*64*/(OS.g_utf16_offset_to_pointer(ptr, end + 1) - ptr); byteStart = Math.min(byteStart, strlen); byteEnd = Math.min(byteEnd, strlen); Font font = style.font; if (font != null && !font.isDisposed() && !defaultFont.equals(font)) { int /*long*/ attr = OS.pango_attr_font_desc_new (font.handle); OS.memmove (attribute, attr, PangoAttribute.sizeof); attribute.start_index = byteStart; attribute.end_index = byteEnd; OS.memmove (attr, attribute, PangoAttribute.sizeof); OS.pango_attr_list_insert(attrList, attr); OS.pango_attr_list_insert(selAttrList, OS.pango_attribute_copy(attr)); } if (style.underline) { int underlineStyle = OS.PANGO_UNDERLINE_NONE; switch (style.underlineStyle) { case SWT.UNDERLINE_SINGLE: underlineStyle = OS.PANGO_UNDERLINE_SINGLE; break; case SWT.UNDERLINE_DOUBLE: underlineStyle = OS.PANGO_UNDERLINE_DOUBLE; break; case SWT.UNDERLINE_SQUIGGLE: case SWT.UNDERLINE_ERROR: underlineStyle = OS.PANGO_UNDERLINE_ERROR; break; case SWT.UNDERLINE_LINK: { if (style.foreground == null) { // Bug 497071: use COLOR_LINK_FOREGROUND for StyledText links int /*long*/ attr; if (GTK.GTK3) { GdkRGBA linkRGBA = device.getSystemColor(SWT.COLOR_LINK_FOREGROUND).handleRGBA; // Manual conversion since PangoAttrColor is a special case. // It uses GdkColor style colors but is supported on GTK3. attr = OS.pango_attr_foreground_new((short)(linkRGBA.red * 0xFFFF), (short)(linkRGBA.green * 0xFFFF), (short)(linkRGBA.blue * 0xFFFF)); } else { GdkColor linkColor = device.getSystemColor(SWT.COLOR_LINK_FOREGROUND).handle; attr = OS.pango_attr_foreground_new(linkColor.red, linkColor.green, linkColor.blue); } OS.memmove (attribute, attr, PangoAttribute.sizeof); attribute.start_index = byteStart; attribute.end_index = byteEnd; OS.memmove (attr, attribute, PangoAttribute.sizeof); OS.pango_attr_list_insert(attrList, attr); } underlineStyle = OS.PANGO_UNDERLINE_SINGLE; break; } } int /*long*/ attr = OS.pango_attr_underline_new(underlineStyle); OS.memmove(attribute, attr, PangoAttribute.sizeof); attribute.start_index = byteStart; attribute.end_index = byteEnd; OS.memmove(attr, attribute, PangoAttribute.sizeof); OS.pango_attr_list_insert(attrList, attr); OS.pango_attr_list_insert(selAttrList, OS.pango_attribute_copy(attr)); if (style.underlineColor != null) { if (GTK.GTK3) { GdkRGBA rgba = style.underlineColor.handleRGBA; attr = OS.pango_attr_underline_color_new((short)(rgba.red * 0xFFFF), (short)(rgba.green * 0xFFFF), (short)(rgba.blue * 0xFFFF)); } else { GdkColor fg = style.underlineColor.handle; attr = OS.pango_attr_underline_color_new(fg.red, fg.green, fg.blue); } if (attr != 0) { OS.memmove(attribute, attr, PangoAttribute.sizeof); attribute.start_index = byteStart; attribute.end_index = byteEnd; OS.memmove(attr, attribute, PangoAttribute.sizeof); OS.pango_attr_list_insert(attrList, attr); OS.pango_attr_list_insert(selAttrList, OS.pango_attribute_copy(attr)); } } } if (style.strikeout) { int /*long*/ attr = OS.pango_attr_strikethrough_new(true); OS.memmove(attribute, attr, PangoAttribute.sizeof); attribute.start_index = byteStart; attribute.end_index = byteEnd; OS.memmove(attr, attribute, PangoAttribute.sizeof); OS.pango_attr_list_insert(attrList, attr); OS.pango_attr_list_insert(selAttrList, OS.pango_attribute_copy(attr)); if (style.strikeoutColor != null) { if (GTK.GTK3) { GdkRGBA rgba = style.strikeoutColor.handleRGBA; attr = OS.pango_attr_strikethrough_color_new((short)(rgba.red * 0xFFFF), (short)(rgba.green * 0xFFFF), (short)(rgba.blue * 0xFFFF)); } else { GdkColor fg = style.strikeoutColor.handle; attr = OS.pango_attr_strikethrough_color_new(fg.red, fg.green, fg.blue); } if (attr != 0) { OS.memmove(attribute, attr, PangoAttribute.sizeof); attribute.start_index = byteStart; attribute.end_index = byteEnd; OS.memmove(attr, attribute, PangoAttribute.sizeof); OS.pango_attr_list_insert(attrList, attr); OS.pango_attr_list_insert(selAttrList, OS.pango_attribute_copy(attr)); } } } Color foreground = style.foreground; if (foreground != null && !foreground.isDisposed()) { int /*long*/ attr; if (GTK.GTK3) { GdkRGBA rgba = foreground.handleRGBA; attr = OS.pango_attr_foreground_new((short)(rgba.red * 0xFFFF), (short)(rgba.green * 0xFFFF), (short)(rgba.blue * 0xFFFF)); } else { GdkColor fg = foreground.handle; attr = OS.pango_attr_foreground_new(fg.red, fg.green, fg.blue); } OS.memmove (attribute, attr, PangoAttribute.sizeof); attribute.start_index = byteStart; attribute.end_index = byteEnd; OS.memmove (attr, attribute, PangoAttribute.sizeof); OS.pango_attr_list_insert(attrList, attr); } Color background = style.background; if (background != null && !background.isDisposed()) { int /*long*/ attr; if (GTK.GTK3) { GdkRGBA rgba = background.handleRGBA; attr = OS.pango_attr_background_new((short)(rgba.red * 0xFFFF), (short)(rgba.green * 0xFFFF), (short)(rgba.blue * 0xFFFF)); } else { GdkColor bg = background.handle; attr = OS.pango_attr_background_new(bg.red, bg.green, bg.blue); } OS.memmove (attribute, attr, PangoAttribute.sizeof); attribute.start_index = byteStart; attribute.end_index = byteEnd; OS.memmove (attr, attribute, PangoAttribute.sizeof); OS.pango_attr_list_insert(attrList, attr); } GlyphMetrics metrics = style.metrics; if (metrics != null) { PangoRectangle rect = new PangoRectangle(); rect.y = -(DPIUtil.autoScaleUp(getDevice(), metrics.ascent) * OS.PANGO_SCALE); rect.height = DPIUtil.autoScaleUp(getDevice(), (metrics.ascent + metrics.descent)) * OS.PANGO_SCALE; rect.width = DPIUtil.autoScaleUp(getDevice(), metrics.width) * OS.PANGO_SCALE; int /*long*/ attr = OS.pango_attr_shape_new (rect, rect); OS.memmove (attribute, attr, PangoAttribute.sizeof); attribute.start_index = byteStart; attribute.end_index = byteEnd; OS.memmove (attr, attribute, PangoAttribute.sizeof); OS.pango_attr_list_insert(attrList, attr); OS.pango_attr_list_insert(selAttrList, OS.pango_attribute_copy(attr)); } int rise = style.rise; if (rise != 0) { int /*long*/ attr = OS.pango_attr_rise_new (DPIUtil.autoScaleUp(getDevice(), rise) * OS.PANGO_SCALE); OS.memmove (attribute, attr, PangoAttribute.sizeof); attribute.start_index = byteStart; attribute.end_index = byteEnd; OS.memmove (attr, attribute, PangoAttribute.sizeof); OS.pango_attr_list_insert(attrList, attr); OS.pango_attr_list_insert(selAttrList, OS.pango_attribute_copy(attr)); } } OS.pango_layout_set_attributes(layout, attrList); } 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 == 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; } @Override void destroy() { font = null; text = null; styles = null; freeRuns(); segments = null; segmentsChars = null; if (layout != 0) OS.g_object_unref(layout); layout = 0; if (context != 0) OS.g_object_unref(context); context = 0; } /** * Draws the receiver's text using the specified GC at the specified * point. * * @param gc the GC to draw * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn * * @exception SWTException- ERROR_NULL_ARGUMENT - if device is null and there is no current device
**
* @exception IllegalArgumentException- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed
**
*/ public void draw(GC gc, int x, int y) { x = DPIUtil.autoScaleUp(getDevice(), x); y = DPIUtil.autoScaleUp(getDevice(), y); drawInPixels(gc, x, y); } void drawInPixels(GC gc, int x, int y) { drawInPixels(gc, x, y, -1, -1, null, null); } /** * Draws the receiver's text using the specified GC at the specified * point. * * @param gc the GC to draw * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn * @param selectionStart the offset where the selections starts, or -1 indicating no selection * @param selectionEnd the offset where the selections ends, or -1 indicating no selection * @param selectionForeground selection foreground, or NULL to use the system default color * @param selectionBackground selection background, or NULL to use the system default color * * @exception SWTException- ERROR_NULL_ARGUMENT - if the gc is null
**
* @exception IllegalArgumentException- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed
**
*/ public void draw(GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground) { checkLayout (); x = DPIUtil.autoScaleUp(getDevice(), x); y = DPIUtil.autoScaleUp(getDevice(), y); drawInPixels(gc, x, y, selectionStart, selectionEnd, selectionForeground, selectionBackground); } void drawInPixels(GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground) { drawInPixels(gc, x, y, selectionStart, selectionEnd, selectionForeground, selectionBackground, 0); } /** * Draws the receiver's text using the specified GC at the specified * point. *- ERROR_NULL_ARGUMENT - if the gc is null
** The parameter
* @param gc the GC to draw * @param x the x coordinate of the top left corner of the rectangular area where the text is to be drawn * @param y the y coordinate of the top left corner of the rectangular area where the text is to be drawn * @param selectionStart the offset where the selections starts, or -1 indicating no selection * @param selectionEnd the offset where the selections ends, or -1 indicating no selection * @param selectionForeground selection foreground, or NULL to use the system default color * @param selectionBackground selection background, or NULL to use the system default color * @param flags drawing options * * @exception SWTExceptionflags
can include one ofSWT.DELIMITER_SELECTION
* orSWT.FULL_SELECTION
to specify the selection behavior on all lines except * for the last line, and can also includeSWT.LAST_LINE_SELECTION
to extend * the specified selection behavior to the last line. **
* @exception IllegalArgumentException- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed
**
* * @since 3.3 */ public void draw(GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground, int flags) { checkLayout (); x = DPIUtil.autoScaleUp(getDevice(), x); y = DPIUtil.autoScaleUp(getDevice(), y); drawInPixels(gc, x, y, selectionStart, selectionEnd, selectionForeground, selectionBackground, flags); } void drawInPixels(GC gc, int x, int y, int selectionStart, int selectionEnd, Color selectionForeground, Color selectionBackground, int flags) { checkLayout (); computeRuns(); if (gc == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); if (gc.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); if (selectionForeground != null && selectionForeground.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); if (selectionBackground != null && selectionBackground.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); gc.checkGC(GC.FOREGROUND); int length = text.length(); x += Math.min (indent, wrapIndent); boolean hasSelection = selectionStart <= selectionEnd && selectionStart != -1 && selectionEnd != -1; GCData data = gc.data; int /*long*/ cairo = data.cairo; if ((flags & (SWT.FULL_SELECTION | SWT.DELIMITER_SELECTION)) != 0 && (hasSelection || (flags & SWT.LAST_LINE_SELECTION) != 0)) { int /*long*/[] attrs = new int /*long*/[1]; int[] nAttrs = new int[1]; PangoLogAttr logAttr = new PangoLogAttr(); PangoRectangle rect = new PangoRectangle(); int lineCount = OS.pango_layout_get_line_count(layout); int /*long*/ ptr = OS.pango_layout_get_text(layout); int /*long*/ iter = OS.pango_layout_get_iter(layout); if (selectionBackground == null) selectionBackground = device.getSystemColor(SWT.COLOR_LIST_SELECTION); Cairo.cairo_save(cairo); if (GTK.GTK3) { GdkRGBA rgba = selectionBackground.handleRGBA; Cairo.cairo_set_source_rgba(cairo, rgba.red, rgba.green, rgba.blue, rgba.alpha); } else { GdkColor color = selectionBackground.handle; Cairo.cairo_set_source_rgba(cairo, (color.red & 0xFFFF) / (float)0xFFFF, (color.green & 0xFFFF) / (float)0xFFFF, (color.blue & 0xFFFF) / (float)0xFFFF, data.alpha / (float)0xFF); } int lineIndex = 0; do { int lineEnd; OS.pango_layout_iter_get_line_extents(iter, null, rect); if (OS.pango_layout_iter_next_line(iter)) { int bytePos = OS.pango_layout_iter_get_index(iter); lineEnd = (int)/*64*/OS.g_utf16_pointer_to_offset(ptr, ptr + bytePos); } else { lineEnd = (int)/*64*/OS.g_utf16_strlen(ptr, -1); } boolean extent = false; if (lineIndex == lineCount - 1 && (flags & SWT.LAST_LINE_SELECTION) != 0) { extent = true; } else { if (attrs[0] == 0) OS.pango_layout_get_log_attrs(layout, attrs, nAttrs); OS.memmove(logAttr, attrs[0] + lineEnd * PangoLogAttr.sizeof, PangoLogAttr.sizeof); if (!logAttr.is_line_break) { if (selectionStart <= lineEnd && lineEnd <= selectionEnd) extent = true; } else { if (selectionStart <= lineEnd && lineEnd < selectionEnd && (flags & SWT.FULL_SELECTION) != 0) { extent = true; } } } if (extent) { int lineX = x + OS.PANGO_PIXELS(rect.x) + OS.PANGO_PIXELS(rect.width); int lineY = y + OS.PANGO_PIXELS(rect.y); int height = OS.PANGO_PIXELS(rect.height); if (ascentInPoints != -1 && descentInPoints != -1) { height = Math.max (height, DPIUtil.autoScaleUp(getDevice(), ascentInPoints + descentInPoints)); } int width = (flags & SWT.FULL_SELECTION) != 0 ? 0x7fff : height / 3; Cairo.cairo_rectangle(cairo, lineX, lineY, width, height); Cairo.cairo_fill(cairo); } lineIndex++; } while (lineIndex < lineCount); OS.pango_layout_iter_free(iter); if (attrs[0] != 0) OS.g_free(attrs[0]); Cairo.cairo_restore(cairo); } if (length == 0) return; if (!hasSelection) { if ((data.style & SWT.MIRRORED) != 0) { Cairo.cairo_save(cairo); Cairo.cairo_scale(cairo, -1, 1); Cairo.cairo_translate(cairo, -2 * x - width(), 0); } Cairo.cairo_move_to(cairo, x, y); OS.pango_cairo_show_layout(cairo, layout); drawBorder(gc, x, y, null); if ((data.style & SWT.MIRRORED) != 0) { Cairo.cairo_restore(cairo); } } else { selectionStart = Math.min(Math.max(0, selectionStart), length - 1); selectionEnd = Math.min(Math.max(0, selectionEnd), length - 1); length = (int)/*64*/OS.g_utf16_strlen(OS.pango_layout_get_text(layout), -1); selectionStart = translateOffset(selectionStart); selectionEnd = translateOffset(selectionEnd); if (selectionForeground == null) selectionForeground = device.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT); if (selectionBackground == null) selectionBackground = device.getSystemColor(SWT.COLOR_LIST_SELECTION); boolean fullSelection = selectionStart == 0 && selectionEnd == length - 1; if (fullSelection) { int /*long*/ ptr = OS.pango_layout_get_text(layout); if ((data.style & SWT.MIRRORED) != 0) { Cairo.cairo_save(cairo); Cairo.cairo_scale(cairo, -1, 1); Cairo.cairo_translate(cairo, -2 * x - width(), 0); } drawWithCairo(gc, x, y, 0, C.strlen(ptr), fullSelection, selectionForeground, selectionBackground); if ((data.style & SWT.MIRRORED) != 0) { Cairo.cairo_restore(cairo); } } else { int /*long*/ ptr = OS.pango_layout_get_text(layout); int byteSelStart = (int)/*64*/(OS.g_utf16_offset_to_pointer(ptr, selectionStart) - ptr); int byteSelEnd = (int)/*64*/(OS.g_utf16_offset_to_pointer(ptr, selectionEnd + 1) - ptr); int strlen = C.strlen(ptr); byteSelStart = Math.min(byteSelStart, strlen); byteSelEnd = Math.min(byteSelEnd, strlen); if ((data.style & SWT.MIRRORED) != 0) { Cairo.cairo_save(cairo); Cairo.cairo_scale(cairo, -1, 1); Cairo.cairo_translate(cairo, -2 * x - width(), 0); } drawWithCairo(gc, x, y, byteSelStart, byteSelEnd, fullSelection, selectionForeground, selectionBackground); if ((data.style & SWT.MIRRORED) != 0) { Cairo.cairo_restore(cairo); } } } Cairo.cairo_new_path(cairo); } // Bug 477950: In order to support GTK2 and GTK3 colors simultaneously, this method's parameters // were modified to accept SWT Color objects instead of GdkColor structs. void drawWithCairo(GC gc, int x, int y, int start, int end, boolean fullSelection, Color fg, Color bg) { GCData data = gc.data; int /*long*/ cairo = data.cairo; Cairo.cairo_save(cairo); if (!fullSelection) { Cairo.cairo_move_to(cairo, x, y); OS.pango_cairo_show_layout(cairo, layout); drawBorder(gc, x, y, null); } int[] ranges = new int[]{start, end}; int /*long*/ rgn = GDK.gdk_pango_layout_get_clip_region(layout, x, y, ranges, ranges.length / 2); if (rgn != 0) { GDK.gdk_cairo_region(cairo, rgn); Cairo.cairo_clip(cairo); if (GTK.GTK3) { Cairo.cairo_set_source_rgba(cairo, bg.handleRGBA.red, bg.handleRGBA.green, bg.handleRGBA.blue, bg.handleRGBA.alpha); } else { Cairo.cairo_set_source_rgba(cairo, (bg.handle.red & 0xFFFF) / (float)0xFFFF, (bg.handle.green & 0xFFFF) / (float)0xFFFF, (bg.handle.blue & 0xFFFF) / (float)0xFFFF, data.alpha / (float)0xFF); } Cairo.cairo_paint(cairo); GDK.gdk_region_destroy(rgn); } if (GTK.GTK3) { Cairo.cairo_set_source_rgba(cairo, fg.handleRGBA.red, fg.handleRGBA.green, fg.handleRGBA.blue, fg.handleRGBA.alpha); } else { Cairo.cairo_set_source_rgba(cairo, (fg.handle.red & 0xFFFF) / (float)0xFFFF, (fg.handle.green & 0xFFFF) / (float)0xFFFF, (fg.handle.blue & 0xFFFF) / (float)0xFFFF, data.alpha / (float)0xFF); } Cairo.cairo_move_to(cairo, x, y); OS.pango_layout_set_attributes(layout, selAttrList); OS.pango_cairo_show_layout(cairo, layout); OS.pango_layout_set_attributes(layout, attrList); drawBorder(gc, x, y, fg); Cairo.cairo_restore(cairo); } //Bug 477950: In order to support GTK2 and GTK3 colors simultaneously, this method's parameters //were modified to accept SWT Color objects instead of GdkColor structs. void drawBorder(GC gc, int x, int y, Color selectionColor) { GCData data = gc.data; int /*long*/ cairo = data.cairo; int /*long*/ ptr = OS.pango_layout_get_text(layout); Cairo.cairo_save(cairo); for (int i = 0; i < stylesCount - 1; i++) { TextStyle style = styles[i].style; if (style == null) continue; boolean drawBorder = style.borderStyle != SWT.NONE; if (drawBorder && !style.isAdherentBorder(styles[i+1].style)) { int start = styles[i].start; for (int j = i; j > 0 && style.isAdherentBorder(styles[j-1].style); j--) { start = styles[j - 1].start; } start = translateOffset(start); int end = translateOffset(styles[i+1].start - 1); int byteStart = (int)/*64*/(OS.g_utf16_offset_to_pointer(ptr, start) - ptr); int byteEnd = (int)/*64*/(OS.g_utf16_offset_to_pointer(ptr, end + 1) - ptr); int[] ranges = new int[]{byteStart, byteEnd}; int /*long*/ rgn = GDK.gdk_pango_layout_get_clip_region(layout, x, y, ranges, ranges.length / 2); if (rgn != 0) { int[] nRects = new int[1]; int /*long*/[] rects = new int /*long*/[1]; Region.gdk_region_get_rectangles(rgn, rects, nRects); GdkRectangle rect = new GdkRectangle(); GdkRGBA colorRGBA = null; GdkColor color = null; if (GTK.GTK3) { if (colorRGBA == null && style.borderColor != null) colorRGBA = style.borderColor.handleRGBA; if (colorRGBA == null && selectionColor != null) colorRGBA = selectionColor.handleRGBA; if (colorRGBA == null && style.foreground != null) colorRGBA = style.foreground.handleRGBA; if (colorRGBA == null) colorRGBA = data.foregroundRGBA; } else { if (color == null && style.borderColor != null) color = style.borderColor.handle; if (color == null && selectionColor != null) color = selectionColor.handle; if (color == null && style.foreground != null) color = style.foreground.handle; if (color == null) color = data.foreground; } int width = 1; float[] dashes = null; switch (style.borderStyle) { case SWT.BORDER_SOLID: break; case SWT.BORDER_DASH: dashes = width != 0 ? GC.LINE_DASH : GC.LINE_DASH_ZERO; break; case SWT.BORDER_DOT: dashes = width != 0 ? GC.LINE_DOT : GC.LINE_DOT_ZERO; break; } if (GTK.GTK3) { Cairo.cairo_set_source_rgba(cairo, colorRGBA.red, colorRGBA.green, colorRGBA.blue, colorRGBA.alpha); } else { Cairo.cairo_set_source_rgba(cairo, (color.red & 0xFFFF) / (float)0xFFFF, (color.green & 0xFFFF) / (float)0xFFFF, (color.blue & 0xFFFF) / (float)0xFFFF, data.alpha / (float)0xFF); } Cairo.cairo_set_line_width(cairo, width); if (dashes != null) { double[] cairoDashes = new double[dashes.length]; for (int j = 0; j < cairoDashes.length; j++) { cairoDashes[j] = width == 0 || data.lineStyle == SWT.LINE_CUSTOM ? dashes[j] : dashes[j] * width; } Cairo.cairo_set_dash(cairo, cairoDashes, cairoDashes.length, 0); } else { Cairo.cairo_set_dash(cairo, null, 0, 0); } for (int j=0; j- ERROR_NULL_ARGUMENT - if the gc is null
*SWT.LEFT SWT.CENTER
or *SWT.RIGHT
. * * @return the alignment used to positioned text horizontally * * @exception SWTException
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_INVALID_ARGUMENT - if the character offset is out of range *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed */ public int getLevel(int offset) { checkLayout(); computeRuns(); int length = text.length(); if (!(0 <= offset && offset <= length)) SWT.error(SWT.ERROR_INVALID_RANGE); offset = translateOffset(offset); int /*long*/ iter = OS.pango_layout_get_iter(layout); if (iter == 0) SWT.error(SWT.ERROR_NO_HANDLES); int level = 0; PangoItem item = new PangoItem(); PangoLayoutRun run = new PangoLayoutRun(); int /*long*/ ptr = OS.pango_layout_get_text(layout); int /*long*/ byteOffset = OS.g_utf16_offset_to_pointer(ptr, offset) - ptr; int strlen = C.strlen(ptr); byteOffset = Math.min(byteOffset, strlen); do { int /*long*/ runPtr = OS.pango_layout_iter_get_run(iter); if (runPtr != 0) { OS.memmove(run, runPtr, PangoLayoutRun.sizeof); OS.memmove(item, run.item, PangoItem.sizeof); if (item.offset <= byteOffset && byteOffset < item.offset + item.length) { level = item.analysis_level; break; } } } while (OS.pango_layout_iter_next_run(iter)); OS.pango_layout_iter_free(iter); return level; } /** * Returns the bounds of the line for the specified line index. * * @param lineIndex the line index * @return the line bounds * * @exception IllegalArgumentException
- ERROR_INVALID_ARGUMENT - if the line index is out of range *
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
- ERROR_INVALID_ARGUMENT - if the character offset is out of range *
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
- ERROR_INVALID_ARGUMENT - if the line index is out of range *
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
- ERROR_INVALID_ARGUMENT - if the offset is out of range *
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
- ERROR_INVALID_ARGUMENT - if the trailing length is less than
1
* - ERROR_NULL_ARGUMENT - if the point is null *
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
- ERROR_INVALID_ARGUMENT - if the trailing length is less than
1
* - ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
- ERROR_INVALID_ARGUMENT - if the offset is out of range *
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
-
*
-
*
-
*
-
*
-
*
-
*
-
*
trailing
argument indicates whether the offset
* corresponds to the leading or trailing edge of the cluster.
*
* @param offset the character offset
* @param trailing the trailing flag
* @return the location of the character offset
*
* @exception SWTException -
*
SWT.MOVEMENT_CHAR
,
* SWT.MOVEMENT_CLUSTER
, SWT.MOVEMENT_WORD
,
* SWT.MOVEMENT_WORD_END
or SWT.MOVEMENT_WORD_START
.
*
* @param offset the start offset
* @param movement the movement type
* @return the next offset
*
* @exception IllegalArgumentException -
*
-
*
-
*
-
*
-
*
-
*
-
*
SWT.MOVEMENT_CHAR
,
* SWT.MOVEMENT_CLUSTER
or SWT.MOVEMENT_WORD
,
* SWT.MOVEMENT_WORD_END
or SWT.MOVEMENT_WORD_START
.
*
* @param offset the start offset
* @param movement the movement type
* @return the previous offset
*
* @exception IllegalArgumentException -
*
-
*
TextStyle
.
*
* @return the ranges, an array of offsets representing the start and end of each
* text style.
*
* @exception SWTException -
*
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
null
if not set
*
* @exception IllegalArgumentException -
*
- ERROR_INVALID_ARGUMENT - if the character offset is out of range *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
true
if the text layout has been disposed,
* and false
otherwise.
* * This method gets the dispose state for the text layout. * When a text layout has been disposed, it is an error to * invoke any other method (except {@link #dispose()}) using the text layout. *
* * @returntrue
when the text layout is disposed and false
otherwise
*/
@Override
public boolean isDisposed () {
return layout == 0;
}
/**
* Sets the text alignment for the receiver. The alignment controls
* how a line of text is positioned horizontally. The argument should
* be one of SWT.LEFT
, SWT.RIGHT
or SWT.CENTER
.
*
* The default alignment is SWT.LEFT
. Note that the receiver's
* width must be set in order to use SWT.RIGHT
or SWT.CENTER
* alignment.
*
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-1
which means that the
* ascent is calculated from the line fonts.
*
* @param ascent the new ascent
*
* @exception IllegalArgumentException -
*
- ERROR_INVALID_ARGUMENT - if the ascent is less than
-1
*
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-1
which means that the
* descent is calculated from the line fonts.
*
* @param descent the new descent
*
* @exception IllegalArgumentException -
*
- ERROR_INVALID_ARGUMENT - if the descent is less than
-1
*
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_INVALID_ARGUMENT - if the font has been disposed *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
SWT.LEFT_TO_RIGHT
or SWT.RIGHT_TO_LEFT
.
*
* @param orientation new orientation style
*
* @exception SWTException -
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_INVALID_ARGUMENT - if the spacing is negative *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
* Each text segment is determined by two consecutive offsets in the
* segments
arrays. The first element of the array should
* always be zero and the last one should always be equals to length of
* the text.
*
* When segments characters are set, the segments are the offsets where * the characters are inserted in the text. *
* * @param segments the text segments offset * * @exception SWTException
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
TextLayout
.
*
* @param segmentsChars the segments characters
*
* @exception SWTException -
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
* Note: Setting the text also clears all the styles. This method * returns without doing anything if the new text is the same as * the current text. *
* * @param text the new text * * @exception IllegalArgumentException-
*
- ERROR_NULL_ARGUMENT - if the text is null *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
SWT.LEFT_TO_RIGHT
, SWT.RIGHT_TO_LEFT
* or SWT.AUTO_TEXT_DIRECTION
.
*
* * Warning: This API is currently only implemented on Windows. * It doesn't set the base text direction on GTK and Cocoa. *
* * @param textDirection the new text direction * * @exception SWTException-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-1
which means wrapping is disabled.
*
* @param width the new width
*
* @exception IllegalArgumentException -
*
- ERROR_INVALID_ARGUMENT - if the width is
0
or less than-1
*
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *
-
*
- ERROR_INVALID_ARGUMENT - if the tabLength is less than
0
*
-
*
- ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed *