fdo#74697 Add Bluez 5 support for impress remote.
[LibreOffice.git] / xmloff / source / text / XMLRedlineExport.cxx
blob6cc8f2d74c36682cde614e7cdb0f3cf7b98c4a15
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "XMLRedlineExport.hxx"
21 #include <tools/debug.hxx>
22 #include <rtl/ustring.hxx>
23 #include <rtl/ustrbuf.hxx>
24 #include <com/sun/star/beans/XPropertySet.hpp>
25 #include <com/sun/star/beans/UnknownPropertyException.hpp>
26 #include <com/sun/star/container/XEnumerationAccess.hpp>
28 #include <com/sun/star/container/XEnumeration.hpp>
29 #include <com/sun/star/document/XRedlinesSupplier.hpp>
30 #include <com/sun/star/text/XText.hpp>
31 #include <com/sun/star/text/XTextContent.hpp>
32 #include <com/sun/star/text/XTextSection.hpp>
33 #include <com/sun/star/util/DateTime.hpp>
35 #include <sax/tools/converter.hxx>
37 #include <xmloff/xmltoken.hxx>
38 #include "xmloff/xmlnmspe.hxx"
39 #include <xmloff/xmlexp.hxx>
40 #include <xmloff/xmluconv.hxx>
43 using namespace ::com::sun::star;
44 using namespace ::xmloff::token;
46 using ::com::sun::star::beans::PropertyValue;
47 using ::com::sun::star::beans::XPropertySet;
48 using ::com::sun::star::beans::UnknownPropertyException;
49 using ::com::sun::star::document::XRedlinesSupplier;
50 using ::com::sun::star::container::XEnumerationAccess;
51 using ::com::sun::star::container::XEnumeration;
52 using ::com::sun::star::text::XText;
53 using ::com::sun::star::text::XTextContent;
54 using ::com::sun::star::text::XTextSection;
55 using ::com::sun::star::uno::Any;
56 using ::com::sun::star::uno::Reference;
57 using ::com::sun::star::uno::Sequence;
58 using ::std::list;
61 XMLRedlineExport::XMLRedlineExport(SvXMLExport& rExp)
62 : sDelete("Delete")
63 , sDeletion(GetXMLToken(XML_DELETION))
64 , sFormat("Format")
65 , sFormatChange(GetXMLToken(XML_FORMAT_CHANGE))
66 , sInsert("Insert")
67 , sInsertion(GetXMLToken(XML_INSERTION))
68 , sIsCollapsed("IsCollapsed")
69 , sIsStart("IsStart")
70 , sRedlineAuthor("RedlineAuthor")
71 , sRedlineComment("RedlineComment")
72 , sRedlineDateTime("RedlineDateTime")
73 , sRedlineSuccessorData("RedlineSuccessorData")
74 , sRedlineText("RedlineText")
75 , sRedlineType("RedlineType")
76 , sStyle("Style")
77 , sTextTable("TextTable")
78 , sUnknownChange("UnknownChange")
79 , sStartRedline("StartRedline")
80 , sEndRedline("EndRedline")
81 , sRedlineIdentifier("RedlineIdentifier")
82 , sIsInHeaderFooter("IsInHeaderFooter")
83 , sRedlineProtectionKey("RedlineProtectionKey")
84 , sRecordChanges("RecordChanges")
85 , sMergeLastPara("MergeLastPara")
86 , sChangePrefix("ct")
87 , rExport(rExp)
88 , pCurrentChangesList(NULL)
93 XMLRedlineExport::~XMLRedlineExport()
95 // delete changes lists
96 for( ChangesMapType::iterator aIter = aChangeMap.begin();
97 aIter != aChangeMap.end();
98 ++aIter )
100 delete aIter->second;
102 aChangeMap.clear();
106 void XMLRedlineExport::ExportChange(
107 const Reference<XPropertySet> & rPropSet,
108 sal_Bool bAutoStyle)
110 if (bAutoStyle)
112 // For the headers/footers, we have to collect the autostyles
113 // here. For the general case, however, it's better to collet
114 // the autostyles by iterating over the global redline
115 // list. So that's what we do: Here, we collect autostyles
116 // only if we have no current list of changes. For the
117 // main-document case, the autostyles are collected in
118 // ExportChangesListAutoStyles().
119 if (pCurrentChangesList != NULL)
120 ExportChangeAutoStyle(rPropSet);
122 else
124 ExportChangeInline(rPropSet);
129 void XMLRedlineExport::ExportChangesList(sal_Bool bAutoStyles)
131 if (bAutoStyles)
133 ExportChangesListAutoStyles();
135 else
137 ExportChangesListElements();
142 void XMLRedlineExport::ExportChangesList(
143 const Reference<XText> & rText,
144 sal_Bool bAutoStyles)
146 // in the header/footer case, auto styles are collected from the
147 // inline change elements.
148 if (bAutoStyles)
149 return;
151 // look for changes list for this XText
152 ChangesMapType::iterator aFind = aChangeMap.find(rText);
153 if (aFind != aChangeMap.end())
155 ChangesListType* pChangesList = aFind->second;
157 // export only if changes are found
158 if (pChangesList->size() > 0)
160 // changes container element
161 SvXMLElementExport aChanges(rExport, XML_NAMESPACE_TEXT,
162 XML_TRACKED_CHANGES,
163 sal_True, sal_True);
165 // iterate over changes list
166 for( ChangesListType::iterator aIter = pChangesList->begin();
167 aIter != pChangesList->end();
168 ++aIter )
170 ExportChangedRegion( *aIter );
173 // else: changes list empty -> ignore
175 // else: no changes list found -> empty
178 void XMLRedlineExport::SetCurrentXText(
179 const Reference<XText> & rText)
181 if (rText.is())
183 // look for appropriate list in map; use the found one, or create new
184 ChangesMapType::iterator aIter = aChangeMap.find(rText);
185 if (aIter == aChangeMap.end())
187 ChangesListType* pList = new ChangesListType;
188 aChangeMap[rText] = pList;
189 pCurrentChangesList = pList;
191 else
192 pCurrentChangesList = aIter->second;
194 else
196 // don't record changes
197 SetCurrentXText();
201 void XMLRedlineExport::SetCurrentXText()
203 pCurrentChangesList = NULL;
207 void XMLRedlineExport::ExportChangesListElements()
209 // get redlines (aka tracked changes) from the model
210 Reference<XRedlinesSupplier> xSupplier(rExport.GetModel(), uno::UNO_QUERY);
211 if (xSupplier.is())
213 Reference<XEnumerationAccess> aEnumAccess = xSupplier->getRedlines();
215 // redline protection key
216 Reference<XPropertySet> aDocPropertySet( rExport.GetModel(),
217 uno::UNO_QUERY );
218 // redlining enabled?
219 sal_Bool bEnabled = *(sal_Bool*)aDocPropertySet->getPropertyValue(
220 sRecordChanges ).getValue();
222 // only export if we have redlines or attributes
223 if ( aEnumAccess->hasElements() || bEnabled )
226 // export only if we have changes, but tracking is not enabled
227 if ( !bEnabled != !aEnumAccess->hasElements() )
229 rExport.AddAttribute(
230 XML_NAMESPACE_TEXT, XML_TRACK_CHANGES,
231 bEnabled ? XML_TRUE : XML_FALSE );
234 // changes container element
235 SvXMLElementExport aChanges(rExport, XML_NAMESPACE_TEXT,
236 XML_TRACKED_CHANGES,
237 sal_True, sal_True);
239 // get enumeration and iterate over elements
240 Reference<XEnumeration> aEnum = aEnumAccess->createEnumeration();
241 while (aEnum->hasMoreElements())
243 Any aAny = aEnum->nextElement();
244 Reference<XPropertySet> xPropSet;
245 aAny >>= xPropSet;
247 DBG_ASSERT(xPropSet.is(),
248 "can't get XPropertySet; skipping Redline");
249 if (xPropSet.is())
251 // export only if not in header or footer
252 // (those must be exported with their XText)
253 aAny = xPropSet->getPropertyValue(sIsInHeaderFooter);
254 if (! *(sal_Bool*)aAny.getValue())
256 // and finally, export change
257 ExportChangedRegion(xPropSet);
260 // else: no XPropertySet -> no export
263 // else: no redlines -> no export
265 // else: no XRedlineSupplier -> no export
268 void XMLRedlineExport::ExportChangeAutoStyle(
269 const Reference<XPropertySet> & rPropSet)
271 // record change (if changes should be recorded)
272 if (NULL != pCurrentChangesList)
274 // put redline in list if it's collapsed or the redline start
275 Any aIsStart = rPropSet->getPropertyValue(sIsStart);
276 Any aIsCollapsed = rPropSet->getPropertyValue(sIsCollapsed);
278 if ( *(sal_Bool*)aIsStart.getValue() ||
279 *(sal_Bool*)aIsCollapsed.getValue() )
280 pCurrentChangesList->push_back(rPropSet);
283 // get XText for export of redline auto styles
284 Any aAny = rPropSet->getPropertyValue(sRedlineText);
285 Reference<XText> xText;
286 aAny >>= xText;
287 if (xText.is())
289 // export the auto styles
290 rExport.GetTextParagraphExport()->collectTextAutoStyles(xText);
294 void XMLRedlineExport::ExportChangesListAutoStyles()
296 // get redlines (aka tracked changes) from the model
297 Reference<XRedlinesSupplier> xSupplier(rExport.GetModel(), uno::UNO_QUERY);
298 if (xSupplier.is())
300 Reference<XEnumerationAccess> aEnumAccess = xSupplier->getRedlines();
302 // only export if we actually have redlines
303 if (aEnumAccess->hasElements())
305 // get enumeration and iterate over elements
306 Reference<XEnumeration> aEnum = aEnumAccess->createEnumeration();
307 while (aEnum->hasMoreElements())
309 Any aAny = aEnum->nextElement();
310 Reference<XPropertySet> xPropSet;
311 aAny >>= xPropSet;
313 DBG_ASSERT(xPropSet.is(),
314 "can't get XPropertySet; skipping Redline");
315 if (xPropSet.is())
318 // export only if not in header or footer
319 // (those must be exported with their XText)
320 aAny = xPropSet->getPropertyValue(sIsInHeaderFooter);
321 if (! *(sal_Bool*)aAny.getValue())
323 ExportChangeAutoStyle(xPropSet);
331 void XMLRedlineExport::ExportChangeInline(
332 const Reference<XPropertySet> & rPropSet)
334 // determine element name (depending on collapsed, start/end)
335 enum XMLTokenEnum eElement = XML_TOKEN_INVALID;
336 Any aAny = rPropSet->getPropertyValue(sIsCollapsed);
337 sal_Bool bCollapsed = *(sal_Bool *)aAny.getValue();
338 sal_Bool bStart = sal_True; // ignored if bCollapsed = sal_True
339 if (bCollapsed)
341 eElement = XML_CHANGE;
343 else
345 aAny = rPropSet->getPropertyValue(sIsStart);
346 bStart = *(sal_Bool *)aAny.getValue();
347 eElement = bStart ? XML_CHANGE_START : XML_CHANGE_END;
350 if (XML_TOKEN_INVALID != eElement)
352 // we always need the ID
353 rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_CHANGE_ID,
354 GetRedlineID(rPropSet));
356 // export the element (no whitespace because we're in the text body)
357 SvXMLElementExport aChangeElem(rExport, XML_NAMESPACE_TEXT,
358 eElement, sal_False, sal_False);
363 void XMLRedlineExport::ExportChangedRegion(
364 const Reference<XPropertySet> & rPropSet)
366 // Redline-ID
367 rExport.AddAttributeIdLegacy(XML_NAMESPACE_TEXT, GetRedlineID(rPropSet));
369 // merge-last-paragraph
370 Any aAny = rPropSet->getPropertyValue(sMergeLastPara);
371 if( ! *(sal_Bool*)aAny.getValue() )
372 rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_MERGE_LAST_PARAGRAPH,
373 XML_FALSE);
375 // export change region element
376 SvXMLElementExport aChangedRegion(rExport, XML_NAMESPACE_TEXT,
377 XML_CHANGED_REGION, sal_True, sal_True);
380 // scope for (first) change element
382 aAny = rPropSet->getPropertyValue(sRedlineType);
383 OUString sType;
384 aAny >>= sType;
385 SvXMLElementExport aChange(rExport, XML_NAMESPACE_TEXT,
386 ConvertTypeName(sType), sal_True, sal_True);
388 ExportChangeInfo(rPropSet);
390 // get XText from the redline and export (if the XText exists)
391 aAny = rPropSet->getPropertyValue(sRedlineText);
392 Reference<XText> xText;
393 aAny >>= xText;
394 if (xText.is())
396 rExport.GetTextParagraphExport()->exportText(xText);
397 // default parameters: bProgress, bExportParagraph ???
399 // else: no text interface -> content is inline and will
400 // be exported there
403 // changed change? Hierarchical changes can onl be two levels
404 // deep. Here we check for the second level.
405 aAny = rPropSet->getPropertyValue(sRedlineSuccessorData);
406 Sequence<PropertyValue> aSuccessorData;
407 aAny >>= aSuccessorData;
409 // if we actually got a hierarchical change, make element and
410 // process change info
411 if (aSuccessorData.getLength() > 0)
413 // The only change that can be "undone" is an insertion -
414 // after all, you can't re-insert an deletion, but you can
415 // delete an insertion. This assumption is asserted in
416 // ExportChangeInfo(Sequence<PropertyValue>&).
417 SvXMLElementExport aSecondChangeElem(
418 rExport, XML_NAMESPACE_TEXT, XML_INSERTION,
419 sal_True, sal_True);
421 ExportChangeInfo(aSuccessorData);
423 // else: no hierarchical change
427 const OUString XMLRedlineExport::ConvertTypeName(
428 const OUString& sApiName)
430 if (sApiName == sDelete)
432 return sDeletion;
434 else if (sApiName == sInsert)
436 return sInsertion;
438 else if (sApiName == sFormat)
440 return sFormatChange;
442 else
444 OSL_FAIL("unknown redline type");
445 return sUnknownChange;
450 /** Create a Redline-ID */
451 const OUString XMLRedlineExport::GetRedlineID(
452 const Reference<XPropertySet> & rPropSet)
454 Any aAny = rPropSet->getPropertyValue(sRedlineIdentifier);
455 OUString sTmp;
456 aAny >>= sTmp;
458 OUStringBuffer sBuf(sChangePrefix);
459 sBuf.append(sTmp);
460 return sBuf.makeStringAndClear();
464 void XMLRedlineExport::ExportChangeInfo(
465 const Reference<XPropertySet> & rPropSet)
468 SvXMLElementExport aChangeInfo(rExport, XML_NAMESPACE_OFFICE,
469 XML_CHANGE_INFO, sal_True, sal_True);
471 Any aAny = rPropSet->getPropertyValue(sRedlineAuthor);
472 OUString sTmp;
473 aAny >>= sTmp;
474 if (!sTmp.isEmpty())
476 SvXMLElementExport aCreatorElem( rExport, XML_NAMESPACE_DC,
477 XML_CREATOR, sal_True,
478 sal_False );
479 rExport.Characters(sTmp);
482 aAny = rPropSet->getPropertyValue(sRedlineDateTime);
483 util::DateTime aDateTime;
484 aAny >>= aDateTime;
486 OUStringBuffer sBuf;
487 ::sax::Converter::convertDateTime(sBuf, aDateTime);
488 SvXMLElementExport aDateElem( rExport, XML_NAMESPACE_DC,
489 XML_DATE, sal_True,
490 sal_False );
491 rExport.Characters(sBuf.makeStringAndClear());
494 // comment as <text:p> sequence
495 aAny = rPropSet->getPropertyValue(sRedlineComment);
496 aAny >>= sTmp;
497 WriteComment( sTmp );
500 void XMLRedlineExport::ExportChangeInfo(
501 const Sequence<PropertyValue> & rPropertyValues)
503 OUString sComment;
505 sal_Int32 nCount = rPropertyValues.getLength();
506 for(sal_Int32 i = 0; i < nCount; i++)
508 const PropertyValue& rVal = rPropertyValues[i];
510 if( rVal.Name.equals(sRedlineAuthor) )
512 OUString sTmp;
513 rVal.Value >>= sTmp;
514 if (!sTmp.isEmpty())
516 rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_CHG_AUTHOR, sTmp);
519 else if( rVal.Name.equals(sRedlineComment) )
521 rVal.Value >>= sComment;
523 else if( rVal.Name.equals(sRedlineDateTime) )
525 util::DateTime aDateTime;
526 rVal.Value >>= aDateTime;
527 OUStringBuffer sBuf;
528 ::sax::Converter::convertDateTime(sBuf, aDateTime);
529 rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_CHG_DATE_TIME,
530 sBuf.makeStringAndClear());
532 else if( rVal.Name.equals(sRedlineType) )
534 // check if this is an insertion; cf. comment at calling location
535 OUString sTmp;
536 rVal.Value >>= sTmp;
537 DBG_ASSERT(sTmp.equals(sInsert),
538 "hierarchical change must be insertion");
540 // else: unknown value -> ignore
543 // finally write element
544 SvXMLElementExport aChangeInfo(rExport, XML_NAMESPACE_OFFICE,
545 XML_CHANGE_INFO, sal_True, sal_True);
547 WriteComment( sComment );
550 void XMLRedlineExport::ExportStartOrEndRedline(
551 const Reference<XPropertySet> & rPropSet,
552 sal_Bool bStart)
554 if( ! rPropSet.is() )
555 return;
557 // get appropriate (start or end) property
558 Any aAny;
561 aAny = rPropSet->getPropertyValue(bStart ? sStartRedline : sEndRedline);
563 catch(const UnknownPropertyException&)
565 // If we don't have the property, there's nothing to do.
566 return;
569 Sequence<PropertyValue> aValues;
570 aAny >>= aValues;
571 const PropertyValue* pValues = aValues.getConstArray();
573 // seek for redline properties
574 sal_Bool bIsCollapsed = sal_False;
575 sal_Bool bIsStart = sal_True;
576 OUString sId;
577 sal_Bool bIdOK = sal_False; // have we seen an ID?
578 sal_Int32 nLength = aValues.getLength();
579 for(sal_Int32 i = 0; i < nLength; i++)
581 if (sRedlineIdentifier.equals(pValues[i].Name))
583 pValues[i].Value >>= sId;
584 bIdOK = sal_True;
586 else if (sIsCollapsed.equals(pValues[i].Name))
588 bIsCollapsed = *(sal_Bool*)pValues[i].Value.getValue();
590 else if (sIsStart.equals(pValues[i].Name))
592 bIsStart = *(sal_Bool*)pValues[i].Value.getValue();
596 if( bIdOK )
598 DBG_ASSERT( !sId.isEmpty(), "Redlines must have IDs" );
600 // TODO: use GetRedlineID or elimiate that function
601 OUStringBuffer sBuffer(sChangePrefix);
602 sBuffer.append(sId);
604 rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_CHANGE_ID,
605 sBuffer.makeStringAndClear());
607 // export the element
608 // (whitespace because we're not inside paragraphs)
609 SvXMLElementExport aChangeElem(
610 rExport, XML_NAMESPACE_TEXT,
611 bIsCollapsed ? XML_CHANGE :
612 ( bIsStart ? XML_CHANGE_START : XML_CHANGE_END ),
613 sal_True, sal_True);
617 void XMLRedlineExport::ExportStartOrEndRedline(
618 const Reference<XTextContent> & rContent,
619 sal_Bool bStart)
621 Reference<XPropertySet> xPropSet(rContent, uno::UNO_QUERY);
622 if (xPropSet.is())
624 ExportStartOrEndRedline(xPropSet, bStart);
626 else
628 OSL_FAIL("XPropertySet expected");
632 void XMLRedlineExport::ExportStartOrEndRedline(
633 const Reference<XTextSection> & rSection,
634 sal_Bool bStart)
636 Reference<XPropertySet> xPropSet(rSection, uno::UNO_QUERY);
637 if (xPropSet.is())
639 ExportStartOrEndRedline(xPropSet, bStart);
641 else
643 OSL_FAIL("XPropertySet expected");
647 void XMLRedlineExport::WriteComment(const OUString& rComment)
649 if (!rComment.isEmpty())
651 // iterate over all string-pieces separated by return (0x0a) and
652 // put each inside a paragraph element.
653 SvXMLTokenEnumerator aEnumerator(rComment, sal_Char(0x0a));
654 OUString aSubString;
655 while (aEnumerator.getNextToken(aSubString))
657 SvXMLElementExport aParagraph(
658 rExport, XML_NAMESPACE_TEXT, XML_P, sal_True, sal_False);
659 rExport.Characters(aSubString);
664 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */