LibreOffice Module sw (master) 1
XMLRedlineImportHelper.cxx
Go to the documentation of this file.
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 <memory>
21#include <sal/config.h>
22#include <sal/log.hxx>
23
24#include <cstddef>
25
27#include <unotextcursor.hxx>
28#include <unotextrange.hxx>
29#include <unocrsr.hxx>
30#include <ndtxt.hxx>
31#include <doc.hxx>
34#include <tools/datetime.hxx>
35#include <poolfmt.hxx>
36#include <fmtanchr.hxx>
37#include <ftnidx.hxx>
38#include <txtftn.hxx>
39#include <unoredline.hxx>
41#include "xmlimp.hxx"
43#include <o3tl/any.hxx>
44#include <xmloff/xmltoken.hxx>
45#include <vcl/svapp.hxx>
46
47using namespace ::com::sun::star;
48using namespace ::com::sun::star::uno;
49using namespace ::xmloff::token;
50
51using ::com::sun::star::text::XTextCursor;
52using ::com::sun::star::text::XTextRange;
53using ::com::sun::star::text::XWordCursor;
54using ::com::sun::star::beans::XPropertySet;
55using ::com::sun::star::beans::XPropertySetInfo;
56// collision with tools/DateTime: use UNO DateTime as util::DateTime
57// using util::DateTime;
58
59// a few helper functions
60static SwDoc* lcl_GetDocViaTunnel( Reference<XTextCursor> const & rCursor )
61{
62 OTextCursorHelper *const pXCursor =
63 dynamic_cast<OTextCursorHelper*>(rCursor.get());
64 OSL_ENSURE( pXCursor, "OTextCursorHelper missing" );
65 return pXCursor ? pXCursor->GetDoc() : nullptr;
66}
67
68static SwDoc* lcl_GetDocViaTunnel( Reference<XTextRange> const & rRange )
69{
70 SwXTextRange *const pXRange = dynamic_cast<SwXTextRange*>(rRange.get());
71 OSL_ENSURE(pXRange, "missing SwXTextRange for XTextRange");
72 // #i115174#: this may be a SvxUnoTextRange
73 // OSL_ENSURE( pXRange, "SwXTextRange missing" );
74 return pXRange ? &pXRange->GetDoc() : nullptr;
75}
76
77// XTextRangeOrNodeIndexPosition: store a position into the text
78// *either* as an XTextRange or as an SwNodeIndex. The reason is that
79// we must store either pointers to StartNodes (because redlines may
80// start on start nodes) or to a text position, and there appears to
81// be no existing type that could do both. Things are complicated by
82// the matter that (e.g in section import) we delete a few characters,
83// which may cause bookmarks (as used by XTextRange) to be deleted.
84
85namespace {
86
87class XTextRangeOrNodeIndexPosition
88{
89 Reference<XTextRange> m_xRange;
90 std::optional<SwNodeIndex> m_oIndex; // pIndex will point to the *previous* node
91
92public:
93 XTextRangeOrNodeIndexPosition();
94
95 void Set( Reference<XTextRange> const & rRange );
96 void Set( SwNode const & rIndex );
97 void SetAsNodeIndex( Reference<XTextRange> const & rRange );
98
99 void CopyPositionInto(SwPosition& rPos, SwDoc & rDoc);
100 SwDoc* GetDoc();
101
102 bool IsValid() const;
103};
104
105}
106
107XTextRangeOrNodeIndexPosition::XTextRangeOrNodeIndexPosition()
108{
109}
110
111void XTextRangeOrNodeIndexPosition::Set( Reference<XTextRange> const & rRange )
112{
113 m_xRange = rRange->getStart(); // set bookmark
114 m_oIndex.reset();
115}
116
117void XTextRangeOrNodeIndexPosition::Set( SwNode const & rIndex )
118{
119 m_oIndex = rIndex;
120 --(*m_oIndex) ; // previous node!!!
121 m_xRange = nullptr;
122}
123
124void XTextRangeOrNodeIndexPosition::SetAsNodeIndex(
125 Reference<XTextRange> const & rRange )
126{
127 // XTextRange -> XTunnel -> SwXTextRange
128 SwDoc* pDoc = lcl_GetDocViaTunnel(rRange);
129
130 if (!pDoc)
131 {
132 SAL_WARN("sw", "no SwDoc");
133 return;
134 }
135
136 // SwXTextRange -> PaM
137 SwUnoInternalPaM aPaM(*pDoc);
138 bool bSuccess = ::sw::XTextRangeToSwPaM(aPaM, rRange);
139 OSL_ENSURE(bSuccess, "illegal range");
140
141 // PaM -> Index
142 Set(aPaM.GetPoint()->GetNode());
143}
144
145void
146XTextRangeOrNodeIndexPosition::CopyPositionInto(SwPosition& rPos, SwDoc & rDoc)
147{
148 OSL_ENSURE(IsValid(), "Can't get Position");
149
150 // create PAM from start cursor (if no node index is present)
151 if (!m_oIndex.has_value())
152 {
153 SwUnoInternalPaM aUnoPaM(rDoc);
154 bool bSuccess = ::sw::XTextRangeToSwPaM(aUnoPaM, m_xRange);
155 OSL_ENSURE(bSuccess, "illegal range");
156
157 rPos = *aUnoPaM.GetPoint();
158 }
159 else
160 {
161 rPos.Assign( m_oIndex->GetNode(), SwNodeOffset(1) ); // pIndex points to previous index !!!
162 }
163}
164
165SwDoc* XTextRangeOrNodeIndexPosition::GetDoc()
166{
167 OSL_ENSURE(IsValid(), "Can't get Doc");
168
169 return m_oIndex.has_value() ? &m_oIndex->GetNodes().GetDoc() : lcl_GetDocViaTunnel(m_xRange);
170}
171
172bool XTextRangeOrNodeIndexPosition::IsValid() const
173{
174 return m_xRange.is() || m_oIndex.has_value();
175}
176
177// RedlineInfo: temporary storage for redline data
179{
180public:
181 RedlineInfo();
182 ~RedlineInfo();
183
184 // redline type (insert, delete, ...)
186
187 // info fields:
188 OUString sAuthor; // change author string
189 OUString sComment; // change comment string
190 util::DateTime aDateTime; // change DateTime
191 bool bMergeLastParagraph; // the SwRangeRedline::IsDelLastPara flag
192
193 // each position can may be either empty, an XTextRange, or an SwNodeIndex
194
195 // start pos of anchor (may be empty)
196 XTextRangeOrNodeIndexPosition aAnchorStart;
197
198 // end pos of anchor (may be empty)
199 XTextRangeOrNodeIndexPosition aAnchorEnd;
200
201 // index of content node (maybe NULL)
203
204 // next redline info (for hierarchical redlines)
206
207 // store whether we expect an adjustment for this redline
209};
210
213 bMergeLastParagraph( false ),
214 pContentIndex(nullptr),
215 pNextRedline(nullptr),
216 bNeedsAdjustment( false )
217{
218}
219
221{
222 delete pContentIndex;
223 delete pNextRedline;
224}
225
226constexpr OUStringLiteral g_sShowChanges = u"ShowChanges";
227constexpr OUStringLiteral g_sRecordChanges = u"RecordChanges";
228constexpr OUStringLiteral g_sRedlineProtectionKey = u"RedlineProtectionKey";
229
231 SvXMLImport & rImport,
232 bool bNoRedlinesPlease,
233 const Reference<XPropertySet> & rModel,
234 const Reference<XPropertySet> & rImportInfo )
235 : m_rImport(rImport),
236 m_sInsertion( GetXMLToken( XML_INSERTION )),
237 m_sDeletion( GetXMLToken( XML_DELETION )),
238 m_sFormatChange( GetXMLToken( XML_FORMAT_CHANGE )),
239 m_bIgnoreRedlines(bNoRedlinesPlease),
240 m_xModelPropertySet(rModel),
241 m_xImportInfoPropertySet(rImportInfo)
242{
243 // check to see if redline mode is handled outside of component
244 bool bHandleShowChanges = true;
245 bool bHandleRecordChanges = true;
246 bool bHandleProtectionKey = true;
247 if ( m_xImportInfoPropertySet.is() )
248 {
249 Reference<XPropertySetInfo> xInfo =
250 m_xImportInfoPropertySet->getPropertySetInfo();
251
252 bHandleShowChanges = ! xInfo->hasPropertyByName( g_sShowChanges );
253 bHandleRecordChanges = ! xInfo->hasPropertyByName( g_sRecordChanges );
254 bHandleProtectionKey = ! xInfo->hasPropertyByName( g_sRedlineProtectionKey );
255 }
256
257 // get redline mode
258 m_bShowChanges = *o3tl::doAccess<bool>(
259 ( bHandleShowChanges ? m_xModelPropertySet : m_xImportInfoPropertySet )
261 m_bRecordChanges = *o3tl::doAccess<bool>(
262 ( bHandleRecordChanges ? m_xModelPropertySet : m_xImportInfoPropertySet )
264 {
265 Any aAny = (bHandleProtectionKey ? m_xModelPropertySet
268 aAny >>= m_aProtectionKey;
269 }
270
271 // set redline mode to "don't record changes"
272 if( bHandleRecordChanges )
273 {
274 m_xModelPropertySet->setPropertyValue( g_sRecordChanges, Any(false) );
275 }
276}
277
279{
280 // delete all left over (and obviously incomplete) RedlineInfos (and map)
281 for( const auto& rEntry : m_aRedlineMap )
282 {
283 RedlineInfo* pInfo = rEntry.second;
284
285 // left-over redlines. Insert them if possible (but assert),
286 // and delete the incomplete ones. Finally, delete it.
287 if( IsReady(pInfo) )
288 {
289 OSL_FAIL("forgotten RedlineInfo; now inserted");
290 InsertIntoDocument( pInfo );
291 }
292 else
293 {
294 // try if only the adjustment was missing
295 pInfo->bNeedsAdjustment = false;
296 if( IsReady(pInfo) )
297 {
298 OSL_FAIL("RedlineInfo without adjustment; now inserted");
299 InsertIntoDocument( pInfo );
300 }
301 else
302 {
303 // this situation occurs if redlines aren't closed
304 // (i.e. end without start, or start without
305 // end). This may well be a problem in the file,
306 // rather than the code.
307 OSL_FAIL("incomplete redline (maybe file was corrupt); "
308 "now deleted");
309 }
310 }
311 delete pInfo;
312 }
313 m_aRedlineMap.clear();
314
315 // set redline mode, either to info property set, or directly to
316 // the document
317 bool bHandleShowChanges = true;
318 bool bHandleRecordChanges = true;
319 bool bHandleProtectionKey = true;
320 if ( m_xImportInfoPropertySet.is() )
321 {
322 Reference<XPropertySetInfo> xInfo =
323 m_xImportInfoPropertySet->getPropertySetInfo();
324
325 bHandleShowChanges = ! xInfo->hasPropertyByName( g_sShowChanges );
326 bHandleRecordChanges = ! xInfo->hasPropertyByName( g_sRecordChanges );
327 bHandleProtectionKey = ! xInfo->hasPropertyByName( g_sRedlineProtectionKey );
328 }
329
330 // set redline mode & key
331 try
332 {
333 Any aAny;
334
335 aAny <<= m_bShowChanges;
336 if ( bHandleShowChanges )
337 {
338 aAny <<= true;
339 m_xModelPropertySet->setPropertyValue( g_sShowChanges, aAny );
340 // TODO maybe we need some property for the view-setting?
341 SwDoc *const pDoc(static_cast<SwXMLImport&>(m_rImport).getDoc());
342 assert(pDoc);
344 }
345 else
346 m_xImportInfoPropertySet->setPropertyValue( g_sShowChanges, aAny );
347
348 aAny <<= m_bRecordChanges;
349 if ( bHandleRecordChanges )
350 m_xModelPropertySet->setPropertyValue( g_sRecordChanges, aAny );
351 else
352 m_xImportInfoPropertySet->setPropertyValue( g_sRecordChanges, aAny );
353
354 aAny <<= m_aProtectionKey;
355 if ( bHandleProtectionKey )
356 m_xModelPropertySet->setPropertyValue( g_sRedlineProtectionKey, aAny );
357 else
358 m_xImportInfoPropertySet->setPropertyValue( g_sRedlineProtectionKey, aAny);
359 }
360 catch (const uno::RuntimeException &) // fdo#65882
361 {
362 SAL_WARN( "sw", "potentially benign ordering issue during shutdown" );
363 }
364}
365
367 std::u16string_view rType,
368 const OUString& rId,
369 const OUString& rAuthor,
370 const OUString& rComment,
371 const util::DateTime& rDateTime,
372 bool bMergeLastPara)
373{
374 // we need to do the following:
375 // 1) parse type string
376 // 2) create RedlineInfo and fill it with data
377 // 3) check for existing redline with same ID
378 // 3a) insert redline into map
379 // 3b) attach to existing redline
380
381 // ad 1)
383 if (rType == m_sInsertion)
384 {
385 eType = RedlineType::Insert;
386 }
387 else if (rType == m_sDeletion)
388 {
389 eType = RedlineType::Delete;
390 }
391 else if (rType == m_sFormatChange)
392 {
393 eType = RedlineType::Format;
394 }
395 else
396 {
397 // no proper type found: early out!
398 return;
399 }
400
401 // ad 2) create a new RedlineInfo
402 RedlineInfo* pInfo = new RedlineInfo();
403
404 // fill entries
405 pInfo->eType = eType;
406 pInfo->sAuthor = rAuthor;
407 pInfo->sComment = rComment;
408 pInfo->aDateTime = rDateTime;
409 pInfo->bMergeLastParagraph = bMergeLastPara;
410
411 // ad 3)
412 auto itPair = m_aRedlineMap.emplace(rId, pInfo);
413 if (itPair.second)
414 return;
415
416 // 3b) we already have a redline with this name: hierarchical redlines
417 // insert pInfo as last element in the chain.
418 // (hierarchy sanity checking happens on inserting into the document)
419
420 // find last element
421 RedlineInfo* pInfoChain;
422 for( pInfoChain = itPair.first->second;
423 nullptr != pInfoChain->pNextRedline;
424 pInfoChain = pInfoChain->pNextRedline) ; // empty loop
425
426 // insert as last element
427 pInfoChain->pNextRedline = pInfo;
428}
429
431 Reference<XTextCursor> const & xOldCursor,
432 const OUString& rId)
433{
434 Reference<XTextCursor> xReturn;
435
436 // this method will modify the document directly -> lock SolarMutex
437 SolarMutexGuard aGuard;
438
439 // get RedlineInfo
440 RedlineMapType::iterator aFind = m_aRedlineMap.find(rId);
441 if (m_aRedlineMap.end() != aFind)
442 {
443 // get document from old cursor (via tunnel)
444 SwDoc* pDoc = lcl_GetDocViaTunnel(xOldCursor);
445
446 if (!pDoc)
447 {
448 SAL_WARN("sw", "no SwDoc => cannot create section.");
449 return nullptr;
450 }
451
452 // create text section for redline
454 (RES_POOLCOLL_STANDARD, false );
455 SwStartNode* pRedlineNode = pDoc->GetNodes().MakeTextSection(
456 pDoc->GetNodes().GetEndOfRedlines(),
458 pColl);
459
460 // remember node-index in RedlineInfo
461 SwNodeIndex aIndex(*pRedlineNode);
462 aFind->second->pContentIndex = new SwNodeIndex(aIndex);
463
464 // create XText for document
466
467 // create (UNO-) cursor
468 SwPosition aPos(*pRedlineNode);
470 new SwXTextCursor(*pDoc, pXText, CursorType::Redline, aPos);
471 pXCursor->GetCursor().Move(fnMoveForward, GoInNode);
472 // cast to avoid ambiguity
473 xReturn = static_cast<text::XWordCursor*>(pXCursor.get());
474 }
475 // else: unknown redline -> Ignore
476
477 return xReturn;
478}
479
481 const OUString& rId,
482 bool bStart,
483 Reference<XTextRange> const & rRange,
484 bool bIsOutsideOfParagraph)
485{
486 RedlineMapType::iterator aFind = m_aRedlineMap.find(rId);
487 if (m_aRedlineMap.end() == aFind)
488 return;
489
490 // RedlineInfo found; now set Cursor
491 RedlineInfo* pInfo = aFind->second;
492 if (bIsOutsideOfParagraph)
493 {
494 // outside of paragraph: remember SwNodeIndex
495 if (bStart)
496 {
497 pInfo->aAnchorStart.SetAsNodeIndex(rRange);
498 }
499 else
500 {
501 pInfo->aAnchorEnd.SetAsNodeIndex(rRange);
502 }
503
504 // also remember that we expect an adjustment for this redline
505 pInfo->bNeedsAdjustment = true;
506 }
507 else
508 {
509 // inside of a paragraph: use regular XTextRanges (bookmarks)
510 if (bStart)
511 pInfo->aAnchorStart.Set(rRange);
512 else
513 pInfo->aAnchorEnd.Set(rRange);
514 }
515
516 // if this Cursor was the last missing info, we insert the
517 // node into the document
518 // then we can remove the entry from the map and destroy the object
519 if (IsReady(pInfo))
520 {
521 InsertIntoDocument(pInfo);
522 m_aRedlineMap.erase(rId);
523 delete pInfo;
524 }
525 // else: unknown Id -> ignore
526}
527
529 const OUString& rId)
530{
531 // this method will modify the document directly -> lock SolarMutex
532 SolarMutexGuard aGuard;
533
534 // start + end nodes are treated the same. For either it's
535 // necessary that the target node already exists.
536
537 RedlineMapType::iterator aFind = m_aRedlineMap.find(rId);
538 if (m_aRedlineMap.end() == aFind)
539 return;
540
541 // RedlineInfo found; now set Cursor
542 RedlineInfo* pInfo = aFind->second;
543
544 pInfo->bNeedsAdjustment = false;
545
546 // if now ready, insert into document
547 if( IsReady(pInfo) )
548 {
549 InsertIntoDocument(pInfo);
550 m_aRedlineMap.erase(rId);
551 delete pInfo;
552 }
553 // else: can't find redline -> ignore
554}
555
557{
558 // we can insert a redline if we have start & end, and we don't
559 // expect adjustments for either of these
560 return ( pRedline->aAnchorEnd.IsValid() &&
561 pRedline->aAnchorStart.IsValid() &&
562 !pRedline->bNeedsAdjustment );
563}
564
566static auto RecursiveContains(SwStartNode const& rRedlineSection, SwNode const& rPos) -> bool
567{
568 if (rRedlineSection.GetIndex() <= rPos.GetIndex()
569 && rPos.GetIndex() <= rRedlineSection.EndOfSectionIndex())
570 {
571 return true;
572 }
573 // loop to iterate "up" in the node tree and find an anchored XText
574 for (SwStartNode const* pStartNode = rPos.StartOfSectionNode();
575 pStartNode != nullptr && pStartNode->GetIndex() != SwNodeOffset(0);
576 pStartNode = pStartNode->StartOfSectionNode())
577 {
578 switch (pStartNode->GetStartNodeType())
579 {
582 continue;
583 break;
584 case SwFlyStartNode:
585 {
586 SwFrameFormat const*const pFormat(pStartNode->GetFlyFormat());
587 assert(pFormat);
588 SwFormatAnchor const& rAnchor(pFormat->GetAnchor());
589 if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
590 {
591 return false;
592 }
593 else if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_FLY)
594 { // anchor is on a start node, avoid skipping it:
595 pStartNode = rAnchor.GetAnchorNode()->GetStartNode();
596 assert(pStartNode);
597 // pass the next node to recursive call - it will call
598 // call StartOfSectionNode on it and go back to pStartNode
599 SwNodeIndex const next(*pStartNode, +1);
600 return RecursiveContains(rRedlineSection, next.GetNode());
601 }
602 else
603 {
604 return RecursiveContains(rRedlineSection, *rAnchor.GetAnchorNode());
605 }
606 }
607 break;
609 { // sigh ... need to search
610 for (SwTextFootnote const*const pFootnote : rRedlineSection.GetDoc().GetFootnoteIdxs())
611 {
612 if (pStartNode == pFootnote->GetStartNode()->GetNode().GetStartNode())
613 {
614 return RecursiveContains(rRedlineSection, pFootnote->GetTextNode());
615 }
616 }
617 assert(false);
618 }
619 break;
622 return false; // headers aren't anchored
623 break;
624 default:
625 assert(false);
626 break;
627 }
628 }
629 return false;
630}
631
633{
634 OSL_ENSURE(nullptr != pRedlineInfo, "need redline info");
635 OSL_ENSURE(IsReady(pRedlineInfo), "redline info not complete yet!");
636
637 // this method will modify the document directly -> lock SolarMutex
638 SolarMutexGuard aGuard;
639
640 // Insert the Redline as described by pRedlineInfo into the
641 // document. If we are in insert mode, don't insert any redlines
642 // (and delete 'deleted' inline redlines)
643
644 // get the document (from one of the positions)
645 SwDoc* pDoc = pRedlineInfo->aAnchorStart.GetDoc();
646
647 if (!pDoc)
648 {
649 SAL_WARN("sw", "no SwDoc => cannot insert redline.");
650 return;
651 }
652
653 // now create the PaM for the redline
654 SwPaM aPaM(pDoc->GetNodes().GetEndOfContent());
655 pRedlineInfo->aAnchorStart.CopyPositionInto(*aPaM.GetPoint(), *pDoc);
656 aPaM.SetMark();
657 pRedlineInfo->aAnchorEnd.CopyPositionInto(*aPaM.GetPoint(), *pDoc);
658
659 // collapse PaM if (start == end)
660 if (*aPaM.GetPoint() == *aPaM.GetMark())
661 {
662 aPaM.DeleteMark();
663 }
664
665 // cover three cases:
666 // 1) empty redlines (no range, no content)
667 // 2) check for:
668 // a) bIgnoreRedline (e.g. insert mode)
669 // b) illegal PaM range (CheckNodesRange())
670 // c) redline with empty content section (quite useless)
671 // 3) normal case: insert redline
672 SwTextNode const* pTempNode(nullptr);
673 if( !aPaM.HasMark() && (pRedlineInfo->pContentIndex == nullptr) )
674 {
675 // these redlines have no function, and will thus be ignored (just as
676 // in sw3io), so no action here
677 }
678 else if ( m_bIgnoreRedlines ||
679 !CheckNodesRange( aPaM.GetPoint()->GetNode(),
680 aPaM.GetMark()->GetNode(),
681 true )
682 || (pRedlineInfo->pContentIndex
683 && (pRedlineInfo->pContentIndex->GetIndex() + 2
684 == pRedlineInfo->pContentIndex->GetNode().EndOfSectionIndex())
685 && (pTempNode = pDoc->GetNodes()[pRedlineInfo->pContentIndex->GetIndex() + 1]->GetTextNode()) != nullptr
686 && pTempNode->GetText().isEmpty()
687 && !pTempNode->GetpSwpHints()
688 && pTempNode->GetAnchoredFlys().empty()))
689 {
690 // ignore redline (e.g. file loaded in insert mode):
691 // delete 'deleted' redlines and forget about the whole thing
692 if (RedlineType::Delete == pRedlineInfo->eType)
693 {
695 // And what about the "deleted nodes"?
696 // They have to be deleted as well (#i80689)!
697 if( m_bIgnoreRedlines && pRedlineInfo->pContentIndex != nullptr )
698 {
699 const SwNodeIndex& rIdx( *pRedlineInfo->pContentIndex );
700 const SwNode* pEnd = rIdx.GetNode().EndOfSectionNode();
701 if( pEnd )
702 {
703 SwPaM aDel( rIdx.GetNode(), 0, *pEnd, 1 );
705 }
706 }
707 }
708 }
709 else if (pRedlineInfo->pContentIndex != nullptr
710 // should be enough to check 1 position of aPaM bc CheckNodesRange() above
711 && RecursiveContains(*pRedlineInfo->pContentIndex->GetNode().GetStartNode(), aPaM.GetPoint()->GetNode()))
712 {
713 SAL_WARN("sw.xml", "Recursive change tracking, removing");
714 // reuse aPaM to remove it from nodes that will be deleted
715 aPaM.GetPoint()->Assign(pRedlineInfo->pContentIndex->GetNode());
716 aPaM.DeleteMark();
718 }
719 else
720 {
721 // regular file loading: insert redline
722
723 // create redline (using pRedlineData which gets copied in SwRangeRedline())
724 SwRedlineData* pRedlineData = ConvertRedline(pRedlineInfo, pDoc);
725 SwRangeRedline* pRedline =
726 new SwRangeRedline( pRedlineData, *aPaM.GetPoint(),
727 !pRedlineInfo->bMergeLastParagraph );
728
729 // tdf#107292 fix order of delete redlines at the same position by removing
730 // the already inserted redlines temporarily and inserting them back in reverse
731 // order after inserting pRedline
732 std::vector<const SwRangeRedline*> aSwapRedlines;
733 if ( RedlineType::Delete == pRedlineInfo->eType )
734 {
736 while ( const SwRangeRedline* pRedline2 =
737 pDoc->getIDocumentRedlineAccess().GetRedline( *pRedline->Start(), &n ) )
738 {
740 aSwapRedlines.push_back(pRedline2);
741 aRedlineTable.Remove(n);
742 }
743 }
744
745 // set mark
746 if( aPaM.HasMark() )
747 {
748 pRedline->SetMark();
749 *(pRedline->GetMark()) = *aPaM.GetMark();
750 }
751
752 // set content node (if necessary)
753 if (nullptr != pRedlineInfo->pContentIndex)
754 {
755 SwNodeOffset nPoint = aPaM.GetPoint()->GetNodeIndex();
756 if( nPoint < pRedlineInfo->pContentIndex->GetIndex() ||
757 nPoint > pRedlineInfo->pContentIndex->GetNode().EndOfSectionIndex() )
758 pRedline->SetContentIdx(*pRedlineInfo->pContentIndex);
759 else
760 SAL_WARN("sw", "Recursive change tracking");
761 }
762
763 // set redline mode (without doing the associated book-keeping)
765 pDoc->getIDocumentRedlineAccess().AppendRedline(pRedline, false);
766
767 // restore the correct order of the delete redlines at the same position
768 for (auto i = aSwapRedlines.rbegin(); i != aSwapRedlines.rend(); ++i)
769 pDoc->getIDocumentRedlineAccess().AppendRedline(const_cast<SwRangeRedline*>(*i), false);
770
772 }
773}
774
776 RedlineInfo* pRedlineInfo,
777 SwDoc* pDoc)
778{
779 // convert info:
780 // 1) Author String -> Author ID (default to zero)
781 std::size_t nAuthorId = (nullptr == pDoc) ? 0 :
783
784 // 2) util::DateTime -> DateTime
786 aDT.SetYear( pRedlineInfo->aDateTime.Year );
787 aDT.SetMonth( pRedlineInfo->aDateTime.Month );
788 aDT.SetDay( pRedlineInfo->aDateTime.Day );
789 aDT.SetHour( pRedlineInfo->aDateTime.Hours );
790 aDT.SetMin( pRedlineInfo->aDateTime.Minutes );
791 aDT.SetSec( pRedlineInfo->aDateTime.Seconds );
792 aDT.SetNanoSec( pRedlineInfo->aDateTime.NanoSeconds );
793
794 // 3) recursively convert next redline
795 // ( check presence and sanity of hierarchical redline info )
796 SwRedlineData* pNext = nullptr;
797 if ( (nullptr != pRedlineInfo->pNextRedline) &&
798 (RedlineType::Delete == pRedlineInfo->eType) &&
799 (RedlineType::Insert == pRedlineInfo->pNextRedline->eType) )
800 {
801 pNext = ConvertRedline(pRedlineInfo->pNextRedline, pDoc);
802 }
803
804 // create redline data
805 SwRedlineData* pData = new SwRedlineData(pRedlineInfo->eType,
806 nAuthorId, aDT,
807 pRedlineInfo->sComment,
808 pNext); // next data (if available)
809
810 return pData;
811}
812
814{
815 m_bShowChanges = bShow;
816}
817
819{
820 m_bRecordChanges = bRecord;
821}
822
824 const Sequence<sal_Int8> & rKey )
825{
826 m_aProtectionKey = rKey;
827}
828
829/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
@ On
RedlineFlags on.
@ NONE
no RedlineFlags
const SvXMLImport & m_rImport
constexpr OUStringLiteral g_sShowChanges
static SwDoc * lcl_GetDocViaTunnel(Reference< XTextCursor > const &rCursor)
static auto RecursiveContains(SwStartNode const &rRedlineSection, SwNode const &rPos) -> bool
recursively check if rPos or its anchor (if in fly or footnote) is in redline section
constexpr OUStringLiteral g_sRedlineProtectionKey
constexpr OUStringLiteral g_sRecordChanges
void SetMonth(sal_uInt16 nNewMonth)
void SetYear(sal_Int16 nNewYear)
void SetDay(sal_uInt16 nNewDay)
virtual void DeleteSection(SwNode *pNode)=0
Delete section containing the node.
virtual void DeleteRange(SwPaM &)=0
Delete a range SwFlyFrameFormat.
virtual void SetRedlineFlags_intern(RedlineFlags eMode)=0
Set a new redline mode.
virtual const SwRedlineTable & GetRedlineTable() const =0
virtual std::size_t InsertRedlineAuthor(const OUString &rAuthor)=0
virtual AppendResult AppendRedline(SwRangeRedline *pNewRedl, bool bCallDelete)=0
Append a new redline.
virtual const SwRangeRedline * GetRedline(const SwPosition &rPos, SwRedlineTable::size_type *pFndPos) const =0
virtual SwTextFormatColl * GetTextCollFromPool(sal_uInt16 nId, bool bRegardLanguage=true)=0
Return "Auto-Collection with ID.
RedlineInfo * pNextRedline
XTextRangeOrNodeIndexPosition aAnchorStart
util::DateTime aDateTime
XTextRangeOrNodeIndexPosition aAnchorEnd
SwNodeIndex * pContentIndex
Definition: doc.hxx:197
IDocumentContentOperations const & getIDocumentContentOperations() const
Definition: doc.cxx:329
SwNodes & GetNodes()
Definition: doc.hxx:422
IDocumentRedlineAccess const & getIDocumentRedlineAccess() const
Definition: doc.cxx:349
::sw::DocumentRedlineManager const & GetDocumentRedlineManager() const
Definition: doc.cxx:359
IDocumentStylePoolAccess const & getIDocumentStylePoolAccess() const
Definition: doc.cxx:440
FlyAnchors.
Definition: fmtanchr.hxx:37
RndStdIds GetAnchorId() const
Definition: fmtanchr.hxx:67
SwNode * GetAnchorNode() const
Definition: atrfrm.cxx:1614
const SwFormatAnchor & GetAnchor(bool=true) const
Definition: fmtanchr.hxx:88
Style of a layout element.
Definition: frmfmt.hxx:72
Marks a node in the document model.
Definition: ndindex.hxx:31
SwNode & GetNode() const
Definition: ndindex.hxx:123
SwNodeOffset GetIndex() const
Definition: ndindex.hxx:111
Base class of the Writer document model elements.
Definition: node.hxx:98
SwStartNode * GetStartNode()
Definition: node.hxx:642
SwNodeOffset EndOfSectionIndex() const
Definition: node.hxx:691
std::vector< SwFrameFormat * > const & GetAnchoredFlys() const
Definition: node.hxx:318
const SwEndNode * EndOfSectionNode() const
Definition: node.hxx:695
SwNode & GetEndOfContent() const
Regular ContentSection (i.e. the BodyText).
Definition: ndarr.hxx:165
SwStartNode * MakeTextSection(const SwNode &rWhere, SwStartNodeType eSttNdTyp, SwTextFormatColl *pColl)
Definition: nodes.cxx:1925
SwNode & GetEndOfRedlines() const
Section for all Redlines.
Definition: ndarr.hxx:160
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:188
const SwPosition * GetMark() const
Definition: pam.hxx:255
virtual void SetMark()
Unless this is called, the getter method of Mark will return Point.
Definition: pam.cxx:643
void DeleteMark()
Definition: pam.hxx:232
const SwPosition * GetPoint() const
Definition: pam.hxx:253
const SwPosition * Start() const
Definition: pam.hxx:258
bool HasMark() const
A PaM marks a selection if Point and Mark are distinct positions.
Definition: pam.hxx:251
void SetContentIdx(const SwNodeIndex &)
Definition: docredln.cxx:1894
void Remove(size_type nPos)
Definition: docredln.cxx:669
vector_type::size_type size_type
Definition: docary.hxx:223
Starts a section of nodes in the document model.
Definition: node.hxx:348
SwTextAttr subclass for footnotes and endnotes.
Definition: txtftn.hxx:34
Represents the style of a paragraph.
Definition: fmtcol.hxx:61
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:112
SwpHints * GetpSwpHints()
Definition: ndtxt.hxx:252
const OUString & GetText() const
Definition: ndtxt.hxx:244
SwXRedlineText provides an XText which may be used to write directly into a redline node.
Definition: unoredline.hxx:38
const SwDoc & GetDoc() const
Definition: unoobj2.cxx:762
css::uno::Sequence< sal_Int8 > m_aProtectionKey
void SetProtectionKey(const css::uno::Sequence< sal_Int8 > &rKey)
SwRedlineData * ConvertRedline(RedlineInfo *pRedline, SwDoc *pDoc)
css::uno::Reference< css::text::XTextCursor > CreateRedlineTextSection(css::uno::Reference< css::text::XTextCursor > const &xOldCursor, const OUString &rId)
void SetCursor(const OUString &rId, bool bStart, css::uno::Reference< css::text::XTextRange > const &rRange, bool bIsOutsideOfParagraph)
void Add(std::u16string_view rType, const OUString &rId, const OUString &rAuthor, const OUString &rComment, const css::util::DateTime &rDateTime, bool bMergeLastParagraph)
css::uno::Reference< css::beans::XPropertySet > m_xModelPropertySet
static bool IsReady(const RedlineInfo *pRedline)
css::uno::Reference< css::beans::XPropertySet > m_xImportInfoPropertySet
void AdjustStartNodeCursor(const OUString &rId)
Adjust the start (end) position for a redline that begins in a start node.
void InsertIntoDocument(RedlineInfo *pRedline)
void SetShowChanges(bool bShowChanges)
void SetRecordChanges(bool bRecordChanges)
XMLRedlineImportHelper(SvXMLImport &rImport, bool bIgnoreRedlines, const css::uno::Reference< css::beans::XPropertySet > &rModel, const css::uno::Reference< css::beans::XPropertySet > &rImportInfoSet)
void SetHideRedlines(bool const bHideRedlines)
void SetMin(sal_uInt16 nNewMin)
void SetNanoSec(sal_uInt32 nNewNanoSec)
void SetSec(sal_uInt16 nNewSec)
void SetHour(sal_uInt16 nNewHour)
RedlineType
virtual void Insert(SotClipboardFormatId nFormat, const OUString &rFormatName) override
float u
std::deque< AttacherIndex_Impl > aIndex
DocumentType eType
sal_Int64 n
#define SAL_WARN(area, stream)
std::unique_ptr< sal_Int32[]> pData
int i
bool getPropertyValue(ValueType &rValue, css::uno::Reference< css::beans::XPropertySet > const &xPropSet, OUString const &propName)
bool XTextRangeToSwPaM(SwUnoInternalPaM &rToFill, const uno::Reference< text::XTextRange > &xTextRange, ::sw::TextRangeMode const eMode)
Definition: unoobj2.cxx:1108
XML_DELETION
XML_INSERTION
XML_FORMAT_CHANGE
const OUString & GetXMLToken(enum XMLTokenEnum eToken)
@ SwNormalStartNode
Definition: ndtyp.hxx:52
@ SwHeaderStartNode
Definition: ndtyp.hxx:56
@ SwFooterStartNode
Definition: ndtyp.hxx:57
@ SwFlyStartNode
Definition: ndtyp.hxx:54
@ SwTableBoxStartNode
Definition: ndtyp.hxx:53
@ SwFootnoteStartNode
Definition: ndtyp.hxx:55
bool GoInNode(SwPaM &rPam, SwMoveFnCollection const &fnMove)
Definition: pam.cxx:1194
bool CheckNodesRange(const SwNode &rStt, const SwNode &rEnd, bool bChkSection)
Check if the given range is inside one of the defined top-level sections.
Definition: pam.cxx:349
SwMoveFnCollection const & fnMoveForward
SwPam::Move()/Find() default argument.
Definition: paminit.cxx:61
@ RES_POOLCOLL_STANDARD
Standard.
Definition: poolfmt.hxx:250
Marks a position in the document model.
Definition: pam.hxx:38
SwNode & GetNode() const
Definition: pam.hxx:81
void Assign(const SwNode &rNd, SwNodeOffset nDelta, sal_Int32 nContentOffset=0)
These all set both nNode and nContent.
Definition: pam.cxx:231
SwNodeOffset GetNodeIndex() const
Definition: pam.hxx:78