| /* |
| * 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; |
| |
| import org.apache.fop.datatypes.PercentBase; |
| import org.apache.fop.fo.pagination.Flow; |
| import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager; |
| import org.apache.fop.area.Area; |
| import org.apache.fop.area.BlockParent; |
| |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.ListIterator; |
| |
| /** |
| * LayoutManager for an fo:flow object. |
| * Its parent LM is the PageSequenceLayoutManager. |
| * This LM is responsible for getting columns of the appropriate size |
| * and filling them with block-level areas generated by its children. |
| * @todo Reintroduce emergency counter (generate error to avoid endless loop) |
| */ |
| public class FlowLayoutManager extends BlockStackingLayoutManager |
| implements BlockLevelLayoutManager { |
| private Flow fobj; |
| |
| /** Array of areas currently being filled stored by area class */ |
| private BlockParent[] currentAreas = new BlockParent[Area.CLASS_MAX]; |
| |
| private int currentSpan = EN_NONE; |
| |
| /** |
| * This is the top level layout manager. |
| * It is created by the PageSequence FO. |
| * @param pslm parent PageSequenceLayoutManager object |
| * @param node Flow object |
| */ |
| public FlowLayoutManager(PageSequenceLayoutManager pslm, Flow node) { |
| super(node); |
| fobj = node; |
| setParent(pslm); |
| } |
| |
| /** @see org.apache.fop.layoutmgr.LayoutManager */ |
| public LinkedList getNextKnuthElements(LayoutContext context, int alignment) { |
| |
| // set layout dimensions |
| int flowIPD = getCurrentPV().getCurrentSpan().getColumnWidth(); |
| int flowBPD = (int) getCurrentPV().getBodyRegion().getBPD(); |
| fobj.setLayoutDimension(PercentBase.REFERENCE_AREA_IPD, flowIPD); |
| fobj.setLayoutDimension(PercentBase.REFERENCE_AREA_BPD, flowBPD); |
| fobj.setLayoutDimension(PercentBase.BLOCK_IPD, context.getRefIPD()); |
| fobj.setLayoutDimension(PercentBase.BLOCK_BPD, context.getStackLimit().opt); |
| |
| // currently active LM |
| BlockLevelLayoutManager curLM; |
| LinkedList returnedList; |
| LinkedList returnList = new LinkedList(); |
| |
| while ((curLM = ((BlockLevelLayoutManager) getChildLM())) != null) { |
| if (curLM instanceof InlineLevelLayoutManager) { |
| log.error("inline area not allowed under flow - ignoring"); |
| curLM.setFinished(true); |
| continue; |
| } |
| |
| int span = EN_NONE; |
| if (curLM instanceof BlockLayoutManager) { |
| span = ((BlockLayoutManager)curLM).getBlockFO().getSpan(); |
| } else if (curLM instanceof BlockContainerLayoutManager) { |
| span = ((BlockContainerLayoutManager)curLM).getBlockContainerFO().getSpan(); |
| } |
| if (currentSpan != span) { |
| log.debug("span change from " + currentSpan + " to " + span); |
| context.signalSpanChange(span); |
| currentSpan = span; |
| return returnList; |
| } |
| |
| // Set up a LayoutContext |
| //MinOptMax bpd = context.getStackLimit(); |
| |
| LayoutContext childLC = new LayoutContext(0); |
| childLC.setStackLimit(context.getStackLimit()); |
| childLC.setRefIPD(context.getRefIPD()); |
| |
| // get elements from curLM |
| returnedList = curLM.getNextKnuthElements(childLC, alignment); |
| //log.debug("FLM.getNextKnuthElements> returnedList.size() = " + returnedList.size()); |
| if (returnList.size() == 0 && childLC.isKeepWithPreviousPending()) { |
| context.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING); |
| childLC.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING, false); |
| } |
| |
| // "wrap" the Position inside each element |
| LinkedList tempList = returnedList; |
| returnedList = new LinkedList(); |
| wrapPositionElements(tempList, returnedList); |
| |
| if (returnedList.size() == 1 |
| && ((KnuthElement)returnedList.getFirst()).isPenalty() |
| && ((KnuthPenalty)returnedList.getFirst()).getP() == -KnuthElement.INFINITE) { |
| // a descendant of this flow has break-before |
| returnList.addAll(returnedList); |
| return returnList; |
| } else { |
| if (returnList.size() > 0) { |
| // there is a block before this one |
| if (context.isKeepWithNextPending() |
| || childLC.isKeepWithPreviousPending()) { |
| //Clear pending keep flag |
| context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING, false); |
| childLC.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING, false); |
| // add an infinite penalty to forbid a break between blocks |
| returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, |
| new Position(this), false)); |
| } else if (!((KnuthElement) returnList.getLast()).isGlue()) { |
| // add a null penalty to allow a break between blocks |
| returnList.add(new KnuthPenalty(0, 0, false, |
| new Position(this), false)); |
| } |
| } |
| if (returnedList.size() > 0) { // controllare! |
| returnList.addAll(returnedList); |
| if (((KnuthElement)returnedList.getLast()).isPenalty() |
| && ((KnuthPenalty)returnedList.getLast()).getP() |
| == -KnuthElement.INFINITE) { |
| // a descendant of this flow has break-after |
| return returnList; |
| } |
| } |
| } |
| if (childLC.isKeepWithNextPending()) { |
| //Clear and propagate |
| childLC.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING, false); |
| context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING); |
| } |
| } |
| |
| setFinished(true); |
| |
| if (returnList.size() > 0) { |
| return returnList; |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager |
| */ |
| public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) { |
| log.debug(" FLM.negotiateBPDAdjustment> " + adj); |
| |
| if (lastElement.getPosition() instanceof NonLeafPosition) { |
| // this element was not created by this FlowLM |
| NonLeafPosition savedPos = (NonLeafPosition)lastElement.getPosition(); |
| lastElement.setPosition(savedPos.getPosition()); |
| int returnValue = ((BlockLevelLayoutManager)lastElement.getLayoutManager()) |
| .negotiateBPDAdjustment(adj, lastElement); |
| lastElement.setPosition(savedPos); |
| log.debug(" FLM.negotiateBPDAdjustment> result " + returnValue); |
| return returnValue; |
| } else { |
| return 0; |
| } |
| } |
| |
| /** |
| * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager |
| */ |
| public void discardSpace(KnuthGlue spaceGlue) { |
| log.debug(" FLM.discardSpace> "); |
| |
| if (spaceGlue.getPosition() instanceof NonLeafPosition) { |
| // this element was not created by this FlowLM |
| NonLeafPosition savedPos = (NonLeafPosition)spaceGlue.getPosition(); |
| spaceGlue.setPosition(savedPos.getPosition()); |
| ((BlockLevelLayoutManager) spaceGlue.getLayoutManager()).discardSpace(spaceGlue); |
| spaceGlue.setPosition(savedPos); |
| } |
| } |
| |
| /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether() */ |
| public boolean mustKeepTogether() { |
| return false; |
| } |
| |
| /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious() */ |
| public boolean mustKeepWithPrevious() { |
| return false; |
| } |
| |
| /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext() */ |
| public boolean mustKeepWithNext() { |
| return false; |
| } |
| |
| /** |
| * @see org.apache.fop.layoutmgr.LayoutManager#getChangedKnuthElements(java.util.List, int) |
| */ |
| public LinkedList getChangedKnuthElements(List oldList, /*int flaggedPenalty,*/ int alignment) { |
| ListIterator oldListIterator = oldList.listIterator(); |
| KnuthElement returnedElement; |
| LinkedList returnedList = new LinkedList(); |
| LinkedList returnList = new LinkedList(); |
| KnuthElement prevElement = null; |
| KnuthElement currElement = null; |
| int fromIndex = 0; |
| |
| // "unwrap" the Positions stored in the elements |
| KnuthElement oldElement; |
| while (oldListIterator.hasNext()) { |
| oldElement = (KnuthElement)oldListIterator.next(); |
| if (oldElement.getPosition() instanceof NonLeafPosition) { |
| // oldElement was created by a descendant of this FlowLM |
| oldElement.setPosition(((NonLeafPosition)oldElement.getPosition()).getPosition()); |
| } else { |
| // thisElement was created by this FlowLM, remove it |
| oldListIterator.remove(); |
| } |
| } |
| // reset the iterator |
| oldListIterator = oldList.listIterator(); |
| |
| |
| while (oldListIterator.hasNext()) { |
| currElement = (KnuthElement) oldListIterator.next(); |
| if (prevElement != null |
| && prevElement.getLayoutManager() != currElement.getLayoutManager()) { |
| // prevElement is the last element generated by the same LM |
| BlockLevelLayoutManager prevLM = (BlockLevelLayoutManager) |
| prevElement.getLayoutManager(); |
| BlockLevelLayoutManager currLM = (BlockLevelLayoutManager) |
| currElement.getLayoutManager(); |
| returnedList.addAll(prevLM.getChangedKnuthElements( |
| oldList.subList(fromIndex, oldListIterator.previousIndex()), alignment)); |
| fromIndex = oldListIterator.previousIndex(); |
| |
| // there is another block after this one |
| if (prevLM.mustKeepWithNext() |
| || currLM.mustKeepWithPrevious()) { |
| // add an infinite penalty to forbid a break between blocks |
| returnedList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, |
| new Position(this), false)); |
| } else if (!((KnuthElement) returnedList.getLast()).isGlue()) { |
| // add a null penalty to allow a break between blocks |
| returnedList.add(new KnuthPenalty(0, 0, false, new Position(this), false)); |
| } |
| } |
| prevElement = currElement; |
| } |
| if (currElement != null) { |
| BlockLevelLayoutManager currLM = (BlockLevelLayoutManager) |
| currElement.getLayoutManager(); |
| returnedList.addAll(currLM.getChangedKnuthElements( |
| oldList.subList(fromIndex, oldList.size()), alignment)); |
| } |
| |
| // "wrap" the Position stored in each element of returnedList |
| // and add elements to returnList |
| ListIterator listIter = returnedList.listIterator(); |
| while (listIter.hasNext()) { |
| returnedElement = (KnuthElement)listIter.next(); |
| if (returnedElement.getLayoutManager() != this) { |
| returnedElement.setPosition( |
| new NonLeafPosition(this, returnedElement.getPosition())); |
| } |
| returnList.add(returnedElement); |
| } |
| |
| return returnList; |
| } |
| |
| /** |
| * @see org.apache.fop.layoutmgr.LayoutManager#addAreas(PositionIterator, LayoutContext) |
| */ |
| public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) { |
| AreaAdditionUtil.addAreas(this, parentIter, layoutContext); |
| flush(); |
| } |
| |
| /** |
| * Add child area to a the correct container, depending on its |
| * area class. A Flow can fill at most one area container of any class |
| * at any one time. The actual work is done by BlockStackingLM. |
| * @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(Area) |
| */ |
| public void addChildArea(Area childArea) { |
| getParentArea(childArea); |
| addChildToArea(childArea, |
| this.currentAreas[childArea.getAreaClass()]); |
| } |
| |
| /** |
| * @see org.apache.fop.layoutmgr.LayoutManager#getParentArea(Area) |
| */ |
| public Area getParentArea(Area childArea) { |
| BlockParent parentArea = null; |
| int aclass = childArea.getAreaClass(); |
| |
| if (aclass == Area.CLASS_NORMAL) { |
| parentArea = getCurrentPV().getCurrentFlow(); |
| } else if (aclass == Area.CLASS_BEFORE_FLOAT) { |
| parentArea = getCurrentPV().getBodyRegion().getBeforeFloat(); |
| } else if (aclass == Area.CLASS_FOOTNOTE) { |
| parentArea = getCurrentPV().getBodyRegion().getFootnote(); |
| } else { |
| throw new IllegalStateException("(internal error) Invalid " |
| + "area class (" + aclass + ") requested."); |
| } |
| |
| this.currentAreas[aclass] = parentArea; |
| setCurrentArea(parentArea); |
| return parentArea; |
| } |
| |
| /** |
| * @see org.apache.fop.layoutmgr.LayoutManager#resetPosition(Position) |
| */ |
| public void resetPosition(Position resetPos) { |
| if (resetPos == null) { |
| reset(null); |
| } |
| } |
| } |
| |