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