/**
  * 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 >= 0
  * @param a the allocated region to render into
  * @param b a bias value of either <code>Position.Bias.Forward</code> or <code>
  *     Position.Bias.Backward</code>
  * @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 {
   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);
 }
예제 #2
0
 /**
  * Fetches the child view index representing the given position in the model.
  *
  * @param pos the position >= 0
  * @return index of the view representing the given position, or -1 if no view represents that
  *     position
  */
 protected int getViewIndexAtPosition(int pos) {
   if (pos >= getStartOffset() && (pos < getEndOffset())) {
     for (int counter = getViewCount() - 1; counter >= 0; counter--) {
       View v = getView(counter);
       if (pos >= v.getStartOffset() && pos < v.getEndOffset()) {
         return counter;
       }
     }
   }
   return -1;
 }
예제 #3
0
    /**
     * Creates a view that can be used to represent the current piece of the flow. This can be
     * either an entire view from the logical view, or a fragment of the logical view.
     *
     * @param fv the view holding the flow
     * @param startOffset the start location for the view being created
     * @param spanLeft the about of span left to fill in the row
     * @param rowIndex the row the view will be placed into
     */
    protected View createView(FlowView fv, int startOffset, int spanLeft, int rowIndex) {
      // Get the child view that contains the given starting position
      View lv = getLogicalView(fv);
      int childIndex = lv.getViewIndex(startOffset, Position.Bias.Forward);
      View v = lv.getView(childIndex);
      if (startOffset == v.getStartOffset()) {
        // return the entire view
        return v;
      }

      // return a fragment.
      v = v.createFragment(startOffset, v.getEndOffset());
      return v;
    }
예제 #4
0
    /**
     * Adjusts the given row if possible to fit within the layout span. By default this will try to
     * find the highest break weight possible nearest the end of the row. If a forced break is
     * encountered, the break will be positioned there.
     *
     * @param rowIndex the row to adjust to the current layout span.
     * @param desiredSpan the current layout span >= 0
     * @param x the location r starts at.
     */
    protected void adjustRow(FlowView fv, int rowIndex, int desiredSpan, int x) {
      final int flowAxis = fv.getFlowAxis();
      View r = fv.getView(rowIndex);
      int n = r.getViewCount();
      int span = 0;
      int bestWeight = BadBreakWeight;
      int bestSpan = 0;
      int bestIndex = -1;
      int bestOffset = 0;
      View v;
      for (int i = 0; i < n; i++) {
        v = r.getView(i);
        int spanLeft = desiredSpan - span;

        int w = v.getBreakWeight(flowAxis, x + span, spanLeft);
        if ((w >= bestWeight) && (w > BadBreakWeight)) {
          bestWeight = w;
          bestIndex = i;
          bestSpan = span;
          if (w >= ForcedBreakWeight) {
            // it's a forced break, so there is
            // no point in searching further.
            break;
          }
        }
        span += v.getPreferredSpan(flowAxis);
      }
      if (bestIndex < 0) {
        // there is nothing that can be broken, leave
        // it in it's current state.
        return;
      }

      // Break the best candidate view, and patch up the row.
      int spanLeft = desiredSpan - bestSpan;
      v = r.getView(bestIndex);
      v = v.breakView(flowAxis, v.getStartOffset(), x + bestSpan, spanLeft);
      View[] va = new View[1];
      va[0] = v;
      View lv = getLogicalView(fv);
      for (int i = bestIndex; i < n; i++) {
        View tmpView = r.getView(i);
        if (contains(lv, tmpView)) {
          tmpView.setParent(lv);
        } else if (tmpView.getViewCount() > 0) {
          recursiveReparent(tmpView, lv);
        }
      }
      r.replace(bestIndex, n - bestIndex, va);
    }
예제 #5
0
  /**
   * Forwards the given <code>DocumentEvent</code> to the child views that need to be notified of
   * the change to the model. If there were changes to the element this view is responsible for,
   * that should be considered when forwarding (i.e. new child views should not get notified).
   *
   * @param ec changes to the element this view is responsible for (may be <code>null</code> if
   *     there were no changes).
   * @param e the change information from the associated document
   * @param a the current allocation of the view
   * @param f the factory to use to rebuild if the view has children
   * @see #insertUpdate
   * @see #removeUpdate
   * @see #changedUpdate
   * @since 1.3
   */
  protected void forwardUpdate(
      DocumentEvent.ElementChange ec, DocumentEvent e, Shape a, ViewFactory f) {
    Element elem = getElement();
    int pos = e.getOffset();
    int index0 = getViewIndex(pos, Position.Bias.Forward);
    if (index0 == -1 && e.getType() == DocumentEvent.EventType.REMOVE && pos >= getEndOffset()) {
      // Event beyond our offsets. We may have represented this, that is
      // the remove may have removed one of our child Elements that
      // represented this, so, we should foward to last element.
      index0 = getViewCount() - 1;
    }
    int index1 = index0;
    View v = (index0 >= 0) ? getView(index0) : null;
    if (v != null) {
      if ((v.getStartOffset() == pos) && (pos > 0)) {
        // If v is at a boundary, forward the event to the previous
        // view too.
        index0 = Math.max(index0 - 1, 0);
      }
    }
    if (e.getType() != DocumentEvent.EventType.REMOVE) {
      index1 = getViewIndex(pos + e.getLength(), Position.Bias.Forward);
      if (index1 < 0) {
        index1 = getViewCount() - 1;
      }
    }
    int hole0 = index1 + 1;
    int hole1 = hole0;
    Element[] addedElems = (ec != null) ? ec.getChildrenAdded() : null;
    if ((addedElems != null) && (addedElems.length > 0)) {
      hole0 = ec.getIndex();
      hole1 = hole0 + addedElems.length - 1;
    }

    // forward to any view not in the forwarding hole
    // formed by added elements (i.e. they will be updated
    // by initialization.
    index0 = Math.max(index0, 0);
    for (int i = index0; i <= index1; i++) {
      if (!((i >= hole0) && (i <= hole1))) {
        v = getView(i);
        if (v != null) {
          Shape childAlloc = getChildAllocation(i, a);
          forwardUpdateToView(v, e, childAlloc, f);
        }
      }
    }
  }
    /**
     * Paints a portion of a highlight.
     *
     * @param g the graphics context
     * @param offs0 the starting model offset &gt;= 0
     * @param offs1 the ending model offset &gt;= offs1
     * @param bounds the bounding box of the view, which is not necessarily the region to paint.
     * @param c the editor
     * @param view View painting for
     * @return region drawing occurred in
     */
    public Shape paintLayer(
        Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c, View view) {
      Color color = getColor();

      if (color == null) {
        g.setColor(c.getSelectionColor());
      } else {
        g.setColor(color);
      }

      Rectangle r;

      if (offs0 == view.getStartOffset() && offs1 == view.getEndOffset()) {
        // Contained in view, can just use bounds.
        if (bounds instanceof Rectangle) {
          r = (Rectangle) bounds;
        } else {
          r = bounds.getBounds();
        }
      } else {
        // Should only render part of View.
        try {
          // --- determine locations ---
          Shape shape =
              view.modelToView(offs0, Position.Bias.Forward, offs1, Position.Bias.Backward, bounds);
          r = (shape instanceof Rectangle) ? (Rectangle) shape : shape.getBounds();
        } catch (BadLocationException e) {
          // can't render
          r = null;
        }
      }

      if (r != null) {
        // If we are asked to highlight, we should draw something even
        // if the model-to-view projection is of zero width (6340106).
        r.width = Math.max(r.width, 1);

        g.fillRect(r.x, r.y, r.width, r.height);
      }

      return r;
    }
  /**
   * 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);
  }
예제 #8
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);
    }
  }
예제 #9
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);
    }
  }