1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
10 #include <sal/config.h>
14 #include <revisionfragment.hxx>
15 #include <oox/core/relations.hxx>
16 #include <oox/core/xmlfilterbase.hxx>
17 #include <oox/core/fastparser.hxx>
18 #include <oox/helper/attributelist.hxx>
19 #include <oox/token/namespaces.hxx>
20 #include <oox/token/tokens.hxx>
21 #include <svl/sharedstringpool.hxx>
22 #include <sax/tools/converter.hxx>
23 #include <editeng/editobj.hxx>
25 #include <chgtrack.hxx>
26 #include <document.hxx>
27 #include <compiler.hxx>
28 #include <editutil.hxx>
29 #include <formulacell.hxx>
30 #include <chgviset.hxx>
31 #include <richstringcontext.hxx>
32 #include <tokenarray.hxx>
34 #include <com/sun/star/util/DateTime.hpp>
36 using namespace com::sun::star
;
38 namespace oox
{ namespace xls
{
50 * For nc (new cell) or oc (old cell) elements under rcc (cell content
53 class RCCCellValueContext
: public WorkbookContextBase
55 sal_Int32
const mnSheetIndex
;
57 ScCellValue
& mrCellValue
;
60 RichStringRef mxRichString
;
64 RevisionLogFragment
& rParent
, sal_Int32 nSheetIndex
, ScAddress
& rPos
, ScCellValue
& rCellValue
) :
65 WorkbookContextBase(rParent
),
66 mnSheetIndex(nSheetIndex
),
68 mrCellValue(rCellValue
),
72 virtual oox::core::ContextHandlerRef
onCreateContext(
73 sal_Int32 nElement
, const AttributeList
& /*rAttribs*/ ) override
75 if (nElement
== XLS_TOKEN(is
))
77 mxRichString
.reset(new RichString(*this));
78 return new RichStringContext(*this, mxRichString
);
84 virtual void onStartElement( const AttributeList
& rAttribs
) override
86 switch (getCurrentElement())
97 virtual void onCharacters( const OUString
& rChars
) override
99 switch (getCurrentElement())
103 if (mnType
== XML_n
|| mnType
== XML_b
)
104 mrCellValue
.set(rChars
.toDouble());
109 if (mnType
== XML_inlineStr
)
111 ScDocument
& rDoc
= getScDocument();
112 svl::SharedStringPool
& rPool
= rDoc
.GetSharedStringPool();
113 mrCellValue
.set(rPool
.intern(rChars
));
120 ScDocument
& rDoc
= getScDocument();
121 ScCompiler
aComp(&rDoc
, mrPos
, formula::FormulaGrammar::GRAM_OOXML
);
122 std::unique_ptr
<ScTokenArray
> pArray
= aComp
.CompileString(rChars
);
126 mrCellValue
.set(new ScFormulaCell(&rDoc
, mrPos
, std::move(pArray
)));
134 virtual void onEndElement() override
136 switch (getCurrentElement())
141 if (mrCellValue
.isEmpty() && mxRichString
)
143 // The value is a rich text string.
144 ScDocument
& rDoc
= getScDocument();
145 std::unique_ptr
<EditTextObject
> pTextObj
= mxRichString
->convert(rDoc
.GetEditEngine(), nullptr);
148 svl::SharedStringPool
& rPool
= rDoc
.GetSharedStringPool();
149 pTextObj
->NormalizeString(rPool
);
150 mrCellValue
.set(pTextObj
.release());
161 void importCell( const AttributeList
& rAttribs
)
163 mnType
= rAttribs
.getToken(XML_t
, XML_n
);
164 OUString aRefStr
= rAttribs
.getString(XML_r
, OUString());
165 if (!aRefStr
.isEmpty())
167 mrPos
.Parse(aRefStr
, &getScDocument(), formula::FormulaGrammar::CONV_XL_OOX
);
168 if (mnSheetIndex
!= -1)
169 mrPos
.SetTab(mnSheetIndex
-1);
174 struct RevisionMetadata
179 RevisionMetadata() : maDateTime(DateTime::EMPTY
) {}
180 RevisionMetadata( const RevisionMetadata
& r
) :
181 maUserName(r
.maUserName
), maDateTime(r
.maDateTime
) {}
186 typedef std::map
<OUString
, RevisionMetadata
> RevDataType
;
188 struct RevisionHeadersFragment::Impl
190 RevDataType maRevData
;
195 RevisionHeadersFragment::RevisionHeadersFragment(
196 const WorkbookHelper
& rHelper
, const OUString
& rFragmentPath
) :
197 WorkbookFragmentBase(rHelper
, rFragmentPath
),
200 RevisionHeadersFragment::~RevisionHeadersFragment()
204 oox::core::ContextHandlerRef
RevisionHeadersFragment::onCreateContext(
205 sal_Int32
/*nElement*/, const AttributeList
& /*rAttribs*/ )
210 void RevisionHeadersFragment::onStartElement( const AttributeList
& rAttribs
)
212 switch (getCurrentElement())
214 case XLS_TOKEN(headers
):
216 case XLS_TOKEN(header
):
217 importHeader(rAttribs
);
219 case XLS_TOKEN(sheetIdMap
):
221 case XLS_TOKEN(sheetId
):
228 void RevisionHeadersFragment::onCharacters( const OUString
& /*rChars*/ ) {}
230 void RevisionHeadersFragment::onEndElement()
232 switch (getCurrentElement())
234 case XLS_TOKEN(headers
):
236 case XLS_TOKEN(header
):
238 case XLS_TOKEN(sheetIdMap
):
240 case XLS_TOKEN(sheetId
):
247 void RevisionHeadersFragment::finalizeImport()
249 ScDocument
& rDoc
= getScDocument();
250 std::unique_ptr
<ScChangeTrack
> pCT(new ScChangeTrack(&rDoc
));
251 OUString aSelfUser
= pCT
->GetUser(); // owner of this document.
252 pCT
->SetUseFixDateTime(true);
254 const oox::core::Relations
& rRels
= getRelations();
255 for (const auto& [rRelId
, rData
] : mpImpl
->maRevData
)
257 OUString aPath
= rRels
.getFragmentPathFromRelId(rRelId
);
261 // Parse each revision log fragment.
262 pCT
->SetUser(rData
.maUserName
);
263 pCT
->SetFixDateTimeLocal(rData
.maDateTime
);
264 std::unique_ptr
<oox::core::FastParser
> xParser(oox::core::XmlFilterBase::createParser());
265 rtl::Reference
<oox::core::FragmentHandler
> xFragment(new RevisionLogFragment(*this, aPath
, *pCT
));
266 importOoxFragment(xFragment
, *xParser
);
269 pCT
->SetUser(aSelfUser
); // set the default user to the document owner.
270 pCT
->SetUseFixDateTime(false);
271 rDoc
.SetChangeTrack(std::move(pCT
));
273 // Turn on visibility of tracked changes.
274 ScChangeViewSettings aSettings
;
275 aSettings
.SetShowChanges(true);
276 rDoc
.SetChangeViewSettings(aSettings
);
279 void RevisionHeadersFragment::importHeader( const AttributeList
& rAttribs
)
281 OUString aRId
= rAttribs
.getString(R_TOKEN(id
), OUString());
283 // All bets are off if we don't have a relation ID.
286 RevisionMetadata aMetadata
;
287 OUString aDateTimeStr
= rAttribs
.getString(XML_dateTime
, OUString());
288 if (!aDateTimeStr
.isEmpty())
290 util::DateTime aDateTime
;
291 sax::Converter::parseDateTime(aDateTime
, aDateTimeStr
);
292 Date
aDate(aDateTime
);
293 tools::Time
aTime(aDateTime
);
294 aMetadata
.maDateTime
.SetDate(aDate
.GetDate());
295 aMetadata
.maDateTime
.SetTime(aTime
.GetTime());
298 aMetadata
.maUserName
= rAttribs
.getString(XML_userName
, OUString());
300 mpImpl
->maRevData
.emplace(aRId
, aMetadata
);
303 struct RevisionLogFragment::Impl
305 ScChangeTrack
& mrChangeTrack
;
307 sal_Int32 mnSheetIndex
;
312 ScAddress maOldCellPos
;
313 ScAddress maNewCellPos
;
314 ScCellValue maOldCellValue
;
315 ScCellValue maNewCellValue
;
322 explicit Impl( ScChangeTrack
& rChangeTrack
) :
323 mrChangeTrack(rChangeTrack
),
326 mbEndOfList(false) {}
329 RevisionLogFragment::RevisionLogFragment(
330 const WorkbookHelper
& rHelper
, const OUString
& rFragmentPath
, ScChangeTrack
& rChangeTrack
) :
331 WorkbookFragmentBase(rHelper
, rFragmentPath
),
332 mpImpl(new Impl(rChangeTrack
)) {}
334 RevisionLogFragment::~RevisionLogFragment()
338 oox::core::ContextHandlerRef
RevisionLogFragment::onCreateContext(
339 sal_Int32 nElement
, const AttributeList
& /*rAttribs*/ )
344 return new RCCCellValueContext(*this, mpImpl
->mnSheetIndex
, mpImpl
->maNewCellPos
, mpImpl
->maNewCellValue
);
346 return new RCCCellValueContext(*this, mpImpl
->mnSheetIndex
, mpImpl
->maOldCellPos
, mpImpl
->maOldCellValue
);
353 void RevisionLogFragment::onStartElement( const AttributeList
& rAttribs
)
355 switch (getCurrentElement())
358 mpImpl
->maNewCellPos
.SetInvalid();
359 mpImpl
->maOldCellPos
.SetInvalid();
360 mpImpl
->maNewCellValue
.clear();
361 mpImpl
->maOldCellValue
.clear();
372 void RevisionLogFragment::onCharacters( const OUString
& /*rChars*/ ) {}
374 void RevisionLogFragment::onEndElement()
376 switch (getCurrentElement())
387 void RevisionLogFragment::finalizeImport() {}
389 void RevisionLogFragment::importCommon( const AttributeList
& rAttribs
)
391 mpImpl
->mnSheetIndex
= rAttribs
.getInteger(XML_sId
, -1);
394 void RevisionLogFragment::importRcc( const AttributeList
& rAttribs
)
396 importCommon(rAttribs
);
398 mpImpl
->meType
= REV_CELLCHANGE
;
401 void RevisionLogFragment::importRrc( const AttributeList
& rAttribs
)
403 importCommon(rAttribs
);
405 if (mpImpl
->mnSheetIndex
== -1)
406 // invalid sheet index, or sheet index not given.
409 mpImpl
->meType
= REV_UNKNOWN
;
410 sal_Int32 nAction
= rAttribs
.getToken(XML_action
, -1);
414 OUString aRefStr
= rAttribs
.getString(XML_ref
, OUString());
415 mpImpl
->maRange
.Parse(aRefStr
, &getScDocument(), formula::FormulaGrammar::CONV_XL_OOX
);
416 if (!mpImpl
->maRange
.IsValid())
422 mpImpl
->meType
= REV_INSERTROW
;
423 mpImpl
->maRange
.aEnd
.SetCol(getScDocument().MaxCol());
424 mpImpl
->maRange
.aStart
.SetTab(mpImpl
->mnSheetIndex
-1);
425 mpImpl
->maRange
.aEnd
.SetTab(mpImpl
->mnSheetIndex
-1);
428 // Unknown action type. Ignore it.
432 mpImpl
->mbEndOfList
= rAttribs
.getBool(XML_eol
, false);
435 void RevisionLogFragment::pushRevision()
437 switch (mpImpl
->meType
)
440 mpImpl
->mrChangeTrack
.AppendContentOnTheFly(
441 mpImpl
->maNewCellPos
, mpImpl
->maOldCellValue
, mpImpl
->maNewCellValue
);
444 mpImpl
->mrChangeTrack
.AppendInsert(mpImpl
->maRange
, mpImpl
->mbEndOfList
);
453 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */