예제 #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
 public int viewToModel(int x, int y) {
   Graphics g = this.hostComponent.createDefaultGraphics();
   LayoutContext context = this.createLayoutContext(g);
   int offset = this.rootBox.viewToModel(context, x, y);
   g.dispose();
   return offset;
 }
예제 #4
0
  public void moveToPreviousLine(boolean select) {
    int x = this.magicX == -1 ? this.caret.getBounds().getX() : this.magicX;

    Graphics g = this.hostComponent.createDefaultGraphics();
    int offset =
        this.rootBox.getPreviousLineOffset(this.createLayoutContext(g), this.getCaretOffset(), x);
    g.dispose();

    this.moveTo(offset, select);
    this.magicX = x;
  }
예제 #5
0
  /** Lay out the area around the caret. */
  private void relayout() {

    long start = System.currentTimeMillis();

    int oldHeight = this.rootBox.getHeight();

    this.iterateLayout(this.getCaretOffset());

    if (this.rootBox.getHeight() != oldHeight) {
      this.hostComponent.setPreferredSize(this.rootBox.getWidth(), this.rootBox.getHeight());
    }

    Graphics g = this.hostComponent.createDefaultGraphics();
    LayoutContext context = this.createLayoutContext(g);
    this.caret = this.rootBox.getCaret(context, this.getCaretOffset());
    g.dispose();

    if (this.isDebugging()) {
      long end = System.currentTimeMillis();
      System.out.println("VexWidget layout took " + (end - start) + "ms");
    }
  }
예제 #6
0
  public void moveTo(int offset, boolean select) {

    if (offset >= 1 && offset <= this.document.getLength() - 1) {

      // repaint the selection area, if any
      this.repaintCaret();
      this.repaintRange(this.getSelectionStart(), this.getSelectionEnd());

      VEXElement oldElement = this.currentElement;

      this.caretOffset = offset;

      this.currentElement = this.document.getElementAt(offset);

      if (select) {
        this.selectionStart = Math.min(this.mark, this.caretOffset);
        this.selectionEnd = Math.max(this.mark, this.caretOffset);

        // move selectionStart and selectionEnd to make sure we don't
        // select a partial element
        VEXElement commonElement =
            this.document.findCommonElement(this.selectionStart, this.selectionEnd);

        VEXElement element = this.document.getElementAt(this.selectionStart);
        while (element != commonElement) {
          this.selectionStart = element.getStartOffset();
          element = this.document.getElementAt(this.selectionStart);
        }

        element = this.document.getElementAt(this.selectionEnd);
        while (element != commonElement) {
          this.selectionEnd = element.getEndOffset() + 1;
          element = this.document.getElementAt(this.selectionEnd);
        }

      } else {
        this.mark = offset;
        this.selectionStart = offset;
        this.selectionEnd = offset;
      }

      if (this.beginWorkCount == 0) {
        this.relayout();
      }

      Graphics g = this.hostComponent.createDefaultGraphics();
      LayoutContext context = this.createLayoutContext(g);
      this.caret = this.rootBox.getCaret(context, offset);

      VEXElement element = this.getCurrentElement();
      if (element != oldElement) {
        this.caretColor = Color.BLACK;
        while (element != null) {
          Color bgColor = this.styleSheet.getStyles(element).getBackgroundColor();
          if (bgColor != null) {
            int red = ~bgColor.getRed() & 0xff;
            int green = ~bgColor.getGreen() & 0xff;
            int blue = ~bgColor.getBlue() & 0xff;
            this.caretColor = new Color(red, green, blue);
            break;
          }
          element = element.getParent();
        }
      }

      g.dispose();

      this.magicX = -1;

      this.scrollCaretVisible();

      this.hostComponent.fireSelectionChanged();

      this.caretVisible = true;

      this.repaintRange(this.getSelectionStart(), this.getSelectionEnd());
    }
  }
예제 #7
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();
  }
예제 #8
0
 private void createRootBox() {
   Graphics g = this.hostComponent.createDefaultGraphics();
   LayoutContext context = this.createLayoutContext(g);
   this.rootBox = new RootBox(context, this.document.getRootElement(), this.getLayoutWidth());
   g.dispose();
 }