001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /* $Id: ListBlockLayoutManager.java 453310 2006-10-05 18:44:15Z spepping $ */
019:
020: package org.apache.fop.layoutmgr.list;
021:
022: import org.apache.commons.logging.Log;
023: import org.apache.commons.logging.LogFactory;
024: import org.apache.fop.fo.flow.ListBlock;
025: import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
026: import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
027: import org.apache.fop.layoutmgr.ConditionalElementListener;
028: import org.apache.fop.layoutmgr.ElementListUtils;
029: import org.apache.fop.layoutmgr.LayoutManager;
030: import org.apache.fop.layoutmgr.LayoutContext;
031: import org.apache.fop.layoutmgr.PositionIterator;
032: import org.apache.fop.layoutmgr.Position;
033: import org.apache.fop.layoutmgr.NonLeafPosition;
034: import org.apache.fop.layoutmgr.RelSide;
035: import org.apache.fop.layoutmgr.TraitSetter;
036: import org.apache.fop.area.Area;
037: import org.apache.fop.area.Block;
038: import org.apache.fop.traits.MinOptMax;
039: import org.apache.fop.traits.SpaceVal;
040:
041: import java.util.Iterator;
042: import java.util.LinkedList;
043: import java.util.List;
044:
045: /**
046: * LayoutManager for a list-block FO.
047: * A list block contains list items which are stacked within
048: * the list block area..
049: */
050: public class ListBlockLayoutManager extends BlockStackingLayoutManager
051: implements ConditionalElementListener {
052:
053: /**
054: * logging instance
055: */
056: private static Log log = LogFactory
057: .getLog(ListBlockLayoutManager.class);
058:
059: private Block curBlockArea;
060:
061: private boolean discardBorderBefore;
062: private boolean discardBorderAfter;
063: private boolean discardPaddingBefore;
064: private boolean discardPaddingAfter;
065: private MinOptMax effSpaceBefore;
066: private MinOptMax effSpaceAfter;
067:
068: private static class StackingIter extends PositionIterator {
069: StackingIter(Iterator parentIter) {
070: super (parentIter);
071: }
072:
073: protected LayoutManager getLM(Object nextObj) {
074: return ((Position) nextObj).getLM();
075: }
076:
077: protected Position getPos(Object nextObj) {
078: return ((Position) nextObj);
079: }
080: }
081:
082: /**
083: * Create a new list block layout manager.
084: * @param node list-block to create the layout manager for
085: */
086: public ListBlockLayoutManager(ListBlock node) {
087: super (node);
088: }
089:
090: /**
091: * Convenience method.
092: * @return the ListBlock node
093: */
094: protected ListBlock getListBlockFO() {
095: return (ListBlock) fobj;
096: }
097:
098: /** @see org.apache.fop.layoutmgr.LayoutManager#initialize() */
099: public void initialize() {
100: foSpaceBefore = new SpaceVal(getListBlockFO()
101: .getCommonMarginBlock().spaceBefore, this ).getSpace();
102: foSpaceAfter = new SpaceVal(getListBlockFO()
103: .getCommonMarginBlock().spaceAfter, this ).getSpace();
104: startIndent = getListBlockFO().getCommonMarginBlock().startIndent
105: .getValue(this );
106: endIndent = getListBlockFO().getCommonMarginBlock().endIndent
107: .getValue(this );
108: }
109:
110: private void resetSpaces() {
111: this .discardBorderBefore = false;
112: this .discardBorderAfter = false;
113: this .discardPaddingBefore = false;
114: this .discardPaddingAfter = false;
115: this .effSpaceBefore = null;
116: this .effSpaceAfter = null;
117: }
118:
119: /** @see org.apache.fop.layoutmgr.BlockStackingLayoutManager */
120: public LinkedList getNextKnuthElements(LayoutContext context,
121: int alignment) {
122: resetSpaces();
123: LinkedList returnList = super .getNextKnuthElements(context,
124: alignment);
125:
126: //fox:widow-content-limit
127: int widowRowLimit = getListBlockFO().getWidowContentLimit()
128: .getValue();
129: if (widowRowLimit != 0) {
130: ElementListUtils.removeLegalBreaks(returnList,
131: widowRowLimit);
132: }
133:
134: //fox:orphan-content-limit
135: int orphanRowLimit = getListBlockFO().getOrphanContentLimit()
136: .getValue();
137: if (orphanRowLimit != 0) {
138: ElementListUtils.removeLegalBreaksFromEnd(returnList,
139: orphanRowLimit);
140: }
141:
142: return returnList;
143: }
144:
145: /** @see org.apache.fop.layoutmgr.LayoutManager#getChangedKnuthElements(java.util.List, int) */
146: public LinkedList getChangedKnuthElements(List oldList,
147: int alignment) {
148: //log.debug("LBLM.getChangedKnuthElements>");
149: return super .getChangedKnuthElements(oldList, alignment);
150: }
151:
152: /**
153: * The table area is a reference area that contains areas for
154: * columns, bodies, rows and the contents are in cells.
155: *
156: * @param parentIter the position iterator
157: * @param layoutContext the layout context for adding areas
158: */
159: public void addAreas(PositionIterator parentIter,
160: LayoutContext layoutContext) {
161: getParentArea(null);
162:
163: // if this will create the first block area in a page
164: // and display-align is after or center, add space before
165: if (layoutContext.getSpaceBefore() > 0) {
166: addBlockSpacing(0.0, new MinOptMax(layoutContext
167: .getSpaceBefore()));
168: }
169:
170: getPSLM().addIDToPage(getListBlockFO().getId());
171:
172: // the list block contains areas stacked from each list item
173:
174: LayoutManager childLM = null;
175: LayoutContext lc = new LayoutContext(0);
176: LayoutManager firstLM = null;
177: LayoutManager lastLM = null;
178: Position firstPos = null;
179: Position lastPos = null;
180:
181: // "unwrap" the NonLeafPositions stored in parentIter
182: // and put them in a new list;
183: LinkedList positionList = new LinkedList();
184: Position pos;
185: while (parentIter.hasNext()) {
186: pos = (Position) parentIter.next();
187: if (pos.getIndex() >= 0) {
188: if (firstPos == null) {
189: firstPos = pos;
190: }
191: lastPos = pos;
192: }
193: if (pos instanceof NonLeafPosition
194: && (pos.getPosition() != null)
195: && ((NonLeafPosition) pos).getPosition().getLM() != this ) {
196: // pos was created by a child of this ListBlockLM
197: positionList.add(((NonLeafPosition) pos).getPosition());
198: lastLM = ((NonLeafPosition) pos).getPosition().getLM();
199: if (firstLM == null) {
200: firstLM = lastLM;
201: }
202: }
203: }
204:
205: if (markers != null) {
206: getCurrentPV().addMarkers(markers, true, isFirst(firstPos),
207: isLast(lastPos));
208: }
209:
210: StackingIter childPosIter = new StackingIter(positionList
211: .listIterator());
212: while ((childLM = childPosIter.getNextChildLM()) != null) {
213: // Add the block areas to Area
214: // set the space adjustment ratio
215: lc.setSpaceAdjust(layoutContext.getSpaceAdjust());
216: lc.setFlags(LayoutContext.FIRST_AREA, childLM == firstLM);
217: lc.setFlags(LayoutContext.LAST_AREA, childLM == lastLM);
218: lc.setStackLimit(layoutContext.getStackLimit());
219: childLM.addAreas(childPosIter, lc);
220: }
221:
222: if (markers != null) {
223: getCurrentPV().addMarkers(markers, false,
224: isFirst(firstPos), isLast(lastPos));
225: }
226:
227: // We are done with this area add the background
228: TraitSetter.addBackground(curBlockArea, getListBlockFO()
229: .getCommonBorderPaddingBackground(), this );
230: TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext
231: .getSpaceAdjust(), effSpaceBefore, effSpaceAfter);
232:
233: flush();
234:
235: curBlockArea = null;
236: resetSpaces();
237:
238: getPSLM().notifyEndOfLayout(((ListBlock) getFObj()).getId());
239: }
240:
241: /**
242: * Return an Area which can contain the passed childArea. The childArea
243: * may not yet have any content, but it has essential traits set.
244: * In general, if the LayoutManager already has an Area it simply returns
245: * it. Otherwise, it makes a new Area of the appropriate class.
246: * It gets a parent area for its area by calling its parent LM.
247: * Finally, based on the dimensions of the parent area, it initializes
248: * its own area. This includes setting the content IPD and the maximum
249: * BPD.
250: *
251: * @param childArea the child area
252: * @return the parent area of the child
253: */
254: public Area getParentArea(Area childArea) {
255: if (curBlockArea == null) {
256: curBlockArea = new Block();
257:
258: // Set up dimensions
259: // Must get dimensions from parent area
260: /*Area parentArea =*/parentLM.getParentArea(curBlockArea);
261:
262: // set traits
263: TraitSetter.setProducerID(curBlockArea, getListBlockFO()
264: .getId());
265: TraitSetter.addBorders(curBlockArea, getListBlockFO()
266: .getCommonBorderPaddingBackground(),
267: discardBorderBefore, discardBorderAfter, false,
268: false, this );
269: TraitSetter.addPadding(curBlockArea, getListBlockFO()
270: .getCommonBorderPaddingBackground(),
271: discardPaddingBefore, discardPaddingAfter, false,
272: false, this );
273: TraitSetter.addMargins(curBlockArea, getListBlockFO()
274: .getCommonBorderPaddingBackground(),
275: getListBlockFO().getCommonMarginBlock(), this );
276: TraitSetter
277: .addBreaks(curBlockArea, getListBlockFO()
278: .getBreakBefore(), getListBlockFO()
279: .getBreakAfter());
280:
281: int contentIPD = referenceIPD - getIPIndents();
282: curBlockArea.setIPD(contentIPD);
283:
284: setCurrentArea(curBlockArea);
285: }
286: return curBlockArea;
287: }
288:
289: /**
290: * Add the child area to this layout manager.
291: *
292: * @param childArea the child area to add
293: */
294: public void addChildArea(Area childArea) {
295: if (curBlockArea != null) {
296: curBlockArea.addBlock((Block) childArea);
297: }
298: }
299:
300: /**
301: * Reset the position of this layout manager.
302: *
303: * @param resetPos the position to reset to
304: */
305: public void resetPosition(Position resetPos) {
306: if (resetPos == null) {
307: reset(null);
308: } else {
309: //TODO Something to put here?
310: }
311: }
312:
313: /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether() */
314: public boolean mustKeepTogether() {
315: //TODO Keeps will have to be more sophisticated sooner or later
316: return ((BlockLevelLayoutManager) getParent())
317: .mustKeepTogether()
318: || !getListBlockFO().getKeepTogether().getWithinPage()
319: .isAuto()
320: || !getListBlockFO().getKeepTogether()
321: .getWithinColumn().isAuto();
322: }
323:
324: /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious() */
325: public boolean mustKeepWithPrevious() {
326: return !getListBlockFO().getKeepWithPrevious().getWithinPage()
327: .isAuto()
328: || !getListBlockFO().getKeepWithPrevious()
329: .getWithinColumn().isAuto();
330: }
331:
332: /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext() */
333: public boolean mustKeepWithNext() {
334: return !getListBlockFO().getKeepWithNext().getWithinPage()
335: .isAuto()
336: || !getListBlockFO().getKeepWithNext()
337: .getWithinColumn().isAuto();
338: }
339:
340: /** @see org.apache.fop.layoutmgr.ConditionalElementListener */
341: public void notifySpace(RelSide side, MinOptMax effectiveLength) {
342: if (RelSide.BEFORE == side) {
343: if (log.isDebugEnabled()) {
344: log
345: .debug(this + ": Space " + side + ", "
346: + this .effSpaceBefore + "-> "
347: + effectiveLength);
348: }
349: this .effSpaceBefore = effectiveLength;
350: } else {
351: if (log.isDebugEnabled()) {
352: log.debug(this + ": Space " + side + ", "
353: + this .effSpaceAfter + "-> " + effectiveLength);
354: }
355: this .effSpaceAfter = effectiveLength;
356: }
357: }
358:
359: /** @see org.apache.fop.layoutmgr.ConditionalElementListener */
360: public void notifyBorder(RelSide side, MinOptMax effectiveLength) {
361: if (effectiveLength == null) {
362: if (RelSide.BEFORE == side) {
363: this .discardBorderBefore = true;
364: } else {
365: this .discardBorderAfter = true;
366: }
367: }
368: if (log.isDebugEnabled()) {
369: log.debug(this + ": Border " + side + " -> "
370: + effectiveLength);
371: }
372: }
373:
374: /** @see org.apache.fop.layoutmgr.ConditionalElementListener */
375: public void notifyPadding(RelSide side, MinOptMax effectiveLength) {
376: if (effectiveLength == null) {
377: if (RelSide.BEFORE == side) {
378: this .discardPaddingBefore = true;
379: } else {
380: this .discardPaddingAfter = true;
381: }
382: }
383: if (log.isDebugEnabled()) {
384: log.debug(this + ": Padding " + side + " -> "
385: + effectiveLength);
386: }
387: }
388:
389: }
|