diff options
Diffstat (limited to 'libjava/classpath/javax/swing/text/PlainView.java')
-rw-r--r-- | libjava/classpath/javax/swing/text/PlainView.java | 371 |
1 files changed, 338 insertions, 33 deletions
diff --git a/libjava/classpath/javax/swing/text/PlainView.java b/libjava/classpath/javax/swing/text/PlainView.java index 91d7547e77c..9f5ee8ad3c8 100644 --- a/libjava/classpath/javax/swing/text/PlainView.java +++ b/libjava/classpath/javax/swing/text/PlainView.java @@ -46,15 +46,35 @@ import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; -public class PlainView extends View - implements TabExpander +import javax.swing.SwingConstants; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentEvent.ElementChange; + +public class PlainView extends View implements TabExpander { Color selectedColor; Color unselectedColor; + + /** + * The color that is used to draw disabled text fields. + */ + Color disabledColor; + Font font; + /** The length of the longest line in the Document **/ + float maxLineLength = -1; + + /** The longest line in the Document **/ + Element longestLine = null; + protected FontMetrics metrics; + /** + * The instance returned by {@link #getLineBuffer()}. + */ + private transient Segment lineBuffer; + public PlainView(Element elem) { super(elem); @@ -104,7 +124,7 @@ public class PlainView extends View // Get the rectangle for position. Element line = getElement().getElement(lineIndex); int lineStart = line.getStartOffset(); - Segment segment = new Segment(); + Segment segment = getLineBuffer(); document.getText(lineStart, position - lineStart, segment); int xoffset = Utilities.getTabbedTextWidth(segment, metrics, rect.x, this, lineStart); @@ -129,7 +149,9 @@ public class PlainView extends View } catch (BadLocationException e) { - // This should never happen. + AssertionError ae = new AssertionError("Unexpected bad location"); + ae.initCause(e); + throw ae; } } @@ -137,7 +159,7 @@ public class PlainView extends View throws BadLocationException { g.setColor(selectedColor); - Segment segment = new Segment(); + Segment segment = getLineBuffer(); getDocument().getText(p0, p1 - p0, segment); return Utilities.drawTabbedText(segment, x, y, g, this, 0); } @@ -145,8 +167,13 @@ public class PlainView extends View protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1) throws BadLocationException { - g.setColor(unselectedColor); - Segment segment = new Segment(); + JTextComponent textComponent = (JTextComponent) getContainer(); + if (textComponent.isEnabled()) + g.setColor(unselectedColor); + else + g.setColor(disabledColor); + + Segment segment = getLineBuffer(); getDocument().getText(p0, p1 - p0, segment); return Utilities.drawTabbedText(segment, x, y, g, this, segment.offset); } @@ -161,7 +188,8 @@ public class PlainView extends View g.setFont(textComponent.getFont()); selectedColor = textComponent.getSelectedTextColor(); unselectedColor = textComponent.getForeground(); - + disabledColor = textComponent.getDisabledTextColor(); + Rectangle rect = s.getBounds(); // FIXME: Text may be scrolled. @@ -176,9 +204,19 @@ public class PlainView extends View } } + /** + * Returns the tab size of a tab. Checks the Document's + * properties for PlainDocument.tabSizeAttribute and returns it if it is + * defined, otherwise returns 8. + * + * @return the tab size. + */ protected int getTabSize() { - return 8; + Object tabSize = getDocument().getProperty(PlainDocument.tabSizeAttribute); + if (tabSize == null) + return 8; + return ((Integer)tabSize).intValue(); } /** @@ -191,10 +229,54 @@ public class PlainView extends View */ public float nextTabStop(float x, int tabStop) { - float tabSizePixels = getTabSize() + metrics.charWidth('m'); + float tabSizePixels = getTabSize() * metrics.charWidth('m'); return (float) (Math.floor(x / tabSizePixels) + 1) * tabSizePixels; } + /** + * Returns the length of the longest line, used for getting the span + * @return the length of the longest line + */ + float determineMaxLineLength() + { + // if the longest line is cached, return the cached value + if (maxLineLength != -1) + return maxLineLength; + + // otherwise we have to go through all the lines and find it + Element el = getElement(); + Segment seg = getLineBuffer(); + float span = 0; + for (int i = 0; i < el.getElementCount(); i++) + { + Element child = el.getElement(i); + int start = child.getStartOffset(); + int end = child.getEndOffset(); + try + { + el.getDocument().getText(start, end - start, seg); + } + catch (BadLocationException ex) + { + AssertionError ae = new AssertionError("Unexpected bad location"); + ae.initCause(ex); + throw ae; + } + + if (seg == null || seg.array == null || seg.count == 0) + continue; + + int width = metrics.charsWidth(seg.array, seg.offset, seg.count); + if (width > span) + { + longestLine = child; + span = width; + } + } + maxLineLength = span; + return maxLineLength; + } + public float getPreferredSpan(int axis) { if (axis != X_AXIS && axis != Y_AXIS) @@ -205,36 +287,16 @@ public class PlainView extends View float span = 0; Element el = getElement(); - Document doc = el.getDocument(); - Segment seg = new Segment(); switch (axis) { case X_AXIS: - // calculate the maximum of the line's widths - for (int i = 0; i < el.getElementCount(); i++) - { - Element child = el.getElement(i); - int start = child.getStartOffset(); - int end = child.getEndOffset(); - try { - doc.getText(start, start + end, seg); - } - catch (BadLocationException ex) - { - // throw new ClasspathAssertionError - // ("no BadLocationException should be thrown here"); - } - int width = metrics.charsWidth(seg.array, seg.offset, seg.count); - span = Math.max(span, width); - } - break; + span = determineMaxLineLength(); case Y_AXIS: default: span = metrics.getHeight() * el.getElementCount(); break; } - return span; } @@ -252,8 +314,251 @@ public class PlainView extends View */ public int viewToModel(float x, float y, Shape a, Position.Bias[] b) { - // FIXME: not implemented - return 0; + Rectangle rec = a.getBounds(); + Document doc = getDocument(); + Element root = doc.getDefaultRootElement(); + + // PlainView doesn't support line-wrapping so we can find out which + // Element was clicked on just by the y-position + int lineClicked = (int) (y - rec.y) / metrics.getHeight(); + if (lineClicked >= root.getElementCount()) + return getEndOffset() - 1; + + Element line = root.getElement(lineClicked); + Segment s = getLineBuffer(); + int start = line.getStartOffset(); + // We don't want the \n at the end of the line. + int end = line.getEndOffset() - 1; + try + { + doc.getText(start, end - start, s); + } + catch (BadLocationException ble) + { + AssertionError ae = new AssertionError("Unexpected bad location"); + ae.initCause(ble); + throw ae; + } + + int pos = Utilities.getTabbedTextOffset(s, metrics, rec.x, (int)x, this, start); + return Math.max (0, pos); + } + + /** + * Since insertUpdate and removeUpdate each deal with children + * Elements being both added and removed, they both have to perform + * the same checks. So they both simply call this method. + * @param changes the DocumentEvent for the changes to the Document. + * @param a the allocation of the View. + * @param f the ViewFactory to use for rebuilding. + */ + protected void updateDamage(DocumentEvent changes, Shape a, ViewFactory f) + { + Element el = getElement(); + ElementChange ec = changes.getChange(el); + + // If ec is null then no lines were added or removed, just + // repaint the changed line + if (ec == null) + { + int line = getElement().getElementIndex(changes.getOffset()); + damageLineRange(line, line, a, getContainer()); + return; + } + + Element[] removed = ec.getChildrenRemoved(); + Element[] newElements = ec.getChildrenAdded(); + + // If no Elements were added or removed, we just want to repaint + // the area containing the line that was modified + if (removed == null && newElements == null) + { + int line = getElement().getElementIndex(changes.getOffset()); + damageLineRange(line, line, a, getContainer()); + return; + } + + // Check to see if we removed the longest line, if so we have to + // search through all lines and find the longest one again + if (removed != null) + { + for (int i = 0; i < removed.length; i++) + if (removed[i].equals(longestLine)) + { + // reset maxLineLength and search through all lines for longest one + maxLineLength = -1; + determineMaxLineLength(); + ((JTextComponent)getContainer()).repaint(); + return; + } + } + + // If we've reached here, that means we haven't removed the longest line + if (newElements == null) + { + // No lines were added, just repaint the container and exit + ((JTextComponent)getContainer()).repaint(); + return; + } + + // Make sure we have the metrics + updateMetrics(); + + // If we've reached here, that means we haven't removed the longest line + // and we have added at least one line, so we have to check if added lines + // are longer than the previous longest line + Segment seg = getLineBuffer(); + float longestNewLength = 0; + Element longestNewLine = null; + + // Loop through the added lines to check their length + for (int i = 0; i < newElements.length; i++) + { + Element child = newElements[i]; + int start = child.getStartOffset(); + int end = child.getEndOffset(); + try + { + el.getDocument().getText(start, end - start, seg); + } + catch (BadLocationException ex) + { + AssertionError ae = new AssertionError("Unexpected bad location"); + ae.initCause(ex); + throw ae; + } + + if (seg == null || seg.array == null || seg.count == 0) + continue; + + int width = metrics.charsWidth(seg.array, seg.offset, seg.count); + if (width > longestNewLength) + { + longestNewLine = child; + longestNewLength = width; + } + } + + // Check if the longest of the new lines is longer than our previous + // longest line, and if so update our values + if (longestNewLength > maxLineLength) + { + maxLineLength = longestNewLength; + longestLine = longestNewLine; + } + // Repaint the container + ((JTextComponent)getContainer()).repaint(); + } + + /** + * This method is called when something is inserted into the Document + * that this View is displaying. + * + * @param changes the DocumentEvent for the changes. + * @param a the allocation of the View + * @param f the ViewFactory used to rebuild + */ + public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f) + { + updateDamage(changes, a, f); + } + + /** + * This method is called when something is removed from the Document + * that this View is displaying. + * + * @param changes the DocumentEvent for the changes. + * @param a the allocation of the View + * @param f the ViewFactory used to rebuild + */ + public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f) + { + updateDamage(changes, a, f); + } + + /** + * This method is called when attributes were changed in the + * Document in a location that this view is responsible for. + */ + public void changedUpdate (DocumentEvent changes, Shape a, ViewFactory f) + { + updateDamage(changes, a, f); + } + + /** + * Repaint the given line range. This is called from insertUpdate, + * changedUpdate, and removeUpdate when no new lines were added + * and no lines were removed, to repaint the line that was + * modified. + * + * @param line0 the start of the range + * @param line1 the end of the range + * @param a the rendering region of the host + * @param host the Component that uses this View (used to call repaint + * on that Component) + * + * @since 1.4 + */ + protected void damageLineRange (int line0, int line1, Shape a, Component host) + { + if (a == null) + return; + + Rectangle rec0 = lineToRect(a, line0); + Rectangle rec1 = lineToRect(a, line1); + + if (rec0 == null || rec1 == null) + // something went wrong, repaint the entire host to be safe + host.repaint(); + else + { + Rectangle repaintRec = rec0.union(rec1); + host.repaint(); + } + } + + /** + * Provides a {@link Segment} object, that can be used to fetch text from + * the document. + * + * @returna {@link Segment} object, that can be used to fetch text from + * the document + */ + protected Segment getLineBuffer() + { + if (lineBuffer == null) + lineBuffer = new Segment(); + return lineBuffer; + } + + /** + * Returns the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction <code>d</code>. + * + * @param c the text component + * @param pos the document position + * @param b the bias for <code>pos</code> + * @param d the direction, must be either {@link SwingConstants#NORTH}, + * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or + * {@link SwingConstants#EAST} + * @param biasRet an array of {@link Position.Bias} that can hold at least + * one element, which is filled with the bias of the return position + * on method exit + * + * @return the document position that is (visually) nearest to the given + * document position <code>pos</code> in the given direction + * <code>d</code> + * + * @throws BadLocationException if <code>pos</code> is not a valid offset in + * the document model + */ + public int getNextVisualPositionFrom(JTextComponent c, int pos, + Position.Bias b, int d, + Position.Bias[] biasRet) + throws BadLocationException + { + // TODO: Implement this properly. + throw new AssertionError("Not implemented yet."); } } |