/**
  * Fetches the allocation for the given child view.
  *
  * <p>Overridden to account for code folding.
  *
  * @param index The index of the child, >= 0 && < getViewCount().
  * @param a The allocation to this view
  * @return The allocation to the child; or <code>null</code> if <code>a</code> is <code>null
  *     </code>; or <code>null</code> if the layout is invalid
  */
 public Shape getChildAllocation(int index, Shape a) {
   if (a != null) {
     Shape ca = getChildAllocationImpl(index, a);
     if ((ca != null) && (!isAllocationValid())) {
       // The child allocation may not have been set yet.
       Rectangle r = (ca instanceof Rectangle) ? (Rectangle) ca : ca.getBounds();
       if ((r.width == 0) && (r.height == 0)) {
         return null;
       }
     }
     return ca;
   }
   return null;
 }
  public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {

    if (!isAllocationValid()) {
      Rectangle alloc = a.getBounds();
      setSize(alloc.width, alloc.height);
    }

    boolean isBackward = (b == Position.Bias.Backward);
    int testPos = (isBackward) ? Math.max(0, pos - 1) : pos;
    if (isBackward && testPos < getStartOffset()) {
      return null;
    }

    int vIndex = getViewIndexAtPosition(testPos);
    if ((vIndex != -1) && (vIndex < getViewCount())) {
      View v = getView(vIndex);
      if (v != null && testPos >= v.getStartOffset() && testPos < v.getEndOffset()) {
        Shape childShape = getChildAllocation(vIndex, a);
        if (childShape == null) {
          // We are likely invalid, fail.
          return null;
        }
        Shape retShape = v.modelToView(pos, childShape, b);
        if (retShape == null && v.getEndOffset() == pos) {
          if (++vIndex < getViewCount()) {
            v = getView(vIndex);
            retShape = v.modelToView(pos, getChildAllocation(vIndex, a), b);
          }
        }
        return retShape;
      }
    }

    throw new BadLocationException("Position not represented by view", pos);
  }
  /**
   * Paints the word-wrapped text.
   *
   * @param g The graphics context in which to paint.
   * @param a The shape (usually a rectangle) in which to paint.
   */
  public void paint(Graphics g, Shape a) {

    Rectangle alloc = (a instanceof Rectangle) ? (Rectangle) a : a.getBounds();
    tabBase = alloc.x;

    Graphics2D g2d = (Graphics2D) g;
    host = (RSyntaxTextArea) getContainer();
    int ascent = host.getMaxAscent();
    int fontHeight = host.getLineHeight();
    FoldManager fm = host.getFoldManager();
    TokenPainter painter = host.getTokenPainter();
    Element root = getElement();

    // Whether token styles should always be painted, even in selections
    int selStart = host.getSelectionStart();
    int selEnd = host.getSelectionEnd();
    boolean useSelectedTextColor = host.getUseSelectedTextColor();

    int n = getViewCount(); // Number of lines.
    int x = alloc.x + getLeftInset();
    tempRect.y = alloc.y + getTopInset();
    Rectangle clip = g.getClipBounds();
    for (int i = 0; i < n; i++) {

      tempRect.x = x + getOffset(X_AXIS, i);
      // tempRect.y = y + getOffset(Y_AXIS, i);
      tempRect.width = getSpan(X_AXIS, i);
      tempRect.height = getSpan(Y_AXIS, i);
      // System.err.println("For line " + i + ": tempRect==" + tempRect);

      if (tempRect.intersects(clip)) {
        Element lineElement = root.getElement(i);
        int startOffset = lineElement.getStartOffset();
        int endOffset = lineElement.getEndOffset() - 1; // Why always "-1"?
        View view = getView(i);
        if (!useSelectedTextColor
            || selStart == selEnd
            || (startOffset >= selEnd || endOffset < selStart)) {
          drawView(painter, g2d, alloc, view, fontHeight, tempRect.y + ascent);
        } else {
          // System.out.println("Drawing line with selection: " + i);
          drawViewWithSelection(
              painter, g2d, alloc, view, fontHeight, tempRect.y + ascent, selStart, selEnd);
        }
      }

      tempRect.y += tempRect.height;

      Fold possibleFold = fm.getFoldForLine(i);
      if (possibleFold != null && possibleFold.isCollapsed()) {
        i += possibleFold.getCollapsedLineCount();
        // Visible indicator of collapsed lines
        Color c = RSyntaxUtilities.getFoldedLineBottomColor(host);
        if (c != null) {
          g.setColor(c);
          g.drawLine(x, tempRect.y - 1, alloc.width, tempRect.y - 1);
        }
      }
    }
  }
Beispiel #4
0
  @Override
  public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {

    int offs = -1;

    if (!isAllocationValid()) {
      Rectangle alloc = a.getBounds();
      setSize(alloc.width, alloc.height);
    }

    // Get the child view for the line at (x,y), and ask it for the
    // specific offset.
    Rectangle alloc = getInsideAllocation(a);
    View v = getViewAtPoint((int) x, (int) y, alloc);
    if (v != null) {
      offs = v.viewToModel(x, y, alloc, bias);
    }

    // Code folding may have hidden the last line.  If so, return the last
    // visible offset instead of the last offset.
    if (host.isCodeFoldingEnabled()
        && v == getView(getViewCount() - 1)
        && offs == v.getEndOffset() - 1) {
      offs = host.getLastVisibleOffset();
    }

    return offs;
  }
  /**
   * Provides a mapping from the view coordinate space to the logical coordinate space of the model.
   *
   * @param fx the X coordinate &gt;= 0
   * @param fy the Y coordinate &gt;= 0
   * @param a the allocated region to render into
   * @return the location within the model that best represents the given point in the view &gt;= 0
   */
  @Override
  public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) {

    bias[0] = Position.Bias.Forward;

    Rectangle alloc = a.getBounds();
    RSyntaxDocument doc = (RSyntaxDocument) getDocument();
    int x = (int) fx;
    int y = (int) fy;

    // If they're asking about a view position above the area covered by
    // this view, then the position is assumed to be the starting position
    // of this view.
    if (y < alloc.y) {
      return getStartOffset();
    }

    // If they're asking about a position below this view, the position
    // is assumed to be the ending position of this view.
    else if (y > alloc.y + alloc.height) {
      return host.getLastVisibleOffset();
    }

    // They're asking about a position within the coverage of this view
    // vertically.  So, we figure out which line the point corresponds to.
    // If the line is greater than the number of lines contained, then
    // simply use the last line as it represents the last possible place
    // we can position to.
    else {

      Element map = doc.getDefaultRootElement();
      int lineIndex = Math.abs((y - alloc.y) / lineHeight); // metrics.getHeight() );
      FoldManager fm = host.getFoldManager();
      // System.out.print("--- " + lineIndex);
      lineIndex += fm.getHiddenLineCountAbove(lineIndex, true);
      // System.out.println(" => " + lineIndex);
      if (lineIndex >= map.getElementCount()) {
        return host.getLastVisibleOffset();
      }

      Element line = map.getElement(lineIndex);

      // If the point is to the left of the line...
      if (x < alloc.x) {
        return line.getStartOffset();
      } else if (x > alloc.x + alloc.width) {
        return line.getEndOffset() - 1;
      } else {
        // Determine the offset into the text
        int p0 = line.getStartOffset();
        Token tokenList = doc.getTokenListForLine(lineIndex);
        tabBase = alloc.x;
        int offs = tokenList.getListOffset((RSyntaxTextArea) getContainer(), this, tabBase, x);
        return offs != -1 ? offs : p0;
      }
    } // End of else.
  }
Beispiel #6
0
    /**
     * Provides a mapping from the document model coordinate space to the coordinate space of the
     * view mapped to it.
     *
     * @param pos the position to convert
     * @param a the allocated region to render into
     * @return the bounding box of the given position is returned
     * @exception BadLocationException if the given position does not represent a valid location in
     *     the associated document.
     */
    @Override
    public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {

      // System.err.println("--- begin modelToView ---");
      Rectangle alloc = a.getBounds();
      RSyntaxTextArea textArea = (RSyntaxTextArea) getContainer();
      alloc.height = textArea.getLineHeight(); // metrics.getHeight();
      alloc.width = 1;
      int p0 = getStartOffset();
      int p1 = getEndOffset();
      int testP = (b == Position.Bias.Forward) ? pos : Math.max(p0, pos - 1);

      // Get the token list for this line so we don't have to keep
      // recomputing it if this logical line spans multiple physical
      // lines.
      RSyntaxDocument doc = (RSyntaxDocument) getDocument();
      Element map = doc.getDefaultRootElement();
      int line = map.getElementIndex(p0);
      Token tokenList = doc.getTokenListForLine(line);
      float x0 = alloc.x; // 0;

      while (p0 < p1) {
        TokenSubList subList =
            TokenUtils.getSubTokenList(
                tokenList, p0, WrappedSyntaxView.this, textArea, x0, lineCountTempToken);
        x0 = subList != null ? subList.x : x0;
        tokenList = subList != null ? subList.tokenList : null;
        int p = calculateBreakPosition(p0, tokenList, x0);
        if ((pos >= p0) && (testP < p)) { // pos < p)) {
          // it's in this line
          alloc =
              RSyntaxUtilities.getLineWidthUpTo(
                  textArea, s, p0, pos, WrappedSyntaxView.this, alloc, alloc.x);
          // System.err.println("--- end modelToView ---");
          return alloc;
        }
        // if (p == p1 && pos == p1) {
        if (p == p1 - 1 && pos == p1 - 1) {
          // Wants end.
          if (pos > p0) {
            alloc =
                RSyntaxUtilities.getLineWidthUpTo(
                    textArea, s, p0, pos, WrappedSyntaxView.this, alloc, alloc.x);
          }
          // System.err.println("--- end modelToView ---");
          return alloc;
        }

        p0 = (p == p0) ? p1 : p;
        // System.err.println("... ... Incrementing y");
        alloc.y += alloc.height;
      }

      throw new BadLocationException(null, pos);
    }
Beispiel #7
0
 /**
  * Returns the tooltip text at the specified location. The default implementation returns the
  * value from the child View identified by the passed in location.
  *
  * @since 1.4
  * @see JTextComponent#getToolTipText
  */
 public String getToolTipText(float x, float y, Shape allocation) {
   int viewIndex = getViewIndex(x, y, allocation);
   if (viewIndex >= 0) {
     allocation = getChildAllocation(viewIndex, allocation);
     Rectangle rect =
         (allocation instanceof Rectangle) ? (Rectangle) allocation : allocation.getBounds();
     if (rect.contains(x, y)) {
       return getView(viewIndex).getToolTipText(x, y, allocation);
     }
   }
   return null;
 }
Beispiel #8
0
 /**
  * Provides a mapping, for a given region, from the document model coordinate space to the view
  * coordinate space. The specified region is created as a union of the first and last character
  * positions.
  *
  * @param p0 the position of the first character (>=0)
  * @param b0 the bias of the first character position, toward the previous character or the next
  *     character represented by the offset, in case the position is a boundary of two views;
  *     <code>b0</code> will have one of these values:
  *     <ul>
  *       <li><code>Position.Bias.Forward</code>
  *       <li><code>Position.Bias.Backward</code>
  *     </ul>
  *
  * @param p1 the position of the last character (>=0)
  * @param b1 the bias for the second character position, defined one of the legal values shown
  *     above
  * @param a the area of the view, which encompasses the requested region
  * @return the bounding box which is a union of the region specified by the first and last
  *     character positions
  * @exception BadLocationException if the given position does not represent a valid location in
  *     the associated document
  * @exception IllegalArgumentException if <code>b0</code> or <code>b1</code> are not one of the
  *     legal <code>Position.Bias</code> values listed above
  * @see View#viewToModel
  */
 public Shape modelToView(int p0, Position.Bias b0, int p1, Position.Bias b1, Shape a)
     throws BadLocationException {
   Shape s0 = modelToView(p0, a, b0);
   Shape s1;
   if (p1 == getEndOffset()) {
     try {
       s1 = modelToView(p1, a, b1);
     } catch (BadLocationException ble) {
       s1 = null;
     }
     if (s1 == null) {
       // Assume extends left to right.
       Rectangle alloc = (a instanceof Rectangle) ? (Rectangle) a : a.getBounds();
       s1 = new Rectangle(alloc.x + alloc.width - 1, alloc.y, 1, alloc.height);
     }
   } else {
     s1 = modelToView(p1, a, b1);
   }
   Rectangle r0 = s0.getBounds();
   Rectangle r1 = (s1 instanceof Rectangle) ? (Rectangle) s1 : s1.getBounds();
   if (r0.y != r1.y) {
     // If it spans lines, force it to be the width of the view.
     Rectangle alloc = (a instanceof Rectangle) ? (Rectangle) a : a.getBounds();
     r0.x = alloc.x;
     r0.width = alloc.width;
   }
   r0.add(r1);
   return r0;
 }
 /**
  * Provides a mapping from the document model coordinate space to the coordinate space of the view
  * mapped to it.
  *
  * @param pos the position to convert
  * @param a the allocated region to render into
  * @return the bounding box of the given position
  * @exception BadLocationException if the given position does not represent a valid location in
  *     the associated document
  * @see View#modelToView
  */
 public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
   int p0 = getStartOffset();
   int p1 = getEndOffset();
   if ((pos >= p0) && (pos <= p1)) {
     Rectangle r = a.getBounds();
     if (pos == p1) {
       r.x += r.width;
     }
     r.width = 0;
     return r;
   }
   return null;
 }
Beispiel #10
0
  /**
   * Returns the child view index representing the given position in the view. This iterates over
   * all the children returning the first with a bounds that contains <code>x</code>, <code>y</code>
   * .
   *
   * @param x the x coordinate
   * @param y the y coordinate
   * @param allocation current allocation of the View.
   * @return index of the view representing the given location, or -1 if no view represents that
   *     position
   * @since 1.4
   */
  public int getViewIndex(float x, float y, Shape allocation) {
    for (int counter = getViewCount() - 1; counter >= 0; counter--) {
      Shape childAllocation = getChildAllocation(counter, allocation);

      if (childAllocation != null) {
        Rectangle rect =
            (childAllocation instanceof Rectangle)
                ? (Rectangle) childAllocation
                : allocation.getBounds();

        if (rect.contains(x, y)) {
          return counter;
        }
      }
    }
    return -1;
  }
 /**
  * Determine the rectangle that represents the given line.
  *
  * @param a The region allocated for the view to render into
  * @param line The line number to find the region of. This must be a valid line number in the
  *     model.
  */
 protected Rectangle lineToRect(Shape a, int line) {
   Rectangle r = null;
   updateMetrics();
   if (metrics != null) {
     Rectangle alloc = a.getBounds();
     // NOTE:  lineHeight is not initially set here, leading to the
     // current line not being highlighted when a document is first
     // opened.  So, we set it here just in case.
     lineHeight = host != null ? host.getLineHeight() : lineHeight;
     if (host != null && host.isCodeFoldingEnabled()) {
       FoldManager fm = host.getFoldManager();
       int hiddenCount = fm.getHiddenLineCountAbove(line);
       line -= hiddenCount;
     }
     r = new Rectangle(alloc.x, alloc.y + line * lineHeight, alloc.width, lineHeight);
   }
   return r;
 }
  /**
   * Translates the immutable allocation given to the view to a mutable allocation that represents
   * the interior allocation (i.e. the bounds of the given allocation with the top, left, bottom,
   * and right insets removed. It is expected that the returned value would be further mutated to
   * represent an allocation to a child view. This is implemented to reuse an instance variable so
   * it avoids creating excessive Rectangles. Typically the result of calling this method would be
   * fed to the <code>childAllocation</code> method.
   *
   * @param a the allocation given to the view
   * @return the allocation that represents the inside of the view after the margins have all been
   *     removed; if the given allocation was <code>null</code>, the return value is <code>null
   *     </code>
   */
  protected Rectangle getInsideAllocation(Shape a) {
    if (a != null) {
      // get the bounds, hopefully without allocating
      // a new rectangle.  The Shape argument should
      // not be modified... we copy it into the
      // child allocation.
      Rectangle alloc;
      if (a instanceof Rectangle) {
        alloc = (Rectangle) a;
      } else {
        alloc = a.getBounds();
      }

      childAlloc.setBounds(alloc);
      childAlloc.x += getLeftInset();
      childAlloc.y += getTopInset();
      childAlloc.width -= getLeftInset() + getRightInset();
      childAlloc.height -= getTopInset() + getBottomInset();
      return childAlloc;
    }
    return null;
  }
  /**
   * Paints the word-wrapped text.
   *
   * @param g The graphics context in which to paint.
   * @param a The shape (usually a rectangle) in which to paint.
   */
  public void paint(Graphics g, Shape a) {

    Rectangle alloc = (a instanceof Rectangle) ? (Rectangle) a : a.getBounds();
    tabBase = alloc.x;

    Graphics2D g2d = (Graphics2D) g;
    host = (RSyntaxTextArea) getContainer();
    int ascent = host.getMaxAscent();
    int fontHeight = host.getLineHeight();
    FoldManager fm = host.getFoldManager();

    int n = getViewCount(); // Number of lines.
    int x = alloc.x + getLeftInset();
    tempRect.y = alloc.y + getTopInset();
    Rectangle clip = g.getClipBounds();
    for (int i = 0; i < n; i++) {
      tempRect.x = x + getOffset(X_AXIS, i);
      // tempRect.y = y + getOffset(Y_AXIS, i);
      tempRect.width = getSpan(X_AXIS, i);
      tempRect.height = getSpan(Y_AXIS, i);
      // System.err.println("For line " + i + ": tempRect==" + tempRect);
      if (tempRect.intersects(clip)) {
        View view = getView(i);
        drawView(g2d, alloc, view, fontHeight, tempRect.y + ascent);
      }
      tempRect.y += tempRect.height;
      Fold possibleFold = fm.getFoldForLine(i);
      if (possibleFold != null && possibleFold.isCollapsed()) {
        i += possibleFold.getCollapsedLineCount();
        // Visible indicator of collapsed lines
        Color c = RSyntaxUtilities.getFoldedLineBottomColor(host);
        if (c != null) {
          g.setColor(c);
          g.drawLine(x, tempRect.y - 1, alloc.width, tempRect.y - 1);
        }
      }
    }
  }
  /**
   * Provides a mapping, for a given region, from the document model coordinate space to the view
   * coordinate space. The specified region is created as a union of the first and last character
   * positions.
   *
   * <p>This is implemented to subtract the width of the second character, as this view's <code>
   * modelToView</code> actually returns the width of the character instead of "1" or "0" like the
   * View implementations in <code>javax.swing.text</code>. Thus, if we don't override this method,
   * the <code>View</code> implementation will return one character's width too much for its
   * consumers (implementations of <code>javax.swing.text.Highlighter</code>).
   *
   * @param p0 the position of the first character (&gt;=0)
   * @param b0 The bias of the first character position, toward the previous character or the next
   *     character represented by the offset, in case the position is a boundary of two views;
   *     <code>b0</code> will have one of these values:
   *     <ul>
   *       <li><code>Position.Bias.Forward</code>
   *       <li><code>Position.Bias.Backward</code>
   *     </ul>
   *
   * @param p1 the position of the last character (&gt;=0)
   * @param b1 the bias for the second character position, defined one of the legal values shown
   *     above
   * @param a the area of the view, which encompasses the requested region
   * @return the bounding box which is a union of the region specified by the first and last
   *     character positions
   * @exception BadLocationException if the given position does not represent a valid location in
   *     the associated document
   * @exception IllegalArgumentException if <code>b0</code> or <code>b1</code> are not one of the
   *     legal <code>Position.Bias</code> values listed above
   * @see View#viewToModel
   */
  @Override
  public Shape modelToView(int p0, Position.Bias b0, int p1, Position.Bias b1, Shape a)
      throws BadLocationException {

    Shape s0 = modelToView(p0, a, b0);
    Shape s1;
    if (p1 == getEndOffset()) {
      try {
        s1 = modelToView(p1, a, b1);
      } catch (BadLocationException ble) {
        s1 = null;
      }
      if (s1 == null) {
        // Assume extends left to right.
        Rectangle alloc = (a instanceof Rectangle) ? (Rectangle) a : a.getBounds();
        s1 = new Rectangle(alloc.x + alloc.width - 1, alloc.y, 1, alloc.height);
      }
    } else {
      s1 = modelToView(p1, a, b1);
    }
    Rectangle r0 = s0 instanceof Rectangle ? (Rectangle) s0 : s0.getBounds();
    Rectangle r1 = s1 instanceof Rectangle ? (Rectangle) s1 : s1.getBounds();
    if (r0.y != r1.y) {
      // If it spans lines, force it to be the width of the view.
      Rectangle alloc = (a instanceof Rectangle) ? (Rectangle) a : a.getBounds();
      r0.x = alloc.x;
      r0.width = alloc.width;
    }

    r0.add(r1);
    // The next line is the only difference between this method and
    // View's implementation.  We're subtracting the width of the second
    // character.  This is because this method is used by Highlighter
    // implementations to get the area to "highlight", and if we don't do
    // this, one character too many is highlighted thanks to our
    // modelToView() implementation returning the actual width of the
    // character requested!
    if (p1 > p0) {
      r0.width -= r1.width;
    }

    return r0;
  }
Beispiel #15
0
 // Graphics2D g=getG();return g==null?null:g.getClip();}
 public Rectangle getClipBounds() {
   Shape s = bufferClip();
   return s == null ? null : s.getBounds();
 }
  /**
   * Provides a mapping from the document model coordinate space to the coordinate space of the view
   * mapped to it.
   *
   * @param p0 the position to convert &gt;= 0
   * @param b0 the bias toward the previous character or the next character represented by p0, in
   *     case the position is a boundary of two views; either <code>Position.Bias.Forward</code> or
   *     <code>Position.Bias.Backward</code>
   * @param p1 the position to convert &gt;= 0
   * @param b1 the bias toward the previous character or the next character represented by p1, in
   *     case the position is a boundary of two views
   * @param a the allocated region to render into
   * @return the bounding box of the given position is returned
   * @exception BadLocationException if the given position does not represent a valid location in
   *     the associated document
   * @exception IllegalArgumentException for an invalid bias argument
   * @see View#viewToModel
   */
  public Shape modelToView(int p0, Position.Bias b0, int p1, Position.Bias b1, Shape a)
      throws BadLocationException {
    if (p0 == getStartOffset() && p1 == getEndOffset()) {
      return a;
    }
    Rectangle alloc = getInsideAllocation(a);
    Rectangle r0 = new Rectangle(alloc);
    View v0 = getViewAtPosition((b0 == Position.Bias.Backward) ? Math.max(0, p0 - 1) : p0, r0);
    Rectangle r1 = new Rectangle(alloc);
    View v1 = getViewAtPosition((b1 == Position.Bias.Backward) ? Math.max(0, p1 - 1) : p1, r1);
    if (v0 == v1) {
      if (v0 == null) {
        return a;
      }
      // Range contained in one view
      return v0.modelToView(p0, b0, p1, b1, r0);
    }
    // Straddles some views.
    int viewCount = getViewCount();
    int counter = 0;
    while (counter < viewCount) {
      View v;
      // Views may not be in same order as model.
      // v0 or v1 may be null if there is a gap in the range this
      // view contains.
      if ((v = getView(counter)) == v0 || v == v1) {
        View endView;
        Rectangle retRect;
        Rectangle tempRect = new Rectangle();
        if (v == v0) {
          retRect =
              v0.modelToView(p0, b0, v0.getEndOffset(), Position.Bias.Backward, r0).getBounds();
          endView = v1;
        } else {
          retRect =
              v1.modelToView(v1.getStartOffset(), Position.Bias.Forward, p1, b1, r1).getBounds();
          endView = v0;
        }

        // Views entirely covered by range.
        while (++counter < viewCount && (v = getView(counter)) != endView) {
          tempRect.setBounds(alloc);
          childAllocation(counter, tempRect);
          retRect.add(tempRect);
        }

        // End view.
        if (endView != null) {
          Shape endShape;
          if (endView == v1) {
            endShape = v1.modelToView(v1.getStartOffset(), Position.Bias.Forward, p1, b1, r1);
          } else {
            endShape = v0.modelToView(p0, b0, v0.getEndOffset(), Position.Bias.Backward, r0);
          }
          if (endShape instanceof Rectangle) {
            retRect.add((Rectangle) endShape);
          } else {
            retRect.add(endShape.getBounds());
          }
        }
        return retRect;
      }
      counter++;
    }
    throw new BadLocationException("Position not represented by view", p0);
  }
Beispiel #17
0
  /**
   * Adjusts the allocation given to the view to be a suitable allocation for a text field. If the
   * view has been allocated more than the preferred span vertically, the allocation is changed to
   * be centered vertically. Horizontally the view is adjusted according to the horizontal alignment
   * property set on the associated JTextField (if that is the type of the hosting component).
   *
   * @param a the allocation given to the view, which may need to be adjusted.
   * @return the allocation that the superclass should use.
   */
  protected Shape adjustAllocation(Shape a) {
    if (a != null) {
      Rectangle bounds = a.getBounds();
      int vspan = (int) getPreferredSpan(Y_AXIS);
      int hspan = (int) getPreferredSpan(X_AXIS);
      if (bounds.height != vspan) {
        int slop = bounds.height - vspan;
        bounds.y += slop / 2;
        bounds.height -= slop;
      }

      // horizontal adjustments
      Component c = getContainer();
      if (c instanceof JTextField) {
        JTextField field = (JTextField) c;
        BoundedRangeModel vis = field.getHorizontalVisibility();
        int max = Math.max(hspan, bounds.width);
        int value = vis.getValue();
        int extent = Math.min(max, bounds.width - 1);
        if ((value + extent) > max) {
          value = max - extent;
        }
        vis.setRangeProperties(value, extent, vis.getMinimum(), max, false);
        if (hspan < bounds.width) {
          // horizontally align the interior
          int slop = bounds.width - 1 - hspan;

          int align = ((JTextField) c).getHorizontalAlignment();
          if (Utilities.isLeftToRight(c)) {
            if (align == LEADING) {
              align = LEFT;
            } else if (align == TRAILING) {
              align = RIGHT;
            }
          } else {
            if (align == LEADING) {
              align = RIGHT;
            } else if (align == TRAILING) {
              align = LEFT;
            }
          }

          switch (align) {
            case SwingConstants.CENTER:
              bounds.x += slop / 2;
              bounds.width -= slop;
              break;
            case SwingConstants.RIGHT:
              bounds.x += slop;
              bounds.width -= slop;
              break;
          }
        } else {
          // adjust the allocation to match the bounded range.
          bounds.width = hspan;
          bounds.x -= vis.getValue();
        }
      }
      return bounds;
    }
    return null;
  }
  /**
   * Actually paints the text area. Only lines that have been damaged are repainted.
   *
   * @param g The graphics context with which to paint.
   * @param a The allocated region in which to render.
   */
  @Override
  public void paint(Graphics g, Shape a) {

    RSyntaxDocument document = (RSyntaxDocument) getDocument();

    Rectangle alloc = a.getBounds();

    tabBase = alloc.x;
    host = (RSyntaxTextArea) getContainer();

    Rectangle clip = g.getClipBounds();
    // An attempt to speed things up for files with long lines.  Note that
    // this will actually slow things down a bit for the common case of
    // regular-length lines, but it doesn't make a perceivable difference.
    clipStart = clip.x;
    clipEnd = clipStart + clip.width;

    lineHeight = host.getLineHeight();
    ascent = host.getMaxAscent(); // metrics.getAscent();
    int heightAbove = clip.y - alloc.y;
    int linesAbove = Math.max(0, heightAbove / lineHeight);

    FoldManager fm = host.getFoldManager();
    linesAbove += fm.getHiddenLineCountAbove(linesAbove, true);
    Rectangle lineArea = lineToRect(a, linesAbove);
    int y = lineArea.y + ascent;
    int x = lineArea.x;
    Element map = getElement();
    int lineCount = map.getElementCount();

    // Whether token styles should always be painted, even in selections
    int selStart = host.getSelectionStart();
    int selEnd = host.getSelectionEnd();

    RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) host.getHighlighter();

    Graphics2D g2d = (Graphics2D) g;
    Token token;
    // System.err.println("Painting lines: " + linesAbove + " to " + (endLine-1));

    TokenPainter painter = host.getTokenPainter();
    int line = linesAbove;
    // int count = 0;
    while (y < clip.y + clip.height + ascent && line < lineCount) {

      Fold fold = fm.getFoldForLine(line);
      Element lineElement = map.getElement(line);
      int startOffset = lineElement.getStartOffset();
      // int endOffset = (line==lineCount ? lineElement.getEndOffset()-1 :
      //							lineElement.getEndOffset()-1);
      int endOffset = lineElement.getEndOffset() - 1; // Why always "-1"?
      h.paintLayeredHighlights(g2d, startOffset, endOffset, a, host, this);

      // Paint a line of text.
      token = document.getTokenListForLine(line);
      if (selStart == selEnd || startOffset >= selEnd || endOffset < selStart) {
        drawLine(painter, token, g2d, x, y, line);
      } else {
        // System.out.println("Drawing line with selection: " + line);
        drawLineWithSelection(painter, token, g2d, x, y, selStart, selEnd);
      }

      if (fold != null && fold.isCollapsed()) {

        // Visible indicator of collapsed lines
        Color c = RSyntaxUtilities.getFoldedLineBottomColor(host);
        if (c != null) {
          g.setColor(c);
          g.drawLine(x, y + lineHeight - ascent - 1, host.getWidth(), y + lineHeight - ascent - 1);
        }

        // Skip to next line to paint, taking extra care for lines with
        // block ends and begins together, e.g. "} else {"
        do {
          int hiddenLineCount = fold.getLineCount();
          if (hiddenLineCount == 0) {
            // Fold parser identified a zero-line fold region.
            // This is really a bug, but we'll be graceful here
            // and avoid an infinite loop.
            break;
          }
          line += hiddenLineCount;
          fold = fm.getFoldForLine(line);
        } while (fold != null && fold.isCollapsed());
      }

      y += lineHeight;
      line++;
      // count++;

    }

    // System.out.println("SyntaxView: lines painted=" + count);

  }
  /**
   * Paints the image.
   *
   * @param g the rendering surface to use
   * @param a the allocated region to render into
   * @see View#paint
   */
  public void paint(Graphics g, Shape a) {
    Color oldColor = g.getColor();
    fBounds = a.getBounds();
    int border = getBorder();
    int x = fBounds.x + border + getSpace(X_AXIS);
    int y = fBounds.y + border + getSpace(Y_AXIS);
    int width = fWidth;
    int height = fHeight;
    int sel = getSelectionState();

    // Make sure my Component is in the right place:
    /*
    if( fComponent == null ) {
    fComponent = new Component() { };
    fComponent.addMouseListener(this);
    fComponent.addMouseMotionListener(this);
    fComponent.setCursor(Cursor.getDefaultCursor());	// use arrow cursor
    fContainer.add(fComponent);
    }
    fComponent.setBounds(x,y,width,height);
    */
    // If no pixels yet, draw gray outline and icon:
    if (!hasPixels(this)) {
      g.setColor(Color.lightGray);
      g.drawRect(x, y, width - 1, height - 1);
      g.setColor(oldColor);
      loadIcons();
      Icon icon = fImage == null ? sMissingImageIcon : sPendingImageIcon;
      if (icon != null) icon.paintIcon(getContainer(), g, x, y);
    }

    // Draw image:
    if (fImage != null) {
      g.drawImage(fImage, x, y, width, height, this);
      // Use the following instead of g.drawImage when
      // BufferedImageGraphics2D.setXORMode is fixed (4158822).

      //  Use Xor mode when selected/highlighted.
      // ! Could darken image instead, but it would be more expensive.
      /*
      if( sel > 0 )
      g.setXORMode(Color.white);
      g.drawImage(fImage,x, y,
      width,height,this);
      if( sel > 0 )
      g.setPaintMode();
      */
    }

    // If selected exactly, we need a black border & grow-box:
    Color bc = getBorderColor();
    if (sel == 2) {
      // Make sure there's room for a border:
      int delta = 2 - border;
      if (delta > 0) {
        x += delta;
        y += delta;
        width -= delta << 1;
        height -= delta << 1;
        border = 2;
      }
      bc = null;
      g.setColor(Color.black);
      // Draw grow box:
      g.fillRect(x + width - 5, y + height - 5, 5, 5);
    }

    // Draw border:
    if (border > 0) {
      if (bc != null) g.setColor(bc);
      // Draw a thick rectangle:
      for (int i = 1; i <= border; i++)
        g.drawRect(x - i, y - i, width - 1 + i + i, height - 1 + i + i);
      g.setColor(oldColor);
    }
  }