?? textlayout.java
字號:
/******************************************************************************* * Copyright (c) 2000, 2004 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/package org.eclipse.swt.graphics;import org.eclipse.swt.internal.*;import org.eclipse.swt.internal.win32.*;import org.eclipse.swt.*;/** * <code>TextLayout</code> is a graphic object that represents * styled text. *<p> * 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. * </p> * * <p> * Application code must explicitly invoke the <code>TextLayout#dispose()</code> * method to release the operating system resources managed by each instance * when those instances are no longer required. * </p> * * @since 3.0 */public final class TextLayout { Device device; Font font; String text, segmentsText; int lineSpacing; int ascent, descent; int alignment; int wrapWidth; int orientation; int[] tabs; int[] segments; StyleItem[] styles; StyleItem[] allRuns; StyleItem[][] runs; int[] lineOffset, lineY, lineWidth; static final char LTR_MARK = '\u200E', RTL_MARK = '\u200F'; static final int SCRIPT_VISATTR_SIZEOF = 2; static final int GOFFSET_SIZEOF = 8; static class StyleItem { TextStyle style; int start, length; boolean lineBreak, softBreak, tab; /*Script cache and analysis */ SCRIPT_ANALYSIS analysis; int psc = 0; /*Shape info (malloc when the run is shaped) */ int glyphs; int glyphCount; int clusters; int visAttrs; /*Place info (malloc when the run is placed) */ int advances; int goffsets; int width; int ascent; int descent; /* ScriptBreak */ int psla; int fallbackFont; void free() { int hHeap = OS.GetProcessHeap(); if (psc != 0) { OS.ScriptFreeCache (psc); OS.HeapFree(hHeap, 0, psc); psc = 0; } if (glyphs != 0) { OS.HeapFree(hHeap, 0, glyphs); glyphs = 0; glyphCount = 0; } if (clusters != 0) { OS.HeapFree(hHeap, 0, clusters); clusters = 0; } if (visAttrs != 0) { OS.HeapFree(hHeap, 0, visAttrs); visAttrs = 0; } if (advances != 0) { OS.HeapFree(hHeap, 0, advances); advances = 0; } if (goffsets != 0) { OS.HeapFree(hHeap, 0, goffsets); goffsets = 0; } if (psla != 0) { OS.HeapFree(hHeap, 0, psla); psla = 0; } if (fallbackFont != 0) { OS.DeleteObject(fallbackFont); fallbackFont = 0; } width = ascent = descent = 0; lineBreak = softBreak = false; } }/** * Constructs a new instance of this class on the given device. * <p> * You must dispose the text layout when it is no longer required. * </p> * * @param device the device on which to allocate the text layout * * @exception IllegalArgumentException <ul> * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li> * </ul> * * @see #dispose() */public TextLayout (Device device) { if (device == null) device = Device.getDevice(); if (device == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); this.device = device; wrapWidth = ascent = descent = -1; lineSpacing = 0; orientation = SWT.LEFT_TO_RIGHT; styles = new StyleItem[2]; styles[0] = new StyleItem(); styles[1] = new StyleItem(); text = ""; //$NON-NLS-1$ if (device.tracking) device.new_Object(this);}void breakRun(StyleItem run) { if (run.psla != 0) return; char[] chars = new char[run.length]; segmentsText.getChars(run.start, run.start + run.length, chars, 0); int hHeap = OS.GetProcessHeap(); run.psla = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, SCRIPT_LOGATTR.sizeof * chars.length); OS.ScriptBreak(chars, chars.length, run.analysis, run.psla);}void checkLayout () { if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);}/* * Compute the runs: itemize, shape, place, and reorder the runs.* Break paragraphs into lines, wraps the text, and initialize caches.*/void computeRuns (GC gc) { if (runs != null) return; int hDC = gc != null ? gc.handle : device.internal_new_GC(null); int srcHdc = OS.CreateCompatibleDC(hDC); allRuns = itemize(); for (int i=0; i<allRuns.length - 1; i++) { StyleItem run = allRuns[i]; OS.SelectObject(srcHdc, getItemFont(run)); shape(srcHdc, run); } SCRIPT_LOGATTR logAttr = new SCRIPT_LOGATTR(); SCRIPT_PROPERTIES properties = new SCRIPT_PROPERTIES(); int lineWidth = 0, lineStart = 0, lineCount = 1; for (int i=0; i<allRuns.length - 1; i++) { StyleItem run = allRuns[i]; if (run.length == 1) { char ch = segmentsText.charAt(run.start); switch (ch) { case '\t': { run.tab = true; if (tabs == null) break; int tabsLength = tabs.length, j; for (j = 0; j < tabsLength; j++) { if (tabs[j] > lineWidth) { run.width = tabs[j] - lineWidth; break; } } if (j == tabsLength) { int tabX = tabs[tabsLength-1]; int lastTabWidth = tabsLength > 1 ? tabs[tabsLength-1] - tabs[tabsLength-2] : tabs[0]; if (lastTabWidth > 0) { while (tabX <= lineWidth) tabX += lastTabWidth; run.width = tabX - lineWidth; } } break; } case '\n': { run.lineBreak = true; break; } case '\r': { run.lineBreak = true; StyleItem next = allRuns[i + 1]; if (next.length != 0 && segmentsText.charAt(next.start) == '\n') { run.length += 1; next.free(); i++; } break; } } } if (wrapWidth != -1 && lineWidth + run.width > wrapWidth && !run.tab) { int start = 0; int[] piDx = new int[run.length]; OS.ScriptGetLogicalWidths(run.analysis, run.length, run.glyphCount, run.advances, run.clusters, run.visAttrs, piDx); int width = 0, maxWidth = wrapWidth - lineWidth; while (width + piDx[start] < maxWidth) { width += piDx[start++]; } int firstStart = start; int firstIndice = i; while (i >= lineStart) { breakRun(run); while (start >= 0) { OS.MoveMemory(logAttr, run.psla + (start * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof); if (logAttr.fSoftBreak || logAttr.fWhiteSpace) break; start--; } /* * Bug in Windows. For some reason Uniscribe sets the fSoftBreak flag for the first letter * after a letter with an accent. This cause a break line to be set in the middle of a word. * The fix is to detect the case and ignore fSoftBreak forcing the algorithm keep searching. */ if (start == 0 && i != lineStart && !run.tab) { if (logAttr.fSoftBreak && !logAttr.fWhiteSpace) { OS.MoveMemory(properties, device.scripts[run.analysis.eScript], SCRIPT_PROPERTIES.sizeof); int langID = properties.langid; StyleItem pRun = allRuns[i - 1]; OS.MoveMemory(properties, device.scripts[pRun.analysis.eScript], SCRIPT_PROPERTIES.sizeof); if (properties.langid == langID || langID == OS.LANG_NEUTRAL || properties.langid == OS.LANG_NEUTRAL) { breakRun(pRun); OS.MoveMemory(logAttr, pRun.psla + ((pRun.length - 1) * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof); if (!logAttr.fWhiteSpace) start = -1; } } } if (start >= 0 || i == lineStart) break; run = allRuns[--i]; start = run.length - 1; } if (start == 0 && i != lineStart && !run.tab) { run = allRuns[--i]; } else if (start <= 0 && i == lineStart) { i = firstIndice; run = allRuns[i]; start = Math.max(1, firstStart); } breakRun(run); while (start < run.length) { OS.MoveMemory(logAttr, run.psla + (start * SCRIPT_LOGATTR.sizeof), SCRIPT_LOGATTR.sizeof); if (!logAttr.fWhiteSpace) break; start++; } if (0 < start && start < run.length) { StyleItem newRun = new StyleItem(); newRun.start = run.start + start; newRun.length = run.length - start; newRun.style = run.style; newRun.analysis = run.analysis; run.free(); run.length = start; OS.SelectObject(srcHdc, getItemFont(run)); shape (srcHdc, run); shape (srcHdc, newRun); StyleItem[] newAllRuns = new StyleItem[allRuns.length + 1]; System.arraycopy(allRuns, 0, newAllRuns, 0, i + 1); System.arraycopy(allRuns, i + 1, newAllRuns, i + 2, allRuns.length - i - 1); allRuns = newAllRuns; allRuns[i + 1] = newRun; } if (i != allRuns.length - 2) { run.softBreak = run.lineBreak = true; } } lineWidth += run.width; if (run.lineBreak) { lineStart = i + 1; lineWidth = 0; lineCount++; } } lineWidth = 0; runs = new StyleItem[lineCount][]; lineOffset = new int[lineCount + 1]; lineY = new int[lineCount + 1]; this.lineWidth = new int[lineCount]; int lineRunCount = 0, line = 0; int ascent = Math.max(0, this.ascent); int descent = Math.max(0, this.descent); StyleItem[] lineRuns = new StyleItem[allRuns.length]; for (int i=0; i<allRuns.length; i++) { StyleItem run = allRuns[i]; lineRuns[lineRunCount++] = run; lineWidth += run.width; ascent = Math.max(ascent, run.ascent); descent = Math.max(descent, run.descent); if (run.lineBreak || i == allRuns.length - 1) { /* Update the run metrics if the last run is a hard break. */ if (lineRunCount == 1 && i == allRuns.length - 1) { TEXTMETRIC lptm = OS.IsUnicode ? (TEXTMETRIC)new TEXTMETRICW() : new TEXTMETRICA(); OS.SelectObject(srcHdc, getItemFont(run)); OS.GetTextMetrics(srcHdc, lptm); run.ascent = lptm.tmAscent; run.descent = lptm.tmDescent; ascent = Math.max(ascent, run.ascent); descent = Math.max(descent, run.descent); } runs[line] = new StyleItem[lineRunCount]; System.arraycopy(lineRuns, 0, runs[line], 0, lineRunCount); StyleItem lastRun = runs[line][lineRunCount - 1]; runs[line] = reorder(runs[line]); this.lineWidth[line] = lineWidth; line++; lineY[line] = lineY[line - 1] + ascent + descent + lineSpacing; lineOffset[line] = lastRun.start + lastRun.length; lineRunCount = lineWidth = 0; ascent = Math.max(0, this.ascent); descent = Math.max(0, this.descent); } } if (srcHdc != 0) OS.DeleteDC(srcHdc); if (gc == 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. */public void dispose () { if (device == null) return; freeRuns(); font = null; text = null; segmentsText = null; tabs = null; styles = null; runs = null; lineOffset = null; lineY = null; lineWidth = null; if (device.tracking) device.dispose_Object(this); device = 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 * * @exception SWTException <ul> * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li> * </ul> */public void draw (GC gc, int x, int y) { draw(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 <ul>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -