예제 #1
0
  /**
   * Repaints area of the control corresponding to a range of offsets in the document.
   *
   * @param startOffset Starting offset of the range.
   * @param endOffset Ending offset of the range.
   */
  private void repaintRange(int startOffset, int endOffset) {

    Graphics g = this.hostComponent.createDefaultGraphics();

    LayoutContext context = this.createLayoutContext(g);

    Rectangle startBounds = this.rootBox.getCaret(context, startOffset).getBounds();
    int top1 = startBounds.getY();
    int bottom1 = top1 + startBounds.getHeight();

    Rectangle endBounds = this.rootBox.getCaret(context, endOffset).getBounds();
    int top2 = endBounds.getY();
    int bottom2 = top2 + endBounds.getHeight();

    int top = Math.min(top1, top2);
    int bottom = Math.max(bottom1, bottom2);
    if (top == bottom) {
      // Account for zero-height horizontal carets
      this.hostComponent.repaint(0, top - 1, this.getLayoutWidth(), bottom - top + 1);
    } else {
      this.hostComponent.repaint(0, top, this.getLayoutWidth(), bottom - top);
    }

    g.dispose();
  }
예제 #2
0
  /**
   * Calls layout() on the rootBox until the y-coordinate of a caret at the given offset converges,
   * i.e. is less than LAYOUT_TOLERANCE pixels from the last call.
   *
   * @param offset Offset around which we should lay out boxes.
   */
  private void iterateLayout(int offset) {

    int repaintStart = Integer.MAX_VALUE;
    int repaintEnd = 0;
    Graphics g = this.hostComponent.createDefaultGraphics();
    LayoutContext context = this.createLayoutContext(g);
    int layoutY = this.rootBox.getCaret(context, offset).getY();

    while (true) {

      int oldLayoutY = layoutY;
      IntRange repaintRange =
          this.rootBox.layout(context, layoutY - LAYOUT_WINDOW / 2, layoutY + LAYOUT_WINDOW / 2);
      if (repaintRange != null) {
        repaintStart = Math.min(repaintStart, repaintRange.getStart());
        repaintEnd = Math.max(repaintEnd, repaintRange.getEnd());
      }

      layoutY = this.rootBox.getCaret(context, offset).getY();
      if (Math.abs(layoutY - oldLayoutY) < LAYOUT_TOLERANCE) {
        break;
      }
    }
    g.dispose();

    if (repaintStart < repaintEnd) {
      Rectangle viewport = this.hostComponent.getViewport();
      if (repaintStart < viewport.getY() + viewport.getHeight() && repaintEnd > viewport.getY()) {
        int start = Math.max(repaintStart, viewport.getY());
        int end = Math.min(repaintEnd, viewport.getY() + viewport.getHeight());
        this.hostComponent.repaint(viewport.getX(), start, viewport.getWidth(), end - start);
      }
    }
  }
예제 #3
0
 /** Repaints the area of the caret. */
 private void repaintCaret() {
   if (this.caret != null) {
     // caret may be null when document is first set
     Rectangle bounds = this.caret.getBounds();
     this.hostComponent.repaint(
         bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight());
   }
 }
예제 #4
0
  private void scrollCaretVisible() {

    Rectangle caretBounds = this.caret.getBounds();
    Rectangle viewport = this.hostComponent.getViewport();

    int x = viewport.getX();
    int y = 0;
    int offset = getCaretOffset();
    if (offset == 1) {
      y = 0;
    } else if (offset == getDocument().getLength() - 1) {
      if (this.rootBox.getHeight() < viewport.getHeight()) {
        y = 0;
      } else {
        y = this.rootBox.getHeight() - viewport.getHeight();
      }
    } else if (caretBounds.getY() < viewport.getY()) {
      y = caretBounds.getY();
    } else if (caretBounds.getY() + caretBounds.getHeight()
        > viewport.getY() + viewport.getHeight()) {
      y = caretBounds.getY() + caretBounds.getHeight() - viewport.getHeight();
    } else {
      // no scrolling required
      return;
    }
    this.hostComponent.scrollTo(x, y);
  }
예제 #5
0
  /**
   * Paints the contents of the widget in the given Graphics at the given point.
   *
   * @param g Graphics in which to draw the widget contents
   * @param x x-coordinate at which to draw the widget
   * @param y y-coordinate at which to draw the widget
   */
  public void paint(Graphics g, int x, int y) {

    if (this.rootBox == null) {
      return;
    }

    LayoutContext context = this.createLayoutContext(g);

    // Since we may be scrolling to sections of the document that have
    // yet to be layed out, lay out any exposed area.
    //
    // TODO: this will probably be inaccurate, since we should really
    // iterate the layout, but we don't have an offset around which
    // to iterate...what to do, what to do....
    Rectangle rect = g.getClipBounds();
    int oldHeight = this.rootBox.getHeight();
    this.rootBox.layout(context, rect.getY(), rect.getY() + rect.getHeight());
    if (this.rootBox.getHeight() != oldHeight) {
      this.hostComponent.setPreferredSize(this.rootBox.getWidth(), this.rootBox.getHeight());
    }

    this.rootBox.paint(context, 0, 0);
    if (this.caretVisible) {
      this.caret.draw(g, this.caretColor);
    }

    // Debug hash marks
    /*
     * ColorResource grey = g.createColor(new Color(160, 160, 160));
     * ColorResource oldColor = g.setColor(grey); for (int y2 = rect.getY()
     * - rect.getY() % 50; y2 < rect.getY() + rect.getHeight(); y2 += 50) {
     * g.drawLine(x, y + y2, x+10, y + y2);
     * g.drawString(Integer.toString(y2), x + 15, y + y2 - 10); }
     * g.setColor(oldColor); grey.dispose();
     */
  }
예제 #6
0
  /**
   * Re-layout the entire widget, due to either a layout width change or a stylesheet range. This
   * method does the actual setting of the width and stylesheet, since it needs to know where the
   * caret is <i>before</i> the change, so that it can do a reasonable job of restoring the position
   * of the viewport after the change.
   *
   * @param newWidth New width for the widget.
   * @param newStyleSheet New stylesheet for the widget.
   */
  private void relayoutAll(int newWidth, StyleSheet newStyleSheet) {

    Graphics g = this.hostComponent.createDefaultGraphics();
    LayoutContext context = this.createLayoutContext(g);

    Rectangle viewport = this.hostComponent.getViewport();

    // true if the caret is within the viewport
    //
    // TODO: incorrect if caret near the bottom and the viewport is
    // shrinking
    // To fix, we probably need to save the viewport height, just like
    // we now store viewport width (as layout width).
    boolean caretVisible = viewport.intersects(this.caret.getBounds());

    // distance from the top of the viewport to the top of the caret
    // use this if the caret is visible in the viewport
    int relCaretY = 0;

    // offset around which we are laying out
    // this is also where we put the top of the viewport if the caret
    // isn't visible
    int offset;

    if (caretVisible) {
      relCaretY = this.caret.getY() - viewport.getY();
      offset = this.getCaretOffset();
    } else {
      offset = this.rootBox.viewToModel(context, 0, viewport.getY());
    }

    this.layoutWidth = newWidth;
    this.styleSheet = newStyleSheet;

    // Re-create the context, since it holds the old stylesheet
    context = this.createLayoutContext(g);

    this.createRootBox();

    this.iterateLayout(offset);

    this.hostComponent.setPreferredSize(this.rootBox.getWidth(), this.rootBox.getHeight());

    this.caret = this.rootBox.getCaret(context, this.getCaretOffset());

    if (caretVisible) {
      int viewportY = this.caret.getY() - Math.min(relCaretY, viewport.getHeight());
      viewportY = Math.min(this.rootBox.getHeight() - viewport.getHeight(), viewportY);
      viewportY = Math.max(0, viewportY); // this must appear after the
      // above line, since
      // that line might set viewportY negative
      this.hostComponent.scrollTo(viewport.getX(), viewportY);
      this.scrollCaretVisible();
    } else {
      int viewportY = this.rootBox.getCaret(context, offset).getY();
      this.hostComponent.scrollTo(viewport.getX(), viewportY);
    }

    this.hostComponent.repaint();

    g.dispose();
  }