blob: d57bef99f57613d02f8280aa21b9df90901e6025 [file] [log] [blame]
/*
* Copyright 1999-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* $Id$ */
package org.apache.fop.layoutmgr.list;
import org.apache.fop.fo.flow.ListBlock;
import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
import org.apache.fop.layoutmgr.LayoutManager;
import org.apache.fop.layoutmgr.LeafPosition;
import org.apache.fop.layoutmgr.BreakPoss;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.PositionIterator;
import org.apache.fop.layoutmgr.Position;
import org.apache.fop.layoutmgr.NonLeafPosition;
import org.apache.fop.layoutmgr.TraitSetter;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.traits.MinOptMax;
import org.apache.fop.traits.SpaceVal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* LayoutManager for a list-block FO.
* A list block contains list items which are stacked within
* the list block area..
*/
public class ListBlockLayoutManager extends BlockStackingLayoutManager {
private ListBlock fobj;
private Block curBlockArea;
private List bodyBreaks = new ArrayList();
//TODO space-before|after: handle space-resolution rules
private MinOptMax spaceBefore;
private MinOptMax spaceAfter;
private static class StackingIter extends PositionIterator {
StackingIter(Iterator parentIter) {
super(parentIter);
}
protected LayoutManager getLM(Object nextObj) {
return ((Position) nextObj).getLM();
}
protected Position getPos(Object nextObj) {
return ((Position) nextObj);
}
}
/*
private class SectionPosition extends LeafPosition {
protected List list;
protected SectionPosition(LayoutManager lm, int pos, List l) {
super(lm, pos);
list = l;
}
}*/
/**
* Create a new list block layout manager.
* @param node list-block to create the layout manager for
*/
public ListBlockLayoutManager(ListBlock node) {
super(node);
fobj = node;
}
/** @see org.apache.fop.layoutmgr.AbstractLayoutManager#initProperties() */
protected void initProperties() {
super.initProperties();
spaceBefore = new SpaceVal(fobj.getCommonMarginBlock().spaceBefore).getSpace();
spaceAfter = new SpaceVal(fobj.getCommonMarginBlock().spaceAfter).getSpace();
}
private int getIPIndents() {
int iIndents = 0;
iIndents += fobj.getCommonMarginBlock().startIndent.getValue();
iIndents += fobj.getCommonMarginBlock().endIndent.getValue();
return iIndents;
}
/**
* Get the next break possibility.
* The break possibility depends on the height of the header and footer
* and possible breaks inside the table body.
*
* @param context the layout context for finding breaks
* @return the next break possibility
*/
public BreakPoss getNextBreakPoss(LayoutContext context) {
// currently active LM
LayoutManager curLM;
referenceIPD = context.getRefIPD();
MinOptMax stackSize = new MinOptMax();
//Add spacing
if (spaceAfter != null) {
stackSize.add(spaceAfter);
}
if (spaceBefore != null) {
stackSize.add(spaceBefore);
}
BreakPoss lastPos = null;
while ((curLM = (LayoutManager)getChildLM()) != null) {
// Make break positions
// Set up a LayoutContext
//int ipd = context.getRefIPD();
BreakPoss bp;
LayoutContext childLC = new LayoutContext(0);
childLC.setStackLimit(
MinOptMax.subtract(context.getStackLimit(),
stackSize));
childLC.setRefIPD(referenceIPD);
boolean over = false;
while (!curLM.isFinished()) {
if ((bp = curLM.getNextBreakPoss(childLC)) != null) {
if (stackSize.opt + bp.getStackingSize().opt > context.getStackLimit().max) {
// reset to last break
if (lastPos != null) {
LayoutManager lm = lastPos.getLayoutManager();
lm.resetPosition(lastPos.getPosition());
if (lm != curLM) {
curLM.resetPosition(null);
}
} else {
curLM.resetPosition(null);
}
over = true;
break;
}
stackSize.add(bp.getStackingSize());
lastPos = bp;
bodyBreaks.add(bp);
if (bp.nextBreakOverflows()) {
over = true;
break;
}
childLC.setStackLimit(MinOptMax.subtract(
context.getStackLimit(), stackSize));
}
}
BreakPoss breakPoss = new BreakPoss(
new LeafPosition(this, bodyBreaks.size() - 1));
if (over) {
breakPoss.setFlag(BreakPoss.NEXT_OVERFLOWS, true);
}
breakPoss.setStackingSize(stackSize);
return breakPoss;
}
setFinished(true);
return null;
}
public LinkedList getChangedKnuthElements(List oldList, int alignment) {
//log.debug("LBLM.getChangedKnuthElements>");
return super.getChangedKnuthElements(oldList, alignment);
}
/**
* The table area is a reference area that contains areas for
* columns, bodies, rows and the contents are in cells.
*
* @param parentIter the position iterator
* @param layoutContext the layout context for adding areas
*/
public void addAreas(PositionIterator parentIter,
LayoutContext layoutContext) {
getParentArea(null);
// if adjusted space before
double adjust = layoutContext.getSpaceAdjust();
addBlockSpacing(adjust, spaceBefore);
spaceBefore = null;
getPSLM().addIDToPage(fobj.getId());
// the list block contains areas stacked from each list item
LayoutManager childLM = null;
LayoutContext lc = new LayoutContext(0);
LayoutManager firstLM = null;
LayoutManager lastLM = null;
// "unwrap" the NonLeafPositions stored in parentIter
// and put them in a new list;
LinkedList positionList = new LinkedList();
Position pos;
while (parentIter.hasNext()) {
pos = (Position)parentIter.next();
if (pos instanceof NonLeafPosition
&& ((NonLeafPosition) pos).getPosition().getLM() != this) {
// pos was created by a child of this ListBlockLM
positionList.add(((NonLeafPosition) pos).getPosition());
lastLM = ((NonLeafPosition) pos).getPosition().getLM();
if (firstLM == null) {
firstLM = lastLM;
}
}
}
StackingIter childPosIter = new StackingIter(positionList.listIterator());
while ((childLM = childPosIter.getNextChildLM()) != null) {
// Add the block areas to Area
lc.setFlags(LayoutContext.FIRST_AREA, childLM == firstLM);
lc.setFlags(LayoutContext.LAST_AREA, childLM == lastLM);
lc.setStackLimit(layoutContext.getStackLimit());
childLM.addAreas(childPosIter, lc);
}
flush();
// if adjusted space after
addBlockSpacing(adjust, spaceAfter);
bodyBreaks.clear();
curBlockArea = null;
}
/**
* Return an Area which can contain the passed childArea. The childArea
* may not yet have any content, but it has essential traits set.
* In general, if the LayoutManager already has an Area it simply returns
* it. Otherwise, it makes a new Area of the appropriate class.
* It gets a parent area for its area by calling its parent LM.
* Finally, based on the dimensions of the parent area, it initializes
* its own area. This includes setting the content IPD and the maximum
* BPD.
*
* @param childArea the child area
* @return the parent area of the child
*/
public Area getParentArea(Area childArea) {
if (curBlockArea == null) {
curBlockArea = new Block();
// Set up dimensions
// Must get dimensions from parent area
/*Area parentArea =*/ parentLM.getParentArea(curBlockArea);
// set traits
TraitSetter.addBorders(curBlockArea, fobj.getCommonBorderPaddingBackground());
TraitSetter.addBackground(curBlockArea, fobj.getCommonBorderPaddingBackground());
TraitSetter.addMargins(curBlockArea,
fobj.getCommonBorderPaddingBackground(),
fobj.getCommonMarginBlock());
TraitSetter.addBreaks(curBlockArea,
fobj.getBreakBefore(), fobj.getBreakAfter());
int contentIPD = referenceIPD - getIPIndents();
curBlockArea.setIPD(contentIPD);
setCurrentArea(curBlockArea);
}
return curBlockArea;
}
/**
* Add the child area to this layout manager.
*
* @param childArea the child area to add
*/
public void addChildArea(Area childArea) {
if (curBlockArea != null) {
curBlockArea.addBlock((Block) childArea);
}
}
/**
* Reset the position of this layout manager.
*
* @param resetPos the position to reset to
*/
public void resetPosition(Position resetPos) {
if (resetPos == null) {
bodyBreaks.clear();
reset(null);
} else {
//TODO Something to put here?
}
}
}