0N/A/*
2362N/A * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/Apackage javax.swing.text;
0N/A
0N/Aimport java.awt.*;
0N/Aimport java.util.Vector;
0N/Aimport javax.swing.event.*;
0N/Aimport javax.swing.SizeRequirements;
0N/A
0N/A/**
0N/A * A View that tries to flow it's children into some
0N/A * partially constrained space. This can be used to
0N/A * build things like paragraphs, pages, etc. The
0N/A * flow is made up of the following pieces of functionality.
0N/A * <ul>
0N/A * <li>A logical set of child views, which as used as a
0N/A * layout pool from which a physical view is formed.
0N/A * <li>A strategy for translating the logical view to
0N/A * a physical (flowed) view.
0N/A * <li>Constraints for the strategy to work against.
0N/A * <li>A physical structure, that represents the flow.
0N/A * The children of this view are where the pieces of
0N/A * of the logical views are placed to create the flow.
0N/A * </ul>
0N/A *
0N/A * @author Timothy Prinzing
0N/A * @see View
0N/A * @since 1.3
0N/A */
0N/Apublic abstract class FlowView extends BoxView {
0N/A
0N/A /**
0N/A * Constructs a FlowView for the given element.
0N/A *
0N/A * @param elem the element that this view is responsible for
0N/A * @param axis may be either View.X_AXIS or View.Y_AXIS
0N/A */
0N/A public FlowView(Element elem, int axis) {
0N/A super(elem, axis);
0N/A layoutSpan = Integer.MAX_VALUE;
0N/A strategy = new FlowStrategy();
0N/A }
0N/A
0N/A /**
0N/A * Fetches the axis along which views should be
0N/A * flowed. By default, this will be the axis
0N/A * orthogonal to the axis along which the flow
0N/A * rows are tiled (the axis of the default flow
0N/A * rows themselves). This is typically used
0N/A * by the <code>FlowStrategy</code>.
0N/A */
0N/A public int getFlowAxis() {
0N/A if (getAxis() == Y_AXIS) {
0N/A return X_AXIS;
0N/A }
0N/A return Y_AXIS;
0N/A }
0N/A
0N/A /**
0N/A * Fetch the constraining span to flow against for
0N/A * the given child index. This is called by the
0N/A * FlowStrategy while it is updating the flow.
0N/A * A flow can be shaped by providing different values
0N/A * for the row constraints. By default, the entire
0N/A * span inside of the insets along the flow axis
0N/A * is returned.
0N/A *
0N/A * @param index the index of the row being updated.
0N/A * This should be a value >= 0 and < getViewCount().
0N/A * @see #getFlowStart
0N/A */
0N/A public int getFlowSpan(int index) {
0N/A return layoutSpan;
0N/A }
0N/A
0N/A /**
0N/A * Fetch the location along the flow axis that the
0N/A * flow span will start at. This is called by the
0N/A * FlowStrategy while it is updating the flow.
0N/A * A flow can be shaped by providing different values
0N/A * for the row constraints.
0N/A
0N/A * @param index the index of the row being updated.
0N/A * This should be a value >= 0 and < getViewCount().
0N/A * @see #getFlowSpan
0N/A */
0N/A public int getFlowStart(int index) {
0N/A return 0;
0N/A }
0N/A
0N/A /**
0N/A * Create a View that should be used to hold a
0N/A * a rows worth of children in a flow. This is
0N/A * called by the FlowStrategy when new children
0N/A * are added or removed (i.e. rows are added or
0N/A * removed) in the process of updating the flow.
0N/A */
0N/A protected abstract View createRow();
0N/A
0N/A // ---- BoxView methods -------------------------------------
0N/A
0N/A /**
0N/A * Loads all of the children to initialize the view.
0N/A * This is called by the <code>setParent</code> method.
0N/A * This is reimplemented to not load any children directly
0N/A * (as they are created in the process of formatting).
0N/A * If the layoutPool variable is null, an instance of
0N/A * LogicalView is created to represent the logical view
0N/A * that is used in the process of formatting.
0N/A *
0N/A * @param f the view factory
0N/A */
0N/A protected void loadChildren(ViewFactory f) {
0N/A if (layoutPool == null) {
0N/A layoutPool = new LogicalView(getElement());
0N/A }
0N/A layoutPool.setParent(this);
0N/A
0N/A // This synthetic insertUpdate call gives the strategy a chance
0N/A // to initialize.
0N/A strategy.insertUpdate(this, null, null);
0N/A }
0N/A
0N/A /**
0N/A * Fetches the child view index representing the given position in
0N/A * the model.
0N/A *
0N/A * @param pos the position >= 0
0N/A * @return index of the view representing the given position, or
0N/A * -1 if no view represents that position
0N/A */
0N/A protected int getViewIndexAtPosition(int pos) {
0N/A if (pos >= getStartOffset() && (pos < getEndOffset())) {
0N/A for (int counter = 0; counter < getViewCount(); counter++) {
0N/A View v = getView(counter);
0N/A if(pos >= v.getStartOffset() &&
0N/A pos < v.getEndOffset()) {
0N/A return counter;
0N/A }
0N/A }
0N/A }
0N/A return -1;
0N/A }
0N/A
0N/A /**
0N/A * Lays out the children. If the span along the flow
0N/A * axis has changed, layout is marked as invalid which
0N/A * which will cause the superclass behavior to recalculate
0N/A * the layout along the box axis. The FlowStrategy.layout
0N/A * method will be called to rebuild the flow rows as
0N/A * appropriate. If the height of this view changes
0N/A * (determined by the perferred size along the box axis),
0N/A * a preferenceChanged is called. Following all of that,
0N/A * the normal box layout of the superclass is performed.
0N/A *
0N/A * @param width the width to lay out against >= 0. This is
0N/A * the width inside of the inset area.
0N/A * @param height the height to lay out against >= 0 This
0N/A * is the height inside of the inset area.
0N/A */
0N/A protected void layout(int width, int height) {
0N/A final int faxis = getFlowAxis();
0N/A int newSpan;
0N/A if (faxis == X_AXIS) {
611N/A newSpan = width;
0N/A } else {
611N/A newSpan = height;
0N/A }
0N/A if (layoutSpan != newSpan) {
0N/A layoutChanged(faxis);
0N/A layoutChanged(getAxis());
0N/A layoutSpan = newSpan;
0N/A }
0N/A
0N/A // repair the flow if necessary
0N/A if (! isLayoutValid(faxis)) {
0N/A final int heightAxis = getAxis();
611N/A int oldFlowHeight = (heightAxis == X_AXIS)? getWidth() : getHeight();
0N/A strategy.layout(this);
0N/A int newFlowHeight = (int) getPreferredSpan(heightAxis);
0N/A if (oldFlowHeight != newFlowHeight) {
0N/A View p = getParent();
0N/A if (p != null) {
0N/A p.preferenceChanged(this, (heightAxis == X_AXIS), (heightAxis == Y_AXIS));
0N/A }
0N/A
0N/A // PENDING(shannonh)
0N/A // Temporary fix for 4250847
0N/A // Can be removed when TraversalContext is added
0N/A Component host = getContainer();
0N/A if (host != null) {
0N/A //nb idk 12/12/2001 host should not be equal to null. We need to add assertion here
0N/A host.repaint();
0N/A }
0N/A }
0N/A }
0N/A
0N/A super.layout(width, height);
0N/A }
0N/A
0N/A /**
0N/A * Calculate equirements along the minor axis. This
0N/A * is implemented to forward the request to the logical
0N/A * view by calling getMinimumSpan, getPreferredSpan, and
0N/A * getMaximumSpan on it.
0N/A */
0N/A protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
0N/A if (r == null) {
0N/A r = new SizeRequirements();
0N/A }
0N/A float pref = layoutPool.getPreferredSpan(axis);
0N/A float min = layoutPool.getMinimumSpan(axis);
0N/A // Don't include insets, Box.getXXXSpan will include them.
0N/A r.minimum = (int)min;
0N/A r.preferred = Math.max(r.minimum, (int) pref);
0N/A r.maximum = Integer.MAX_VALUE;
0N/A r.alignment = 0.5f;
0N/A return r;
0N/A }
0N/A
0N/A // ---- View methods ----------------------------------------------------
0N/A
0N/A /**
0N/A * Gives notification that something was inserted into the document
0N/A * in a location that this view is responsible for.
0N/A *
0N/A * @param changes the change information from the associated document
0N/A * @param a the current allocation of the view
0N/A * @param f the factory to use to rebuild if the view has children
0N/A * @see View#insertUpdate
0N/A */
0N/A public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
0N/A layoutPool.insertUpdate(changes, a, f);
0N/A strategy.insertUpdate(this, changes, getInsideAllocation(a));
0N/A }
0N/A
0N/A /**
0N/A * Gives notification that something was removed from the document
0N/A * in a location that this view is responsible for.
0N/A *
0N/A * @param changes the change information from the associated document
0N/A * @param a the current allocation of the view
0N/A * @param f the factory to use to rebuild if the view has children
0N/A * @see View#removeUpdate
0N/A */
0N/A public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
0N/A layoutPool.removeUpdate(changes, a, f);
0N/A strategy.removeUpdate(this, changes, getInsideAllocation(a));
0N/A }
0N/A
0N/A /**
0N/A * Gives notification from the document that attributes were changed
0N/A * in a location that this view is responsible for.
0N/A *
0N/A * @param changes the change information from the associated document
0N/A * @param a the current allocation of the view
0N/A * @param f the factory to use to rebuild if the view has children
0N/A * @see View#changedUpdate
0N/A */
0N/A public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
0N/A layoutPool.changedUpdate(changes, a, f);
0N/A strategy.changedUpdate(this, changes, getInsideAllocation(a));
0N/A }
0N/A
0N/A /** {@inheritDoc} */
0N/A public void setParent(View parent) {
0N/A super.setParent(parent);
0N/A if (parent == null
0N/A && layoutPool != null ) {
0N/A layoutPool.setParent(null);
0N/A }
0N/A }
0N/A
0N/A // --- variables -----------------------------------------------
0N/A
0N/A /**
0N/A * Default constraint against which the flow is
0N/A * created against.
0N/A */
0N/A protected int layoutSpan;
0N/A
0N/A /**
0N/A * These are the views that represent the child elements
0N/A * of the element this view represents (The logical view
0N/A * to translate to a physical view). These are not
0N/A * directly children of this view. These are either
0N/A * placed into the rows directly or used for the purpose
0N/A * of breaking into smaller chunks, to form the physical
0N/A * view.
0N/A */
0N/A protected View layoutPool;
0N/A
0N/A /**
0N/A * The behavior for keeping the flow updated. By
0N/A * default this is a singleton shared by all instances
0N/A * of FlowView (FlowStrategy is stateless). Subclasses
0N/A * can create an alternative strategy, which might keep
0N/A * state.
0N/A */
0N/A protected FlowStrategy strategy;
0N/A
0N/A /**
0N/A * Strategy for maintaining the physical form
0N/A * of the flow. The default implementation is
0N/A * completely stateless, and recalculates the
0N/A * entire flow if the layout is invalid on the
0N/A * given FlowView. Alternative strategies can
0N/A * be implemented by subclassing, and might
0N/A * perform incrementatal repair to the layout
0N/A * or alternative breaking behavior.
0N/A * @since 1.3
0N/A */
0N/A public static class FlowStrategy {
339N/A Position damageStart = null;
0N/A Vector<View> viewBuffer;
0N/A
0N/A void addDamage(FlowView fv, int offset) {
0N/A if (offset >= fv.getStartOffset() && offset < fv.getEndOffset()) {
339N/A if (damageStart == null || offset < damageStart.getOffset()) {
339N/A try {
339N/A damageStart = fv.getDocument().createPosition(offset);
339N/A } catch (BadLocationException e) {
339N/A // shouldn't happen since offset is inside view bounds
339N/A assert(false);
339N/A }
339N/A }
0N/A }
0N/A }
0N/A
0N/A void unsetDamage() {
339N/A damageStart = null;
0N/A }
0N/A
0N/A /**
0N/A * Gives notification that something was inserted into the document
0N/A * in a location that the given flow view is responsible for. The
0N/A * strategy should update the appropriate changed region (which
0N/A * depends upon the strategy used for repair).
0N/A *
0N/A * @param e the change information from the associated document
0N/A * @param alloc the current allocation of the view inside of the insets.
0N/A * This value will be null if the view has not yet been displayed.
0N/A * @see View#insertUpdate
0N/A */
0N/A public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
0N/A // FlowView.loadChildren() makes a synthetic call into this,
0N/A // passing null as e
0N/A if (e != null) {
0N/A addDamage(fv, e.getOffset());
0N/A }
0N/A
0N/A if (alloc != null) {
0N/A Component host = fv.getContainer();
0N/A if (host != null) {
0N/A host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
0N/A }
0N/A } else {
0N/A fv.preferenceChanged(null, true, true);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Gives notification that something was removed from the document
0N/A * in a location that the given flow view is responsible for.
0N/A *
0N/A * @param e the change information from the associated document
0N/A * @param alloc the current allocation of the view inside of the insets.
0N/A * @see View#removeUpdate
0N/A */
0N/A public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
0N/A addDamage(fv, e.getOffset());
0N/A if (alloc != null) {
0N/A Component host = fv.getContainer();
0N/A if (host != null) {
0N/A host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
0N/A }
0N/A } else {
0N/A fv.preferenceChanged(null, true, true);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Gives notification from the document that attributes were changed
0N/A * in a location that this view is responsible for.
0N/A *
0N/A * @param fv the <code>FlowView</code> containing the changes
0N/A * @param e the <code>DocumentEvent</code> describing the changes
0N/A * done to the Document
0N/A * @param alloc Bounds of the View
0N/A * @see View#changedUpdate
0N/A */
0N/A public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
0N/A addDamage(fv, e.getOffset());
0N/A if (alloc != null) {
0N/A Component host = fv.getContainer();
0N/A if (host != null) {
0N/A host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
0N/A }
0N/A } else {
0N/A fv.preferenceChanged(null, true, true);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * This method gives flow strategies access to the logical
0N/A * view of the FlowView.
0N/A */
0N/A protected View getLogicalView(FlowView fv) {
0N/A return fv.layoutPool;
0N/A }
0N/A
0N/A /**
0N/A * Update the flow on the given FlowView. By default, this causes
0N/A * all of the rows (child views) to be rebuilt to match the given
0N/A * constraints for each row. This is called by a FlowView.layout
0N/A * to update the child views in the flow.
0N/A *
0N/A * @param fv the view to reflow
0N/A */
0N/A public void layout(FlowView fv) {
0N/A View pool = getLogicalView(fv);
0N/A int rowIndex, p0;
0N/A int p1 = fv.getEndOffset();
0N/A
0N/A if (fv.majorAllocValid) {
339N/A if (damageStart == null) {
0N/A return;
0N/A }
0N/A // In some cases there's no view at position damageStart, so
0N/A // step back and search again. See 6452106 for details.
339N/A int offset = damageStart.getOffset();
339N/A while ((rowIndex = fv.getViewIndexAtPosition(offset)) < 0) {
339N/A offset--;
0N/A }
0N/A if (rowIndex > 0) {
0N/A rowIndex--;
0N/A }
0N/A p0 = fv.getView(rowIndex).getStartOffset();
0N/A } else {
0N/A rowIndex = 0;
0N/A p0 = fv.getStartOffset();
0N/A }
0N/A reparentViews(pool, p0);
0N/A
0N/A viewBuffer = new Vector<View>(10, 10);
0N/A int rowCount = fv.getViewCount();
0N/A while (p0 < p1) {
0N/A View row;
0N/A if (rowIndex >= rowCount) {
0N/A row = fv.createRow();
0N/A fv.append(row);
0N/A } else {
0N/A row = fv.getView(rowIndex);
0N/A }
0N/A p0 = layoutRow(fv, rowIndex, p0);
0N/A rowIndex++;
0N/A }
0N/A viewBuffer = null;
0N/A
0N/A if (rowIndex < rowCount) {
0N/A fv.replace(rowIndex, rowCount - rowIndex, null);
0N/A }
0N/A unsetDamage();
0N/A }
0N/A
0N/A /**
0N/A * Creates a row of views that will fit within the
0N/A * layout span of the row. This is called by the layout method.
0N/A * This is implemented to fill the row by repeatedly calling
0N/A * the createView method until the available span has been
0N/A * exhausted, a forced break was encountered, or the createView
0N/A * method returned null. If the remaining span was exhaused,
0N/A * the adjustRow method will be called to perform adjustments
0N/A * to the row to try and make it fit into the given span.
0N/A *
0N/A * @param rowIndex the index of the row to fill in with views. The
0N/A * row is assumed to be empty on entry.
0N/A * @param pos The current position in the children of
0N/A * this views element from which to start.
0N/A * @return the position to start the next row
0N/A */
0N/A protected int layoutRow(FlowView fv, int rowIndex, int pos) {
0N/A View row = fv.getView(rowIndex);
0N/A float x = fv.getFlowStart(rowIndex);
0N/A float spanLeft = fv.getFlowSpan(rowIndex);
0N/A int end = fv.getEndOffset();
0N/A TabExpander te = (fv instanceof TabExpander) ? (TabExpander)fv : null;
0N/A final int flowAxis = fv.getFlowAxis();
0N/A
0N/A int breakWeight = BadBreakWeight;
0N/A float breakX = 0f;
0N/A float breakSpan = 0f;
0N/A int breakIndex = -1;
0N/A int n = 0;
0N/A
0N/A viewBuffer.clear();
0N/A while (pos < end && spanLeft >= 0) {
0N/A View v = createView(fv, pos, (int)spanLeft, rowIndex);
0N/A if (v == null) {
0N/A break;
0N/A }
0N/A
0N/A int bw = v.getBreakWeight(flowAxis, x, spanLeft);
0N/A if (bw >= ForcedBreakWeight) {
0N/A View w = v.breakView(flowAxis, pos, x, spanLeft);
0N/A if (w != null) {
0N/A viewBuffer.add(w);
0N/A } else if (n == 0) {
0N/A // if the view does not break, and it is the only view
0N/A // in a row, use the whole view
0N/A viewBuffer.add(v);
0N/A }
0N/A break;
0N/A } else if (bw >= breakWeight && bw > BadBreakWeight) {
0N/A breakWeight = bw;
0N/A breakX = x;
0N/A breakSpan = spanLeft;
0N/A breakIndex = n;
0N/A }
0N/A
0N/A float chunkSpan;
0N/A if (flowAxis == X_AXIS && v instanceof TabableView) {
0N/A chunkSpan = ((TabableView)v).getTabbedSpan(x, te);
0N/A } else {
0N/A chunkSpan = v.getPreferredSpan(flowAxis);
0N/A }
0N/A
0N/A if (chunkSpan > spanLeft && breakIndex >= 0) {
0N/A // row is too long, and we may break
0N/A if (breakIndex < n) {
0N/A v = viewBuffer.get(breakIndex);
0N/A }
0N/A for (int i = n - 1; i >= breakIndex; i--) {
0N/A viewBuffer.remove(i);
0N/A }
0N/A v = v.breakView(flowAxis, v.getStartOffset(), breakX, breakSpan);
0N/A }
0N/A
0N/A spanLeft -= chunkSpan;
0N/A x += chunkSpan;
0N/A viewBuffer.add(v);
0N/A pos = v.getEndOffset();
0N/A n++;
0N/A }
0N/A
0N/A View[] views = new View[viewBuffer.size()];
0N/A viewBuffer.toArray(views);
0N/A row.replace(0, row.getViewCount(), views);
0N/A return (views.length > 0 ? row.getEndOffset() : pos);
0N/A }
0N/A
0N/A /**
0N/A * Adjusts the given row if possible to fit within the
0N/A * layout span. By default this will try to find the
0N/A * highest break weight possible nearest the end of
0N/A * the row. If a forced break is encountered, the
0N/A * break will be positioned there.
0N/A *
0N/A * @param rowIndex the row to adjust to the current layout
0N/A * span.
0N/A * @param desiredSpan the current layout span >= 0
0N/A * @param x the location r starts at.
0N/A */
0N/A protected void adjustRow(FlowView fv, int rowIndex, int desiredSpan, int x) {
0N/A final int flowAxis = fv.getFlowAxis();
0N/A View r = fv.getView(rowIndex);
0N/A int n = r.getViewCount();
0N/A int span = 0;
0N/A int bestWeight = BadBreakWeight;
0N/A int bestSpan = 0;
0N/A int bestIndex = -1;
0N/A View v;
0N/A for (int i = 0; i < n; i++) {
0N/A v = r.getView(i);
0N/A int spanLeft = desiredSpan - span;
0N/A
0N/A int w = v.getBreakWeight(flowAxis, x + span, spanLeft);
0N/A if ((w >= bestWeight) && (w > BadBreakWeight)) {
0N/A bestWeight = w;
0N/A bestIndex = i;
0N/A bestSpan = span;
0N/A if (w >= ForcedBreakWeight) {
0N/A // it's a forced break, so there is
0N/A // no point in searching further.
0N/A break;
0N/A }
0N/A }
0N/A span += v.getPreferredSpan(flowAxis);
0N/A }
0N/A if (bestIndex < 0) {
0N/A // there is nothing that can be broken, leave
0N/A // it in it's current state.
0N/A return;
0N/A }
0N/A
0N/A // Break the best candidate view, and patch up the row.
0N/A int spanLeft = desiredSpan - bestSpan;
0N/A v = r.getView(bestIndex);
0N/A v = v.breakView(flowAxis, v.getStartOffset(), x + bestSpan, spanLeft);
0N/A View[] va = new View[1];
0N/A va[0] = v;
0N/A View lv = getLogicalView(fv);
0N/A int p0 = r.getView(bestIndex).getStartOffset();
0N/A int p1 = r.getEndOffset();
0N/A for (int i = 0; i < lv.getViewCount(); i++) {
0N/A View tmpView = lv.getView(i);
0N/A if (tmpView.getEndOffset() > p1) {
0N/A break;
0N/A }
0N/A if (tmpView.getStartOffset() >= p0) {
0N/A tmpView.setParent(lv);
0N/A }
0N/A }
0N/A r.replace(bestIndex, n - bestIndex, va);
0N/A }
0N/A
0N/A void reparentViews(View pool, int startPos) {
0N/A int n = pool.getViewIndex(startPos, Position.Bias.Forward);
0N/A if (n >= 0) {
0N/A for (int i = n; i < pool.getViewCount(); i++) {
0N/A pool.getView(i).setParent(pool);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Creates a view that can be used to represent the current piece
0N/A * of the flow. This can be either an entire view from the
0N/A * logical view, or a fragment of the logical view.
0N/A *
0N/A * @param fv the view holding the flow
0N/A * @param startOffset the start location for the view being created
0N/A * @param spanLeft the about of span left to fill in the row
0N/A * @param rowIndex the row the view will be placed into
0N/A */
0N/A protected View createView(FlowView fv, int startOffset, int spanLeft, int rowIndex) {
0N/A // Get the child view that contains the given starting position
0N/A View lv = getLogicalView(fv);
0N/A int childIndex = lv.getViewIndex(startOffset, Position.Bias.Forward);
0N/A View v = lv.getView(childIndex);
0N/A if (startOffset==v.getStartOffset()) {
0N/A // return the entire view
0N/A return v;
0N/A }
0N/A
0N/A // return a fragment.
0N/A v = v.createFragment(startOffset, v.getEndOffset());
0N/A return v;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * This class can be used to represent a logical view for
0N/A * a flow. It keeps the children updated to reflect the state
0N/A * of the model, gives the logical child views access to the
0N/A * view hierarchy, and calculates a preferred span. It doesn't
0N/A * do any rendering, layout, or model/view translation.
0N/A */
0N/A static class LogicalView extends CompositeView {
0N/A
0N/A LogicalView(Element elem) {
0N/A super(elem);
0N/A }
0N/A
0N/A protected int getViewIndexAtPosition(int pos) {
0N/A Element elem = getElement();
0N/A if (elem.isLeaf()) {
0N/A return 0;
0N/A }
0N/A return super.getViewIndexAtPosition(pos);
0N/A }
0N/A
0N/A protected void loadChildren(ViewFactory f) {
0N/A Element elem = getElement();
0N/A if (elem.isLeaf()) {
0N/A View v = new LabelView(elem);
0N/A append(v);
0N/A } else {
0N/A super.loadChildren(f);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Fetches the attributes to use when rendering. This view
0N/A * isn't directly responsible for an element so it returns
0N/A * the outer classes attributes.
0N/A */
0N/A public AttributeSet getAttributes() {
0N/A View p = getParent();
0N/A return (p != null) ? p.getAttributes() : null;
0N/A }
0N/A
0N/A /**
0N/A * Determines the preferred span for this view along an
0N/A * axis.
0N/A *
0N/A * @param axis may be either View.X_AXIS or View.Y_AXIS
0N/A * @return the span the view would like to be rendered into.
0N/A * Typically the view is told to render into the span
0N/A * that is returned, although there is no guarantee.
0N/A * The parent may choose to resize or break the view.
0N/A * @see View#getPreferredSpan
0N/A */
0N/A public float getPreferredSpan(int axis) {
0N/A float maxpref = 0;
0N/A float pref = 0;
0N/A int n = getViewCount();
0N/A for (int i = 0; i < n; i++) {
0N/A View v = getView(i);
0N/A pref += v.getPreferredSpan(axis);
0N/A if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE) >= ForcedBreakWeight) {
0N/A maxpref = Math.max(maxpref, pref);
0N/A pref = 0;
0N/A }
0N/A }
0N/A maxpref = Math.max(maxpref, pref);
0N/A return maxpref;
0N/A }
0N/A
0N/A /**
0N/A * Determines the minimum span for this view along an
0N/A * axis. The is implemented to find the minimum unbreakable
0N/A * span.
0N/A *
0N/A * @param axis may be either View.X_AXIS or View.Y_AXIS
0N/A * @return the span the view would like to be rendered into.
0N/A * Typically the view is told to render into the span
0N/A * that is returned, although there is no guarantee.
0N/A * The parent may choose to resize or break the view.
0N/A * @see View#getPreferredSpan
0N/A */
0N/A public float getMinimumSpan(int axis) {
0N/A float maxmin = 0;
0N/A float min = 0;
0N/A boolean nowrap = false;
0N/A int n = getViewCount();
0N/A for (int i = 0; i < n; i++) {
0N/A View v = getView(i);
0N/A if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE) == BadBreakWeight) {
0N/A min += v.getPreferredSpan(axis);
0N/A nowrap = true;
0N/A } else if (nowrap) {
0N/A maxmin = Math.max(min, maxmin);
0N/A nowrap = false;
0N/A min = 0;
0N/A }
0N/A if (v instanceof ComponentView) {
0N/A maxmin = Math.max(maxmin, v.getMinimumSpan(axis));
0N/A }
0N/A }
0N/A maxmin = Math.max(maxmin, min);
0N/A return maxmin;
0N/A }
0N/A
0N/A /**
0N/A * Forward the DocumentEvent to the given child view. This
0N/A * is implemented to reparent the child to the logical view
0N/A * (the children may have been parented by a row in the flow
0N/A * if they fit without breaking) and then execute the superclass
0N/A * behavior.
0N/A *
0N/A * @param v the child view to forward the event to.
0N/A * @param e the change information from the associated document
0N/A * @param a the current allocation of the view
0N/A * @param f the factory to use to rebuild if the view has children
0N/A * @see #forwardUpdate
0N/A * @since 1.3
0N/A */
0N/A protected void forwardUpdateToView(View v, DocumentEvent e,
0N/A Shape a, ViewFactory f) {
0N/A View parent = v.getParent();
0N/A v.setParent(this);
0N/A super.forwardUpdateToView(v, e, a, f);
0N/A v.setParent(parent);
0N/A }
0N/A
0N/A // The following methods don't do anything useful, they
0N/A // simply keep the class from being abstract.
0N/A
0N/A /**
0N/A * Renders using the given rendering surface and area on that
0N/A * surface. This is implemented to do nothing, the logical
0N/A * view is never visible.
0N/A *
0N/A * @param g the rendering surface to use
0N/A * @param allocation the allocated region to render into
0N/A * @see View#paint
0N/A */
0N/A public void paint(Graphics g, Shape allocation) {
0N/A }
0N/A
0N/A /**
0N/A * Tests whether a point lies before the rectangle range.
0N/A * Implemented to return false, as hit detection is not
0N/A * performed on the logical view.
0N/A *
0N/A * @param x the X coordinate >= 0
0N/A * @param y the Y coordinate >= 0
0N/A * @param alloc the rectangle
0N/A * @return true if the point is before the specified range
0N/A */
0N/A protected boolean isBefore(int x, int y, Rectangle alloc) {
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * Tests whether a point lies after the rectangle range.
0N/A * Implemented to return false, as hit detection is not
0N/A * performed on the logical view.
0N/A *
0N/A * @param x the X coordinate >= 0
0N/A * @param y the Y coordinate >= 0
0N/A * @param alloc the rectangle
0N/A * @return true if the point is after the specified range
0N/A */
0N/A protected boolean isAfter(int x, int y, Rectangle alloc) {
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * Fetches the child view at the given point.
0N/A * Implemented to return null, as hit detection is not
0N/A * performed on the logical view.
0N/A *
0N/A * @param x the X coordinate >= 0
0N/A * @param y the Y coordinate >= 0
0N/A * @param alloc the parent's allocation on entry, which should
0N/A * be changed to the child's allocation on exit
0N/A * @return the child view
0N/A */
0N/A protected View getViewAtPoint(int x, int y, Rectangle alloc) {
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Returns the allocation for a given child.
0N/A * Implemented to do nothing, as the logical view doesn't
0N/A * perform layout on the children.
0N/A *
0N/A * @param index the index of the child, >= 0 && < getViewCount()
0N/A * @param a the allocation to the interior of the box on entry,
0N/A * and the allocation of the child view at the index on exit.
0N/A */
0N/A protected void childAllocation(int index, Rectangle a) {
0N/A }
0N/A }
0N/A
0N/A
0N/A}