/* * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.swing.text; import java.awt.*; import java.util.Vector; import javax.swing.event.*; import javax.swing.SizeRequirements; /** * A View that tries to flow it's children into some * partially constrained space. This can be used to * build things like paragraphs, pages, etc. The * flow is made up of the following pieces of functionality. *
FlowStrategy
.
*/
public int getFlowAxis() {
if (getAxis() == Y_AXIS) {
return X_AXIS;
}
return Y_AXIS;
}
/**
* Fetch the constraining span to flow against for
* the given child index. This is called by the
* FlowStrategy while it is updating the flow.
* A flow can be shaped by providing different values
* for the row constraints. By default, the entire
* span inside of the insets along the flow axis
* is returned.
*
* @param index the index of the row being updated.
* This should be a value >= 0 and < getViewCount().
* @see #getFlowStart
*/
public int getFlowSpan(int index) {
return layoutSpan;
}
/**
* Fetch the location along the flow axis that the
* flow span will start at. This is called by the
* FlowStrategy while it is updating the flow.
* A flow can be shaped by providing different values
* for the row constraints.
* @param index the index of the row being updated.
* This should be a value >= 0 and < getViewCount().
* @see #getFlowSpan
*/
public int getFlowStart(int index) {
return 0;
}
/**
* Create a View that should be used to hold a
* a rows worth of children in a flow. This is
* called by the FlowStrategy when new children
* are added or removed (i.e. rows are added or
* removed) in the process of updating the flow.
*/
protected abstract View createRow();
// ---- BoxView methods -------------------------------------
/**
* Loads all of the children to initialize the view.
* This is called by the setParent
method.
* This is reimplemented to not load any children directly
* (as they are created in the process of formatting).
* If the layoutPool variable is null, an instance of
* LogicalView is created to represent the logical view
* that is used in the process of formatting.
*
* @param f the view factory
*/
protected void loadChildren(ViewFactory f) {
if (layoutPool == null) {
layoutPool = new LogicalView(getElement());
}
layoutPool.setParent(this);
// This synthetic insertUpdate call gives the strategy a chance
// to initialize.
strategy.insertUpdate(this, null, null);
}
/**
* 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 = 0; counter < getViewCount(); counter++) {
View v = getView(counter);
if(pos >= v.getStartOffset() &&
pos < v.getEndOffset()) {
return counter;
}
}
}
return -1;
}
/**
* Lays out the children. If the span along the flow
* axis has changed, layout is marked as invalid which
* which will cause the superclass behavior to recalculate
* the layout along the box axis. The FlowStrategy.layout
* method will be called to rebuild the flow rows as
* appropriate. If the height of this view changes
* (determined by the perferred size along the box axis),
* a preferenceChanged is called. Following all of that,
* the normal box layout of the superclass is performed.
*
* @param width the width to lay out against >= 0. This is
* the width inside of the inset area.
* @param height the height to lay out against >= 0 This
* is the height inside of the inset area.
*/
protected void layout(int width, int height) {
final int faxis = getFlowAxis();
int newSpan;
if (faxis == X_AXIS) {
newSpan = width;
} else {
newSpan = height;
}
if (layoutSpan != newSpan) {
layoutChanged(faxis);
layoutChanged(getAxis());
layoutSpan = newSpan;
}
// repair the flow if necessary
if (! isLayoutValid(faxis)) {
final int heightAxis = getAxis();
int oldFlowHeight = (heightAxis == X_AXIS)? getWidth() : getHeight();
strategy.layout(this);
int newFlowHeight = (int) getPreferredSpan(heightAxis);
if (oldFlowHeight != newFlowHeight) {
View p = getParent();
if (p != null) {
p.preferenceChanged(this, (heightAxis == X_AXIS), (heightAxis == Y_AXIS));
}
// PENDING(shannonh)
// Temporary fix for 4250847
// Can be removed when TraversalContext is added
Component host = getContainer();
if (host != null) {
//nb idk 12/12/2001 host should not be equal to null. We need to add assertion here
host.repaint();
}
}
}
super.layout(width, height);
}
/**
* Calculate equirements along the minor axis. This
* is implemented to forward the request to the logical
* view by calling getMinimumSpan, getPreferredSpan, and
* getMaximumSpan on it.
*/
protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
if (r == null) {
r = new SizeRequirements();
}
float pref = layoutPool.getPreferredSpan(axis);
float min = layoutPool.getMinimumSpan(axis);
// Don't include insets, Box.getXXXSpan will include them.
r.minimum = (int)min;
r.preferred = Math.max(r.minimum, (int) pref);
r.maximum = Integer.MAX_VALUE;
r.alignment = 0.5f;
return r;
}
// ---- View methods ----------------------------------------------------
/**
* Gives notification that something was inserted into the document
* in a location that this view is responsible for.
*
* @param changes 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 View#insertUpdate
*/
public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
layoutPool.insertUpdate(changes, a, f);
strategy.insertUpdate(this, changes, getInsideAllocation(a));
}
/**
* Gives notification that something was removed from the document
* in a location that this view is responsible for.
*
* @param changes 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 View#removeUpdate
*/
public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
layoutPool.removeUpdate(changes, a, f);
strategy.removeUpdate(this, changes, getInsideAllocation(a));
}
/**
* Gives notification from the document that attributes were changed
* in a location that this view is responsible for.
*
* @param changes 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 View#changedUpdate
*/
public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
layoutPool.changedUpdate(changes, a, f);
strategy.changedUpdate(this, changes, getInsideAllocation(a));
}
/** {@inheritDoc} */
public void setParent(View parent) {
super.setParent(parent);
if (parent == null
&& layoutPool != null ) {
layoutPool.setParent(null);
}
}
// --- variables -----------------------------------------------
/**
* Default constraint against which the flow is
* created against.
*/
protected int layoutSpan;
/**
* These are the views that represent the child elements
* of the element this view represents (The logical view
* to translate to a physical view). These are not
* directly children of this view. These are either
* placed into the rows directly or used for the purpose
* of breaking into smaller chunks, to form the physical
* view.
*/
protected View layoutPool;
/**
* The behavior for keeping the flow updated. By
* default this is a singleton shared by all instances
* of FlowView (FlowStrategy is stateless). Subclasses
* can create an alternative strategy, which might keep
* state.
*/
protected FlowStrategy strategy;
/**
* Strategy for maintaining the physical form
* of the flow. The default implementation is
* completely stateless, and recalculates the
* entire flow if the layout is invalid on the
* given FlowView. Alternative strategies can
* be implemented by subclassing, and might
* perform incrementatal repair to the layout
* or alternative breaking behavior.
* @since 1.3
*/
public static class FlowStrategy {
Position damageStart = null;
VectorFlowView
containing the changes
* @param e the DocumentEvent
describing the changes
* done to the Document
* @param alloc Bounds of the View
* @see View#changedUpdate
*/
public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
addDamage(fv, e.getOffset());
if (alloc != null) {
Component host = fv.getContainer();
if (host != null) {
host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
}
} else {
fv.preferenceChanged(null, true, true);
}
}
/**
* This method gives flow strategies access to the logical
* view of the FlowView.
*/
protected View getLogicalView(FlowView fv) {
return fv.layoutPool;
}
/**
* 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) {
View pool = getLogicalView(fv);
int rowIndex, p0;
int p1 = fv.getEndOffset();
if (fv.majorAllocValid) {
if (damageStart == null) {
return;
}
// In some cases there's no view at position damageStart, so
// step back and search again. See 6452106 for details.
int offset = damageStart.getOffset();
while ((rowIndex = fv.getViewIndexAtPosition(offset)) < 0) {
offset--;
}
if (rowIndex > 0) {
rowIndex--;
}
p0 = fv.getView(rowIndex).getStartOffset();
} else {
rowIndex = 0;
p0 = fv.getStartOffset();
}
reparentViews(pool, p0);
viewBuffer = new Vector