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