/* -*- mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: t -*- */ /* AbiWord * Copyright (C) 1998 AbiSource, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ #include "fb_ColumnBreaker.h" #include "fl_DocLayout.h" #include "fl_SectionLayout.h" #include "fp_ContainerObject.h" #include "fp_TableContainer.h" #include "fp_FootnoteContainer.h" #include "fp_TOCContainer.h" #include "fl_BlockLayout.h" #include "fp_Line.h" #include "fp_Column.h" #include "ut_assert.h" #include "fl_ContainerLayout.h" #include "fl_FootnoteLayout.h" #include "fp_Page.h" fb_ColumnBreaker::fb_ColumnBreaker(fl_DocSectionLayout * pDSL) : m_pStartPage(NULL), m_bStartFromStart(true), m_bReBreak(false), m_pDocSec(pDSL), m_pCurrentBlock(NULL) { } void fb_ColumnBreaker::setStartPage(fp_Page * pPage) { // if(!m_bStartFromStart) { if(m_pStartPage == NULL) { m_pStartPage = pPage; return; } FL_DocLayout * pDL = m_pDocSec->getDocLayout(); UT_sint32 iCurPage = pDL->findPage(m_pStartPage); UT_sint32 iNewPage = pDL->findPage(pPage); if(iCurPage < 0 && iNewPage >= 0) { m_pStartPage = pPage; } else if( (iNewPage >= 0) && (iNewPage < iCurPage)) { m_pStartPage = pPage; } else if( (iNewPage < 0) && (iCurPage < 0)) { m_pStartPage = NULL; m_bStartFromStart = true; } } if(pPage == NULL) { m_bStartFromStart = true; m_pStartPage = NULL; } return; } /*! * Returns true if Annotations are to be display */ bool fb_ColumnBreaker::_displayAnnotations(void) { return m_pDocSec->getDocLayout()->displayAnnotations(); } /*! * Returns NULL if no rebreak is required. Otherwise returns a pointer to the page requiring the rebreak. */ fp_Page * fb_ColumnBreaker::needsRebreak(void) { fl_ContainerLayout * pCL = m_pDocSec->getLastLayout(); fl_BlockLayout * pBL = NULL; if(pCL && (pCL->getContainerType() == FL_CONTAINER_BLOCK)) { pBL = static_cast(pCL); } else if(pCL) { pBL = pCL->getPrevBlockInDocument(); } else { return _getLastValidPage(); } if(pBL) { fp_Line * pLine = static_cast(pBL->getLastContainer()); if(pLine == NULL) { return _getLastValidPage(); } fp_Page * pPage = pLine->getPage(); if(pPage == NULL) { return _getLastValidPage(); } else if(pLine->getY() > m_pDocSec->getActualColumnHeight()) { return pPage; } else { fp_Column * pCol = pPage->getNthColumnLeader(0); if(pCol->getHeight() > m_pDocSec->getActualColumnHeight()) { return pPage; } } } return NULL; } /*! Layout sections on pages \return zero This is the function that controls how sections and thereby columns, blocks, and lines are laid out on the pages. Doing so it refers to the various layout configurations such as orphan/widow controls and break Runs embedded in the text. At the eof the break we check to see that that all the text has been layed out. If it hasn't we rebreak from invalid page forward. FIXME we should try to detect the error in the code rather than rebreak. */ UT_sint32 fb_ColumnBreaker::breakSection() { fp_Page * pStartPage = m_pStartPage; m_pDocSec->setNeedsSectionBreak(false,m_pStartPage); FL_DocLayout * pDL = m_pDocSec->getDocLayout(); m_bStartFromStart = true; UT_sint32 iPage = pDL->findPage(pStartPage); if(iPage < 0) pStartPage = NULL; UT_sint32 iVal = _breakSection(pStartPage); UT_sint32 icnt = 0; pStartPage = needsRebreak(); if(m_pStartPage) { icnt = 0; pStartPage = m_pStartPage; } while(pStartPage && (icnt < 50)) { UT_DEBUGMSG(("Try to get a decent break Column %d times \n",icnt)); iPage = pDL->findPage(pStartPage); if(iPage < 0) { pStartPage = NULL; } else { UT_DEBUGMSG(("Retrying from page %d \n",iPage)); if(icnt >15) { if(pStartPage->getAvailableHeight() <0) { while(pStartPage->countFootnoteContainers()>0) { fp_FootnoteContainer* pFC = pStartPage->getNthFootnoteContainer(0); pStartPage->removeFootnoteContainer(pFC); } } } } iVal = _breakSection(pStartPage); pStartPage = needsRebreak(); if(m_pStartPage) { pStartPage = m_pStartPage; if(icnt > 10) { pStartPage = pStartPage->getPrev(); } } icnt++; } pDL->deleteEmptyColumnsAndPages(); return iVal; } fp_Page * fb_ColumnBreaker::_getLastValidPage(void) { fp_Page * pPage = NULL; fp_Page * pFoundPage = NULL; UT_sint32 i = 0; FL_DocLayout * pDL = m_pDocSec->getDocLayout(); for(i=0; icountPages();i++) { pPage = pDL->getNthPage(i); if(pPage->getOwningSection() == m_pDocSec) { pFoundPage = pPage; } else if(pFoundPage != NULL) break; } return pFoundPage; } //If a table is like this, // ===|===|===|===|=== <-- YBreak. Offset of the Master Table Container. // ===|===|===|===|=== // ===|===|===|===|=== Page One. // ===|===|===|===|=== First broken Table Container. The offset of this container from the master table is 0 always. // ===|===|===|===|=== // ===|===|===|===|=== <-- this is the YBottom // ====================================== Page Break // ===|===|===|===|=== <-- YBreak. Offset of the second broken Table Container from Master Table. // ===|===|===|===|=== // ===|===|===|===|=== // ===|===|===|===|=== Page Two. // ===|===|===|===|=== Second broken Table Container. // ===|===|===|===|=== // ===|===|===|===|=== <-- YBottom. // // Table Container height = YBottom - YBreak. UT_sint32 fb_ColumnBreaker::_breakSection(fp_Page * pStartPage) { m_bReBreak = false; m_pStartPage = NULL; FL_DocLayout * pDocLayout = m_pDocSec->getDocLayout(); fl_ContainerLayout* pFirstLayout = NULL; fp_Container* pOuterContainer = NULL; fp_Column* pCurColumn = NULL; fp_Column* pPrevColumn = NULL; UT_sint32 iCurPage = 0; UT_sint32 iColCons = 0; xxx_UT_DEBUGMSG(("Doing ColumnBreak for section %x at page %x \n",m_pDecSec,m_pStartPage)); // UT_ASSERT(m_pDocSec->needsSectionBreak()); pFirstLayout = m_pDocSec->getFirstLayout(); if (!pFirstLayout) { m_pStartPage = NULL; m_bStartFromStart = false; return 0; } if(pStartPage) { iCurPage = pDocLayout->findPage(pStartPage); if(iCurPage == 0) pStartPage=NULL; } pOuterContainer = pFirstLayout->getFirstContainer(); if(pOuterContainer && pOuterContainer->getContainerType() == FP_CONTAINER_FRAME) { fl_ContainerLayout * pCL = pOuterContainer->getSectionLayout()->getNext(); if(pCL) { pOuterContainer = static_cast(pCL->getFirstContainer()); } } pCurColumn = static_cast(m_pDocSec->getFirstContainer()); xxx_UT_DEBUGMSG(("fb_ColumnBreaker: For DocSec %x first column %x \n",m_pDecSec,pCurColumn)); if(pStartPage && (pStartPage->getNthColumnLeader(0))) { xxx_UT_DEBUGMSG(("Layout from page %x \n",pStartPage)); pCurColumn = pStartPage->getNthColumnLeader(0); pOuterContainer = pCurColumn->getFirstContainer(); if(pOuterContainer && pOuterContainer->getContainerType() == FP_CONTAINER_LINE) { pFirstLayout = static_cast(static_cast(pOuterContainer)->getBlock()); } else if(pOuterContainer && pOuterContainer->getContainerType() == FP_CONTAINER_ENDNOTE) { pFirstLayout = static_cast(pOuterContainer->getSectionLayout()); } else if(pOuterContainer &&pOuterContainer->getContainerType() == FP_CONTAINER_TABLE ) { UT_ASSERT(pOuterContainer->getContainerType() == FP_CONTAINER_TABLE); pFirstLayout = static_cast(pOuterContainer->getSectionLayout()); } else if(pOuterContainer) { UT_ASSERT(pOuterContainer->getContainerType() == FP_CONTAINER_TOC); pFirstLayout = static_cast(pOuterContainer->getSectionLayout()); } } fp_Page * pPrevPage = NULL; // attachment 3627 from bug 9878 has pCurColumn == NULL if (pCurColumn) { pPrevPage = pCurColumn->getPage(); } xxx_UT_DEBUGMSG(("Starting ColumnBreak now pOuterContainer %p \n",pOuterContainer)); while (pCurColumn) { if( m_pStartPage != NULL) { // Abort and start again break; } iColCons = -1; if(pCurColumn && pCurColumn->getPrev()) { fp_Column * pPrevCol = static_cast(pCurColumn->getPrev()); fp_Page * pPrevPrevPage = pPrevCol->getPage(); if((pPrevCol->getHeight() == 0) && !pPrevCol->containsPageBreak() && pOuterContainer) { bool bPageBreak = false; fp_Column * pCol2 = static_cast(pPrevCol->getPrev()); while(pCol2 && (pCol2->getPage() == pPrevPrevPage) && !pCol2->containsPageBreak()) { pCol2 = static_cast(pCol2->getPrev()); } bPageBreak = (pCol2 && pCol2->containsPageBreak()); // Abort and start again from this page if(!bPageBreak) { m_pStartPage = pPrevPrevPage; break; } } if(pPrevCol->getHeight() > m_pDocSec->getActualColumnHeight()) { // Abort and start again from this page m_pStartPage = pPrevPrevPage; break; } } xxx_UT_DEBUGMSG(("New column pOuterContainer %p \n",pOuterContainer)); bool bSaveset = false; if(pOuterContainer) { pDocLayout->setSaveContainerPointer(pOuterContainer); bSaveset = true; } if(pCurColumn->getPage() != pPrevPage) { if(pPrevPage) { // // check and update wrapped text around positioned objects // UT_sint32 iPage = m_pDocSec->getDocLayout()->findPage(pPrevPage); pDocLayout->setFramePageNumbers(iPage); //pOuterContainer->setAllowDelete(false); // // pOuterContainer can be deleted by this call // fp_Container * pNextContainer = pPrevPage->updatePageForWrapping(pCurColumn); //pOuterContainer->setAllowDelete(true); xxx_UT_DEBUGMSG(("Returned container updatePage %x \n",pNextContainer)); if(pNextContainer != NULL) { pOuterContainer = pNextContainer; } } } if((pDocLayout->getRebuiltBlock() == NULL) && bSaveset) { pDocLayout->getSavedContainerPointer()->setAllowDelete(true); } else if(bSaveset) { // // pOuterContainer has been deleted! // if(pDocLayout->getSavedContainerPointer() == pOuterContainer) { UT_DEBUGMSG(("AbiWord Saved from a dangling pointer in fb_ColumnBreak \n")); UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); pOuterContainer = pDocLayout->getRebuiltBlock()->getFirstContainer(); } } pPrevPage = pCurColumn->getPage(); fp_Container* pFirstContainerToKeep = pOuterContainer; xxx_UT_DEBUGMSG(("SEVIOR: first to keep 1 %x \n",pFirstContainerToKeep)); fp_Container* pLastContainerToKeep = NULL; fp_Container* pOffendingContainer = NULL; UT_sint32 iMaxSecCol = m_pDocSec->getActualColumnHeight(); UT_sint32 iMaxColHeight = pCurColumn->getMaxHeight(); bool bEquivColumnBreak = false; xxx_UT_DEBUGMSG(("fb_ColumnBreaker: iMaxSecCol = %d iMaxColHeight = %d \n",iMaxSecCol,iMaxColHeight)); if((iMaxSecCol > 0) && (iMaxSecCol < iMaxColHeight)) { iMaxColHeight = iMaxSecCol; bEquivColumnBreak = true; } UT_sint32 iWorkingColHeight = 0; UT_sint32 iHeaderAdjust = 0; fp_Container* pCurContainer = pFirstContainerToKeep; // Special handling of columns that should be skipped due to // page breaks. If the previous line contains a page break, // skip the present column if it is on the same page. if (pCurContainer) { fp_Container* pPrevContainer = static_cast(pCurContainer->getPrevContainerInSection()); if(pPrevContainer && (pPrevContainer->getContainerType() == FP_CONTAINER_LINE)) { fp_Line * pL = static_cast(pPrevContainer); if (pL && pL->containsForcedPageBreak() && pL->getContainer() && (pCurColumn->getPage() == pL->getContainer()->getPage())) { if (pCurColumn->getNext()) { pCurColumn = static_cast(pCurColumn->getNext()); } else { pCurColumn = static_cast(m_pDocSec->getNewContainer(pCurContainer)); } continue; } } } bool bBreakOnColumnBreak = false; // plam: this var is redundant if the #if 0 remains dead. //bool bDoTableBreak = false; UT_sint32 iTotalContainerSpace = 0; while (pCurContainer) { // pCurContainer->setMyBrokenContainer(NULL); iColCons++; xxx_UT_DEBUGMSG(("curContainer pointer %p type %s \n",iLoop,pCurContainer,pCurContainer->getContainerString())); if(pCurContainer->getDocSectionLayout() != m_pDocSec) { break; } if((pCurContainer->getContainerType() == FP_CONTAINER_FOOTNOTE) || (pCurContainer->getContainerType() == FP_CONTAINER_ANNOTATION) || (pCurContainer->getContainerType() == FP_CONTAINER_FRAME)) { // skip this! We've already taken its height into account or // its height is ignore in the layout (frame). pCurContainer = _getNext(pCurContainer); continue; } if(pCurContainer->isVBreakable()) { // Check if the table or TOC can be added to the column. // Break it if necessary bool bAddToCol = _checkVBreakableContainer(pCurContainer, iMaxColHeight - iWorkingColHeight); if (bAddToCol && pCurContainer->getNext()) { // The object is broken at the column break pLastContainerToKeep = pCurContainer; pOffendingContainer = pCurContainer->getNextContainerInSection(); break; } } UT_sint32 iContainerHeight = pCurContainer->getHeight();; UT_sint32 iContainerMarginAfter = pCurContainer->getMarginAfter(); iTotalContainerSpace = iContainerHeight + iContainerMarginAfter; if (pCurContainer && pCurContainer->getContainerType() == FP_CONTAINER_LINE) { fp_Line* pCurLine = static_cast(pCurContainer); if(pCurLine->isSameYAsPrevious()) { // // No vertical changes to skip this // pCurContainer = _getNext(pCurContainer); continue; } // Add the amount of space occupied by the footnotes and annotations UT_sint32 iTheseFootnotes = 0; UT_sint32 iTheseAnnotations = 0; if (pCurLine->containsFootnoteReference()) { // OK get a vector of the footnote containers in this line. UT_GenericVector vecFootnotes; pCurLine->getFootnoteContainers(&vecFootnotes); // Now loop through all these and add them to the height. UT_sint32 i = 0; for(i = 0; i < vecFootnotes.getItemCount(); i++) { fp_FootnoteContainer * pFC = vecFootnotes.getNthItem(i); if (pFC) { iTheseFootnotes += pFC->getHeight(); } } } if(_displayAnnotations() && pCurLine->containsAnnotations()) { // OK get a vector of the Annotation containers in this line. UT_GenericVector vecAnnotations; pCurLine->getAnnotationContainers(&vecAnnotations); // Now loop through all these and add them to the height. UT_sint32 i = 0; for(i = 0; i < vecAnnotations.getItemCount(); i++) { fp_AnnotationContainer * pAC = vecAnnotations.getNthItem(i); if (pAC) { UT_ASSERT(pAC->getHeight() < iMaxSecCol); iTheseAnnotations += pAC->getHeight(); } } } if (iTheseAnnotations + iTheseFootnotes + pCurLine->getHeight() > iMaxSecCol) { // We are in trouble: the line and all its footnotes and annotations // occupy more than one column UT_ASSERT(UT_SHOULD_NOT_HAPPEN); iTheseFootnotes = iMaxSecCol - pCurLine->getHeight() - 1; iTheseAnnotations = 0; } xxx_UT_DEBUGMSG(("got footnote iTheseFootnotes value: %d\n", iTheseFootnotes)); iWorkingColHeight += iTheseFootnotes + iTheseAnnotations; iHeaderAdjust += iTheseFootnotes + iTheseAnnotations;; } if (pCurContainer && pCurContainer->getContainerType() == FP_CONTAINER_TABLE) { fp_TableContainer * pCurTable = static_cast(pCurContainer); if ((pCurTable->containsFootnoteReference()) || (_displayAnnotations() && pCurTable->containsAnnotations())) { iWorkingColHeight += pCurTable->sumFootnoteHeight(); } } xxx_UT_DEBUGMSG(("WorkingColHeight = %d \n",iWorkingColHeight)); xxx_UT_DEBUGMSG(("iTotalContainerSpace = %d MarginAfter %d \n",iTotalContainerSpace,iContainerMarginAfter)); if ((iWorkingColHeight + iTotalContainerSpace) > iMaxColHeight) { if (iWorkingColHeight + iTotalContainerSpace - iContainerMarginAfter <= iMaxColHeight) { pLastContainerToKeep = pCurContainer; iTotalContainerSpace -= iContainerMarginAfter; while(pCurContainer) { pCurContainer = _getNext(pCurContainer); if (pCurContainer && pCurContainer->getContainerType() == FP_CONTAINER_LINE) { fp_Line* pCurLine = static_cast(pCurContainer); if(pCurLine->isSameYAsPrevious()) pLastContainerToKeep = pCurContainer; else break; } else break; } break; } pOffendingContainer = pCurContainer; /* We have found the offending container (the first one which won't fit in the column) and we now need to decide whether we can break the column just before it. First, check to see if the offending container is a table and if it can be broken to fit in the column. */ xxx_UT_DEBUGMSG(("SEVIOR: iWorkingColHeight %d iTotalContainerSpace %d iMaxColHeight %d pCurContainer %x height %d \n",iWorkingColHeight,iTotalContainerSpace,iMaxColHeight, pCurContainer, iContainerHeight)); if(pOffendingContainer == NULL) { break; } else if (pOffendingContainer->isVBreakable()) { xxx_UT_DEBUGMSG(("fb_ColumnBreak 1 Broken Container num %d \n", (static_cast(pOffendingContainer))->getBrokenNumber())); if(pOffendingContainer && ((pOffendingContainer->getContainerType() == FP_CONTAINER_TABLE) || (pOffendingContainer->getContainerType() == FP_CONTAINER_TOC))) { // // Can't break the table or the TOC; so bump it to next column. // pCurContainer = pOffendingContainer; pLastContainerToKeep = pOffendingContainer->getPrevContainerInSection(); UT_DEBUGMSG(("Can't break table or TOC. pOffendingContainer %p pLastContainerToKeep %p\n", pCurContainer,pLastContainerToKeep)); break; } } if (pOffendingContainer == pFirstContainerToKeep) { // Wow! The very first line in this column won't // fit. Big line. (or maybe a small column) // TODO: what should we do here? For now, we // force it. pLastContainerToKeep = pFirstContainerToKeep; xxx_UT_DEBUGMSG(("SEVIOR: Set lasttokeep 2 %x \n",pLastContainerToKeep)); break; } UT_uint32 iWidows = 0; UT_uint32 iOrphans = 0; bool bIsTableOrTOC = false; fl_BlockLayout* pBlock = NULL; fl_ContainerLayout * pConLayout = NULL; if(pOffendingContainer == NULL) break; if(pOffendingContainer->getContainerType() == FP_CONTAINER_LINE) { pBlock = static_cast(pOffendingContainer)->getBlock(); iWidows = pBlock->getProp_Widows(); iOrphans = pBlock->getProp_Orphans(); pConLayout = static_cast(pBlock); } else if(pOffendingContainer->getContainerType() == FP_CONTAINER_ENDNOTE) { pConLayout = static_cast(pOffendingContainer->getSectionLayout()); pLastContainerToKeep = pOffendingContainer->getPrevContainerInSection(); if(pLastContainerToKeep == NULL) { fl_DocSectionLayout * pDSL = pConLayout->getDocSectionLayout(); UT_ASSERT(pDSL); UT_ASSERT(pDSL->getContainerType() == FL_CONTAINER_DOCSECTION); pLastContainerToKeep = pDSL->getLastContainer(); } UT_ASSERT(pLastContainerToKeep); // // All this deals with widows and orphans. We don't need this for // tables or TOCs // break; } else { bIsTableOrTOC = true; } if(bIsTableOrTOC) { pLastContainerToKeep = pOffendingContainer->getPrevContainerInSection(); xxx_UT_DEBUGMSG(("SEVIOR: Set lasttokeep 3 %x \n",pLastContainerToKeep)); UT_ASSERT(pLastContainerToKeep); // // All this deals with widows and orphans. We don't need this for // tables // break; } UT_uint32 iNumContainersBeforeOffending = 0; UT_uint32 iNumContainersAfterOffending = 0; bool bFoundOffending = false; fp_Container* pFirstContainerInBlock = pConLayout->getFirstContainer(); pCurContainer = pFirstContainerInBlock; while (pCurContainer) { if (bFoundOffending) { iNumContainersAfterOffending++; } else { if (pCurContainer == pOffendingContainer) { iNumContainersAfterOffending = 1; bFoundOffending = true; } else { iNumContainersBeforeOffending++; } } pCurContainer = static_cast(pCurContainer->getNext()); } UT_uint32 iNumContainersInLayout = iNumContainersBeforeOffending+iNumContainersAfterOffending; UT_uint32 iNumLayoutContainersInThisColumn = 0; pCurContainer = static_cast(pOffendingContainer->getPrev()); while (pCurContainer) { iNumLayoutContainersInThisColumn++; if (pCurContainer == pFirstContainerToKeep) break; pCurContainer=static_cast(pCurContainer->getPrev()); } if (pBlock && pBlock->getProp_KeepTogether() && (iNumContainersBeforeOffending == iNumLayoutContainersInThisColumn) && (pBlock->getFirstContainer() != pFirstContainerToKeep)) { /* This block or Table wants to be kept all in the same column. Bump the whole block to the next column. */ /* If the block is simply too big to fit in a single column, then we can spawn an infinite loop by continually punting it to the next one. So, we assume that if the first line in the block is the first line in this column, we just keep it and cope. This will be slightly incorrect in cases where pushing it to the next column would allow the block to try to live in a larger column, thus staying all together. */ pLastContainerToKeep = static_cast(pFirstContainerInBlock->getPrevContainerInSection()); xxx_UT_DEBUGMSG(("SEVIOR: Set lasttokeep 4 %x \n",pLastContainerToKeep)); } else if ((iNumContainersInLayout < (iWidows + iOrphans)) && (iNumContainersBeforeOffending == iNumLayoutContainersInThisColumn) ) { /* There are not enough lines to divide between the two columns while still satisfying both constraints. Bump the whole block to the next column. */ pLastContainerToKeep = static_cast(pFirstContainerInBlock->getPrevContainerInSection()); // // If this happens to be a table better get the LAST of the broken tables // if(pLastContainerToKeep && pLastContainerToKeep->getContainerType() == FP_CONTAINER_TABLE && pFirstContainerToKeep->getContainerType() == FP_CONTAINER_TABLE) { // // Can get into trouble if the pFirstContainer // points to a broken table after // this. Check for this. // for (fp_TableContainer * pTab = static_cast(pLastContainerToKeep); pTab; pTab=static_cast(pTab->getNext())) { if(pTab == pFirstContainerToKeep) { xxx_UT_DEBUGMSG(("SEVIOR: FirstContainer l325 %x \n",pFirstContainerToKeep)); pFirstContainerToKeep = pLastContainerToKeep; break; } } } xxx_UT_DEBUGMSG(("SEVIOR: Set lasttokeep 5 %x \n",pLastContainerToKeep)); } else if ( (iNumContainersBeforeOffending < iOrphans) && (iNumContainersBeforeOffending == iNumLayoutContainersInThisColumn) ) { /* We're leaving too few lines in the current column. Bump the whole block. */ pLastContainerToKeep = static_cast(pFirstContainerInBlock->getPrevContainerInSection()); xxx_UT_DEBUGMSG(("SEVIOR: Set lasttokeep 6 %x \n",pLastContainerToKeep)); } else if ( (iNumContainersAfterOffending < iWidows) && ((iWidows - iNumContainersAfterOffending) < iNumLayoutContainersInThisColumn) ) { /* There aren't going to be enough lines in the next column. Bump just enough. */ UT_uint32 iNumContainersNeeded = (iWidows - iNumContainersAfterOffending); pLastContainerToKeep = static_cast(pOffendingContainer->getPrevContainerInSection()); xxx_UT_DEBUGMSG(("SEVIOR: Set lasttokeep 7 %x \n",pLastContainerToKeep)); for (UT_uint32 iBump = 0; iBump < iNumContainersNeeded; iBump++) { pLastContainerToKeep = static_cast(pLastContainerToKeep->getPrevContainerInSection()); xxx_UT_DEBUGMSG(("SEVIOR: Set lasttokeep 8 %x \n",pLastContainerToKeep)); } } else { pLastContainerToKeep = static_cast(pOffendingContainer->getPrevContainerInSection()); xxx_UT_DEBUGMSG(("SEVIOR: Set lasttokeep 9 %x \n",pLastContainerToKeep)); } break; } else { iWorkingColHeight += iTotalContainerSpace; if(pCurContainer->getContainerType() == FP_CONTAINER_LINE) { fp_Line * pL = static_cast(pCurContainer); if (pL->containsForcedColumnBreak() || pL->containsForcedPageBreak()) { pLastContainerToKeep = pCurContainer; xxx_UT_DEBUGMSG(("SEVIOR: Set lasttokeep 10 %x \n",pLastContainerToKeep)); bBreakOnColumnBreak = (pL->containsForcedColumnBreak()) ; if(iWorkingColHeight >= iMaxColHeight) bBreakOnColumnBreak = false; break; } } } pCurContainer = _getNext(pCurContainer); } // // End of inner while loop here. After this we've found LastContainerToKeep // bEquivColumnBreak = bEquivColumnBreak && ( iMaxColHeight < (iWorkingColHeight + iTotalContainerSpace)); if (pLastContainerToKeep) { while(pLastContainerToKeep && ((pLastContainerToKeep->getContainerType() == FP_CONTAINER_FOOTNOTE ) || (pLastContainerToKeep->getContainerType() == FP_CONTAINER_ANNOTATION))) { pLastContainerToKeep = pLastContainerToKeep->getPrevContainerInSection(); } if(pLastContainerToKeep) { pOuterContainer = _getNext(pLastContainerToKeep); } else { pOuterContainer = NULL; } } else pOuterContainer = NULL; // // OK fill our column with content between pFirstContainerToKeep and pLastContainerToKeep // xxx_UT_DEBUGMSG(("Doing column fill now pCurContainer %x pFirstContainer %x \n",pCurContainer,pFirstContainerToKeep)); pCurContainer = pFirstContainerToKeep; fp_TableContainer * pTab = static_cast(pFirstContainerToKeep); UT_UNUSED(pTab); // only for debug. UT_sint32 conPos = 0; while (pCurContainer) { xxx_UT_DEBUGMSG(("Container %x is in Column %x Type %d numCons %d \n",pCurContainer,pCurColumn,pCurContainer->getContainerType(),pCurColumn->countCons())); if(pCurContainer->getContainerType() == FP_CONTAINER_FOOTNOTE || (pCurContainer->getContainerType() == FP_CONTAINER_ANNOTATION) ) { // // Skip this. It doesn't go in this column at all. // pCurContainer = _getNext(pCurContainer); continue; } if(pCurContainer->getDocSectionLayout() != m_pDocSec) { break; } if (pCurContainer->getContainer() != pCurColumn || (pCurColumn->findCon(pCurContainer) < 0) ) { // // Endnotes don't get placed in columns until here // if(pCurContainer->getContainerType() == FP_CONTAINER_ENDNOTE) { if(pCurContainer->getContainer() == NULL) { pCurColumn->addContainer(pCurContainer); } } // UT_ASSERT(pCurContainer->getContainer()); if(pCurContainer->getContainer() && pCurContainer->getContainer()->findCon(pCurContainer) >= 0) { fp_VerticalContainer *pVert = static_cast(pCurContainer->getContainer()); #if 0 if(pCurContainer->getContainer() != pCurColumn) { pCurColumn->addContainer(pCurContainer); } #endif pVert->removeContainer(pCurContainer,true); } if((pCurContainer->getContainer() != pCurColumn) && (pCurContainer->getDocSectionLayout() == pCurColumn->getDocSectionLayout())) { if(pCurContainer->getContainerType() != FP_CONTAINER_FRAME) { pCurColumn->addContainer(pCurContainer); } else { fp_Page * pPage = pCurContainer->getPage(); fp_Page * pNextPage = pCurColumn->getPage(); if(pNextPage && pPage && (pNextPage != pPage)) { fp_FrameContainer * pFC = reinterpret_cast(pCurContainer); if(( pDocLayout->findPage(pPage) >= 0) && ( pDocLayout->findPage(pNextPage) >= 0)) { if((pPage->findFrameContainer(pFC) >=0) && (pNextPage->findFrameContainer(pFC) < 0)) { pPage->removeFrameContainer(pFC); pNextPage->insertFrameContainer(pFC); } } } } } } // // Now make sure footnotes and annotations are on the same page as the reference. // if(pCurContainer->getContainerType() == FP_CONTAINER_LINE) { fp_Line * pCurLine = static_cast(pCurContainer); xxx_UT_DEBUGMSG(("About to call containerFootnoteReferenced \n")); if(pCurLine->containsFootnoteReference()) { // OK get a vector of the footnote containers in this line. UT_GenericVector vecFootnotes; pCurLine->getFootnoteContainers(&vecFootnotes); // Now loop through all these and check they're on this // page. If not add them. fp_Page * pCurPage = pCurColumn->getPage(); UT_ASSERT(pCurPage); UT_sint32 i =0; for(i=0; i< vecFootnotes.getItemCount();i++) { fp_FootnoteContainer * pFC = vecFootnotes.getNthItem(i); if(pFC != NULL) { fp_Page * myPage = pFC->getPage(); xxx_UT_DEBUGMSG(("Footnote %x is on Page %x \n",pFC,myPage)); if(myPage != pCurPage) { if(myPage == NULL) { pCurPage->insertFootnoteContainer(pFC); } else { myPage->removeFootnoteContainer(pFC); pCurPage->insertFootnoteContainer(pFC); } } } } } if(pCurLine->containsAnnotations() && _displayAnnotations()) { // OK get a vector of the footnote containers in this line. UT_GenericVector vecAnnotations; pCurLine->getAnnotationContainers(&vecAnnotations); // Now loop through all these and check they're on this // page. If not add them. fp_Page * pCurPage = pCurColumn->getPage(); UT_ASSERT(pCurPage); UT_sint32 i =0; for(i=0; i< vecAnnotations.getItemCount();i++) { fp_AnnotationContainer * pAC = vecAnnotations.getNthItem(i); if(pAC != NULL) { fp_Page * myPage = pAC->getPage(); UT_DEBUGMSG(("Annotation %p is on Page %p \n",pAC,myPage)); if(myPage != pCurPage) { if(myPage == NULL) { pCurPage->insertAnnotationContainer(pAC); } else { myPage->removeAnnotationContainer(pAC); pCurPage->insertAnnotationContainer(pAC); } } } } } } // // Do the same for footnotes inside broken tables // if(pCurContainer->getContainerType() == FP_CONTAINER_TABLE) { fp_TableContainer * pCurTable = static_cast(pCurContainer); if(!pCurTable->isThisBroken() && pCurTable->getFirstBrokenTable()) { pCurTable = pCurTable->getFirstBrokenTable(); } if(pCurTable->isThisBroken()) { if(pCurTable->containsFootnoteReference()) { // OK get a vector of the footnote containers in this line. UT_GenericVector vecFootnotes; pCurTable->getFootnoteContainers(&vecFootnotes); // Now loop through all these and check they're on this // page. If not add them. fp_Page * pCurPage = pCurColumn->getPage(); UT_ASSERT(pCurPage); UT_sint32 i =0; for(i=0; i< vecFootnotes.getItemCount();i++) { xxx_UT_DEBUGMSG(("Found reference %d in broken table %x \n",i,pCurTable)); fp_FootnoteContainer * pFC = vecFootnotes.getNthItem(i); fp_Page * myPage = pFC->getPage(); xxx_UT_DEBUGMSG(("Footnote %x is on Page %x \n",pFC,myPage)); if(myPage != pCurPage) { xxx_UT_DEBUGMSG((" Moving anchor from %x to %x \n",myPage,pCurPage)); if(myPage == NULL) { pCurPage->insertFootnoteContainer(pFC); } else { myPage->removeFootnoteContainer(pFC); pCurPage->insertFootnoteContainer(pFC); } } } } if(pCurTable->containsAnnotations() && _displayAnnotations()) { // OK get a vector of the footnote containers in this line. UT_GenericVector vecAnnotations; pCurTable->getAnnotationContainers(&vecAnnotations); // Now loop through all these and check they're on this // page. If not add them. fp_Page * pCurPage = pCurColumn->getPage(); UT_ASSERT(pCurPage); UT_sint32 i =0; for(i=0; i< vecAnnotations.getItemCount();i++) { xxx_UT_DEBUGMSG(("Found reference %d in broken table %x \n",i,pCurTable)); fp_AnnotationContainer * pAC = vecAnnotations.getNthItem(i); fp_Page * myPage = pAC->getPage(); xxx_UT_DEBUGMSG(("Annotation %x is on Page %x \n",pAC,myPage)); if(myPage != pCurPage) { xxx_UT_DEBUGMSG((" Moving anchor from %x to %x \n",myPage,pCurPage)); if(myPage == NULL) { pCurPage->insertAnnotationContainer(pAC); } else { myPage->removeAnnotationContainer(pAC); pCurPage->insertAnnotationContainer(pAC); } } } } } } // // Code to fix order in the column // #if 1 if((pCurColumn->findCon(pCurContainer) >= 0) && (pCurColumn->findCon(pCurContainer) != conPos)) { xxx_UT_DEBUGMSG(("fb_ColumnBreaker:Container out of order. Should be at %d is at %d \n",conPos,pCurColumn->findCon(pCurContainer))); xxx_UT_DEBUGMSG(("fb_ColumnBreak: Fixing this now orig num cons %d \n",pCurColumn->countCons())); static_cast(pCurContainer->getContainer())->removeContainer(pCurContainer); pCurColumn->insertConAt(pCurContainer,conPos); pCurContainer->setContainer(pCurColumn); xxx_UT_DEBUGMSG(("fb_ColumnBreak: Insert at %d now num cons \n",conPos,pCurColumn->countCons())); // UT_ASSERT(UT_SHOULD_NOT_HAPPEN); } #endif conPos++; if (pCurContainer == pLastContainerToKeep) { break; } else { if((pLastContainerToKeep!=NULL) && (_getNext(pCurContainer) == NULL)) { UT_DEBUGMSG(("Non null LastContainerToKeep yet next container is NULL!!!!!!!!!!!! \n")); UT_DEBUGMSG((" CurContainer %p type %d \n",pCurContainer,pCurContainer->getContainerType())); UT_DEBUGMSG((" FirstContainer to keep %p Last container to keep %p \n",pTab,pLastContainerToKeep)); UT_DEBUGMSG(("Try to recover.... \n")); // UT_ASSERT(UT_SHOULD_NOT_HAPPEN); pLastContainerToKeep = NULL; break; } pCurContainer = _getNext(pCurContainer); } } // // end of add container to columns // if (pLastContainerToKeep && pCurColumn->getLastContainer() != pLastContainerToKeep) { if(pLastContainerToKeep->getColumn()!=pCurColumn) { UT_DEBUGMSG(("4111 bug \n")); } UT_ASSERT(pLastContainerToKeep->getColumn()==pCurColumn); fp_Page* pPrevPage2 = pCurColumn->getPage(); fp_Column* pNextColumn = pCurColumn; do { // Make sure there is a next column and that it // falls on the next page if there's a page break. pNextColumn = static_cast(pNextColumn->getNext()); if(bBreakOnColumnBreak || bEquivColumnBreak) { if((pNextColumn != NULL) && (pNextColumn != pCurColumn->getFollower()) && (pNextColumn->getPage() != pCurColumn->getPage())) pNextColumn = NULL; } if (!pNextColumn) { if(bBreakOnColumnBreak || bEquivColumnBreak) pNextColumn = static_cast (m_pDocSec->getNewContainer(_getNext(pLastContainerToKeep))); else pNextColumn = static_cast(m_pDocSec->getNewContainer(NULL)); } } while (pLastContainerToKeep->getContainerType() == FP_CONTAINER_LINE && static_cast(pLastContainerToKeep) ->containsForcedPageBreak() && (pNextColumn->getPage() == pPrevPage2)); if(pCurColumn != NULL && (pCurColumn == pNextColumn)) { pCurColumn->layout(); } // Bump content down the columns while (pCurColumn != NULL && pCurColumn != pNextColumn) { xxx_UT_DEBUGMSG(("Start of bump loop pNextColumn %x \n",pNextColumn)); bool isTOCTABLE = false; if(pOuterContainer && ((pOuterContainer->getContainerType() == FP_CONTAINER_TABLE) || (pOuterContainer->getContainerType() == FP_CONTAINER_TABLE))) isTOCTABLE= true; pCurColumn->bumpContainers(pLastContainerToKeep); pCurColumn->layout(); // // Layout might delete a broken table or TOC. Check for this. // fp_Container * pCon = pCurColumn->getLastContainer(); bool bDoneTabTOC = false; if(pCon && pCon->getContainerType() == FP_CONTAINER_TABLE) { bDoneTabTOC = true; pOuterContainer = _getNext(pCon); } if(pCon && pCon->getContainerType() == FP_CONTAINER_TOC) { xxx_UT_DEBUGMSG(("Last Con was TOC \n")); bDoneTabTOC = true; pOuterContainer = _getNext(pCon); } if(!bDoneTabTOC && isTOCTABLE) { if(pCon) { pOuterContainer = _getNext(pCon); } else { pOuterContainer = NULL; pPrevColumn = pCurColumn; pCurColumn = NULL; pLastContainerToKeep = NULL; break; } } // pCurColumn->validate(); pPrevColumn = pCurColumn; pCurColumn = static_cast(pCurColumn->getNext()); // This is only relevant for the initial column. All // other columns should flush their entire content. pLastContainerToKeep = NULL; xxx_UT_DEBUGMSG(("Last of bump loop pCurColumn %x \n",pCurColumn)); } } else { UT_ASSERT((!pLastContainerToKeep) || (pCurColumn->getLastContainer() == pLastContainerToKeep)); bool bTableTest = false; bool bTOCTest = false; if(pOuterContainer) bTableTest = (pOuterContainer->getContainerType() == FP_CONTAINER_TABLE); if(pOuterContainer) bTOCTest = (pOuterContainer->getContainerType() == FP_CONTAINER_TOC); pCurColumn->layout(); // pCurColumn->layout() might delete a broken table or TOC; fixup. if(bTableTest && pOuterContainer != pCurColumn->getLastContainer()) { pOuterContainer = _getNext(pCurColumn->getLastContainer()); bTableTest = true; } if(bTOCTest && pOuterContainer != pCurColumn->getLastContainer()) { pOuterContainer = _getNext(pCurColumn->getLastContainer()); bTOCTest = true; } // pCurColumn->validate(); pPrevColumn = pCurColumn; pCurColumn = static_cast(pCurColumn->getNext()); if(pCurColumn == NULL && (bTableTest || bTOCTest) && pOuterContainer != NULL) { if(pOuterContainer->isVBreakable()) { pCurColumn = static_cast(m_pDocSec->getNewContainer(NULL)); } else { pCurColumn = static_cast(m_pDocSec->getNewContainer(pOuterContainer)); } } else if(pCurColumn == NULL && pLastContainerToKeep && _getNext(pLastContainerToKeep)) { fp_Container * pCon = _getNext(pLastContainerToKeep); if(pCon->isVBreakable()) { pCurColumn = static_cast(m_pDocSec->getNewContainer(NULL)); } else { pCurColumn = static_cast(m_pDocSec->getNewContainer(pCon)); } } } if(pCurColumn == NULL && pLastContainerToKeep && _getNext(pLastContainerToKeep)) { fp_Container * pCon = _getNext(pLastContainerToKeep); if(pCon->isVBreakable()) { pCurColumn = static_cast(m_pDocSec->getNewContainer(NULL)); } else { pCurColumn = static_cast(m_pDocSec->getNewContainer(pCon)); } } // // check for wrapped objects on last page // if(pCurColumn == NULL) { UT_ASSERT(pOuterContainer == NULL); fp_Page * pPage = pPrevColumn->getPage(); UT_sint32 iPage = m_pDocSec->getDocLayout()->findPage(pPage); m_pDocSec->getDocLayout()->setFramePageNumbers(iPage); fp_Container * pNextContainer = pPage->updatePageForWrapping(pCurColumn); xxx_UT_DEBUGMSG(("Returned container updatePage %x \n",pNextContainer)); if(pNextContainer == NULL) { pCurColumn = NULL; pOuterContainer = NULL; } else { pOuterContainer = pNextContainer; } } if(pCurContainer && (pCurContainer->getDocSectionLayout() != m_pDocSec)) { pCurColumn = NULL; } // // Loop back for next pCurContainer. Finish if pCurColumn == NULL. // xxx_UT_DEBUGMSG(("fb_ColumnBreaker:: Finished this column, doing next now. \n")); } // // End of massive while loop here // if(m_bReBreak) { UT_ASSERT(0); m_pStartPage = NULL; m_bStartFromStart = false; breakSection(); } return 0; // TODO return code } /* Check if a vertical breakable container (a table or a TOC) can fit in the column. The function breaks (or rebreak) the container if necessary. If the container is broken, it is the last element added to a given column. The function returns true if the container can fit in the column and false otherwise. */ bool fb_ColumnBreaker::_checkVBreakableContainer(fp_Container * pContainer, UT_sint32 iAvail) { UT_ASSERT((pContainer->getContainerType() == FP_CONTAINER_TABLE) || (pContainer->getContainerType() == FP_CONTAINER_TOC)); fp_VerticalContainer * pVCon = static_cast(pContainer); UT_sint32 iBreakAt = pVCon->wantVBreakAt(iAvail-1); if (!pVCon->getNext()) { // This is the last segment of the object (possibly the only segment) if (iBreakAt < 0) { return (iBreakAt == -1); } else { pVCon->setLastWantedVBreak(iBreakAt); } } else { if (iBreakAt != pVCon->getLastWantedVBreak()) { pVCon->deleteBrokenAfter(true); pVCon->setLastWantedVBreak(iBreakAt); if (iBreakAt < 0) { return (iBreakAt == -1); } } else { if (iBreakAt < 0) { UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); pVCon->deleteBrokenAfter(true); return (iBreakAt == -1); } return true; } } /* Break container */ UT_DEBUGMSG(("breakTable column: iAvail %d actual break at %d \n",iAvail,iBreakAt)); // // Look to see if the table can be broken and if the break is sufficently large. // If not, the table is moved to the next column // if(iBreakAt < FP_TABLE_MIN_BROKEN_HEIGHT) { return false; } fp_VerticalContainer * pBroke = NULL; UT_ASSERT(iBreakAt <= (iAvail-1)); // // OK we can break this table and keep some of it in this column. The // rest goes into the next column. // // Look to see if this table is broken. // if(!pVCon->getFirstBrokenContainer()) { // // Break it at 0 first. // xxx_UT_DEBUGMSG(("SEVIOR: Breaking Master iBreakAt %d yloc = %d \n",iBreakAt,pTab->getY())); pVCon->VBreakAt(0); pBroke = static_cast(pVCon->getFirstBrokenContainer()); if (!pBroke) { UT_ASSERT(UT_SHOULD_NOT_HAPPEN); pBroke = pVCon; } } else { pBroke = pVCon; } // // When we break the table, the bit broken will be placed after the current // table in the column. This then becomes the offending container and will // be bumped into the next column. Pretty cool eh ? :-) // fp_ContainerObject * pNewCon = pBroke->VBreakAt(iBreakAt); UT_ASSERT(pBroke->getHeight() > 0); if(!pNewCon) { return false; } UT_ASSERT(pNewCon->getHeight() > 0); xxx_UT_DEBUGMSG(("SEVIOR: Created broken object %p height %d \n",pNewCon,pNewCon->getHeight())); return true; } /* get the container that follows pCon in the section. Returns NULL if pCon is the last container of the section. */ fp_Container * fb_ColumnBreaker::_getNext(fp_Container * pCon) { UT_return_val_if_fail(pCon,NULL); fp_Container * pNext = NULL; if(pCon->getContainerType() != FP_CONTAINER_ENDNOTE) { pNext = pCon->getNextContainerInSection(); if(pNext) { UT_return_val_if_fail(pNext->getDocSectionLayout() == m_pDocSec,NULL); } if(pNext != NULL) { xxx_UT_DEBUGMSG(("_getNext: 1 Returning %p \n",pNext)); return pNext; } xxx_UT_DEBUGMSG(("_getNext: Returning First endnote \n")); pNext = m_pDocSec->getFirstEndnoteContainer(); xxx_UT_DEBUGMSG(("_getNext: Returning endnote pNext %x \n",pNext)); #if DEBUG if(pNext) { UT_ASSERT(pNext->getContainerType() == FP_CONTAINER_ENDNOTE); } #endif } else { xxx_UT_DEBUGMSG(("_getNext: Returning next endnote \n")); pNext = static_cast(pCon->getNext()); xxx_UT_DEBUGMSG(("_getNext: Returning endnote pNext %x \n",pNext)); #if DEBUG if(pNext) { UT_ASSERT(pNext->getContainerType() == FP_CONTAINER_ENDNOTE); } #endif } #if DEBUG if(pNext) { UT_ASSERT(pNext->getContainerType() != FP_CONTAINER_FRAME); } #endif xxx_UT_DEBUGMSG(("_getNext: 2 Returning %p \n",pNext)); return pNext; }