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