xref: /AOO41X/main/sw/source/filter/xml/XMLRedlineImportHelper.cxx (revision efeef26f81c84063fb0a91bde3856d4a51172d90)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sw.hxx"
26 
27 
28 #include "XMLRedlineImportHelper.hxx"
29 #include <unotextcursor.hxx>
30 #include <unotextrange.hxx>
31 #include <unocrsr.hxx>
32 #include "doc.hxx"
33 #include <tools/datetime.hxx>
34 #include "poolfmt.hxx"
35 #include "unoredline.hxx"
36 #include <xmloff/xmltoken.hxx>
37 #include <com/sun/star/frame/XModel.hpp>
38 
39 // for locking SolarMutex: svapp + mutex
40 #include <vcl/svapp.hxx>
41 #include <vos/mutex.hxx>
42 
43 
44 
45 using namespace ::com::sun::star;
46 using namespace ::com::sun::star::uno;
47 using namespace ::xmloff::token;
48 
49 using ::rtl::OUString;
50 using ::com::sun::star::frame::XModel;
51 using ::com::sun::star::text::XTextCursor;
52 using ::com::sun::star::text::XTextRange;
53 using ::com::sun::star::text::XText;
54 using ::com::sun::star::text::XWordCursor;
55 using ::com::sun::star::lang::XUnoTunnel;
56 using ::com::sun::star::beans::XPropertySet;
57 using ::com::sun::star::beans::XPropertySetInfo;
58 // collision with tools/DateTime: use UNO DateTime as util::DateTime
59 // using util::DateTime;
60 
61 
62 //
63 // a few helper functions
64 //
65 
lcl_GetDocViaTunnel(Reference<XTextCursor> & rCursor)66 SwDoc* lcl_GetDocViaTunnel( Reference<XTextCursor> & rCursor )
67 {
68     Reference<XUnoTunnel> xTunnel( rCursor, UNO_QUERY);
69     OSL_ENSURE(xTunnel.is(), "missing XUnoTunnel for XTextCursor");
70     OTextCursorHelper *const pXCursor =
71         ::sw::UnoTunnelGetImplementation<OTextCursorHelper>(xTunnel);
72     OSL_ENSURE( pXCursor, "OTextCursorHelper missing" );
73     return (pXCursor) ? pXCursor->GetDoc() : 0;
74 }
75 
lcl_GetDocViaTunnel(Reference<XTextRange> & rRange)76 SwDoc* lcl_GetDocViaTunnel( Reference<XTextRange> & rRange )
77 {
78     Reference<XUnoTunnel> xTunnel(rRange, UNO_QUERY);
79     OSL_ENSURE(xTunnel.is(), "missing XUnoTunnel for XTextRange");
80     SwXTextRange *const pXRange =
81         ::sw::UnoTunnelGetImplementation<SwXTextRange>(xTunnel);
82     // #i115174#: this may be a SvxUnoTextRange
83 //    OSL_ENSURE( pXRange, "SwXTextRange missing" );
84     return (pXRange) ? pXRange->GetDoc() : 0;
85 }
86 
87 
88 //
89 // XTextRangeOrNodeIndexPosition: store a position into the text
90 // *either* as an XTextRange or as an SwNodeIndex. The reason is that
91 // we must store either pointers to StartNodes (because redlines may
92 // start on start nodes) or to a text position, and there appears to
93 // be no existing type that could do both. Things are complicated by
94 // the matter that (e.g in section import) we delete a few characters,
95 // which may cause bookmarks (as used by XTextRange) to be deleted.
96 //
97 
98 class XTextRangeOrNodeIndexPosition
99 {
100     Reference<XTextRange> xRange;
101     SwNodeIndex* pIndex;    /// pIndex will point to the *previous* node
102 
103 public:
104     XTextRangeOrNodeIndexPosition();
105     ~XTextRangeOrNodeIndexPosition();
106 
107     void Set( Reference<XTextRange> & rRange );
108     void Set( SwNodeIndex& rIndex );
109     void SetAsNodeIndex( Reference<XTextRange> & rRange );
110 
111     void CopyPositionInto(SwPosition& rPos, SwDoc & rDoc);
112     SwDoc* GetDoc();
113 
114     sal_Bool IsValid();
115 };
116 
XTextRangeOrNodeIndexPosition()117 XTextRangeOrNodeIndexPosition::XTextRangeOrNodeIndexPosition() :
118     xRange(NULL),
119     pIndex(NULL)
120 {
121 }
122 
~XTextRangeOrNodeIndexPosition()123 XTextRangeOrNodeIndexPosition::~XTextRangeOrNodeIndexPosition()
124 {
125     delete pIndex;
126 }
127 
Set(Reference<XTextRange> & rRange)128 void XTextRangeOrNodeIndexPosition::Set( Reference<XTextRange> & rRange )
129 {
130     xRange = rRange->getStart();    // set bookmark
131     if (NULL != pIndex)
132     {
133         delete pIndex;
134         pIndex = NULL;
135     }
136 }
137 
Set(SwNodeIndex & rIndex)138 void XTextRangeOrNodeIndexPosition::Set( SwNodeIndex& rIndex )
139 {
140     if (NULL != pIndex)
141         delete pIndex;
142 
143     pIndex = new SwNodeIndex(rIndex);
144     (*pIndex)-- ;   // previous node!!!
145     xRange = NULL;
146 }
147 
SetAsNodeIndex(Reference<XTextRange> & rRange)148 void XTextRangeOrNodeIndexPosition::SetAsNodeIndex(
149     Reference<XTextRange> & rRange )
150 {
151     // XTextRange -> XTunnel -> SwXTextRange
152     SwDoc* pDoc = lcl_GetDocViaTunnel(rRange);
153 
154     if (!pDoc)
155     {
156         OSL_TRACE("SetAsNodeIndex: no SwDoc");
157         return;
158     }
159 
160     // SwXTextRange -> PaM
161     SwUnoInternalPaM aPaM(*pDoc);
162 #ifdef DBG_UTIL
163     sal_Bool bSuccess =
164 #endif
165         ::sw::XTextRangeToSwPaM(aPaM, rRange);
166     DBG_ASSERT(bSuccess, "illegal range");
167 
168     // PaM -> Index
169     Set(aPaM.GetPoint()->nNode);
170 }
171 
172 void
CopyPositionInto(SwPosition & rPos,SwDoc & rDoc)173 XTextRangeOrNodeIndexPosition::CopyPositionInto(SwPosition& rPos, SwDoc & rDoc)
174 {
175     DBG_ASSERT(IsValid(), "Can't get Position");
176 
177     // create PAM from start cursor (if no node index is present)
178     if (NULL == pIndex)
179     {
180         SwUnoInternalPaM aUnoPaM(rDoc);
181 #ifdef DBG_UTIL
182         sal_Bool bSuccess =
183 #endif
184             ::sw::XTextRangeToSwPaM(aUnoPaM, xRange);
185         DBG_ASSERT(bSuccess, "illegal range");
186 
187         rPos = *aUnoPaM.GetPoint();
188     }
189     else
190     {
191         rPos.nNode = *pIndex;
192         rPos.nNode++;           // pIndex points to previous index !!!
193         rPos.nContent.Assign( rPos.nNode.GetNode().GetCntntNode(), 0 );
194     }
195 }
196 
GetDoc()197 SwDoc* XTextRangeOrNodeIndexPosition::GetDoc()
198 {
199     DBG_ASSERT(IsValid(), "Can't get Doc");
200 
201     return (NULL != pIndex) ? pIndex->GetNodes().GetDoc() : lcl_GetDocViaTunnel(xRange);
202 }
203 
IsValid()204 sal_Bool XTextRangeOrNodeIndexPosition::IsValid()
205 {
206     return ( xRange.is() || (pIndex != NULL) );
207 }
208 
209 
210 //
211 // RedlineInfo: temporary storage for redline data
212 //
213 
214 class RedlineInfo
215 {
216 public:
217     RedlineInfo();
218     ~RedlineInfo();
219 
220     /// redline type (insert, delete, ...)
221     RedlineType_t eType;
222 
223     // info fields:
224     OUString sAuthor;               /// change author string
225     OUString sComment;              /// change comment string
226     util::DateTime aDateTime;       /// change DateTime
227     sal_Bool bMergeLastParagraph;   /// the SwRedline::IsDelLastPara flag
228 
229     // each position can may be either empty, an XTextRange, or an SwNodeIndex
230 
231     // start pos of anchor (may be empty)
232     XTextRangeOrNodeIndexPosition aAnchorStart;
233 
234     // end pos of anchor (may be empty)
235     XTextRangeOrNodeIndexPosition aAnchorEnd;
236 
237     /// index of content node (maybe NULL)
238     SwNodeIndex* pContentIndex;
239 
240     /// next redline info (for hierarchical redlines)
241     RedlineInfo* pNextRedline;
242 
243     /// store whether we expect an adjustment for this redline
244     sal_Bool bNeedsAdjustment;
245 };
246 
RedlineInfo()247 RedlineInfo::RedlineInfo() :
248     eType(nsRedlineType_t::REDLINE_INSERT),
249     sAuthor(),
250     sComment(),
251     aDateTime(),
252     bMergeLastParagraph( sal_False ),
253     aAnchorStart(),
254     aAnchorEnd(),
255     pContentIndex(NULL),
256     pNextRedline(NULL),
257     bNeedsAdjustment( sal_False )
258 {
259 }
260 
~RedlineInfo()261 RedlineInfo::~RedlineInfo()
262 {
263     delete pContentIndex;
264     delete pNextRedline;
265 }
266 
267 
268 //
269 // XMLRedlineImportHelper
270 //
271 
XMLRedlineImportHelper(sal_Bool bNoRedlinesPlease,const Reference<XPropertySet> & rModel,const Reference<XPropertySet> & rImportInfo)272 XMLRedlineImportHelper::XMLRedlineImportHelper(
273     sal_Bool bNoRedlinesPlease,
274     const Reference<XPropertySet> & rModel,
275     const Reference<XPropertySet> & rImportInfo ) :
276         sEmpty(),
277         sInsertion( GetXMLToken( XML_INSERTION )),
278         sDeletion( GetXMLToken( XML_DELETION )),
279         sFormatChange( GetXMLToken( XML_FORMAT_CHANGE )),
280         sShowChanges(RTL_CONSTASCII_USTRINGPARAM("ShowChanges")),
281         sRecordChanges(RTL_CONSTASCII_USTRINGPARAM("RecordChanges")),
282         sRedlineProtectionKey(RTL_CONSTASCII_USTRINGPARAM("RedlineProtectionKey")),
283         aRedlineMap(),
284         bIgnoreRedlines(bNoRedlinesPlease),
285         xModelPropertySet(rModel),
286         xImportInfoPropertySet(rImportInfo)
287 {
288     // check to see if redline mode is handled outside of component
289     sal_Bool bHandleShowChanges = sal_True;
290     sal_Bool bHandleRecordChanges = sal_True;
291     sal_Bool bHandleProtectionKey = sal_True;
292     if ( xImportInfoPropertySet.is() )
293     {
294         Reference<XPropertySetInfo> xInfo =
295             xImportInfoPropertySet->getPropertySetInfo();
296 
297         bHandleShowChanges = ! xInfo->hasPropertyByName( sShowChanges );
298         bHandleRecordChanges = ! xInfo->hasPropertyByName( sRecordChanges );
299         bHandleProtectionKey = ! xInfo->hasPropertyByName( sRedlineProtectionKey );
300     }
301 
302     // get redline mode
303     bShowChanges = *(sal_Bool*)
304         ( bHandleShowChanges ? xModelPropertySet : xImportInfoPropertySet )
305         ->getPropertyValue( sShowChanges ).getValue();
306     bRecordChanges = *(sal_Bool*)
307         ( bHandleRecordChanges ? xModelPropertySet : xImportInfoPropertySet )
308         ->getPropertyValue( sRecordChanges ).getValue();
309     {
310         Any aAny = (bHandleProtectionKey  ? xModelPropertySet
311                                           : xImportInfoPropertySet )
312                         ->getPropertyValue( sRedlineProtectionKey );
313         aAny >>= aProtectionKey;
314     }
315 
316     // set redline mode to "don't record changes"
317     if( bHandleRecordChanges )
318     {
319         Any aAny;
320         sal_Bool bTmp = sal_False;
321         aAny.setValue( &bTmp, ::getBooleanCppuType() );
322         xModelPropertySet->setPropertyValue( sRecordChanges, aAny );
323     }
324 }
325 
~XMLRedlineImportHelper()326 XMLRedlineImportHelper::~XMLRedlineImportHelper()
327 {
328     // delete all left over (and obviously incomplete) RedlineInfos (and map)
329     RedlineMapType::iterator aFind = aRedlineMap.begin();
330     for( ; aRedlineMap.end() != aFind; aFind++ )
331     {
332         RedlineInfo* pInfo = aFind->second;
333 
334         // left-over redlines. Insert them if possible (but assert),
335         // and delete the incomplete ones. Finally, delete it.
336         if( IsReady(pInfo) )
337         {
338             DBG_ERROR("forgotten RedlineInfo; now inserted");
339             InsertIntoDocument( pInfo );
340         }
341         else
342         {
343             // try if only the adjustment was missing
344             pInfo->bNeedsAdjustment = sal_False;
345             if( IsReady(pInfo) )
346             {
347                 DBG_ERROR("RedlineInfo without adjustment; now inserted");
348                 InsertIntoDocument( pInfo );
349             }
350             else
351             {
352                 // this situation occurs if redlines aren't closed
353                 // (i.e. end without start, or start without
354                 // end). This may well be a problem in the file,
355                 // rather than the code.
356                 DBG_ERROR("incomplete redline (maybe file was corrupt); "
357                           "now deleted");
358             }
359         }
360         delete pInfo;
361     }
362     aRedlineMap.clear();
363 
364     // set redline mode, either to info property set, or directly to
365     // the document
366     sal_Bool bHandleShowChanges = sal_True;
367     sal_Bool bHandleRecordChanges = sal_True;
368     sal_Bool bHandleProtectionKey = sal_True;
369     if ( xImportInfoPropertySet.is() )
370     {
371         Reference<XPropertySetInfo> xInfo =
372             xImportInfoPropertySet->getPropertySetInfo();
373 
374         bHandleShowChanges = ! xInfo->hasPropertyByName( sShowChanges );
375         bHandleRecordChanges = ! xInfo->hasPropertyByName( sRecordChanges );
376         bHandleProtectionKey = ! xInfo->hasPropertyByName( sRedlineProtectionKey );
377     }
378 
379     // set redline mode & key
380     Any aAny;
381 
382     aAny.setValue( &bShowChanges, ::getBooleanCppuType() );
383     if ( bHandleShowChanges )
384         xModelPropertySet->setPropertyValue( sShowChanges, aAny );
385     else
386         xImportInfoPropertySet->setPropertyValue( sShowChanges, aAny );
387 
388     aAny.setValue( &bRecordChanges, ::getBooleanCppuType() );
389     if ( bHandleRecordChanges )
390         xModelPropertySet->setPropertyValue( sRecordChanges, aAny );
391     else
392         xImportInfoPropertySet->setPropertyValue( sRecordChanges, aAny );
393 
394     aAny <<= aProtectionKey;
395     if ( bHandleProtectionKey )
396         xModelPropertySet->setPropertyValue( sRedlineProtectionKey, aAny );
397     else
398         xImportInfoPropertySet->setPropertyValue( sRedlineProtectionKey, aAny);
399 }
400 
Add(const OUString & rType,const OUString & rId,const OUString & rAuthor,const OUString & rComment,const util::DateTime & rDateTime,sal_Bool bMergeLastPara)401 void XMLRedlineImportHelper::Add(
402     const OUString& rType,
403     const OUString& rId,
404     const OUString& rAuthor,
405     const OUString& rComment,
406     const util::DateTime& rDateTime,
407     sal_Bool bMergeLastPara)
408 {
409     // we need to do the following:
410     // 1) parse type string
411     // 2) create RedlineInfo and fill it with data
412     // 3) check for existing redline with same ID
413     // 3a) insert redline into map
414     // 3b) attach to existing redline
415 
416     // ad 1)
417     RedlineType_t eType;
418     if (rType.equals(sInsertion))
419     {
420         eType = nsRedlineType_t::REDLINE_INSERT;
421     }
422     else if (rType.equals(sDeletion))
423     {
424         eType = nsRedlineType_t::REDLINE_DELETE;
425     }
426     else if (rType.equals(sFormatChange))
427     {
428         eType = nsRedlineType_t::REDLINE_FORMAT;
429     }
430     else
431     {
432         // no proper type found: early out!
433         return;
434     }
435 
436     // ad 2) create a new RedlineInfo
437     RedlineInfo* pInfo = new RedlineInfo();
438 
439     // fill entries
440     pInfo->eType = eType;
441     pInfo->sAuthor = rAuthor;
442     pInfo->sComment = rComment;
443     pInfo->aDateTime = rDateTime;
444     pInfo->bMergeLastParagraph = bMergeLastPara;
445 
446 
447     // ad 3)
448     if (aRedlineMap.end() == aRedlineMap.find(rId))
449     {
450         // 3a) insert into map
451         aRedlineMap[rId] = pInfo;
452     }
453     else
454     {
455         // 3b) we already have a redline with this name: hierarchical redlines
456         // insert pInfo as last element in the chain.
457         // (hierarchy sanity checking happens on insertino into the document)
458 
459         // find last element
460         RedlineInfo* pInfoChain;
461         for( pInfoChain = aRedlineMap[rId];
462             NULL != pInfoChain->pNextRedline;
463             pInfoChain = pInfoChain->pNextRedline) ; // empty loop
464 
465         // insert as last element
466         pInfoChain->pNextRedline = pInfo;
467     }
468 }
469 
CreateRedlineTextSection(Reference<XTextCursor> xOldCursor,const OUString & rId)470 Reference<XTextCursor> XMLRedlineImportHelper::CreateRedlineTextSection(
471     Reference<XTextCursor> xOldCursor,
472     const OUString& rId)
473 {
474     Reference<XTextCursor> xReturn;
475 
476     // this method will modify the document directly -> lock SolarMutex
477     vos::OGuard aGuard(Application::GetSolarMutex());
478 
479     // get RedlineInfo
480     RedlineMapType::iterator aFind = aRedlineMap.find(rId);
481     if (aRedlineMap.end() != aFind)
482     {
483         // get document from old cursor (via tunnel)
484         SwDoc* pDoc = lcl_GetDocViaTunnel(xOldCursor);
485 
486         if (!pDoc)
487         {
488             OSL_TRACE("XMLRedlineImportHelper::CreateRedlineTextSection: "
489                 "no SwDoc => cannot create section.");
490             return 0;
491         }
492 
493         // create text section for redline
494         SwTxtFmtColl *pColl = pDoc->GetTxtCollFromPool
495             (RES_POOLCOLL_STANDARD, false );
496         SwStartNode* pRedlineNode = pDoc->GetNodes().MakeTextSection(
497             pDoc->GetNodes().GetEndOfRedlines(),
498             SwNormalStartNode,
499             pColl);
500 
501         // remember node-index in RedlineInfo
502         SwNodeIndex aIndex(*pRedlineNode);
503         aFind->second->pContentIndex = new SwNodeIndex(aIndex);
504 
505         // create XText for document
506         SwXText* pXText = new SwXRedlineText(pDoc, aIndex);
507         Reference<XText> xText = pXText;  // keep Reference until end of method
508 
509         // create (UNO-) cursor
510         SwPosition aPos(*pRedlineNode);
511         SwXTextCursor *const pXCursor =
512             new SwXTextCursor(*pDoc, pXText, CURSOR_REDLINE, aPos);
513         pXCursor->GetCursor()->Move(fnMoveForward, fnGoNode);
514         // cast to avoid ambiguity
515         xReturn = static_cast<text::XWordCursor*>(pXCursor);
516     }
517     // else: unknown redline -> Ignore
518 
519     return xReturn;
520 }
521 
SetCursor(const OUString & rId,sal_Bool bStart,Reference<XTextRange> & rRange,sal_Bool bIsOutsideOfParagraph)522 void XMLRedlineImportHelper::SetCursor(
523     const OUString& rId,
524     sal_Bool bStart,
525     Reference<XTextRange> & rRange,
526     sal_Bool bIsOutsideOfParagraph)
527 {
528     RedlineMapType::iterator aFind = aRedlineMap.find(rId);
529     if (aRedlineMap.end() != aFind)
530     {
531         // RedlineInfo found; now set Cursor
532         RedlineInfo* pInfo = aFind->second;
533         if (bIsOutsideOfParagraph)
534         {
535             // outside of paragraph: remember SwNodeIndex
536             if (bStart)
537             {
538                 pInfo->aAnchorStart.SetAsNodeIndex(rRange);
539             }
540             else
541             {
542                 pInfo->aAnchorEnd.SetAsNodeIndex(rRange);
543             }
544 
545             // also remember that we expect an adjustment for this redline
546             pInfo->bNeedsAdjustment = sal_True;
547         }
548         else
549         {
550             // inside of a paragraph: use regular XTextRanges (bookmarks)
551             if (bStart)
552                 pInfo->aAnchorStart.Set(rRange);
553             else
554                 pInfo->aAnchorEnd.Set(rRange);
555         }
556 
557         // if this Cursor was the last missing info, we insert the
558         // node into the document
559         // then we can remove the entry from the map and destroy the object
560         if (IsReady(pInfo))
561         {
562             InsertIntoDocument(pInfo);
563             aRedlineMap.erase(rId);
564             delete pInfo;
565         }
566     }
567     // else: unknown Id -> ignore
568 }
569 
AdjustStartNodeCursor(const OUString & rId,sal_Bool,Reference<XTextRange> &)570 void XMLRedlineImportHelper::AdjustStartNodeCursor(
571     const OUString& rId,        /// ID used in RedlineAdd() call
572     sal_Bool /*bStart*/,
573     Reference<XTextRange> & /*rRange*/)
574 {
575     // this method will modify the document directly -> lock SolarMutex
576     vos::OGuard aGuard(Application::GetSolarMutex());
577 
578     // start + end nodes are treated the same. For either it's
579     // necessary that the target node already exists.
580 
581     RedlineMapType::iterator aFind = aRedlineMap.find(rId);
582     if (aRedlineMap.end() != aFind)
583     {
584         // RedlineInfo found; now set Cursor
585         RedlineInfo* pInfo = aFind->second;
586 
587         pInfo->bNeedsAdjustment = sal_False;
588 
589         // if now ready, insert into document
590         if( IsReady(pInfo) )
591         {
592             InsertIntoDocument(pInfo);
593             aRedlineMap.erase(rId);
594             delete pInfo;
595         }
596     }
597     // else: can't find redline -> ignore
598 }
599 
600 
IsReady(RedlineInfo * pRedline)601 inline sal_Bool XMLRedlineImportHelper::IsReady(RedlineInfo* pRedline)
602 {
603     // we can insert a redline if we have start & end, and we don't
604     // expect adjustments for either of these
605     return ( pRedline->aAnchorEnd.IsValid() &&
606              pRedline->aAnchorStart.IsValid() &&
607              !pRedline->bNeedsAdjustment );
608 }
609 
InsertIntoDocument(RedlineInfo * pRedlineInfo)610 void XMLRedlineImportHelper::InsertIntoDocument(RedlineInfo* pRedlineInfo)
611 {
612     DBG_ASSERT(NULL != pRedlineInfo, "need redline info");
613     DBG_ASSERT(IsReady(pRedlineInfo), "redline info not complete yet!");
614 
615     // this method will modify the document directly -> lock SolarMutex
616     vos::OGuard aGuard(Application::GetSolarMutex());
617 
618     // Insert the Redline as described by pRedlineInfo into the
619     // document.  If we are in insert mode, don't insert any redlines
620     // (and delete 'deleted' inline redlines)
621 
622     // get the document (from one of the positions)
623     SwDoc* pDoc = pRedlineInfo->aAnchorStart.GetDoc();
624 
625     if (!pDoc)
626     {
627         OSL_TRACE("XMLRedlineImportHelper::InsertIntoDocument: "
628                 "no SwDoc => cannot insert redline.");
629         return;
630     }
631 
632     // now create the PaM for the redline
633     SwPaM aPaM(pDoc->GetNodes().GetEndOfContent());
634     pRedlineInfo->aAnchorStart.CopyPositionInto(*aPaM.GetPoint(), *pDoc);
635     aPaM.SetMark();
636     pRedlineInfo->aAnchorEnd.CopyPositionInto(*aPaM.GetPoint(), *pDoc);
637 
638     // collapse PaM if (start == end)
639     if (*aPaM.GetPoint() == *aPaM.GetMark())
640     {
641         aPaM.DeleteMark();
642     }
643 
644 
645     // cover three cases:
646     // 1) empty redlines (no range, no content) #100921#
647     // 2) check for:
648     //    a) bIgnoreRedline (e.g. insert mode)
649     //    b) illegal PaM range (CheckNodesRange())
650     // 3) normal case: insert redline
651     if( !aPaM.HasMark() && (pRedlineInfo->pContentIndex == NULL) )
652     {
653         // these redlines have no function, and will thus be ignored (just as
654         // in sw3io), so no action here
655     }
656     else if ( bIgnoreRedlines ||
657          !CheckNodesRange( aPaM.GetPoint()->nNode,
658                            aPaM.GetMark()->nNode,
659                            sal_True ) )
660     {
661         // ignore redline (e.g. file loaded in insert mode):
662         // delete 'deleted' redlines and forget about the whole thing
663         if (nsRedlineType_t::REDLINE_DELETE == pRedlineInfo->eType)
664         {
665             pDoc->DeleteRange(aPaM);
666             // And what about the "deleted nodes"?
667             // They have to be deleted as well (#i80689)!
668             if( bIgnoreRedlines && pRedlineInfo->pContentIndex != NULL )
669             {
670                 SwNodeIndex aIdx( *pRedlineInfo->pContentIndex );
671                 const SwNode* pEnd = aIdx.GetNode().EndOfSectionNode();
672                 if( pEnd )
673                 {
674                     SwNodeIndex aEnd( *pEnd, 1 );
675                     SwPaM aDel( aIdx, aEnd );
676                     pDoc->DeleteRange(aDel);
677                 }
678             }
679         }
680     }
681     else
682     {
683         // regular file loading: insert redline
684 
685         // create redline (using pRedlineData which gets copied in SwRedline())
686         SwRedlineData* pRedlineData = ConvertRedline(pRedlineInfo, pDoc);
687         SwRedline* pRedline =
688             new SwRedline( pRedlineData, *aPaM.GetPoint(), sal_True,
689                            !pRedlineInfo->bMergeLastParagraph, sal_False );
690 
691         // set mark
692         if( aPaM.HasMark() )
693         {
694             pRedline->SetMark();
695             *(pRedline->GetMark()) = *aPaM.GetMark();
696         }
697 
698         // set content node (if necessary)
699         if (NULL != pRedlineInfo->pContentIndex)
700         {
701             sal_uLong nPoint = aPaM.GetPoint()->nNode.GetIndex();
702             if( nPoint < pRedlineInfo->pContentIndex->GetIndex() ||
703                 nPoint > pRedlineInfo->pContentIndex->GetNode().EndOfSectionIndex() )
704                 pRedline->SetContentIdx(pRedlineInfo->pContentIndex);
705 #ifdef DBG_UTIL
706             else
707                 ASSERT( false, "Recursive change tracking" );
708 #endif
709         }
710 
711         // set redline mode (without doing the associated book-keeping)
712         pDoc->SetRedlineMode_intern(nsRedlineMode_t::REDLINE_ON);
713         pDoc->AppendRedline(pRedline, false);
714         pDoc->SetRedlineMode_intern(nsRedlineMode_t::REDLINE_NONE);
715     }
716 }
717 
ConvertRedline(RedlineInfo * pRedlineInfo,SwDoc * pDoc)718 SwRedlineData* XMLRedlineImportHelper::ConvertRedline(
719     RedlineInfo* pRedlineInfo,
720     SwDoc* pDoc)
721 {
722     // convert info:
723     // 1) Author String -> Author ID (default to zero)
724     sal_uInt16 nAuthorId = (NULL == pDoc) ? 0 :
725         pDoc->InsertRedlineAuthor( pRedlineInfo->sAuthor );
726 
727     // 2) util::DateTime -> DateTime
728     DateTime aDT;
729     aDT.SetYear(    pRedlineInfo->aDateTime.Year );
730     aDT.SetMonth(   pRedlineInfo->aDateTime.Month );
731     aDT.SetDay(     pRedlineInfo->aDateTime.Day );
732     aDT.SetHour(    pRedlineInfo->aDateTime.Hours );
733     aDT.SetMin(     pRedlineInfo->aDateTime.Minutes );
734     aDT.SetSec(     pRedlineInfo->aDateTime.Seconds );
735     aDT.Set100Sec(  pRedlineInfo->aDateTime.HundredthSeconds );
736 
737     // 3) recursively convert next redline
738     //    ( check presence and sanity of hierarchical redline info )
739     SwRedlineData* pNext = NULL;
740     if ( (NULL != pRedlineInfo->pNextRedline) &&
741          (nsRedlineType_t::REDLINE_DELETE == pRedlineInfo->eType) &&
742          (nsRedlineType_t::REDLINE_INSERT == pRedlineInfo->pNextRedline->eType) )
743     {
744         pNext = ConvertRedline(pRedlineInfo->pNextRedline, pDoc);
745     }
746 
747     // create redline data
748     SwRedlineData* pData = new SwRedlineData(pRedlineInfo->eType,
749                                              nAuthorId, aDT,
750                                              pRedlineInfo->sComment,
751                                              pNext, // next data (if available)
752                                              NULL); // no extra data
753 
754     return pData;
755 }
756 
757 
SetShowChanges(sal_Bool bShow)758 void XMLRedlineImportHelper::SetShowChanges( sal_Bool bShow )
759 {
760     bShowChanges = bShow;
761 }
762 
SetRecordChanges(sal_Bool bRecord)763 void XMLRedlineImportHelper::SetRecordChanges( sal_Bool bRecord )
764 {
765     bRecordChanges = bRecord;
766 }
767 
SetProtectionKey(const Sequence<sal_Int8> & rKey)768 void XMLRedlineImportHelper::SetProtectionKey(
769     const Sequence<sal_Int8> & rKey )
770 {
771     aProtectionKey = rKey;
772 }
773