1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <libxml/xmlwriter.h>
22 #include <IShellCursorSupplier.hxx>
24 #include <fmtanchr.hxx>
28 #include <UndoManager.hxx>
29 #include <IDocumentRedlineAccess.hxx>
35 #include <UndoCore.hxx>
37 #include <ndnotxt.hxx>
40 #include <redline.hxx>
41 #include <crossrefbookmark.hxx>
42 #include <strings.hrc>
45 #include <frameformats.hxx>
46 #include <o3tl/deleter.hxx>
47 #include <sal/log.hxx>
49 // This class saves the Pam as integers and can recompose those into a PaM
51 : m_nSttNode( 0 ), m_nEndNode( 0 ), m_nSttContent( 0 ), m_nEndContent( 0 )
55 SwUndRng::SwUndRng( const SwPaM
& rPam
)
60 void SwUndRng::SetValues( const SwPaM
& rPam
)
62 const SwPosition
*pStt
= rPam
.Start();
65 const SwPosition
*pEnd
= rPam
.End();
66 m_nEndNode
= pEnd
->GetNodeIndex();
67 m_nEndContent
= pEnd
->GetContentIndex();
72 m_nEndNode
= SwNodeOffset(0);
73 m_nEndContent
= COMPLETE_STRING
;
76 m_nSttNode
= pStt
->GetNodeIndex();
77 m_nSttContent
= pStt
->GetContentIndex();
80 void SwUndRng::SetPaM( SwPaM
& rPam
, bool bCorrToContent
) const
83 rPam
.GetPoint()->Assign( m_nSttNode
, m_nSttContent
);
84 SwNode
& rNd
= rPam
.GetPointNode();
85 if( !rNd
.IsContentNode() && bCorrToContent
)
86 rPam
.Move( fnMoveForward
, GoInContent
);
88 if( !m_nEndNode
&& COMPLETE_STRING
== m_nEndContent
) // no selection
92 if( m_nSttNode
== m_nEndNode
&& m_nSttContent
== m_nEndContent
)
93 return; // nothing left to do
95 rPam
.GetPoint()->Assign( m_nEndNode
, m_nEndContent
);
96 if( !rPam
.GetPointNode().IsContentNode() && bCorrToContent
)
97 rPam
.Move( fnMoveBackward
, GoInContent
);
100 SwPaM
& SwUndRng::AddUndoRedoPaM(
101 ::sw::UndoRedoContext
& rContext
, bool const bCorrToContent
) const
103 SwCursor
& rPaM( rContext
.GetCursorSupplier().CreateNewShellCursor() );
104 SetPaM( rPaM
, bCorrToContent
);
108 void SwUndo::RemoveIdxFromSection( SwDoc
& rDoc
, SwNodeOffset nSttIdx
,
109 const SwNodeOffset
* pEndIdx
)
111 SwNodeIndex
aIdx( rDoc
.GetNodes(), nSttIdx
);
112 SwNodeIndex
aEndIdx( rDoc
.GetNodes(), pEndIdx
? *pEndIdx
113 : aIdx
.GetNode().EndOfSectionIndex() );
114 SwPosition
aPos( rDoc
.GetNodes().GetEndOfPostIts() );
115 SwDoc::CorrAbs( aIdx
, aEndIdx
, aPos
, true );
118 void SwUndo::RemoveIdxFromRange( SwPaM
& rPam
, bool bMoveNext
)
120 const SwPosition
* pEnd
= rPam
.End();
123 if( pEnd
!= rPam
.GetPoint() )
126 SwNodeIndex
aStt( rPam
.GetMark()->GetNode() );
127 SwNodeIndex
aEnd( rPam
.GetPoint()->GetNode() );
129 if( !rPam
.Move( fnMoveForward
) )
132 if( !rPam
.Move( fnMoveBackward
) )
134 rPam
.GetPoint()->Assign( rPam
.GetDoc().GetNodes().GetEndOfPostIts() );
138 SwDoc::CorrAbs( aStt
, aEnd
, *rPam
.GetPoint(), true );
141 SwDoc::CorrAbs( rPam
, *pEnd
, true );
144 void SwUndo::RemoveIdxRel( SwNodeOffset nIdx
, const SwPosition
& rPos
)
146 // Move only the Cursor. Bookmarks/TOXMarks/etc. are done by the corresponding
148 ::PaMCorrRel( *rPos
.GetNode().GetNodes()[nIdx
], rPos
);
151 SwUndo::SwUndo(SwUndoId
const nId
, const SwDoc
* pDoc
)
152 : m_nId(nId
), m_nOrigRedlineFlags(RedlineFlags::NONE
)
153 , m_nViewShellId(CreateViewShellId(pDoc
))
154 , m_isRepeatIgnored(false)
155 , m_bCacheComment(true)
159 ViewShellId
SwUndo::CreateViewShellId(const SwDoc
* pDoc
)
161 ViewShellId
nRet(-1);
163 if (const SwDocShell
* pDocShell
= pDoc
->GetDocShell())
165 if (const SwView
* pView
= pDocShell
->GetView())
166 nRet
= pView
->GetViewShellId();
172 bool SwUndo::IsDelBox() const
174 return GetId() == SwUndoId::COL_DELETE
|| GetId() == SwUndoId::ROW_DELETE
||
175 GetId() == SwUndoId::TABLE_DELBOX
;
184 class UndoRedoRedlineGuard
187 UndoRedoRedlineGuard(::sw::UndoRedoContext
const & rContext
, SwUndo
const & rUndo
)
188 : m_rRedlineAccess(rContext
.GetDoc().getIDocumentRedlineAccess())
189 , m_eMode(m_rRedlineAccess
.GetRedlineFlags())
191 RedlineFlags
const eTmpMode
= rUndo
.GetRedlineFlags();
192 if ((RedlineFlags::ShowMask
& eTmpMode
) != (RedlineFlags::ShowMask
& m_eMode
))
194 m_rRedlineAccess
.SetRedlineFlags( eTmpMode
);
196 m_rRedlineAccess
.SetRedlineFlags_intern( eTmpMode
| RedlineFlags::Ignore
);
198 ~UndoRedoRedlineGuard()
200 m_rRedlineAccess
.SetRedlineFlags(m_eMode
);
203 IDocumentRedlineAccess
& m_rRedlineAccess
;
204 RedlineFlags
const m_eMode
;
211 assert(false); // SwUndo::Undo(): ERROR: must call UndoWithContext instead
216 assert(false); // SwUndo::Redo(): ERROR: must call RedoWithContext instead
219 void SwUndo::UndoWithContext(SfxUndoContext
& rContext
)
221 ::sw::UndoRedoContext
*const pContext(
222 dynamic_cast< ::sw::UndoRedoContext
* >(& rContext
));
224 const UndoRedoRedlineGuard
aUndoRedoRedlineGuard(*pContext
, *this);
228 void SwUndo::RedoWithContext(SfxUndoContext
& rContext
)
230 ::sw::UndoRedoContext
*const pContext(
231 dynamic_cast< ::sw::UndoRedoContext
* >(& rContext
));
233 const UndoRedoRedlineGuard
aUndoRedoRedlineGuard(*pContext
, *this);
237 void SwUndo::Repeat(SfxRepeatTarget
& rContext
)
239 if (m_isRepeatIgnored
)
241 return; // ignore Repeat for multi-selections
243 ::sw::RepeatContext
*const pRepeatContext(
244 dynamic_cast< ::sw::RepeatContext
* >(& rContext
));
245 assert(pRepeatContext
);
246 RepeatImpl(*pRepeatContext
);
249 bool SwUndo::CanRepeat(SfxRepeatTarget
& rContext
) const
251 assert(dynamic_cast< ::sw::RepeatContext
* >(& rContext
));
253 // a MultiSelection action that doesn't do anything must still return true
254 return (SwUndoId::REPEAT_START
<= GetId()) && (GetId() < SwUndoId::REPEAT_END
);
257 void SwUndo::RepeatImpl( ::sw::RepeatContext
& )
261 OUString
GetUndoComment(SwUndoId eId
)
266 case SwUndoId::EMPTY
:
269 case SwUndoId::START
:
272 case SwUndoId::DELETE
:
273 pId
= STR_DELETE_UNDO
;
275 case SwUndoId::INSERT
:
276 pId
= STR_INSERT_UNDO
;
278 case SwUndoId::OVERWRITE
:
281 case SwUndoId::SPLITNODE
:
282 pId
= STR_SPLITNODE_UNDO
;
284 case SwUndoId::INSATTR
:
285 pId
= STR_INSATTR_UNDO
;
287 case SwUndoId::SETFMTCOLL
:
288 pId
= STR_SETFMTCOLL_UNDO
;
290 case SwUndoId::RESETATTR
:
291 pId
= STR_RESET_ATTR_UNDO
;
293 case SwUndoId::INSFMTATTR
:
294 pId
= STR_INSFMT_ATTR_UNDO
;
296 case SwUndoId::INSDOKUMENT
:
297 pId
= STR_INSERT_DOC_UNDO
;
302 case SwUndoId::INSTABLE
:
303 pId
= STR_INSTABLE_UNDO
;
305 case SwUndoId::TABLETOTEXT
:
306 pId
= STR_TABLETOTEXT_UNDO
;
308 case SwUndoId::TEXTTOTABLE
:
309 pId
= STR_TEXTTOTABLE_UNDO
;
311 case SwUndoId::SORT_TXT
:
314 case SwUndoId::INSLAYFMT
:
317 case SwUndoId::TABLEHEADLINE
:
318 pId
= STR_TABLEHEADLINE
;
320 case SwUndoId::INSSECTION
:
321 pId
= STR_INSERTSECTION
;
323 case SwUndoId::OUTLINE_LR
:
324 pId
= STR_OUTLINE_LR
;
326 case SwUndoId::OUTLINE_UD
:
327 pId
= STR_OUTLINE_UD
;
329 case SwUndoId::OUTLINE_EDIT
:
330 pId
= STR_OUTLINE_EDIT
;
332 case SwUndoId::INSNUM
:
335 case SwUndoId::NUMUP
:
338 case SwUndoId::MOVENUM
:
341 case SwUndoId::INSDRAWFMT
:
342 pId
= STR_INSERTDRAW
;
344 case SwUndoId::NUMORNONUM
:
345 pId
= STR_NUMORNONUM
;
347 case SwUndoId::INC_LEFTMARGIN
:
348 pId
= STR_INC_LEFTMARGIN
;
350 case SwUndoId::DEC_LEFTMARGIN
:
351 pId
= STR_DEC_LEFTMARGIN
;
353 case SwUndoId::INSERTLABEL
:
354 pId
= STR_INSERTLABEL
;
356 case SwUndoId::SETNUMRULESTART
:
357 pId
= STR_SETNUMRULESTART
;
359 case SwUndoId::CHGFTN
:
362 case SwUndoId::REDLINE
:
363 SAL_INFO("sw.core", "Should NEVER be used/translated");
365 case SwUndoId::ACCEPT_REDLINE
:
366 pId
= STR_ACCEPT_REDLINE
;
368 case SwUndoId::REJECT_REDLINE
:
369 pId
= STR_REJECT_REDLINE
;
371 case SwUndoId::SPLIT_TABLE
:
372 pId
= STR_SPLIT_TABLE
;
374 case SwUndoId::DONTEXPAND
:
375 pId
= STR_DONTEXPAND
;
377 case SwUndoId::AUTOCORRECT
:
378 pId
= STR_AUTOCORRECT
;
380 case SwUndoId::MERGE_TABLE
:
381 pId
= STR_MERGE_TABLE
;
383 case SwUndoId::TRANSLITERATE
:
384 pId
= STR_TRANSLITERATE
;
386 case SwUndoId::PASTE_CLIPBOARD
:
387 pId
= STR_PASTE_CLIPBOARD_UNDO
;
389 case SwUndoId::TYPING
:
390 pId
= STR_TYPING_UNDO
;
395 case SwUndoId::INSGLOSSARY
:
396 pId
= STR_INSERT_GLOSSARY
;
398 case SwUndoId::DELBOOKMARK
:
399 pId
= STR_DELBOOKMARK
;
401 case SwUndoId::INSBOOKMARK
:
402 pId
= STR_INSBOOKMARK
;
404 case SwUndoId::SORT_TBL
:
407 case SwUndoId::DELLAYFMT
:
410 case SwUndoId::AUTOFORMAT
:
411 pId
= STR_AUTOFORMAT
;
413 case SwUndoId::REPLACE
:
416 case SwUndoId::DELSECTION
:
417 pId
= STR_DELETESECTION
;
419 case SwUndoId::CHGSECTION
:
420 pId
= STR_CHANGESECTION
;
422 case SwUndoId::SETDEFTATTR
:
423 pId
= STR_CHANGEDEFATTR
;
425 case SwUndoId::DELNUM
:
428 case SwUndoId::DRAWUNDO
:
431 case SwUndoId::DRAWGROUP
:
434 case SwUndoId::DRAWUNGROUP
:
435 pId
= STR_DRAWUNGROUP
;
437 case SwUndoId::DRAWDELETE
:
438 pId
= STR_DRAWDELETE
;
440 case SwUndoId::REREAD
:
443 case SwUndoId::DELGRF
:
446 case SwUndoId::TABLE_ATTR
:
447 pId
= STR_TABLE_ATTR
;
449 case SwUndoId::TABLE_AUTOFMT
:
450 pId
= STR_UNDO_TABLE_AUTOFMT
;
452 case SwUndoId::TABLE_INSCOL
:
453 pId
= STR_UNDO_TABLE_INSCOL
;
455 case SwUndoId::TABLE_INSROW
:
456 pId
= STR_UNDO_TABLE_INSROW
;
458 case SwUndoId::TABLE_DELBOX
:
459 pId
= STR_UNDO_TABLE_DELBOX
;
461 case SwUndoId::TABLE_SPLIT
:
462 pId
= STR_UNDO_TABLE_SPLIT
;
464 case SwUndoId::TABLE_MERGE
:
465 pId
= STR_UNDO_TABLE_MERGE
;
467 case SwUndoId::TBLNUMFMT
:
468 pId
= STR_TABLE_NUMFORMAT
;
470 case SwUndoId::INSTOX
:
471 pId
= STR_INSERT_TOX
;
473 case SwUndoId::CLEARTOXRANGE
:
474 pId
= STR_CLEAR_TOX_RANGE
;
476 case SwUndoId::TBLCPYTBL
:
477 pId
= STR_TABLE_TBLCPYTBL
;
479 case SwUndoId::CPYTBL
:
480 pId
= STR_TABLE_CPYTBL
;
482 case SwUndoId::INS_FROM_SHADOWCRSR
:
483 pId
= STR_INS_FROM_SHADOWCRSR
;
485 case SwUndoId::CHAINE
:
486 pId
= STR_UNDO_CHAIN
;
488 case SwUndoId::UNCHAIN
:
489 pId
= STR_UNDO_UNCHAIN
;
491 case SwUndoId::FTNINFO
:
492 pId
= STR_UNDO_FTNINFO
;
494 case SwUndoId::COMPAREDOC
:
495 pId
= STR_UNDO_COMPAREDOC
;
497 case SwUndoId::SETFLYFRMFMT
:
498 pId
= STR_UNDO_SETFLYFRMFMT
;
500 case SwUndoId::SETRUBYATTR
:
501 pId
= STR_UNDO_SETRUBYATTR
;
503 case SwUndoId::TOXCHANGE
:
506 case SwUndoId::CREATE_PAGEDESC
:
507 pId
= STR_UNDO_PAGEDESC_CREATE
;
509 case SwUndoId::CHANGE_PAGEDESC
:
510 pId
= STR_UNDO_PAGEDESC
;
512 case SwUndoId::DELETE_PAGEDESC
:
513 pId
= STR_UNDO_PAGEDESC_DELETE
;
515 case SwUndoId::HEADER_FOOTER
:
516 pId
= STR_UNDO_HEADER_FOOTER
;
518 case SwUndoId::FIELD
:
519 pId
= STR_UNDO_FIELD
;
521 case SwUndoId::TXTFMTCOL_CREATE
:
522 pId
= STR_UNDO_TXTFMTCOL_CREATE
;
524 case SwUndoId::TXTFMTCOL_DELETE
:
525 pId
= STR_UNDO_TXTFMTCOL_DELETE
;
527 case SwUndoId::TXTFMTCOL_RENAME
:
528 pId
= STR_UNDO_TXTFMTCOL_RENAME
;
530 case SwUndoId::CHARFMT_CREATE
:
531 pId
= STR_UNDO_CHARFMT_CREATE
;
533 case SwUndoId::CHARFMT_DELETE
:
534 pId
= STR_UNDO_CHARFMT_DELETE
;
536 case SwUndoId::CHARFMT_RENAME
:
537 pId
= STR_UNDO_CHARFMT_RENAME
;
539 case SwUndoId::FRMFMT_CREATE
:
540 pId
= STR_UNDO_FRMFMT_CREATE
;
542 case SwUndoId::FRMFMT_DELETE
:
543 pId
= STR_UNDO_FRMFMT_DELETE
;
545 case SwUndoId::FRMFMT_RENAME
:
546 pId
= STR_UNDO_FRMFMT_RENAME
;
548 case SwUndoId::NUMRULE_CREATE
:
549 pId
= STR_UNDO_NUMRULE_CREATE
;
551 case SwUndoId::NUMRULE_DELETE
:
552 pId
= STR_UNDO_NUMRULE_DELETE
;
554 case SwUndoId::NUMRULE_RENAME
:
555 pId
= STR_UNDO_NUMRULE_RENAME
;
557 case SwUndoId::BOOKMARK_RENAME
:
558 pId
= STR_UNDO_BOOKMARK_RENAME
;
560 case SwUndoId::INDEX_ENTRY_INSERT
:
561 pId
= STR_UNDO_INDEX_ENTRY_INSERT
;
563 case SwUndoId::INDEX_ENTRY_DELETE
:
564 pId
= STR_UNDO_INDEX_ENTRY_DELETE
;
566 case SwUndoId::COL_DELETE
:
567 pId
= STR_UNDO_COL_DELETE
;
569 case SwUndoId::ROW_DELETE
:
570 pId
= STR_UNDO_ROW_DELETE
;
572 case SwUndoId::RENAME_PAGEDESC
:
573 pId
= STR_UNDO_PAGEDESC_RENAME
;
575 case SwUndoId::NUMDOWN
:
578 case SwUndoId::FLYFRMFMT_TITLE
:
579 pId
= STR_UNDO_FLYFRMFMT_TITLE
;
581 case SwUndoId::FLYFRMFMT_DESCRIPTION
:
582 pId
= STR_UNDO_FLYFRMFMT_DESCRIPTION
;
584 case SwUndoId::TBLSTYLE_CREATE
:
585 pId
= STR_UNDO_TBLSTYLE_CREATE
;
587 case SwUndoId::TBLSTYLE_DELETE
:
588 pId
= STR_UNDO_TBLSTYLE_DELETE
;
590 case SwUndoId::TBLSTYLE_UPDATE
:
591 pId
= STR_UNDO_TBLSTYLE_UPDATE
;
593 case SwUndoId::UI_REPLACE
:
594 pId
= STR_REPLACE_UNDO
;
596 case SwUndoId::UI_INSERT_PAGE_BREAK
:
597 pId
= STR_INSERT_PAGE_BREAK_UNDO
;
599 case SwUndoId::UI_INSERT_COLUMN_BREAK
:
600 pId
= STR_INSERT_COLUMN_BREAK_UNDO
;
602 case SwUndoId::UI_INSERT_ENVELOPE
:
603 pId
= STR_INSERT_ENV_UNDO
;
605 case SwUndoId::UI_DRAG_AND_COPY
:
606 pId
= STR_DRAG_AND_COPY
;
608 case SwUndoId::UI_DRAG_AND_MOVE
:
609 pId
= STR_DRAG_AND_MOVE
;
611 case SwUndoId::UI_INSERT_CHART
:
612 pId
= STR_INSERT_CHART
;
614 case SwUndoId::UI_INSERT_FOOTNOTE
:
615 pId
= STR_INSERT_FOOTNOTE
;
617 case SwUndoId::UI_INSERT_URLBTN
:
618 pId
= STR_INSERT_URLBTN
;
620 case SwUndoId::UI_INSERT_URLTXT
:
621 pId
= STR_INSERT_URLTXT
;
623 case SwUndoId::UI_DELETE_INVISIBLECNTNT
:
624 pId
= STR_DELETE_INVISIBLECNTNT
;
626 case SwUndoId::UI_REPLACE_STYLE
:
627 pId
= STR_REPLACE_STYLE
;
629 case SwUndoId::UI_DELETE_PAGE_BREAK
:
630 pId
= STR_DELETE_PAGE_BREAK
;
632 case SwUndoId::UI_TEXT_CORRECTION
:
633 pId
= STR_TEXT_CORRECTION
;
635 case SwUndoId::UI_TABLE_DELETE
:
636 pId
= STR_UNDO_TABLE_DELETE
;
638 case SwUndoId::CONFLICT
:
640 case SwUndoId::PARA_SIGN_ADD
:
641 pId
= STR_PARAGRAPH_SIGN_UNDO
;
643 case SwUndoId::INSERT_FORM_FIELD
:
644 pId
= STR_UNDO_INSERT_FORM_FIELD
;
646 case SwUndoId::INSERT_PAGE_NUMBER
:
647 pId
= STR_UNDO_INSERT_PAGE_NUMBER
;
649 case SwUndoId::UPDATE_FORM_FIELD
:
650 pId
= STR_UNDO_UPDATE_FORM_FIELD
;
652 case SwUndoId::UPDATE_FORM_FIELDS
:
653 pId
= STR_UNDO_UPDATE_FORM_FIELDS
;
655 case SwUndoId::DELETE_FORM_FIELDS
:
656 pId
= STR_UNDO_DELETE_FORM_FIELDS
;
658 case SwUndoId::UPDATE_BOOKMARK
:
659 pId
= STR_UPDATE_BOOKMARK
;
661 case SwUndoId::UPDATE_BOOKMARKS
:
662 pId
= STR_UPDATE_BOOKMARKS
;
664 case SwUndoId::DELETE_BOOKMARKS
:
665 pId
= STR_DELETE_BOOKMARKS
;
667 case SwUndoId::UPDATE_FIELD
:
668 pId
= STR_UPDATE_FIELD
;
670 case SwUndoId::UPDATE_FIELDS
:
671 pId
= STR_UPDATE_FIELDS
;
673 case SwUndoId::DELETE_FIELDS
:
674 pId
= STR_DELETE_FIELDS
;
676 case SwUndoId::UPDATE_SECTIONS
:
677 pId
= STR_UPDATE_SECTIONS
;
679 case SwUndoId::CHANGE_THEME
:
680 pId
= STR_UNDO_CHANGE_THEME_COLORS
;
682 case SwUndoId::DELETE_SECTIONS
:
683 pId
= STR_DELETE_SECTIONS
;
685 case SwUndoId::FLYFRMFMT_DECORATIVE
:
686 pId
= STR_UNDO_FLYFRMFMT_DECORATIVE
;
694 OUString
SwUndo::GetComment() const
702 maComment
= GetUndoComment(GetId());
704 SwRewriter aRewriter
= GetRewriter();
706 maComment
= aRewriter
.Apply(*maComment
);
709 aResult
= *maComment
;
713 aResult
= GetUndoComment(GetId());
715 SwRewriter aRewriter
= GetRewriter();
717 aResult
= aRewriter
.Apply(aResult
);
723 ViewShellId
SwUndo::GetViewShellId() const
725 return m_nViewShellId
;
728 SwRewriter
SwUndo::GetRewriter() const
735 SwUndoSaveContent::SwUndoSaveContent()
738 SwUndoSaveContent::~SwUndoSaveContent() COVERITY_NOEXCEPT_FALSE
742 void SwUndoSaveContent::dumpAsXml(xmlTextWriterPtr pWriter
) const
744 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwUndoSaveContent"));
745 (void)xmlTextWriterWriteFormatAttribute(pWriter
, BAD_CAST("ptr"), "%p", this);
749 m_pHistory
->dumpAsXml(pWriter
);
752 (void)xmlTextWriterEndElement(pWriter
);
755 // This is needed when deleting content. For REDO all contents will be moved
756 // into the UndoNodesArray. These methods always create a new node to insert
757 // content. As a result, the attributes will not be expanded.
758 // - MoveTo moves from NodesArray into UndoNodesArray
759 // - MoveFrom moves from UndoNodesArray into NodesArray
761 // If pEndNdIdx is given, Undo/Redo calls -Ins/DelFly. In that case the whole
762 // section should be moved.
763 void SwUndoSaveContent::MoveToUndoNds( SwPaM
& rPaM
, SwNodeIndex
* pNodeIdx
,
764 SwNodeOffset
* pEndNdIdx
)
766 SwDoc
& rDoc
= rPaM
.GetDoc();
767 ::sw::UndoGuard
const undoGuard(rDoc
.GetIDocumentUndoRedo());
769 SwNoTextNode
* pCpyNd
= rPaM
.GetPointNode().GetNoTextNode();
771 // here comes the actual delete (move)
772 SwNodes
& rNds
= rDoc
.GetUndoManager().GetUndoNodes();
773 SwPosition
aPos( pEndNdIdx
? rNds
.GetEndOfPostIts()
774 : rNds
.GetEndOfExtras() );
776 const SwPosition
* pStt
= rPaM
.Start(), *pEnd
= rPaM
.End();
778 SwNodeOffset nTmpMvNode
= aPos
.GetNodeIndex();
780 if( pCpyNd
|| pEndNdIdx
)
782 SwNodeRange
aRg( pStt
->GetNode(), SwNodeOffset(0), pEnd
->GetNode(), SwNodeOffset(1) );
783 rDoc
.GetNodes().MoveNodes( aRg
, rNds
, aPos
.GetNode(), true );
784 aPos
.Adjust(SwNodeOffset(-1));
788 rDoc
.GetNodes().MoveRange( rPaM
, aPos
, rNds
);
791 *pEndNdIdx
= aPos
.GetNodeIndex();
794 aPos
.Assign(nTmpMvNode
);
796 *pNodeIdx
= aPos
.GetNode();
799 void SwUndoSaveContent::MoveFromUndoNds( SwDoc
& rDoc
, SwNodeOffset nNodeIdx
,
801 const SwNodeOffset
* pEndNdIdx
, bool const bForceCreateFrames
)
803 // here comes the recovery
804 SwNodes
& rNds
= rDoc
.GetUndoManager().GetUndoNodes();
805 if( nNodeIdx
== rNds
.GetEndOfPostIts().GetIndex() )
806 return; // nothing saved
808 ::sw::UndoGuard
const undoGuard(rDoc
.GetIDocumentUndoRedo());
810 SwPaM
aPaM( rInsPos
);
811 if( pEndNdIdx
) // than get the section from it
812 aPaM
.GetPoint()->Assign( *rNds
[SwNodeOffset(0)], *pEndNdIdx
);
815 aPaM
.GetPoint()->Assign( rNds
.GetEndOfExtras() );
816 GoInContent( aPaM
, fnMoveBackward
);
819 SwTextNode
* pTextNd
= aPaM
.GetPointNode().GetTextNode();
820 if (!pEndNdIdx
&& pTextNd
)
823 aPaM
.GetPoint()->Assign(nNodeIdx
, 0);
825 SaveRedlEndPosForRestore
aRedlRest( rInsPos
.GetNode(), rInsPos
.GetContentIndex() );
827 rNds
.MoveRange( aPaM
, rInsPos
, rDoc
.GetNodes() );
829 // delete the last Node as well
830 bool bDeleteLastNode
= false;
831 if( !aPaM
.GetPoint()->GetContentIndex() )
832 bDeleteLastNode
= true;
835 // still empty Nodes at the end?
836 aPaM
.GetPoint()->Adjust(SwNodeOffset(1));
837 if ( &rNds
.GetEndOfExtras() != &aPaM
.GetPoint()->GetNode() )
838 bDeleteLastNode
= true;
840 if( bDeleteLastNode
)
842 SwNode
& rDelNode
= aPaM
.GetPoint()->GetNode();
843 SwNodeOffset nDelOffset
= rNds
.GetEndOfExtras().GetIndex() -
844 aPaM
.GetPoint()->GetNodeIndex();
845 //move it so we don't have SwContentIndex pointing at a node when it is deleted.
846 aPaM
.GetPoint()->Adjust(SwNodeOffset(-1));
848 rNds
.Delete( rDelNode
, nDelOffset
);
855 SwNodeRange
aRg( rNds
, nNodeIdx
, (pEndNdIdx
857 : rNds
.GetEndOfExtras().GetIndex() ) );
858 rNds
.MoveNodes(aRg
, rDoc
.GetNodes(), rInsPos
.GetNode(), nullptr == pEndNdIdx
|| bForceCreateFrames
);
863 // These two methods save and restore the Point of PaM.
864 // If the point cannot be moved, a "backup" is created on the previous node.
865 // Either way, returned, inserting at its original position will not move it.
866 ::std::optional
<SwNodeIndex
> SwUndoSaveContent::MovePtBackward(SwPaM
& rPam
)
869 if( rPam
.Move( fnMoveBackward
))
872 return { SwNodeIndex(rPam
.GetPoint()->GetNode(), -1) };
875 void SwUndoSaveContent::MovePtForward(SwPaM
& rPam
, ::std::optional
<SwNodeIndex
> && oMvBkwrd
)
877 // Was there content before this position?
879 rPam
.Move( fnMoveForward
);
882 *rPam
.GetPoint() = SwPosition(*oMvBkwrd
);
883 rPam
.GetPoint()->Adjust(SwNodeOffset(1));
884 SwContentNode
* pCNd
= rPam
.GetPointContentNode();
886 rPam
.Move( fnMoveForward
);
890 // Delete all objects that have ContentIndices to the given area.
891 // Currently (1994) these exist:
896 // #i81002# - extending method
897 // delete certain (not all) cross-reference bookmarks at text node of <rMark>
898 // and at text node of <rPoint>, if these text nodes aren't the same.
899 void SwUndoSaveContent::DelContentIndex( const SwPosition
& rMark
,
900 const SwPosition
& rPoint
,
901 DelContentType nDelContentType
)
903 const SwPosition
*pStt
= rMark
< rPoint
? &rMark
: &rPoint
,
904 *pEnd
= &rMark
== pStt
? &rPoint
: &rMark
;
906 SwDoc
& rDoc
= rMark
.GetNode().GetDoc();
908 // if it's not in the doc array, probably missing some invalidation somewhere
909 assert(&rPoint
.GetNodes() == &rDoc
.GetNodes());
910 assert(&rMark
.GetNodes() == &rDoc
.GetNodes());
912 ::sw::UndoGuard
const undoGuard(rDoc
.GetIDocumentUndoRedo());
915 if( DelContentType::Ftn
& nDelContentType
)
917 SwFootnoteIdxs
& rFootnoteArr
= rDoc
.GetFootnoteIdxs();
918 if( !rFootnoteArr
.empty() )
920 const SwNode
* pFootnoteNd
;
922 rFootnoteArr
.SeekEntry( pStt
->GetNode(), &nPos
);
923 SwTextFootnote
* pSrch
;
925 // for now delete all that come afterwards
926 while( nPos
< rFootnoteArr
.size() && ( pFootnoteNd
=
927 &( pSrch
= rFootnoteArr
[ nPos
] )->GetTextNode())->GetIndex()
928 <= pEnd
->GetNodeIndex() )
930 const sal_Int32 nFootnoteSttIdx
= pSrch
->GetStart();
931 if( (DelContentType::CheckNoCntnt
& nDelContentType
)
932 ? (&pEnd
->GetNode() == pFootnoteNd
)
933 : (( &pStt
->GetNode() == pFootnoteNd
&&
934 pStt
->GetContentIndex() > nFootnoteSttIdx
) ||
935 ( &pEnd
->GetNode() == pFootnoteNd
&&
936 nFootnoteSttIdx
>= pEnd
->GetContentIndex() )) )
938 ++nPos
; // continue searching
942 // FIXME: duplicated code here and below -> refactor?
943 // Unfortunately an index needs to be created. Otherwise there
944 // will be problems with TextNode because the index will be
945 // deleted in the DTOR of SwFootnote!
946 SwTextNode
* pTextNd
= const_cast<SwTextNode
*>(static_cast<const SwTextNode
*>(pFootnoteNd
));
948 m_pHistory
.reset( new SwHistory
);
949 SwTextAttr
* const pFootnoteHint
=
950 pTextNd
->GetTextAttrForCharAt( nFootnoteSttIdx
);
951 assert(pFootnoteHint
);
952 SwContentIndex
aIdx( pTextNd
, nFootnoteSttIdx
);
953 m_pHistory
->Add( pFootnoteHint
, pTextNd
->GetIndex(), false );
954 pTextNd
->EraseText( aIdx
, 1 );
957 while( nPos
-- && ( pFootnoteNd
= &( pSrch
= rFootnoteArr
[ nPos
] )->
958 GetTextNode())->GetIndex() >= pStt
->GetNodeIndex() )
960 const sal_Int32 nFootnoteSttIdx
= pSrch
->GetStart();
961 if( !(DelContentType::CheckNoCntnt
& nDelContentType
) && (
962 ( &pStt
->GetNode() == pFootnoteNd
&&
963 pStt
->GetContentIndex() > nFootnoteSttIdx
) ||
964 ( &pEnd
->GetNode() == pFootnoteNd
&&
965 nFootnoteSttIdx
>= pEnd
->GetContentIndex() )))
966 continue; // continue searching
968 // Unfortunately an index needs to be created. Otherwise there
969 // will be problems with TextNode because the index will be
970 // deleted in the DTOR of SwFootnote!
971 SwTextNode
* pTextNd
= const_cast<SwTextNode
*>(static_cast<const SwTextNode
*>(pFootnoteNd
));
973 m_pHistory
.reset( new SwHistory
);
974 SwTextAttr
* const pFootnoteHint
=
975 pTextNd
->GetTextAttrForCharAt( nFootnoteSttIdx
);
976 assert(pFootnoteHint
);
977 SwContentIndex
aIdx( pTextNd
, nFootnoteSttIdx
);
978 m_pHistory
->Add( pFootnoteHint
, pTextNd
->GetIndex(), false );
979 pTextNd
->EraseText( aIdx
, 1 );
985 if( DelContentType::Fly
& nDelContentType
)
987 sal_uInt16 nChainInsPos
= m_pHistory
? m_pHistory
->Count() : 0;
988 const sw::SpzFrameFormats
& rSpzArr
= *rDoc
.GetSpzFrameFormats();
989 if( !rSpzArr
.empty() )
991 sw::SpzFrameFormat
* pFormat
;
992 const SwFormatAnchor
* pAnchor
;
993 size_t n
= rSpzArr
.size();
994 const SwPosition
* pAPos
;
996 while( n
&& !rSpzArr
.empty() )
998 pFormat
= rSpzArr
[--n
];
999 pAnchor
= &pFormat
->GetAnchor();
1000 switch( pAnchor
->GetAnchorId() )
1002 case RndStdIds::FLY_AS_CHAR
:
1003 if( nullptr != (pAPos
= pAnchor
->GetContentAnchor() ) &&
1004 (( DelContentType::CheckNoCntnt
& nDelContentType
)
1005 ? ( pStt
->GetNode() <= pAPos
->GetNode() &&
1006 pAPos
->GetNode() < pEnd
->GetNode() )
1007 : ( *pStt
<= *pAPos
&& *pAPos
< *pEnd
)) )
1010 m_pHistory
.reset( new SwHistory
);
1011 SwTextNode
*const pTextNd
=
1012 pAPos
->GetNode().GetTextNode();
1013 SwTextAttr
* const pFlyHint
= pTextNd
->GetTextAttrForCharAt(
1014 pAPos
->GetContentIndex());
1016 m_pHistory
->Add( pFlyHint
, SwNodeOffset(0), false );
1017 // reset n so that no Format is skipped
1018 n
= n
>= rSpzArr
.size() ? rSpzArr
.size() : n
+1;
1021 case RndStdIds::FLY_AT_PARA
:
1023 pAPos
= pAnchor
->GetContentAnchor();
1025 pStt
->GetNode() <= pAPos
->GetNode() && pAPos
->GetNode() <= pEnd
->GetNode())
1028 m_pHistory
.reset( new SwHistory
);
1030 if (!(DelContentType::Replace
& nDelContentType
)
1031 && IsSelectFrameAnchoredAtPara(*pAPos
, *pStt
, *pEnd
, nDelContentType
))
1033 m_pHistory
->AddDeleteFly(*pFormat
, nChainInsPos
);
1034 // reset n so that no Format is skipped
1035 n
= n
>= rSpzArr
.size() ? rSpzArr
.size() : n
+1;
1037 // Moving the anchor?
1038 else if (!((DelContentType::CheckNoCntnt
|DelContentType::ExcludeFlyAtStartEnd
)
1039 & nDelContentType
) &&
1040 // for SwUndoDelete: rPoint is the node that
1041 // will be Joined - so anchor should be moved
1042 // off it - but UndoImpl() split will insert
1043 // new node *before* existing one so a no-op
1044 // may need to be done here to add it to
1045 // history for Undo.
1046 (rPoint
.GetNodeIndex() == pAPos
->GetNodeIndex()
1047 || pStt
->GetNodeIndex() == pAPos
->GetNodeIndex())
1048 // Do not try to move the anchor to a table!
1049 && rMark
.GetNode().IsTextNode())
1051 m_pHistory
->AddChangeFlyAnchor(*pFormat
);
1052 SwFormatAnchor
aAnch( *pAnchor
);
1053 SwPosition
aPos( rMark
.GetNode() );
1054 aAnch
.SetAnchor( &aPos
);
1055 pFormat
->SetFormatAttr( aAnch
);
1060 case RndStdIds::FLY_AT_CHAR
:
1061 if( nullptr != (pAPos
= pAnchor
->GetContentAnchor() ) &&
1062 ( pStt
->GetNode() <= pAPos
->GetNode() && pAPos
->GetNode() <= pEnd
->GetNode() ) )
1065 m_pHistory
.reset( new SwHistory
);
1066 if (!(DelContentType::Replace
& nDelContentType
)
1067 && IsDestroyFrameAnchoredAtChar(
1068 *pAPos
, *pStt
, *pEnd
, nDelContentType
))
1070 m_pHistory
->AddDeleteFly(*pFormat
, nChainInsPos
);
1071 n
= n
>= rSpzArr
.size() ? rSpzArr
.size() : n
+1;
1073 else if (!((DelContentType::CheckNoCntnt
|
1074 DelContentType::ExcludeFlyAtStartEnd
)
1077 if( *pStt
<= *pAPos
&& *pAPos
< *pEnd
)
1079 // These are the objects anchored
1080 // between section start and end position
1081 // Do not try to move the anchor to a table!
1082 if( rMark
.GetNode().GetTextNode() )
1084 m_pHistory
->AddChangeFlyAnchor(*pFormat
);
1085 SwFormatAnchor
aAnch( *pAnchor
);
1086 aAnch
.SetAnchor( &rMark
);
1087 pFormat
->SetFormatAttr( aAnch
);
1093 case RndStdIds::FLY_AT_FLY
:
1095 if( nullptr != (pAPos
= pAnchor
->GetContentAnchor() ) &&
1096 pStt
->GetNode() == pAPos
->GetNode() )
1099 m_pHistory
.reset( new SwHistory
);
1101 m_pHistory
->AddDeleteFly(*pFormat
, nChainInsPos
);
1103 // reset n so that no Format is skipped
1104 n
= n
>= rSpzArr
.size() ? rSpzArr
.size() : n
+1;
1114 if( !(DelContentType::Bkm
& nDelContentType
) )
1117 IDocumentMarkAccess
* const pMarkAccess
= rDoc
.getIDocumentMarkAccess();
1118 if( !pMarkAccess
->getAllMarksCount() )
1121 for( sal_Int32 n
= 0; n
< pMarkAccess
->getAllMarksCount(); ++n
)
1124 bool bSavePos
= false;
1125 bool bSaveOtherPos
= false;
1126 const ::sw::mark::IMark
*const pBkmk
= pMarkAccess
->getAllMarksBegin()[n
];
1127 auto const type(IDocumentMarkAccess::GetType(*pBkmk
));
1129 if( DelContentType::CheckNoCntnt
& nDelContentType
)
1131 if ( pStt
->GetNode() <= pBkmk
->GetMarkPos().GetNode()
1132 && pBkmk
->GetMarkPos().GetNode() < pEnd
->GetNode() )
1136 if ( pBkmk
->IsExpanded()
1137 && pStt
->GetNode() <= pBkmk
->GetOtherMarkPos().GetNode()
1138 && pBkmk
->GetOtherMarkPos().GetNode() < pEnd
->GetNode() )
1140 bSaveOtherPos
= true;
1146 // keep cross-reference bookmarks, if content inside one paragraph is deleted.
1147 if ( rMark
.GetNode() == rPoint
.GetNode()
1148 && ( type
== IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK
1149 || type
== IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK
))
1154 bool bMaybe
= false;
1155 if ( *pStt
<= pBkmk
->GetMarkPos() && pBkmk
->GetMarkPos() <= *pEnd
)
1157 if ( pBkmk
->GetMarkPos() == *pEnd
1158 || ( *pStt
== pBkmk
->GetMarkPos() && pBkmk
->IsExpanded() ) )
1163 if( pBkmk
->IsExpanded() &&
1164 *pStt
<= pBkmk
->GetOtherMarkPos() && pBkmk
->GetOtherMarkPos() <= *pEnd
)
1166 assert(!bSaveOtherPos
);
1168 || (*pStt
< pBkmk
->GetOtherMarkPos() && pBkmk
->GetOtherMarkPos() < *pEnd
)
1170 && ( type
== IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
1171 || type
== IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK
1172 || type
== IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK
1173 || type
== IDocumentMarkAccess::MarkType::DATE_FIELDMARK
))
1175 && !(nDelContentType
& DelContentType::Replace
)
1176 && type
== IDocumentMarkAccess::MarkType::BOOKMARK
1177 && pStt
->GetContentIndex() == 0 // entire paragraph deleted?
1178 && pEnd
->GetContentIndex() == pEnd
->GetNode().GetTextNode()->Len()))
1182 bSaveOtherPos
= true;
1186 if ( !bSavePos
&& !bSaveOtherPos
1187 && dynamic_cast< const ::sw::mark::CrossRefBookmark
* >(pBkmk
) )
1189 // certain special handling for cross-reference bookmarks
1190 const bool bDifferentTextNodesAtMarkAndPoint
=
1191 rMark
.GetNode() != rPoint
.GetNode()
1192 && rMark
.GetNode().GetTextNode()
1193 && rPoint
.GetNode().GetTextNode();
1194 if ( bDifferentTextNodesAtMarkAndPoint
)
1196 // delete cross-reference bookmark at <pStt>, if only part of
1197 // <pEnd> text node content is deleted.
1198 if( pStt
->GetNode() == pBkmk
->GetMarkPos().GetNode()
1199 && pEnd
->GetContentIndex() != pEnd
->GetNode().GetTextNode()->Len() )
1202 bSaveOtherPos
= false; // cross-reference bookmarks are not expanded
1204 // delete cross-reference bookmark at <pEnd>, if only part of
1205 // <pStt> text node content is deleted.
1206 else if( pEnd
->GetNode() == pBkmk
->GetMarkPos().GetNode() &&
1207 pStt
->GetContentIndex() != 0 )
1210 bSaveOtherPos
= false; // cross-reference bookmarks are not expanded
1214 else if (type
== IDocumentMarkAccess::MarkType::ANNOTATIONMARK
)
1216 // delete annotation marks, if its end position is covered by the deletion
1217 const SwPosition
& rAnnotationEndPos
= pBkmk
->GetMarkEnd();
1218 if ( *pStt
< rAnnotationEndPos
&& rAnnotationEndPos
<= *pEnd
)
1221 bSaveOtherPos
= pBkmk
->IsExpanded(); //tdf#90138, only save the other pos if there is one
1226 if ( bSavePos
|| bSaveOtherPos
)
1228 if (type
!= IDocumentMarkAccess::MarkType::UNO_BOOKMARK
)
1231 m_pHistory
.reset( new SwHistory
);
1232 m_pHistory
->Add( *pBkmk
, bSavePos
, bSaveOtherPos
);
1236 || !pBkmk
->IsExpanded() ) )
1238 pMarkAccess
->deleteMark(pMarkAccess
->getAllMarksBegin()+n
, false);
1245 // save a complete section into UndoNodes array
1246 SwUndoSaveSection::SwUndoSaveSection()
1247 : m_nMoveLen( 0 ), m_nStartPos( NODE_OFFSET_MAX
)
1251 SwUndoSaveSection::~SwUndoSaveSection()
1253 if (m_oMovedStart
) // delete also the section from UndoNodes array
1255 // SaveSection saves the content in the PostIt section.
1256 SwNodes
& rUNds
= m_oMovedStart
->GetNode().GetNodes();
1257 // cid#1486004 Uncaught exception
1258 suppress_fun_call_w_exception(rUNds
.Delete(*m_oMovedStart
, m_nMoveLen
));
1260 m_oMovedStart
.reset();
1262 m_pRedlineSaveData
.reset();
1265 void SwUndoSaveSection::SaveSection( const SwNodeIndex
& rSttIdx
)
1267 SwNodeRange
aRg( rSttIdx
.GetNode(), *rSttIdx
.GetNode().EndOfSectionNode() );
1271 void SwUndoSaveSection::SaveSection(
1272 const SwNodeRange
& rRange
, bool const bExpandNodes
)
1274 SwPaM
aPam( rRange
.aStart
, rRange
.aEnd
);
1276 // delete all footnotes, fly frames, bookmarks
1277 DelContentIndex( *aPam
.GetMark(), *aPam
.GetPoint() );
1279 // redlines *before* CorrAbs, because DelBookmarks will make them 0-length
1280 // but *after* DelContentIndex because that also may use FillSaveData (in
1281 // flys) and that will be restored *after* this one...
1282 m_pRedlineSaveData
.reset( new SwRedlineSaveDatas
);
1283 if (!SwUndo::FillSaveData( aPam
, *m_pRedlineSaveData
))
1285 m_pRedlineSaveData
.reset();
1289 // move certain indexes out of deleted range
1290 SwNodeIndex
aSttIdx( aPam
.Start()->GetNode() );
1291 SwNodeIndex
aEndIdx( aPam
.End()->GetNode() );
1292 SwNodeIndex
aMvStt( aEndIdx
, 1 );
1293 SwDoc::CorrAbs( aSttIdx
, aEndIdx
, SwPosition( aMvStt
), true );
1296 m_nStartPos
= rRange
.aStart
.GetIndex();
1300 aPam
.GetPoint()->Adjust(SwNodeOffset(-1));
1301 aPam
.GetMark()->Adjust(SwNodeOffset(+1));
1304 SwContentNode
* pCNd
= aPam
.GetMarkContentNode();
1306 aPam
.GetMark()->SetContent( 0 );
1307 pCNd
= aPam
.GetPointContentNode();
1308 if( nullptr != pCNd
)
1309 aPam
.GetPoint()->SetContent( pCNd
->Len() );
1311 // Keep positions as SwContentIndex so that this section can be deleted in DTOR
1313 m_oMovedStart
= rRange
.aStart
;
1314 MoveToUndoNds(aPam
, &*m_oMovedStart
, &nEnd
);
1315 m_nMoveLen
= nEnd
- m_oMovedStart
->GetIndex() + 1;
1318 void SwUndoSaveSection::RestoreSection( SwDoc
* pDoc
, SwNodeIndex
* pIdx
,
1319 sal_uInt16 nSectType
)
1321 if( NODE_OFFSET_MAX
== m_nStartPos
) // was there any content?
1324 // check if the content is at the old position
1325 SwNodeIndex
aSttIdx( pDoc
->GetNodes(), m_nStartPos
);
1327 // move the content from UndoNodes array into Fly
1328 SwStartNode
* pSttNd
= SwNodes::MakeEmptySection( aSttIdx
.GetNode(),
1329 static_cast<SwStartNodeType
>(nSectType
) );
1331 RestoreSection( pDoc
, *pSttNd
->EndOfSectionNode() );
1337 void SwUndoSaveSection::RestoreSection(
1338 SwDoc
*const pDoc
, const SwNode
& rInsPos
, bool bForceCreateFrames
)
1340 if( NODE_OFFSET_MAX
== m_nStartPos
) // was there any content?
1343 SwPosition
aInsPos( rInsPos
);
1344 SwNodeOffset nEnd
= m_oMovedStart
->GetIndex() + m_nMoveLen
- 1;
1345 MoveFromUndoNds(*pDoc
, m_oMovedStart
->GetIndex(), aInsPos
, &nEnd
, bForceCreateFrames
);
1347 // destroy indices again, content was deleted from UndoNodes array
1348 m_oMovedStart
.reset();
1349 m_nMoveLen
= SwNodeOffset(0);
1351 if( m_pRedlineSaveData
)
1353 SwUndo::SetSaveData( *pDoc
, *m_pRedlineSaveData
);
1354 m_pRedlineSaveData
.reset();
1358 void SwUndoSaveSection::dumpAsXml(xmlTextWriterPtr pWriter
) const
1360 SwUndoSaveContent::dumpAsXml(pWriter
);
1363 // save and set the RedlineData
1364 SwRedlineSaveData::SwRedlineSaveData(
1365 SwComparePosition eCmpPos
,
1366 const SwPosition
& rSttPos
,
1367 const SwPosition
& rEndPos
,
1368 SwRangeRedline
& rRedl
,
1371 , SwRedlineData( rRedl
.GetRedlineData(), bCopyNext
)
1373 assert( SwComparePosition::Outside
== eCmpPos
||
1374 !rRedl
.GetContentIdx() ); // "Redline with Content"
1378 case SwComparePosition::OverlapBefore
: // Pos1 overlaps Pos2 at the beginning
1379 m_nEndNode
= rEndPos
.GetNodeIndex();
1380 m_nEndContent
= rEndPos
.GetContentIndex();
1383 case SwComparePosition::OverlapBehind
: // Pos1 overlaps Pos2 at the end
1384 m_nSttNode
= rSttPos
.GetNodeIndex();
1385 m_nSttContent
= rSttPos
.GetContentIndex();
1388 case SwComparePosition::Inside
: // Pos1 lays completely in Pos2
1389 m_nSttNode
= rSttPos
.GetNodeIndex();
1390 m_nSttContent
= rSttPos
.GetContentIndex();
1391 m_nEndNode
= rEndPos
.GetNodeIndex();
1392 m_nEndContent
= rEndPos
.GetContentIndex();
1395 case SwComparePosition::Outside
: // Pos2 lays completely in Pos1
1396 if ( rRedl
.GetContentIdx() )
1398 // than move section into UndoArray and memorize it
1399 SaveSection( *rRedl
.GetContentIdx() );
1400 rRedl
.ClearContentIdx();
1404 case SwComparePosition::Equal
: // Pos1 is exactly as big as Pos2
1411 #if OSL_DEBUG_LEVEL > 0
1412 m_nRedlineCount
= rSttPos
.GetNode().GetDoc().getIDocumentRedlineAccess().GetRedlineTable().size();
1413 m_bRedlineMoved
= rRedl
.IsMoved();
1417 SwRedlineSaveData::~SwRedlineSaveData()
1421 void SwRedlineSaveData::RedlineToDoc( SwPaM
const & rPam
)
1423 SwDoc
& rDoc
= rPam
.GetDoc();
1424 SwRangeRedline
* pRedl
= new SwRangeRedline( *this, rPam
);
1428 SwNodeIndex
aIdx( rDoc
.GetNodes() );
1429 RestoreSection( &rDoc
, &aIdx
, SwNormalStartNode
);
1431 GetHistory()->Rollback( &rDoc
);
1432 pRedl
->SetContentIdx( aIdx
);
1435 // First, delete the "old" so that in an Append no unexpected things will
1436 // happen, e.g. a delete in an insert. In the latter case the just restored
1437 // content will be deleted and not the one you originally wanted.
1438 rDoc
.getIDocumentRedlineAccess().DeleteRedline( *pRedl
, false, RedlineType::Any
);
1440 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
1441 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
| RedlineFlags::DontCombineRedlines
);
1442 //#i92154# let UI know about a new redline with comment
1443 if (rDoc
.GetDocShell() && (!pRedl
->GetComment().isEmpty()) )
1444 rDoc
.GetDocShell()->Broadcast(SwRedlineHint());
1446 auto const result(rDoc
.getIDocumentRedlineAccess().AppendRedline(pRedl
, true));
1447 assert(result
!= IDocumentRedlineAccess::AppendResult::IGNORED
); // SwRedlineSaveData::RedlineToDoc: insert redline failed
1448 (void) result
; // unused in non-debug
1449 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
1452 bool SwUndo::FillSaveData(
1453 const SwPaM
& rRange
,
1454 SwRedlineSaveDatas
& rSData
,
1460 auto [pStt
, pEnd
] = rRange
.StartEnd(); // SwPosition*
1461 const SwRedlineTable
& rTable
= rRange
.GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
1462 SwRedlineTable::size_type n
= 0;
1463 rRange
.GetDoc().getIDocumentRedlineAccess().GetRedline( *pStt
, &n
);
1464 for ( ; n
< rTable
.size(); ++n
)
1466 SwRangeRedline
* pRedl
= rTable
[n
];
1468 const SwComparePosition eCmpPos
=
1469 ComparePosition( *pStt
, *pEnd
, *pRedl
->Start(), *pRedl
->End() );
1470 if ( eCmpPos
!= SwComparePosition::Before
1471 && eCmpPos
!= SwComparePosition::Behind
1472 && eCmpPos
!= SwComparePosition::CollideEnd
1473 && eCmpPos
!= SwComparePosition::CollideStart
)
1476 rSData
.push_back(std::unique_ptr
<SwRedlineSaveData
>(new SwRedlineSaveData(eCmpPos
, *pStt
, *pEnd
, *pRedl
, bCopyNext
)));
1479 if( !rSData
.empty() && bDelRange
)
1481 rRange
.GetDoc().getIDocumentRedlineAccess().DeleteRedline( rRange
, false, RedlineType::Any
);
1483 return !rSData
.empty();
1486 bool SwUndo::FillSaveDataForFormat(
1487 const SwPaM
& rRange
,
1488 SwRedlineSaveDatas
& rSData
)
1492 const SwPosition
*pStt
= rRange
.Start(), *pEnd
= rRange
.End();
1493 const SwRedlineTable
& rTable
= rRange
.GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
1494 SwRedlineTable::size_type n
= 0;
1495 rRange
.GetDoc().getIDocumentRedlineAccess().GetRedline( *pStt
, &n
);
1496 for ( ; n
< rTable
.size(); ++n
)
1498 SwRangeRedline
* pRedl
= rTable
[n
];
1499 if ( RedlineType::Format
== pRedl
->GetType() )
1501 const SwComparePosition eCmpPos
= ComparePosition( *pStt
, *pEnd
, *pRedl
->Start(), *pRedl
->End() );
1502 if ( eCmpPos
!= SwComparePosition::Before
1503 && eCmpPos
!= SwComparePosition::Behind
1504 && eCmpPos
!= SwComparePosition::CollideEnd
1505 && eCmpPos
!= SwComparePosition::CollideStart
)
1507 rSData
.push_back(std::unique_ptr
<SwRedlineSaveData
>(new SwRedlineSaveData(eCmpPos
, *pStt
, *pEnd
, *pRedl
, true)));
1512 return !rSData
.empty();
1516 void SwUndo::SetSaveData( SwDoc
& rDoc
, SwRedlineSaveDatas
& rSData
)
1518 RedlineFlags eOld
= rDoc
.getIDocumentRedlineAccess().GetRedlineFlags();
1519 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld
& ~RedlineFlags::Ignore
) | RedlineFlags::On
);
1520 SwPaM
aPam( rDoc
.GetNodes().GetEndOfContent() );
1522 for( size_t n
= rSData
.size(); n
; )
1523 rSData
[ --n
].RedlineToDoc( aPam
);
1525 #if OSL_DEBUG_LEVEL > 0
1526 // check redline count against count saved in RedlineSaveData object
1527 // except in the case of moved redlines
1528 assert(rSData
.empty() || rSData
[0].m_bRedlineMoved
||
1529 (rSData
[0].m_nRedlineCount
== rDoc
.getIDocumentRedlineAccess().GetRedlineTable().size()));
1530 // "redline count not restored properly"
1533 rDoc
.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld
);
1536 bool SwUndo::HasHiddenRedlines( const SwRedlineSaveDatas
& rSData
)
1538 for( size_t n
= rSData
.size(); n
; )
1539 if( rSData
[ --n
].GetMvSttIdx() )
1544 bool SwUndo::CanRedlineGroup( SwRedlineSaveDatas
& rCurr
,
1545 const SwRedlineSaveDatas
& rCheck
, bool bCurrIsEnd
)
1547 if( rCurr
.size() != rCheck
.size() )
1550 for( size_t n
= 0; n
< rCurr
.size(); ++n
)
1552 const SwRedlineSaveData
& rSet
= rCurr
[ n
];
1553 const SwRedlineSaveData
& rGet
= rCheck
[ n
];
1554 if( rSet
.m_nSttNode
!= rGet
.m_nSttNode
||
1555 rSet
.GetMvSttIdx() || rGet
.GetMvSttIdx() ||
1556 ( bCurrIsEnd
? rSet
.m_nSttContent
!= rGet
.m_nEndContent
1557 : rSet
.m_nEndContent
!= rGet
.m_nSttContent
) ||
1558 !rGet
.CanCombine( rSet
) )
1564 for( size_t n
= 0; n
< rCurr
.size(); ++n
)
1566 SwRedlineSaveData
& rSet
= rCurr
[ n
];
1567 const SwRedlineSaveData
& rGet
= rCheck
[ n
];
1569 rSet
.m_nSttContent
= rGet
.m_nSttContent
;
1571 rSet
.m_nEndContent
= rGet
.m_nEndContent
;
1576 OUString
ShortenString(const OUString
& rStr
, sal_Int32 nLength
, std::u16string_view aFillStr
)
1578 assert(nLength
- aFillStr
.size() >= 2);
1580 if (rStr
.getLength() <= nLength
)
1583 nLength
-= aFillStr
.size();
1587 const sal_Int32 nFrontLen
= nLength
- nLength
/ 2;
1588 const sal_Int32 nBackLen
= nLength
- nFrontLen
;
1590 return OUString::Concat(rStr
.subView(0, nFrontLen
))
1592 + rStr
.subView(rStr
.getLength() - nBackLen
);
1595 static bool IsAtEndOfSection(SwPosition
const& rAnchorPos
)
1597 SwNodeIndex
node(*rAnchorPos
.GetNode().EndOfSectionNode());
1598 SwContentNode
*const pNode(SwNodes::GoPrevious(&node
));
1600 assert(rAnchorPos
.GetNode() <= node
.GetNode()); // last valid anchor pos is last content
1601 return node
== rAnchorPos
.GetNode()
1602 // at-para fly has no SwContentIndex!
1603 && (rAnchorPos
.GetContentIndex() == pNode
->Len() || rAnchorPos
.GetContentNode() == nullptr);
1606 static bool IsAtStartOfSection(SwPosition
const& rAnchorPos
)
1608 SwNodes
const& rNodes(rAnchorPos
.GetNodes());
1609 SwNodeIndex
node(*rAnchorPos
.GetNode().StartOfSectionNode());
1610 SwContentNode
*const pNode(rNodes
.GoNext(&node
));
1613 assert(node
<= rAnchorPos
.GetNode());
1614 return node
== rAnchorPos
.GetNode() && rAnchorPos
.GetContentIndex() == 0;
1617 /// passed start / end position could be on section start / end node
1618 static bool IsAtEndOfSection2(SwPosition
const& rPos
)
1620 return rPos
.GetNode().IsEndNode()
1621 || IsAtEndOfSection(rPos
);
1624 static bool IsAtStartOfSection2(SwPosition
const& rPos
)
1626 return rPos
.GetNode().IsStartNode()
1627 || IsAtStartOfSection(rPos
);
1630 static bool IsNotBackspaceHeuristic(
1631 SwPosition
const& rStart
, SwPosition
const& rEnd
)
1633 // check if the selection is backspace/delete created by DelLeft/DelRight
1634 if (rStart
.GetNodeIndex() + 1 != rEnd
.GetNodeIndex())
1636 if (rEnd
.GetContentIndex() != 0)
1638 const SwTextNode
* pTextNode
= rStart
.GetNode().GetTextNode();
1639 if (!pTextNode
|| rStart
.GetContentIndex() != pTextNode
->Len())
1644 bool IsDestroyFrameAnchoredAtChar(SwPosition
const & rAnchorPos
,
1645 SwPosition
const & rStart
, SwPosition
const & rEnd
,
1646 DelContentType
const nDelContentType
)
1648 assert(rStart
<= rEnd
);
1650 // CheckNoCntnt means DelFullPara which is obvious to handle
1651 if (DelContentType::CheckNoCntnt
& nDelContentType
)
1652 { // exclude selection end node because it won't be deleted
1653 return (rAnchorPos
.GetNode() < rEnd
.GetNode())
1654 && (rStart
.GetNode() <= rAnchorPos
.GetNode());
1657 if ((nDelContentType
& DelContentType::WriterfilterHack
)
1658 && rAnchorPos
.GetDoc().IsInWriterfilterImport())
1659 { // FIXME hack for writerfilter RemoveLastParagraph() and MakeFlyAndMove(); can't test file format more specific?
1660 return (rStart
< rAnchorPos
) && (rAnchorPos
< rEnd
);
1663 if (nDelContentType
& DelContentType::ExcludeFlyAtStartEnd
)
1664 { // exclude selection start and end node
1665 return (rAnchorPos
.GetNode() < rEnd
.GetNode())
1666 && (rStart
.GetNode() < rAnchorPos
.GetNode());
1669 // in general, exclude the start and end position
1670 return ((rStart
< rAnchorPos
)
1671 || (rStart
== rAnchorPos
1672 // special case: fully deleted node
1673 && ((rStart
.GetNode() != rEnd
.GetNode() && rStart
.GetContentIndex() == 0
1674 // but not if the selection is backspace/delete!
1675 && IsNotBackspaceHeuristic(rStart
, rEnd
))
1676 || (IsAtStartOfSection(rAnchorPos
) && IsAtEndOfSection2(rEnd
)))))
1677 && ((rAnchorPos
< rEnd
)
1678 || (rAnchorPos
== rEnd
1679 // special case: fully deleted node
1680 && ((rEnd
.GetNode() != rStart
.GetNode() && rEnd
.GetContentIndex() == rEnd
.GetNode().GetTextNode()->Len()
1681 && IsNotBackspaceHeuristic(rStart
, rEnd
))
1682 || (IsAtEndOfSection(rAnchorPos
) && IsAtStartOfSection2(rStart
)))));
1685 bool IsSelectFrameAnchoredAtPara(SwPosition
const & rAnchorPos
,
1686 SwPosition
const & rStart
, SwPosition
const & rEnd
,
1687 DelContentType
const nDelContentType
)
1689 assert(rStart
<= rEnd
);
1691 // CheckNoCntnt means DelFullPara which is obvious to handle
1692 if (DelContentType::CheckNoCntnt
& nDelContentType
)
1693 { // exclude selection end node because it won't be deleted
1694 return (rAnchorPos
.GetNode() < rEnd
.GetNode())
1695 && (rStart
.GetNode() <= rAnchorPos
.GetNode());
1698 if ((nDelContentType
& DelContentType::WriterfilterHack
)
1699 && rAnchorPos
.GetDoc().IsInWriterfilterImport())
1700 { // FIXME hack for writerfilter RemoveLastParagraph() and MakeFlyAndMove(); can't test file format more specific?
1701 // but it MUST NOT be done during the SetRedlineFlags at the end of ODF
1702 // import, where the IsInXMLImport() cannot be checked because the
1703 // stupid code temp. overrides it - instead rely on setting the ALLFLYS
1704 // flag in MoveFromSection() and converting that to CheckNoCntnt with
1706 return (rStart
.GetNode() < rAnchorPos
.GetNode()) && (rAnchorPos
.GetNode() < rEnd
.GetNode());
1709 // in general, exclude the start and end position
1710 return ((rStart
.GetNode() < rAnchorPos
.GetNode())
1711 || (rStart
.GetNode() == rAnchorPos
.GetNode()
1712 && !(nDelContentType
& DelContentType::ExcludeFlyAtStartEnd
)
1713 // special case: fully deleted node
1714 && ((rStart
.GetNode() != rEnd
.GetNode() && rStart
.GetContentIndex() == 0
1715 // but not if the selection is backspace/delete!
1716 && IsNotBackspaceHeuristic(rStart
, rEnd
))
1717 || (IsAtStartOfSection2(rStart
) && IsAtEndOfSection2(rEnd
)))))
1718 && ((rAnchorPos
.GetNode() < rEnd
.GetNode())
1719 || (rAnchorPos
.GetNode() == rEnd
.GetNode()
1720 && !(nDelContentType
& DelContentType::ExcludeFlyAtStartEnd
)
1721 // special case: fully deleted node
1722 && ((rEnd
.GetNode() != rStart
.GetNode() && rEnd
.GetContentIndex() == rEnd
.GetNode().GetTextNode()->Len()
1723 && IsNotBackspaceHeuristic(rStart
, rEnd
))
1724 || (IsAtEndOfSection2(rEnd
) && IsAtStartOfSection2(rStart
)))));
1727 bool IsFlySelectedByCursor(SwDoc
const & rDoc
,
1728 SwPosition
const & rStart
, SwPosition
const & rEnd
)
1730 for (SwFrameFormat
const*const pFly
: *rDoc
.GetSpzFrameFormats())
1732 SwFormatAnchor
const& rAnchor(pFly
->GetAnchor());
1733 switch (rAnchor
.GetAnchorId())
1735 case RndStdIds::FLY_AT_CHAR
:
1736 case RndStdIds::FLY_AT_PARA
:
1738 SwPosition
const*const pAnchorPos(rAnchor
.GetContentAnchor());
1739 // can this really be null?
1740 if (pAnchorPos
!= nullptr
1741 && ((rAnchor
.GetAnchorId() == RndStdIds::FLY_AT_CHAR
)
1742 ? IsDestroyFrameAnchoredAtChar(*pAnchorPos
, rStart
, rEnd
)
1743 : IsSelectFrameAnchoredAtPara(*pAnchorPos
, rStart
, rEnd
)))
1749 default: // other types not relevant
1756 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */