diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/classpath/javax/swing/text/Utilities.java | |
download | cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.bz2 cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libjava/classpath/javax/swing/text/Utilities.java')
-rw-r--r-- | libjava/classpath/javax/swing/text/Utilities.java | 730 |
1 files changed, 730 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/text/Utilities.java b/libjava/classpath/javax/swing/text/Utilities.java new file mode 100644 index 000000000..622139232 --- /dev/null +++ b/libjava/classpath/javax/swing/text/Utilities.java @@ -0,0 +1,730 @@ +/* Utilities.java -- + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text; + +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Point; +import java.text.BreakIterator; + +import javax.swing.text.Position.Bias; + +/** + * A set of utilities to deal with text. This is used by several other classes + * inside this package. + * + * @author Roman Kennke (roman@ontographics.com) + * @author Robert Schuster (robertschuster@fsfe.org) + */ +public class Utilities +{ + + /** + * Creates a new <code>Utilities</code> object. + */ + public Utilities() + { + // Nothing to be done here. + } + + /** + * Draws the given text segment. Contained tabs and newline characters + * are taken into account. Tabs are expanded using the + * specified {@link TabExpander}. + * + * + * The X and Y coordinates denote the start of the <em>baseline</em> where + * the text should be drawn. + * + * @param s the text fragment to be drawn. + * @param x the x position for drawing. + * @param y the y position for drawing. + * @param g the {@link Graphics} context for drawing. + * @param e the {@link TabExpander} which specifies the Tab-expanding + * technique. + * @param startOffset starting offset in the text. + * @return the x coordinate at the end of the drawn text. + */ + public static final int drawTabbedText(Segment s, int x, int y, Graphics g, + TabExpander e, int startOffset) + { + // This buffers the chars to be drawn. + char[] buffer = s.array; + + // The font metrics of the current selected font. + FontMetrics metrics = g.getFontMetrics(); + + int ascent = metrics.getAscent(); + + // The current x and y pixel coordinates. + int pixelX = x; + + int pos = s.offset; + int len = 0; + + int end = s.offset + s.count; + + for (int offset = s.offset; offset < end; ++offset) + { + char c = buffer[offset]; + switch (c) + { + case '\t': + if (len > 0) { + g.drawChars(buffer, pos, len, pixelX, y); + pixelX += metrics.charsWidth(buffer, pos, len); + len = 0; + } + pos = offset+1; + if (e != null) + pixelX = (int) e.nextTabStop((float) pixelX, startOffset + offset + - s.offset); + else + pixelX += metrics.charWidth(' '); + x = pixelX; + break; + case '\n': + case '\r': + if (len > 0) { + g.drawChars(buffer, pos, len, pixelX, y); + pixelX += metrics.charsWidth(buffer, pos, len); + len = 0; + } + x = pixelX; + break; + default: + len += 1; + } + } + + if (len > 0) + { + g.drawChars(buffer, pos, len, pixelX, y); + pixelX += metrics.charsWidth(buffer, pos, len); + } + + return pixelX; + } + + /** + * Determines the width, that the given text <code>s</code> would take + * if it was printed with the given {@link java.awt.FontMetrics} on the + * specified screen position. + * @param s the text fragment + * @param metrics the font metrics of the font to be used + * @param x the x coordinate of the point at which drawing should be done + * @param e the {@link TabExpander} to be used + * @param startOffset the index in <code>s</code> where to start + * @returns the width of the given text s. This takes tabs and newlines + * into account. + */ + public static final int getTabbedTextWidth(Segment s, FontMetrics metrics, + int x, TabExpander e, + int startOffset) + { + // This buffers the chars to be drawn. + char[] buffer = s.array; + + // The current x coordinate. + int pixelX = x; + + // The current maximum width. + int maxWidth = 0; + + int end = s.offset + s.count; + int count = 0; + for (int offset = s.offset; offset < end; offset++) + { + switch (buffer[offset]) + { + case '\t': + // In case we have a tab, we just 'jump' over the tab. + // When we have no tab expander we just use the width of 'm'. + if (e != null) + pixelX = (int) e.nextTabStop(pixelX, + startOffset + offset - s.offset); + else + pixelX += metrics.charWidth(' '); + break; + case '\n': + // In case we have a newline, we must 'draw' + // the buffer and jump on the next line. + pixelX += metrics.charsWidth(buffer, offset - count, count); + count = 0; + break; + default: + count++; + } + } + + // Take the last line into account. + pixelX += metrics.charsWidth(buffer, end - count, count); + + return pixelX - x; + } + + /** + * Provides a facility to map screen coordinates into a model location. For a + * given text fragment and start location within this fragment, this method + * determines the model location so that the resulting fragment fits best + * into the span <code>[x0, x]</code>. + * + * The parameter <code>round</code> controls which model location is returned + * if the view coordinates are on a character: If <code>round</code> is + * <code>true</code>, then the result is rounded up to the next character, so + * that the resulting fragment is the smallest fragment that is larger than + * the specified span. If <code>round</code> is <code>false</code>, then the + * resulting fragment is the largest fragment that is smaller than the + * specified span. + * + * @param s the text segment + * @param fm the font metrics to use + * @param x0 the starting screen location + * @param x the target screen location at which the requested fragment should + * end + * @param te the tab expander to use; if this is <code>null</code>, TABs are + * expanded to one space character + * @param p0 the starting model location + * @param round if <code>true</code> round up to the next location, otherwise + * round down to the current location + * + * @return the model location, so that the resulting fragment fits within the + * specified span + */ + public static final int getTabbedTextOffset(Segment s, FontMetrics fm, int x0, + int x, TabExpander te, int p0, + boolean round) + { + int found = s.count; + int currentX = x0; + int nextX = currentX; + + int end = s.offset + s.count; + for (int pos = s.offset; pos < end && found == s.count; pos++) + { + char nextChar = s.array[pos]; + + if (nextChar != '\t') + nextX += fm.charWidth(nextChar); + else + { + if (te == null) + nextX += fm.charWidth(' '); + else + nextX += ((int) te.nextTabStop(nextX, p0 + pos - s.offset)); + } + + if (x >= currentX && x < nextX) + { + // Found position. + if ((! round) || ((x - currentX) < (nextX - x))) + { + found = pos - s.offset; + } + else + { + found = pos + 1 - s.offset; + } + } + currentX = nextX; + } + + return found; + } + + /** + * Provides a facility to map screen coordinates into a model location. For a + * given text fragment and start location within this fragment, this method + * determines the model location so that the resulting fragment fits best + * into the span <code>[x0, x]</code>. + * + * This method rounds up to the next location, so that the resulting fragment + * will be the smallest fragment of the text, that is greater than the + * specified span. + * + * @param s the text segment + * @param fm the font metrics to use + * @param x0 the starting screen location + * @param x the target screen location at which the requested fragment should + * end + * @param te the tab expander to use; if this is <code>null</code>, TABs are + * expanded to one space character + * @param p0 the starting model location + * + * @return the model location, so that the resulting fragment fits within the + * specified span + */ + public static final int getTabbedTextOffset(Segment s, FontMetrics fm, int x0, + int x, TabExpander te, int p0) + { + return getTabbedTextOffset(s, fm, x0, x, te, p0, true); + } + + /** + * Finds the start of the next word for the given offset. + * + * @param c + * the text component + * @param offs + * the offset in the document + * @return the location in the model of the start of the next word. + * @throws BadLocationException + * if the offset is invalid. + */ + public static final int getNextWord(JTextComponent c, int offs) + throws BadLocationException + { + if (offs < 0 || offs > (c.getText().length() - 1)) + throw new BadLocationException("invalid offset specified", offs); + String text = c.getText(); + BreakIterator wb = BreakIterator.getWordInstance(); + wb.setText(text); + + int last = wb.following(offs); + int current = wb.next(); + int cp; + + while (current != BreakIterator.DONE) + { + for (int i = last; i < current; i++) + { + cp = text.codePointAt(i); + + // Return the last found bound if there is a letter at the current + // location or is not whitespace (meaning it is a number or + // punctuation). The first case means that 'last' denotes the + // beginning of a word while the second case means it is the start + // of something else. + if (Character.isLetter(cp) + || !Character.isWhitespace(cp)) + return last; + } + last = current; + current = wb.next(); + } + + throw new BadLocationException("no more words", offs); + } + + /** + * Finds the start of the previous word for the given offset. + * + * @param c + * the text component + * @param offs + * the offset in the document + * @return the location in the model of the start of the previous word. + * @throws BadLocationException + * if the offset is invalid. + */ + public static final int getPreviousWord(JTextComponent c, int offs) + throws BadLocationException + { + String text = c.getText(); + + if (offs <= 0 || offs > text.length()) + throw new BadLocationException("invalid offset specified", offs); + + BreakIterator wb = BreakIterator.getWordInstance(); + wb.setText(text); + int last = wb.preceding(offs); + int current = wb.previous(); + int cp; + + while (current != BreakIterator.DONE) + { + for (int i = last; i < offs; i++) + { + cp = text.codePointAt(i); + + // Return the last found bound if there is a letter at the current + // location or is not whitespace (meaning it is a number or + // punctuation). The first case means that 'last' denotes the + // beginning of a word while the second case means it is the start + // of some else. + if (Character.isLetter(cp) + || !Character.isWhitespace(cp)) + return last; + } + last = current; + current = wb.previous(); + } + + return 0; + } + + /** + * Finds the start of a word for the given location. + * @param c the text component + * @param offs the offset location + * @return the location of the word beginning + * @throws BadLocationException if the offset location is invalid + */ + public static final int getWordStart(JTextComponent c, int offs) + throws BadLocationException + { + String text = c.getText(); + + if (offs < 0 || offs > text.length()) + throw new BadLocationException("invalid offset specified", offs); + + BreakIterator wb = BreakIterator.getWordInstance(); + wb.setText(text); + + if (wb.isBoundary(offs)) + return offs; + + return wb.preceding(offs); + } + + /** + * Finds the end of a word for the given location. + * @param c the text component + * @param offs the offset location + * @return the location of the word end + * @throws BadLocationException if the offset location is invalid + */ + public static final int getWordEnd(JTextComponent c, int offs) + throws BadLocationException + { + if (offs < 0 || offs >= c.getText().length()) + throw new BadLocationException("invalid offset specified", offs); + + String text = c.getText(); + BreakIterator wb = BreakIterator.getWordInstance(); + wb.setText(text); + return wb.following(offs); + } + + /** + * Get the model position of the end of the row that contains the + * specified model position. Return null if the given JTextComponent + * does not have a size. + * @param c the JTextComponent + * @param offs the model position + * @return the model position of the end of the row containing the given + * offset + * @throws BadLocationException if the offset is invalid + */ + public static final int getRowEnd(JTextComponent c, int offs) + throws BadLocationException + { + String text = c.getText(); + if (text == null) + return -1; + + // Do a binary search for the smallest position X > offs + // such that that character at positino X is not on the same + // line as the character at position offs + int high = offs + ((text.length() - 1 - offs) / 2); + int low = offs; + int oldHigh = text.length() + 1; + while (true) + { + if (c.modelToView(high).y != c.modelToView(offs).y) + { + oldHigh = high; + high = low + ((high + 1 - low) / 2); + if (oldHigh == high) + return high - 1; + } + else + { + low = high; + high += ((oldHigh - high) / 2); + if (low == high) + return low; + } + } + } + + /** + * Get the model position of the start of the row that contains the specified + * model position. Return null if the given JTextComponent does not have a + * size. + * + * @param c the JTextComponent + * @param offs the model position + * @return the model position of the start of the row containing the given + * offset + * @throws BadLocationException if the offset is invalid + */ + public static final int getRowStart(JTextComponent c, int offs) + throws BadLocationException + { + String text = c.getText(); + if (text == null) + return -1; + + // Do a binary search for the greatest position X < offs + // such that the character at position X is not on the same + // row as the character at position offs + int high = offs; + int low = 0; + int oldLow = 0; + while (true) + { + if (c.modelToView(low).y != c.modelToView(offs).y) + { + oldLow = low; + low = high - ((high + 1 - low) / 2); + if (oldLow == low) + return low + 1; + } + else + { + high = low; + low -= ((low - oldLow) / 2); + if (low == high) + return low; + } + } + } + + /** + * Determine where to break the text in the given Segment, attempting to find + * a word boundary. + * @param s the Segment that holds the text + * @param metrics the font metrics used for calculating the break point + * @param x0 starting view location representing the start of the text + * @param x the target view location + * @param e the TabExpander used for expanding tabs (if this is null tabs + * are expanded to 1 space) + * @param startOffset the offset in the Document of the start of the text + * @return the offset at which we should break the text + */ + public static final int getBreakLocation(Segment s, FontMetrics metrics, + int x0, int x, TabExpander e, + int startOffset) + { + int mark = Utilities.getTabbedTextOffset(s, metrics, x0, x, e, startOffset, + false); + int breakLoc = mark; + // If mark is equal to the end of the string, just use that position. + if (mark < s.count - 1) + { + for (int i = s.offset + mark; i >= s.offset; i--) + { + char ch = s.array[i]; + if (ch < 256) + { + // For ASCII simply scan backwards for whitespace. + if (Character.isWhitespace(ch)) + { + breakLoc = i - s.offset + 1; + break; + } + } + else + { + // Only query BreakIterator for complex chars. + BreakIterator bi = BreakIterator.getLineInstance(); + bi.setText(s); + int pos = bi.preceding(i + 1); + if (pos > s.offset) + { + breakLoc = breakLoc - s.offset; + } + break; + } + } + } + return breakLoc; + } + + /** + * Returns the paragraph element in the text component <code>c</code> at + * the specified location <code>offset</code>. + * + * @param c the text component + * @param offset the offset of the paragraph element to return + * + * @return the paragraph element at <code>offset</code> + */ + public static final Element getParagraphElement(JTextComponent c, int offset) + { + Document doc = c.getDocument(); + Element par = null; + if (doc instanceof StyledDocument) + { + StyledDocument styledDoc = (StyledDocument) doc; + par = styledDoc.getParagraphElement(offset); + } + else + { + Element root = c.getDocument().getDefaultRootElement(); + int parIndex = root.getElementIndex(offset); + par = root.getElement(parIndex); + } + return par; + } + + /** + * Returns the document position that is closest above to the specified x + * coordinate in the row containing <code>offset</code>. + * + * @param c the text component + * @param offset the offset + * @param x the x coordinate + * + * @return the document position that is closest above to the specified x + * coordinate in the row containing <code>offset</code> + * + * @throws BadLocationException if <code>offset</code> is not a valid offset + */ + public static final int getPositionAbove(JTextComponent c, int offset, int x) + throws BadLocationException + { + int offs = getRowStart(c, offset); + + if(offs == -1) + return -1; + + // Effectively calculates the y value of the previous line. + Point pt = c.modelToView(offs-1).getLocation(); + + pt.x = x; + + // Calculate a simple fitting offset. + offs = c.viewToModel(pt); + + // Find out the real x positions of the calculated character and its + // neighbour. + int offsX = c.modelToView(offs).getLocation().x; + int offsXNext = c.modelToView(offs+1).getLocation().x; + + // Chose the one which is nearer to us and return its offset. + if (Math.abs(offsX-x) <= Math.abs(offsXNext-x)) + return offs; + else + return offs+1; + } + + /** + * Returns the document position that is closest below to the specified x + * coordinate in the row containing <code>offset</code>. + * + * @param c the text component + * @param offset the offset + * @param x the x coordinate + * + * @return the document position that is closest above to the specified x + * coordinate in the row containing <code>offset</code> + * + * @throws BadLocationException if <code>offset</code> is not a valid offset + */ + public static final int getPositionBelow(JTextComponent c, int offset, int x) + throws BadLocationException + { + int offs = getRowEnd(c, offset); + + if(offs == -1) + return -1; + + Point pt = null; + + // Note: Some views represent the position after the last + // typed character others do not. Converting offset 3 in "a\nb" + // in a PlainView will return a valid rectangle while in a + // WrappedPlainView this will throw a BadLocationException. + // This behavior has been observed in the RI. + try + { + // Effectively calculates the y value of the next line. + pt = c.modelToView(offs+1).getLocation(); + } + catch(BadLocationException ble) + { + return offset; + } + + pt.x = x; + + // Calculate a simple fitting offset. + offs = c.viewToModel(pt); + + if (offs == c.getDocument().getLength()) + return offs; + + // Find out the real x positions of the calculated character and its + // neighbour. + int offsX = c.modelToView(offs).getLocation().x; + int offsXNext = c.modelToView(offs+1).getLocation().x; + + // Chose the one which is nearer to us and return its offset. + if (Math.abs(offsX-x) <= Math.abs(offsXNext-x)) + return offs; + else + return offs+1; + } + + /** This is an internal helper method which is used by the + * <code>javax.swing.text</code> package. It simply delegates the + * call to a method with the same name on the <code>NavigationFilter</code> + * of the provided <code>JTextComponent</code> (if it has one) or its UI. + * + * If the underlying method throws a <code>BadLocationException</code> it + * will be swallowed and the initial offset is returned. + */ + static int getNextVisualPositionFrom(JTextComponent t, int offset, int direction) + { + NavigationFilter nf = t.getNavigationFilter(); + + try + { + return (nf != null) + ? nf.getNextVisualPositionFrom(t, + offset, + Bias.Forward, + direction, + new Position.Bias[1]) + : t.getUI().getNextVisualPositionFrom(t, + offset, + Bias.Forward, + direction, + new Position.Bias[1]); + } + catch (BadLocationException ble) + { + return offset; + } + + } + +} |