mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			725 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			725 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Java
		
	
	
	
/* PlainView.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.Color;
 | 
						|
import java.awt.Component;
 | 
						|
import java.awt.Font;
 | 
						|
import java.awt.FontMetrics;
 | 
						|
import java.awt.Graphics;
 | 
						|
import java.awt.Rectangle;
 | 
						|
import java.awt.Shape;
 | 
						|
 | 
						|
import javax.swing.SwingUtilities;
 | 
						|
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;
 | 
						|
 | 
						|
  /**
 | 
						|
   * While painting this is the textcomponent's current start index
 | 
						|
   * of the selection.
 | 
						|
   */
 | 
						|
  int selectionStart;
 | 
						|
 | 
						|
  /**
 | 
						|
   * While painting this is the textcomponent's current end index
 | 
						|
   * of the selection.
 | 
						|
   */
 | 
						|
  int selectionEnd;
 | 
						|
 | 
						|
  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;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The base offset for tab calculations.
 | 
						|
   */
 | 
						|
  private int tabBase;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The tab size.
 | 
						|
   */
 | 
						|
  private int tabSize;
 | 
						|
 | 
						|
  public PlainView(Element elem)
 | 
						|
  {
 | 
						|
    super(elem);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * @since 1.4
 | 
						|
   */
 | 
						|
  protected void updateMetrics()
 | 
						|
  {
 | 
						|
    Component component = getContainer();
 | 
						|
    Font font = component.getFont();
 | 
						|
 | 
						|
    if (this.font != font)
 | 
						|
      {
 | 
						|
        this.font = font;
 | 
						|
        metrics = component.getFontMetrics(font);
 | 
						|
        tabSize = getTabSize() * metrics.charWidth('m');
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * @since 1.4
 | 
						|
   */
 | 
						|
  protected Rectangle lineToRect(Shape a, int line)
 | 
						|
  {
 | 
						|
    // Ensure metrics are up-to-date.
 | 
						|
    updateMetrics();
 | 
						|
 | 
						|
    Rectangle rect = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
 | 
						|
    int fontHeight = metrics.getHeight();
 | 
						|
    return new Rectangle(rect.x, rect.y + (line * fontHeight),
 | 
						|
                         rect.width, fontHeight);
 | 
						|
  }
 | 
						|
 | 
						|
  public Shape modelToView(int position, Shape a, Position.Bias b)
 | 
						|
    throws BadLocationException
 | 
						|
  {
 | 
						|
    // Ensure metrics are up-to-date.
 | 
						|
    updateMetrics();
 | 
						|
 | 
						|
    Document document = getDocument();
 | 
						|
 | 
						|
    // Get rectangle of the line containing position.
 | 
						|
    int lineIndex = getElement().getElementIndex(position);
 | 
						|
    Rectangle rect = lineToRect(a, lineIndex);
 | 
						|
    tabBase = rect.x;
 | 
						|
 | 
						|
    // Get the rectangle for position.
 | 
						|
    Element line = getElement().getElement(lineIndex);
 | 
						|
    int lineStart = line.getStartOffset();
 | 
						|
    Segment segment = getLineBuffer();
 | 
						|
    document.getText(lineStart, position - lineStart, segment);
 | 
						|
    int xoffset = Utilities.getTabbedTextWidth(segment, metrics, tabBase,
 | 
						|
                                               this, lineStart);
 | 
						|
 | 
						|
    // Calc the real rectangle.
 | 
						|
    rect.x += xoffset;
 | 
						|
    rect.width = 1;
 | 
						|
    rect.height = metrics.getHeight();
 | 
						|
 | 
						|
    return rect;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws a line of text. The X and Y coordinates specify the start of
 | 
						|
   * the <em>baseline</em> of the line.
 | 
						|
   *
 | 
						|
   * @param lineIndex the index of the line
 | 
						|
   * @param g the graphics to use for drawing the text
 | 
						|
   * @param x the X coordinate of the baseline
 | 
						|
   * @param y the Y coordinate of the baseline
 | 
						|
   */
 | 
						|
  protected void drawLine(int lineIndex, Graphics g, int x, int y)
 | 
						|
  {
 | 
						|
    try
 | 
						|
      {
 | 
						|
        Element line = getElement().getElement(lineIndex);
 | 
						|
        int startOffset = line.getStartOffset();
 | 
						|
        int endOffset = line.getEndOffset() - 1;
 | 
						|
 | 
						|
        if (selectionStart <= startOffset)
 | 
						|
          // Selection starts before the line ...
 | 
						|
          if (selectionEnd <= startOffset)
 | 
						|
            {
 | 
						|
              // end ends before the line: Draw completely unselected text.
 | 
						|
              drawUnselectedText(g, x, y, startOffset, endOffset);
 | 
						|
            }
 | 
						|
          else if (selectionEnd <= endOffset)
 | 
						|
            {
 | 
						|
              // and ends within the line: First part is selected,
 | 
						|
              // second is not.
 | 
						|
              x = drawSelectedText(g, x, y, startOffset, selectionEnd);
 | 
						|
              drawUnselectedText(g, x, y, selectionEnd, endOffset);
 | 
						|
            }
 | 
						|
          else
 | 
						|
            // and ends behind the line: Draw completely selected text.
 | 
						|
            drawSelectedText(g, x, y, startOffset, endOffset);
 | 
						|
        else if (selectionStart < endOffset)
 | 
						|
          // Selection starts within the line ..
 | 
						|
          if (selectionEnd < endOffset)
 | 
						|
            {
 | 
						|
              // and ends within it: First part unselected, second part
 | 
						|
              // selected, third part unselected.
 | 
						|
              x = drawUnselectedText(g, x, y, startOffset, selectionStart);
 | 
						|
              x = drawSelectedText(g, x, y, selectionStart, selectionEnd);
 | 
						|
              drawUnselectedText(g, x, y, selectionEnd, endOffset);
 | 
						|
            }
 | 
						|
          else
 | 
						|
            {
 | 
						|
              // and ends behind the line: First part unselected, second
 | 
						|
              // part selected.
 | 
						|
              x = drawUnselectedText(g, x, y, startOffset, selectionStart);
 | 
						|
              drawSelectedText(g, x, y, selectionStart, endOffset);
 | 
						|
            }
 | 
						|
        else
 | 
						|
          // Selection is behind this line: Draw completely unselected text.
 | 
						|
          drawUnselectedText(g, x, y, startOffset, endOffset);
 | 
						|
      }
 | 
						|
    catch (BadLocationException e)
 | 
						|
    {
 | 
						|
      AssertionError ae = new AssertionError("Unexpected bad location");
 | 
						|
      ae.initCause(e);
 | 
						|
      throw ae;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  protected int drawSelectedText(Graphics g, int x, int y, int p0, int p1)
 | 
						|
    throws BadLocationException
 | 
						|
  {
 | 
						|
    g.setColor(selectedColor);
 | 
						|
    Segment segment = getLineBuffer();
 | 
						|
    getDocument().getText(p0, p1 - p0, segment);
 | 
						|
    return Utilities.drawTabbedText(segment, x, y, g, this, segment.offset);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Draws a chunk of unselected text.
 | 
						|
   *
 | 
						|
   * @param g the graphics to use for drawing the text
 | 
						|
   * @param x the X coordinate of the baseline
 | 
						|
   * @param y the Y coordinate of the baseline
 | 
						|
   * @param p0 the start position in the text model
 | 
						|
   * @param p1 the end position in the text model
 | 
						|
   *
 | 
						|
   * @return the X location of the end of the range
 | 
						|
   *
 | 
						|
   * @throws BadLocationException if <code>p0</code> or <code>p1</code> are
 | 
						|
   *         invalid
 | 
						|
   */
 | 
						|
  protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1)
 | 
						|
    throws BadLocationException
 | 
						|
  {
 | 
						|
    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);
 | 
						|
  }
 | 
						|
 | 
						|
  public void paint(Graphics g, Shape s)
 | 
						|
  {
 | 
						|
    // Ensure metrics are up-to-date.
 | 
						|
    updateMetrics();
 | 
						|
 | 
						|
    JTextComponent textComponent = (JTextComponent) getContainer();
 | 
						|
 | 
						|
    selectedColor = textComponent.getSelectedTextColor();
 | 
						|
    unselectedColor = textComponent.getForeground();
 | 
						|
    disabledColor = textComponent.getDisabledTextColor();
 | 
						|
    selectionStart = textComponent.getSelectionStart();
 | 
						|
    selectionEnd = textComponent.getSelectionEnd();
 | 
						|
 | 
						|
    Rectangle rect = s instanceof Rectangle ? (Rectangle) s : s.getBounds();
 | 
						|
    tabBase = rect.x;
 | 
						|
 | 
						|
    // FIXME: Text may be scrolled.
 | 
						|
    Document document = textComponent.getDocument();
 | 
						|
    Element root = getElement();
 | 
						|
    int height = metrics.getHeight();
 | 
						|
 | 
						|
    // For layered highlighters we need to paint the layered highlights
 | 
						|
    // before painting any text.
 | 
						|
    LayeredHighlighter hl = null;
 | 
						|
    Highlighter h = textComponent.getHighlighter();
 | 
						|
    if (h instanceof LayeredHighlighter)
 | 
						|
      hl = (LayeredHighlighter) h;
 | 
						|
 | 
						|
    int count = root.getElementCount();
 | 
						|
 | 
						|
    // Determine first and last line inside the clip.
 | 
						|
    Rectangle clip = g.getClipBounds();
 | 
						|
    SwingUtilities.computeIntersection(rect.x, rect.y, rect.width, rect.height,
 | 
						|
                                       clip);
 | 
						|
    int line0 = (clip.y - rect.y) / height;
 | 
						|
    line0 = Math.max(0, Math.min(line0, count - 1));
 | 
						|
    int line1 = (clip.y + clip.height - rect.y) / height;
 | 
						|
    line1 = Math.max(0, Math.min(line1, count - 1));
 | 
						|
    int y = rect.y + metrics.getAscent() + height * line0;
 | 
						|
    for (int i = line0; i <= line1; i++)
 | 
						|
      {
 | 
						|
        if (hl != null)
 | 
						|
          {
 | 
						|
            Element lineEl = root.getElement(i);
 | 
						|
            // Exclude the trailing newline from beeing highlighted.
 | 
						|
            if (i == count)
 | 
						|
              hl.paintLayeredHighlights(g, lineEl.getStartOffset(),
 | 
						|
                                        lineEl.getEndOffset(), s, textComponent,
 | 
						|
                                        this);
 | 
						|
            else
 | 
						|
              hl.paintLayeredHighlights(g, lineEl.getStartOffset(),
 | 
						|
                                        lineEl.getEndOffset() - 1, s,
 | 
						|
                                        textComponent, this);
 | 
						|
          }
 | 
						|
        drawLine(i, g, rect.x, y);
 | 
						|
        y += height;
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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()
 | 
						|
  {
 | 
						|
    Object tabSize = getDocument().getProperty(PlainDocument.tabSizeAttribute);
 | 
						|
    if (tabSize == null)
 | 
						|
      return 8;
 | 
						|
    return ((Integer)tabSize).intValue();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the next tab stop position after a given reference position.
 | 
						|
   *
 | 
						|
   * This implementation ignores the <code>tabStop</code> argument.
 | 
						|
   *
 | 
						|
   * @param x the current x position in pixels
 | 
						|
   * @param tabStop the position within the text stream that the tab occured at
 | 
						|
   */
 | 
						|
  public float nextTabStop(float x, int tabStop)
 | 
						|
  {
 | 
						|
    float next = x;
 | 
						|
    if (tabSize != 0)
 | 
						|
      {
 | 
						|
        int numTabs = (((int) x) - tabBase) / tabSize;
 | 
						|
        next = tabBase + (numTabs + 1) * tabSize;
 | 
						|
      }
 | 
						|
    return next;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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() - 1;
 | 
						|
        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)
 | 
						|
      throw new IllegalArgumentException();
 | 
						|
 | 
						|
    // make sure we have the metrics
 | 
						|
    updateMetrics();
 | 
						|
 | 
						|
    Element el = getElement();
 | 
						|
    float span;
 | 
						|
 | 
						|
    switch (axis)
 | 
						|
      {
 | 
						|
      case X_AXIS:
 | 
						|
        span = determineMaxLineLength();
 | 
						|
        break;
 | 
						|
      case Y_AXIS:
 | 
						|
      default:
 | 
						|
        span = metrics.getHeight() * el.getElementCount();
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
    return span;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Maps coordinates from the <code>View</code>'s space into a position
 | 
						|
   * in the document model.
 | 
						|
   *
 | 
						|
   * @param x the x coordinate in the view space
 | 
						|
   * @param y the y coordinate in the view space
 | 
						|
   * @param a the allocation of this <code>View</code>
 | 
						|
   * @param b the bias to use
 | 
						|
   *
 | 
						|
   * @return the position in the document that corresponds to the screen
 | 
						|
   *         coordinates <code>x, y</code>
 | 
						|
   */
 | 
						|
  public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
 | 
						|
  {
 | 
						|
    Rectangle rec = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
 | 
						|
    tabBase = rec.x;
 | 
						|
 | 
						|
    int pos;
 | 
						|
    if ((int) y < rec.y)
 | 
						|
      // Above our area vertically. Return start offset.
 | 
						|
      pos = getStartOffset();
 | 
						|
    else if ((int) y > rec.y + rec.height)
 | 
						|
      // Below our area vertically. Return end offset.
 | 
						|
      pos = getEndOffset() - 1;
 | 
						|
    else
 | 
						|
      {
 | 
						|
        // Inside the allocation vertically. Determine line and X offset.
 | 
						|
        Document doc = getDocument();
 | 
						|
        Element root = doc.getDefaultRootElement();
 | 
						|
        int line = Math.abs(((int) y - rec.y) / metrics.getHeight());
 | 
						|
        if (line >= root.getElementCount())
 | 
						|
          pos = getEndOffset() - 1;
 | 
						|
        else
 | 
						|
          {
 | 
						|
            Element lineEl = root.getElement(line);
 | 
						|
            if (x < rec.x)
 | 
						|
              // To the left of the allocation area.
 | 
						|
              pos = lineEl.getStartOffset();
 | 
						|
            else if (x > rec.x + rec.width)
 | 
						|
              // To the right of the allocation area.
 | 
						|
              pos = lineEl.getEndOffset() - 1;
 | 
						|
            else
 | 
						|
              {
 | 
						|
                try
 | 
						|
                  {
 | 
						|
                    int p0 = lineEl.getStartOffset();
 | 
						|
                    int p1 = lineEl.getEndOffset();
 | 
						|
                    Segment s = new Segment();
 | 
						|
                    doc.getText(p0, p1 - p0, s);
 | 
						|
                    tabBase = rec.x;
 | 
						|
                    pos = p0 + Utilities.getTabbedTextOffset(s, metrics,
 | 
						|
                                                             tabBase, (int) x,
 | 
						|
                                                             this, p0);
 | 
						|
                  }
 | 
						|
                catch (BadLocationException ex)
 | 
						|
                  {
 | 
						|
                    // Should not happen.
 | 
						|
                    pos = -1;
 | 
						|
                  }
 | 
						|
              }
 | 
						|
 | 
						|
          }
 | 
						|
      }
 | 
						|
    // Bias is always forward.
 | 
						|
    b[0] = Position.Bias.Forward;
 | 
						|
    return 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)
 | 
						|
  {
 | 
						|
    // This happens during initialization.
 | 
						|
    if (metrics == null)
 | 
						|
      {
 | 
						|
        updateMetrics();
 | 
						|
        preferenceChanged(null, true, true);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
    Element element = getElement();
 | 
						|
 | 
						|
    // Find longest line if it hasn't been initialized yet.
 | 
						|
    if (longestLine == null)
 | 
						|
      findLongestLine(0, element.getElementCount() - 1);
 | 
						|
 | 
						|
    ElementChange change = changes.getChange(element);
 | 
						|
    if (changes.getType() == DocumentEvent.EventType.INSERT)
 | 
						|
      {
 | 
						|
        // Handles character/line insertion.
 | 
						|
 | 
						|
        // Determine if lines have been added. In this case we repaint
 | 
						|
        // differently.
 | 
						|
        boolean linesAdded = true;
 | 
						|
        if (change == null)
 | 
						|
          linesAdded = false;
 | 
						|
 | 
						|
        // Determine the start line.
 | 
						|
        int start;
 | 
						|
        if (linesAdded)
 | 
						|
          start = change.getIndex();
 | 
						|
        else
 | 
						|
          start = element.getElementIndex(changes.getOffset());
 | 
						|
 | 
						|
        // Determine the length of the updated region.
 | 
						|
        int length = 0;
 | 
						|
        if (linesAdded)
 | 
						|
          length = change.getChildrenAdded().length - 1;
 | 
						|
 | 
						|
        // Update the longest line and length.
 | 
						|
        int oldMaxLength = (int) maxLineLength;
 | 
						|
        if (longestLine.getEndOffset() < changes.getOffset()
 | 
						|
            || longestLine.getStartOffset() > changes.getOffset()
 | 
						|
                                             + changes.getLength())
 | 
						|
          {
 | 
						|
            findLongestLine(start, start + length);
 | 
						|
          }
 | 
						|
        else
 | 
						|
          {
 | 
						|
            findLongestLine(0, element.getElementCount() - 1);
 | 
						|
          }
 | 
						|
 | 
						|
        // Trigger a preference change so that the layout gets updated
 | 
						|
        // correctly.
 | 
						|
        preferenceChanged(null, maxLineLength != oldMaxLength, linesAdded);
 | 
						|
 | 
						|
        // Damage the updated line range.
 | 
						|
        int endLine = start;
 | 
						|
        if (linesAdded)
 | 
						|
          endLine = element.getElementCount() - 1;
 | 
						|
        damageLineRange(start, endLine, a, getContainer());
 | 
						|
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        // Handles character/lines removals.
 | 
						|
 | 
						|
        // Update the longest line and length and trigger preference changed.
 | 
						|
        int oldMaxLength = (int) maxLineLength;
 | 
						|
        if (change != null)
 | 
						|
          {
 | 
						|
            // Line(s) have been removed.
 | 
						|
            findLongestLine(0, element.getElementCount() - 1);
 | 
						|
            preferenceChanged(null, maxLineLength != oldMaxLength, true);
 | 
						|
          }
 | 
						|
        else
 | 
						|
          {
 | 
						|
            // No line has been removed.
 | 
						|
            int lineNo = getElement().getElementIndex(changes.getOffset());
 | 
						|
            Element line = getElement().getElement(lineNo);
 | 
						|
            if (longestLine == line)
 | 
						|
              {
 | 
						|
                findLongestLine(0, element.getElementCount() - 1);
 | 
						|
                preferenceChanged(null, maxLineLength != oldMaxLength, false);
 | 
						|
            }
 | 
						|
            damageLineRange(lineNo, lineNo, a, getContainer());
 | 
						|
        }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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 = SwingUtilities.computeUnion(rec0.x, rec0.y,
 | 
						|
                                                           rec0.width,
 | 
						|
                                                           rec0.height, rec1);
 | 
						|
        host.repaint(repaintRec.x, repaintRec.y, repaintRec.width,
 | 
						|
                     repaintRec.height);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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 final Segment getLineBuffer()
 | 
						|
  {
 | 
						|
    if (lineBuffer == null)
 | 
						|
      lineBuffer = new Segment();
 | 
						|
    return lineBuffer;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Finds and updates the longest line in the view inside an interval of
 | 
						|
   * lines.
 | 
						|
   *
 | 
						|
   * @param start the start of the search interval
 | 
						|
   * @param end the end of the search interval
 | 
						|
   */
 | 
						|
  private void findLongestLine(int start, int end)
 | 
						|
  {
 | 
						|
    for (int i = start; i <= end; i++)
 | 
						|
      {
 | 
						|
        int w = getLineLength(i);
 | 
						|
        if (w > maxLineLength)
 | 
						|
          {
 | 
						|
            maxLineLength = w;
 | 
						|
            longestLine = getElement().getElement(i);
 | 
						|
          }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Determines the length of the specified line.
 | 
						|
   *
 | 
						|
   * @param line the number of the line
 | 
						|
   *
 | 
						|
   * @return the length of the line in pixels
 | 
						|
   */
 | 
						|
  private int getLineLength(int line)
 | 
						|
  {
 | 
						|
    Element lineEl = getElement().getElement(line);
 | 
						|
    Segment buffer = getLineBuffer();
 | 
						|
    try
 | 
						|
      {
 | 
						|
        Document doc = getDocument();
 | 
						|
        doc.getText(lineEl.getStartOffset(),
 | 
						|
                    lineEl.getEndOffset() - lineEl.getStartOffset() - 1,
 | 
						|
                    buffer);
 | 
						|
      }
 | 
						|
    catch (BadLocationException ex)
 | 
						|
      {
 | 
						|
        AssertionError err = new AssertionError("Unexpected bad location");
 | 
						|
        err.initCause(ex);
 | 
						|
        throw err;
 | 
						|
      }
 | 
						|
 | 
						|
    return Utilities.getTabbedTextWidth(buffer, metrics, tabBase, this,
 | 
						|
                                        lineEl.getStartOffset());
 | 
						|
  }
 | 
						|
}
 |