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