/*
 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package javax.swing.text;

import java.awt.Component;
import java.awt.Rectangle;
import java.awt.Graphics;
import java.awt.FontMetrics;
import java.awt.Shape;
import java.awt.Graphics2D;
import java.awt.font.TextAttribute;
import java.awt.geom.Rectangle2D;

import java.text.*;
import javax.swing.JComponent;
import javax.swing.SwingConstants;
import javax.swing.text.ParagraphView.Row;
import sun.swing.SwingUtilities2;
import static sun.swing.SwingUtilities2.drawChars;
import static sun.swing.SwingUtilities2.getFontCharWidth;
import static sun.swing.SwingUtilities2.getFontCharsWidth;

A collection of methods to deal with various text related activities.
Author: Timothy Prinzing
/** * A collection of methods to deal with various text * related activities. * * @author Timothy Prinzing */
public class Utilities {
If view's container is a JComponent it is returned, after casting.
/** * If <code>view</code>'s container is a <code>JComponent</code> it * is returned, after casting. */
static JComponent getJComponent(View view) { if (view != null) { Component component = view.getContainer(); if (component instanceof JComponent) { return (JComponent)component; } } return null; }
Draws the given text, expanding any tabs that are contained using the given tab expansion technique. This particular implementation renders in a 1.1 style coordinate system where ints are used and 72dpi is assumed.
Params:
  • s – the source of the text
  • x – the X origin >= 0
  • y – the Y origin >= 0
  • g – the graphics context
  • e – how to expand the tabs. If this value is null, tabs will be expanded as a space character.
  • startOffset – starting offset of the text in the document >= 0
Returns: the X location at the end of the rendered text
Deprecated:replaced by drawTabbedText(Segment, float, float, Graphics2D, TabExpander, int)
/** * Draws the given text, expanding any tabs that are contained * using the given tab expansion technique. This particular * implementation renders in a 1.1 style coordinate system * where ints are used and 72dpi is assumed. * * @param s the source of the text * @param x the X origin &gt;= 0 * @param y the Y origin &gt;= 0 * @param g the graphics context * @param e how to expand the tabs. If this value is null, * tabs will be expanded as a space character. * @param startOffset starting offset of the text in the document &gt;= 0 * @return the X location at the end of the rendered text * * @deprecated replaced by * {@link #drawTabbedText(Segment, float, float, Graphics2D, TabExpander, int)} */
@Deprecated(since = "9") public static final int drawTabbedText(Segment s, int x, int y, Graphics g, TabExpander e, int startOffset) { return drawTabbedText(null, s, x, y, g, e, startOffset); }
Draws the given text, expanding any tabs that are contained using the given tab expansion technique.
Params:
  • s – the source of the text
  • x – the X origin >= 0
  • y – the Y origin >= 0
  • g – the graphics context
  • e – how to expand the tabs. If this value is null, tabs will be expanded as a space character.
  • startOffset – starting offset of the text in the document >= 0
Returns: the X location at the end of the rendered text
Since:9
/** * Draws the given text, expanding any tabs that are contained * using the given tab expansion technique. * * @param s the source of the text * @param x the X origin {@code >= 0} * @param y the Y origin {@code >= 0} * @param g the graphics context * @param e how to expand the tabs. If this value is null, * tabs will be expanded as a space character. * @param startOffset starting offset of the text in the document {@code >= 0} * @return the X location at the end of the rendered text * * @since 9 */
public static final float drawTabbedText(Segment s, float x, float y, Graphics2D g, TabExpander e, int startOffset) { return drawTabbedText(null, s, x, y, g, e, startOffset, null, true); }
Draws the given text, expanding any tabs that are contained using the given tab expansion technique. This particular implementation renders in a 1.1 style coordinate system where ints are used and 72dpi is assumed.
Params:
  • view – View requesting rendering, may be null.
  • s – the source of the text
  • x – the X origin >= 0
  • y – the Y origin >= 0
  • g – the graphics context
  • e – how to expand the tabs. If this value is null, tabs will be expanded as a space character.
  • startOffset – starting offset of the text in the document >= 0
Returns: the X location at the end of the rendered text
/** * Draws the given text, expanding any tabs that are contained * using the given tab expansion technique. This particular * implementation renders in a 1.1 style coordinate system * where ints are used and 72dpi is assumed. * * @param view View requesting rendering, may be null. * @param s the source of the text * @param x the X origin &gt;= 0 * @param y the Y origin &gt;= 0 * @param g the graphics context * @param e how to expand the tabs. If this value is null, * tabs will be expanded as a space character. * @param startOffset starting offset of the text in the document &gt;= 0 * @return the X location at the end of the rendered text */
static final int drawTabbedText(View view, Segment s, int x, int y, Graphics g, TabExpander e, int startOffset) { return drawTabbedText(view, s, x, y, g, e, startOffset, null); } // In addition to the previous method it can extend spaces for // justification. // // all params are the same as in the preious method except the last // one: // @param justificationData justificationData for the row. // if null not justification is needed static final int drawTabbedText(View view, Segment s, int x, int y, Graphics g, TabExpander e, int startOffset, int [] justificationData) { return (int) drawTabbedText(view, s, x, y, g, e, startOffset, justificationData, false); } static final float drawTabbedText(View view, Segment s, float x, float y, Graphics g, TabExpander e, int startOffset, int [] justificationData, boolean useFPAPI) { JComponent component = getJComponent(view); FontMetrics metrics = SwingUtilities2.getFontMetrics(component, g); float nextX = x; char[] txt = s.array; int txtOffset = s.offset; int flushLen = 0; int flushIndex = s.offset; int spaceAddon = 0; int spaceAddonLeftoverEnd = -1; int startJustifiableContent = 0; int endJustifiableContent = 0; if (justificationData != null) { int offset = - startOffset + txtOffset; View parent = null; if (view != null && (parent = view.getParent()) != null) { offset += parent.getStartOffset(); } spaceAddon = justificationData[Row.SPACE_ADDON]; spaceAddonLeftoverEnd = justificationData[Row.SPACE_ADDON_LEFTOVER_END] + offset; startJustifiableContent = justificationData[Row.START_JUSTIFIABLE] + offset; endJustifiableContent = justificationData[Row.END_JUSTIFIABLE] + offset; } int n = s.offset + s.count; for (int i = txtOffset; i < n; i++) { if (txt[i] == '\t' || ((spaceAddon != 0 || i <= spaceAddonLeftoverEnd) && (txt[i] == ' ') && startJustifiableContent <= i && i <= endJustifiableContent )) { if (flushLen > 0) { nextX = drawChars(component, g, txt, flushIndex, flushLen, x, y); flushLen = 0; } flushIndex = i + 1; if (txt[i] == '\t') { if (e != null) { nextX = e.nextTabStop(nextX, startOffset + i - txtOffset); } else { nextX += getFontCharWidth(' ', metrics, useFPAPI); } } else if (txt[i] == ' ') { float spaceWidth = getFontCharWidth(' ', metrics, useFPAPI); nextX += spaceWidth + spaceAddon; if (i <= spaceAddonLeftoverEnd) { nextX++; } } x = nextX; } else if ((txt[i] == '\n') || (txt[i] == '\r')) { if (flushLen > 0) { nextX = drawChars(component, g, txt, flushIndex, flushLen, x, y, useFPAPI); flushLen = 0; } flushIndex = i + 1; x = nextX; } else { flushLen += 1; } } if (flushLen > 0) { nextX = drawChars(component, g,txt, flushIndex, flushLen, x, y, useFPAPI); } return nextX; }
Determines the width of the given segment of text taking tabs into consideration. This is implemented in a 1.1 style coordinate system where ints are used and 72dpi is assumed.
Params:
  • s – the source of the text
  • metrics – the font metrics to use for the calculation
  • x – the X origin >= 0
  • e – how to expand the tabs. If this value is null, tabs will be expanded as a space character.
  • startOffset – starting offset of the text in the document >= 0
Returns: the width of the text
Deprecated:replaced by getTabbedTextWidth(Segment, FontMetrics, float, TabExpander, int)
/** * Determines the width of the given segment of text taking tabs * into consideration. This is implemented in a 1.1 style coordinate * system where ints are used and 72dpi is assumed. * * @param s the source of the text * @param metrics the font metrics to use for the calculation * @param x the X origin &gt;= 0 * @param e how to expand the tabs. If this value is null, * tabs will be expanded as a space character. * @param startOffset starting offset of the text in the document &gt;= 0 * @return the width of the text * * @deprecated replaced by * {@link #getTabbedTextWidth(Segment, FontMetrics, float, TabExpander, int)} */
@Deprecated(since = "9") public static final int getTabbedTextWidth(Segment s, FontMetrics metrics, int x, TabExpander e, int startOffset) { return getTabbedTextWidth(null, s, metrics, x, e, startOffset, null); }
Determines the width of the given segment of text taking tabs into consideration.
Params:
  • s – the source of the text
  • metrics – the font metrics to use for the calculation
  • x – the X origin >= 0
  • e – how to expand the tabs. If this value is null, tabs will be expanded as a space character.
  • startOffset – starting offset of the text in the document >= 0
Returns: the width of the text
Since:9
/** * Determines the width of the given segment of text taking tabs * into consideration. * * @param s the source of the text * @param metrics the font metrics to use for the calculation * @param x the X origin {@code >= 0} * @param e how to expand the tabs. If this value is null, * tabs will be expanded as a space character. * @param startOffset starting offset of the text in the document {@code >= 0} * @return the width of the text * * @since 9 */
public static final float getTabbedTextWidth(Segment s, FontMetrics metrics, float x, TabExpander e, int startOffset) { return getTabbedTextWidth(null, s, metrics, x, e, startOffset, null); } // In addition to the previous method it can extend spaces for // justification. // // all params are the same as in the preious method except the last // one: // @param justificationData justificationData for the row. // if null not justification is needed static final int getTabbedTextWidth(View view, Segment s, FontMetrics metrics, int x, TabExpander e, int startOffset, int[] justificationData) { return (int) getTabbedTextWidth(view, s, metrics, x, e, startOffset, justificationData, false); } static final float getTabbedTextWidth(View view, Segment s, FontMetrics metrics, float x, TabExpander e, int startOffset, int[] justificationData) { return getTabbedTextWidth(view, s, metrics, x, e, startOffset, justificationData, true); } static final float getTabbedTextWidth(View view, Segment s, FontMetrics metrics, float x, TabExpander e, int startOffset, int[] justificationData, boolean useFPAPI) { float nextX = x; char[] txt = s.array; int txtOffset = s.offset; int n = s.offset + s.count; int charCount = 0; int spaceAddon = 0; int spaceAddonLeftoverEnd = -1; int startJustifiableContent = 0; int endJustifiableContent = 0; if (justificationData != null) { int offset = - startOffset + txtOffset; View parent = null; if (view != null && (parent = view.getParent()) != null) { offset += parent.getStartOffset(); } spaceAddon = justificationData[Row.SPACE_ADDON]; spaceAddonLeftoverEnd = justificationData[Row.SPACE_ADDON_LEFTOVER_END] + offset; startJustifiableContent = justificationData[Row.START_JUSTIFIABLE] + offset; endJustifiableContent = justificationData[Row.END_JUSTIFIABLE] + offset; } for (int i = txtOffset; i < n; i++) { if (txt[i] == '\t' || ((spaceAddon != 0 || i <= spaceAddonLeftoverEnd) && (txt[i] == ' ') && startJustifiableContent <= i && i <= endJustifiableContent )) { nextX += metrics.charsWidth(txt, i-charCount, charCount); charCount = 0; if (txt[i] == '\t') { if (e != null) { nextX = e.nextTabStop(nextX, startOffset + i - txtOffset); } else { nextX += getFontCharWidth(' ', metrics, useFPAPI); } } else if (txt[i] == ' ') { float spaceWidth = getFontCharWidth(' ', metrics, useFPAPI); nextX += spaceWidth + spaceAddon; if (i <= spaceAddonLeftoverEnd) { nextX++; } } } else if(txt[i] == '\n') { // Ignore newlines, they take up space and we shouldn't be // counting them. nextX += getFontCharsWidth(txt, i - charCount, charCount, metrics, useFPAPI); charCount = 0; } else { charCount++; } } nextX += getFontCharsWidth(txt, n - charCount, charCount, metrics, useFPAPI); return nextX - x; }
Determines the relative offset into the given text that best represents the given span in the view coordinate system. This is implemented in a 1.1 style coordinate system where ints are used and 72dpi is assumed.
Params:
  • s – the source of the text
  • metrics – the font metrics to use for the calculation
  • x0 – the starting view location representing the start of the given text >= 0.
  • x – the target view location to translate to an offset into the text >= 0.
  • e – how to expand the tabs. If this value is null, tabs will be expanded as a space character.
  • startOffset – starting offset of the text in the document >= 0
Returns: the offset into the text >= 0
Deprecated:replaced by getTabbedTextOffset(Segment, FontMetrics, float, float, TabExpander, int, boolean)
/** * Determines the relative offset into the given text that * best represents the given span in the view coordinate * system. This is implemented in a 1.1 style coordinate * system where ints are used and 72dpi is assumed. * * @param s the source of the text * @param metrics the font metrics to use for the calculation * @param x0 the starting view location representing the start * of the given text &gt;= 0. * @param x the target view location to translate to an * offset into the text &gt;= 0. * @param e how to expand the tabs. If this value is null, * tabs will be expanded as a space character. * @param startOffset starting offset of the text in the document &gt;= 0 * @return the offset into the text &gt;= 0 * * @deprecated replaced by * {@link #getTabbedTextOffset(Segment, FontMetrics, float, float, * TabExpander, int, boolean)} */
@Deprecated(since = "9") public static final int getTabbedTextOffset(Segment s, FontMetrics metrics, int x0, int x, TabExpander e, int startOffset) { return getTabbedTextOffset(s, metrics, x0, x, e, startOffset, true); } static final int getTabbedTextOffset(View view, Segment s, FontMetrics metrics, int x0, int x, TabExpander e, int startOffset, int[] justificationData) { return getTabbedTextOffset(view, s, metrics, x0, x, e, startOffset, true, justificationData, false); }
Determines the relative offset into the given text that best represents the given span in the view coordinate system.
Params:
  • s – the source of the text
  • metrics – the font metrics to use for the calculation
  • x0 – the starting view location representing the start of the given text >= 0.
  • x – the target view location to translate to an offset into the text >= 0.
  • e – how to expand the tabs. If this value is null, tabs will be expanded as a space character.
  • startOffset – starting offset of the text in the document >= 0
  • round – whether or not to round
Returns: the offset into the text >= 0
Deprecated:replaced by getTabbedTextOffset(Segment, FontMetrics, float, float, TabExpander, int, boolean)
/** * Determines the relative offset into the given text that * best represents the given span in the view coordinate * system. * * @param s the source of the text * @param metrics the font metrics to use for the calculation * @param x0 the starting view location representing the start * of the given text &gt;= 0. * @param x the target view location to translate to an * offset into the text &gt;= 0. * @param e how to expand the tabs. If this value is null, * tabs will be expanded as a space character. * @param startOffset starting offset of the text in the document &gt;= 0 * @param round whether or not to round * @return the offset into the text &gt;= 0 * * @deprecated replaced by * {@link #getTabbedTextOffset(Segment, FontMetrics, float, float, * TabExpander, int, boolean)} */
@Deprecated(since = "9") public static final int getTabbedTextOffset(Segment s, FontMetrics metrics, int x0, int x, TabExpander e, int startOffset, boolean round) { return getTabbedTextOffset(null, s, metrics, x0, x, e, startOffset, round, null, false); }
Determines the relative offset into the given text that best represents the given span in the view coordinate system.
Params:
  • s – the source of the text
  • metrics – the font metrics to use for the calculation
  • x0 – the starting view location representing the start of the given text >= 0.
  • x – the target view location to translate to an offset into the text >= 0.
  • e – how to expand the tabs. If this value is null, tabs will be expanded as a space character.
  • startOffset – starting offset of the text in the document >= 0
  • round – whether or not to round
Returns: the offset into the text >= 0
Since:9
/** * Determines the relative offset into the given text that * best represents the given span in the view coordinate * system. * * @param s the source of the text * @param metrics the font metrics to use for the calculation * @param x0 the starting view location representing the start * of the given text {@code >= 0}. * @param x the target view location to translate to an * offset into the text {@code >= 0}. * @param e how to expand the tabs. If this value is null, * tabs will be expanded as a space character. * @param startOffset starting offset of the text in the document {@code >= 0} * @param round whether or not to round * @return the offset into the text {@code >= 0} * * @since 9 */
public static final int getTabbedTextOffset(Segment s, FontMetrics metrics, float x0, float x, TabExpander e, int startOffset, boolean round) { return getTabbedTextOffset(null, s, metrics, x0, x, e, startOffset, round, null, true); } // In addition to the previous method it can extend spaces for // justification. // // all params are the same as in the preious method except the last // one: // @param justificationData justificationData for the row. // if null not justification is needed static final int getTabbedTextOffset(View view, Segment s, FontMetrics metrics, float x0, float x, TabExpander e, int startOffset, boolean round, int[] justificationData, boolean useFPAPI) { if (x0 >= x) { // x before x0, return. return 0; } float nextX = x0; // s may be a shared segment, so it is copied prior to calling // the tab expander char[] txt = s.array; int txtOffset = s.offset; int txtCount = s.count; int spaceAddon = 0 ; int spaceAddonLeftoverEnd = -1; int startJustifiableContent = 0 ; int endJustifiableContent = 0; if (justificationData != null) { int offset = - startOffset + txtOffset; View parent = null; if (view != null && (parent = view.getParent()) != null) { offset += parent.getStartOffset(); } spaceAddon = justificationData[Row.SPACE_ADDON]; spaceAddonLeftoverEnd = justificationData[Row.SPACE_ADDON_LEFTOVER_END] + offset; startJustifiableContent = justificationData[Row.START_JUSTIFIABLE] + offset; endJustifiableContent = justificationData[Row.END_JUSTIFIABLE] + offset; } int n = s.offset + s.count; for (int i = s.offset; i < n; i++) { if (txt[i] == '\t' || ((spaceAddon != 0 || i <= spaceAddonLeftoverEnd) && (txt[i] == ' ') && startJustifiableContent <= i && i <= endJustifiableContent )){ if (txt[i] == '\t') { if (e != null) { nextX = e.nextTabStop(nextX, startOffset + i - txtOffset); } else { nextX += getFontCharWidth(' ', metrics, useFPAPI); } } else if (txt[i] == ' ') { nextX += getFontCharWidth(' ', metrics, useFPAPI); nextX += spaceAddon; if (i <= spaceAddonLeftoverEnd) { nextX++; } } } else { nextX += getFontCharWidth(txt[i], metrics, useFPAPI); } if (x < nextX) { // found the hit position... return the appropriate side int offset; // the length of the string measured as a whole may differ from // the sum of individual character lengths, for example if // fractional metrics are enabled; and we must guard from this. if (round) { offset = i + 1 - txtOffset; float width = getFontCharsWidth(txt, txtOffset, offset, metrics, useFPAPI); float span = x - x0; if (span < width) { while (offset > 0) { float charsWidth = getFontCharsWidth(txt, txtOffset, offset - 1, metrics, useFPAPI); float nextWidth = offset > 1 ? charsWidth : 0; if (span >= nextWidth) { if (span - nextWidth < width - span) { offset--; } break; } width = nextWidth; offset--; } } } else { offset = i - txtOffset; while (offset > 0 && getFontCharsWidth(txt, txtOffset, offset, metrics, useFPAPI) > (x - x0)) { offset--; } } return offset; } } // didn't find, return end offset return txtCount; }
Determine where to break the given text to fit within the given span. This tries to find a word boundary.
Params:
  • s – the source of the text
  • metrics – the font metrics to use for the calculation
  • x0 – the starting view location representing the start of the given text.
  • x – the target view location to translate to an offset into the text.
  • e – how to expand the tabs. If this value is null, tabs will be expanded as a space character.
  • startOffset – starting offset in the document of the text
Returns: the offset into the given text
Deprecated:replaced by getBreakLocation(Segment, FontMetrics, float, float, TabExpander, int)
/** * Determine where to break the given text to fit * within the given span. This tries to find a word boundary. * @param s the source of the text * @param metrics the font metrics to use for the calculation * @param x0 the starting view location representing the start * of the given text. * @param x the target view location to translate to an * offset into the text. * @param e how to expand the tabs. If this value is null, * tabs will be expanded as a space character. * @param startOffset starting offset in the document of the text * @return the offset into the given text * * @deprecated replaced by * {@link #getBreakLocation(Segment, FontMetrics, float, float, * TabExpander, int)} */
@Deprecated(since = "9") public static final int getBreakLocation(Segment s, FontMetrics metrics, int x0, int x, TabExpander e, int startOffset) { return getBreakLocation(s, metrics, x0, x, e, startOffset, false); } static final int getBreakLocation(Segment s, FontMetrics metrics, float x0, float x, TabExpander e, int startOffset, boolean useFPIAPI) { char[] txt = s.array; int txtOffset = s.offset; int txtCount = s.count; int index = getTabbedTextOffset(null, s, metrics, x0, x, e, startOffset, false, null, useFPIAPI); if (index >= txtCount - 1) { return txtCount; } for (int i = txtOffset + index; i >= txtOffset; i--) { char ch = txt[i]; if (ch < 256) { // break on whitespace if (Character.isWhitespace(ch)) { index = i - txtOffset + 1; break; } } else { // a multibyte char found; use BreakIterator to find line break BreakIterator bit = BreakIterator.getLineInstance(); bit.setText(s); int breakPos = bit.preceding(i + 1); if (breakPos > txtOffset) { index = breakPos - txtOffset; } break; } } return index; }
Determine where to break the given text to fit within the given span. This tries to find a word boundary.
Params:
  • s – the source of the text
  • metrics – the font metrics to use for the calculation
  • x0 – the starting view location representing the start of the given text.
  • x – the target view location to translate to an offset into the text.
  • e – how to expand the tabs. If this value is null, tabs will be expanded as a space character.
  • startOffset – starting offset in the document of the text
Returns: the offset into the given text
Since:9
/** * Determine where to break the given text to fit * within the given span. This tries to find a word boundary. * @param s the source of the text * @param metrics the font metrics to use for the calculation * @param x0 the starting view location representing the start * of the given text. * @param x the target view location to translate to an * offset into the text. * @param e how to expand the tabs. If this value is null, * tabs will be expanded as a space character. * @param startOffset starting offset in the document of the text * @return the offset into the given text * * @since 9 */
public static final int getBreakLocation(Segment s, FontMetrics metrics, float x0, float x, TabExpander e, int startOffset) { return getBreakLocation(s, metrics, x0, x, e, startOffset, false); }
Determines the starting row model position of the row that contains the specified model position. The component given must have a size to compute the result. If the component doesn't have a size a value of -1 will be returned.
Params:
  • c – the editor
  • offs – the offset in the document >= 0
Throws:
Returns:the position >= 0 if the request can be computed, otherwise a value of -1 will be returned.
/** * Determines the starting row model position of the row that contains * the specified model position. The component given must have a * size to compute the result. If the component doesn't have a size * a value of -1 will be returned. * * @param c the editor * @param offs the offset in the document &gt;= 0 * @return the position &gt;= 0 if the request can be computed, otherwise * a value of -1 will be returned. * @exception BadLocationException if the offset is out of range */
@SuppressWarnings("deprecation") public static final int getRowStart(JTextComponent c, int offs) throws BadLocationException { Rectangle r = c.modelToView(offs); if (r == null) { return -1; } int lastOffs = offs; int y = r.y; while ((r != null) && (y == r.y)) { // Skip invisible elements if(r.height !=0) { offs = lastOffs; } lastOffs -= 1; r = (lastOffs >= 0) ? c.modelToView(lastOffs) : null; } return offs; }
Determines the ending row model position of the row that contains the specified model position. The component given must have a size to compute the result. If the component doesn't have a size a value of -1 will be returned.
Params:
  • c – the editor
  • offs – the offset in the document >= 0
Throws:
Returns:the position >= 0 if the request can be computed, otherwise a value of -1 will be returned.
/** * Determines the ending row model position of the row that contains * the specified model position. The component given must have a * size to compute the result. If the component doesn't have a size * a value of -1 will be returned. * * @param c the editor * @param offs the offset in the document &gt;= 0 * @return the position &gt;= 0 if the request can be computed, otherwise * a value of -1 will be returned. * @exception BadLocationException if the offset is out of range */
@SuppressWarnings("deprecation") public static final int getRowEnd(JTextComponent c, int offs) throws BadLocationException { Rectangle2D r = c.modelToView2D(offs); if (r == null) { return -1; } int n = c.getDocument().getLength(); int lastOffs = offs; double y = r.getY(); while ((r != null) && (y == r.getY())) { // Skip invisible elements if (r.getHeight() !=0) { offs = lastOffs; } lastOffs += 1; r = (lastOffs <= n) ? c.modelToView(lastOffs) : null; } return offs; }
Determines the position in the model that is closest to the given view location in the row above. The component given must have a size to compute the result. If the component doesn't have a size a value of -1 will be returned.
Params:
  • c – the editor
  • offs – the offset in the document >= 0
  • x – the X coordinate >= 0
Throws:
Returns:the position >= 0 if the request can be computed, otherwise a value of -1 will be returned.
Deprecated:replaced by getPositionAbove(JTextComponent, int, float)
/** * Determines the position in the model that is closest to the given * view location in the row above. The component given must have a * size to compute the result. If the component doesn't have a size * a value of -1 will be returned. * * @param c the editor * @param offs the offset in the document &gt;= 0 * @param x the X coordinate &gt;= 0 * @return the position &gt;= 0 if the request can be computed, otherwise * a value of -1 will be returned. * @exception BadLocationException if the offset is out of range * * @deprecated replaced by * {@link #getPositionAbove(JTextComponent, int, float)} */
@Deprecated(since = "9") public static final int getPositionAbove(JTextComponent c, int offs, int x) throws BadLocationException { return getPositionAbove(c, offs, x, false); } @SuppressWarnings("deprecation") static final int getPositionAbove(JTextComponent c, int offs, float x, boolean useFPAPI) throws BadLocationException { int lastOffs = getRowStart(c, offs) - 1; if (lastOffs < 0) { return -1; } double bestSpan = Integer.MAX_VALUE; double y = 0; Rectangle2D r = null; if (lastOffs >= 0) { r = useFPAPI ? c.modelToView2D(lastOffs) : c.modelToView(lastOffs); y = r.getY(); } while ((r != null) && (y == r.getY())) { double span = Math.abs(r.getX() - x); if (span < bestSpan) { offs = lastOffs; bestSpan = span; } lastOffs -= 1; if ((lastOffs >= 0)) { r = useFPAPI ? c.modelToView2D(lastOffs) : c.modelToView(lastOffs); } else { r = null; } } return offs; }
Determines the position in the model that is closest to the given view location in the row above. The component given must have a size to compute the result. If the component doesn't have a size a value of -1 will be returned.
Params:
  • c – the editor
  • offs – the offset in the document >= 0
  • x – the X coordinate >= 0
Throws:
Returns:the position >= 0 if the request can be computed, otherwise a value of -1 will be returned.
Since:9
/** * Determines the position in the model that is closest to the given * view location in the row above. The component given must have a * size to compute the result. If the component doesn't have a size * a value of -1 will be returned. * * @param c the editor * @param offs the offset in the document {@code >= 0} * @param x the X coordinate {@code >= 0} * @return the position {@code >= 0} if the request can be computed, otherwise * a value of -1 will be returned. * @exception BadLocationException if the offset is out of range * * @since 9 */
public static final int getPositionAbove(JTextComponent c, int offs, float x) throws BadLocationException { return getPositionAbove(c, offs, x, true); }
Determines the position in the model that is closest to the given view location in the row below. The component given must have a size to compute the result. If the component doesn't have a size a value of -1 will be returned.
Params:
  • c – the editor
  • offs – the offset in the document >= 0
  • x – the X coordinate >= 0
Throws:
Returns:the position >= 0 if the request can be computed, otherwise a value of -1 will be returned.
Deprecated:replaced by getPositionBelow(JTextComponent, int, float)
/** * Determines the position in the model that is closest to the given * view location in the row below. The component given must have a * size to compute the result. If the component doesn't have a size * a value of -1 will be returned. * * @param c the editor * @param offs the offset in the document &gt;= 0 * @param x the X coordinate &gt;= 0 * @return the position &gt;= 0 if the request can be computed, otherwise * a value of -1 will be returned. * @exception BadLocationException if the offset is out of range * * @deprecated replaced by * {@link #getPositionBelow(JTextComponent, int, float)} */
@Deprecated(since = "9") public static final int getPositionBelow(JTextComponent c, int offs, int x) throws BadLocationException { return getPositionBelow(c, offs, x, false); } @SuppressWarnings("deprecation") static final int getPositionBelow(JTextComponent c, int offs, float x, boolean useFPAPI) throws BadLocationException { int lastOffs = getRowEnd(c, offs) + 1; if (lastOffs <= 0) { return -1; } double bestSpan = Integer.MAX_VALUE; int n = c.getDocument().getLength(); double y = 0; Rectangle2D r = null; if (lastOffs <= n) { r = useFPAPI ? c.modelToView2D(lastOffs) : c.modelToView(lastOffs); y = r.getY(); } while ((r != null) && (y == r.getY())) { double span = Math.abs(x - r.getX()); if (span < bestSpan) { offs = lastOffs; bestSpan = span; } lastOffs += 1; if (lastOffs <= n) { r = useFPAPI ? c.modelToView2D(lastOffs) : c.modelToView(lastOffs); } else { r = null; } } return offs; }
Determines the position in the model that is closest to the given view location in the row below. The component given must have a size to compute the result. If the component doesn't have a size a value of -1 will be returned.
Params:
  • c – the editor
  • offs – the offset in the document >= 0
  • x – the X coordinate >= 0
Throws:
Returns:the position >= 0 if the request can be computed, otherwise a value of -1 will be returned.
Since:9
/** * Determines the position in the model that is closest to the given * view location in the row below. The component given must have a * size to compute the result. If the component doesn't have a size * a value of -1 will be returned. * * @param c the editor * @param offs the offset in the document {@code >= 0} * @param x the X coordinate {@code >= 0} * @return the position {@code >= 0} if the request can be computed, otherwise * a value of -1 will be returned. * @exception BadLocationException if the offset is out of range * * @since 9 */
public static final int getPositionBelow(JTextComponent c, int offs, float x) throws BadLocationException { return getPositionBelow(c, offs, x, true); }
Determines the start of a word for the given model location. Uses BreakIterator.getWordInstance() to actually get the words.
Params:
  • c – the editor
  • offs – the offset in the document >= 0
Throws:
Returns:the location in the model of the word start >= 0
/** * Determines the start of a word for the given model location. * Uses BreakIterator.getWordInstance() to actually get the words. * * @param c the editor * @param offs the offset in the document &gt;= 0 * @return the location in the model of the word start &gt;= 0 * @exception BadLocationException if the offset is out of range */
public static final int getWordStart(JTextComponent c, int offs) throws BadLocationException { Document doc = c.getDocument(); Element line = getParagraphElement(c, offs); if (line == null) { throw new BadLocationException("No word at " + offs, offs); } int lineStart = line.getStartOffset(); int lineEnd = Math.min(line.getEndOffset(), doc.getLength()); Segment seg = SegmentCache.getSharedSegment(); doc.getText(lineStart, lineEnd - lineStart, seg); if(seg.count > 0) { BreakIterator words = BreakIterator.getWordInstance(c.getLocale()); words.setText(seg); int wordPosition = seg.offset + offs - lineStart; if(wordPosition >= words.last()) { wordPosition = words.last() - 1; } words.following(wordPosition); offs = lineStart + words.previous() - seg.offset; } SegmentCache.releaseSharedSegment(seg); return offs; }
Determines the end of a word for the given location. Uses BreakIterator.getWordInstance() to actually get the words.
Params:
  • c – the editor
  • offs – the offset in the document >= 0
Throws:
Returns:the location in the model of the word end >= 0
/** * Determines the end of a word for the given location. * Uses BreakIterator.getWordInstance() to actually get the words. * * @param c the editor * @param offs the offset in the document &gt;= 0 * @return the location in the model of the word end &gt;= 0 * @exception BadLocationException if the offset is out of range */
public static final int getWordEnd(JTextComponent c, int offs) throws BadLocationException { Document doc = c.getDocument(); Element line = getParagraphElement(c, offs); if (line == null) { throw new BadLocationException("No word at " + offs, offs); } int lineStart = line.getStartOffset(); int lineEnd = Math.min(line.getEndOffset(), doc.getLength()); Segment seg = SegmentCache.getSharedSegment(); doc.getText(lineStart, lineEnd - lineStart, seg); if(seg.count > 0) { BreakIterator words = BreakIterator.getWordInstance(c.getLocale()); words.setText(seg); int wordPosition = offs - lineStart + seg.offset; if(wordPosition >= words.last()) { wordPosition = words.last() - 1; } offs = lineStart + words.following(wordPosition) - seg.offset; } SegmentCache.releaseSharedSegment(seg); return offs; }
Determines the start of the next word for the given location. Uses BreakIterator.getWordInstance() to actually get the words.
Params:
  • c – the editor
  • offs – the offset in the document >= 0
Throws:
Returns:the location in the model of the word start >= 0
/** * Determines the start of the next word for the given location. * Uses BreakIterator.getWordInstance() to actually get the words. * * @param c the editor * @param offs the offset in the document &gt;= 0 * @return the location in the model of the word start &gt;= 0 * @exception BadLocationException if the offset is out of range */
public static final int getNextWord(JTextComponent c, int offs) throws BadLocationException { int nextWord; Element line = getParagraphElement(c, offs); for (nextWord = getNextWordInParagraph(c, line, offs, false); nextWord == BreakIterator.DONE; nextWord = getNextWordInParagraph(c, line, offs, true)) { // didn't find in this line, try the next line offs = line.getEndOffset(); line = getParagraphElement(c, offs); } return nextWord; }
Finds the next word in the given elements text. The first parameter allows searching multiple paragraphs where even the first offset is desired. Returns the offset of the next word, or BreakIterator.DONE if there are no more words in the element.
/** * Finds the next word in the given elements text. The first * parameter allows searching multiple paragraphs where even * the first offset is desired. * Returns the offset of the next word, or BreakIterator.DONE * if there are no more words in the element. */
static int getNextWordInParagraph(JTextComponent c, Element line, int offs, boolean first) throws BadLocationException { if (line == null) { throw new BadLocationException("No more words", offs); } Document doc = line.getDocument(); int lineStart = line.getStartOffset(); int lineEnd = Math.min(line.getEndOffset(), doc.getLength()); if ((offs >= lineEnd) || (offs < lineStart)) { throw new BadLocationException("No more words", offs); } Segment seg = SegmentCache.getSharedSegment(); doc.getText(lineStart, lineEnd - lineStart, seg); BreakIterator words = BreakIterator.getWordInstance(c.getLocale()); words.setText(seg); if ((first && (words.first() == (seg.offset + offs - lineStart))) && (! Character.isWhitespace(seg.array[words.first()]))) { return offs; } int wordPosition = words.following(seg.offset + offs - lineStart); if ((wordPosition == BreakIterator.DONE) || (wordPosition >= seg.offset + seg.count)) { // there are no more words on this line. return BreakIterator.DONE; } // if we haven't shot past the end... check to // see if the current boundary represents whitespace. // if so, we need to try again char ch = seg.array[wordPosition]; if (! Character.isWhitespace(ch)) { return lineStart + wordPosition - seg.offset; } // it was whitespace, try again. The assumption // is that it must be a word start if the last // one had whitespace following it. wordPosition = words.next(); if (wordPosition != BreakIterator.DONE) { offs = lineStart + wordPosition - seg.offset; if (offs != lineEnd) { return offs; } } SegmentCache.releaseSharedSegment(seg); return BreakIterator.DONE; }
Determine the start of the prev word for the given location. Uses BreakIterator.getWordInstance() to actually get the words.
Params:
  • c – the editor
  • offs – the offset in the document >= 0
Throws:
Returns:the location in the model of the word start >= 0
/** * Determine the start of the prev word for the given location. * Uses BreakIterator.getWordInstance() to actually get the words. * * @param c the editor * @param offs the offset in the document &gt;= 0 * @return the location in the model of the word start &gt;= 0 * @exception BadLocationException if the offset is out of range */
public static final int getPreviousWord(JTextComponent c, int offs) throws BadLocationException { int prevWord; Element line = getParagraphElement(c, offs); for (prevWord = getPrevWordInParagraph(c, line, offs); prevWord == BreakIterator.DONE; prevWord = getPrevWordInParagraph(c, line, offs)) { // didn't find in this line, try the prev line offs = line.getStartOffset() - 1; line = getParagraphElement(c, offs); } return prevWord; }
Finds the previous word in the given elements text. The first parameter allows searching multiple paragraphs where even the first offset is desired. Returns the offset of the next word, or BreakIterator.DONE if there are no more words in the element.
/** * Finds the previous word in the given elements text. The first * parameter allows searching multiple paragraphs where even * the first offset is desired. * Returns the offset of the next word, or BreakIterator.DONE * if there are no more words in the element. */
static int getPrevWordInParagraph(JTextComponent c, Element line, int offs) throws BadLocationException { if (line == null) { throw new BadLocationException("No more words", offs); } Document doc = line.getDocument(); int lineStart = line.getStartOffset(); int lineEnd = line.getEndOffset(); if ((offs > lineEnd) || (offs < lineStart)) { throw new BadLocationException("No more words", offs); } Segment seg = SegmentCache.getSharedSegment(); doc.getText(lineStart, lineEnd - lineStart, seg); BreakIterator words = BreakIterator.getWordInstance(c.getLocale()); words.setText(seg); if (words.following(seg.offset + offs - lineStart) == BreakIterator.DONE) { words.last(); } int wordPosition = words.previous(); if (wordPosition == (seg.offset + offs - lineStart)) { wordPosition = words.previous(); } if (wordPosition == BreakIterator.DONE) { // there are no more words on this line. return BreakIterator.DONE; } // if we haven't shot past the end... check to // see if the current boundary represents whitespace. // if so, we need to try again char ch = seg.array[wordPosition]; if (! Character.isWhitespace(ch)) { return lineStart + wordPosition - seg.offset; } // it was whitespace, try again. The assumption // is that it must be a word start if the last // one had whitespace following it. wordPosition = words.previous(); if (wordPosition != BreakIterator.DONE) { return lineStart + wordPosition - seg.offset; } SegmentCache.releaseSharedSegment(seg); return BreakIterator.DONE; }
Determines the element to use for a paragraph/line.
Params:
  • c – the editor
  • offs – the starting offset in the document >= 0
Returns:the element
/** * Determines the element to use for a paragraph/line. * * @param c the editor * @param offs the starting offset in the document &gt;= 0 * @return the element */
public static final Element getParagraphElement(JTextComponent c, int offs) { Document doc = c.getDocument(); if (doc instanceof StyledDocument) { return ((StyledDocument)doc).getParagraphElement(offs); } Element map = doc.getDefaultRootElement(); int index = map.getElementIndex(offs); Element paragraph = map.getElement(index); if ((offs >= paragraph.getStartOffset()) && (offs < paragraph.getEndOffset())) { return paragraph; } return null; } static boolean isComposedTextElement(Document doc, int offset) { Element elem = doc.getDefaultRootElement(); while (!elem.isLeaf()) { elem = elem.getElement(elem.getElementIndex(offset)); } return isComposedTextElement(elem); } static boolean isComposedTextElement(Element elem) { AttributeSet as = elem.getAttributes(); return isComposedTextAttributeDefined(as); } static boolean isComposedTextAttributeDefined(AttributeSet as) { return ((as != null) && (as.isDefined(StyleConstants.ComposedTextAttribute))); }
Draws the given composed text passed from an input method.
Params:
  • view – View hosting text
  • attr – the attributes containing the composed text
  • g – the graphics context
  • x – the X origin
  • y – the Y origin
  • p0 – starting offset in the composed text to be rendered
  • p1 – ending offset in the composed text to be rendered
Returns: the new insertion position
/** * Draws the given composed text passed from an input method. * * @param view View hosting text * @param attr the attributes containing the composed text * @param g the graphics context * @param x the X origin * @param y the Y origin * @param p0 starting offset in the composed text to be rendered * @param p1 ending offset in the composed text to be rendered * @return the new insertion position */
static int drawComposedText(View view, AttributeSet attr, Graphics g, int x, int y, int p0, int p1) throws BadLocationException { return (int) drawComposedText(view, attr, g, x, y, p0, p1, false); } static float drawComposedText(View view, AttributeSet attr, Graphics g, float x, float y, int p0, int p1) throws BadLocationException { return drawComposedText(view, attr, g, x, y, p0, p1, true); } static float drawComposedText(View view, AttributeSet attr, Graphics g, float x, float y, int p0, int p1, boolean useFPAPI) throws BadLocationException { Graphics2D g2d = (Graphics2D)g; AttributedString as = (AttributedString)attr.getAttribute( StyleConstants.ComposedTextAttribute); as.addAttribute(TextAttribute.FONT, g.getFont()); if (p0 >= p1) return x; AttributedCharacterIterator aci = as.getIterator(null, p0, p1); return x + SwingUtilities2.drawString(getJComponent(view), g2d, aci, x, y); }
Paints the composed text in a GlyphView
/** * Paints the composed text in a GlyphView */
static void paintComposedText(Graphics g, Rectangle alloc, GlyphView v) { if (g instanceof Graphics2D) { Graphics2D g2d = (Graphics2D) g; int p0 = v.getStartOffset(); int p1 = v.getEndOffset(); AttributeSet attrSet = v.getElement().getAttributes(); AttributedString as = (AttributedString)attrSet.getAttribute(StyleConstants.ComposedTextAttribute); int start = v.getElement().getStartOffset(); int y = alloc.y + alloc.height - (int)v.getGlyphPainter().getDescent(v); int x = alloc.x; //Add text attributes as.addAttribute(TextAttribute.FONT, v.getFont()); as.addAttribute(TextAttribute.FOREGROUND, v.getForeground()); if (StyleConstants.isBold(v.getAttributes())) { as.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD); } if (StyleConstants.isItalic(v.getAttributes())) { as.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE); } if (v.isUnderline()) { as.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); } if (v.isStrikeThrough()) { as.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON); } if (v.isSuperscript()) { as.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER); } if (v.isSubscript()) { as.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB); } // draw AttributedCharacterIterator aci = as.getIterator(null, p0 - start, p1 - start); SwingUtilities2.drawString(getJComponent(v), g2d,aci,x,y); } } /* * Convenience function for determining ComponentOrientation. Helps us * avoid having Munge directives throughout the code. */ static boolean isLeftToRight( java.awt.Component c ) { return c.getComponentOrientation().isLeftToRight(); }
Provides a way to determine the next visually represented model location that one might place a caret. Some views may not be visible, they might not be in the same order found in the model, or they just might not allow access to some of the locations in the model.

This implementation assumes the views are layed out in a logical manner. That is, that the view at index x + 1 is visually after the View at index x, and that the View at index x - 1 is visually before the View at x. There is support for reversing this behavior only if the passed in View is an instance of CompositeView. The CompositeView must then override the flipEastAndWestAtEnds method.

Params:
  • v – View to query
  • pos – the position to convert >= 0
  • alloc – the allocated region to render into
  • direction – the direction from the current position that can be thought of as the arrow keys typically found on a keyboard; this may be one of the following:
    • SwingConstants.WEST
    • SwingConstants.EAST
    • SwingConstants.NORTH
    • SwingConstants.SOUTH
  • biasRet – an array contain the bias that was checked
Throws:
Returns:the location within the model that best represents the next location visual position
/** * Provides a way to determine the next visually represented model * location that one might place a caret. Some views may not be visible, * they might not be in the same order found in the model, or they just * might not allow access to some of the locations in the model. * <p> * This implementation assumes the views are layed out in a logical * manner. That is, that the view at index x + 1 is visually after * the View at index x, and that the View at index x - 1 is visually * before the View at x. There is support for reversing this behavior * only if the passed in <code>View</code> is an instance of * <code>CompositeView</code>. The <code>CompositeView</code> * must then override the <code>flipEastAndWestAtEnds</code> method. * * @param v View to query * @param pos the position to convert &gt;= 0 * @param alloc the allocated region to render into * @param direction the direction from the current position that can * be thought of as the arrow keys typically found on a keyboard; * this may be one of the following: * <ul> * <li><code>SwingConstants.WEST</code> * <li><code>SwingConstants.EAST</code> * <li><code>SwingConstants.NORTH</code> * <li><code>SwingConstants.SOUTH</code> * </ul> * @param biasRet an array contain the bias that was checked * @return the location within the model that best represents the next * location visual position * @exception BadLocationException * @exception IllegalArgumentException if <code>direction</code> is invalid */
static int getNextVisualPositionFrom(View v, int pos, Position.Bias b, Shape alloc, int direction, Position.Bias[] biasRet) throws BadLocationException { if (v.getViewCount() == 0) { // Nothing to do. return pos; } boolean top = (direction == SwingConstants.NORTH || direction == SwingConstants.WEST); int retValue; if (pos == -1) { // Start from the first View. int childIndex = (top) ? v.getViewCount() - 1 : 0; View child = v.getView(childIndex); Shape childBounds = v.getChildAllocation(childIndex, alloc); retValue = child.getNextVisualPositionFrom(pos, b, childBounds, direction, biasRet); if (retValue == -1 && !top && v.getViewCount() > 1) { // Special case that should ONLY happen if first view // isn't valid (can happen when end position is put at // beginning of line. child = v.getView(1); childBounds = v.getChildAllocation(1, alloc); retValue = child.getNextVisualPositionFrom(-1, biasRet[0], childBounds, direction, biasRet); } } else { int increment = (top) ? -1 : 1; int childIndex; if (b == Position.Bias.Backward && pos > 0) { childIndex = v.getViewIndex(pos - 1, Position.Bias.Forward); } else { childIndex = v.getViewIndex(pos, Position.Bias.Forward); } View child = v.getView(childIndex); Shape childBounds = v.getChildAllocation(childIndex, alloc); retValue = child.getNextVisualPositionFrom(pos, b, childBounds, direction, biasRet); if ((direction == SwingConstants.EAST || direction == SwingConstants.WEST) && (v instanceof CompositeView) && ((CompositeView)v).flipEastAndWestAtEnds(pos, b)) { increment *= -1; } childIndex += increment; if (retValue == -1 && childIndex >= 0 && childIndex < v.getViewCount()) { child = v.getView(childIndex); childBounds = v.getChildAllocation(childIndex, alloc); retValue = child.getNextVisualPositionFrom( -1, b, childBounds, direction, biasRet); // If there is a bias change, it is a fake position // and we should skip it. This is usually the result // of two elements side be side flowing the same way. if (retValue == pos && biasRet[0] != b) { return getNextVisualPositionFrom(v, pos, biasRet[0], alloc, direction, biasRet); } } else if (retValue != -1 && biasRet[0] != b && ((increment == 1 && child.getEndOffset() == retValue) || (increment == -1 && child.getStartOffset() == retValue)) && childIndex >= 0 && childIndex < v.getViewCount()) { // Reached the end of a view, make sure the next view // is a different direction. child = v.getView(childIndex); childBounds = v.getChildAllocation(childIndex, alloc); Position.Bias originalBias = biasRet[0]; int nextPos = child.getNextVisualPositionFrom( -1, b, childBounds, direction, biasRet); if (biasRet[0] == b) { retValue = nextPos; } else { biasRet[0] = originalBias; } } } return retValue; } }