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