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
;
50 * For nc (new cell) or oc (old cell) elements under rcc (cell content
53 class RCCCellValueContext
: public WorkbookContextBase
55 sal_Int32 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
= std::make_shared
<RichString
>();
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(std::move(pTextObj
));
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 struct RevisionHeadersFragment::Impl
188 std::map
<OUString
, RevisionMetadata
> maRevData
;
193 RevisionHeadersFragment::RevisionHeadersFragment(
194 const WorkbookHelper
& rHelper
, const OUString
& rFragmentPath
) :
195 WorkbookFragmentBase(rHelper
, rFragmentPath
),
198 RevisionHeadersFragment::~RevisionHeadersFragment()
202 oox::core::ContextHandlerRef
RevisionHeadersFragment::onCreateContext(
203 sal_Int32
/*nElement*/, const AttributeList
& /*rAttribs*/ )
208 void RevisionHeadersFragment::onStartElement( const AttributeList
& rAttribs
)
210 switch (getCurrentElement())
212 case XLS_TOKEN(headers
):
214 case XLS_TOKEN(header
):
215 importHeader(rAttribs
);
217 case XLS_TOKEN(sheetIdMap
):
219 case XLS_TOKEN(sheetId
):
226 void RevisionHeadersFragment::onCharacters( const OUString
& /*rChars*/ ) {}
228 void RevisionHeadersFragment::onEndElement()
230 switch (getCurrentElement())
232 case XLS_TOKEN(headers
):
234 case XLS_TOKEN(header
):
236 case XLS_TOKEN(sheetIdMap
):
238 case XLS_TOKEN(sheetId
):
245 void RevisionHeadersFragment::finalizeImport()
247 ScDocument
& rDoc
= getScDocument();
248 std::unique_ptr
<ScChangeTrack
> pCT(new ScChangeTrack(rDoc
));
249 OUString aSelfUser
= pCT
->GetUser(); // owner of this document.
250 pCT
->SetUseFixDateTime(true);
252 const oox::core::Relations
& rRels
= getRelations();
253 for (const auto& [rRelId
, rData
] : mpImpl
->maRevData
)
255 OUString aPath
= rRels
.getFragmentPathFromRelId(rRelId
);
259 // Parse each revision log fragment.
260 pCT
->SetUser(rData
.maUserName
);
261 pCT
->SetFixDateTimeLocal(rData
.maDateTime
);
262 std::unique_ptr
<oox::core::FastParser
> xParser(oox::core::XmlFilterBase::createParser());
263 rtl::Reference
<oox::core::FragmentHandler
> xFragment(new RevisionLogFragment(*this, aPath
, *pCT
));
264 importOoxFragment(xFragment
, *xParser
);
267 pCT
->SetUser(aSelfUser
); // set the default user to the document owner.
268 pCT
->SetUseFixDateTime(false);
269 rDoc
.SetChangeTrack(std::move(pCT
));
271 // Turn on visibility of tracked changes.
272 ScChangeViewSettings aSettings
;
273 aSettings
.SetShowChanges(true);
274 rDoc
.SetChangeViewSettings(aSettings
);
277 void RevisionHeadersFragment::importHeader( const AttributeList
& rAttribs
)
279 OUString aRId
= rAttribs
.getString(R_TOKEN(id
), OUString());
281 // All bets are off if we don't have a relation ID.
284 RevisionMetadata aMetadata
;
285 OUString aDateTimeStr
= rAttribs
.getString(XML_dateTime
, OUString());
286 if (!aDateTimeStr
.isEmpty())
288 util::DateTime aDateTime
;
289 if (sax::Converter::parseDateTime(aDateTime
, aDateTimeStr
))
290 aMetadata
.maDateTime
= aDateTime
;
292 SAL_WARN("sc.filter", "RevisionHeadersFragment: broken DateTime '" << aDateTimeStr
<< "'");
295 aMetadata
.maUserName
= rAttribs
.getString(XML_userName
, OUString());
297 mpImpl
->maRevData
.emplace(aRId
, aMetadata
);
300 struct RevisionLogFragment::Impl
302 ScChangeTrack
& mrChangeTrack
;
304 sal_Int32 mnSheetIndex
;
309 ScAddress maOldCellPos
;
310 ScAddress maNewCellPos
;
311 ScCellValue maOldCellValue
;
312 ScCellValue maNewCellValue
;
319 explicit Impl( ScChangeTrack
& rChangeTrack
) :
320 mrChangeTrack(rChangeTrack
),
323 mbEndOfList(false) {}
326 RevisionLogFragment::RevisionLogFragment(
327 const WorkbookHelper
& rHelper
, const OUString
& rFragmentPath
, ScChangeTrack
& rChangeTrack
) :
328 WorkbookFragmentBase(rHelper
, rFragmentPath
),
329 mpImpl(new Impl(rChangeTrack
)) {}
331 RevisionLogFragment::~RevisionLogFragment()
335 oox::core::ContextHandlerRef
RevisionLogFragment::onCreateContext(
336 sal_Int32 nElement
, const AttributeList
& /*rAttribs*/ )
341 return new RCCCellValueContext(*this, mpImpl
->mnSheetIndex
, mpImpl
->maNewCellPos
, mpImpl
->maNewCellValue
);
343 return new RCCCellValueContext(*this, mpImpl
->mnSheetIndex
, mpImpl
->maOldCellPos
, mpImpl
->maOldCellValue
);
350 void RevisionLogFragment::onStartElement( const AttributeList
& rAttribs
)
352 switch (getCurrentElement())
355 mpImpl
->maNewCellPos
.SetInvalid();
356 mpImpl
->maOldCellPos
.SetInvalid();
357 mpImpl
->maNewCellValue
.clear();
358 mpImpl
->maOldCellValue
.clear();
369 void RevisionLogFragment::onCharacters( const OUString
& /*rChars*/ ) {}
371 void RevisionLogFragment::onEndElement()
373 switch (getCurrentElement())
384 void RevisionLogFragment::finalizeImport() {}
386 void RevisionLogFragment::importCommon( const AttributeList
& rAttribs
)
388 mpImpl
->mnSheetIndex
= rAttribs
.getInteger(XML_sId
, -1);
391 void RevisionLogFragment::importRcc( const AttributeList
& rAttribs
)
393 importCommon(rAttribs
);
395 mpImpl
->meType
= REV_CELLCHANGE
;
398 void RevisionLogFragment::importRrc( const AttributeList
& rAttribs
)
400 importCommon(rAttribs
);
402 if (mpImpl
->mnSheetIndex
== -1)
403 // invalid sheet index, or sheet index not given.
406 mpImpl
->meType
= REV_UNKNOWN
;
407 sal_Int32 nAction
= rAttribs
.getToken(XML_action
, -1);
411 OUString aRefStr
= rAttribs
.getString(XML_ref
, OUString());
412 mpImpl
->maRange
.Parse(aRefStr
, getScDocument(), formula::FormulaGrammar::CONV_XL_OOX
);
413 if (!mpImpl
->maRange
.IsValid())
419 mpImpl
->meType
= REV_INSERTROW
;
420 mpImpl
->maRange
.aEnd
.SetCol(getScDocument().MaxCol());
421 mpImpl
->maRange
.aStart
.SetTab(mpImpl
->mnSheetIndex
-1);
422 mpImpl
->maRange
.aEnd
.SetTab(mpImpl
->mnSheetIndex
-1);
425 // Unknown action type. Ignore it.
429 mpImpl
->mbEndOfList
= rAttribs
.getBool(XML_eol
, false);
432 void RevisionLogFragment::pushRevision()
434 switch (mpImpl
->meType
)
437 mpImpl
->mrChangeTrack
.AppendContentOnTheFly(
438 mpImpl
->maNewCellPos
, mpImpl
->maOldCellValue
, mpImpl
->maNewCellValue
);
441 mpImpl
->mrChangeTrack
.AppendInsert(mpImpl
->maRange
, mpImpl
->mbEndOfList
);
450 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */