android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / undo / rolbck.cxx
blobd17c08e4795b4a8fe444801a22361affb510e9dc
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <rolbck.hxx>
22 #include <libxml/xmlwriter.h>
24 #include <svl/itemiter.hxx>
25 #include <editeng/formatbreakitem.hxx>
26 #include <hints.hxx>
27 #include <hintids.hxx>
28 #include <fmtftn.hxx>
29 #include <fchrfmt.hxx>
30 #include <fmtflcnt.hxx>
31 #include <fmtrfmrk.hxx>
32 #include <fmtfld.hxx>
33 #include <fmtpdsc.hxx>
34 #include <txtfld.hxx>
35 #include <txtrfmrk.hxx>
36 #include <txttxmrk.hxx>
37 #include <txtftn.hxx>
38 #include <txtflcnt.hxx>
39 #include <fmtanchr.hxx>
40 #include <fmtcnct.hxx>
41 #include <frmfmt.hxx>
42 #include <ftnidx.hxx>
43 #include <doc.hxx>
44 #include <IDocumentUndoRedo.hxx>
45 #include <IDocumentFieldsAccess.hxx>
46 #include <IDocumentLayoutAccess.hxx>
47 #include <docary.hxx>
48 #include <ndtxt.hxx>
49 #include <paratr.hxx>
50 #include <cellatr.hxx>
51 #include <fldbas.hxx>
52 #include <pam.hxx>
53 #include <swtable.hxx>
54 #include <UndoCore.hxx>
55 #include <IMark.hxx>
56 #include <charfmt.hxx>
57 #include <strings.hrc>
58 #include <bookmark.hxx>
59 #include <frameformats.hxx>
60 #include <memory>
61 #include <utility>
63 OUString SwHistoryHint::GetDescription() const
65 return OUString();
68 void SwHistoryHint::dumpAsXml(xmlTextWriterPtr pWriter) const
70 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHistoryHint"));
71 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
72 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("symbol"), BAD_CAST(typeid(*this).name()));
73 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_eWhichId"),
74 BAD_CAST(OString::number(m_eWhichId).getStr()));
75 (void)xmlTextWriterEndElement(pWriter);
78 SwHistorySetFormat::SwHistorySetFormat( const SfxPoolItem* pFormatHt, SwNodeOffset nNd )
79 : SwHistoryHint( HSTRY_SETFMTHNT )
80 , m_pAttr( pFormatHt->Clone() )
81 , m_nNodeIndex( nNd )
83 switch ( m_pAttr->Which() )
85 case RES_PAGEDESC:
86 static_cast<SwFormatPageDesc&>(*m_pAttr).ChgDefinedIn( nullptr );
87 break;
88 case RES_PARATR_DROP:
89 static_cast<SwFormatDrop&>(*m_pAttr).ChgDefinedIn(nullptr);
90 break;
91 case RES_BOXATR_FORMULA:
93 // save formulas always in plain text
94 SwTableBoxFormula& rNew = static_cast<SwTableBoxFormula&>(*m_pAttr);
95 if ( rNew.IsIntrnlName() )
97 const SwTableBoxFormula& rOld =
98 *static_cast<const SwTableBoxFormula*>(pFormatHt);
99 const SwNode* pNd = rOld.GetNodeOfFormula();
100 if ( pNd )
102 const SwTableNode* pTableNode = pNd->FindTableNode();
103 if (pTableNode)
105 auto pCpyTable = const_cast<SwTable*>(&pTableNode->GetTable());
106 pCpyTable->SwitchFormulasToExternalRepresentation();
107 rNew.ChgDefinedIn(rOld.GetDefinedIn());
108 rNew.ToRelBoxNm(pCpyTable);
112 rNew.ChgDefinedIn( nullptr );
114 break;
118 OUString SwHistorySetFormat::GetDescription() const
120 OUString aResult;
122 switch (m_pAttr->Which())
124 case RES_BREAK:
125 switch (static_cast<SvxFormatBreakItem &>(*m_pAttr).GetBreak())
127 case SvxBreak::PageBefore:
128 case SvxBreak::PageAfter:
129 case SvxBreak::PageBoth:
130 aResult = SwResId(STR_UNDO_PAGEBREAKS);
132 break;
133 case SvxBreak::ColumnBefore:
134 case SvxBreak::ColumnAfter:
135 case SvxBreak::ColumnBoth:
136 aResult = SwResId(STR_UNDO_COLBRKS);
138 break;
139 default:
140 break;
142 break;
143 default:
144 break;
147 return aResult;
150 void SwHistorySetFormat::dumpAsXml(xmlTextWriterPtr pWriter) const
152 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHistorySetFormat"));
153 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nNodeIndex"),
154 BAD_CAST(OString::number(sal_Int32(m_nNodeIndex)).getStr()));
155 SwHistoryHint::dumpAsXml(pWriter);
157 if (m_pAttr)
159 m_pAttr->dumpAsXml(pWriter);
162 (void)xmlTextWriterEndElement(pWriter);
165 void SwHistorySetFormat::SetInDoc( SwDoc* pDoc, bool bTmpSet )
167 SwNode * pNode = pDoc->GetNodes()[ m_nNodeIndex ];
168 if ( pNode->IsContentNode() )
170 static_cast<SwContentNode*>(pNode)->SetAttr( *m_pAttr );
172 else if ( pNode->IsTableNode() )
174 static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat()->SetFormatAttr(
175 *m_pAttr );
177 else if ( pNode->IsStartNode() && (SwTableBoxStartNode ==
178 static_cast<SwStartNode*>(pNode)->GetStartNodeType()) )
180 SwTableNode* pTNd = pNode->FindTableNode();
181 if ( pTNd )
183 SwTableBox* pBox = pTNd->GetTable().GetTableBox( m_nNodeIndex );
184 if (pBox)
186 pBox->ClaimFrameFormat()->SetFormatAttr( *m_pAttr );
191 if ( !bTmpSet )
193 m_pAttr.reset();
197 SwHistorySetFormat::~SwHistorySetFormat()
201 SwHistoryResetFormat::SwHistoryResetFormat(const SfxPoolItem* pFormatHt, SwNodeOffset nNodeIdx)
202 : SwHistoryHint( HSTRY_RESETFMTHNT )
203 , m_nNodeIndex( nNodeIdx )
204 , m_nWhich( pFormatHt->Which() )
208 void SwHistoryResetFormat::SetInDoc( SwDoc* pDoc, bool )
210 SwNode * pNode = pDoc->GetNodes()[ m_nNodeIndex ];
211 if ( pNode->IsContentNode() )
213 static_cast<SwContentNode*>(pNode)->ResetAttr( m_nWhich );
215 else if ( pNode->IsTableNode() )
217 static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat()->
218 ResetFormatAttr( m_nWhich );
222 SwHistorySetText::SwHistorySetText( SwTextAttr* pTextHt, SwNodeOffset nNodePos )
223 : SwHistoryHint( HSTRY_SETTXTHNT )
224 , m_nNodeIndex( nNodePos )
225 , m_nStart( pTextHt->GetStart() )
226 , m_nEnd( pTextHt->GetAnyEnd() )
227 , m_bFormatIgnoreStart(pTextHt->IsFormatIgnoreStart())
228 , m_bFormatIgnoreEnd (pTextHt->IsFormatIgnoreEnd ())
230 // Caution: the following attributes generate no format attributes:
231 // - NoLineBreak, NoHyphen, Inserted, Deleted
232 // These cases must be handled separately !!!
234 // a little bit complicated but works: first assign a copy of the
235 // default value and afterwards the values from text attribute
236 if ( RES_TXTATR_CHARFMT == pTextHt->Which() )
238 m_pAttr.reset( new SwFormatCharFormat( pTextHt->GetCharFormat().GetCharFormat() ) );
240 else
242 m_pAttr.reset( pTextHt->GetAttr().Clone() );
246 SwHistorySetText::~SwHistorySetText()
250 void SwHistorySetText::SetInDoc( SwDoc* pDoc, bool )
252 if (!m_pAttr)
253 return;
255 if ( RES_TXTATR_CHARFMT == m_pAttr->Which() )
257 // ask the Doc if the CharFormat still exists
258 if (!pDoc->GetCharFormats()->ContainsFormat(static_cast<SwFormatCharFormat&>(*m_pAttr).GetCharFormat()))
259 return; // do not set, format does not exist
262 SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
263 OSL_ENSURE( pTextNd, "SwHistorySetText::SetInDoc: not a TextNode" );
265 if ( !pTextNd )
266 return;
268 SwTextAttr *const pAttr = pTextNd->InsertItem(*m_pAttr, m_nStart, m_nEnd,
269 SetAttrMode::NOTXTATRCHR |
270 SetAttrMode::NOHINTADJUST );
271 // shouldn't be possible to hit any error/merging path from here
272 assert(pAttr);
273 if (m_bFormatIgnoreStart)
275 pAttr->SetFormatIgnoreStart(true);
277 if (m_bFormatIgnoreEnd)
279 pAttr->SetFormatIgnoreEnd(true);
283 SwHistorySetTextField::SwHistorySetTextField( const SwTextField* pTextField, SwNodeOffset nNodePos )
284 : SwHistoryHint( HSTRY_SETTXTFLDHNT )
285 , m_pField( new SwFormatField( *pTextField->GetFormatField().GetField() ) )
287 // only copy if not Sys-FieldType
288 SwDoc& rDoc = pTextField->GetTextNode().GetDoc();
290 m_nFieldWhich = m_pField->GetField()->GetTyp()->Which();
291 if (m_nFieldWhich == SwFieldIds::Database ||
292 m_nFieldWhich == SwFieldIds::User ||
293 m_nFieldWhich == SwFieldIds::SetExp ||
294 m_nFieldWhich == SwFieldIds::Dde ||
295 !rDoc.getIDocumentFieldsAccess().GetSysFieldType( m_nFieldWhich ))
297 m_pFieldType = m_pField->GetField()->GetTyp()->Copy();
298 m_pField->GetField()->ChgTyp( m_pFieldType.get() ); // change field type
300 m_nNodeIndex = nNodePos;
301 m_nPos = pTextField->GetStart();
304 OUString SwHistorySetTextField::GetDescription() const
306 return m_pField->GetField()->GetDescription();
309 SwHistorySetTextField::~SwHistorySetTextField()
313 void SwHistorySetTextField::SetInDoc( SwDoc* pDoc, bool )
315 if (!m_pField)
316 return;
318 SwFieldType* pNewFieldType = m_pFieldType.get();
319 if ( !pNewFieldType )
321 pNewFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType( m_nFieldWhich );
323 else
325 // register type with the document
326 pNewFieldType = pDoc->getIDocumentFieldsAccess().InsertFieldType( *m_pFieldType );
329 m_pField->GetField()->ChgTyp( pNewFieldType ); // change field type
331 SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
332 OSL_ENSURE( pTextNd, "SwHistorySetTextField: no TextNode" );
334 if ( pTextNd )
336 pTextNd->InsertItem( *m_pField, m_nPos, m_nPos,
337 SetAttrMode::NOTXTATRCHR );
341 SwHistorySetRefMark::SwHistorySetRefMark( const SwTextRefMark* pTextHt, SwNodeOffset nNodePos )
342 : SwHistoryHint( HSTRY_SETREFMARKHNT )
343 , m_RefName( pTextHt->GetRefMark().GetRefName() )
344 , m_nNodeIndex( nNodePos )
345 , m_nStart( pTextHt->GetStart() )
346 , m_nEnd( pTextHt->GetAnyEnd() )
350 void SwHistorySetRefMark::SetInDoc( SwDoc* pDoc, bool )
352 SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
353 OSL_ENSURE( pTextNd, "SwHistorySetRefMark: no TextNode" );
354 if ( !pTextNd )
355 return;
357 SwFormatRefMark aRefMark( m_RefName );
359 // if a reference mark without an end already exists here: must not insert!
360 if ( m_nStart != m_nEnd ||
361 !pTextNd->GetTextAttrForCharAt( m_nStart, RES_TXTATR_REFMARK ) )
363 pTextNd->InsertItem( aRefMark, m_nStart, m_nEnd,
364 SetAttrMode::NOTXTATRCHR );
368 SwHistorySetTOXMark::SwHistorySetTOXMark( const SwTextTOXMark* pTextHt, SwNodeOffset nNodePos )
369 : SwHistoryHint( HSTRY_SETTOXMARKHNT )
370 , m_TOXMark( pTextHt->GetTOXMark() )
371 , m_TOXName( m_TOXMark.GetTOXType()->GetTypeName() )
372 , m_eTOXTypes( m_TOXMark.GetTOXType()->GetType() )
373 , m_nNodeIndex( nNodePos )
374 , m_nStart( pTextHt->GetStart() )
375 , m_nEnd( pTextHt->GetAnyEnd() )
377 static_cast<SvtListener*>(&m_TOXMark)->EndListeningAll();
380 SwTOXType* SwHistorySetTOXMark::GetSwTOXType(SwDoc& rDoc, TOXTypes eTOXTypes, const OUString& rTOXName)
382 // search for respective TOX type
383 const sal_uInt16 nCnt = rDoc.GetTOXTypeCount(eTOXTypes);
384 SwTOXType* pToxType = nullptr;
385 for ( sal_uInt16 n = 0; n < nCnt; ++n )
387 pToxType = const_cast<SwTOXType*>(rDoc.GetTOXType(eTOXTypes, n));
388 if (pToxType->GetTypeName() == rTOXName)
389 break;
390 pToxType = nullptr;
393 if ( !pToxType ) // TOX type not found, create new
395 pToxType = const_cast<SwTOXType*>(
396 rDoc.InsertTOXType(SwTOXType(rDoc, eTOXTypes, rTOXName)));
399 return pToxType;
402 void SwHistorySetTOXMark::SetInDoc( SwDoc* pDoc, bool )
404 SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
405 OSL_ENSURE( pTextNd, "SwHistorySetTOXMark: no TextNode" );
406 if ( !pTextNd )
407 return;
409 SwTOXType* pToxType = GetSwTOXType(*pDoc, m_eTOXTypes, m_TOXName);
411 SwTOXMark aNew( m_TOXMark );
412 aNew.RegisterToTOXType( *pToxType );
414 pTextNd->InsertItem( aNew, m_nStart, m_nEnd,
415 SetAttrMode::NOTXTATRCHR );
418 bool SwHistorySetTOXMark::IsEqual( const SwTOXMark& rCmp ) const
420 return m_TOXName == rCmp.GetTOXType()->GetTypeName() &&
421 m_eTOXTypes == rCmp.GetTOXType()->GetType() &&
422 m_TOXMark.GetAlternativeText() == rCmp.GetAlternativeText() &&
423 ( (TOX_INDEX == m_eTOXTypes)
424 ? ( m_TOXMark.GetPrimaryKey() == rCmp.GetPrimaryKey() &&
425 m_TOXMark.GetSecondaryKey() == rCmp.GetSecondaryKey() )
426 : m_TOXMark.GetLevel() == rCmp.GetLevel()
430 SwHistoryResetText::SwHistoryResetText( sal_uInt16 nWhich,
431 sal_Int32 nAttrStart, sal_Int32 nAttrEnd, SwNodeOffset nNodePos )
432 : SwHistoryHint( HSTRY_RESETTXTHNT )
433 , m_nNodeIndex( nNodePos ), m_nStart( nAttrStart ), m_nEnd( nAttrEnd )
434 , m_nAttr( nWhich )
438 void SwHistoryResetText::SetInDoc( SwDoc* pDoc, bool )
440 SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
441 OSL_ENSURE( pTextNd, "SwHistoryResetText: no TextNode" );
442 if ( pTextNd )
444 pTextNd->DeleteAttributes( m_nAttr, m_nStart, m_nEnd );
448 SwHistorySetFootnote::SwHistorySetFootnote( SwTextFootnote* pTextFootnote, SwNodeOffset nNodePos )
449 : SwHistoryHint( HSTRY_SETFTNHNT )
450 , m_pUndo( new SwUndoSaveSection )
451 , m_FootnoteNumber( pTextFootnote->GetFootnote().GetNumStr() )
452 , m_nNodeIndex( nNodePos )
453 , m_nStart( pTextFootnote->GetStart() )
454 , m_bEndNote( pTextFootnote->GetFootnote().IsEndNote() )
456 OSL_ENSURE( pTextFootnote->GetStartNode(),
457 "SwHistorySetFootnote: Footnote without Section" );
459 // keep the old NodePos (because who knows what later will be saved/deleted
460 // in SaveSection)
461 SwDoc& rDoc = const_cast<SwDoc&>(pTextFootnote->GetTextNode().GetDoc());
462 SwNode* pSaveNd = rDoc.GetNodes()[ m_nNodeIndex ];
464 // keep pointer to StartNode of FootnoteSection and reset its attribute for now
465 // (as a result, its/all Frames will be deleted automatically)
466 SwNodeIndex aSttIdx( *pTextFootnote->GetStartNode() );
467 pTextFootnote->SetStartNode( nullptr, false );
469 m_pUndo->SaveSection( aSttIdx );
470 m_nNodeIndex = pSaveNd->GetIndex();
473 SwHistorySetFootnote::SwHistorySetFootnote( const SwTextFootnote &rTextFootnote )
474 : SwHistoryHint( HSTRY_SETFTNHNT )
475 , m_FootnoteNumber( rTextFootnote.GetFootnote().GetNumStr() )
476 , m_nNodeIndex( SwTextFootnote_GetIndex( (&rTextFootnote) ) )
477 , m_nStart( rTextFootnote.GetStart() )
478 , m_bEndNote( rTextFootnote.GetFootnote().IsEndNote() )
480 OSL_ENSURE( rTextFootnote.GetStartNode(),
481 "SwHistorySetFootnote: Footnote without Section" );
484 OUString SwHistorySetFootnote::GetDescription() const
486 return SwResId(STR_FOOTNOTE);
489 SwHistorySetFootnote::~SwHistorySetFootnote()
493 void SwHistorySetFootnote::SetInDoc( SwDoc* pDoc, bool )
495 SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
496 OSL_ENSURE( pTextNd, "SwHistorySetFootnote: no TextNode" );
497 if ( !pTextNd )
498 return;
500 if (m_pUndo)
502 // set the footnote in the TextNode
503 SwFormatFootnote aTemp( m_bEndNote );
504 SwFormatFootnote& rNew = const_cast<SwFormatFootnote&>(
505 pDoc->GetAttrPool().Put(aTemp) );
506 if ( !m_FootnoteNumber.isEmpty() )
508 rNew.SetNumStr( m_FootnoteNumber );
510 SwTextFootnote* pTextFootnote = new SwTextFootnote( rNew, m_nStart );
512 // create the section of the Footnote
513 SwNodeIndex aIdx( *pTextNd );
514 m_pUndo->RestoreSection( pDoc, &aIdx, SwFootnoteStartNode );
515 pTextFootnote->SetStartNode( &aIdx );
516 if ( m_pUndo->GetHistory() )
518 // create frames only now
519 m_pUndo->GetHistory()->Rollback( pDoc );
522 pTextNd->InsertHint( pTextFootnote );
524 else
526 SwTextFootnote * const pFootnote =
527 static_cast<SwTextFootnote*>(
528 pTextNd->GetTextAttrForCharAt( m_nStart ));
529 assert(pFootnote);
530 SwFormatFootnote &rFootnote = const_cast<SwFormatFootnote&>(pFootnote->GetFootnote());
531 rFootnote.SetNumStr( m_FootnoteNumber );
532 if ( rFootnote.IsEndNote() != m_bEndNote )
534 rFootnote.SetEndNote( m_bEndNote );
535 pFootnote->CheckCondColl();
540 SwHistoryChangeFormatColl::SwHistoryChangeFormatColl( SwFormatColl* pFormatColl, SwNodeOffset nNd,
541 SwNodeType nNodeWhich )
542 : SwHistoryHint( HSTRY_CHGFMTCOLL )
543 , m_pColl( pFormatColl )
544 , m_nNodeIndex( nNd )
545 , m_nNodeType( nNodeWhich )
549 void SwHistoryChangeFormatColl::SetInDoc( SwDoc* pDoc, bool )
551 SwContentNode * pContentNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetContentNode();
552 OSL_ENSURE( pContentNd, "SwHistoryChangeFormatColl: no ContentNode" );
554 // before setting the format, check if it is still available in the
555 // document. if it has been deleted, there is no undo!
556 if ( !(pContentNd && m_nNodeType == pContentNd->GetNodeType()) )
557 return;
559 if ( SwNodeType::Text == m_nNodeType )
561 if (pDoc->GetTextFormatColls()->IsAlive(static_cast<SwTextFormatColl *>(m_pColl)))
563 pContentNd->ChgFormatColl( m_pColl );
566 else if (pDoc->GetGrfFormatColls()->IsAlive(static_cast<SwGrfFormatColl *>(m_pColl)))
568 pContentNd->ChgFormatColl( m_pColl );
572 SwHistoryTextFlyCnt::SwHistoryTextFlyCnt( SwFrameFormat* const pFlyFormat )
573 : SwHistoryHint( HSTRY_FLYCNT )
574 , m_pUndo( new SwUndoDelLayFormat( pFlyFormat ) )
576 OSL_ENSURE( pFlyFormat, "SwHistoryTextFlyCnt: no Format" );
577 m_pUndo->ChgShowSel( false );
580 SwHistoryTextFlyCnt::~SwHistoryTextFlyCnt()
584 void SwHistoryTextFlyCnt::SetInDoc( SwDoc* pDoc, bool )
586 ::sw::IShellCursorSupplier *const pISCS(pDoc->GetIShellCursorSupplier());
587 assert(pISCS);
588 ::sw::UndoRedoContext context(*pDoc, *pISCS);
589 m_pUndo->UndoImpl(context);
592 void SwHistoryTextFlyCnt::dumpAsXml(xmlTextWriterPtr pWriter) const
594 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHistoryTextFlyCnt"));
595 SwHistoryHint::dumpAsXml(pWriter);
597 if (m_pUndo)
599 m_pUndo->dumpAsXml(pWriter);
602 (void)xmlTextWriterEndElement(pWriter);
605 SwHistoryBookmark::SwHistoryBookmark(
606 const ::sw::mark::IMark& rBkmk,
607 bool bSavePos,
608 bool bSaveOtherPos)
609 : SwHistoryHint(HSTRY_BOOKMARK)
610 , m_aName(rBkmk.GetName())
611 , m_bHidden(false)
612 , m_nNode(bSavePos ?
613 rBkmk.GetMarkPos().GetNodeIndex() : SwNodeOffset(0))
614 , m_nOtherNode(bSaveOtherPos ?
615 rBkmk.GetOtherMarkPos().GetNodeIndex() : SwNodeOffset(0))
616 , m_nContent(bSavePos ?
617 rBkmk.GetMarkPos().GetContentIndex() : 0)
618 , m_nOtherContent(bSaveOtherPos ?
619 rBkmk.GetOtherMarkPos().GetContentIndex() :0)
620 , m_bSavePos(bSavePos)
621 , m_bSaveOtherPos(bSaveOtherPos)
622 , m_bHadOtherPos(rBkmk.IsExpanded())
623 , m_eBkmkType(IDocumentMarkAccess::GetType(rBkmk))
625 const ::sw::mark::IBookmark* const pBookmark = dynamic_cast< const ::sw::mark::IBookmark* >(&rBkmk);
626 if(!pBookmark)
627 return;
629 m_aKeycode = pBookmark->GetKeyCode();
630 m_aShortName = pBookmark->GetShortName();
631 m_bHidden = pBookmark->IsHidden();
632 m_aHideCondition = pBookmark->GetHideCondition();
634 ::sfx2::Metadatable const*const pMetadatable(
635 dynamic_cast< ::sfx2::Metadatable const* >(pBookmark));
636 if (pMetadatable)
638 m_pMetadataUndo = pMetadatable->CreateUndo();
642 void SwHistoryBookmark::SetInDoc( SwDoc* pDoc, bool )
644 ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
646 SwNodes& rNds = pDoc->GetNodes();
647 IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
648 std::optional<SwPaM> pPam;
649 ::sw::mark::IMark* pMark = nullptr;
651 if(m_bSavePos)
653 SwContentNode* const pContentNd = rNds[m_nNode]->GetContentNode();
654 OSL_ENSURE(pContentNd,
655 "<SwHistoryBookmark::SetInDoc(..)>"
656 " - wrong node for a mark");
658 // #111660# don't crash when nNode1 doesn't point to content node.
659 if(pContentNd)
660 pPam.emplace(*pContentNd, m_nContent);
662 else
664 pMark = *pMarkAccess->findMark(m_aName);
665 pPam.emplace(pMark->GetMarkPos());
668 if(m_bSaveOtherPos)
670 SwContentNode* const pContentNd = rNds[m_nOtherNode]->GetContentNode();
671 OSL_ENSURE(pContentNd,
672 "<SwHistoryBookmark::SetInDoc(..)>"
673 " - wrong node for a mark");
675 if (pPam && pContentNd)
677 pPam->SetMark();
678 pPam->GetMark()->Assign(*pContentNd, m_nOtherContent);
681 else if(m_bHadOtherPos)
683 if(!pMark)
684 pMark = *pMarkAccess->findMark(m_aName);
685 OSL_ENSURE(pMark->IsExpanded(),
686 "<SwHistoryBookmark::SetInDoc(..)>"
687 " - missing pos on old mark");
688 pPam->SetMark();
689 *pPam->GetMark() = pMark->GetOtherMarkPos();
692 if (!pPam)
693 return;
695 if ( pMark != nullptr )
697 pMarkAccess->deleteMark( pMark );
699 ::sw::mark::IBookmark* const pBookmark =
700 dynamic_cast<::sw::mark::IBookmark*>(
701 pMarkAccess->makeMark(*pPam, m_aName, m_eBkmkType, sw::mark::InsertMode::New));
702 if ( pBookmark == nullptr )
703 return;
705 pBookmark->SetKeyCode(m_aKeycode);
706 pBookmark->SetShortName(m_aShortName);
707 pBookmark->Hide(m_bHidden);
708 pBookmark->SetHideCondition(m_aHideCondition);
710 if (m_pMetadataUndo)
712 ::sfx2::Metadatable * const pMeta(
713 dynamic_cast< ::sfx2::Metadatable* >(pBookmark));
714 OSL_ENSURE(pMeta, "metadata undo, but not metadatable?");
715 if (pMeta)
717 pMeta->RestoreMetadata(m_pMetadataUndo);
722 bool SwHistoryBookmark::IsEqualBookmark(const ::sw::mark::IMark& rBkmk)
724 return m_nNode == rBkmk.GetMarkPos().GetNodeIndex()
725 && m_nContent == rBkmk.GetMarkPos().GetContentIndex()
726 && m_aName == rBkmk.GetName();
729 SwHistoryNoTextFieldmark::SwHistoryNoTextFieldmark(const ::sw::mark::IFieldmark& rFieldMark)
730 : SwHistoryHint(HSTRY_NOTEXTFIELDMARK)
731 , m_sType(rFieldMark.GetFieldname())
732 , m_nNode(rFieldMark.GetMarkStart().GetNodeIndex())
733 , m_nContent(rFieldMark.GetMarkStart().GetContentIndex())
737 void SwHistoryNoTextFieldmark::SetInDoc(SwDoc* pDoc, bool)
739 ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
741 SwNodes& rNds = pDoc->GetNodes();
742 std::optional<SwPaM> pPam;
744 const SwContentNode* pContentNd = rNds[m_nNode]->GetContentNode();
745 if(pContentNd)
746 pPam.emplace(*pContentNd, m_nContent);
748 if (pPam)
750 IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
751 pMarkAccess->makeNoTextFieldBookmark(*pPam, OUString(), m_sType);
755 void SwHistoryNoTextFieldmark::ResetInDoc(SwDoc& rDoc)
757 ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
759 SwNodes& rNds = rDoc.GetNodes();
760 std::optional<SwPaM> pPam;
762 const SwContentNode* pContentNd = rNds[m_nNode]->GetContentNode();
763 assert(pContentNd);
764 pPam.emplace(*pContentNd, m_nContent);
766 if (pPam)
768 IDocumentMarkAccess* pMarkAccess = rDoc.getIDocumentMarkAccess();
769 pMarkAccess->deleteFieldmarkAt(*pPam->GetPoint());
773 SwHistoryTextFieldmark::SwHistoryTextFieldmark(const ::sw::mark::IFieldmark& rFieldMark)
774 : SwHistoryHint(HSTRY_TEXTFIELDMARK)
775 , m_sName(rFieldMark.GetName())
776 , m_sType(rFieldMark.GetFieldname())
777 , m_nStartNode(rFieldMark.GetMarkStart().GetNodeIndex())
778 , m_nStartContent(rFieldMark.GetMarkStart().GetContentIndex())
779 , m_nEndNode(rFieldMark.GetMarkEnd().GetNodeIndex())
780 , m_nEndContent(rFieldMark.GetMarkEnd().GetContentIndex())
782 SwPosition const sepPos(sw::mark::FindFieldSep(rFieldMark));
783 m_nSepNode = sepPos.GetNodeIndex();
784 m_nSepContent = sepPos.GetContentIndex();
787 void SwHistoryTextFieldmark::SetInDoc(SwDoc* pDoc, bool)
789 ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
791 SwNodes& rNds = pDoc->GetNodes();
793 assert(rNds[m_nStartNode]->IsContentNode());
794 assert(rNds[m_nEndNode]->IsContentNode());
795 assert(rNds[m_nSepNode]->IsContentNode());
797 SwPaM const pam(*rNds[m_nStartNode]->GetContentNode(), m_nStartContent,
798 *rNds[m_nEndNode]->GetContentNode(),
799 // subtract 1 for the CH_TXT_ATR_FIELDEND itself,
800 // plus more if same node as other CH_TXT_ATR
801 m_nStartNode == m_nEndNode
802 ? (m_nEndContent - 3)
803 : m_nSepNode == m_nEndNode
804 ? (m_nEndContent - 2)
805 : (m_nEndContent - 1));
806 SwPosition const sepPos(*rNds[m_nSepNode]->GetContentNode(),
807 m_nStartNode == m_nSepNode ? (m_nSepContent - 1) : m_nSepContent);
809 IDocumentMarkAccess & rMarksAccess(*pDoc->getIDocumentMarkAccess());
810 rMarksAccess.makeFieldBookmark(pam, m_sName, m_sType, &sepPos);
813 void SwHistoryTextFieldmark::ResetInDoc(SwDoc& rDoc)
815 ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
817 SwNodes& rNds = rDoc.GetNodes();
819 assert(rNds[m_nStartNode]->IsContentNode());
820 assert(rNds[m_nEndNode]->IsContentNode());
821 assert(rNds[m_nSepNode]->IsContentNode());
823 SwPosition const pos(*rNds[m_nStartNode]->GetContentNode(), m_nStartContent);
825 IDocumentMarkAccess & rMarksAccess(*rDoc.getIDocumentMarkAccess());
826 rMarksAccess.deleteFieldmarkAt(pos);
829 SwHistorySetAttrSet::SwHistorySetAttrSet( const SfxItemSet& rSet,
830 SwNodeOffset nNodePos, const o3tl::sorted_vector<sal_uInt16> &rSetArr )
831 : SwHistoryHint( HSTRY_SETATTRSET )
832 , m_OldSet( rSet )
833 , m_ResetArray( 0, 4 )
834 , m_nNodeIndex( nNodePos )
836 SfxItemIter aIter( m_OldSet ), aOrigIter( rSet );
837 const SfxPoolItem* pItem = aIter.GetCurItem(),
838 * pOrigItem = aOrigIter.GetCurItem();
839 while (pItem && pOrigItem)
841 if( !rSetArr.count( pOrigItem->Which() ))
843 m_ResetArray.push_back( pOrigItem->Which() );
844 m_OldSet.ClearItem( pOrigItem->Which() );
846 else
848 switch ( pItem->Which() )
850 case RES_PAGEDESC:
851 static_cast<SwFormatPageDesc*>(
852 const_cast<SfxPoolItem*>(pItem))->ChgDefinedIn( nullptr );
853 break;
855 case RES_PARATR_DROP:
856 static_cast<SwFormatDrop*>(
857 const_cast<SfxPoolItem*>(pItem))->ChgDefinedIn(nullptr);
858 break;
860 case RES_BOXATR_FORMULA:
862 // When a formula is set, never save the value. It
863 // possibly must be recalculated!
864 // Save formulas always in plain text
865 m_OldSet.ClearItem( RES_BOXATR_VALUE );
867 SwTableBoxFormula& rNew =
868 *static_cast<SwTableBoxFormula*>(
869 const_cast<SfxPoolItem*>(pItem));
870 if ( rNew.IsIntrnlName() )
872 const SwTableBoxFormula& rOld =
873 rSet.Get( RES_BOXATR_FORMULA );
874 const SwNode* pNd = rOld.GetNodeOfFormula();
875 if ( pNd )
877 const SwTableNode* pTableNode
878 = pNd->FindTableNode();
879 if(pTableNode)
881 auto pCpyTable = const_cast<SwTable*>(&pTableNode->GetTable());
882 pCpyTable->SwitchFormulasToExternalRepresentation();
883 rNew.ChgDefinedIn(rOld.GetDefinedIn());
884 rNew.PtrToBoxNm(pCpyTable);
888 rNew.ChgDefinedIn( nullptr );
890 break;
894 pItem = aIter.NextItem();
895 pOrigItem = aOrigIter.NextItem();
899 void SwHistorySetAttrSet::SetInDoc( SwDoc* pDoc, bool )
901 ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
903 SwNode * pNode = pDoc->GetNodes()[ m_nNodeIndex ];
904 if ( pNode->IsContentNode() )
906 static_cast<SwContentNode*>(pNode)->SetAttr( m_OldSet );
907 if ( !m_ResetArray.empty() )
909 static_cast<SwContentNode*>(pNode)->ResetAttr( m_ResetArray );
912 else if ( pNode->IsTableNode() )
914 SwFormat& rFormat =
915 *static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat();
916 rFormat.SetFormatAttr( m_OldSet );
917 if ( !m_ResetArray.empty() )
919 rFormat.ResetFormatAttr( m_ResetArray.front() );
924 SwHistoryChangeFlyAnchor::SwHistoryChangeFlyAnchor(sw::SpzFrameFormat& rFormat)
925 : SwHistoryHint( HSTRY_CHGFLYANCHOR )
926 , m_rFormat(rFormat)
927 , m_nOldNodeIndex( rFormat.GetAnchor().GetAnchorNode()->GetIndex() )
928 , m_nOldContentIndex( (RndStdIds::FLY_AT_CHAR == rFormat.GetAnchor().GetAnchorId())
929 ? rFormat.GetAnchor().GetAnchorContentOffset()
930 : COMPLETE_STRING )
934 void SwHistoryChangeFlyAnchor::SetInDoc( SwDoc* pDoc, bool )
936 ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
938 // One would expect m_rFormat to only contain FlyFormats, given the name of
939 // this class, but apparently it is also used for DrawFormats.
940 if (!pDoc->GetSpzFrameFormats()->IsAlive(&m_rFormat)) // Format does still exist
941 return;
943 SwFormatAnchor aTmp( m_rFormat.GetAnchor() );
945 SwNode* pNd = pDoc->GetNodes()[ m_nOldNodeIndex ];
946 SwContentNode* pCNd = pNd->GetContentNode();
947 SwPosition aPos( *pNd );
948 if ( COMPLETE_STRING != m_nOldContentIndex )
949 aPos.SetContent( m_nOldContentIndex );
950 aTmp.SetAnchor( &aPos );
952 // so the Layout does not get confused
953 if (!pCNd->getLayoutFrame(pDoc->getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr))
955 m_rFormat.DelFrames();
958 m_rFormat.SetFormatAttr( aTmp );
961 SwHistoryChangeFlyChain::SwHistoryChangeFlyChain( SwFlyFrameFormat& rFormat,
962 const SwFormatChain& rAttr )
963 : SwHistoryHint( HSTRY_CHGFLYCHAIN )
964 , m_pPrevFormat( rAttr.GetPrev() )
965 , m_pNextFormat( rAttr.GetNext() )
966 , m_pFlyFormat( &rFormat )
970 void SwHistoryChangeFlyChain::SetInDoc( SwDoc* pDoc, bool )
972 if (!pDoc->GetSpzFrameFormats()->IsAlive(m_pFlyFormat))
973 return;
975 SwFormatChain aChain;
977 if (m_pPrevFormat &&
978 pDoc->GetSpzFrameFormats()->IsAlive(m_pPrevFormat))
980 aChain.SetPrev( m_pPrevFormat );
981 SwFormatChain aTmp( m_pPrevFormat->GetChain() );
982 aTmp.SetNext( m_pFlyFormat );
983 m_pPrevFormat->SetFormatAttr( aTmp );
986 if (m_pNextFormat &&
987 pDoc->GetSpzFrameFormats()->IsAlive(m_pNextFormat))
989 aChain.SetNext( m_pNextFormat );
990 SwFormatChain aTmp( m_pNextFormat->GetChain() );
991 aTmp.SetPrev( m_pFlyFormat );
992 m_pNextFormat->SetFormatAttr( aTmp );
995 if ( aChain.GetNext() || aChain.GetPrev() )
997 m_pFlyFormat->SetFormatAttr( aChain );
1001 // -> #i27615#
1002 SwHistoryChangeCharFormat::SwHistoryChangeCharFormat(SfxItemSet aSet,
1003 OUString sFormat)
1004 : SwHistoryHint(HSTRY_CHGCHARFMT)
1005 , m_OldSet(std::move(aSet)), m_Format(std::move(sFormat))
1009 void SwHistoryChangeCharFormat::SetInDoc(SwDoc * pDoc, bool )
1011 SwCharFormat * pCharFormat = pDoc->FindCharFormatByName(m_Format);
1013 if (pCharFormat)
1015 pCharFormat->SetFormatAttr(m_OldSet);
1018 // <- #i27615#
1020 SwHistory::SwHistory()
1021 : m_nEndDiff( 0 )
1025 SwHistory::~SwHistory()
1029 void SwHistory::Add(
1030 const SfxPoolItem* pOldValue,
1031 const SfxPoolItem* pNewValue,
1032 SwNodeOffset nNodeIdx)
1034 OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
1035 const sal_uInt16 nWhich(pNewValue->Which());
1037 // excluded values
1038 if(nWhich == RES_TXTATR_FIELD || nWhich == RES_TXTATR_ANNOTATION)
1040 return;
1043 // no default Attribute?
1044 std::unique_ptr<SwHistoryHint> pHt;
1046 // To be able to include the DrawingLayer FillItems something more
1047 // general has to be done to check if an Item is default than to check
1048 // if its pointer equals that in Writer's global PoolDefaults (held in
1049 // aAttrTab and used to fill the pool defaults in Writer - looks as if
1050 // Writer is *older* than the SfxItemPool ?). I checked the possibility to
1051 // get the SfxItemPool here (works), but decided to use the SfxPoolItem's
1052 // global tooling aka IsDefaultItem(const SfxPoolItem*) for now
1053 if(pOldValue && !IsDefaultItem(pOldValue))
1055 pHt.reset( new SwHistorySetFormat( pOldValue, nNodeIdx ) );
1057 else
1059 pHt.reset( new SwHistoryResetFormat( pNewValue, nNodeIdx ) );
1062 m_SwpHstry.push_back( std::move(pHt) );
1065 // FIXME: refactor the following "Add" methods (DRY)?
1066 void SwHistory::Add( SwTextAttr* pHint, SwNodeOffset nNodeIdx, bool bNewAttr )
1068 OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
1070 std::unique_ptr<SwHistoryHint> pHt;
1071 if( !bNewAttr )
1073 switch ( pHint->Which() )
1075 case RES_TXTATR_FTN:
1076 pHt.reset( new SwHistorySetFootnote(
1077 static_cast<SwTextFootnote*>(pHint), nNodeIdx ) );
1078 break;
1079 case RES_TXTATR_FLYCNT:
1080 pHt.reset( new SwHistoryTextFlyCnt( static_cast<SwTextFlyCnt*>(pHint)
1081 ->GetFlyCnt().GetFrameFormat() ) );
1082 break;
1083 case RES_TXTATR_FIELD:
1084 case RES_TXTATR_ANNOTATION:
1085 pHt.reset( new SwHistorySetTextField(
1086 static_txtattr_cast<SwTextField*>(pHint), nNodeIdx) );
1087 break;
1088 case RES_TXTATR_TOXMARK:
1089 pHt.reset( new SwHistorySetTOXMark(
1090 static_txtattr_cast<SwTextTOXMark*>(pHint), nNodeIdx) );
1091 break;
1092 case RES_TXTATR_REFMARK:
1093 pHt.reset( new SwHistorySetRefMark(
1094 static_txtattr_cast<SwTextRefMark*>(pHint), nNodeIdx) );
1095 break;
1096 default:
1097 pHt.reset( new SwHistorySetText( pHint, nNodeIdx ) );
1100 else
1102 pHt.reset( new SwHistoryResetText( pHint->Which(), pHint->GetStart(),
1103 pHint->GetAnyEnd(), nNodeIdx ) );
1105 m_SwpHstry.push_back( std::move(pHt) );
1108 void SwHistory::Add( SwFormatColl* pColl, SwNodeOffset nNodeIdx, SwNodeType nWhichNd )
1110 OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
1112 std::unique_ptr<SwHistoryHint> pHt(
1113 new SwHistoryChangeFormatColl( pColl, nNodeIdx, nWhichNd ));
1114 m_SwpHstry.push_back( std::move(pHt) );
1117 void SwHistory::Add(const ::sw::mark::IMark& rBkmk, bool bSavePos, bool bSaveOtherPos)
1119 OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
1121 std::unique_ptr<SwHistoryHint> pHt;
1123 switch (IDocumentMarkAccess::GetType(rBkmk))
1125 case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK:
1126 case IDocumentMarkAccess::MarkType::DATE_FIELDMARK:
1127 assert(bSavePos && bSaveOtherPos); // must be deleted completely!
1128 pHt.reset(new SwHistoryTextFieldmark(dynamic_cast<sw::mark::IFieldmark const&>(rBkmk)));
1129 break;
1130 case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK:
1131 case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK:
1132 assert(bSavePos && bSaveOtherPos); // must be deleted completely!
1133 pHt.reset(new SwHistoryNoTextFieldmark(dynamic_cast<sw::mark::IFieldmark const&>(rBkmk)));
1134 break;
1135 default:
1136 pHt.reset(new SwHistoryBookmark(rBkmk, bSavePos, bSaveOtherPos));
1137 break;
1140 assert(pHt);
1141 m_SwpHstry.push_back( std::move(pHt) );
1144 void SwHistory::AddChangeFlyAnchor(sw::SpzFrameFormat& rFormat)
1146 std::unique_ptr<SwHistoryHint> pHt(new SwHistoryChangeFlyAnchor(rFormat));
1147 m_SwpHstry.push_back( std::move(pHt) );
1150 void SwHistory::AddDeleteFly(SwFrameFormat& rFormat, sal_uInt16& rSetPos)
1152 OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
1154 const sal_uInt16 nWh = rFormat.Which();
1155 (void) nWh;
1156 // only Flys!
1157 assert((RES_FLYFRMFMT == nWh && dynamic_cast<SwFlyFrameFormat*>(&rFormat))
1158 || (RES_DRAWFRMFMT == nWh && dynamic_cast<SwDrawFrameFormat*>(&rFormat)));
1160 std::unique_ptr<SwHistoryHint> pHint(new SwHistoryTextFlyCnt( &rFormat ));
1161 m_SwpHstry.push_back( std::move(pHint) );
1163 if( const SwFormatChain* pChainItem = rFormat.GetItemIfSet( RES_CHAIN, false ) )
1165 assert(RES_FLYFRMFMT == nWh); // not supported on SdrObjects
1166 if( pChainItem->GetNext() || pChainItem->GetPrev() )
1168 std::unique_ptr<SwHistoryHint> pHt(
1169 new SwHistoryChangeFlyChain(static_cast<SwFlyFrameFormat&>(rFormat), *pChainItem));
1170 m_SwpHstry.insert( m_SwpHstry.begin() + rSetPos++, std::move(pHt) );
1171 if ( pChainItem->GetNext() )
1173 SwFormatChain aTmp( pChainItem->GetNext()->GetChain() );
1174 aTmp.SetPrev( nullptr );
1175 pChainItem->GetNext()->SetFormatAttr( aTmp );
1177 if ( pChainItem->GetPrev() )
1179 SwFormatChain aTmp( pChainItem->GetPrev()->GetChain() );
1180 aTmp.SetNext( nullptr );
1181 pChainItem->GetPrev()->SetFormatAttr( aTmp );
1184 rFormat.ResetFormatAttr( RES_CHAIN );
1189 void SwHistory::Add( const SwTextFootnote& rFootnote )
1191 std::unique_ptr<SwHistoryHint> pHt(new SwHistorySetFootnote( rFootnote ));
1192 m_SwpHstry.push_back( std::move(pHt) );
1195 // #i27615#
1196 void SwHistory::Add(const SfxItemSet & rSet, const SwCharFormat & rFormat)
1198 std::unique_ptr<SwHistoryHint> pHt(new SwHistoryChangeCharFormat(rSet, rFormat.GetName()));
1199 m_SwpHstry.push_back( std::move(pHt) );
1202 bool SwHistory::Rollback( SwDoc* pDoc, sal_uInt16 nStart )
1204 if ( !Count() )
1205 return false;
1207 for ( sal_uInt16 i = Count(); i > nStart ; )
1209 SwHistoryHint * pHHt = m_SwpHstry[ --i ].get();
1210 pHHt->SetInDoc( pDoc, false );
1212 m_SwpHstry.erase( m_SwpHstry.begin() + nStart, m_SwpHstry.end() );
1213 m_nEndDiff = 0;
1214 return true;
1217 bool SwHistory::TmpRollback( SwDoc* pDoc, sal_uInt16 nStart, bool bToFirst )
1219 sal_uInt16 nEnd = Count() - m_nEndDiff;
1220 if ( !Count() || !nEnd || nStart >= nEnd )
1221 return false;
1223 if ( bToFirst )
1225 for ( ; nEnd > nStart; ++m_nEndDiff )
1227 SwHistoryHint* pHHt = m_SwpHstry[ --nEnd ].get();
1228 pHHt->SetInDoc( pDoc, true );
1231 else
1233 for ( ; nStart < nEnd; ++m_nEndDiff, ++nStart )
1235 SwHistoryHint* pHHt = m_SwpHstry[ nStart ].get();
1236 pHHt->SetInDoc( pDoc, true );
1239 return true;
1242 sal_uInt16 SwHistory::SetTmpEnd( sal_uInt16 nNewTmpEnd )
1244 OSL_ENSURE( nNewTmpEnd <= Count(), "SwHistory::SetTmpEnd: out of bounds" );
1246 const sal_uInt16 nOld = Count() - m_nEndDiff;
1247 m_nEndDiff = Count() - nNewTmpEnd;
1249 // for every SwHistoryFlyCnt, call the Redo of its UndoObject.
1250 // this saves the formats of the flys!
1251 for ( sal_uInt16 n = nOld; n < nNewTmpEnd; n++ )
1253 if ( HSTRY_FLYCNT == (*this)[ n ]->Which() )
1255 static_cast<SwHistoryTextFlyCnt*>((*this)[ n ])
1256 ->GetUDelLFormat()->RedoForRollback();
1260 return nOld;
1263 void SwHistory::CopyFormatAttr(
1264 const SfxItemSet& rSet,
1265 SwNodeOffset nNodeIdx)
1267 if(!rSet.Count())
1268 return;
1270 SfxItemIter aIter(rSet);
1271 const SfxPoolItem* pItem = aIter.GetCurItem();
1274 if(!IsInvalidItem(pItem))
1276 Add(pItem, pItem, nNodeIdx);
1279 pItem = aIter.NextItem();
1281 } while(pItem);
1284 void SwHistory::dumpAsXml(xmlTextWriterPtr pWriter) const
1286 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHistory"));
1287 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
1289 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_SwpHstry"));
1290 for (const auto& pHistory : m_SwpHstry)
1292 pHistory->dumpAsXml(pWriter);
1294 (void)xmlTextWriterEndElement(pWriter);
1296 (void)xmlTextWriterEndElement(pWriter);
1299 void SwHistory::CopyAttr(
1300 SwpHints const * pHts,
1301 const SwNodeOffset nNodeIdx,
1302 const sal_Int32 nStart,
1303 const sal_Int32 nEnd,
1304 const bool bCopyFields )
1306 if( !pHts )
1307 return;
1309 // copy all attributes of the TextNode in the area from nStart to nEnd
1310 SwTextAttr* pHt;
1311 for( size_t n = 0; n < pHts->Count(); ++n )
1313 // nAttrStt must even be set when !pEndIdx
1314 pHt = pHts->Get(n);
1315 const sal_Int32 nAttrStt = pHt->GetStart();
1316 const sal_Int32 * pEndIdx = pHt->GetEnd();
1317 if( nullptr != pEndIdx && nAttrStt > nEnd )
1318 break;
1320 // never copy Flys and Footnote !!
1321 bool bNextAttr = false;
1322 switch( pHt->Which() )
1324 case RES_TXTATR_FIELD:
1325 case RES_TXTATR_ANNOTATION:
1326 case RES_TXTATR_INPUTFIELD:
1327 if( !bCopyFields )
1328 bNextAttr = true;
1329 break;
1330 case RES_TXTATR_FLYCNT:
1331 case RES_TXTATR_FTN:
1332 bNextAttr = true;
1333 break;
1336 if( bNextAttr )
1337 continue;
1339 // save all attributes that are somehow in this area
1340 if ( nStart <= nAttrStt )
1342 if ( nEnd > nAttrStt )
1344 Add( pHt, nNodeIdx, false );
1347 else if ( pEndIdx && nStart < *pEndIdx )
1349 Add( pHt, nNodeIdx, false );
1354 // Class to register the history at a Node, Format, HintsArray, ...
1355 SwRegHistory::SwRegHistory( SwHistory* pHst )
1356 : SwClient( nullptr )
1357 , m_pHistory( pHst )
1358 , m_nNodeIndex( NODE_OFFSET_MAX )
1360 MakeSetWhichIds();
1363 SwRegHistory::SwRegHistory( sw::BroadcastingModify* pRegIn, const SwNode& rNd,
1364 SwHistory* pHst )
1365 : SwClient( pRegIn )
1366 , m_pHistory( pHst )
1367 , m_nNodeIndex( rNd.GetIndex() )
1369 MakeSetWhichIds();
1372 SwRegHistory::SwRegHistory( const SwNode& rNd, SwHistory* pHst )
1373 : SwClient( nullptr )
1374 , m_pHistory( pHst )
1375 , m_nNodeIndex( rNd.GetIndex() )
1377 MakeSetWhichIds();
1380 void SwRegHistory::SwClientNotify(const SwModify&, const SfxHint& rHint)
1382 if (rHint.GetId() != SfxHintId::SwLegacyModify)
1383 return;
1384 auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
1385 if ( !(m_pHistory && pLegacyHint->m_pNew && pLegacyHint->m_pOld != pLegacyHint->m_pNew) )
1386 return;
1388 if ( pLegacyHint->m_pNew->Which() < POOLATTR_END )
1390 if(RES_UPDATE_ATTR == pLegacyHint->m_pNew->Which())
1392 m_pHistory->Add(pLegacyHint->m_pOld, pLegacyHint->m_pNew, m_nNodeIndex);
1394 else
1396 OSL_ENSURE(false, "Unexpected update attribute (!)");
1399 else if (pLegacyHint->m_pOld && RES_ATTRSET_CHG == pLegacyHint->m_pNew->Which())
1401 std::unique_ptr<SwHistoryHint> pNewHstr;
1402 const SfxItemSet& rSet = *static_cast< const SwAttrSetChg* >(pLegacyHint->m_pOld)->GetChgSet();
1404 if ( 1 < rSet.Count() )
1406 pNewHstr.reset( new SwHistorySetAttrSet( rSet, m_nNodeIndex, m_WhichIdSet ) );
1408 else if (const SfxPoolItem* pItem = SfxItemIter(rSet).GetCurItem())
1410 if ( m_WhichIdSet.count( pItem->Which() ) )
1412 pNewHstr.reset( new SwHistorySetFormat( pItem, m_nNodeIndex ) );
1414 else
1416 pNewHstr.reset( new SwHistoryResetFormat( pItem, m_nNodeIndex ) );
1420 if (pNewHstr)
1421 m_pHistory->m_SwpHstry.push_back( std::move(pNewHstr) );
1425 void SwRegHistory::AddHint( SwTextAttr* pHt, const bool bNew )
1427 m_pHistory->Add( pHt, m_nNodeIndex, bNew );
1430 bool SwRegHistory::InsertItems( const SfxItemSet& rSet,
1431 sal_Int32 const nStart, sal_Int32 const nEnd, SetAttrMode const nFlags,
1432 SwTextAttr **ppNewTextAttr )
1434 if( !rSet.Count() )
1435 return false;
1437 SwTextNode * const pTextNode =
1438 dynamic_cast<SwTextNode *>(GetRegisteredIn());
1440 OSL_ENSURE(pTextNode, "SwRegHistory not registered at text node?");
1441 if (!pTextNode)
1442 return false;
1444 if (m_pHistory)
1446 pTextNode->GetOrCreateSwpHints().Register(this);
1449 const bool bInserted = pTextNode->SetAttr( rSet, nStart, nEnd, nFlags, ppNewTextAttr );
1451 // Caution: The array can be deleted when inserting an attribute!
1452 // This can happen when the value that should be added first deletes
1453 // an existing attribute but does not need to be added itself because
1454 // the paragraph attributes are identical
1455 // ( -> bForgetAttr in SwpHints::Insert )
1456 if ( pTextNode->GetpSwpHints() && m_pHistory )
1458 pTextNode->GetpSwpHints()->DeRegister();
1461 #ifndef NDEBUG
1462 if ( m_pHistory && bInserted )
1464 SfxItemIter aIter(rSet);
1465 for (SfxPoolItem const* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
1466 { // check that the history recorded a hint to reset every item
1467 sal_uInt16 const nWhich(pItem->Which());
1468 sal_uInt16 const nExpected(
1469 (isCHRATR(nWhich) || RES_TXTATR_UNKNOWN_CONTAINER == nWhich)
1470 ? RES_TXTATR_AUTOFMT
1471 : nWhich);
1472 if (RES_TXTATR_AUTOFMT == nExpected)
1473 continue; // special case, may get set on text node itself
1474 // tdf#105077 even worse, node's set could cause
1475 // nothing at all to be inserted
1476 assert(std::any_of(
1477 m_pHistory->m_SwpHstry.begin(), m_pHistory->m_SwpHstry.end(),
1478 [nExpected](std::unique_ptr<SwHistoryHint> const& pHint) -> bool {
1479 SwHistoryResetText const*const pReset(
1480 dynamic_cast<SwHistoryResetText const*>(pHint.get()));
1481 return pReset && (pReset->GetWhich() == nExpected);
1482 }));
1485 #endif
1487 return bInserted;
1490 void SwRegHistory::RegisterInModify( sw::BroadcastingModify* pRegIn, const SwNode& rNd )
1492 if ( m_pHistory && pRegIn )
1494 pRegIn->Add( this );
1495 m_nNodeIndex = rNd.GetIndex();
1496 MakeSetWhichIds();
1498 else
1500 m_WhichIdSet.clear();
1504 void SwRegHistory::MakeSetWhichIds()
1506 if (!m_pHistory) return;
1508 m_WhichIdSet.clear();
1510 if( !GetRegisteredIn() )
1511 return;
1513 const SfxItemSet* pSet = nullptr;
1514 if( auto pContentNode = dynamic_cast< const SwContentNode *>( GetRegisteredIn() ) )
1516 pSet = pContentNode->GetpSwAttrSet();
1518 else if ( auto pSwFormat = dynamic_cast< const SwFormat *>( GetRegisteredIn() ) )
1520 pSet = &pSwFormat->GetAttrSet();
1522 if( pSet && pSet->Count() )
1524 SfxItemIter aIter( *pSet );
1525 for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
1527 sal_uInt16 nW = pItem->Which();
1528 m_WhichIdSet.insert( nW );
1532 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */