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