@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;
  }
 /**
  * Determines the preferred span for this view along an axis.
  *
  * @param axis may be either View.X_AXIS or View.Y_AXIS
  * @return the span the view would like to be rendered into >= 0. Typically the view is told to
  *     render into the span that is returned, although there is no guarantee. The parent may
  *     choose to resize or break the view.
  * @exception IllegalArgumentException for an invalid axis
  */
 @Override
 public float getPreferredSpan(int axis) {
   updateMetrics();
   switch (axis) {
     case View.X_AXIS:
       float span = longLineWidth + getRhsCorrection(); // fudge factor
       if (host.getEOLMarkersVisible()) {
         span += metrics.charWidth('\u00B6');
       }
       return span;
     case View.Y_AXIS:
       // We update lineHeight here as when this method is first
       // called, lineHeight isn't initialized.  If we don't do it
       // here, we get no vertical scrollbar (as lineHeight==0).
       lineHeight = host != null ? host.getLineHeight() : lineHeight;
       //				return getElement().getElementCount() * lineHeight;
       int visibleLineCount = getElement().getElementCount();
       if (host.isCodeFoldingEnabled()) {
         visibleLineCount -= host.getFoldManager().getHiddenLineCount();
       }
       return visibleLineCount * (float) lineHeight;
     default:
       throw new IllegalArgumentException("Invalid axis: " + axis);
   }
 }
  /**
   * Provides a mapping from the view coordinate space to the logical coordinate space of the model.
   *
   * @param fx the X coordinate >= 0
   * @param fy the Y coordinate >= 0
   * @param a the allocated region to render into
   * @return the location within the model that best represents the given point in the view >= 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.
  }
    /**
     * 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);
    }
 /**
  * Workaround for JTextComponents allowing the caret to be rendered entirely off-screen if the
  * entire "previous" character fit entirely.
  *
  * @return The amount of space to add to the x-axis preferred span.
  */
 private int getRhsCorrection() {
   int rhsCorrection = 10;
   if (host != null) {
     rhsCorrection = host.getRightHandSideCorrection();
   }
   return rhsCorrection;
 }
  /** Overridden to allow for folded regions. */
  @Override
  protected View getViewAtPoint(int x, int y, Rectangle alloc) {

    int lineCount = getViewCount();
    int curY = alloc.y + getOffset(Y_AXIS, 0); // Always at least 1 line
    host = (RSyntaxTextArea) getContainer();
    FoldManager fm = host.getFoldManager();

    for (int line = 1; line < lineCount; line++) {
      int span = getSpan(Y_AXIS, line - 1);
      if (y < curY + span) {
        childAllocation2(line - 1, curY, alloc);
        return getView(line - 1);
      }
      curY += span;
      Fold fold = fm.getFoldForLine(line - 1);
      if (fold != null && fold.isCollapsed()) {
        line += fold.getCollapsedLineCount();
      }
    }

    // Not found - return last line's view.
    childAllocation2(lineCount - 1, curY, alloc);
    return getView(lineCount - 1);
  }
  /**
   * 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);
        }
      }
    }
  }
  /** {@inheritDoc} */
  public int yForLine(Rectangle alloc, int line) throws BadLocationException {

    // Rectangle lineArea = lineToRect(alloc, lineIndex);
    updateMetrics();
    if (metrics != null) {
      // 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;
      FoldManager fm = host.getFoldManager();
      if (!fm.isLineHidden(line)) {
        line -= fm.getHiddenLineCountAbove(line);
        return alloc.y + line * lineHeight;
      }
    }

    return -1;
  }
 /** {@inheritDoc} */
 public int yForLineContaining(Rectangle alloc, int offs) throws BadLocationException {
   if (isAllocationValid()) {
     // TODO: make cached Y_AXIS offsets valid even with folding enabled
     // to speed this back up!
     Rectangle r = (Rectangle) modelToView(offs, alloc, Bias.Forward);
     if (r != null) {
       if (host.isCodeFoldingEnabled()) {
         int line = host.getLineOfOffset(offs);
         FoldManager fm = host.getFoldManager();
         if (fm.isLineHidden(line)) {
           return -1;
         }
       }
       return r.y;
     }
   }
   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;
 }
 /** Checks to see if the font metrics and longest line are up-to-date. */
 private void updateMetrics() {
   host = (RSyntaxTextArea) getContainer();
   Font f = host.getFont();
   if (font != f) {
     // The font changed, we need to recalculate the longest line!
     // This also updates cached font and tab size.
     calculateLongestLine();
   }
 }
Exemple #12
0
  /**
   * Sets the allocation rectangle for a given line's view, but sets the y value to the passed-in
   * value. This should be used instead of {@link #childAllocation(int, Rectangle)} since it allows
   * you to account for hidden lines in collapsed fold regions.
   *
   * @param line
   * @param y
   * @param alloc
   */
  private void childAllocation2(int line, int y, Rectangle alloc) {
    alloc.x += getOffset(X_AXIS, line);
    alloc.y += y;
    alloc.width = getSpan(X_AXIS, line);
    alloc.height = getSpan(Y_AXIS, line);

    // FIXME: This is required due to a bug that I can't track down.  The
    // top margin is being added twice somewhere in wrapped views, so we
    // have to adjust for it here.
    alloc.y -= host.getMargin().top;
  }
Exemple #13
0
 private void handleDocumentEvent(DocumentEvent e, Shape a, ViewFactory f) {
   int n = calculateLineCount();
   if (this.nlines != n) {
     this.nlines = n;
     WrappedSyntaxView.this.preferenceChanged(this, false, true);
     // have to repaint any views after the receiver.
     RSyntaxTextArea textArea = (RSyntaxTextArea) getContainer();
     textArea.repaint();
     // Must also revalidate container so gutter components, such
     // as line numbers, get updated for this line's new height
     Gutter gutter = RSyntaxUtilities.getGutter(textArea);
     if (gutter != null) {
       gutter.revalidate();
       gutter.repaint();
     }
   } else if (a != null) {
     Component c = getContainer();
     Rectangle alloc = (Rectangle) a;
     c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
   }
 }
 /**
  * Returns a token list for the <i>physical</i> line below the physical line containing the
  * specified offset into the document. Note that for this plain (non-wrapped) view, this is simply
  * the token list for the logical line below the line containing <code>offset</code>, since lines
  * are not wrapped.
  *
  * @param offset The offset in question.
  * @return A token list for the physical (and in this view, logical) line after this one. If
  *     <code>offset</code> is in the last physical line in the document, <code>null</code> is
  *     returned.
  */
 public Token getTokenListForPhysicalLineBelow(int offset) {
   RSyntaxDocument document = (RSyntaxDocument) getDocument();
   Element map = document.getDefaultRootElement();
   int lineCount = map.getElementCount();
   int line = map.getElementIndex(offset);
   if (!host.isCodeFoldingEnabled()) {
     if (line < lineCount - 1) {
       return document.getTokenListForLine(line + 1);
     }
   } else {
     FoldManager fm = host.getFoldManager();
     line = fm.getVisibleLineBelow(line);
     if (line >= 0 && line < lineCount) {
       return document.getTokenListForLine(line);
     }
   }
   //		int line = map.getElementIndex(offset);
   //		int lineCount = map.getElementCount();
   //		if (line<lineCount-1)
   //			return document.getTokenListForLine(line+1);
   return null;
 }
  /**
   * Draws the passed-in text using syntax highlighting for the current language. It is assumed that
   * the entire line is either not in a selected region, or painting with a selection-foreground
   * color is turned off.
   *
   * @param painter The painter to render the tokens.
   * @param token The list of tokens to draw.
   * @param g The graphics context in which to draw.
   * @param x The x-coordinate at which to draw.
   * @param y The y-coordinate at which to draw.
   * @return The x-coordinate representing the end of the painted text.
   */
  private float drawLine(
      TokenPainter painter, Token token, Graphics2D g, float x, float y, int line) {

    float nextX = x; // The x-value at the end of our text.
    boolean paintBG = host.getPaintTokenBackgrounds(line, y);

    while (token != null && token.isPaintable() && nextX < clipEnd) {
      nextX = painter.paint(token, g, nextX, y, host, this, clipStart, paintBG);
      token = token.getNextToken();
    }

    // NOTE: We should re-use code from Token (paintBackground()) here,
    // but don't because I'm just too lazy.
    if (host.getEOLMarkersVisible()) {
      g.setColor(host.getForegroundForTokenType(Token.WHITESPACE));
      g.setFont(host.getFontForTokenType(Token.WHITESPACE));
      g.drawString("\u00B6", nextX, y);
    }

    // Return the x-coordinate at the end of the painted text.
    return nextX;
  }
  /**
   * 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);
        }
      }
    }
  }
 /**
  * Determines the preferred span for this view along an axis. This is implemented to provide the
  * superclass behavior after first making sure that the current font metrics are cached (for the
  * nested lines which use the metrics to determine the height of the potentially wrapped lines).
  *
  * @param axis may be either View.X_AXIS or View.Y_AXIS
  * @return the span the view would like to be rendered into. Typically the view is told to render
  *     into the span that is returned, although there is no guarantee. The parent may choose to
  *     resize or break the view.
  * @see View#getPreferredSpan
  */
 public float getPreferredSpan(int axis) {
   updateMetrics();
   float span = 0;
   if (axis == View.X_AXIS) { // Add EOL marker
     span = super.getPreferredSpan(axis);
     span += metrics.charWidth('\u00b6'); // metrics set in updateMetrics
   } else {
     span = super.getPreferredSpan(axis);
     host = (RSyntaxTextArea) getContainer();
     if (host.isCodeFoldingEnabled()) {
       // TODO: Cache y-offsets again to speed this up
       // System.out.println("y-axis baby");
       int lineCount = host.getLineCount();
       FoldManager fm = host.getFoldManager();
       for (int i = 0; i < lineCount; i++) {
         if (fm.isLineHidden(i)) {
           span -= getSpan(View.Y_AXIS, i);
         }
       }
     }
   }
   return span;
 }
Exemple #18
0
  /**
   * Fetches the allocation for the given child view to render into.
   *
   * <p>Overridden to account for lines hidden by collapsed folded regions.
   *
   * @param line The index of the child, >= 0 && < getViewCount()
   * @param a The allocation to this view
   * @return The allocation to the child
   */
  public Shape getChildAllocationImpl(int line, Shape a) {

    Rectangle alloc = getInsideAllocation(a);
    host = (RSyntaxTextArea) getContainer();
    FoldManager fm = host.getFoldManager();
    int y = alloc.y;

    // TODO: Make cached getOffset() calls for Y_AXIS valid even for
    // folding, to speed this up!
    for (int i = 0; i < line; i++) {
      y += getSpan(Y_AXIS, i);
      Fold fold = fm.getFoldForLine(i);
      if (fold != null && fold.isCollapsed()) {
        i += fold.getCollapsedLineCount();
      }
    }

    childAllocation2(line, y, alloc);
    return alloc;
  }
 /**
  * Returns a token list for the <i>physical</i> line above the physical line containing the
  * specified offset into the document. Note that for this plain (non-wrapped) view, this is simply
  * the token list for the logical line above the line containing <code>offset</code>, since lines
  * are not wrapped.
  *
  * @param offset The offset in question.
  * @return A token list for the physical (and in this view, logical) line before this one. If
  *     <code>offset</code> is in the first line in the document, <code>null</code> is returned.
  */
 public Token getTokenListForPhysicalLineAbove(int offset) {
   RSyntaxDocument document = (RSyntaxDocument) getDocument();
   Element map = document.getDefaultRootElement();
   int line = map.getElementIndex(offset);
   FoldManager fm = host.getFoldManager();
   if (fm == null) {
     line--;
     if (line >= 0) {
       return document.getTokenListForLine(line);
     }
   } else {
     line = fm.getVisibleLineAbove(line);
     if (line >= 0) {
       return document.getTokenListForLine(line);
     }
   }
   //		int line = map.getElementIndex(offset) - 1;
   //		if (line>=0)
   //			return document.getTokenListForLine(line);
   return null;
 }
Exemple #20
0
  /**
   * Draws a single view (i.e., a line of text for a wrapped view), wrapping the text onto multiple
   * lines if necessary. Any selected text is rendered with the editor's "selected text" color.
   *
   * @param painter The painter to use to render tokens.
   * @param g The graphics context in which to paint.
   * @param r The rectangle in which to paint.
   * @param view The <code>View</code> to paint.
   * @param fontHeight The height of the font being used.
   * @param y The y-coordinate at which to begin painting.
   * @param selStart The start of the selection.
   * @param selEnd The end of the selection.
   */
  protected void drawViewWithSelection(
      TokenPainter painter,
      Graphics2D g,
      Rectangle r,
      View view,
      int fontHeight,
      int y,
      int selStart,
      int selEnd) {

    float x = r.x;

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

    RSyntaxDocument document = (RSyntaxDocument) getDocument();
    Element map = getElement();

    int p0 = view.getStartOffset();
    int lineNumber = map.getElementIndex(p0);
    int p1 = view.getEndOffset(); // - 1;

    setSegment(p0, p1 - 1, document, drawSeg);
    // System.err.println("drawSeg=='" + drawSeg + "' (p0/p1==" + p0 + "/" + p1 + ")");
    int start = p0 - drawSeg.offset;
    Token token = document.getTokenListForLine(lineNumber);

    // If this line is an empty line, then the token list is simply a
    // null token.  In this case, the line highlight will be skipped in
    // the loop below, so unfortunately we must manually do it here.
    if (token != null && token.getType() == Token.NULL) {
      h.paintLayeredHighlights(g, p0, p1, r, host, this);
      return;
    }

    // Loop through all tokens in this view and paint them!
    while (token != null && token.isPaintable()) {

      int p = calculateBreakPosition(p0, token, x);
      x = r.x;

      h.paintLayeredHighlights(g, p0, p, r, host, this);

      while (token != null && token.isPaintable() && token.getEndOffset() - 1 < p) { // <=p) {

        // Selection starts in this token
        if (token.containsPosition(selStart)) {

          if (selStart > token.getOffset()) {
            tempToken.copyFrom(token);
            tempToken.textCount = selStart - tempToken.getOffset();
            x = painter.paint(tempToken, g, x, y, host, this);
            tempToken.textCount = token.length();
            tempToken.makeStartAt(selStart);
            // Clone required since token and tempToken must be
            // different tokens for else statement below
            token = new TokenImpl(tempToken);
          }

          int selCount = Math.min(token.length(), selEnd - token.getOffset());
          if (selCount == token.length()) {
            x = painter.paintSelected(token, g, x, y, host, this);
          } else {
            tempToken.copyFrom(token);
            tempToken.textCount = selCount;
            x = painter.paintSelected(tempToken, g, x, y, host, this);
            tempToken.textCount = token.length();
            tempToken.makeStartAt(token.getOffset() + selCount);
            token = tempToken;
            x = painter.paint(token, g, x, y, host, this);
          }

        }

        // Selection ends in this token
        else if (token.containsPosition(selEnd)) {
          tempToken.copyFrom(token);
          tempToken.textCount = selEnd - tempToken.getOffset();
          x = painter.paintSelected(tempToken, g, x, y, host, this);
          tempToken.textCount = token.length();
          tempToken.makeStartAt(selEnd);
          token = tempToken;
          x = painter.paint(token, g, x, y, host, this);
        }

        // This token is entirely selected
        else if (token.getOffset() >= selStart && token.getEndOffset() <= selEnd) {
          x = painter.paintSelected(token, g, x, y, host, this);
        }

        // This token is entirely unselected
        else {
          x = painter.paint(token, g, x, y, host, this);
        }

        token = token.getNextToken();
      }

      // If there's a token that's going to be split onto the next line
      if (token != null && token.isPaintable() && token.getOffset() < p) {

        int tokenOffset = token.getOffset();
        Token orig = token;
        token =
            new TokenImpl(
                drawSeg, tokenOffset - start, p - 1 - start, tokenOffset, token.getType());

        // Selection starts in this token
        if (token.containsPosition(selStart)) {

          if (selStart > token.getOffset()) {
            tempToken.copyFrom(token);
            tempToken.textCount = selStart - tempToken.getOffset();
            x = painter.paint(tempToken, g, x, y, host, this);
            tempToken.textCount = token.length();
            tempToken.makeStartAt(selStart);
            // Clone required since token and tempToken must be
            // different tokens for else statement below
            token = new TokenImpl(tempToken);
          }

          int selCount = Math.min(token.length(), selEnd - token.getOffset());
          if (selCount == token.length()) {
            x = painter.paintSelected(token, g, x, y, host, this);
          } else {
            tempToken.copyFrom(token);
            tempToken.textCount = selCount;
            x = painter.paintSelected(tempToken, g, x, y, host, this);
            tempToken.textCount = token.length();
            tempToken.makeStartAt(token.getOffset() + selCount);
            token = tempToken;
            x = painter.paint(token, g, x, y, host, this);
          }

        }

        // Selection ends in this token
        else if (token.containsPosition(selEnd)) {
          tempToken.copyFrom(token);
          tempToken.textCount = selEnd - tempToken.getOffset();
          x = painter.paintSelected(tempToken, g, x, y, host, this);
          tempToken.textCount = token.length();
          tempToken.makeStartAt(selEnd);
          token = tempToken;
          x = painter.paint(token, g, x, y, host, this);
        }

        // This token is entirely selected
        else if (token.getOffset() >= selStart && token.getEndOffset() <= selEnd) {
          x = painter.paintSelected(token, g, x, y, host, this);
        }

        // This token is entirely unselected
        else {
          x = painter.paint(token, g, x, y, host, this);
        }

        token = new TokenImpl(orig);
        ((TokenImpl) token).makeStartAt(p);
      }

      p0 = (p == p0) ? p1 : p;
      y += fontHeight;
    } // End of while (token!=null && token.isPaintable()).

    // NOTE: We should re-use code from Token (paintBackground()) here,
    // but don't because I'm just too lazy.
    if (host.getEOLMarkersVisible()) {
      g.setColor(host.getForegroundForTokenType(Token.WHITESPACE));
      g.setFont(host.getFontForTokenType(Token.WHITESPACE));
      g.drawString("\u00B6", x, y - fontHeight);
    }
  }
Exemple #21
0
  /**
   * Draws a single view (i.e., a line of text for a wrapped view), wrapping the text onto multiple
   * lines if necessary.
   *
   * @param painter The painter to use to render tokens.
   * @param g The graphics context in which to paint.
   * @param r The rectangle in which to paint.
   * @param view The <code>View</code> to paint.
   * @param fontHeight The height of the font being used.
   * @param y The y-coordinate at which to begin painting.
   */
  protected void drawView(
      TokenPainter painter, Graphics2D g, Rectangle r, View view, int fontHeight, int y) {

    float x = r.x;

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

    RSyntaxDocument document = (RSyntaxDocument) getDocument();
    Element map = getElement();

    int p0 = view.getStartOffset();
    int lineNumber = map.getElementIndex(p0);
    int p1 = view.getEndOffset(); // - 1;

    setSegment(p0, p1 - 1, document, drawSeg);
    // System.err.println("drawSeg=='" + drawSeg + "' (p0/p1==" + p0 + "/" + p1 + ")");
    int start = p0 - drawSeg.offset;
    Token token = document.getTokenListForLine(lineNumber);

    // If this line is an empty line, then the token list is simply a
    // null token.  In this case, the line highlight will be skipped in
    // the loop below, so unfortunately we must manually do it here.
    if (token != null && token.getType() == Token.NULL) {
      h.paintLayeredHighlights(g, p0, p1, r, host, this);
      return;
    }

    // Loop through all tokens in this view and paint them!
    while (token != null && token.isPaintable()) {

      int p = calculateBreakPosition(p0, token, x);
      x = r.x;

      h.paintLayeredHighlights(g, p0, p, r, host, this);

      while (token != null && token.isPaintable() && token.getEndOffset() - 1 < p) { // <=p) {
        x = painter.paint(token, g, x, y, host, this);
        token = token.getNextToken();
      }

      if (token != null && token.isPaintable() && token.getOffset() < p) {
        int tokenOffset = token.getOffset();
        tempToken.set(
            drawSeg.array, tokenOffset - start, p - 1 - start, tokenOffset, token.getType());
        painter.paint(tempToken, g, x, y, host, this);
        tempToken.copyFrom(token);
        tempToken.makeStartAt(p);
        token = new TokenImpl(tempToken);
      }

      p0 = (p == p0) ? p1 : p;
      y += fontHeight;
    } // End of while (token!=null && token.isPaintable()).

    // NOTE: We should re-use code from Token (paintBackground()) here,
    // but don't because I'm just too lazy.
    if (host.getEOLMarkersVisible()) {
      g.setColor(host.getForegroundForTokenType(Token.WHITESPACE));
      g.setFont(host.getFontForTokenType(Token.WHITESPACE));
      g.drawString("\u00B6", x, y - fontHeight);
    }
  }
Exemple #22
0
    /**
     * Provides a mapping from the view coordinate space to the logical coordinate space of the
     * model.
     *
     * @param fx the X coordinate
     * @param fy the Y coordinate
     * @param a the allocated region to render into
     * @return the location within the model that best represents the given point in the view
     * @see View#viewToModel
     */
    @Override
    public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) {

      // PENDING(prinz) implement bias properly
      bias[0] = Position.Bias.Forward;

      Rectangle alloc = (Rectangle) a;
      RSyntaxDocument doc = (RSyntaxDocument) getDocument();
      int x = (int) fx;
      int y = (int) fy;
      if (y < alloc.y) {
        // above the area covered by this icon, so the the position
        // is assumed to be the start of the coverage for this view.
        return getStartOffset();
      } else if (y > alloc.y + alloc.height) {
        // below the area covered by this icon, so the the position
        // is assumed to be the end of the coverage for this view.
        return getEndOffset() - 1;
      } else {

        // positioned 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.

        RSyntaxTextArea textArea = (RSyntaxTextArea) getContainer();
        alloc.height = textArea.getLineHeight();
        int p1 = getEndOffset();

        // Get the token list for this line so we don't have to keep
        // recomputing it if this logical line spans multiple
        // physical lines.
        Element map = doc.getDefaultRootElement();
        int p0 = getStartOffset();
        int line = map.getElementIndex(p0);
        Token tlist = doc.getTokenListForLine(line);

        // Look at each physical line-chunk of this logical line.
        while (p0 < p1) {

          // We can always use alloc.x since we always break
          // lines so they start at the beginning of a physical
          // line.
          TokenSubList subList =
              TokenUtils.getSubTokenList(
                  tlist, p0, WrappedSyntaxView.this, textArea, alloc.x, lineCountTempToken);
          tlist = subList != null ? subList.tokenList : null;
          int p = calculateBreakPosition(p0, tlist, alloc.x);

          // If desired view position is in this physical chunk.
          if ((y >= alloc.y) && (y < (alloc.y + alloc.height))) {

            // Point is to the left of the line
            if (x < alloc.x) {
              return p0;
            }

            // Point is to the right of the line
            else if (x > alloc.x + alloc.width) {
              return p - 1;
            }

            // Point is in this physical line!
            else {

              // Start at alloc.x since this chunk starts
              // at the beginning of a physical line.
              int n = tlist.getListOffset(textArea, WrappedSyntaxView.this, alloc.x, x);

              // NOTE:  We needed to add the max() with
              // p0 as getTokenListForLine returns -1
              // for empty lines (just a null token).
              // How did this work before?
              // FIXME:  Have null tokens have their
              // offset but a -1 length.
              return Math.max(Math.min(n, p1 - 1), p0);
            } // End of else.
          } // End of if ((y>=alloc.y) && ...

          p0 = (p == p0) ? p1 : p;
          alloc.y += alloc.height;
        } // End of while (p0<p1).

        return getEndOffset() - 1;
      } // End of else.
    }
  /**
   * 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);

  }
  /**
   * Draws the passed-in text using syntax highlighting for the current language. Tokens are checked
   * for being in a selected region, and are rendered appropriately if they are.
   *
   * @param painter The painter to render the tokens.
   * @param token The list of tokens to draw.
   * @param g The graphics context in which to draw.
   * @param x The x-coordinate at which to draw.
   * @param y The y-coordinate at which to draw.
   * @param selStart The start of the selection.
   * @param selEnd The end of the selection.
   * @return The x-coordinate representing the end of the painted text.
   */
  private float drawLineWithSelection(
      TokenPainter painter, Token token, Graphics2D g, float x, float y, int selStart, int selEnd) {

    float nextX = x; // The x-value at the end of our text.
    boolean useSTC = host.getUseSelectedTextColor();

    while (token != null && token.isPaintable() && nextX < clipEnd) {

      // Selection starts in this token
      if (token.containsPosition(selStart)) {

        if (selStart > token.getOffset()) {
          tempToken.copyFrom(token);
          tempToken.textCount = selStart - tempToken.getOffset();
          nextX = painter.paint(tempToken, g, nextX, y, host, this, clipStart);
          tempToken.textCount = token.length();
          tempToken.makeStartAt(selStart);
          // Clone required since token and tempToken must be
          // different tokens for else statement below
          token = new TokenImpl(tempToken);
        }

        int tokenLen = token.length();
        int selCount = Math.min(tokenLen, selEnd - token.getOffset());
        if (selCount == tokenLen) {
          nextX = painter.paintSelected(token, g, nextX, y, host, this, clipStart, useSTC);
        } else {
          tempToken.copyFrom(token);
          tempToken.textCount = selCount;
          nextX = painter.paintSelected(tempToken, g, nextX, y, host, this, clipStart, useSTC);
          tempToken.textCount = token.length();
          tempToken.makeStartAt(token.getOffset() + selCount);
          token = tempToken;
          nextX = painter.paint(token, g, nextX, y, host, this, clipStart);
        }

      }

      // Selection ends in this token
      else if (token.containsPosition(selEnd)) {
        tempToken.copyFrom(token);
        tempToken.textCount = selEnd - tempToken.getOffset();
        nextX = painter.paintSelected(tempToken, g, nextX, y, host, this, clipStart, useSTC);
        tempToken.textCount = token.length();
        tempToken.makeStartAt(selEnd);
        token = tempToken;
        nextX = painter.paint(token, g, nextX, y, host, this, clipStart);
      }

      // This token is entirely selected
      else if (token.getOffset() >= selStart && token.getEndOffset() <= selEnd) {
        nextX = painter.paintSelected(token, g, nextX, y, host, this, clipStart, useSTC);
      }

      // This token is entirely unselected
      else {
        nextX = painter.paint(token, g, nextX, y, host, this, clipStart);
      }

      token = token.getNextToken();
    }

    // NOTE: We should re-use code from Token (paintBackground()) here,
    // but don't because I'm just too lazy.
    if (host.getEOLMarkersVisible()) {
      g.setColor(host.getForegroundForTokenType(Token.WHITESPACE));
      g.setFont(host.getFontForTokenType(Token.WHITESPACE));
      g.drawString("\u00B6", nextX, y);
    }

    // Return the x-coordinate at the end of the painted text.
    return nextX;
  }