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