/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include OUString SwHistoryHint::GetDescription() const { return OUString(); } SwHistorySetFormat::SwHistorySetFormat( const SfxPoolItem* pFormatHt, sal_uLong nNd ) : SwHistoryHint( HSTRY_SETFMTHNT ) , m_pAttr( pFormatHt->Clone() ) , m_nNodeIndex( nNd ) { switch ( m_pAttr->Which() ) { case RES_PAGEDESC: static_cast(*m_pAttr).ChgDefinedIn( nullptr ); break; case RES_PARATR_DROP: static_cast(*m_pAttr).ChgDefinedIn( nullptr ); break; case RES_BOXATR_FORMULA: { // save formulas always in plain text SwTableBoxFormula& rNew = static_cast(*m_pAttr); if ( rNew.IsIntrnlName() ) { const SwTableBoxFormula& rOld = *static_cast(pFormatHt); const SwNode* pNd = rOld.GetNodeOfFormula(); if ( pNd ) { const SwTableNode* pTableNode = pNd->FindTableNode(); if (pTableNode) { SwTableFormulaUpdate aMsgHint( &pTableNode->GetTable() ); aMsgHint.m_eFlags = TBL_BOXNAME; rNew.ChgDefinedIn( rOld.GetDefinedIn() ); rNew.ChangeState( &aMsgHint ); } } } rNew.ChgDefinedIn( nullptr ); } break; } } OUString SwHistorySetFormat::GetDescription() const { OUString aResult; switch (m_pAttr->Which()) { case RES_BREAK: switch (static_cast(*m_pAttr).GetBreak()) { case SvxBreak::PageBefore: case SvxBreak::PageAfter: case SvxBreak::PageBoth: aResult = SwResId(STR_UNDO_PAGEBREAKS); break; case SvxBreak::ColumnBefore: case SvxBreak::ColumnAfter: case SvxBreak::ColumnBoth: aResult = SwResId(STR_UNDO_COLBRKS); break; default: break; } break; default: break; } return aResult; } void SwHistorySetFormat::SetInDoc( SwDoc* pDoc, bool bTmpSet ) { SwNode * pNode = pDoc->GetNodes()[ m_nNodeIndex ]; if ( pNode->IsContentNode() ) { static_cast(pNode)->SetAttr( *m_pAttr ); } else if ( pNode->IsTableNode() ) { static_cast(pNode)->GetTable().GetFrameFormat()->SetFormatAttr( *m_pAttr ); } else if ( pNode->IsStartNode() && (SwTableBoxStartNode == static_cast(pNode)->GetStartNodeType()) ) { SwTableNode* pTNd = pNode->FindTableNode(); if ( pTNd ) { SwTableBox* pBox = pTNd->GetTable().GetTableBox( m_nNodeIndex ); if (pBox) { pBox->ClaimFrameFormat()->SetFormatAttr( *m_pAttr ); } } } if ( !bTmpSet ) { m_pAttr.reset(); } } SwHistorySetFormat::~SwHistorySetFormat() { } SwHistoryResetFormat::SwHistoryResetFormat(const SfxPoolItem* pFormatHt, sal_uLong nNodeIdx) : SwHistoryHint( HSTRY_RESETFMTHNT ) , m_nNodeIndex( nNodeIdx ) , m_nWhich( pFormatHt->Which() ) { } void SwHistoryResetFormat::SetInDoc( SwDoc* pDoc, bool ) { SwNode * pNode = pDoc->GetNodes()[ m_nNodeIndex ]; if ( pNode->IsContentNode() ) { static_cast(pNode)->ResetAttr( m_nWhich ); } else if ( pNode->IsTableNode() ) { static_cast(pNode)->GetTable().GetFrameFormat()-> ResetFormatAttr( m_nWhich ); } } SwHistorySetText::SwHistorySetText( SwTextAttr* pTextHt, sal_uLong nNodePos ) : SwHistoryHint( HSTRY_SETTXTHNT ) , m_nNodeIndex( nNodePos ) , m_nStart( pTextHt->GetStart() ) , m_nEnd( pTextHt->GetAnyEnd() ) , m_bFormatIgnoreStart(pTextHt->IsFormatIgnoreStart()) , m_bFormatIgnoreEnd (pTextHt->IsFormatIgnoreEnd ()) { // Caution: the following attributes generate no format attributes: // - NoLineBreak, NoHyphen, Inserted, Deleted // These cases must be handled separately !!! // a little bit complicated but works: first assign a copy of the // default value and afterwards the values from text attribute if ( RES_TXTATR_CHARFMT == pTextHt->Which() ) { m_pAttr.reset( new SwFormatCharFormat( pTextHt->GetCharFormat().GetCharFormat() ) ); } else { m_pAttr.reset( pTextHt->GetAttr().Clone() ); } } SwHistorySetText::~SwHistorySetText() { } void SwHistorySetText::SetInDoc( SwDoc* pDoc, bool ) { if (!m_pAttr) return; if ( RES_TXTATR_CHARFMT == m_pAttr->Which() ) { // ask the Doc if the CharFormat still exists if (!pDoc->GetCharFormats()->IsAlive(static_cast(*m_pAttr).GetCharFormat())) return; // do not set, format does not exist } SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode(); OSL_ENSURE( pTextNd, "SwHistorySetText::SetInDoc: not a TextNode" ); if ( pTextNd ) { SwTextAttr *const pAttr = pTextNd->InsertItem(*m_pAttr, m_nStart, m_nEnd, SetAttrMode::NOTXTATRCHR | SetAttrMode::NOHINTADJUST ); // shouldn't be possible to hit any error/merging path from here assert(pAttr); if (m_bFormatIgnoreStart) { pAttr->SetFormatIgnoreStart(true); } if (m_bFormatIgnoreEnd) { pAttr->SetFormatIgnoreEnd(true); } } } SwHistorySetTextField::SwHistorySetTextField( const SwTextField* pTextField, sal_uLong nNodePos ) : SwHistoryHint( HSTRY_SETTXTFLDHNT ) , m_pField( new SwFormatField( *pTextField->GetFormatField().GetField() ) ) { // only copy if not Sys-FieldType SwDoc* pDoc = pTextField->GetTextNode().GetDoc(); m_nFieldWhich = m_pField->GetField()->GetTyp()->Which(); if (m_nFieldWhich == SwFieldIds::Database || m_nFieldWhich == SwFieldIds::User || m_nFieldWhich == SwFieldIds::SetExp || m_nFieldWhich == SwFieldIds::Dde || !pDoc->getIDocumentFieldsAccess().GetSysFieldType( m_nFieldWhich )) { m_pFieldType = m_pField->GetField()->GetTyp()->Copy(); m_pField->GetField()->ChgTyp( m_pFieldType.get() ); // change field type } m_nNodeIndex = nNodePos; m_nPos = pTextField->GetStart(); } OUString SwHistorySetTextField::GetDescription() const { return m_pField->GetField()->GetDescription(); } SwHistorySetTextField::~SwHistorySetTextField() { } void SwHistorySetTextField::SetInDoc( SwDoc* pDoc, bool ) { if (!m_pField) return; SwFieldType* pNewFieldType = m_pFieldType.get(); if ( !pNewFieldType ) { pNewFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType( m_nFieldWhich ); } else { // register type with the document pNewFieldType = pDoc->getIDocumentFieldsAccess().InsertFieldType( *m_pFieldType ); } m_pField->GetField()->ChgTyp( pNewFieldType ); // change field type SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode(); OSL_ENSURE( pTextNd, "SwHistorySetTextField: no TextNode" ); if ( pTextNd ) { pTextNd->InsertItem( *m_pField, m_nPos, m_nPos, SetAttrMode::NOTXTATRCHR ); } } SwHistorySetRefMark::SwHistorySetRefMark( const SwTextRefMark* pTextHt, sal_uLong nNodePos ) : SwHistoryHint( HSTRY_SETREFMARKHNT ) , m_RefName( pTextHt->GetRefMark().GetRefName() ) , m_nNodeIndex( nNodePos ) , m_nStart( pTextHt->GetStart() ) , m_nEnd( pTextHt->GetAnyEnd() ) { } void SwHistorySetRefMark::SetInDoc( SwDoc* pDoc, bool ) { SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode(); OSL_ENSURE( pTextNd, "SwHistorySetRefMark: no TextNode" ); if ( !pTextNd ) return; SwFormatRefMark aRefMark( m_RefName ); // if a reference mark without an end already exists here: must not insert! if ( m_nStart != m_nEnd || !pTextNd->GetTextAttrForCharAt( m_nStart, RES_TXTATR_REFMARK ) ) { pTextNd->InsertItem( aRefMark, m_nStart, m_nEnd, SetAttrMode::NOTXTATRCHR ); } } SwHistorySetTOXMark::SwHistorySetTOXMark( const SwTextTOXMark* pTextHt, sal_uLong nNodePos ) : SwHistoryHint( HSTRY_SETTOXMARKHNT ) , m_TOXMark( pTextHt->GetTOXMark() ) , m_TOXName( m_TOXMark.GetTOXType()->GetTypeName() ) , m_eTOXTypes( m_TOXMark.GetTOXType()->GetType() ) , m_nNodeIndex( nNodePos ) , m_nStart( pTextHt->GetStart() ) , m_nEnd( pTextHt->GetAnyEnd() ) { m_TOXMark.EndListeningAll(); } SwTOXType* SwHistorySetTOXMark::GetSwTOXType(SwDoc& rDoc, TOXTypes eTOXTypes, const OUString& rTOXName) { // search for respective TOX type const sal_uInt16 nCnt = rDoc.GetTOXTypeCount(eTOXTypes); SwTOXType* pToxType = nullptr; for ( sal_uInt16 n = 0; n < nCnt; ++n ) { pToxType = const_cast(rDoc.GetTOXType(eTOXTypes, n)); if (pToxType->GetTypeName() == rTOXName) break; pToxType = nullptr; } if ( !pToxType ) // TOX type not found, create new { pToxType = const_cast( rDoc.InsertTOXType(SwTOXType(rDoc, eTOXTypes, rTOXName))); } return pToxType; } void SwHistorySetTOXMark::SetInDoc( SwDoc* pDoc, bool ) { SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode(); OSL_ENSURE( pTextNd, "SwHistorySetTOXMark: no TextNode" ); if ( !pTextNd ) return; SwTOXType* pToxType = GetSwTOXType(*pDoc, m_eTOXTypes, m_TOXName); SwTOXMark aNew( m_TOXMark ); aNew.RegisterToTOXType( *pToxType ); pTextNd->InsertItem( aNew, m_nStart, m_nEnd, SetAttrMode::NOTXTATRCHR ); } bool SwHistorySetTOXMark::IsEqual( const SwTOXMark& rCmp ) const { return m_TOXName == rCmp.GetTOXType()->GetTypeName() && m_eTOXTypes == rCmp.GetTOXType()->GetType() && m_TOXMark.GetAlternativeText() == rCmp.GetAlternativeText() && ( (TOX_INDEX == m_eTOXTypes) ? ( m_TOXMark.GetPrimaryKey() == rCmp.GetPrimaryKey() && m_TOXMark.GetSecondaryKey() == rCmp.GetSecondaryKey() ) : m_TOXMark.GetLevel() == rCmp.GetLevel() ); } SwHistoryResetText::SwHistoryResetText( sal_uInt16 nWhich, sal_Int32 nAttrStart, sal_Int32 nAttrEnd, sal_uLong nNodePos ) : SwHistoryHint( HSTRY_RESETTXTHNT ) , m_nNodeIndex( nNodePos ), m_nStart( nAttrStart ), m_nEnd( nAttrEnd ) , m_nAttr( nWhich ) { } void SwHistoryResetText::SetInDoc( SwDoc* pDoc, bool ) { SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode(); OSL_ENSURE( pTextNd, "SwHistoryResetText: no TextNode" ); if ( pTextNd ) { pTextNd->DeleteAttributes( m_nAttr, m_nStart, m_nEnd ); } } SwHistorySetFootnote::SwHistorySetFootnote( SwTextFootnote* pTextFootnote, sal_uLong nNodePos ) : SwHistoryHint( HSTRY_SETFTNHNT ) , m_pUndo( new SwUndoSaveSection ) , m_FootnoteNumber( pTextFootnote->GetFootnote().GetNumStr() ) , m_nNodeIndex( nNodePos ) , m_nStart( pTextFootnote->GetStart() ) , m_bEndNote( pTextFootnote->GetFootnote().IsEndNote() ) { OSL_ENSURE( pTextFootnote->GetStartNode(), "SwHistorySetFootnote: Footnote without Section" ); // keep the old NodePos (because who knows what later will be saved/deleted // in SaveSection) SwDoc* pDoc = const_cast(pTextFootnote->GetTextNode().GetDoc()); SwNode* pSaveNd = pDoc->GetNodes()[ m_nNodeIndex ]; // keep pointer to StartNode of FootnoteSection and reset its attribute for now // (as a result, its/all Frames will be deleted automatically) SwNodeIndex aSttIdx( *pTextFootnote->GetStartNode() ); pTextFootnote->SetStartNode( nullptr, false ); m_pUndo->SaveSection( aSttIdx ); m_nNodeIndex = pSaveNd->GetIndex(); } SwHistorySetFootnote::SwHistorySetFootnote( const SwTextFootnote &rTextFootnote ) : SwHistoryHint( HSTRY_SETFTNHNT ) , m_FootnoteNumber( rTextFootnote.GetFootnote().GetNumStr() ) , m_nNodeIndex( SwTextFootnote_GetIndex( (&rTextFootnote) ) ) , m_nStart( rTextFootnote.GetStart() ) , m_bEndNote( rTextFootnote.GetFootnote().IsEndNote() ) { OSL_ENSURE( rTextFootnote.GetStartNode(), "SwHistorySetFootnote: Footnote without Section" ); } OUString SwHistorySetFootnote::GetDescription() const { return SwResId(STR_FOOTNOTE); } SwHistorySetFootnote::~SwHistorySetFootnote() { } void SwHistorySetFootnote::SetInDoc( SwDoc* pDoc, bool ) { SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode(); OSL_ENSURE( pTextNd, "SwHistorySetFootnote: no TextNode" ); if ( !pTextNd ) return; if (m_pUndo) { // set the footnote in the TextNode SwFormatFootnote aTemp( m_bEndNote ); SwFormatFootnote& rNew = const_cast( pDoc->GetAttrPool().Put(aTemp) ); if ( !m_FootnoteNumber.isEmpty() ) { rNew.SetNumStr( m_FootnoteNumber ); } SwTextFootnote* pTextFootnote = new SwTextFootnote( rNew, m_nStart ); // create the section of the Footnote SwNodeIndex aIdx( *pTextNd ); m_pUndo->RestoreSection( pDoc, &aIdx, SwFootnoteStartNode ); pTextFootnote->SetStartNode( &aIdx ); if ( m_pUndo->GetHistory() ) { // create frames only now m_pUndo->GetHistory()->Rollback( pDoc ); } pTextNd->InsertHint( pTextFootnote ); } else { SwTextFootnote * const pFootnote = static_cast( pTextNd->GetTextAttrForCharAt( m_nStart )); assert(pFootnote); SwFormatFootnote &rFootnote = const_cast(pFootnote->GetFootnote()); rFootnote.SetNumStr( m_FootnoteNumber ); if ( rFootnote.IsEndNote() != m_bEndNote ) { rFootnote.SetEndNote( m_bEndNote ); pFootnote->CheckCondColl(); } } } SwHistoryChangeFormatColl::SwHistoryChangeFormatColl( SwFormatColl* pFormatColl, sal_uLong nNd, SwNodeType nNodeWhich ) : SwHistoryHint( HSTRY_CHGFMTCOLL ) , m_pColl( pFormatColl ) , m_nNodeIndex( nNd ) , m_nNodeType( nNodeWhich ) { } void SwHistoryChangeFormatColl::SetInDoc( SwDoc* pDoc, bool ) { SwContentNode * pContentNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetContentNode(); OSL_ENSURE( pContentNd, "SwHistoryChangeFormatColl: no ContentNode" ); // before setting the format, check if it is still available in the // document. if it has been deleted, there is no undo! if ( pContentNd && m_nNodeType == pContentNd->GetNodeType() ) { if ( SwNodeType::Text == m_nNodeType ) { if (pDoc->GetTextFormatColls()->IsAlive(static_cast(m_pColl))) { pContentNd->ChgFormatColl( m_pColl ); } } else if (pDoc->GetGrfFormatColls()->IsAlive(static_cast(m_pColl))) { pContentNd->ChgFormatColl( m_pColl ); } } } SwHistoryTextFlyCnt::SwHistoryTextFlyCnt( SwFrameFormat* const pFlyFormat ) : SwHistoryHint( HSTRY_FLYCNT ) , m_pUndo( new SwUndoDelLayFormat( pFlyFormat ) ) { OSL_ENSURE( pFlyFormat, "SwHistoryTextFlyCnt: no Format" ); m_pUndo->ChgShowSel( false ); } SwHistoryTextFlyCnt::~SwHistoryTextFlyCnt() { } void SwHistoryTextFlyCnt::SetInDoc( SwDoc* pDoc, bool ) { ::sw::IShellCursorSupplier *const pISCS(pDoc->GetIShellCursorSupplier()); assert(pISCS); ::sw::UndoRedoContext context(*pDoc, *pISCS); m_pUndo->UndoImpl(context); } SwHistoryBookmark::SwHistoryBookmark( const ::sw::mark::IMark& rBkmk, bool bSavePos, bool bSaveOtherPos) : SwHistoryHint(HSTRY_BOOKMARK) , m_aName(rBkmk.GetName()) , m_aShortName() , m_aKeycode() , m_nNode(bSavePos ? rBkmk.GetMarkPos().nNode.GetIndex() : 0) , m_nOtherNode(bSaveOtherPos ? rBkmk.GetOtherMarkPos().nNode.GetIndex() : 0) , m_nContent(bSavePos ? rBkmk.GetMarkPos().nContent.GetIndex() : 0) , m_nOtherContent(bSaveOtherPos ? rBkmk.GetOtherMarkPos().nContent.GetIndex() :0) , m_bSavePos(bSavePos) , m_bSaveOtherPos(bSaveOtherPos) , m_bHadOtherPos(rBkmk.IsExpanded()) , m_eBkmkType(IDocumentMarkAccess::GetType(rBkmk)) { const ::sw::mark::IBookmark* const pBookmark = dynamic_cast< const ::sw::mark::IBookmark* >(&rBkmk); if(pBookmark) { m_aKeycode = pBookmark->GetKeyCode(); m_aShortName = pBookmark->GetShortName(); ::sfx2::Metadatable const*const pMetadatable( dynamic_cast< ::sfx2::Metadatable const* >(pBookmark)); if (pMetadatable) { m_pMetadataUndo = pMetadatable->CreateUndo(); } } } void SwHistoryBookmark::SetInDoc( SwDoc* pDoc, bool ) { ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); SwNodes& rNds = pDoc->GetNodes(); IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess(); std::unique_ptr pPam; ::sw::mark::IMark* pMark = nullptr; if(m_bSavePos) { SwContentNode* const pContentNd = rNds[m_nNode]->GetContentNode(); OSL_ENSURE(pContentNd, "" " - wrong node for a mark"); // #111660# don't crash when nNode1 doesn't point to content node. if(pContentNd) pPam.reset(new SwPaM(*pContentNd, m_nContent)); } else { pMark = *pMarkAccess->findMark(m_aName); pPam.reset(new SwPaM(pMark->GetMarkPos())); } if(m_bSaveOtherPos) { SwContentNode* const pContentNd = rNds[m_nOtherNode]->GetContentNode(); OSL_ENSURE(pContentNd, "" " - wrong node for a mark"); if (pPam != nullptr && pContentNd) { pPam->SetMark(); pPam->GetMark()->nNode = m_nOtherNode; pPam->GetMark()->nContent.Assign(pContentNd, m_nOtherContent); } } else if(m_bHadOtherPos) { if(!pMark) pMark = *pMarkAccess->findMark(m_aName); OSL_ENSURE(pMark->IsExpanded(), "" " - missing pos on old mark"); pPam->SetMark(); *pPam->GetMark() = pMark->GetOtherMarkPos(); } if (pPam) { if ( pMark != nullptr ) { pMarkAccess->deleteMark( pMark ); } ::sw::mark::IBookmark* const pBookmark = dynamic_cast<::sw::mark::IBookmark*>( pMarkAccess->makeMark(*pPam, m_aName, m_eBkmkType, sw::mark::InsertMode::New)); if ( pBookmark != nullptr ) { pBookmark->SetKeyCode(m_aKeycode); pBookmark->SetShortName(m_aShortName); if (m_pMetadataUndo) { ::sfx2::Metadatable * const pMeta( dynamic_cast< ::sfx2::Metadatable* >(pBookmark)); OSL_ENSURE(pMeta, "metadata undo, but not metadatable?"); if (pMeta) { pMeta->RestoreMetadata(m_pMetadataUndo); } } } } } bool SwHistoryBookmark::IsEqualBookmark(const ::sw::mark::IMark& rBkmk) { return m_nNode == rBkmk.GetMarkPos().nNode.GetIndex() && m_nContent == rBkmk.GetMarkPos().nContent.GetIndex() && m_aName == rBkmk.GetName(); } SwHistoryNoTextFieldmark::SwHistoryNoTextFieldmark(const ::sw::mark::IFieldmark& rFieldMark) : SwHistoryHint(HSTRY_NOTEXTFIELDMARK) , m_sType(rFieldMark.GetFieldname()) , m_nNode(rFieldMark.GetMarkPos().nNode.GetIndex()) , m_nContent(rFieldMark.GetMarkPos().nContent.GetIndex()) { } void SwHistoryNoTextFieldmark::SetInDoc(SwDoc* pDoc, bool) { ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); SwNodes& rNds = pDoc->GetNodes(); std::unique_ptr pPam; const SwContentNode* pContentNd = rNds[m_nNode]->GetContentNode(); if(pContentNd) pPam.reset(new SwPaM(*pContentNd, m_nContent)); if (pPam) { IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess(); pMarkAccess->makeNoTextFieldBookmark(*pPam, OUString(), m_sType); } } void SwHistoryNoTextFieldmark::ResetInDoc(SwDoc* pDoc) { ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); SwNodes& rNds = pDoc->GetNodes(); std::unique_ptr pPam; const SwContentNode* pContentNd = rNds[m_nNode]->GetContentNode(); if(pContentNd) pPam.reset(new SwPaM(*pContentNd, m_nContent-1)); if (pPam) { IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess(); pMarkAccess->deleteFieldmarkAt(*pPam->GetPoint()); } } SwHistoryTextFieldmark::SwHistoryTextFieldmark(const ::sw::mark::IFieldmark& rFieldMark) : SwHistoryHint(HSTRY_TEXTFIELDMARK) , m_sName(rFieldMark.GetName()) , m_sType(rFieldMark.GetFieldname()) , m_nStartNode(rFieldMark.GetMarkStart().nNode.GetIndex()) , m_nStartContent(rFieldMark.GetMarkStart().nContent.GetIndex()) , m_nEndNode(rFieldMark.GetMarkEnd().nNode.GetIndex()) , m_nEndContent(rFieldMark.GetMarkEnd().nContent.GetIndex()) { SwPosition const sepPos(sw::mark::FindFieldSep(rFieldMark)); m_nSepNode = sepPos.nNode.GetIndex(); m_nSepContent = sepPos.nContent.GetIndex(); } void SwHistoryTextFieldmark::SetInDoc(SwDoc* pDoc, bool) { ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); SwNodes& rNds = pDoc->GetNodes(); assert(rNds[m_nStartNode]->IsContentNode()); assert(rNds[m_nEndNode]->IsContentNode()); assert(rNds[m_nSepNode]->IsContentNode()); SwPaM const pam(*rNds[m_nStartNode]->GetContentNode(), m_nStartContent, *rNds[m_nEndNode]->GetContentNode(), // subtract 1 for the CH_TXT_ATR_FIELDEND itself, // plus more if same node as other CH_TXT_ATR m_nStartNode == m_nEndNode ? (m_nEndContent - 3) : m_nSepNode == m_nEndNode ? (m_nEndContent - 2) : (m_nEndContent - 1)); SwPosition const sepPos(*rNds[m_nSepNode]->GetContentNode(), m_nStartNode == m_nSepNode ? (m_nSepContent - 1) : m_nSepContent); IDocumentMarkAccess & rMarksAccess(*pDoc->getIDocumentMarkAccess()); rMarksAccess.makeFieldBookmark(pam, m_sName, m_sType, &sepPos); } void SwHistoryTextFieldmark::ResetInDoc(SwDoc* pDoc) { ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); SwNodes& rNds = pDoc->GetNodes(); assert(rNds[m_nStartNode]->IsContentNode()); assert(rNds[m_nEndNode]->IsContentNode()); assert(rNds[m_nSepNode]->IsContentNode()); SwPosition const pos(*rNds[m_nStartNode]->GetContentNode(), m_nStartContent); IDocumentMarkAccess & rMarksAccess(*pDoc->getIDocumentMarkAccess()); rMarksAccess.deleteFieldmarkAt(pos); } SwHistorySetAttrSet::SwHistorySetAttrSet( const SfxItemSet& rSet, sal_uLong nNodePos, const std::set &rSetArr ) : SwHistoryHint( HSTRY_SETATTRSET ) , m_OldSet( rSet ) , m_ResetArray( 0, 4 ) , m_nNodeIndex( nNodePos ) { SfxItemIter aIter( m_OldSet ), aOrigIter( rSet ); const SfxPoolItem* pItem = aIter.GetCurItem(), * pOrigItem = aOrigIter.GetCurItem(); while (pItem && pOrigItem) { if( !rSetArr.count( pOrigItem->Which() )) { m_ResetArray.push_back( pOrigItem->Which() ); m_OldSet.ClearItem( pOrigItem->Which() ); } else { switch ( pItem->Which() ) { case RES_PAGEDESC: static_cast( const_cast(pItem))->ChgDefinedIn( nullptr ); break; case RES_PARATR_DROP: static_cast( const_cast(pItem))->ChgDefinedIn( nullptr ); break; case RES_BOXATR_FORMULA: { // When a formula is set, never save the value. It // possibly must be recalculated! // Save formulas always in plain text m_OldSet.ClearItem( RES_BOXATR_VALUE ); SwTableBoxFormula& rNew = *static_cast( const_cast(pItem)); if ( rNew.IsIntrnlName() ) { const SwTableBoxFormula& rOld = rSet.Get( RES_BOXATR_FORMULA ); const SwNode* pNd = rOld.GetNodeOfFormula(); if ( pNd ) { const SwTableNode* pTableNode = pNd->FindTableNode(); if (pTableNode) { SwTableFormulaUpdate aMsgHint( &pTableNode->GetTable() ); aMsgHint.m_eFlags = TBL_BOXNAME; rNew.ChgDefinedIn( rOld.GetDefinedIn() ); rNew.ChangeState( &aMsgHint ); } } } rNew.ChgDefinedIn( nullptr ); } break; } } pItem = aIter.NextItem(); pOrigItem = aOrigIter.NextItem(); } } void SwHistorySetAttrSet::SetInDoc( SwDoc* pDoc, bool ) { ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); SwNode * pNode = pDoc->GetNodes()[ m_nNodeIndex ]; if ( pNode->IsContentNode() ) { static_cast(pNode)->SetAttr( m_OldSet ); if ( !m_ResetArray.empty() ) { static_cast(pNode)->ResetAttr( m_ResetArray ); } } else if ( pNode->IsTableNode() ) { SwFormat& rFormat = *static_cast(pNode)->GetTable().GetFrameFormat(); rFormat.SetFormatAttr( m_OldSet ); if ( !m_ResetArray.empty() ) { rFormat.ResetFormatAttr( m_ResetArray.front() ); } } } SwHistoryChangeFlyAnchor::SwHistoryChangeFlyAnchor( SwFrameFormat& rFormat ) : SwHistoryHint( HSTRY_CHGFLYANCHOR ) , m_rFormat( rFormat ) , m_nOldNodeIndex( rFormat.GetAnchor().GetContentAnchor()->nNode.GetIndex() ) , m_nOldContentIndex( (RndStdIds::FLY_AT_CHAR == rFormat.GetAnchor().GetAnchorId()) ? rFormat.GetAnchor().GetContentAnchor()->nContent.GetIndex() : COMPLETE_STRING ) { } void SwHistoryChangeFlyAnchor::SetInDoc( SwDoc* pDoc, bool ) { ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); if (pDoc->GetSpzFrameFormats()->IsAlive(&m_rFormat)) // Format does still exist { SwFormatAnchor aTmp( m_rFormat.GetAnchor() ); SwNode* pNd = pDoc->GetNodes()[ m_nOldNodeIndex ]; SwContentNode* pCNd = pNd->GetContentNode(); SwPosition aPos( *pNd ); if ( COMPLETE_STRING != m_nOldContentIndex ) { OSL_ENSURE(pCNd, "SwHistoryChangeFlyAnchor: no ContentNode"); if (pCNd) { aPos.nContent.Assign( pCNd, m_nOldContentIndex ); } } aTmp.SetAnchor( &aPos ); // so the Layout does not get confused if (!pCNd || !pCNd->getLayoutFrame(pDoc->getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr)) { m_rFormat.DelFrames(); } m_rFormat.SetFormatAttr( aTmp ); } } SwHistoryChangeFlyChain::SwHistoryChangeFlyChain( SwFlyFrameFormat& rFormat, const SwFormatChain& rAttr ) : SwHistoryHint( HSTRY_CHGFLYCHAIN ) , m_pPrevFormat( rAttr.GetPrev() ) , m_pNextFormat( rAttr.GetNext() ) , m_pFlyFormat( &rFormat ) { } void SwHistoryChangeFlyChain::SetInDoc( SwDoc* pDoc, bool ) { if (pDoc->GetSpzFrameFormats()->IsAlive(m_pFlyFormat)) { SwFormatChain aChain; if (m_pPrevFormat && pDoc->GetSpzFrameFormats()->IsAlive(m_pPrevFormat)) { aChain.SetPrev( m_pPrevFormat ); SwFormatChain aTmp( m_pPrevFormat->GetChain() ); aTmp.SetNext( m_pFlyFormat ); m_pPrevFormat->SetFormatAttr( aTmp ); } if (m_pNextFormat && pDoc->GetSpzFrameFormats()->IsAlive(m_pNextFormat)) { aChain.SetNext( m_pNextFormat ); SwFormatChain aTmp( m_pNextFormat->GetChain() ); aTmp.SetPrev( m_pFlyFormat ); m_pNextFormat->SetFormatAttr( aTmp ); } if ( aChain.GetNext() || aChain.GetPrev() ) { m_pFlyFormat->SetFormatAttr( aChain ); } } } // -> #i27615# SwHistoryChangeCharFormat::SwHistoryChangeCharFormat(const SfxItemSet & rSet, const OUString & sFormat) : SwHistoryHint(HSTRY_CHGCHARFMT) , m_OldSet(rSet), m_Format(sFormat) { } void SwHistoryChangeCharFormat::SetInDoc(SwDoc * pDoc, bool ) { SwCharFormat * pCharFormat = pDoc->FindCharFormatByName(m_Format); if (pCharFormat) { pCharFormat->SetFormatAttr(m_OldSet); } } // <- #i27615# SwHistory::SwHistory() : m_SwpHstry() , m_nEndDiff( 0 ) { } SwHistory::~SwHistory() { } void SwHistory::Add( const SfxPoolItem* pOldValue, const SfxPoolItem* pNewValue, sal_uLong nNodeIdx) { OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" ); const sal_uInt16 nWhich(pNewValue->Which()); // excluded values if(nWhich == RES_TXTATR_FIELD || nWhich == RES_TXTATR_ANNOTATION) { return; } // no default Attribute? std::unique_ptr pHt; // To be able to include the DrawingLayer FillItems something more // general has to be done to check if an Item is default than to check // if its pointer equals that in Writer's global PoolDefaults (held in // aAttrTab and used to fill the pool defaults in Writer - looks as if // Writer is *older* than the SfxItemPool ?). I checked the possibility to // get the SfxItemPool here (works), but decided to use the SfxPoolItem's // global tooling aka IsDefaultItem(const SfxPoolItem*) for now if(pOldValue && !IsDefaultItem(pOldValue)) { pHt.reset( new SwHistorySetFormat( pOldValue, nNodeIdx ) ); } else { pHt.reset( new SwHistoryResetFormat( pNewValue, nNodeIdx ) ); } m_SwpHstry.push_back( std::move(pHt) ); } // FIXME: refactor the following "Add" methods (DRY)? void SwHistory::Add( SwTextAttr* pHint, sal_uLong nNodeIdx, bool bNewAttr ) { OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" ); std::unique_ptr pHt; if( !bNewAttr ) { switch ( pHint->Which() ) { case RES_TXTATR_FTN: pHt.reset( new SwHistorySetFootnote( static_cast(pHint), nNodeIdx ) ); break; case RES_TXTATR_FLYCNT: pHt.reset( new SwHistoryTextFlyCnt( static_cast(pHint) ->GetFlyCnt().GetFrameFormat() ) ); break; case RES_TXTATR_FIELD: case RES_TXTATR_ANNOTATION: pHt.reset( new SwHistorySetTextField( static_txtattr_cast(pHint), nNodeIdx) ); break; case RES_TXTATR_TOXMARK: pHt.reset( new SwHistorySetTOXMark( static_txtattr_cast(pHint), nNodeIdx) ); break; case RES_TXTATR_REFMARK: pHt.reset( new SwHistorySetRefMark( static_txtattr_cast(pHint), nNodeIdx) ); break; default: pHt.reset( new SwHistorySetText( pHint, nNodeIdx ) ); } } else { pHt.reset( new SwHistoryResetText( pHint->Which(), pHint->GetStart(), pHint->GetAnyEnd(), nNodeIdx ) ); } m_SwpHstry.push_back( std::move(pHt) ); } void SwHistory::Add( SwFormatColl* pColl, sal_uLong nNodeIdx, SwNodeType nWhichNd ) { OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" ); std::unique_ptr pHt( new SwHistoryChangeFormatColl( pColl, nNodeIdx, nWhichNd )); m_SwpHstry.push_back( std::move(pHt) ); } void SwHistory::Add(const ::sw::mark::IMark& rBkmk, bool bSavePos, bool bSaveOtherPos) { OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" ); std::unique_ptr pHt; switch (IDocumentMarkAccess::GetType(rBkmk)) { case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK: case IDocumentMarkAccess::MarkType::DATE_FIELDMARK: assert(bSavePos && bSaveOtherPos); // must be deleted completely! pHt.reset(new SwHistoryTextFieldmark(dynamic_cast(rBkmk))); break; case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK: case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK: assert(bSavePos && bSaveOtherPos); // must be deleted completely! pHt.reset(new SwHistoryNoTextFieldmark(dynamic_cast(rBkmk))); break; default: pHt.reset(new SwHistoryBookmark(rBkmk, bSavePos, bSaveOtherPos)); break; } assert(pHt); m_SwpHstry.push_back( std::move(pHt) ); } void SwHistory::AddChangeFlyAnchor(SwFrameFormat& rFormat) { std::unique_ptr pHt(new SwHistoryChangeFlyAnchor( rFormat )); m_SwpHstry.push_back( std::move(pHt) ); } void SwHistory::AddDeleteFly(SwFrameFormat& rFormat, sal_uInt16& rSetPos) { OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" ); const sal_uInt16 nWh = rFormat.Which(); (void) nWh; // only Flys! assert((RES_FLYFRMFMT == nWh && dynamic_cast(&rFormat)) || (RES_DRAWFRMFMT == nWh && dynamic_cast(&rFormat))); { std::unique_ptr pHint(new SwHistoryTextFlyCnt( &rFormat )); m_SwpHstry.push_back( std::move(pHint) ); const SwFormatChain* pChainItem; if( SfxItemState::SET == rFormat.GetItemState( RES_CHAIN, false, reinterpret_cast(&pChainItem) )) { assert(RES_FLYFRMFMT == nWh); // not supported on SdrObjects if( pChainItem->GetNext() || pChainItem->GetPrev() ) { std::unique_ptr pHt( new SwHistoryChangeFlyChain(static_cast(rFormat), *pChainItem)); m_SwpHstry.insert( m_SwpHstry.begin() + rSetPos++, std::move(pHt) ); if ( pChainItem->GetNext() ) { SwFormatChain aTmp( pChainItem->GetNext()->GetChain() ); aTmp.SetPrev( nullptr ); pChainItem->GetNext()->SetFormatAttr( aTmp ); } if ( pChainItem->GetPrev() ) { SwFormatChain aTmp( pChainItem->GetPrev()->GetChain() ); aTmp.SetNext( nullptr ); pChainItem->GetPrev()->SetFormatAttr( aTmp ); } } rFormat.ResetFormatAttr( RES_CHAIN ); } } } void SwHistory::Add( const SwTextFootnote& rFootnote ) { std::unique_ptr pHt(new SwHistorySetFootnote( rFootnote )); m_SwpHstry.push_back( std::move(pHt) ); } // #i27615# void SwHistory::Add(const SfxItemSet & rSet, const SwCharFormat & rFormat) { std::unique_ptr pHt(new SwHistoryChangeCharFormat(rSet, rFormat.GetName())); m_SwpHstry.push_back( std::move(pHt) ); } bool SwHistory::Rollback( SwDoc* pDoc, sal_uInt16 nStart ) { if ( !Count() ) return false; for ( sal_uInt16 i = Count(); i > nStart ; ) { SwHistoryHint * pHHt = m_SwpHstry[ --i ].get(); pHHt->SetInDoc( pDoc, false ); } m_SwpHstry.erase( m_SwpHstry.begin() + nStart, m_SwpHstry.end() ); m_nEndDiff = 0; return true; } bool SwHistory::TmpRollback( SwDoc* pDoc, sal_uInt16 nStart, bool bToFirst ) { sal_uInt16 nEnd = Count() - m_nEndDiff; if ( !Count() || !nEnd || nStart >= nEnd ) return false; if ( bToFirst ) { for ( ; nEnd > nStart; ++m_nEndDiff ) { SwHistoryHint* pHHt = m_SwpHstry[ --nEnd ].get(); pHHt->SetInDoc( pDoc, true ); } } else { for ( ; nStart < nEnd; ++m_nEndDiff, ++nStart ) { SwHistoryHint* pHHt = m_SwpHstry[ nStart ].get(); pHHt->SetInDoc( pDoc, true ); } } return true; } sal_uInt16 SwHistory::SetTmpEnd( sal_uInt16 nNewTmpEnd ) { OSL_ENSURE( nNewTmpEnd <= Count(), "SwHistory::SetTmpEnd: out of bounds" ); const sal_uInt16 nOld = Count() - m_nEndDiff; m_nEndDiff = Count() - nNewTmpEnd; // for every SwHistoryFlyCnt, call the Redo of its UndoObject. // this saves the formats of the flys! for ( sal_uInt16 n = nOld; n < nNewTmpEnd; n++ ) { if ( HSTRY_FLYCNT == (*this)[ n ]->Which() ) { static_cast((*this)[ n ]) ->GetUDelLFormat()->RedoForRollback(); } } return nOld; } void SwHistory::CopyFormatAttr( const SfxItemSet& rSet, sal_uLong nNodeIdx) { if(rSet.Count()) { SfxItemIter aIter(rSet); const SfxPoolItem* pItem = aIter.GetCurItem(); do { if(!IsInvalidItem(pItem)) { Add(pItem, pItem, nNodeIdx); } pItem = aIter.NextItem(); } while(pItem); } } void SwHistory::CopyAttr( SwpHints const * pHts, const sal_uLong nNodeIdx, const sal_Int32 nStart, const sal_Int32 nEnd, const bool bCopyFields ) { if( !pHts ) return; // copy all attributes of the TextNode in the area from nStart to nEnd SwTextAttr* pHt; for( size_t n = 0; n < pHts->Count(); ++n ) { // nAttrStt must even be set when !pEndIdx pHt = pHts->Get(n); const sal_Int32 nAttrStt = pHt->GetStart(); const sal_Int32 * pEndIdx = pHt->GetEnd(); if( nullptr != pEndIdx && nAttrStt > nEnd ) break; // never copy Flys and Footnote !! bool bNextAttr = false; switch( pHt->Which() ) { case RES_TXTATR_FIELD: case RES_TXTATR_ANNOTATION: case RES_TXTATR_INPUTFIELD: if( !bCopyFields ) bNextAttr = true; break; case RES_TXTATR_FLYCNT: case RES_TXTATR_FTN: bNextAttr = true; break; } if( bNextAttr ) continue; // save all attributes that are somehow in this area if ( nStart <= nAttrStt ) { if ( nEnd > nAttrStt ) { Add( pHt, nNodeIdx, false ); } } else if ( pEndIdx && nStart < *pEndIdx ) { Add( pHt, nNodeIdx, false ); } } } // Class to register the history at a Node, Format, HintsArray, ... SwRegHistory::SwRegHistory( SwHistory* pHst ) : SwClient( nullptr ) , m_pHistory( pHst ) , m_nNodeIndex( ULONG_MAX ) { MakeSetWhichIds(); } SwRegHistory::SwRegHistory( SwModify* pRegIn, const SwNode& rNd, SwHistory* pHst ) : SwClient( pRegIn ) , m_pHistory( pHst ) , m_nNodeIndex( rNd.GetIndex() ) { MakeSetWhichIds(); } SwRegHistory::SwRegHistory( const SwNode& rNd, SwHistory* pHst ) : SwClient( nullptr ) , m_pHistory( pHst ) , m_nNodeIndex( rNd.GetIndex() ) { MakeSetWhichIds(); } void SwRegHistory::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) { if ( m_pHistory && pNew && pOld != pNew ) { if ( pNew->Which() < POOLATTR_END ) { if(RES_UPDATE_ATTR == pNew->Which()) { // const SfxItemPool& rPool = static_cast< const SwUpdateAttr* >(pNew)->GetSfxItemPool(); m_pHistory->Add( // rPool, pOld, pNew, m_nNodeIndex); } else { OSL_ENSURE(false, "Unexpected update attribute (!)"); } } else if (pOld && RES_ATTRSET_CHG == pNew->Which()) { std::unique_ptr pNewHstr; const SfxItemSet& rSet = *static_cast< const SwAttrSetChg* >(pOld)->GetChgSet(); if ( 1 < rSet.Count() ) { pNewHstr.reset( new SwHistorySetAttrSet( rSet, m_nNodeIndex, m_WhichIdSet ) ); } else if (const SfxPoolItem* pItem = SfxItemIter(rSet).GetCurItem()) { if ( m_WhichIdSet.count( pItem->Which() ) ) { pNewHstr.reset( new SwHistorySetFormat( pItem, m_nNodeIndex ) ); } else { pNewHstr.reset( new SwHistoryResetFormat( pItem, m_nNodeIndex ) ); } } if (pNewHstr) m_pHistory->m_SwpHstry.push_back( std::move(pNewHstr) ); } } } void SwRegHistory::AddHint( SwTextAttr* pHt, const bool bNew ) { m_pHistory->Add( pHt, m_nNodeIndex, bNew ); } bool SwRegHistory::InsertItems( const SfxItemSet& rSet, sal_Int32 const nStart, sal_Int32 const nEnd, SetAttrMode const nFlags, SwTextAttr **ppNewTextAttr ) { if( !rSet.Count() ) return false; SwTextNode * const pTextNode = dynamic_cast(GetRegisteredIn()); OSL_ENSURE(pTextNode, "SwRegHistory not registered at text node?"); if (!pTextNode) return false; if (m_pHistory) { pTextNode->GetOrCreateSwpHints().Register(this); } const bool bInserted = pTextNode->SetAttr( rSet, nStart, nEnd, nFlags, ppNewTextAttr ); // Caution: The array can be deleted when inserting an attribute! // This can happen when the value that should be added first deletes // an existing attribute but does not need to be added itself because // the paragraph attributes are identical // ( -> bForgetAttr in SwpHints::Insert ) if ( pTextNode->GetpSwpHints() && m_pHistory ) { pTextNode->GetpSwpHints()->DeRegister(); } #ifndef NDEBUG if ( m_pHistory && bInserted ) { SfxItemIter aIter(rSet); for (SfxPoolItem const* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) { // check that the history recorded a hint to reset every item sal_uInt16 const nWhich(pItem->Which()); sal_uInt16 const nExpected( (isCHRATR(nWhich) || RES_TXTATR_UNKNOWN_CONTAINER == nWhich) ? RES_TXTATR_AUTOFMT : nWhich); if (RES_TXTATR_AUTOFMT == nExpected) continue; // special case, may get set on text node itself // tdf#105077 even worse, node's set could cause // nothing at all to be inserted assert(std::any_of( m_pHistory->m_SwpHstry.begin(), m_pHistory->m_SwpHstry.end(), [nExpected](std::unique_ptr const& pHint) -> bool { SwHistoryResetText const*const pReset( dynamic_cast(pHint.get())); return pReset && (pReset->GetWhich() == nExpected); })); } } #endif return bInserted; } void SwRegHistory::RegisterInModify( SwModify* pRegIn, const SwNode& rNd ) { if ( m_pHistory && pRegIn ) { pRegIn->Add( this ); m_nNodeIndex = rNd.GetIndex(); MakeSetWhichIds(); } else { m_WhichIdSet.clear(); } } void SwRegHistory::MakeSetWhichIds() { if (!m_pHistory) return; m_WhichIdSet.clear(); if( GetRegisteredIn() ) { const SfxItemSet* pSet = nullptr; if( dynamic_cast< const SwContentNode *>( GetRegisteredIn() ) != nullptr ) { pSet = static_cast( GetRegisteredIn())->GetpSwAttrSet(); } else if ( dynamic_cast< const SwFormat *>( GetRegisteredIn() ) != nullptr ) { pSet = &static_cast( GetRegisteredIn())->GetAttrSet(); } if( pSet && pSet->Count() ) { SfxItemIter aIter( *pSet ); for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) { sal_uInt16 nW = pItem->Which(); m_WhichIdSet.insert( nW ); } } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */