xref: /core/sw/source/core/undo/unins.cxx (revision 3e86a879508e55673b4f6d24ce84eb98b332da63)
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 <UndoInsert.hxx>
21 
22 #include <hintids.hxx>
23 #include <unotools/charclass.hxx>
24 #include <editeng/keepitem.hxx>
25 #include <svx/svdobj.hxx>
26 #include <osl/diagnose.h>
27 
28 #include <fmtcntnt.hxx>
29 #include <frmfmt.hxx>
30 #include <doc.hxx>
31 #include <IDocumentUndoRedo.hxx>
32 #include <IDocumentDrawModelAccess.hxx>
33 #include <IDocumentRedlineAccess.hxx>
34 #include <IDocumentLayoutAccess.hxx>
35 #include <IShellCursorSupplier.hxx>
36 #include <swundo.hxx>
37 #include <pam.hxx>
38 #include <ndtxt.hxx>
39 #include <UndoCore.hxx>
40 #include <UndoDelete.hxx>
41 #include <UndoAttribute.hxx>
42 #include <rolbck.hxx>
43 #include <ndgrf.hxx>
44 #include <ndole.hxx>
45 #include <grfatr.hxx>
46 #include <cntfrm.hxx>
47 #include <flyfrm.hxx>
48 #include <swcrsr.hxx>
49 #include <swtable.hxx>
50 #include <redline.hxx>
51 #include <docary.hxx>
52 #include <acorrect.hxx>
53 
54 #include <strings.hrc>
55 #include <utility>
56 
57 using namespace ::com::sun::star;
58 
59 // INSERT
60 
GetTextFromDoc() const61 std::optional<OUString> SwUndoInsert::GetTextFromDoc() const
62 {
63     std::optional<OUString> aResult;
64 
65     SwNodeIndex aNd( m_rDoc.GetNodes(), m_nNode);
66     SwContentNode* pCNd = aNd.GetNode().GetContentNode();
67 
68     if( pCNd->IsTextNode() )
69     {
70         OUString sText = pCNd->GetTextNode()->GetText();
71 
72         sal_Int32 nStart = m_nContent-m_nLen;
73         sal_Int32 nLength = m_nLen;
74 
75         if (nStart < 0)
76         {
77             nLength += nStart;
78             nStart = 0;
79         }
80 
81         aResult = sText.copy(nStart, nLength);
82     }
83 
84     return aResult;
85 }
86 
Init()87 void SwUndoInsert::Init()
88 {
89     // consider Redline
90     if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
91     {
92         m_pRedlData.reset( new SwRedlineData( RedlineType::Insert,
93                                        m_rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() ) );
94         SetRedlineFlags( m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags() );
95     }
96 
97     maUndoText = GetTextFromDoc();
98 
99     m_bCacheComment = false;
100 }
101 
SwUndoInsert(const SwNode & rNd,sal_Int32 nCnt,sal_Int32 nL,const SwInsertFlags nInsertFlags,bool bWDelim)102 SwUndoInsert::SwUndoInsert( const SwNode& rNd, sal_Int32 nCnt,
103             sal_Int32 nL,
104             const SwInsertFlags nInsertFlags,
105             bool bWDelim )
106     : SwUndo(SwUndoId::TYPING, rNd.GetDoc()),
107         m_nNode( rNd.GetIndex() ), m_nContent(nCnt), m_nLen(nL),
108         m_bIsWordDelim( bWDelim ), m_bIsAppend( false )
109     , m_bWithRsid(false)
110     , m_nInsertFlags(nInsertFlags)
111     , m_rDoc(const_cast<SwDoc&>(rNd.GetDoc()))
112 {
113     Init();
114 }
115 
SwUndoInsert(const SwNode & rNd)116 SwUndoInsert::SwUndoInsert( const SwNode& rNd )
117     : SwUndo(SwUndoId::SPLITNODE, rNd.GetDoc()),
118         m_nNode( rNd.GetIndex() ), m_nContent(0), m_nLen(1),
119         m_bIsWordDelim( false ), m_bIsAppend( true )
120     , m_bWithRsid(false)
121     , m_nInsertFlags(SwInsertFlags::EMPTYEXPAND)
122     , m_rDoc(const_cast<SwDoc&>(rNd.GetDoc()))
123 {
124     Init();
125 }
126 
127 // Check if the next Insert can be combined with the current one. If so
128 // change the length and InsPos. As a result, SwDoc::Insert will not add a
129 // new object into the Undo list.
130 
CanGrouping(sal_Unicode cIns)131 bool SwUndoInsert::CanGrouping( sal_Unicode cIns )
132 {
133     if( !m_bIsAppend && m_bIsWordDelim ==
134         !GetAppCharClass().isLetterNumeric( OUString( cIns )) )
135     {
136         m_nLen++;
137         m_nContent++;
138 
139         if (maUndoText)
140             (*maUndoText) += OUStringChar(cIns);
141 
142         return true;
143     }
144     return false;
145 }
146 
CanGrouping(const SwPosition & rPos)147 bool SwUndoInsert::CanGrouping( const SwPosition& rPos )
148 {
149     bool bRet = false;
150     if( m_nNode == rPos.GetNodeIndex() &&
151         m_nContent == rPos.GetContentIndex() )
152     {
153         // consider Redline
154         SwDoc& rDoc = rPos.GetNode().GetDoc();
155         if( ( ~RedlineFlags::ShowMask & rDoc.getIDocumentRedlineAccess().GetRedlineFlags() ) ==
156             ( ~RedlineFlags::ShowMask & GetRedlineFlags() ) )
157         {
158             bRet = true;
159 
160             // then there is or was still an active Redline:
161             // Check if there is another Redline at the InsPosition. If the
162             // same exists only once, it can be combined.
163             const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
164             if( !rTable.empty() )
165             {
166                 SwRedlineData aRData( RedlineType::Insert, rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() );
167                 const SwContentNode* pIReg = rPos.GetContentNode();
168                 for(SwRangeRedline* pRedl : rTable)
169                 {
170                     const SwPosition& rIdx = *pRedl->End();
171                     if( pIReg == rIdx.GetContentNode() &&
172                         m_nContent == rIdx.GetContentIndex() )
173                     {
174                         if( !pRedl->HasMark() || !m_pRedlData ||
175                             *pRedl != *m_pRedlData || *pRedl != aRData )
176                         {
177                             bRet = false;
178                             break;
179                         }
180                     }
181                 }
182             }
183         }
184     }
185     return bRet;
186 }
187 
~SwUndoInsert()188 SwUndoInsert::~SwUndoInsert()
189 {
190     if (m_oUndoNodeIndex) // delete the section from UndoNodes array
191     {
192         // Insert saves the content in IconSection
193         SwNodes& rUNds = m_oUndoNodeIndex->GetNodes();
194         rUNds.Delete(*m_oUndoNodeIndex,
195             rUNds.GetEndOfExtras().GetIndex() - m_oUndoNodeIndex->GetIndex());
196         m_oUndoNodeIndex.reset();
197     }
198     else     // the inserted text
199     {
200         maText.reset();
201     }
202     m_pRedlData.reset();
203 }
204 
UndoImpl(::sw::UndoRedoContext & rContext)205 void SwUndoInsert::UndoImpl(::sw::UndoRedoContext & rContext)
206 {
207     SwDoc *const pTmpDoc = & rContext.GetDoc();
208     SwCursor *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
209 
210     if( m_bIsAppend )
211     {
212         pPam->GetPoint()->Assign(m_nNode);
213 
214         if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
215         {
216             pPam->SetMark();
217             pPam->Move( fnMoveBackward );
218             pPam->Exchange();
219             pTmpDoc->getIDocumentRedlineAccess().DeleteRedline( *pPam, true, RedlineType::Any );
220         }
221         pPam->DeleteMark();
222         pTmpDoc->getIDocumentContentOperations().DelFullPara( *pPam );
223         pPam->GetPoint()->SetContent( 0 );
224     }
225     else
226     {
227         SwNodeOffset nNd = m_nNode;
228         sal_Int32 nCnt = m_nContent;
229         if( m_nLen )
230         {
231             SwNodeIndex aNd( pTmpDoc->GetNodes(), m_nNode);
232             SwContentNode* pCNd = aNd.GetNode().GetContentNode();
233             SwPaM aPaM( *pCNd, m_nContent );
234 
235             aPaM.SetMark();
236 
237             SwTextNode * const pTextNode( pCNd->GetTextNode() );
238             if ( pTextNode )
239             {
240                 aPaM.GetPoint()->AdjustContent( - m_nLen );
241                 if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
242                     pTmpDoc->getIDocumentRedlineAccess().DeleteRedline( aPaM, true, RedlineType::Any );
243                 if (m_bWithRsid)
244                 {
245                     // RSID was added: remove any CHARFMT/AUTOFMT that may be
246                     // set on the deleted text; EraseText will leave empty
247                     // ones behind otherwise
248                     pTextNode->DeleteAttributes(RES_TXTATR_AUTOFMT,
249                         aPaM.GetPoint()->GetContentIndex(),
250                         aPaM.GetMark()->GetContentIndex());
251                     pTextNode->DeleteAttributes(RES_TXTATR_CHARFMT,
252                         aPaM.GetPoint()->GetContentIndex(),
253                         aPaM.GetMark()->GetContentIndex());
254                 }
255                 RemoveIdxFromRange( aPaM, false );
256                 maText = pTextNode->GetText().copy(m_nContent-m_nLen, m_nLen);
257                 pTextNode->EraseText( *aPaM.GetPoint(), m_nLen );
258             }
259             else                // otherwise Graphics/OLE/Text/...
260             {
261                 aPaM.Move(fnMoveBackward);
262                 if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
263                     pTmpDoc->getIDocumentRedlineAccess().DeleteRedline( aPaM, true, RedlineType::Any );
264                 RemoveIdxFromRange( aPaM, false );
265             }
266 
267             nNd = aPaM.GetPoint()->GetNodeIndex();
268             nCnt = aPaM.GetPoint()->GetContentIndex();
269 
270             if (!maText)
271             {
272                 m_oUndoNodeIndex.emplace(m_rDoc.GetNodes().GetEndOfContent());
273                 MoveToUndoNds(aPaM, &*m_oUndoNodeIndex);
274             }
275             m_nNode = aPaM.GetPoint()->GetNodeIndex();
276             m_nContent = aPaM.GetPoint()->GetContentIndex();
277         }
278 
279         // set cursor to Undo range
280         pPam->DeleteMark();
281 
282         pPam->GetPoint()->Assign( nNd, nCnt );
283     }
284 
285     maUndoText.reset();
286 }
287 
RedoImpl(::sw::UndoRedoContext & rContext)288 void SwUndoInsert::RedoImpl(::sw::UndoRedoContext & rContext)
289 {
290     SwDoc *const pTmpDoc = & rContext.GetDoc();
291     SwCursor *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
292     pPam->DeleteMark();
293 
294     if( m_bIsAppend )
295     {
296         pPam->GetPoint()->Assign( m_nNode - 1 );
297         pTmpDoc->getIDocumentContentOperations().AppendTextNode( *pPam->GetPoint() );
298 
299         pPam->SetMark();
300         pPam->Move( fnMoveBackward );
301         pPam->Exchange();
302 
303         if( m_pRedlData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
304         {
305             RedlineFlags eOld = pTmpDoc->getIDocumentRedlineAccess().GetRedlineFlags();
306             pTmpDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags::Ignore);
307             pTmpDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlData, *pPam ), true);
308             pTmpDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
309         }
310         else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) &&
311                 !pTmpDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() )
312             pTmpDoc->getIDocumentRedlineAccess().SplitRedline( *pPam );
313 
314         pPam->DeleteMark();
315     }
316     else
317     {
318         pPam->GetPoint()->Assign( m_nNode );
319         SwContentNode *const pCNd =
320             pPam->GetPoint()->GetNode().GetContentNode();
321         pPam->GetPoint()->SetContent( m_nContent );
322 
323         if( m_nLen )
324         {
325             ::std::optional<SwNodeIndex> oMvBkwrd = MovePtBackward(*pPam);
326 
327             if (maText)
328             {
329                 SwTextNode *const pTextNode = pCNd->GetTextNode();
330                 OSL_ENSURE( pTextNode, "where is my textnode ?" );
331                 OUString const ins(
332                     pTextNode->InsertText( *maText, *pPam->GetMark(),
333                     m_nInsertFlags) );
334                 assert(ins.getLength() == maText->getLength()); // must succeed
335                 maText.reset();
336                 if (m_bWithRsid) // re-insert RSID
337                 {
338                     SwPaM pam(*pPam->GetMark(), nullptr); // mark -> point
339                     pTmpDoc->UpdateRsid(pam, ins.getLength());
340                 }
341             }
342             else
343             {
344                 // re-insert content again (first detach m_oUndoNodeIndex!)
345                 SwNodeOffset const nMvNd = m_oUndoNodeIndex->GetIndex();
346                 m_oUndoNodeIndex.reset();
347                 MoveFromUndoNds(*pTmpDoc, nMvNd, *pPam->GetMark());
348             }
349             m_nNode = pPam->GetMark()->GetNodeIndex();
350             m_nContent = pPam->GetMark()->GetContentIndex();
351 
352             MovePtForward(*pPam, ::std::move(oMvBkwrd));
353             pPam->Exchange();
354             if( m_pRedlData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
355             {
356                 RedlineFlags eOld = pTmpDoc->getIDocumentRedlineAccess().GetRedlineFlags();
357                 pTmpDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags::Ignore);
358                 pTmpDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlData,
359                                             *pPam ), true);
360                 pTmpDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
361             }
362             else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) &&
363                     !pTmpDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() )
364                 pTmpDoc->getIDocumentRedlineAccess().SplitRedline(*pPam);
365         }
366     }
367 
368     maUndoText = GetTextFromDoc();
369 }
370 
RepeatImpl(::sw::RepeatContext & rContext)371 void SwUndoInsert::RepeatImpl(::sw::RepeatContext & rContext)
372 {
373     if( !m_nLen )
374         return;
375 
376     SwDoc & rDoc = rContext.GetDoc();
377     SwNodeIndex aNd( rDoc.GetNodes(), m_nNode );
378     SwContentNode* pCNd = aNd.GetNode().GetContentNode();
379 
380     if( !m_bIsAppend && 1 == m_nLen )       // >1 than always Text, otherwise Graphics/OLE/Text/...
381     {
382         SwPaM aPaM( *pCNd, m_nContent );
383         aPaM.SetMark();
384         aPaM.Move(fnMoveBackward);
385         pCNd = aPaM.GetPointContentNode();
386     }
387 
388 // What happens with the possible selected range ???
389 
390     switch( pCNd->GetNodeType() )
391     {
392     case SwNodeType::Text:
393         if( m_bIsAppend )
394         {
395             rDoc.getIDocumentContentOperations().AppendTextNode( *rContext.GetRepeatPaM().GetPoint() );
396         }
397         else
398         {
399             OUString const aText( pCNd->GetTextNode()->GetText() );
400             ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
401             rDoc.getIDocumentContentOperations().InsertString( rContext.GetRepeatPaM(),
402                 aText.copy(m_nContent - m_nLen, m_nLen) );
403         }
404         break;
405     case SwNodeType::Grf:
406         {
407             SwGrfNode* pGrfNd = static_cast<SwGrfNode*>(pCNd);
408             OUString sFile;
409             OUString sFilter;
410             if( pGrfNd->IsGrfLink() )
411                 pGrfNd->GetFileFilterNms( &sFile, &sFilter );
412 
413             rDoc.getIDocumentContentOperations().InsertGraphic(
414                                 rContext.GetRepeatPaM(), sFile, sFilter,
415                                 &pGrfNd->GetGrf(),
416                                 nullptr/* Graphics collection*/, nullptr, nullptr );
417         }
418         break;
419 
420     case SwNodeType::Ole:
421         {
422             // StarView does not yet provide an option to copy a StarOBJ
423             SwOLEObj& rSwOLE = static_cast<SwOLENode*>(pCNd)->GetOLEObj();
424 
425             // temporary storage until object is inserted
426             // TODO/MBA: seems that here a physical copy is done - not as in drawing layer! Testing!
427             // TODO/LATER: Copying through the container would copy the replacement image as well
428             comphelper::EmbeddedObjectContainer aCnt;
429             OUString aName = aCnt.CreateUniqueObjectName();
430             if (aCnt.StoreEmbeddedObject(rSwOLE.GetOleRef(), aName, true, OUString(), OUString()))
431             {
432                 uno::Reference < embed::XEmbeddedObject > aNew = aCnt.GetEmbeddedObject( aName );
433                 rDoc.getIDocumentContentOperations().InsertEmbObject(
434                     rContext.GetRepeatPaM(),
435                     svt::EmbeddedObjectRef( aNew,
436                         static_cast<SwOLENode*>(pCNd)->GetAspect() ),
437                     nullptr );
438             }
439 
440             break;
441         }
442 
443     default: break;
444     }
445 }
446 
GetRewriter() const447 SwRewriter SwUndoInsert::GetRewriter() const
448 {
449     SwRewriter aResult;
450     std::optional<OUString> aStr;
451     bool bDone = false;
452 
453     if (maText)
454         aStr = maText;
455     else if (maUndoText)
456         aStr = maUndoText;
457 
458     if (aStr)
459     {
460         OUString aString = ShortenString(DenoteSpecialCharacters(*aStr),
461                                        nUndoStringLength,
462                                        SwResId(STR_LDOTS));
463 
464         aResult.AddRule(UndoArg1, aString);
465 
466         bDone = true;
467     }
468 
469     if ( ! bDone )
470     {
471         aResult.AddRule(UndoArg1, u"??"_ustr);
472     }
473 
474     return aResult;
475 }
476 
IsIndependent(const SwUndoInsert & rOther) const477 bool SwUndoInsert::IsIndependent(const SwUndoInsert& rOther) const
478 {
479     return m_nNode != rOther.m_nNode;
480 }
481 
482 class SwUndoReplace::Impl
483     : private SwUndoSaveContent
484 {
485     OUString m_sOld;
486     OUString m_sIns;
487     SwNodeOffset m_nSttNd, m_nEndNd, m_nOffset;
488     sal_Int32 m_nSttCnt, m_nEndCnt, m_nSetPos, m_nSelEnd;
489     bool m_bSplitNext : 1;
490     bool m_bRegExp : 1;
491     // metadata references for paragraph and following para (if m_bSplitNext)
492     std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoStart;
493     std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoEnd;
494 
495 public:
496     Impl(SwPaM const& rPam, OUString aIns, bool const bRegExp);
497 
498     void UndoImpl( ::sw::UndoRedoContext & );
499     void RedoImpl( ::sw::UndoRedoContext & );
500 
501     void SetEnd(SwPaM const& rPam);
502 
GetOld() const503     OUString const& GetOld() const { return m_sOld; }
GetIns() const504     OUString const& GetIns() const { return m_sIns; }
505 };
506 
SwUndoReplace(SwPaM const & rPam,OUString const & rIns,bool const bRegExp)507 SwUndoReplace::SwUndoReplace(SwPaM const& rPam,
508         OUString const& rIns, bool const bRegExp)
509     : SwUndo( SwUndoId::REPLACE, rPam.GetDoc() )
510     , m_pImpl(std::make_unique<Impl>(rPam, rIns, bRegExp))
511 {
512 }
513 
~SwUndoReplace()514 SwUndoReplace::~SwUndoReplace()
515 {
516 }
517 
UndoImpl(::sw::UndoRedoContext & rContext)518 void SwUndoReplace::UndoImpl(::sw::UndoRedoContext & rContext)
519 {
520     m_pImpl->UndoImpl(rContext);
521 }
522 
RedoImpl(::sw::UndoRedoContext & rContext)523 void SwUndoReplace::RedoImpl(::sw::UndoRedoContext & rContext)
524 {
525     m_pImpl->RedoImpl(rContext);
526 }
527 
528 SwRewriter
MakeUndoReplaceRewriter(sal_uLong const occurrences,OUString const & sOld,OUString const & sNew)529 MakeUndoReplaceRewriter(sal_uLong const occurrences,
530         OUString const& sOld, OUString const& sNew)
531 {
532     SwRewriter aResult;
533 
534     if (1 < occurrences)
535     {
536         aResult.AddRule(UndoArg1, OUString::number(occurrences));
537         aResult.AddRule(UndoArg2, SwResId(STR_OCCURRENCES_OF));
538 
539         OUString aTmpStr =
540             SwResId(STR_START_QUOTE)
541             + ShortenString(sOld, nUndoStringLength, SwResId(STR_LDOTS))
542             + SwResId(STR_END_QUOTE);
543         aResult.AddRule(UndoArg3, aTmpStr);
544     }
545     else if (1 == occurrences)
546     {
547         {
548             // #i33488 #
549             OUString aTmpStr =
550                 SwResId(STR_START_QUOTE)
551                 + ShortenString(sOld, nUndoStringLength, SwResId(STR_LDOTS))
552                 + SwResId(STR_END_QUOTE);
553             aResult.AddRule(UndoArg1, aTmpStr);
554         }
555 
556         aResult.AddRule(UndoArg2, SwResId(STR_YIELDS));
557 
558         {
559             // #i33488 #
560             OUString aTmpStr =
561                 SwResId(STR_START_QUOTE)
562                 + ShortenString(sNew, nUndoStringLength, SwResId(STR_LDOTS))
563                 + SwResId(STR_END_QUOTE);
564             aResult.AddRule(UndoArg3, aTmpStr);
565         }
566     }
567 
568     return aResult;
569 }
570 
GetRewriter() const571 SwRewriter SwUndoReplace::GetRewriter() const
572 {
573     return MakeUndoReplaceRewriter(1, m_pImpl->GetOld(), m_pImpl->GetIns());
574 }
575 
SetEnd(SwPaM const & rPam)576 void SwUndoReplace::SetEnd(SwPaM const& rPam)
577 {
578     m_pImpl->SetEnd(rPam);
579 }
580 
Impl(SwPaM const & rPam,OUString aIns,bool const bRegExp)581 SwUndoReplace::Impl::Impl(
582         SwPaM const& rPam, OUString aIns, bool const bRegExp)
583     : m_sIns(std::move( aIns ))
584     , m_nOffset( 0 )
585     , m_bRegExp(bRegExp)
586 {
587 
588     auto [pStart, pEnd] = rPam.StartEnd(); // SwPosition*
589 
590     m_nSttNd = m_nEndNd = pStart->GetNodeIndex();
591     m_nSttCnt = pStart->GetContentIndex();
592     m_nSelEnd = m_nEndCnt = pEnd->GetContentIndex();
593 
594     m_bSplitNext = m_nSttNd != pEnd->GetNodeIndex();
595 
596     SwTextNode* pNd = pStart->GetNode().GetTextNode();
597     assert(pNd && "Dude, where's my TextNode?");
598 
599     m_pHistory.reset( new SwHistory );
600     DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), DelContentType::AllMask | DelContentType::Replace);
601 
602     m_nSetPos = m_pHistory->Count();
603 
604     SwNodeOffset nNewPos = pStart->GetNodeIndex();
605     m_nOffset = m_nSttNd - nNewPos;
606 
607     if ( pNd->GetpSwpHints() )
608     {
609         m_pHistory->CopyAttr( pNd->GetpSwpHints(), nNewPos, 0,
610                             pNd->GetText().getLength(), true );
611     }
612 
613     if ( m_bSplitNext )
614     {
615         if( pNd->HasSwAttrSet() )
616             m_pHistory->CopyFormatAttr( *pNd->GetpSwAttrSet(), nNewPos );
617         m_pHistory->AddColl(pNd->GetTextColl(), nNewPos, SwNodeType::Text);
618 
619         SwTextNode* pNext = pEnd->GetNode().GetTextNode();
620         SwNodeOffset nTmp = pNext->GetIndex();
621         m_pHistory->CopyAttr( pNext->GetpSwpHints(), nTmp, 0,
622                             pNext->GetText().getLength(), true );
623         if( pNext->HasSwAttrSet() )
624             m_pHistory->CopyFormatAttr( *pNext->GetpSwAttrSet(), nTmp );
625         m_pHistory->AddColl(pNext->GetTextColl(),nTmp, SwNodeType::Text);
626         // METADATA: store
627         m_pMetadataUndoStart = pNd  ->CreateUndo();
628         m_pMetadataUndoEnd   = pNext->CreateUndo();
629     }
630 
631     if( !m_pHistory->Count() )
632     {
633         m_pHistory.reset();
634     }
635 
636     const sal_Int32 nECnt = m_bSplitNext ? pNd->GetText().getLength()
637         : pEnd->GetContentIndex();
638     m_sOld = pNd->GetText().copy( m_nSttCnt, nECnt - m_nSttCnt );
639 }
640 
UndoImpl(::sw::UndoRedoContext & rContext)641 void SwUndoReplace::Impl::UndoImpl(::sw::UndoRedoContext & rContext)
642 {
643     SwDoc& rDoc = rContext.GetDoc();
644     SwCursor & rPam(rContext.GetCursorSupplier().CreateNewShellCursor());
645     rPam.DeleteMark();
646 
647     SwTextNode* pNd = rDoc.GetNodes()[ m_nSttNd - m_nOffset ]->GetTextNode();
648     OSL_ENSURE( pNd, "Dude, where's my TextNode?" );
649 
650     SwAutoCorrExceptWord* pACEWord = rDoc.GetAutoCorrExceptWord();
651     if( pACEWord )
652     {
653         if ((1 == m_sIns.getLength()) && (1 == m_sOld.getLength()))
654         {
655             SwPosition aPos( *pNd, m_nSttCnt );
656             pACEWord->CheckChar( aPos, m_sOld[ 0 ] );
657         }
658         rDoc.SetAutoCorrExceptWord( nullptr );
659     }
660 
661     // don't look at m_sIns for deletion, maybe it was not completely inserted
662     {
663         rPam.GetPoint()->Assign(*pNd, m_nSttCnt );
664         rPam.SetMark();
665         rPam.GetPoint()->Assign( m_nSttNd - m_nOffset, m_nSttNd == m_nEndNd ? m_nEndCnt : pNd->Len());
666 
667         // replace only in start node, without regex
668         bool const ret = rDoc.getIDocumentContentOperations().ReplaceRange(rPam, m_sOld, false);
669         assert(ret); (void)ret;
670         if (m_nSttNd != m_nEndNd)
671         {   // in case of regex inserting paragraph breaks, join nodes...
672             assert(rPam.GetMark()->GetContentIndex() == rPam.GetMark()->GetNode().GetTextNode()->Len());
673             rPam.GetPoint()->Assign( m_nEndNd - m_nOffset, m_nEndCnt );
674             rDoc.getIDocumentContentOperations().DeleteAndJoin(rPam);
675         }
676         if (*rPam.GetMark() == *rPam.GetPoint())
677             rPam.DeleteMark();
678         else
679             rPam.Normalize(false);
680         pNd = rDoc.GetNodes()[ m_nSttNd - m_nOffset ]->GetTextNode();
681         OSL_ENSURE( pNd, "Dude, where's my TextNode?" );
682     }
683 
684     if( m_bSplitNext )
685     {
686         assert(pNd && m_nSttCnt + m_sOld.getLength() <= pNd->Len());
687         SwPosition aPos(*pNd, m_nSttCnt + m_sOld.getLength());
688         rDoc.getIDocumentContentOperations().SplitNode( aPos, false );
689         pNd->RestoreMetadata(m_pMetadataUndoEnd);
690         pNd = rDoc.GetNodes()[ m_nSttNd - m_nOffset ]->GetTextNode();
691         // METADATA: restore
692         pNd->RestoreMetadata(m_pMetadataUndoStart);
693     }
694 
695     if( m_pHistory )
696     {
697         if( pNd->GetpSwpHints() )
698             pNd->ClearSwpHintsArr( true );
699 
700         m_pHistory->TmpRollback( rDoc, m_nSetPos, false );
701         if ( m_nSetPos ) // there were footnotes/FlyFrames
702         {
703             // are there others than these?
704             if( m_nSetPos < m_pHistory->Count() )
705             {
706                 // than save those attributes as well
707                 SwHistory aHstr;
708                 aHstr.Move( 0, m_pHistory.get(), m_nSetPos );
709                 m_pHistory->Rollback( rDoc );
710                 m_pHistory->Move( 0, &aHstr );
711             }
712             else
713             {
714                 m_pHistory->Rollback( rDoc );
715                 m_pHistory.reset();
716             }
717         }
718     }
719 }
720 
RedoImpl(::sw::UndoRedoContext & rContext)721 void SwUndoReplace::Impl::RedoImpl(::sw::UndoRedoContext & rContext)
722 {
723     SwDoc & rDoc = rContext.GetDoc();
724     SwCursor & rPam(rContext.GetCursorSupplier().CreateNewShellCursor());
725     rPam.DeleteMark();
726     rPam.GetPoint()->Assign( m_nSttNd, m_nSttCnt );
727 
728     rPam.SetMark();
729     if( m_bSplitNext )
730         rPam.GetPoint()->Assign( m_nSttNd + 1 );
731     rPam.GetPoint()->SetContent( m_nSelEnd );
732 
733     if( m_pHistory )
734     {
735         auto xSave = std::make_unique<SwHistory>();
736         std::swap(m_pHistory, xSave);
737 
738         DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), DelContentType::AllMask | DelContentType::Replace);
739         m_nSetPos = m_pHistory->Count();
740 
741         std::swap(xSave, m_pHistory);
742         m_pHistory->Move(0, xSave.get());
743     }
744     else
745     {
746         m_pHistory.reset( new SwHistory );
747         DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), DelContentType::AllMask | DelContentType::Replace);
748         m_nSetPos = m_pHistory->Count();
749         if( !m_nSetPos )
750         {
751             m_pHistory.reset();
752         }
753     }
754 
755     rDoc.getIDocumentContentOperations().ReplaceRange( rPam, m_sIns, m_bRegExp );
756     if (*rPam.GetMark() == *rPam.GetPoint())
757         rPam.DeleteMark();
758     else
759         rPam.Normalize(false);
760 }
761 
SetEnd(SwPaM const & rPam)762 void SwUndoReplace::Impl::SetEnd(SwPaM const& rPam)
763 {
764     const SwPosition* pEnd = rPam.End();
765     m_nEndNd = m_nOffset + pEnd->GetNodeIndex();
766     m_nEndCnt = pEnd->GetContentIndex();
767 }
768 
SwUndoReRead(const SwPaM & rPam,const SwGrfNode & rGrfNd)769 SwUndoReRead::SwUndoReRead( const SwPaM& rPam, const SwGrfNode& rGrfNd )
770     : SwUndo( SwUndoId::REREAD, rPam.GetDoc() ), mnPosition( rPam.GetPoint()->GetNodeIndex() )
771 {
772     SaveGraphicData( rGrfNd );
773 }
774 
~SwUndoReRead()775 SwUndoReRead::~SwUndoReRead()
776 {
777 }
778 
SetAndSave(::sw::UndoRedoContext & rContext)779 void SwUndoReRead::SetAndSave(::sw::UndoRedoContext & rContext)
780 {
781     SwDoc & rDoc = rContext.GetDoc();
782     SwGrfNode* pGrfNd = rDoc.GetNodes()[ mnPosition ]->GetGrfNode();
783 
784     if( !pGrfNd )
785         return ;
786 
787     // cache the old values
788     std::optional<Graphic> oOldGrf(moGraphic);
789     std::optional<OUString> aOldNm = maNm;
790     MirrorGraph nOldMirr = mnMirror;
791     // since all of them are cleared/modified by SaveGraphicData:
792     SaveGraphicData( *pGrfNd );
793 
794     if( aOldNm )
795     {
796         pGrfNd->ReRead( *aOldNm, maFltr ? *maFltr : OUString() );
797     }
798     else
799     {
800         pGrfNd->ReRead( OUString(), OUString(), oOldGrf ? &*oOldGrf : nullptr );
801     }
802 
803     if( MirrorGraph::Dont != nOldMirr )
804         pGrfNd->SetAttr( SwMirrorGrf() );
805 
806     rContext.SetSelections(pGrfNd->GetFlyFormat(), nullptr);
807 }
808 
UndoImpl(::sw::UndoRedoContext & rContext)809 void SwUndoReRead::UndoImpl(::sw::UndoRedoContext & rContext)
810 {
811     SetAndSave(rContext);
812 }
813 
RedoImpl(::sw::UndoRedoContext & rContext)814 void SwUndoReRead::RedoImpl(::sw::UndoRedoContext & rContext)
815 {
816     SetAndSave(rContext);
817 }
818 
SaveGraphicData(const SwGrfNode & rGrfNd)819 void SwUndoReRead::SaveGraphicData( const SwGrfNode& rGrfNd )
820 {
821     if( rGrfNd.IsGrfLink() )
822     {
823         maNm = OUString();
824         maFltr = OUString();
825         rGrfNd.GetFileFilterNms(&*maNm, &*maFltr);
826         moGraphic.reset();
827     }
828     else
829     {
830         moGraphic.emplace( rGrfNd.GetGrf(true) );
831         maNm.reset();
832         maFltr.reset();
833     }
834     mnMirror = rGrfNd.GetSwAttrSet().GetMirrorGrf().GetValue();
835 }
836 
SwUndoInsertLabel(const SwLabelType eTyp,OUString aText,OUString aSeparator,OUString aNumberSeparator,const bool bBef,const sal_uInt16 nInitId,UIName aCharacterStyle,const bool bCpyBorder,const SwDoc & rDoc)837 SwUndoInsertLabel::SwUndoInsertLabel( const SwLabelType eTyp,
838                                       OUString aText,
839                                       OUString aSeparator,
840                                       OUString aNumberSeparator,
841                                       const bool bBef,
842                                       const sal_uInt16 nInitId,
843                                       UIName aCharacterStyle,
844                                       const bool bCpyBorder,
845                                       const SwDoc& rDoc )
846     : SwUndo( SwUndoId::INSERTLABEL, rDoc ),
847       m_sText(std::move( aText )),
848       m_sSeparator(std::move( aSeparator )),
849       m_sNumberSeparator(std::move( aNumberSeparator )),//#i61007# order of captions
850       m_sCharacterStyle(std::move( aCharacterStyle )),
851       m_nFieldId( nInitId ),
852       m_eType( eTyp ),
853       m_nLayerId( 0 ),
854       m_bBefore( bBef ),
855       m_bCopyBorder( bCpyBorder )
856 {
857     m_bUndoKeep = false;
858     OBJECT.pUndoFly = nullptr;
859     OBJECT.pUndoAttr = nullptr;
860 }
861 
~SwUndoInsertLabel()862 SwUndoInsertLabel::~SwUndoInsertLabel()
863 {
864     if( SwLabelType::Object == m_eType || SwLabelType::Draw == m_eType )
865     {
866         delete OBJECT.pUndoFly;
867         delete OBJECT.pUndoAttr;
868     }
869     else
870         delete NODE.pUndoInsNd;
871 }
872 
UndoImpl(::sw::UndoRedoContext & rContext)873 void SwUndoInsertLabel::UndoImpl(::sw::UndoRedoContext & rContext)
874 {
875     SwDoc & rDoc = rContext.GetDoc();
876 
877     if( SwLabelType::Object == m_eType || SwLabelType::Draw == m_eType )
878     {
879         OSL_ENSURE( OBJECT.pUndoAttr && OBJECT.pUndoFly, "Pointer not initialized" );
880         SwFrameFormat* pFormat;
881         SdrObject *pSdrObj = nullptr;
882         if( OBJECT.pUndoAttr &&
883             nullptr != (pFormat = static_cast<SwFrameFormat*>(OBJECT.pUndoAttr->GetFormat( rDoc ))) &&
884             ( SwLabelType::Draw != m_eType ||
885               nullptr != (pSdrObj = pFormat->FindSdrObject()) ) )
886         {
887             OBJECT.pUndoAttr->UndoImpl(rContext);
888             OBJECT.pUndoFly->UndoImpl(rContext);
889             if( SwLabelType::Draw == m_eType )
890             {
891                 pSdrObj->SetLayer( m_nLayerId );
892             }
893         }
894     }
895     else if( NODE.nNode )
896     {
897         if ( m_eType == SwLabelType::Table && m_bUndoKeep )
898         {
899             SwTableNode *pNd = rDoc.GetNodes()[
900                         rDoc.GetNodes()[NODE.nNode-1]->StartOfSectionIndex()]->GetTableNode();
901             if ( pNd )
902                 pNd->GetTable().GetFrameFormat()->ResetFormatAttr( RES_KEEP );
903         }
904         SwPaM aPam( rDoc.GetNodes().GetEndOfContent() );
905         aPam.GetPoint()->Assign( NODE.nNode );
906         aPam.SetMark();
907         aPam.GetPoint()->Assign( NODE.nNode + 1 );
908         NODE.pUndoInsNd = new SwUndoDelete(aPam, SwDeleteFlags::Default, true);
909     }
910 }
911 
RedoImpl(::sw::UndoRedoContext & rContext)912 void SwUndoInsertLabel::RedoImpl(::sw::UndoRedoContext & rContext)
913 {
914     SwDoc & rDoc = rContext.GetDoc();
915 
916     if( SwLabelType::Object == m_eType || SwLabelType::Draw == m_eType )
917     {
918         OSL_ENSURE( OBJECT.pUndoAttr && OBJECT.pUndoFly, "Pointer not initialized" );
919         SwFrameFormat* pFormat;
920         SdrObject *pSdrObj = nullptr;
921         if( OBJECT.pUndoAttr &&
922             nullptr != (pFormat = static_cast<SwFrameFormat*>(OBJECT.pUndoAttr->GetFormat( rDoc ))) &&
923             ( SwLabelType::Draw != m_eType ||
924               nullptr != (pSdrObj = pFormat->FindSdrObject()) ) )
925         {
926             OBJECT.pUndoFly->RedoImpl(rContext);
927             OBJECT.pUndoAttr->RedoImpl(rContext);
928             if( SwLabelType::Draw == m_eType )
929             {
930                 pSdrObj->SetLayer( m_nLayerId );
931                 if( pSdrObj->GetLayer() == rDoc.getIDocumentDrawModelAccess().GetHellId() )
932                     pSdrObj->SetLayer( rDoc.getIDocumentDrawModelAccess().GetHeavenId() );
933                 // OD 02.07.2003 #108784#
934                 else if( pSdrObj->GetLayer() == rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId() )
935                     pSdrObj->SetLayer( rDoc.getIDocumentDrawModelAccess().GetInvisibleHeavenId() );
936             }
937         }
938     }
939     else if( NODE.pUndoInsNd )
940     {
941         if ( m_eType == SwLabelType::Table && m_bUndoKeep )
942         {
943             SwTableNode *pNd = rDoc.GetNodes()[
944                         rDoc.GetNodes()[NODE.nNode-1]->StartOfSectionIndex()]->GetTableNode();
945             if ( pNd )
946                 pNd->GetTable().GetFrameFormat()->SetFormatAttr( SvxFormatKeepItem(true, RES_KEEP) );
947         }
948         NODE.pUndoInsNd->UndoImpl(rContext);
949         delete NODE.pUndoInsNd;
950         NODE.pUndoInsNd = nullptr;
951     }
952 }
953 
RepeatImpl(::sw::RepeatContext & rContext)954 void SwUndoInsertLabel::RepeatImpl(::sw::RepeatContext & rContext)
955 {
956     SwDoc & rDoc = rContext.GetDoc();
957     const SwPosition& rPos = *rContext.GetRepeatPaM().GetPoint();
958 
959     SwNodeOffset nIdx(0);
960 
961     SwContentNode* pCNd = rPos.GetNode().GetContentNode();
962     if( pCNd )
963         switch( m_eType )
964         {
965         case SwLabelType::Table:
966             {
967                 const SwTableNode* pTNd = pCNd->FindTableNode();
968                 if( pTNd )
969                     nIdx = pTNd->GetIndex();
970             }
971             break;
972 
973         case SwLabelType::Fly:
974         case SwLabelType::Object:
975             {
976                 SwFlyFrame* pFly;
977                 SwContentFrame *pCnt = pCNd->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() );
978                 if( pCnt && nullptr != ( pFly = pCnt->FindFlyFrame() ) )
979                     nIdx = pFly->GetFormat()->GetContent().GetContentIdx()->GetIndex();
980             }
981             break;
982         case SwLabelType::Draw:
983             break;
984         }
985 
986     if( nIdx )
987     {
988         rDoc.InsertLabel( m_eType, m_sText, m_sSeparator, m_sNumberSeparator, m_bBefore,
989             m_nFieldId, nIdx, m_sCharacterStyle, m_bCopyBorder );
990     }
991 }
992 
GetRewriter() const993 SwRewriter SwUndoInsertLabel::GetRewriter() const
994 {
995     return CreateRewriter(m_sText);
996 }
997 
CreateRewriter(const OUString & rStr)998 SwRewriter SwUndoInsertLabel::CreateRewriter(const OUString &rStr)
999 {
1000     SwRewriter aRewriter;
1001 
1002     OUString aTmpStr;
1003 
1004     if (!rStr.isEmpty())
1005     {
1006         aTmpStr =
1007             SwResId(STR_START_QUOTE)
1008             + ShortenString(rStr, nUndoStringLength, SwResId(STR_LDOTS))
1009             + SwResId(STR_END_QUOTE);
1010     }
1011 
1012     aRewriter.AddRule(UndoArg1, aTmpStr);
1013 
1014     return aRewriter;
1015 }
1016 
SetFlys(SwFrameFormat & rOldFly,SfxItemSet const & rChgSet,SwFrameFormat & rNewFly)1017 void SwUndoInsertLabel::SetFlys( SwFrameFormat& rOldFly, SfxItemSet const & rChgSet,
1018                                 SwFrameFormat& rNewFly )
1019 {
1020     if( SwLabelType::Object == m_eType || SwLabelType::Draw == m_eType )
1021     {
1022         SwUndoFormatAttrHelper aTmp( rOldFly, false );
1023         rOldFly.SetFormatAttr( rChgSet );
1024         if ( aTmp.GetUndo() )
1025         {
1026             OBJECT.pUndoAttr = aTmp.ReleaseUndo().release();
1027         }
1028         OBJECT.pUndoFly = new SwUndoInsLayFormat( &rNewFly, SwNodeOffset(0), 0 );
1029     }
1030 }
1031 
SetDrawObj(SdrLayerID nLId)1032 void SwUndoInsertLabel::SetDrawObj( SdrLayerID nLId )
1033 {
1034     if( SwLabelType::Draw == m_eType )
1035     {
1036         m_nLayerId = nLId;
1037     }
1038 }
1039 
1040 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1041