1 17 18 19 20 package org.apache.fop.layoutmgr.list; 21 22 import org.apache.commons.logging.Log; 23 import org.apache.commons.logging.LogFactory; 24 import org.apache.fop.fo.flow.ListItem; 25 import org.apache.fop.fo.flow.ListItemBody; 26 import org.apache.fop.fo.flow.ListItemLabel; 27 import org.apache.fop.layoutmgr.BlockLevelLayoutManager; 28 import org.apache.fop.layoutmgr.BlockStackingLayoutManager; 29 import org.apache.fop.layoutmgr.BreakElement; 30 import org.apache.fop.layoutmgr.ConditionalElementListener; 31 import org.apache.fop.layoutmgr.ElementListObserver; 32 import org.apache.fop.layoutmgr.ElementListUtils; 33 import org.apache.fop.layoutmgr.LayoutManager; 34 import org.apache.fop.layoutmgr.LayoutContext; 35 import org.apache.fop.layoutmgr.PositionIterator; 36 import org.apache.fop.layoutmgr.Position; 37 import org.apache.fop.layoutmgr.NonLeafPosition; 38 import org.apache.fop.layoutmgr.RelSide; 39 import org.apache.fop.layoutmgr.SpaceResolver; 40 import org.apache.fop.layoutmgr.TraitSetter; 41 import org.apache.fop.layoutmgr.KnuthElement; 42 import org.apache.fop.layoutmgr.KnuthBox; 43 import org.apache.fop.layoutmgr.KnuthPenalty; 44 import org.apache.fop.layoutmgr.KnuthPossPosIter; 45 import org.apache.fop.area.Area; 46 import org.apache.fop.area.Block; 47 import org.apache.fop.traits.MinOptMax; 48 import org.apache.fop.traits.SpaceVal; 49 50 import java.util.ArrayList ; 51 import java.util.List ; 52 import java.util.LinkedList ; 53 import java.util.ListIterator ; 54 55 59 public class ListItemLayoutManager extends BlockStackingLayoutManager 60 implements ConditionalElementListener { 61 62 65 private static Log log = LogFactory.getLog(ListItemLayoutManager.class); 66 67 private ListItemContentLayoutManager label; 68 private ListItemContentLayoutManager body; 69 70 private Block curBlockArea = null; 71 72 private LinkedList labelList = null; 73 private LinkedList bodyList = null; 74 75 private int listItemHeight; 76 77 private boolean discardBorderBefore; 78 private boolean discardBorderAfter; 79 private boolean discardPaddingBefore; 80 private boolean discardPaddingAfter; 81 private MinOptMax effSpaceBefore; 82 private MinOptMax effSpaceAfter; 83 84 private boolean keepWithNextPendingOnLabel; 85 private boolean keepWithNextPendingOnBody; 86 87 private class ListItemPosition extends Position { 88 private int iLabelFirstIndex; 89 private int iLabelLastIndex; 90 private int iBodyFirstIndex; 91 private int iBodyLastIndex; 92 93 public ListItemPosition(LayoutManager lm, int labelFirst, int labelLast, 94 int bodyFirst, int bodyLast) { 95 super(lm); 96 iLabelFirstIndex = labelFirst; 97 iLabelLastIndex = labelLast; 98 iBodyFirstIndex = bodyFirst; 99 iBodyLastIndex = bodyLast; 100 } 101 102 public int getLabelFirstIndex() { 103 return iLabelFirstIndex; 104 } 105 106 public int getLabelLastIndex() { 107 return iLabelLastIndex; 108 } 109 110 public int getBodyFirstIndex() { 111 return iBodyFirstIndex; 112 } 113 114 public int getBodyLastIndex() { 115 return iBodyLastIndex; 116 } 117 118 119 public String toString() { 120 StringBuffer sb = new StringBuffer ("ListItemPosition:"); 121 sb.append(getIndex()).append("("); 122 sb.append("label:").append(iLabelFirstIndex).append("-").append(iLabelLastIndex); 123 sb.append(" body:").append(iBodyFirstIndex).append("-").append(iBodyLastIndex); 124 sb.append(")"); 125 return sb.toString(); 126 } 127 } 128 129 133 public ListItemLayoutManager(ListItem node) { 134 super(node); 135 setLabel(node.getLabel()); 136 setBody(node.getBody()); 137 } 138 139 143 protected ListItem getListItemFO() { 144 return (ListItem)fobj; 145 } 146 147 151 public void setLabel(ListItemLabel node) { 152 label = new ListItemContentLayoutManager(node); 153 label.setParent(this); 154 } 155 156 160 public void setBody(ListItemBody node) { 161 body = new ListItemContentLayoutManager(node); 162 body.setParent(this); 163 } 164 165 166 public void initialize() { 167 foSpaceBefore = new SpaceVal( 168 getListItemFO().getCommonMarginBlock().spaceBefore, this).getSpace(); 169 foSpaceAfter = new SpaceVal( 170 getListItemFO().getCommonMarginBlock().spaceAfter, this).getSpace(); 171 startIndent = getListItemFO().getCommonMarginBlock().startIndent.getValue(this); 172 endIndent = getListItemFO().getCommonMarginBlock().endIndent.getValue(this); 173 } 174 175 private void resetSpaces() { 176 this.discardBorderBefore = false; 177 this.discardBorderAfter = false; 178 this.discardPaddingBefore = false; 179 this.discardPaddingAfter = false; 180 this.effSpaceBefore = null; 181 this.effSpaceAfter = null; 182 } 183 184 185 public LinkedList getNextKnuthElements(LayoutContext context, int alignment) { 186 referenceIPD = context.getRefIPD(); 187 LayoutContext childLC; 188 189 LinkedList returnList = new LinkedList (); 190 191 if (!breakBeforeServed) { 192 try { 193 if (addKnuthElementsForBreakBefore(returnList, context)) { 194 return returnList; 195 } 196 } finally { 197 breakBeforeServed = true; 198 } 199 } 200 201 addKnuthElementsForSpaceBefore(returnList, alignment); 202 203 addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed); 204 firstVisibleMarkServed = true; 205 206 addPendingMarks(context); 208 209 childLC = new LayoutContext(0); 211 childLC.setRefIPD(context.getRefIPD()); 212 label.initialize(); 213 labelList = label.getNextKnuthElements(childLC, alignment); 214 215 SpaceResolver.resolveElementList(labelList); 218 ElementListObserver.observe(labelList, "list-item-label", label.getPartFO().getId()); 219 220 if (childLC.isKeepWithPreviousPending()) { 221 context.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING); 222 } 223 this.keepWithNextPendingOnLabel = childLC.isKeepWithNextPending(); 224 225 childLC = new LayoutContext(0); 227 childLC.setRefIPD(context.getRefIPD()); 228 body.initialize(); 229 bodyList = body.getNextKnuthElements(childLC, alignment); 230 231 SpaceResolver.resolveElementList(bodyList); 234 ElementListObserver.observe(bodyList, "list-item-body", body.getPartFO().getId()); 235 236 if (childLC.isKeepWithPreviousPending()) { 237 context.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING); 238 } 239 this.keepWithNextPendingOnBody = childLC.isKeepWithNextPending(); 240 241 LinkedList returnedList = getCombinedKnuthElementsForListItem(labelList, bodyList, context); 243 244 wrapPositionElements(returnedList, returnList, true); 246 247 addKnuthElementsForBorderPaddingAfter(returnList, true); 248 addKnuthElementsForSpaceAfter(returnList, alignment); 249 addKnuthElementsForBreakAfter(returnList, context); 250 251 if (keepWithNextPendingOnLabel || keepWithNextPendingOnBody || mustKeepWithNext()) { 252 context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING); 253 } 254 if (mustKeepWithPrevious()) { 255 context.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING); 256 } 257 258 setFinished(true); 259 resetSpaces(); 260 return returnList; 261 } 262 263 private LinkedList getCombinedKnuthElementsForListItem(LinkedList labelElements, 264 LinkedList bodyElements, 265 LayoutContext context) { 266 List [] elementLists = {new ArrayList (labelElements), 268 new ArrayList (bodyElements)}; 269 int[] fullHeights = {ElementListUtils.calcContentLength(elementLists[0]), 270 ElementListUtils.calcContentLength(elementLists[1])}; 271 int[] partialHeights = {0, 0}; 272 int[] start = {-1, -1}; 273 int[] end = {-1, -1}; 274 275 int totalHeight = Math.max(fullHeights[0], fullHeights[1]); 276 int step; 277 int addedBoxHeight = 0; 278 boolean keepWithNextActive = false; 279 280 LinkedList returnList = new LinkedList (); 281 while ((step = getNextStep(elementLists, start, end, partialHeights)) 282 > 0) { 283 284 if (end[0] + 1 == elementLists[0].size()) { 285 if (keepWithNextPendingOnLabel) { 286 keepWithNextActive = true; 287 } 288 } 289 if (end[1] + 1 == elementLists[1].size()) { 290 if (keepWithNextPendingOnBody) { 291 keepWithNextActive = true; 292 } 293 } 294 295 int penaltyHeight = step 297 + getMaxRemainingHeight(fullHeights, partialHeights) 298 - totalHeight; 299 300 int additionalPenaltyHeight = 0; 302 KnuthElement endEl = (KnuthElement)elementLists[0].get(end[0]); 303 if (endEl instanceof KnuthPenalty) { 304 additionalPenaltyHeight = ((KnuthPenalty)endEl).getW(); 305 } 306 endEl = (KnuthElement)elementLists[1].get(end[1]); 307 if (endEl instanceof KnuthPenalty) { 308 additionalPenaltyHeight = Math.max( 309 additionalPenaltyHeight, ((KnuthPenalty)endEl).getW()); 310 } 311 312 int boxHeight = step - addedBoxHeight - penaltyHeight; 313 penaltyHeight += additionalPenaltyHeight; 315 addedBoxHeight += boxHeight; 317 ListItemPosition stepPosition = new ListItemPosition(this, 318 start[0], end[0], start[1], end[1]); 319 returnList.add(new KnuthBox(boxHeight, stepPosition, false)); 320 if (addedBoxHeight < totalHeight) { 321 int p = 0; 322 if (keepWithNextActive || mustKeepTogether()) { 323 p = KnuthPenalty.INFINITE; 324 } 325 returnList.add(new BreakElement(stepPosition, penaltyHeight, p, -1, context)); 326 } 327 } 328 329 return returnList; 330 } 331 332 private int getNextStep(List [] elementLists, int[] start, int[] end, int[] partialHeights) { 333 int[] backupHeights = {partialHeights[0], partialHeights[1]}; 335 336 start[0] = end[0] + 1; 338 start[1] = end[1] + 1; 339 340 int seqCount = 0; 342 for (int i = 0; i < start.length; i++) { 343 while (end[i] + 1 < elementLists[i].size()) { 344 end[i]++; 345 KnuthElement el = (KnuthElement)elementLists[i].get(end[i]); 346 if (el.isPenalty()) { 347 if (el.getP() < KnuthElement.INFINITE) { 348 break; 350 } 351 } else if (el.isGlue()) { 352 if (end[i] > 0) { 353 KnuthElement prev = (KnuthElement)elementLists[i].get(end[i] - 1); 354 if (prev.isBox()) { 355 break; 357 } 358 } 359 partialHeights[i] += el.getW(); 360 } else { 361 partialHeights[i] += el.getW(); 362 } 363 } 364 if (end[i] < start[i]) { 365 partialHeights[i] = backupHeights[i]; 366 } else { 367 seqCount++; 368 } 369 } 370 if (seqCount == 0) { 371 return 0; 372 } 373 374 int step; 376 if (backupHeights[0] == 0 && backupHeights[1] == 0) { 377 step = Math.max((end[0] >= start[0] ? partialHeights[0] : Integer.MIN_VALUE), 381 (end[1] >= start[1] ? partialHeights[1] : Integer.MIN_VALUE)); 382 } else { 383 step = Math.min((end[0] >= start[0] ? partialHeights[0] : Integer.MAX_VALUE), 385 (end[1] >= start[1] ? partialHeights[1] : Integer.MAX_VALUE)); 386 } 387 388 for (int i = 0; i < partialHeights.length; i++) { 390 if (partialHeights[i] > step) { 391 partialHeights[i] = backupHeights[i]; 392 end[i] = start[i] - 1; 393 } 394 } 395 396 return step; 397 } 398 399 private int getMaxRemainingHeight(int[] fullHeights, int[] partialHeights) { 400 return Math.max(fullHeights[0] - partialHeights[0], 401 fullHeights[1] - partialHeights[1]); 402 } 403 404 407 public LinkedList getChangedKnuthElements(List oldList, int alignment) { 408 labelList = label.getChangedKnuthElements(labelList, alignment); 411 412 ListIterator oldListIterator = oldList.listIterator(); 416 KnuthElement oldElement = null; 417 while (oldListIterator.hasNext()) { 418 oldElement = (KnuthElement)oldListIterator.next(); 419 Position innerPosition = ((NonLeafPosition) oldElement.getPosition()).getPosition(); 420 if (innerPosition != null) { 426 oldElement.setPosition(innerPosition); 428 } else { 429 oldElement.setPosition(new Position(this)); 433 } 434 } 435 436 LinkedList returnedList = body.getChangedKnuthElements(oldList, alignment); 437 LinkedList tempList = returnedList; 439 KnuthElement tempElement; 440 returnedList = new LinkedList (); 441 ListIterator listIter = tempList.listIterator(); 442 while (listIter.hasNext()) { 443 tempElement = (KnuthElement)listIter.next(); 444 tempElement.setPosition(new NonLeafPosition(this, tempElement.getPosition())); 445 returnedList.add(tempElement); 446 } 447 448 return returnedList; 449 } 450 451 458 public void addAreas(PositionIterator parentIter, 459 LayoutContext layoutContext) { 460 getParentArea(null); 461 462 getPSLM().addIDToPage(getListItemFO().getId()); 463 464 LayoutContext lc = new LayoutContext(0); 465 Position firstPos = null; 466 Position lastPos = null; 467 468 LinkedList positionList = new LinkedList (); 470 Position pos; 471 while (parentIter.hasNext()) { 472 pos = (Position) parentIter.next(); 473 if (pos.getIndex() >= 0) { 474 if (firstPos == null) { 475 firstPos = pos; 476 } 477 lastPos = pos; 478 } 479 if (pos instanceof NonLeafPosition && pos.getPosition() != null) { 480 positionList.add(((NonLeafPosition) pos).getPosition()); 482 } 483 } 484 485 if (markers != null) { 486 getCurrentPV().addMarkers(markers, true, isFirst(firstPos), isLast(lastPos)); 487 } 488 489 int labelFirstIndex = ((ListItemPosition) positionList.getFirst()).getLabelFirstIndex(); 492 int labelLastIndex = ((ListItemPosition) positionList.getLast()).getLabelLastIndex(); 493 int bodyFirstIndex = ((ListItemPosition) positionList.getFirst()).getBodyFirstIndex(); 494 int bodyLastIndex = ((ListItemPosition) positionList.getLast()).getBodyLastIndex(); 495 496 int previousBreak = ElementListUtils.determinePreviousBreak(labelList, labelFirstIndex); 498 SpaceResolver.performConditionalsNotification(labelList, 499 labelFirstIndex, labelLastIndex, previousBreak); 500 501 previousBreak = ElementListUtils.determinePreviousBreak(bodyList, bodyFirstIndex); 503 SpaceResolver.performConditionalsNotification(bodyList, 504 bodyFirstIndex, bodyLastIndex, previousBreak); 505 506 if (labelFirstIndex <= labelLastIndex) { 508 KnuthPossPosIter labelIter = new KnuthPossPosIter(labelList, 509 labelFirstIndex, labelLastIndex + 1); 510 lc.setFlags(LayoutContext.FIRST_AREA, layoutContext.isFirstArea()); 511 lc.setFlags(LayoutContext.LAST_AREA, layoutContext.isLastArea()); 512 lc.setSpaceAdjust(layoutContext.getSpaceAdjust()); 514 lc.setStackLimit(layoutContext.getStackLimit()); 516 label.addAreas(labelIter, lc); 517 } 518 519 int savedBPD = 0; 521 if (labelFirstIndex <= labelLastIndex 522 && bodyFirstIndex <= bodyLastIndex) { 523 savedBPD = curBlockArea.getBPD(); 524 curBlockArea.setBPD(0); 525 } 526 527 if (bodyFirstIndex <= bodyLastIndex) { 529 KnuthPossPosIter bodyIter = new KnuthPossPosIter(bodyList, 530 bodyFirstIndex, bodyLastIndex + 1); 531 lc.setFlags(LayoutContext.FIRST_AREA, layoutContext.isFirstArea()); 532 lc.setFlags(LayoutContext.LAST_AREA, layoutContext.isLastArea()); 533 lc.setSpaceAdjust(layoutContext.getSpaceAdjust()); 535 lc.setStackLimit(layoutContext.getStackLimit()); 537 body.addAreas(bodyIter, lc); 538 } 539 540 if (curBlockArea.getBPD() < savedBPD) { 542 curBlockArea.setBPD(savedBPD); 543 } 544 545 if (markers != null) { 546 getCurrentPV().addMarkers(markers, false, isFirst(firstPos), isLast(lastPos)); 547 } 548 549 TraitSetter.addBackground(curBlockArea, 551 getListItemFO().getCommonBorderPaddingBackground(), 552 this); 553 TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext.getSpaceAdjust(), 554 effSpaceBefore, effSpaceAfter); 555 556 flush(); 557 558 curBlockArea = null; 559 resetSpaces(); 560 561 getPSLM().notifyEndOfLayout(((ListItem)getFObj()).getId()); 562 } 563 564 570 public int getListItemHeight() { 571 return listItemHeight; 572 } 573 574 587 public Area getParentArea(Area childArea) { 588 if (curBlockArea == null) { 589 curBlockArea = new Block(); 590 591 parentLM.getParentArea(curBlockArea); 593 594 TraitSetter.setProducerID(curBlockArea, getListItemFO().getId()); 596 TraitSetter.addBorders(curBlockArea, 597 getListItemFO().getCommonBorderPaddingBackground(), 598 discardBorderBefore, discardBorderAfter, false, false, this); 599 TraitSetter.addPadding(curBlockArea, 600 getListItemFO().getCommonBorderPaddingBackground(), 601 discardPaddingBefore, discardPaddingAfter, false, false, this); 602 TraitSetter.addMargins(curBlockArea, 603 getListItemFO().getCommonBorderPaddingBackground(), 604 getListItemFO().getCommonMarginBlock(), this); 605 TraitSetter.addBreaks(curBlockArea, 606 getListItemFO().getBreakBefore(), 607 getListItemFO().getBreakAfter()); 608 609 int contentIPD = referenceIPD - getIPIndents(); 610 curBlockArea.setIPD(contentIPD); 611 612 setCurrentArea(curBlockArea); 613 } 614 return curBlockArea; 615 } 616 617 624 public void addChildArea(Area childArea) { 625 if (curBlockArea != null) { 626 curBlockArea.addBlock((Block) childArea); 627 } 628 } 629 630 635 public void resetPosition(Position resetPos) { 636 if (resetPos == null) { 637 reset(null); 638 } 639 } 640 641 642 public boolean mustKeepTogether() { 643 return ((BlockLevelLayoutManager)getParent()).mustKeepTogether() 645 || !getListItemFO().getKeepTogether().getWithinPage().isAuto() 646 || !getListItemFO().getKeepTogether().getWithinColumn().isAuto(); 647 } 648 649 650 public boolean mustKeepWithPrevious() { 651 return !getListItemFO().getKeepWithPrevious().getWithinPage().isAuto() 652 || !getListItemFO().getKeepWithPrevious().getWithinColumn().isAuto(); 653 } 654 655 656 public boolean mustKeepWithNext() { 657 return !getListItemFO().getKeepWithNext().getWithinPage().isAuto() 658 || !getListItemFO().getKeepWithNext().getWithinColumn().isAuto(); 659 } 660 661 662 public void notifySpace(RelSide side, MinOptMax effectiveLength) { 663 if (RelSide.BEFORE == side) { 664 if (log.isDebugEnabled()) { 665 log.debug(this + ": Space " + side + ", " 666 + this.effSpaceBefore + "-> " + effectiveLength); 667 } 668 this.effSpaceBefore = effectiveLength; 669 } else { 670 if (log.isDebugEnabled()) { 671 log.debug(this + ": Space " + side + ", " 672 + this.effSpaceAfter + "-> " + effectiveLength); 673 } 674 this.effSpaceAfter = effectiveLength; 675 } 676 } 677 678 679 public void notifyBorder(RelSide side, MinOptMax effectiveLength) { 680 if (effectiveLength == null) { 681 if (RelSide.BEFORE == side) { 682 this.discardBorderBefore = true; 683 } else { 684 this.discardBorderAfter = true; 685 } 686 } 687 if (log.isDebugEnabled()) { 688 log.debug(this + ": Border " + side + " -> " + effectiveLength); 689 } 690 } 691 692 693 public void notifyPadding(RelSide side, MinOptMax effectiveLength) { 694 if (effectiveLength == null) { 695 if (RelSide.BEFORE == side) { 696 this.discardPaddingBefore = true; 697 } else { 698 this.discardPaddingAfter = true; 699 } 700 } 701 if (log.isDebugEnabled()) { 702 log.debug(this + ": Padding " + side + " -> " + effectiveLength); 703 } 704 } 705 706 707 } 708 709 | Popular Tags |