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