/**
     * 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);
    }
    /**
     * Update the flow on the given FlowView. By default, this causes all of the rows (child views)
     * to be rebuilt to match the given constraints for each row. This is called by a
     * FlowView.layout to update the child views in the flow.
     *
     * @param fv the view to reflow
     */
    public void layout(FlowView fv) {
      int p0 = fv.getStartOffset();
      int p1 = fv.getEndOffset();

      // we want to preserve all views from the logicalView from being
      // removed
      View lv = getLogicalView(fv);
      int n = lv.getViewCount();
      for (int i = 0; i < n; i++) {
        View v = lv.getView(i);
        v.setParent(lv);
      }
      fv.removeAll();
      for (int rowIndex = 0; p0 < p1; rowIndex++) {
        View row = fv.createRow();
        fv.append(row);

        // layout the row to the current span.  If nothing fits,
        // force something.
        int next = layoutRow(fv, rowIndex, p0);
        if (row.getViewCount() == 0) {
          row.append(createView(fv, p0, Integer.MAX_VALUE, rowIndex));
          next = row.getEndOffset();
        }
        if (next <= p0) {
          throw new StateInvariantError("infinite loop in formatting");
        } else {
          p0 = next;
        }
      }
    }
 private boolean contains(View logicalView, View v) {
   int n = logicalView.getViewCount();
   for (int i = 0; i < n; i++) {
     if (logicalView.getView(i) == v) {
       return true;
     }
   }
   return false;
 }
 private void recursiveReparent(View v, View logicalView) {
   int n = v.getViewCount();
   for (int i = 0; i < n; i++) {
     View tmpView = v.getView(i);
     if (contains(logicalView, tmpView)) {
       tmpView.setParent(logicalView);
     } else {
       recursiveReparent(tmpView, logicalView);
     }
   }
 }
  /**
   * GlyphGutter copy pasted bolerplate method. It invokes {@link #paintView} that contains actual
   * business logic.
   */
  @Override
  public void paintComponent(Graphics g) {
    super.paintComponent(g);

    Rectangle clip = g.getClipBounds();

    JTextComponent component = editorUI.getComponent();
    if (component == null) return;

    BaseTextUI textUI = (BaseTextUI) component.getUI();
    View rootView = Utilities.getDocumentView(component);
    if (rootView == null) return;

    g.setColor(backgroundColor());
    g.fillRect(clip.x, clip.y, clip.width, clip.height);

    AbstractDocument doc = (AbstractDocument) component.getDocument();
    doc.readLock();
    try {
      foldHierarchy.lock();
      try {
        int startPos = textUI.getPosFromY(clip.y);
        int startViewIndex = rootView.getViewIndex(startPos, Position.Bias.Forward);
        int rootViewCount = rootView.getViewCount();

        if (startViewIndex >= 0 && startViewIndex < rootViewCount) {
          int clipEndY = clip.y + clip.height;
          for (int i = startViewIndex; i < rootViewCount; i++) {
            View view = rootView.getView(i);
            Rectangle rec = component.modelToView(view.getStartOffset());
            if (rec == null) {
              break;
            }
            int y = rec.y;
            paintView(view, g, y);
            if (y >= clipEndY) {
              break;
            }
          }
        }

      } finally {
        foldHierarchy.unlock();
      }
    } catch (BadLocationException ble) {
      Mercurial.LOG.log(Level.WARNING, null, ble);
    } finally {
      doc.readUnlock();
    }
  }
    /**
     * 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;
    }
Exemple #7
0
 /** Returns the baseline for single line text components, like <code>JTextField</code>. */
 private static int getSingleLineTextBaseline(JTextComponent textComponent, int h) {
   View rootView = textComponent.getUI().getRootView(textComponent);
   if (rootView.getViewCount() > 0) {
     Insets insets = textComponent.getInsets();
     int height = h - insets.top - insets.bottom;
     int y = insets.top;
     View fieldView = rootView.getView(0);
     int vspan = (int) fieldView.getPreferredSpan(View.Y_AXIS);
     if (height != vspan) {
       int slop = height - vspan;
       y += slop / 2;
     }
     FontMetrics fm = textComponent.getFontMetrics(textComponent.getFont());
     y += fm.getAscent();
     return y;
   }
   return -1;
 }