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