1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <rolbck.hxx>
21 
22 #include <libxml/xmlwriter.h>
23 
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 <bookmrk.hxx>
59 #include <frameformats.hxx>
60 #include <memory>
61 
GetDescription() const62 OUString SwHistoryHint::GetDescription() const
63 {
64     return OUString();
65 }
66 
dumpAsXml(xmlTextWriterPtr pWriter) const67 void SwHistoryHint::dumpAsXml(xmlTextWriterPtr pWriter) const
68 {
69     (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHistoryHint"));
70     (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
71     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("symbol"), BAD_CAST(typeid(*this).name()));
72     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_eWhichId"),
73                                 BAD_CAST(OString::number(m_eWhichId).getStr()));
74     (void)xmlTextWriterEndElement(pWriter);
75 }
76 
SwHistorySetFormat(const SfxPoolItem * pFormatHt,sal_uLong nNd)77 SwHistorySetFormat::SwHistorySetFormat( const SfxPoolItem* pFormatHt, sal_uLong nNd )
78     :  SwHistoryHint( HSTRY_SETFMTHNT )
79     ,  m_pAttr( pFormatHt->Clone() )
80     ,  m_nNodeIndex( nNd )
81 {
82     switch ( m_pAttr->Which() )
83     {
84         case RES_PAGEDESC:
85             static_cast<SwFormatPageDesc&>(*m_pAttr).ChgDefinedIn( nullptr );
86             break;
87         case RES_PARATR_DROP:
88             static_cast<SwFormatDrop&>(*m_pAttr).ChgDefinedIn(nullptr);
89             break;
90         case RES_BOXATR_FORMULA:
91         {
92             // save formulas always in plain text
93             SwTableBoxFormula& rNew = static_cast<SwTableBoxFormula&>(*m_pAttr);
94             if ( rNew.IsIntrnlName() )
95             {
96                 const SwTableBoxFormula& rOld =
97                     *static_cast<const SwTableBoxFormula*>(pFormatHt);
98                 const SwNode* pNd = rOld.GetNodeOfFormula();
99                 if ( pNd )
100                 {
101                     const SwTableNode* pTableNode = pNd->FindTableNode();
102                     if (pTableNode)
103                     {
104                         SwTableFormulaUpdate aMsgHint( &pTableNode->GetTable() );
105                         aMsgHint.m_eFlags = TBL_BOXNAME;
106                         rNew.ChgDefinedIn( rOld.GetDefinedIn() );
107                         rNew.ChangeState( &aMsgHint );
108                     }
109                 }
110             }
111             rNew.ChgDefinedIn( nullptr );
112         }
113         break;
114     }
115 }
116 
GetDescription() const117 OUString SwHistorySetFormat::GetDescription() const
118 {
119     OUString aResult;
120 
121     switch (m_pAttr->Which())
122     {
123     case RES_BREAK:
124         switch (static_cast<SvxFormatBreakItem &>(*m_pAttr).GetBreak())
125         {
126         case SvxBreak::PageBefore:
127         case SvxBreak::PageAfter:
128         case SvxBreak::PageBoth:
129             aResult = SwResId(STR_UNDO_PAGEBREAKS);
130 
131             break;
132         case SvxBreak::ColumnBefore:
133         case SvxBreak::ColumnAfter:
134         case SvxBreak::ColumnBoth:
135             aResult = SwResId(STR_UNDO_COLBRKS);
136 
137             break;
138         default:
139             break;
140         }
141         break;
142     default:
143         break;
144     }
145 
146     return aResult;
147 }
148 
dumpAsXml(xmlTextWriterPtr pWriter) const149 void SwHistorySetFormat::dumpAsXml(xmlTextWriterPtr pWriter) const
150 {
151     (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHistorySetFormat"));
152     (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nNodeIndex"),
153                                 BAD_CAST(OString::number(m_nNodeIndex).getStr()));
154     SwHistoryHint::dumpAsXml(pWriter);
155 
156     if (m_pAttr)
157     {
158         m_pAttr->dumpAsXml(pWriter);
159     }
160 
161     (void)xmlTextWriterEndElement(pWriter);
162 }
163 
SetInDoc(SwDoc * pDoc,bool bTmpSet)164 void SwHistorySetFormat::SetInDoc( SwDoc* pDoc, bool bTmpSet )
165 {
166     SwNode * pNode = pDoc->GetNodes()[ m_nNodeIndex ];
167     if ( pNode->IsContentNode() )
168     {
169         static_cast<SwContentNode*>(pNode)->SetAttr( *m_pAttr );
170     }
171     else if ( pNode->IsTableNode() )
172     {
173         static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat()->SetFormatAttr(
174                 *m_pAttr );
175     }
176     else if ( pNode->IsStartNode() && (SwTableBoxStartNode ==
177                 static_cast<SwStartNode*>(pNode)->GetStartNodeType()) )
178     {
179         SwTableNode* pTNd = pNode->FindTableNode();
180         if ( pTNd )
181         {
182             SwTableBox* pBox = pTNd->GetTable().GetTableBox( m_nNodeIndex );
183             if (pBox)
184             {
185                 pBox->ClaimFrameFormat()->SetFormatAttr( *m_pAttr );
186             }
187         }
188     }
189 
190     if ( !bTmpSet )
191     {
192         m_pAttr.reset();
193     }
194 }
195 
~SwHistorySetFormat()196 SwHistorySetFormat::~SwHistorySetFormat()
197 {
198 }
199 
SwHistoryResetFormat(const SfxPoolItem * pFormatHt,sal_uLong nNodeIdx)200 SwHistoryResetFormat::SwHistoryResetFormat(const SfxPoolItem* pFormatHt, sal_uLong nNodeIdx)
201     : SwHistoryHint( HSTRY_RESETFMTHNT )
202     , m_nNodeIndex( nNodeIdx )
203     , m_nWhich( pFormatHt->Which() )
204 {
205 }
206 
SetInDoc(SwDoc * pDoc,bool)207 void SwHistoryResetFormat::SetInDoc( SwDoc* pDoc, bool )
208 {
209     SwNode * pNode = pDoc->GetNodes()[ m_nNodeIndex ];
210     if ( pNode->IsContentNode() )
211     {
212         static_cast<SwContentNode*>(pNode)->ResetAttr( m_nWhich );
213     }
214     else if ( pNode->IsTableNode() )
215     {
216         static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat()->
217             ResetFormatAttr( m_nWhich );
218     }
219 }
220 
SwHistorySetText(SwTextAttr * pTextHt,sal_uLong nNodePos)221 SwHistorySetText::SwHistorySetText( SwTextAttr* pTextHt, sal_uLong nNodePos )
222     : SwHistoryHint( HSTRY_SETTXTHNT )
223     , m_nNodeIndex( nNodePos )
224     , m_nStart( pTextHt->GetStart() )
225     , m_nEnd( pTextHt->GetAnyEnd() )
226     , m_bFormatIgnoreStart(pTextHt->IsFormatIgnoreStart())
227     , m_bFormatIgnoreEnd  (pTextHt->IsFormatIgnoreEnd  ())
228 {
229     // Caution: the following attributes generate no format attributes:
230     //  - NoLineBreak, NoHyphen, Inserted, Deleted
231     // These cases must be handled separately !!!
232 
233     // a little bit complicated but works: first assign a copy of the
234     // default value and afterwards the values from text attribute
235     if ( RES_TXTATR_CHARFMT == pTextHt->Which() )
236     {
237         m_pAttr.reset( new SwFormatCharFormat( pTextHt->GetCharFormat().GetCharFormat() ) );
238     }
239     else
240     {
241         m_pAttr.reset( pTextHt->GetAttr().Clone() );
242     }
243 }
244 
~SwHistorySetText()245 SwHistorySetText::~SwHistorySetText()
246 {
247 }
248 
SetInDoc(SwDoc * pDoc,bool)249 void SwHistorySetText::SetInDoc( SwDoc* pDoc, bool )
250 {
251     if (!m_pAttr)
252         return;
253 
254     if ( RES_TXTATR_CHARFMT == m_pAttr->Which() )
255     {
256         // ask the Doc if the CharFormat still exists
257         if (!pDoc->GetCharFormats()->IsAlive(static_cast<SwFormatCharFormat&>(*m_pAttr).GetCharFormat()))
258             return; // do not set, format does not exist
259     }
260 
261     SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
262     OSL_ENSURE( pTextNd, "SwHistorySetText::SetInDoc: not a TextNode" );
263 
264     if ( !pTextNd )
265         return;
266 
267     SwTextAttr *const pAttr = pTextNd->InsertItem(*m_pAttr, m_nStart, m_nEnd,
268                     SetAttrMode::NOTXTATRCHR |
269                     SetAttrMode::NOHINTADJUST );
270     // shouldn't be possible to hit any error/merging path from here
271     assert(pAttr);
272     if (m_bFormatIgnoreStart)
273     {
274         pAttr->SetFormatIgnoreStart(true);
275     }
276     if (m_bFormatIgnoreEnd)
277     {
278         pAttr->SetFormatIgnoreEnd(true);
279     }
280 }
281 
SwHistorySetTextField(const SwTextField * pTextField,sal_uLong nNodePos)282 SwHistorySetTextField::SwHistorySetTextField( const SwTextField* pTextField, sal_uLong nNodePos )
283     : SwHistoryHint( HSTRY_SETTXTFLDHNT )
284     , m_pField( new SwFormatField( *pTextField->GetFormatField().GetField() ) )
285 {
286     // only copy if not Sys-FieldType
287     SwDoc& rDoc = pTextField->GetTextNode().GetDoc();
288 
289     m_nFieldWhich = m_pField->GetField()->GetTyp()->Which();
290     if (m_nFieldWhich == SwFieldIds::Database ||
291         m_nFieldWhich == SwFieldIds::User ||
292         m_nFieldWhich == SwFieldIds::SetExp ||
293         m_nFieldWhich == SwFieldIds::Dde ||
294         !rDoc.getIDocumentFieldsAccess().GetSysFieldType( m_nFieldWhich ))
295     {
296         m_pFieldType = m_pField->GetField()->GetTyp()->Copy();
297         m_pField->GetField()->ChgTyp( m_pFieldType.get() ); // change field type
298     }
299     m_nNodeIndex = nNodePos;
300     m_nPos = pTextField->GetStart();
301 }
302 
GetDescription() const303 OUString SwHistorySetTextField::GetDescription() const
304 {
305     return m_pField->GetField()->GetDescription();
306 }
307 
~SwHistorySetTextField()308 SwHistorySetTextField::~SwHistorySetTextField()
309 {
310 }
311 
SetInDoc(SwDoc * pDoc,bool)312 void SwHistorySetTextField::SetInDoc( SwDoc* pDoc, bool )
313 {
314     if (!m_pField)
315         return;
316 
317     SwFieldType* pNewFieldType = m_pFieldType.get();
318     if ( !pNewFieldType )
319     {
320         pNewFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType( m_nFieldWhich );
321     }
322     else
323     {
324         // register type with the document
325         pNewFieldType = pDoc->getIDocumentFieldsAccess().InsertFieldType( *m_pFieldType );
326     }
327 
328     m_pField->GetField()->ChgTyp( pNewFieldType ); // change field type
329 
330     SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
331     OSL_ENSURE( pTextNd, "SwHistorySetTextField: no TextNode" );
332 
333     if ( pTextNd )
334     {
335         pTextNd->InsertItem( *m_pField, m_nPos, m_nPos,
336                     SetAttrMode::NOTXTATRCHR );
337     }
338 }
339 
SwHistorySetRefMark(const SwTextRefMark * pTextHt,sal_uLong nNodePos)340 SwHistorySetRefMark::SwHistorySetRefMark( const SwTextRefMark* pTextHt, sal_uLong nNodePos )
341     : SwHistoryHint( HSTRY_SETREFMARKHNT )
342     , m_RefName( pTextHt->GetRefMark().GetRefName() )
343     , m_nNodeIndex( nNodePos )
344     , m_nStart( pTextHt->GetStart() )
345     , m_nEnd( pTextHt->GetAnyEnd() )
346 {
347 }
348 
SetInDoc(SwDoc * pDoc,bool)349 void SwHistorySetRefMark::SetInDoc( SwDoc* pDoc, bool )
350 {
351     SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
352     OSL_ENSURE( pTextNd, "SwHistorySetRefMark: no TextNode" );
353     if ( !pTextNd )
354         return;
355 
356     SwFormatRefMark aRefMark( m_RefName );
357 
358     // if a reference mark without an end already exists here: must not insert!
359     if ( m_nStart != m_nEnd ||
360          !pTextNd->GetTextAttrForCharAt( m_nStart, RES_TXTATR_REFMARK ) )
361     {
362         pTextNd->InsertItem( aRefMark, m_nStart, m_nEnd,
363                             SetAttrMode::NOTXTATRCHR );
364     }
365 }
366 
SwHistorySetTOXMark(const SwTextTOXMark * pTextHt,sal_uLong nNodePos)367 SwHistorySetTOXMark::SwHistorySetTOXMark( const SwTextTOXMark* pTextHt, sal_uLong nNodePos )
368     : SwHistoryHint( HSTRY_SETTOXMARKHNT )
369     , m_TOXMark( pTextHt->GetTOXMark() )
370     , m_TOXName( m_TOXMark.GetTOXType()->GetTypeName() )
371     , m_eTOXTypes( m_TOXMark.GetTOXType()->GetType() )
372     , m_nNodeIndex( nNodePos )
373     , m_nStart( pTextHt->GetStart() )
374     , m_nEnd( pTextHt->GetAnyEnd() )
375 {
376     static_cast<SvtListener*>(&m_TOXMark)->EndListeningAll();
377 }
378 
GetSwTOXType(SwDoc & rDoc,TOXTypes eTOXTypes,const OUString & rTOXName)379 SwTOXType* SwHistorySetTOXMark::GetSwTOXType(SwDoc& rDoc, TOXTypes eTOXTypes, const OUString& rTOXName)
380 {
381     // search for respective TOX type
382     const sal_uInt16 nCnt = rDoc.GetTOXTypeCount(eTOXTypes);
383     SwTOXType* pToxType = nullptr;
384     for ( sal_uInt16 n = 0; n < nCnt; ++n )
385     {
386         pToxType = const_cast<SwTOXType*>(rDoc.GetTOXType(eTOXTypes, n));
387         if (pToxType->GetTypeName() == rTOXName)
388             break;
389         pToxType = nullptr;
390     }
391 
392     if ( !pToxType )  // TOX type not found, create new
393     {
394         pToxType = const_cast<SwTOXType*>(
395                 rDoc.InsertTOXType(SwTOXType(rDoc, eTOXTypes, rTOXName)));
396     }
397 
398     return pToxType;
399 }
400 
SetInDoc(SwDoc * pDoc,bool)401 void SwHistorySetTOXMark::SetInDoc( SwDoc* pDoc, bool )
402 {
403     SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
404     OSL_ENSURE( pTextNd, "SwHistorySetTOXMark: no TextNode" );
405     if ( !pTextNd )
406         return;
407 
408     SwTOXType* pToxType = GetSwTOXType(*pDoc, m_eTOXTypes, m_TOXName);
409 
410     SwTOXMark aNew( m_TOXMark );
411     aNew.RegisterToTOXType( *pToxType );
412 
413     pTextNd->InsertItem( aNew, m_nStart, m_nEnd,
414                         SetAttrMode::NOTXTATRCHR );
415 }
416 
IsEqual(const SwTOXMark & rCmp) const417 bool SwHistorySetTOXMark::IsEqual( const SwTOXMark& rCmp ) const
418 {
419     return m_TOXName   == rCmp.GetTOXType()->GetTypeName() &&
420            m_eTOXTypes == rCmp.GetTOXType()->GetType() &&
421            m_TOXMark.GetAlternativeText() == rCmp.GetAlternativeText() &&
422            ( (TOX_INDEX == m_eTOXTypes)
423               ?   ( m_TOXMark.GetPrimaryKey()   == rCmp.GetPrimaryKey()  &&
424                     m_TOXMark.GetSecondaryKey() == rCmp.GetSecondaryKey()   )
425               :   m_TOXMark.GetLevel() == rCmp.GetLevel()
426            );
427 }
428 
SwHistoryResetText(sal_uInt16 nWhich,sal_Int32 nAttrStart,sal_Int32 nAttrEnd,sal_uLong nNodePos)429 SwHistoryResetText::SwHistoryResetText( sal_uInt16 nWhich,
430             sal_Int32 nAttrStart, sal_Int32 nAttrEnd, sal_uLong nNodePos )
431     : SwHistoryHint( HSTRY_RESETTXTHNT )
432     , m_nNodeIndex( nNodePos ), m_nStart( nAttrStart ), m_nEnd( nAttrEnd )
433     , m_nAttr( nWhich )
434 {
435 }
436 
SetInDoc(SwDoc * pDoc,bool)437 void SwHistoryResetText::SetInDoc( SwDoc* pDoc, bool )
438 {
439     SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
440     OSL_ENSURE( pTextNd, "SwHistoryResetText: no TextNode" );
441     if ( pTextNd )
442     {
443         pTextNd->DeleteAttributes( m_nAttr, m_nStart, m_nEnd );
444     }
445 }
446 
SwHistorySetFootnote(SwTextFootnote * pTextFootnote,sal_uLong nNodePos)447 SwHistorySetFootnote::SwHistorySetFootnote( SwTextFootnote* pTextFootnote, sal_uLong nNodePos )
448     : SwHistoryHint( HSTRY_SETFTNHNT )
449     , m_pUndo( new SwUndoSaveSection )
450     , m_FootnoteNumber( pTextFootnote->GetFootnote().GetNumStr() )
451     , m_nNodeIndex( nNodePos )
452     , m_nStart( pTextFootnote->GetStart() )
453     , m_bEndNote( pTextFootnote->GetFootnote().IsEndNote() )
454 {
455     OSL_ENSURE( pTextFootnote->GetStartNode(),
456             "SwHistorySetFootnote: Footnote without Section" );
457 
458     // keep the old NodePos (because who knows what later will be saved/deleted
459     // in SaveSection)
460     SwDoc& rDoc = const_cast<SwDoc&>(pTextFootnote->GetTextNode().GetDoc());
461     SwNode* pSaveNd = rDoc.GetNodes()[ m_nNodeIndex ];
462 
463     // keep pointer to StartNode of FootnoteSection and reset its attribute for now
464     // (as a result, its/all Frames will be deleted automatically)
465     SwNodeIndex aSttIdx( *pTextFootnote->GetStartNode() );
466     pTextFootnote->SetStartNode( nullptr, false );
467 
468     m_pUndo->SaveSection( aSttIdx );
469     m_nNodeIndex = pSaveNd->GetIndex();
470 }
471 
SwHistorySetFootnote(const SwTextFootnote & rTextFootnote)472 SwHistorySetFootnote::SwHistorySetFootnote( const SwTextFootnote &rTextFootnote )
473     : SwHistoryHint( HSTRY_SETFTNHNT )
474     , m_FootnoteNumber( rTextFootnote.GetFootnote().GetNumStr() )
475     , m_nNodeIndex( SwTextFootnote_GetIndex( (&rTextFootnote) ) )
476     , m_nStart( rTextFootnote.GetStart() )
477     , m_bEndNote( rTextFootnote.GetFootnote().IsEndNote() )
478 {
479     OSL_ENSURE( rTextFootnote.GetStartNode(),
480             "SwHistorySetFootnote: Footnote without Section" );
481 }
482 
GetDescription() const483 OUString SwHistorySetFootnote::GetDescription() const
484 {
485     return SwResId(STR_FOOTNOTE);
486 }
487 
~SwHistorySetFootnote()488 SwHistorySetFootnote::~SwHistorySetFootnote()
489 {
490 }
491 
SetInDoc(SwDoc * pDoc,bool)492 void SwHistorySetFootnote::SetInDoc( SwDoc* pDoc, bool )
493 {
494     SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
495     OSL_ENSURE( pTextNd, "SwHistorySetFootnote: no TextNode" );
496     if ( !pTextNd )
497         return;
498 
499     if (m_pUndo)
500     {
501         // set the footnote in the TextNode
502         SwFormatFootnote aTemp( m_bEndNote );
503         SwFormatFootnote& rNew = const_cast<SwFormatFootnote&>(
504                 pDoc->GetAttrPool().Put(aTemp) );
505         if ( !m_FootnoteNumber.isEmpty() )
506         {
507             rNew.SetNumStr( m_FootnoteNumber );
508         }
509         SwTextFootnote* pTextFootnote = new SwTextFootnote( rNew, m_nStart );
510 
511         // create the section of the Footnote
512         SwNodeIndex aIdx( *pTextNd );
513         m_pUndo->RestoreSection( pDoc, &aIdx, SwFootnoteStartNode );
514         pTextFootnote->SetStartNode( &aIdx );
515         if ( m_pUndo->GetHistory() )
516         {
517             // create frames only now
518             m_pUndo->GetHistory()->Rollback( pDoc );
519         }
520 
521         pTextNd->InsertHint( pTextFootnote );
522     }
523     else
524     {
525         SwTextFootnote * const pFootnote =
526             static_cast<SwTextFootnote*>(
527                 pTextNd->GetTextAttrForCharAt( m_nStart ));
528         assert(pFootnote);
529         SwFormatFootnote &rFootnote = const_cast<SwFormatFootnote&>(pFootnote->GetFootnote());
530         rFootnote.SetNumStr( m_FootnoteNumber  );
531         if ( rFootnote.IsEndNote() != m_bEndNote )
532         {
533             rFootnote.SetEndNote( m_bEndNote );
534             pFootnote->CheckCondColl();
535         }
536     }
537 }
538 
SwHistoryChangeFormatColl(SwFormatColl * pFormatColl,sal_uLong nNd,SwNodeType nNodeWhich)539 SwHistoryChangeFormatColl::SwHistoryChangeFormatColl( SwFormatColl* pFormatColl, sal_uLong nNd,
540                             SwNodeType nNodeWhich )
541     : SwHistoryHint( HSTRY_CHGFMTCOLL )
542     , m_pColl( pFormatColl )
543     , m_nNodeIndex( nNd )
544     , m_nNodeType( nNodeWhich )
545 {
546 }
547 
SetInDoc(SwDoc * pDoc,bool)548 void SwHistoryChangeFormatColl::SetInDoc( SwDoc* pDoc, bool )
549 {
550     SwContentNode * pContentNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetContentNode();
551     OSL_ENSURE( pContentNd, "SwHistoryChangeFormatColl: no ContentNode" );
552 
553     // before setting the format, check if it is still available in the
554     // document. if it has been deleted, there is no undo!
555     if ( !(pContentNd && m_nNodeType == pContentNd->GetNodeType()) )
556         return;
557 
558     if ( SwNodeType::Text == m_nNodeType )
559     {
560         if (pDoc->GetTextFormatColls()->IsAlive(static_cast<SwTextFormatColl *>(m_pColl)))
561         {
562             pContentNd->ChgFormatColl( m_pColl );
563         }
564     }
565     else if (pDoc->GetGrfFormatColls()->IsAlive(static_cast<SwGrfFormatColl *>(m_pColl)))
566     {
567         pContentNd->ChgFormatColl( m_pColl );
568     }
569 }
570 
SwHistoryTextFlyCnt(SwFrameFormat * const pFlyFormat)571 SwHistoryTextFlyCnt::SwHistoryTextFlyCnt( SwFrameFormat* const pFlyFormat )
572     : SwHistoryHint( HSTRY_FLYCNT )
573     , m_pUndo( new SwUndoDelLayFormat( pFlyFormat ) )
574 {
575     OSL_ENSURE( pFlyFormat, "SwHistoryTextFlyCnt: no Format" );
576     m_pUndo->ChgShowSel( false );
577 }
578 
~SwHistoryTextFlyCnt()579 SwHistoryTextFlyCnt::~SwHistoryTextFlyCnt()
580 {
581 }
582 
SetInDoc(SwDoc * pDoc,bool)583 void SwHistoryTextFlyCnt::SetInDoc( SwDoc* pDoc, bool )
584 {
585     ::sw::IShellCursorSupplier *const pISCS(pDoc->GetIShellCursorSupplier());
586     assert(pISCS);
587     ::sw::UndoRedoContext context(*pDoc, *pISCS);
588     m_pUndo->UndoImpl(context);
589 }
590 
dumpAsXml(xmlTextWriterPtr pWriter) const591 void SwHistoryTextFlyCnt::dumpAsXml(xmlTextWriterPtr pWriter) const
592 {
593     (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHistoryTextFlyCnt"));
594     SwHistoryHint::dumpAsXml(pWriter);
595 
596     if (m_pUndo)
597     {
598         m_pUndo->dumpAsXml(pWriter);
599     }
600 
601     (void)xmlTextWriterEndElement(pWriter);
602 }
603 
SwHistoryBookmark(const::sw::mark::IMark & rBkmk,bool bSavePos,bool bSaveOtherPos)604 SwHistoryBookmark::SwHistoryBookmark(
605     const ::sw::mark::IMark& rBkmk,
606     bool bSavePos,
607     bool bSaveOtherPos)
608     : SwHistoryHint(HSTRY_BOOKMARK)
609     , m_aName(rBkmk.GetName())
610     , m_bHidden(false)
611     , m_nNode(bSavePos ?
612         rBkmk.GetMarkPos().nNode.GetIndex() : 0)
613     , m_nOtherNode(bSaveOtherPos ?
614         rBkmk.GetOtherMarkPos().nNode.GetIndex() : 0)
615     , m_nContent(bSavePos ?
616         rBkmk.GetMarkPos().nContent.GetIndex() : 0)
617     , m_nOtherContent(bSaveOtherPos ?
618         rBkmk.GetOtherMarkPos().nContent.GetIndex() :0)
619     , m_bSavePos(bSavePos)
620     , m_bSaveOtherPos(bSaveOtherPos)
621     , m_bHadOtherPos(rBkmk.IsExpanded())
622     , m_eBkmkType(IDocumentMarkAccess::GetType(rBkmk))
623 {
624     const ::sw::mark::IBookmark* const pBookmark = dynamic_cast< const ::sw::mark::IBookmark* >(&rBkmk);
625     if(!pBookmark)
626         return;
627 
628     m_aKeycode = pBookmark->GetKeyCode();
629     m_aShortName = pBookmark->GetShortName();
630     m_bHidden = pBookmark->IsHidden();
631     m_aHideCondition = pBookmark->GetHideCondition();
632 
633     ::sfx2::Metadatable const*const pMetadatable(
634             dynamic_cast< ::sfx2::Metadatable const* >(pBookmark));
635     if (pMetadatable)
636     {
637         m_pMetadataUndo = pMetadatable->CreateUndo();
638     }
639 }
640 
SetInDoc(SwDoc * pDoc,bool)641 void SwHistoryBookmark::SetInDoc( SwDoc* pDoc, bool )
642 {
643     ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
644 
645     SwNodes& rNds = pDoc->GetNodes();
646     IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
647     std::unique_ptr<SwPaM> pPam;
648     ::sw::mark::IMark* pMark = nullptr;
649 
650     if(m_bSavePos)
651     {
652         SwContentNode* const pContentNd = rNds[m_nNode]->GetContentNode();
653         OSL_ENSURE(pContentNd,
654             "<SwHistoryBookmark::SetInDoc(..)>"
655             " - wrong node for a mark");
656 
657         // #111660# don't crash when nNode1 doesn't point to content node.
658         if(pContentNd)
659             pPam.reset(new SwPaM(*pContentNd, m_nContent));
660     }
661     else
662     {
663         pMark = *pMarkAccess->findMark(m_aName);
664         pPam.reset(new SwPaM(pMark->GetMarkPos()));
665     }
666 
667     if(m_bSaveOtherPos)
668     {
669         SwContentNode* const pContentNd = rNds[m_nOtherNode]->GetContentNode();
670         OSL_ENSURE(pContentNd,
671             "<SwHistoryBookmark::SetInDoc(..)>"
672             " - wrong node for a mark");
673 
674         if (pPam != nullptr && pContentNd)
675         {
676             pPam->SetMark();
677             pPam->GetMark()->nNode = m_nOtherNode;
678             pPam->GetMark()->nContent.Assign(pContentNd, m_nOtherContent);
679         }
680     }
681     else if(m_bHadOtherPos)
682     {
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();
690     }
691 
692     if (!pPam)
693         return;
694 
695     if ( pMark != nullptr )
696     {
697         pMarkAccess->deleteMark( pMark );
698     }
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;
704 
705     pBookmark->SetKeyCode(m_aKeycode);
706     pBookmark->SetShortName(m_aShortName);
707     pBookmark->Hide(m_bHidden);
708     pBookmark->SetHideCondition(m_aHideCondition);
709 
710     if (m_pMetadataUndo)
711     {
712         ::sfx2::Metadatable * const pMeta(
713             dynamic_cast< ::sfx2::Metadatable* >(pBookmark));
714         OSL_ENSURE(pMeta, "metadata undo, but not metadatable?");
715         if (pMeta)
716         {
717             pMeta->RestoreMetadata(m_pMetadataUndo);
718         }
719     }
720 }
721 
IsEqualBookmark(const::sw::mark::IMark & rBkmk)722 bool SwHistoryBookmark::IsEqualBookmark(const ::sw::mark::IMark& rBkmk)
723 {
724     return m_nNode == rBkmk.GetMarkPos().nNode.GetIndex()
725         && m_nContent == rBkmk.GetMarkPos().nContent.GetIndex()
726         && m_aName == rBkmk.GetName();
727 }
728 
SwHistoryNoTextFieldmark(const::sw::mark::IFieldmark & rFieldMark)729 SwHistoryNoTextFieldmark::SwHistoryNoTextFieldmark(const ::sw::mark::IFieldmark& rFieldMark)
730     : SwHistoryHint(HSTRY_NOTEXTFIELDMARK)
731     , m_sType(rFieldMark.GetFieldname())
732     , m_nNode(rFieldMark.GetMarkPos().nNode.GetIndex())
733     , m_nContent(rFieldMark.GetMarkPos().nContent.GetIndex())
734 {
735 }
736 
SetInDoc(SwDoc * pDoc,bool)737 void SwHistoryNoTextFieldmark::SetInDoc(SwDoc* pDoc, bool)
738 {
739     ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
740 
741     SwNodes& rNds = pDoc->GetNodes();
742     std::unique_ptr<SwPaM> pPam;
743 
744     const SwContentNode* pContentNd = rNds[m_nNode]->GetContentNode();
745     if(pContentNd)
746         pPam.reset(new SwPaM(*pContentNd, m_nContent));
747 
748     if (pPam)
749     {
750         IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
751         pMarkAccess->makeNoTextFieldBookmark(*pPam, OUString(), m_sType);
752     }
753 }
754 
ResetInDoc(SwDoc & rDoc)755 void SwHistoryNoTextFieldmark::ResetInDoc(SwDoc& rDoc)
756 {
757     ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
758 
759     SwNodes& rNds = rDoc.GetNodes();
760     std::unique_ptr<SwPaM> pPam;
761 
762     const SwContentNode* pContentNd = rNds[m_nNode]->GetContentNode();
763     if(pContentNd)
764         pPam.reset(new SwPaM(*pContentNd, m_nContent-1));
765 
766     if (pPam)
767     {
768         IDocumentMarkAccess* pMarkAccess = rDoc.getIDocumentMarkAccess();
769         pMarkAccess->deleteFieldmarkAt(*pPam->GetPoint());
770     }
771 }
772 
SwHistoryTextFieldmark(const::sw::mark::IFieldmark & rFieldMark)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().nNode.GetIndex())
778     , m_nStartContent(rFieldMark.GetMarkStart().nContent.GetIndex())
779     , m_nEndNode(rFieldMark.GetMarkEnd().nNode.GetIndex())
780     , m_nEndContent(rFieldMark.GetMarkEnd().nContent.GetIndex())
781 {
782     SwPosition const sepPos(sw::mark::FindFieldSep(rFieldMark));
783     m_nSepNode = sepPos.nNode.GetIndex();
784     m_nSepContent = sepPos.nContent.GetIndex();
785 }
786 
SetInDoc(SwDoc * pDoc,bool)787 void SwHistoryTextFieldmark::SetInDoc(SwDoc* pDoc, bool)
788 {
789     ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
790 
791     SwNodes& rNds = pDoc->GetNodes();
792 
793     assert(rNds[m_nStartNode]->IsContentNode());
794     assert(rNds[m_nEndNode]->IsContentNode());
795     assert(rNds[m_nSepNode]->IsContentNode());
796 
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);
808 
809     IDocumentMarkAccess & rMarksAccess(*pDoc->getIDocumentMarkAccess());
810     rMarksAccess.makeFieldBookmark(pam, m_sName, m_sType, &sepPos);
811 }
812 
ResetInDoc(SwDoc & rDoc)813 void SwHistoryTextFieldmark::ResetInDoc(SwDoc& rDoc)
814 {
815     ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
816 
817     SwNodes& rNds = rDoc.GetNodes();
818 
819     assert(rNds[m_nStartNode]->IsContentNode());
820     assert(rNds[m_nEndNode]->IsContentNode());
821     assert(rNds[m_nSepNode]->IsContentNode());
822 
823     SwPosition const pos(*rNds[m_nStartNode]->GetContentNode(), m_nStartContent);
824 
825     IDocumentMarkAccess & rMarksAccess(*rDoc.getIDocumentMarkAccess());
826     rMarksAccess.deleteFieldmarkAt(pos);
827 }
828 
SwHistorySetAttrSet(const SfxItemSet & rSet,sal_uLong nNodePos,const o3tl::sorted_vector<sal_uInt16> & rSetArr)829 SwHistorySetAttrSet::SwHistorySetAttrSet( const SfxItemSet& rSet,
830                         sal_uLong 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 )
835 {
836     SfxItemIter aIter( m_OldSet ), aOrigIter( rSet );
837     const SfxPoolItem* pItem = aIter.GetCurItem(),
838                      * pOrigItem = aOrigIter.GetCurItem();
839     while (pItem && pOrigItem)
840     {
841         if( !rSetArr.count( pOrigItem->Which() ))
842         {
843             m_ResetArray.push_back( pOrigItem->Which() );
844             m_OldSet.ClearItem( pOrigItem->Which() );
845         }
846         else
847         {
848             switch ( pItem->Which() )
849             {
850                 case RES_PAGEDESC:
851                     static_cast<SwFormatPageDesc*>(
852                         const_cast<SfxPoolItem*>(pItem))->ChgDefinedIn( nullptr );
853                     break;
854 
855                 case RES_PARATR_DROP:
856                     static_cast<SwFormatDrop*>(
857                         const_cast<SfxPoolItem*>(pItem))->ChgDefinedIn(nullptr);
858                     break;
859 
860                 case RES_BOXATR_FORMULA:
861                     {
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 );
866 
867                         SwTableBoxFormula& rNew =
868                             *static_cast<SwTableBoxFormula*>(
869                                 const_cast<SfxPoolItem*>(pItem));
870                         if ( rNew.IsIntrnlName() )
871                         {
872                             const SwTableBoxFormula& rOld =
873                                         rSet.Get( RES_BOXATR_FORMULA );
874                             const SwNode* pNd = rOld.GetNodeOfFormula();
875                             if ( pNd )
876                             {
877                                 const SwTableNode* pTableNode
878                                     = pNd->FindTableNode();
879                                 if (pTableNode)
880                                 {
881                                     SwTableFormulaUpdate aMsgHint(
882                                         &pTableNode->GetTable() );
883                                     aMsgHint.m_eFlags = TBL_BOXNAME;
884                                     rNew.ChgDefinedIn( rOld.GetDefinedIn() );
885                                     rNew.ChangeState( &aMsgHint );
886                                 }
887                             }
888                         }
889                         rNew.ChgDefinedIn( nullptr );
890                     }
891                     break;
892             }
893         }
894 
895         pItem = aIter.NextItem();
896         pOrigItem = aOrigIter.NextItem();
897     }
898 }
899 
SetInDoc(SwDoc * pDoc,bool)900 void SwHistorySetAttrSet::SetInDoc( SwDoc* pDoc, bool )
901 {
902     ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
903 
904     SwNode * pNode = pDoc->GetNodes()[ m_nNodeIndex ];
905     if ( pNode->IsContentNode() )
906     {
907         static_cast<SwContentNode*>(pNode)->SetAttr( m_OldSet );
908         if ( !m_ResetArray.empty() )
909         {
910             static_cast<SwContentNode*>(pNode)->ResetAttr( m_ResetArray );
911         }
912     }
913     else if ( pNode->IsTableNode() )
914     {
915         SwFormat& rFormat =
916             *static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat();
917         rFormat.SetFormatAttr( m_OldSet );
918         if ( !m_ResetArray.empty() )
919         {
920             rFormat.ResetFormatAttr( m_ResetArray.front() );
921         }
922     }
923 }
924 
SwHistoryChangeFlyAnchor(SwFrameFormat & rFormat)925 SwHistoryChangeFlyAnchor::SwHistoryChangeFlyAnchor( SwFrameFormat& rFormat )
926     : SwHistoryHint( HSTRY_CHGFLYANCHOR )
927     , m_rFormat( rFormat )
928     , m_nOldNodeIndex( rFormat.GetAnchor().GetContentAnchor()->nNode.GetIndex() )
929     , m_nOldContentIndex( (RndStdIds::FLY_AT_CHAR == rFormat.GetAnchor().GetAnchorId())
930             ?   rFormat.GetAnchor().GetContentAnchor()->nContent.GetIndex()
931             :   COMPLETE_STRING )
932 {
933 }
934 
SetInDoc(SwDoc * pDoc,bool)935 void SwHistoryChangeFlyAnchor::SetInDoc( SwDoc* pDoc, bool )
936 {
937     ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
938 
939     if (!pDoc->GetSpzFrameFormats()->IsAlive(&m_rFormat)) // Format does still exist
940         return;
941 
942     SwFormatAnchor aTmp( m_rFormat.GetAnchor() );
943 
944     SwNode* pNd = pDoc->GetNodes()[ m_nOldNodeIndex ];
945     SwContentNode* pCNd = pNd->GetContentNode();
946     SwPosition aPos( *pNd );
947     if ( COMPLETE_STRING != m_nOldContentIndex )
948     {
949         OSL_ENSURE(pCNd, "SwHistoryChangeFlyAnchor: no ContentNode");
950         if (pCNd)
951         {
952             aPos.nContent.Assign( pCNd, m_nOldContentIndex );
953         }
954     }
955     aTmp.SetAnchor( &aPos );
956 
957     // so the Layout does not get confused
958     if (!pCNd || !pCNd->getLayoutFrame(pDoc->getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr))
959     {
960         m_rFormat.DelFrames();
961     }
962 
963     m_rFormat.SetFormatAttr( aTmp );
964 }
965 
SwHistoryChangeFlyChain(SwFlyFrameFormat & rFormat,const SwFormatChain & rAttr)966 SwHistoryChangeFlyChain::SwHistoryChangeFlyChain( SwFlyFrameFormat& rFormat,
967                                         const SwFormatChain& rAttr )
968     : SwHistoryHint( HSTRY_CHGFLYCHAIN )
969     , m_pPrevFormat( rAttr.GetPrev() )
970     , m_pNextFormat( rAttr.GetNext() )
971     , m_pFlyFormat( &rFormat )
972 {
973 }
974 
SetInDoc(SwDoc * pDoc,bool)975 void SwHistoryChangeFlyChain::SetInDoc( SwDoc* pDoc, bool )
976 {
977     if (!pDoc->GetSpzFrameFormats()->IsAlive(m_pFlyFormat))
978         return;
979 
980     SwFormatChain aChain;
981 
982     if (m_pPrevFormat &&
983         pDoc->GetSpzFrameFormats()->IsAlive(m_pPrevFormat))
984     {
985         aChain.SetPrev( m_pPrevFormat );
986         SwFormatChain aTmp( m_pPrevFormat->GetChain() );
987         aTmp.SetNext( m_pFlyFormat );
988         m_pPrevFormat->SetFormatAttr( aTmp );
989     }
990 
991     if (m_pNextFormat &&
992         pDoc->GetSpzFrameFormats()->IsAlive(m_pNextFormat))
993     {
994         aChain.SetNext( m_pNextFormat );
995         SwFormatChain aTmp( m_pNextFormat->GetChain() );
996         aTmp.SetPrev( m_pFlyFormat );
997         m_pNextFormat->SetFormatAttr( aTmp );
998     }
999 
1000     if ( aChain.GetNext() || aChain.GetPrev() )
1001     {
1002         m_pFlyFormat->SetFormatAttr( aChain );
1003     }
1004 }
1005 
1006 // -> #i27615#
SwHistoryChangeCharFormat(const SfxItemSet & rSet,const OUString & sFormat)1007 SwHistoryChangeCharFormat::SwHistoryChangeCharFormat(const SfxItemSet & rSet,
1008                                      const OUString & sFormat)
1009     : SwHistoryHint(HSTRY_CHGCHARFMT)
1010     , m_OldSet(rSet), m_Format(sFormat)
1011 {
1012 }
1013 
SetInDoc(SwDoc * pDoc,bool)1014 void SwHistoryChangeCharFormat::SetInDoc(SwDoc * pDoc, bool )
1015 {
1016     SwCharFormat * pCharFormat = pDoc->FindCharFormatByName(m_Format);
1017 
1018     if (pCharFormat)
1019     {
1020         pCharFormat->SetFormatAttr(m_OldSet);
1021     }
1022 }
1023 // <- #i27615#
1024 
SwHistory()1025 SwHistory::SwHistory()
1026     : m_SwpHstry()
1027     , m_nEndDiff( 0 )
1028 {
1029 }
1030 
~SwHistory()1031 SwHistory::~SwHistory()
1032 {
1033 }
1034 
Add(const SfxPoolItem * pOldValue,const SfxPoolItem * pNewValue,sal_uLong nNodeIdx)1035 void SwHistory::Add(
1036     const SfxPoolItem* pOldValue,
1037     const SfxPoolItem* pNewValue,
1038     sal_uLong nNodeIdx)
1039 {
1040     OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
1041     const sal_uInt16 nWhich(pNewValue->Which());
1042 
1043     // excluded values
1044     if(nWhich == RES_TXTATR_FIELD || nWhich == RES_TXTATR_ANNOTATION)
1045     {
1046         return;
1047     }
1048 
1049     // no default Attribute?
1050     std::unique_ptr<SwHistoryHint> pHt;
1051 
1052     // To be able to include the DrawingLayer FillItems something more
1053     // general has to be done to check if an Item is default than to check
1054     // if its pointer equals that in Writer's global PoolDefaults (held in
1055     // aAttrTab and used to fill the pool defaults in Writer - looks as if
1056     // Writer is *older* than the SfxItemPool ?). I checked the possibility to
1057     // get the SfxItemPool here (works), but decided to use the SfxPoolItem's
1058     // global tooling aka IsDefaultItem(const SfxPoolItem*) for now
1059     if(pOldValue && !IsDefaultItem(pOldValue))
1060     {
1061         pHt.reset( new SwHistorySetFormat( pOldValue, nNodeIdx ) );
1062     }
1063     else
1064     {
1065         pHt.reset( new SwHistoryResetFormat( pNewValue, nNodeIdx ) );
1066     }
1067 
1068     m_SwpHstry.push_back( std::move(pHt) );
1069 }
1070 
1071 // FIXME: refactor the following "Add" methods (DRY)?
Add(SwTextAttr * pHint,sal_uLong nNodeIdx,bool bNewAttr)1072 void SwHistory::Add( SwTextAttr* pHint, sal_uLong nNodeIdx, bool bNewAttr )
1073 {
1074     OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
1075 
1076     std::unique_ptr<SwHistoryHint> pHt;
1077     if( !bNewAttr )
1078     {
1079         switch ( pHint->Which() )
1080         {
1081             case RES_TXTATR_FTN:
1082                 pHt.reset( new SwHistorySetFootnote(
1083                             static_cast<SwTextFootnote*>(pHint), nNodeIdx ) );
1084                 break;
1085             case RES_TXTATR_FLYCNT:
1086                 pHt.reset( new SwHistoryTextFlyCnt( static_cast<SwTextFlyCnt*>(pHint)
1087                             ->GetFlyCnt().GetFrameFormat() ) );
1088                 break;
1089             case RES_TXTATR_FIELD:
1090             case RES_TXTATR_ANNOTATION:
1091                 pHt.reset( new SwHistorySetTextField(
1092                         static_txtattr_cast<SwTextField*>(pHint), nNodeIdx) );
1093                 break;
1094             case RES_TXTATR_TOXMARK:
1095                 pHt.reset( new SwHistorySetTOXMark(
1096                         static_txtattr_cast<SwTextTOXMark*>(pHint), nNodeIdx) );
1097                 break;
1098             case RES_TXTATR_REFMARK:
1099                 pHt.reset( new SwHistorySetRefMark(
1100                         static_txtattr_cast<SwTextRefMark*>(pHint), nNodeIdx) );
1101                 break;
1102             default:
1103                 pHt.reset( new SwHistorySetText( pHint, nNodeIdx ) );
1104         }
1105     }
1106     else
1107     {
1108         pHt.reset( new SwHistoryResetText( pHint->Which(), pHint->GetStart(),
1109                                     pHint->GetAnyEnd(), nNodeIdx ) );
1110     }
1111     m_SwpHstry.push_back( std::move(pHt) );
1112 }
1113 
Add(SwFormatColl * pColl,sal_uLong nNodeIdx,SwNodeType nWhichNd)1114 void SwHistory::Add( SwFormatColl* pColl, sal_uLong nNodeIdx, SwNodeType nWhichNd )
1115 {
1116     OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
1117 
1118     std::unique_ptr<SwHistoryHint> pHt(
1119         new SwHistoryChangeFormatColl( pColl, nNodeIdx, nWhichNd ));
1120     m_SwpHstry.push_back( std::move(pHt) );
1121 }
1122 
Add(const::sw::mark::IMark & rBkmk,bool bSavePos,bool bSaveOtherPos)1123 void SwHistory::Add(const ::sw::mark::IMark& rBkmk, bool bSavePos, bool bSaveOtherPos)
1124 {
1125     OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
1126 
1127     std::unique_ptr<SwHistoryHint> pHt;
1128 
1129     switch (IDocumentMarkAccess::GetType(rBkmk))
1130     {
1131         case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK:
1132         case IDocumentMarkAccess::MarkType::DATE_FIELDMARK:
1133             assert(bSavePos && bSaveOtherPos); // must be deleted completely!
1134             pHt.reset(new SwHistoryTextFieldmark(dynamic_cast<sw::mark::IFieldmark const&>(rBkmk)));
1135             break;
1136         case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK:
1137         case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK:
1138             assert(bSavePos && bSaveOtherPos); // must be deleted completely!
1139             pHt.reset(new SwHistoryNoTextFieldmark(dynamic_cast<sw::mark::IFieldmark const&>(rBkmk)));
1140             break;
1141         default:
1142             pHt.reset(new SwHistoryBookmark(rBkmk, bSavePos, bSaveOtherPos));
1143             break;
1144     }
1145 
1146     assert(pHt);
1147     m_SwpHstry.push_back( std::move(pHt) );
1148 }
1149 
AddChangeFlyAnchor(SwFrameFormat & rFormat)1150 void SwHistory::AddChangeFlyAnchor(SwFrameFormat& rFormat)
1151 {
1152     std::unique_ptr<SwHistoryHint> pHt(new SwHistoryChangeFlyAnchor( rFormat ));
1153     m_SwpHstry.push_back( std::move(pHt) );
1154 }
1155 
AddDeleteFly(SwFrameFormat & rFormat,sal_uInt16 & rSetPos)1156 void SwHistory::AddDeleteFly(SwFrameFormat& rFormat, sal_uInt16& rSetPos)
1157 {
1158     OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
1159 
1160     const sal_uInt16 nWh = rFormat.Which();
1161     (void) nWh;
1162     // only Flys!
1163     assert((RES_FLYFRMFMT == nWh && dynamic_cast<SwFlyFrameFormat*>(&rFormat))
1164         || (RES_DRAWFRMFMT == nWh && dynamic_cast<SwDrawFrameFormat*>(&rFormat)));
1165     {
1166         std::unique_ptr<SwHistoryHint> pHint(new SwHistoryTextFlyCnt( &rFormat ));
1167         m_SwpHstry.push_back( std::move(pHint) );
1168 
1169         const SwFormatChain* pChainItem;
1170         if( SfxItemState::SET == rFormat.GetItemState( RES_CHAIN, false,
1171             reinterpret_cast<const SfxPoolItem**>(&pChainItem) ))
1172         {
1173             assert(RES_FLYFRMFMT == nWh); // not supported on SdrObjects
1174             if( pChainItem->GetNext() || pChainItem->GetPrev() )
1175             {
1176                 std::unique_ptr<SwHistoryHint> pHt(
1177                     new SwHistoryChangeFlyChain(static_cast<SwFlyFrameFormat&>(rFormat), *pChainItem));
1178                 m_SwpHstry.insert( m_SwpHstry.begin() + rSetPos++, std::move(pHt) );
1179                 if ( pChainItem->GetNext() )
1180                 {
1181                     SwFormatChain aTmp( pChainItem->GetNext()->GetChain() );
1182                     aTmp.SetPrev( nullptr );
1183                     pChainItem->GetNext()->SetFormatAttr( aTmp );
1184                 }
1185                 if ( pChainItem->GetPrev() )
1186                 {
1187                     SwFormatChain aTmp( pChainItem->GetPrev()->GetChain() );
1188                     aTmp.SetNext( nullptr );
1189                     pChainItem->GetPrev()->SetFormatAttr( aTmp );
1190                 }
1191             }
1192             rFormat.ResetFormatAttr( RES_CHAIN );
1193         }
1194     }
1195 }
1196 
Add(const SwTextFootnote & rFootnote)1197 void SwHistory::Add( const SwTextFootnote& rFootnote )
1198 {
1199     std::unique_ptr<SwHistoryHint> pHt(new SwHistorySetFootnote( rFootnote ));
1200     m_SwpHstry.push_back( std::move(pHt) );
1201 }
1202 
1203 // #i27615#
Add(const SfxItemSet & rSet,const SwCharFormat & rFormat)1204 void SwHistory::Add(const SfxItemSet & rSet, const SwCharFormat & rFormat)
1205 {
1206     std::unique_ptr<SwHistoryHint> pHt(new SwHistoryChangeCharFormat(rSet, rFormat.GetName()));
1207     m_SwpHstry.push_back( std::move(pHt) );
1208 }
1209 
Rollback(SwDoc * pDoc,sal_uInt16 nStart)1210 bool SwHistory::Rollback( SwDoc* pDoc, sal_uInt16 nStart )
1211 {
1212     if ( !Count() )
1213         return false;
1214 
1215     for ( sal_uInt16 i = Count(); i > nStart ; )
1216     {
1217         SwHistoryHint * pHHt = m_SwpHstry[ --i ].get();
1218         pHHt->SetInDoc( pDoc, false );
1219     }
1220     m_SwpHstry.erase( m_SwpHstry.begin() + nStart, m_SwpHstry.end() );
1221     m_nEndDiff = 0;
1222     return true;
1223 }
1224 
TmpRollback(SwDoc * pDoc,sal_uInt16 nStart,bool bToFirst)1225 bool SwHistory::TmpRollback( SwDoc* pDoc, sal_uInt16 nStart, bool bToFirst )
1226 {
1227     sal_uInt16 nEnd = Count() - m_nEndDiff;
1228     if ( !Count() || !nEnd || nStart >= nEnd )
1229         return false;
1230 
1231     if ( bToFirst )
1232     {
1233         for ( ; nEnd > nStart; ++m_nEndDiff )
1234         {
1235             SwHistoryHint* pHHt = m_SwpHstry[ --nEnd ].get();
1236             pHHt->SetInDoc( pDoc, true );
1237         }
1238     }
1239     else
1240     {
1241         for ( ; nStart < nEnd; ++m_nEndDiff, ++nStart )
1242         {
1243             SwHistoryHint* pHHt = m_SwpHstry[ nStart ].get();
1244             pHHt->SetInDoc( pDoc, true );
1245         }
1246     }
1247     return true;
1248 }
1249 
SetTmpEnd(sal_uInt16 nNewTmpEnd)1250 sal_uInt16 SwHistory::SetTmpEnd( sal_uInt16 nNewTmpEnd )
1251 {
1252     OSL_ENSURE( nNewTmpEnd <= Count(), "SwHistory::SetTmpEnd: out of bounds" );
1253 
1254     const sal_uInt16 nOld = Count() - m_nEndDiff;
1255     m_nEndDiff = Count() - nNewTmpEnd;
1256 
1257     // for every SwHistoryFlyCnt, call the Redo of its UndoObject.
1258     // this saves the formats of the flys!
1259     for ( sal_uInt16 n = nOld; n < nNewTmpEnd; n++ )
1260     {
1261         if ( HSTRY_FLYCNT == (*this)[ n ]->Which() )
1262         {
1263             static_cast<SwHistoryTextFlyCnt*>((*this)[ n ])
1264                 ->GetUDelLFormat()->RedoForRollback();
1265         }
1266     }
1267 
1268     return nOld;
1269 }
1270 
CopyFormatAttr(const SfxItemSet & rSet,sal_uLong nNodeIdx)1271 void SwHistory::CopyFormatAttr(
1272     const SfxItemSet& rSet,
1273     sal_uLong nNodeIdx)
1274 {
1275     if(!rSet.Count())
1276         return;
1277 
1278     SfxItemIter aIter(rSet);
1279     const SfxPoolItem* pItem = aIter.GetCurItem();
1280     do
1281     {
1282         if(!IsInvalidItem(pItem))
1283         {
1284             Add(pItem, pItem, nNodeIdx);
1285         }
1286 
1287         pItem = aIter.NextItem();
1288 
1289     } while(pItem);
1290 }
1291 
dumpAsXml(xmlTextWriterPtr pWriter) const1292 void SwHistory::dumpAsXml(xmlTextWriterPtr pWriter) const
1293 {
1294     (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHistory"));
1295     (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
1296 
1297     (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_SwpHstry"));
1298     for (const auto& pHistory : m_SwpHstry)
1299     {
1300         pHistory->dumpAsXml(pWriter);
1301     }
1302     (void)xmlTextWriterEndElement(pWriter);
1303 
1304     (void)xmlTextWriterEndElement(pWriter);
1305 }
1306 
CopyAttr(SwpHints const * pHts,const sal_uLong nNodeIdx,const sal_Int32 nStart,const sal_Int32 nEnd,const bool bCopyFields)1307 void SwHistory::CopyAttr(
1308     SwpHints const * pHts,
1309     const sal_uLong nNodeIdx,
1310     const sal_Int32 nStart,
1311     const sal_Int32 nEnd,
1312     const bool bCopyFields )
1313 {
1314     if( !pHts  )
1315         return;
1316 
1317     // copy all attributes of the TextNode in the area from nStart to nEnd
1318     SwTextAttr* pHt;
1319     for( size_t n = 0; n < pHts->Count(); ++n )
1320     {
1321         // nAttrStt must even be set when !pEndIdx
1322         pHt = pHts->Get(n);
1323         const sal_Int32 nAttrStt = pHt->GetStart();
1324         const sal_Int32 * pEndIdx = pHt->GetEnd();
1325         if( nullptr !=  pEndIdx && nAttrStt > nEnd )
1326             break;
1327 
1328         // never copy Flys and Footnote !!
1329         bool bNextAttr = false;
1330         switch( pHt->Which() )
1331         {
1332         case RES_TXTATR_FIELD:
1333         case RES_TXTATR_ANNOTATION:
1334         case RES_TXTATR_INPUTFIELD:
1335             if( !bCopyFields )
1336                 bNextAttr = true;
1337             break;
1338         case RES_TXTATR_FLYCNT:
1339         case RES_TXTATR_FTN:
1340             bNextAttr = true;
1341             break;
1342         }
1343 
1344         if( bNextAttr )
1345             continue;
1346 
1347         // save all attributes that are somehow in this area
1348         if ( nStart <= nAttrStt )
1349         {
1350             if ( nEnd > nAttrStt )
1351             {
1352                 Add( pHt, nNodeIdx, false );
1353             }
1354         }
1355         else if ( pEndIdx && nStart < *pEndIdx )
1356         {
1357             Add( pHt, nNodeIdx, false );
1358         }
1359     }
1360 }
1361 
1362 // Class to register the history at a Node, Format, HintsArray, ...
SwRegHistory(SwHistory * pHst)1363 SwRegHistory::SwRegHistory( SwHistory* pHst )
1364     : SwClient( nullptr )
1365     , m_pHistory( pHst )
1366     , m_nNodeIndex( ULONG_MAX )
1367 {
1368     MakeSetWhichIds();
1369 }
1370 
SwRegHistory(sw::BroadcastingModify * pRegIn,const SwNode & rNd,SwHistory * pHst)1371 SwRegHistory::SwRegHistory( sw::BroadcastingModify* pRegIn, const SwNode& rNd,
1372                             SwHistory* pHst )
1373     : SwClient( pRegIn )
1374     , m_pHistory( pHst )
1375     , m_nNodeIndex( rNd.GetIndex() )
1376 {
1377     MakeSetWhichIds();
1378 }
1379 
SwRegHistory(const SwNode & rNd,SwHistory * pHst)1380 SwRegHistory::SwRegHistory( const SwNode& rNd, SwHistory* pHst )
1381     : SwClient( nullptr )
1382     , m_pHistory( pHst )
1383     , m_nNodeIndex( rNd.GetIndex() )
1384 {
1385     MakeSetWhichIds();
1386 }
1387 
SwClientNotify(const SwModify &,const SfxHint & rHint)1388 void SwRegHistory::SwClientNotify(const SwModify&, const SfxHint& rHint)
1389 {
1390     if (rHint.GetId() != SfxHintId::SwLegacyModify)
1391         return;
1392     auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
1393     if ( !(m_pHistory && pLegacyHint->m_pNew && pLegacyHint->m_pOld != pLegacyHint->m_pNew) )
1394         return;
1395 
1396     if ( pLegacyHint->m_pNew->Which() < POOLATTR_END )
1397     {
1398         if(RES_UPDATE_ATTR == pLegacyHint->m_pNew->Which())
1399         {
1400             m_pHistory->Add(pLegacyHint->m_pOld, pLegacyHint->m_pNew, m_nNodeIndex);
1401         }
1402         else
1403         {
1404             OSL_ENSURE(false, "Unexpected update attribute (!)");
1405         }
1406     }
1407     else if (pLegacyHint->m_pOld && RES_ATTRSET_CHG == pLegacyHint->m_pNew->Which())
1408     {
1409         std::unique_ptr<SwHistoryHint> pNewHstr;
1410         const SfxItemSet& rSet = *static_cast< const SwAttrSetChg* >(pLegacyHint->m_pOld)->GetChgSet();
1411 
1412         if ( 1 < rSet.Count() )
1413         {
1414             pNewHstr.reset( new SwHistorySetAttrSet( rSet, m_nNodeIndex, m_WhichIdSet ) );
1415         }
1416         else if (const SfxPoolItem* pItem = SfxItemIter(rSet).GetCurItem())
1417         {
1418             if ( m_WhichIdSet.count( pItem->Which() ) )
1419             {
1420                 pNewHstr.reset( new SwHistorySetFormat( pItem, m_nNodeIndex ) );
1421             }
1422             else
1423             {
1424                 pNewHstr.reset( new SwHistoryResetFormat( pItem, m_nNodeIndex ) );
1425             }
1426         }
1427 
1428         if (pNewHstr)
1429             m_pHistory->m_SwpHstry.push_back( std::move(pNewHstr) );
1430     }
1431 }
1432 
AddHint(SwTextAttr * pHt,const bool bNew)1433 void SwRegHistory::AddHint( SwTextAttr* pHt, const bool bNew )
1434 {
1435     m_pHistory->Add( pHt, m_nNodeIndex, bNew );
1436 }
1437 
InsertItems(const SfxItemSet & rSet,sal_Int32 const nStart,sal_Int32 const nEnd,SetAttrMode const nFlags,SwTextAttr ** ppNewTextAttr)1438 bool SwRegHistory::InsertItems( const SfxItemSet& rSet,
1439     sal_Int32 const nStart, sal_Int32 const nEnd, SetAttrMode const nFlags,
1440     SwTextAttr **ppNewTextAttr )
1441 {
1442     if( !rSet.Count() )
1443         return false;
1444 
1445     SwTextNode * const pTextNode =
1446         dynamic_cast<SwTextNode *>(GetRegisteredIn());
1447 
1448     OSL_ENSURE(pTextNode, "SwRegHistory not registered at text node?");
1449     if (!pTextNode)
1450         return false;
1451 
1452     if (m_pHistory)
1453     {
1454         pTextNode->GetOrCreateSwpHints().Register(this);
1455     }
1456 
1457     const bool bInserted = pTextNode->SetAttr( rSet, nStart, nEnd, nFlags, ppNewTextAttr );
1458 
1459     // Caution: The array can be deleted when inserting an attribute!
1460     // This can happen when the value that should be added first deletes
1461     // an existing attribute but does not need to be added itself because
1462     // the paragraph attributes are identical
1463     // ( -> bForgetAttr in SwpHints::Insert )
1464     if ( pTextNode->GetpSwpHints() && m_pHistory )
1465     {
1466         pTextNode->GetpSwpHints()->DeRegister();
1467     }
1468 
1469 #ifndef NDEBUG
1470     if ( m_pHistory && bInserted )
1471     {
1472         SfxItemIter aIter(rSet);
1473         for (SfxPoolItem const* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
1474         {   // check that the history recorded a hint to reset every item
1475             sal_uInt16 const nWhich(pItem->Which());
1476             sal_uInt16 const nExpected(
1477                 (isCHRATR(nWhich) || RES_TXTATR_UNKNOWN_CONTAINER == nWhich)
1478                     ? RES_TXTATR_AUTOFMT
1479                     : nWhich);
1480             if (RES_TXTATR_AUTOFMT == nExpected)
1481                 continue; // special case, may get set on text node itself
1482                           // tdf#105077 even worse, node's set could cause
1483                           // nothing at all to be inserted
1484             assert(std::any_of(
1485                 m_pHistory->m_SwpHstry.begin(), m_pHistory->m_SwpHstry.end(),
1486                 [nExpected](std::unique_ptr<SwHistoryHint> const& pHint) -> bool {
1487                     SwHistoryResetText const*const pReset(
1488                             dynamic_cast<SwHistoryResetText const*>(pHint.get()));
1489                     return pReset && (pReset->GetWhich() == nExpected);
1490                 }));
1491         }
1492     }
1493 #endif
1494 
1495     return bInserted;
1496 }
1497 
RegisterInModify(sw::BroadcastingModify * pRegIn,const SwNode & rNd)1498 void SwRegHistory::RegisterInModify( sw::BroadcastingModify* pRegIn, const SwNode& rNd )
1499 {
1500     if ( m_pHistory && pRegIn )
1501     {
1502         pRegIn->Add( this );
1503         m_nNodeIndex = rNd.GetIndex();
1504         MakeSetWhichIds();
1505     }
1506     else
1507     {
1508         m_WhichIdSet.clear();
1509     }
1510 }
1511 
MakeSetWhichIds()1512 void SwRegHistory::MakeSetWhichIds()
1513 {
1514     if (!m_pHistory) return;
1515 
1516     m_WhichIdSet.clear();
1517 
1518     if( !GetRegisteredIn() )
1519         return;
1520 
1521     const SfxItemSet* pSet = nullptr;
1522     if( auto pContentNode = dynamic_cast< const SwContentNode *>( GetRegisteredIn() )  )
1523     {
1524         pSet = pContentNode->GetpSwAttrSet();
1525     }
1526     else if ( auto pSwFormat = dynamic_cast< const SwFormat *>( GetRegisteredIn() )  )
1527     {
1528         pSet = &pSwFormat->GetAttrSet();
1529     }
1530     if( pSet && pSet->Count() )
1531     {
1532         SfxItemIter aIter( *pSet );
1533         for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
1534         {
1535             sal_uInt16 nW = pItem->Which();
1536             m_WhichIdSet.insert( nW );
1537         }
1538     }
1539 }
1540 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1541