/* 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., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ // deleteStrux-related functions for class pt_PieceTable. #include "ut_types.h" #include "ut_misc.h" #include "ut_assert.h" #include "ut_debugmsg.h" #include "ut_growbuf.h" #include "pt_PieceTable.h" #include "pf_Frag.h" #include "pf_Frag_Strux.h" #include "pf_Frag_Strux_Block.h" #include "pf_Frag_Strux_Section.h" #include "pf_Frag_Text.h" #include "pf_Fragments.h" #include "px_ChangeRecord.h" #include "px_CR_Span.h" #include "px_CR_SpanChange.h" #include "px_CR_Strux.h" /****************************************************************/ /****************************************************************/ bool pt_PieceTable::_unlinkStrux(pf_Frag_Strux * pfs, pf_Frag ** ppfEnd, UT_uint32 * pfragOffsetEnd) { switch (pfs->getStruxType()) { case PTX_Section: case PTX_SectionHdrFtr: case PTX_SectionEndnote: return _unlinkStrux_Section(pfs,ppfEnd,pfragOffsetEnd); case PTX_Block: return _unlinkStrux_Block(pfs,ppfEnd,pfragOffsetEnd); default: UT_ASSERT(UT_SHOULD_NOT_HAPPEN); return false; } } bool pt_PieceTable::_unlinkStrux_Block(pf_Frag_Strux * pfs, pf_Frag ** ppfEnd, UT_uint32 * pfragOffsetEnd) { UT_ASSERT(pfs->getStruxType()==PTX_Block); // unlink this Block strux from the document. // the caller is responsible for deleting pfs. if (ppfEnd) *ppfEnd = pfs->getNext(); if (pfragOffsetEnd) *pfragOffsetEnd = 0; // find the previous strux (either a paragraph or something else). pf_Frag_Strux * pfsPrev = NULL; pf_Frag * pf = pfs->getPrev(); while (pf && !pfsPrev) { if (pf->getType() == pf_Frag::PFT_Strux) pfsPrev = static_cast (pf); pf = pf->getPrev(); } UT_ASSERT(pfsPrev); // we have a block that's not in a section ?? // // Code to prevent a crash. But this should not happen and if it does not everything will // be deleted - Sevior. // if(pfsPrev == NULL) { _unlinkFrag(pfs,ppfEnd,pfragOffsetEnd); return false; } switch (pfsPrev->getStruxType()) { case PTX_Block: // if there is a paragraph before us, we can delete this // paragraph knowing that our content will be assimilated // in to the previous one. _unlinkFrag(pfs,ppfEnd,pfragOffsetEnd); return true; case PTX_SectionEndnote: case PTX_Section: // we are the first paragraph in this section. if we have // content, we cannot be deleted, since there is no one to // inherit our content. if (_struxHasContent(pfs)) { // TODO decide if this should assert or just fail... UT_DEBUGMSG(("Cannot delete first paragraph with content.\n")); UT_ASSERT(0); return false; } case PTX_SectionHdrFtr: // we are the first paragraph in this section. if we have // content, we cannot be deleted, since there is no one to // inherit our content. if (_struxHasContent(pfs)) { // TODO decide if this should assert or just fail... UT_DEBUGMSG(("Cannot delete first paragraph with content.\n")); UT_ASSERT(0); return false; } // no content in this paragraph. _unlinkFrag(pfs,ppfEnd,pfragOffsetEnd); return true; default: UT_ASSERT(0); return false; } } bool pt_PieceTable::_unlinkStrux_Section(pf_Frag_Strux * pfs, pf_Frag ** ppfEnd, UT_uint32 * pfragOffsetEnd) { UT_ASSERT(pfs->getStruxType()==PTX_Section || pfs->getStruxType()==PTX_SectionHdrFtr || pfs->getStruxType()==PTX_SectionEndnote); // unlink this Section strux from the document. // the caller is responsible for deleting pfs. if (ppfEnd) *ppfEnd = pfs->getNext(); if (pfragOffsetEnd) *pfragOffsetEnd = 0; // find the previous strux (either a paragraph or something else). pf_Frag_Strux * pfsPrev = NULL; pf_Frag * pf = pfs->getPrev(); while (pf && !pfsPrev) { if (pf->getType() == pf_Frag::PFT_Strux) pfsPrev = static_cast (pf); pf = pf->getPrev(); } if (!pfsPrev) { // first section in the document cannot be deleted. // TODO decide if this should assesrt or just file... UT_DEBUGMSG(("Cannot delete first section in document.\n")); UT_ASSERT(0); return false; } switch (pfsPrev->getStruxType()) { case PTX_Block: // if there is a paragraph before us, we can delete this // section knowing that our paragraphs will be assimilated // in to the previous section (that is, the container of // this block). _unlinkFrag(pfs,ppfEnd,pfragOffsetEnd); return true; case PTX_Section: // there are no blocks (paragraphs) between this section // and the previous section. this is not possible. // TODO decide if this should assert or just fail... UT_DEBUGMSG(("No blocks between sections ??\n")); UT_ASSERT(0); return false; case PTX_SectionHdrFtr: // there are no blocks (paragraphs) between this section // and the previous section. this is not possible. // TODO decide if this should assert or just fail... // // Actually this is OK if it's a hdrFtr that has not been // "realized" yet. Like an even hdrftr that has been defined // but no even pages exist yet. UT_DEBUGMSG(("No blocks between sections ??\n")); // _unlinkFrag(pfs,ppfEnd,pfragOffsetEnd); UT_ASSERT(0); return false; default: UT_ASSERT(0); return false; } } bool pt_PieceTable::_deleteStruxWithNotify(PT_DocPosition dpos, pf_Frag_Strux * pfs, pf_Frag ** ppfEnd, UT_uint32 * pfragOffsetEnd) { PX_ChangeRecord_Strux * pcrs = new PX_ChangeRecord_Strux(PX_ChangeRecord::PXT_DeleteStrux, dpos, pfs->getIndexAP(), pfs->getStruxType()); UT_ASSERT(pcrs); if (!_unlinkStrux(pfs,ppfEnd,pfragOffsetEnd)) return false; // add record to history. we do not attempt to coalesce these. m_history.addChangeRecord(pcrs); m_pDocument->notifyListeners(pfs,pcrs); delete pfs; return true; } bool pt_PieceTable::_deleteStrux_norec(PT_DocPosition dpos, pf_Frag_Strux * pfs, pf_Frag ** ppfEnd, UT_uint32 * pfragOffsetEnd) { PX_ChangeRecord_Strux * pcrs = new PX_ChangeRecord_Strux(PX_ChangeRecord::PXT_DeleteStrux, dpos, pfs->getIndexAP(), pfs->getStruxType()); UT_ASSERT(pcrs); if (!_unlinkStrux(pfs,ppfEnd,pfragOffsetEnd)) return false; // No history for field updates.. // m_history.addChangeRecord(pcrs); m_pDocument->notifyListeners(pfs,pcrs); delete pfs; return true; } /*! * This method scans the piecetAble from the section Frag_strux given looking * for any Header/Footers that belong to the strux. If it finds them, they * are deleted with notifications. \params pf_Frag_Strux_Section pfStruxSec the Section strux that might have headers * or footers belonging to it. * These must be deleted with notification otherwise they won't be recreated on * an undo */ bool pt_PieceTable::_deleteHdrFtrsFromSectionStruxIfPresent(pf_Frag_Strux_Section * pfStruxSec) { // // Get the index to the Attributes/properties of the section strux to see if // if there is a header defined for this strux. // // FIXME: Handle all the new header/footer types. PT_AttrPropIndex indexAP = pfStruxSec->getIndexAP(); const PP_AttrProp * pAP = NULL; getAttrProp(indexAP, &pAP); UT_Vector vecHdrFtr; UT_String HeaderV,HeaderEvenV,HeaderLastV,HeaderFirstV; UT_String FooterV,FooterEvenV,FooterLastV,FooterFirstV; vecHdrFtr.clear(); const XML_Char * szHeaderV = NULL; bool bres = pAP->getAttribute("header",szHeaderV); if(szHeaderV && *szHeaderV && (UT_strcmp(szHeaderV,"0") != 0)) { HeaderV = szHeaderV; vecHdrFtr.addItem((void *) HeaderV.c_str()); } szHeaderV = NULL; bres = pAP->getAttribute("header-even",szHeaderV); if(szHeaderV && *szHeaderV && (UT_strcmp(szHeaderV,"0") != 0)) { HeaderEvenV = szHeaderV; vecHdrFtr.addItem((void *) HeaderEvenV.c_str()); } szHeaderV = NULL; bres = pAP->getAttribute("header-last",szHeaderV); if(szHeaderV && *szHeaderV && (UT_strcmp(szHeaderV,"0") != 0)) { HeaderLastV = szHeaderV; vecHdrFtr.addItem((void *) HeaderLastV.c_str()); } szHeaderV = NULL; bres = pAP->getAttribute("header-first",szHeaderV); if(szHeaderV && *szHeaderV && (UT_strcmp(szHeaderV,"0") != 0)) { HeaderFirstV = szHeaderV; vecHdrFtr.addItem((void *) HeaderFirstV.c_str()); } szHeaderV = NULL; bres = pAP->getAttribute("footer",szHeaderV); if(szHeaderV && *szHeaderV && (UT_strcmp(szHeaderV,"0") != 0)) { FooterV = szHeaderV; vecHdrFtr.addItem((void *) FooterV.c_str()); } szHeaderV = NULL; bres = pAP->getAttribute("footer-even",szHeaderV); if(szHeaderV && *szHeaderV && (UT_strcmp(szHeaderV,"0") != 0)) { FooterEvenV = szHeaderV; vecHdrFtr.addItem((void *) FooterEvenV.c_str()); } szHeaderV = NULL; bres = pAP->getAttribute("footer-last",szHeaderV); if(szHeaderV && *szHeaderV && (UT_strcmp(szHeaderV,"0") != 0)) { FooterLastV = szHeaderV; vecHdrFtr.addItem((void *) FooterLastV.c_str()); } szHeaderV = NULL; bres = pAP->getAttribute("footer-first",szHeaderV); if(szHeaderV && *szHeaderV && (UT_strcmp(szHeaderV,"0") != 0)) { FooterFirstV = szHeaderV; vecHdrFtr.addItem((void *) FooterFirstV.c_str()); } UT_sint32 countHdrFtr = (UT_sint32) vecHdrFtr.getItemCount(); UT_DEBUGMSG(("SEVIOR: Deleting HdrFtrs from Document, num Header/Footers %d\n",countHdrFtr)); if(0 == countHdrFtr) { return true; } // // This section has a header or footer attribute. Scan the piecetable to see // if there is a header strux somewhere with an ID that matches our section. // pf_Frag * curFrag = NULL; getFragments().cleanFrags(); // clean up to be safe... // // Do this loop for all and headers and footers. // UT_sint32 i = 0; for(i=0; i< countHdrFtr; i++) { curFrag = static_cast(pfStruxSec); bool bFoundIt = false; pf_Frag_Strux * curStrux = NULL; while(curFrag != getFragments().getLast() && !bFoundIt) { if(curFrag->getType() == pf_Frag::PFT_Strux) { curStrux = static_cast(curFrag); if(curStrux->getStruxType() == PTX_SectionHdrFtr) { // // OK we've got a candidate // PT_AttrPropIndex indexAPHdr = curStrux->getIndexAP(); const PP_AttrProp * pAPHdr = NULL; getAttrProp(indexAPHdr, &pAPHdr); const XML_Char * szID = NULL; bres = pAPHdr->getAttribute("id",szID); UT_DEBUGMSG(("SEVIOR: Found candidate id = %s \n",szID)); if(bres && (szID != NULL)) { // // Look for a match. // szHeaderV = (const char *) vecHdrFtr.getNthItem(i); if(szHeaderV != NULL && strcmp(szHeaderV,szID) == 0) { bFoundIt = true; } } } } curFrag = curFrag->getNext(); } if(bFoundIt) { // // This Header belongs to our section. It must be deleted. // _deleteHdrFtrStruxWithNotify(curStrux); // // Clean up after deleting the text in the Header. // getFragments().cleanFrags(); } } return true; } /*! * This method deletes the Header/Footer from the pieceTable in the order that * will allow an undo to recreate it. */ void pt_PieceTable::_deleteHdrFtrStruxWithNotify( pf_Frag_Strux * pfFragStruxHdrFtr) { // // First we need the document position of the header/footer strux. // UT_DEBUGMSG(("SEVIOR: Deleting hdrftr \n")); const pf_Frag * pfFrag = NULL; pfFrag = static_cast(pfFragStruxHdrFtr); PT_DocPosition HdrFtrPos = getFragPosition(pfFrag); UT_Vector vecFragStrux; UT_DEBUGMSG(("SEVIOR: Deleting hdrftr Strux Pos = %d \n",HdrFtrPos)); // // Now find the first Non-strux frag within this hdrftr // bool bStop = false; while((pfFrag->getType() == pf_Frag::PFT_Strux) && (pfFrag != getFragments().getLast()) && !bStop) { const pf_Frag_Strux * pfs = static_cast(pfFrag); if(pfs != pfFragStruxHdrFtr && pfs->getStruxType() != PTX_Block) { bStop = true; } else { vecFragStrux.addItem((void *) pfFrag); pfFrag = pfFrag->getNext(); } } PT_DocPosition TextStartPos = getFragPosition(pfFrag); UT_DEBUGMSG(("SEVIOR: Deleting hdrftr Text Start Pos = %d \n",TextStartPos)); // // Now find the end of the text in the header/footer // bool foundEnd = false; if(!bStop) { while(!foundEnd) { foundEnd = pfFrag == getFragments().getLast(); if(!foundEnd && pfFrag->getType() == pf_Frag::PFT_Strux) { const pf_Frag_Strux * pfFragStrux = static_cast(pfFrag); foundEnd = pfFragStrux->getStruxType() != PTX_Block; } if(!foundEnd) { pfFrag = pfFrag->getNext(); } } PT_DocPosition TextEndPos = getFragPosition(pfFrag); UT_DEBUGMSG(("SEVIOR: Deleting hdrftr Text End Pos = %d \n",TextEndPos)); // // OK delete the text // if(TextEndPos > TextStartPos) { deleteSpan(TextStartPos,TextEndPos,NULL,true); } } // // Now delete the struxes at the start. // // I assume there are blocks then sections. First delete all the blocks, then the // section strux. // UT_uint32 count = vecFragStrux.getItemCount(); UT_ASSERT(count > 1); UT_uint32 i=0; bool bres = false; for(i=1; igetPos(),pfs,NULL,NULL); UT_ASSERT(bres); } bres = _deleteStruxWithNotify(pfFragStruxHdrFtr->getPos(),pfFragStruxHdrFtr,NULL,NULL); UT_ASSERT(bres); // deleteSpan(HdrFtrPos,TextStartPos,NULL,true); }