From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- sc/source/filter/xcl97/XclExpChangeTrack.cxx | 1647 ++++++++++++++++++++++++++ 1 file changed, 1647 insertions(+) create mode 100644 sc/source/filter/xcl97/XclExpChangeTrack.cxx (limited to 'sc/source/filter/xcl97/XclExpChangeTrack.cxx') diff --git a/sc/source/filter/xcl97/XclExpChangeTrack.cxx b/sc/source/filter/xcl97/XclExpChangeTrack.cxx new file mode 100644 index 000000000..2ef1b5bce --- /dev/null +++ b/sc/source/filter/xcl97/XclExpChangeTrack.cxx @@ -0,0 +1,1647 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace oox; + +static OString lcl_DateTimeToOString( const DateTime& rDateTime ) +{ + char sBuf[ 200 ]; + snprintf( sBuf, sizeof( sBuf ), + "%d-%02d-%02dT%02d:%02d:%02d.%09" SAL_PRIuUINT32 "Z", + rDateTime.GetYear(), rDateTime.GetMonth(), rDateTime.GetDay(), + rDateTime.GetHour(), rDateTime.GetMin(), rDateTime.GetSec(), + rDateTime.GetNanoSec() ); + return OString(sBuf); +} + +// local functions + +static void lcl_WriteDateTime( XclExpStream& rStrm, const DateTime& rDateTime ) +{ + rStrm.SetSliceSize( 7 ); + rStrm << static_cast(rDateTime.GetYear()) + << static_cast(rDateTime.GetMonth()) + << static_cast(rDateTime.GetDay()) + << static_cast(rDateTime.GetHour()) + << static_cast(rDateTime.GetMin()) + << static_cast(rDateTime.GetSec()); + rStrm.SetSliceSize( 0 ); +} + +// write string and fill rest of with zero bytes +// is without string header +static void lcl_WriteFixedString( XclExpStream& rStrm, const XclExpString& rString, std::size_t nLength ) +{ + std::size_t nStrBytes = rString.GetBufferSize(); + OSL_ENSURE( nLength >= nStrBytes, "lcl_WriteFixedString - String too long" ); + if( rString.Len() > 0 ) + rStrm << rString; + if( nLength > nStrBytes ) + rStrm.WriteZeroBytes( nLength - nStrBytes ); +} + +static void lcl_GenerateGUID( sal_uInt8* pGUID, bool& rValidGUID ) +{ + rtl_createUuid( pGUID, rValidGUID ? pGUID : nullptr, false ); + rValidGUID = true; +} + +static void lcl_WriteGUID( XclExpStream& rStrm, const sal_uInt8* pGUID ) +{ + rStrm.SetSliceSize( 16 ); + for( std::size_t nIndex = 0; nIndex < 16; nIndex++ ) + rStrm << pGUID[ nIndex ]; + rStrm.SetSliceSize( 0 ); +} + +XclExpUserBView::XclExpUserBView( const OUString& rUsername, const sal_uInt8* pGUID ) : + sUsername( rUsername ) +{ + memcpy( aGUID, pGUID, 16 ); +} + +void XclExpUserBView::SaveCont( XclExpStream& rStrm ) +{ + rStrm << sal_uInt32(0xFF078014) + << sal_uInt32(0x00000001); + lcl_WriteGUID( rStrm, aGUID ); + rStrm.WriteZeroBytes( 8 ); + rStrm << sal_uInt32(1200) + << sal_uInt32(1000) + << sal_uInt16(1000) + << sal_uInt16(0x0CF7) + << sal_uInt16(0x0000) + << sal_uInt16(0x0001) + << sal_uInt16(0x0000); + if( sUsername.Len() > 0 ) + rStrm << sUsername; +} + +sal_uInt16 XclExpUserBView::GetNum() const +{ + return 0x01A9; +} + +std::size_t XclExpUserBView::GetLen() const +{ + return 50 + ((sUsername.Len() > 0) ? sUsername.GetSize() : 0); +} + +XclExpUserBViewList::XclExpUserBViewList( const ScChangeTrack& rChangeTrack ) +{ + sal_uInt8 aGUID[ 16 ]; + bool bValidGUID = false; + const std::set& rStrColl = rChangeTrack.GetUserCollection(); + aViews.reserve(rStrColl.size()); + for (const auto& rStr : rStrColl) + { + lcl_GenerateGUID( aGUID, bValidGUID ); + aViews.emplace_back( rStr, aGUID ); + } +} + +XclExpUserBViewList::~XclExpUserBViewList() +{ +} + +void XclExpUserBViewList::Save( XclExpStream& rStrm ) +{ + for( XclExpUserBView& rView : aViews ) + rView.Save( rStrm ); +} + +XclExpUsersViewBegin::XclExpUsersViewBegin( const sal_uInt8* pGUID, sal_uInt32 nTab ) : + nCurrTab( nTab ) +{ + memcpy( aGUID, pGUID, 16 ); +} + +void XclExpUsersViewBegin::SaveCont( XclExpStream& rStrm ) +{ + lcl_WriteGUID( rStrm, aGUID ); + rStrm << nCurrTab + << sal_uInt32(100) + << sal_uInt32(64) + << sal_uInt32(3) + << sal_uInt32(0x0000003C) + << sal_uInt16(0) + << sal_uInt16(3) + << sal_uInt16(0) + << sal_uInt16(3) + << double(0) + << double(0) + << sal_Int16(-1) + << sal_Int16(-1); +} + +sal_uInt16 XclExpUsersViewBegin::GetNum() const +{ + return 0x01AA; +} + +std::size_t XclExpUsersViewBegin::GetLen() const +{ + return 64; +} + +void XclExpUsersViewEnd::SaveCont( XclExpStream& rStrm ) +{ + rStrm << sal_uInt16(0x0001); +} + +sal_uInt16 XclExpUsersViewEnd::GetNum() const +{ + return 0x01AB; +} + +std::size_t XclExpUsersViewEnd::GetLen() const +{ + return 2; +} + +void XclExpChTr0x0191::SaveCont( XclExpStream& rStrm ) +{ + rStrm << sal_uInt16(0x0000); +} + +sal_uInt16 XclExpChTr0x0191::GetNum() const +{ + return 0x0191; +} + +std::size_t XclExpChTr0x0191::GetLen() const +{ + return 2; +} + +void XclExpChTr0x0198::SaveCont( XclExpStream& rStrm ) +{ + rStrm << sal_uInt16(0x0006) + << sal_uInt16(0x0000); +} + +sal_uInt16 XclExpChTr0x0198::GetNum() const +{ + return 0x0198; +} + +std::size_t XclExpChTr0x0198::GetLen() const +{ + return 4; +} + +void XclExpChTr0x0192::SaveCont( XclExpStream& rStrm ) +{ + rStrm << sal_uInt16( 0x0022 ); + rStrm.WriteZeroBytes( 510 ); +} + +sal_uInt16 XclExpChTr0x0192::GetNum() const +{ + return 0x0192; +} + +std::size_t XclExpChTr0x0192::GetLen() const +{ + return 512; +} + +void XclExpChTr0x0197::SaveCont( XclExpStream& rStrm ) +{ + rStrm << sal_uInt16(0x0000); +} + +sal_uInt16 XclExpChTr0x0197::GetNum() const +{ + return 0x0197; +} + +std::size_t XclExpChTr0x0197::GetLen() const +{ + return 2; +} + +XclExpChTrEmpty::~XclExpChTrEmpty() +{ +} + +sal_uInt16 XclExpChTrEmpty::GetNum() const +{ + return nRecNum; +} + +std::size_t XclExpChTrEmpty::GetLen() const +{ + return 0; +} + +XclExpChTr0x0195::~XclExpChTr0x0195() +{ +} + +void XclExpChTr0x0195::SaveCont( XclExpStream& rStrm ) +{ + rStrm.WriteZeroBytes( 162 ); +} + +sal_uInt16 XclExpChTr0x0195::GetNum() const +{ + return 0x0195; +} + +std::size_t XclExpChTr0x0195::GetLen() const +{ + return 162; +} + +XclExpChTr0x0194::~XclExpChTr0x0194() +{ +} + +void XclExpChTr0x0194::SaveCont( XclExpStream& rStrm ) +{ + rStrm << sal_uInt32(0); + lcl_WriteDateTime( rStrm, aDateTime ); + rStrm << sal_uInt8(0); + lcl_WriteFixedString( rStrm, sUsername, 147 ); +} + +sal_uInt16 XclExpChTr0x0194::GetNum() const +{ + return 0x0194; +} + +std::size_t XclExpChTr0x0194::GetLen() const +{ + return 162; +} + +XclExpChTrHeader::~XclExpChTrHeader() +{ +} + +void XclExpChTrHeader::SaveCont( XclExpStream& rStrm ) +{ + rStrm << sal_uInt16(0x0006) + << sal_uInt16(0x0000) + << sal_uInt16(0x000D); + lcl_WriteGUID( rStrm, aGUID ); + lcl_WriteGUID( rStrm, aGUID ); + rStrm << nCount + << sal_uInt16(0x0001) + << sal_uInt32(0x00000000) + << sal_uInt16(0x001E); +} + +sal_uInt16 XclExpChTrHeader::GetNum() const +{ + return 0x0196; +} + +std::size_t XclExpChTrHeader::GetLen() const +{ + return 50; +} + +void XclExpChTrHeader::SaveXml( XclExpXmlStream& rRevisionHeadersStrm ) +{ + sax_fastparser::FSHelperPtr pHeaders = rRevisionHeadersStrm.GetCurrentStream(); + tools::Guid aGuid(aGUID); + rRevisionHeadersStrm.WriteAttributes( + XML_guid, aGuid.getString(), + XML_lastGuid, nullptr, // OOXTODO + XML_shared, nullptr, // OOXTODO + XML_diskRevisions, nullptr, // OOXTODO + XML_history, nullptr, // OOXTODO + XML_trackRevisions, nullptr, // OOXTODO + XML_exclusive, nullptr, // OOXTODO + XML_revisionId, nullptr, // OOXTODO + XML_version, nullptr, // OOXTODO + XML_keepChangeHistory, nullptr, // OOXTODO + XML_protected, nullptr, // OOXTODO + XML_preserveHistory, nullptr); // OOXTODO + pHeaders->write( ">" ); +} + +void XclExpXmlChTrHeaders::SetGUID( const sal_uInt8* pGUID ) +{ + memcpy(maGUID, pGUID, 16); +} + +void XclExpXmlChTrHeaders::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr pHeaders = rStrm.GetCurrentStream(); + + pHeaders->write("<")->writeId(XML_headers); + + tools::Guid aGuid(maGUID); + rStrm.WriteAttributes( + XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)), + FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)), + XML_guid, aGuid.getString(), + XML_lastGuid, nullptr, // OOXTODO + XML_shared, nullptr, // OOXTODO + XML_diskRevisions, nullptr, // OOXTODO + XML_history, nullptr, // OOXTODO + XML_trackRevisions, nullptr, // OOXTODO + XML_exclusive, nullptr, // OOXTODO + XML_revisionId, nullptr, // OOXTODO + XML_version, nullptr, // OOXTODO + XML_keepChangeHistory, nullptr, // OOXTODO + XML_protected, nullptr, // OOXTODO + XML_preserveHistory, nullptr); // OOXTODO + + pHeaders->write(">"); +} + +XclExpXmlChTrHeader::XclExpXmlChTrHeader( + const OUString& rUserName, const DateTime& rDateTime, const sal_uInt8* pGUID, + sal_Int32 nLogNumber, const XclExpChTrTabIdBuffer& rBuf ) : + maUserName(rUserName), maDateTime(rDateTime), mnLogNumber(nLogNumber), + mnMinAction(0), mnMaxAction(0) +{ + memcpy(maGUID, pGUID, 16); + if (rBuf.GetBufferCount()) + { + maTabBuffer.resize(rBuf.GetBufferCount()); + rBuf.GetBufferCopy(maTabBuffer.data()); + } +} + +void XclExpXmlChTrHeader::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr pHeader = rStrm.GetCurrentStream(); + + pHeader->write("<")->writeId(XML_header); + + OUString aRelId; + sax_fastparser::FSHelperPtr pRevLogStrm = rStrm.CreateOutputStream( + XclXmlUtils::GetStreamName("xl/revisions/", "revisionLog", mnLogNumber), + XclXmlUtils::GetStreamName(nullptr, "revisionLog", mnLogNumber), + rStrm.GetCurrentStream()->getOutputStream(), + "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml", + CREATE_OFFICEDOC_RELATION_TYPE("revisionLog"), + &aRelId); + + tools::Guid aGuid(maGUID); + rStrm.WriteAttributes( + XML_guid, aGuid.getString(), + XML_dateTime, lcl_DateTimeToOString(maDateTime), + XML_userName, maUserName, + FSNS(XML_r, XML_id), aRelId); + + if (mnMinAction) + rStrm.WriteAttributes(XML_minRId, OUString::number(mnMinAction)); + + if (mnMaxAction) + rStrm.WriteAttributes(XML_maxRId, OUString::number(mnMaxAction)); + + if (!maTabBuffer.empty()) + // next available sheet index. + rStrm.WriteAttributes(XML_maxSheetId, OUString::number(maTabBuffer.back()+1)); + + pHeader->write(">"); + + if (!maTabBuffer.empty()) + { + // Write sheet index map. + size_t n = maTabBuffer.size(); + pHeader->startElement(XML_sheetIdMap, XML_count, OString::number(n)); + + for (size_t i = 0; i < n; ++i) + { + pHeader->singleElement(XML_sheetId, XML_val, OString::number(maTabBuffer[i])); + } + pHeader->endElement(XML_sheetIdMap); + } + + // Write all revision logs in a separate stream. + + rStrm.PushStream(pRevLogStrm); + + pRevLogStrm->write("<")->writeId(XML_revisions); + + rStrm.WriteAttributes( + XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)), + FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel))); + + pRevLogStrm->write(">"); + + for (const auto& rxAction : maActions) + { + rxAction->SaveXml(rStrm); + } + + pRevLogStrm->write("writeId(XML_revisions)->write(">"); + + rStrm.PopStream(); + + pHeader->write("writeId(XML_header)->write(">"); +} + +void XclExpXmlChTrHeader::AppendAction( std::unique_ptr pAction ) +{ + sal_uInt32 nActionNum = pAction->GetActionNumber(); + if (!mnMinAction || mnMinAction > nActionNum) + mnMinAction = nActionNum; + + if (!mnMaxAction || mnMaxAction < nActionNum) + mnMaxAction = nActionNum; + + maActions.push_back(std::move(pAction)); +} + +XclExpChTrInfo::XclExpChTrInfo( const OUString& rUsername, const DateTime& rDateTime, const sal_uInt8* pGUID ) : + sUsername( rUsername ), + aDateTime( rDateTime ) +{ + memcpy( aGUID, pGUID, 16 ); +} + +XclExpChTrInfo::~XclExpChTrInfo() +{ +} + +void XclExpChTrInfo::SaveCont( XclExpStream& rStrm ) +{ + rStrm << sal_uInt32(0xFFFFFFFF) + << sal_uInt32(0x00000000) + << sal_uInt32(0x00000020) + << sal_uInt16(0xFFFF); + lcl_WriteGUID( rStrm, aGUID ); + rStrm << sal_uInt16(0x04B0); + lcl_WriteFixedString( rStrm, sUsername, 113 ); + lcl_WriteDateTime( rStrm, aDateTime ); + rStrm << sal_uInt8(0x0000) + << sal_uInt16(0x0002); +} + +sal_uInt16 XclExpChTrInfo::GetNum() const +{ + return 0x0138; +} + +std::size_t XclExpChTrInfo::GetLen() const +{ + return 158; +} + +XclExpChTrTabIdBuffer::XclExpChTrTabIdBuffer( sal_uInt16 nCount ) : + nBufSize( nCount ), + nLastId( nCount ) +{ + pBuffer.reset( new sal_uInt16[ nBufSize ] ); + memset( pBuffer.get(), 0, sizeof(sal_uInt16) * nBufSize ); + pLast = pBuffer.get() + nBufSize - 1; +} + +XclExpChTrTabIdBuffer::XclExpChTrTabIdBuffer( const XclExpChTrTabIdBuffer& rCopy ) : + nBufSize( rCopy.nBufSize ), + nLastId( rCopy.nLastId ) +{ + pBuffer.reset( new sal_uInt16[ nBufSize ] ); + memcpy( pBuffer.get(), rCopy.pBuffer.get(), sizeof(sal_uInt16) * nBufSize ); + pLast = pBuffer.get() + nBufSize - 1; +} + +XclExpChTrTabIdBuffer::~XclExpChTrTabIdBuffer() +{ +} + +void XclExpChTrTabIdBuffer::InitFill( sal_uInt16 nIndex ) +{ + OSL_ENSURE( nIndex < nLastId, "XclExpChTrTabIdBuffer::Insert - out of range" ); + + sal_uInt16 nFreeCount = 0; + for( sal_uInt16* pElem = pBuffer.get(); pElem <= pLast; pElem++ ) + { + if( !*pElem ) + nFreeCount++; + if( nFreeCount > nIndex ) + { + *pElem = nLastId--; + return; + } + } +} + +void XclExpChTrTabIdBuffer::InitFillup() +{ + sal_uInt16 nFreeCount = 1; + for( sal_uInt16* pElem = pBuffer.get(); pElem <= pLast; pElem++ ) + if( !*pElem ) + *pElem = nFreeCount++; + nLastId = nBufSize; +} + +sal_uInt16 XclExpChTrTabIdBuffer::GetId( sal_uInt16 nIndex ) const +{ + OSL_ENSURE( nIndex < nBufSize, "XclExpChTrTabIdBuffer::GetId - out of range" ); + return pBuffer[ nIndex ]; +} + +void XclExpChTrTabIdBuffer::Remove() +{ + OSL_ENSURE( pBuffer.get() <= pLast, "XclExpChTrTabIdBuffer::Remove - buffer empty" ); + sal_uInt16* pElem = pBuffer.get(); + while( (pElem <= pLast) && (*pElem != nLastId) ) + pElem++; + while( pElem < pLast ) + { + *pElem = *(pElem + 1); + pElem++; + } + pLast--; + nLastId--; +} + +XclExpChTrTabId::XclExpChTrTabId( const XclExpChTrTabIdBuffer& rBuffer ) + : nTabCount( rBuffer.GetBufferCount() ) +{ + pBuffer.reset( new sal_uInt16[ nTabCount ] ); + rBuffer.GetBufferCopy( pBuffer.get() ); +} + +XclExpChTrTabId::~XclExpChTrTabId() +{ + Clear(); +} + +void XclExpChTrTabId::Copy( const XclExpChTrTabIdBuffer& rBuffer ) +{ + Clear(); + nTabCount = rBuffer.GetBufferCount(); + pBuffer.reset( new sal_uInt16[ nTabCount ] ); + rBuffer.GetBufferCopy( pBuffer.get() ); +} + +void XclExpChTrTabId::SaveCont( XclExpStream& rStrm ) +{ + rStrm.EnableEncryption(); + if( pBuffer ) + rStrm.Write(pBuffer.get(), nTabCount); + else + for( sal_uInt16 nIndex = 1; nIndex <= nTabCount; nIndex++ ) + rStrm << nIndex; +} + +sal_uInt16 XclExpChTrTabId::GetNum() const +{ + return 0x013D; +} + +std::size_t XclExpChTrTabId::GetLen() const +{ + return nTabCount << 1; +} + +// ! does not copy additional actions +XclExpChTrAction::XclExpChTrAction( const XclExpChTrAction& rCopy ) : + ExcRecord( rCopy ), + sUsername( rCopy.sUsername ), + aDateTime( rCopy.aDateTime ), + nIndex( 0 ), + bAccepted( rCopy.bAccepted ), + rTabInfo( rCopy.rTabInfo ), + rIdBuffer( rCopy.rIdBuffer ), + nLength( rCopy.nLength ), + nOpCode( rCopy.nOpCode ), + bForceInfo( rCopy.bForceInfo ) +{ +} + +XclExpChTrAction::XclExpChTrAction( + const ScChangeAction& rAction, + const XclExpRoot& rRoot, + const XclExpChTrTabIdBuffer& rTabIdBuffer, + sal_uInt16 nNewOpCode ) : + sUsername( rAction.GetUser() ), + aDateTime( rAction.GetDateTime() ), + nIndex( 0 ), + bAccepted( rAction.IsAccepted() ), + rTabInfo( rRoot.GetTabInfo() ), + rIdBuffer( rTabIdBuffer ), + nLength( 0 ), + nOpCode( nNewOpCode ), + bForceInfo( false ) +{ + aDateTime.SetSec( 0 ); + aDateTime.SetNanoSec( 0 ); +} + +XclExpChTrAction::~XclExpChTrAction() +{ +} + +void XclExpChTrAction::SetAddAction( XclExpChTrAction* pAction ) +{ + if( pAddAction ) + pAddAction->SetAddAction( pAction ); + else + pAddAction.reset( pAction ); +} + +void XclExpChTrAction::AddDependentContents( + const ScChangeAction& rAction, + const XclExpRoot& rRoot, + const ScChangeTrack& rChangeTrack ) +{ + ScChangeActionMap aActionMap; + + rChangeTrack.GetDependents( const_cast(&rAction), aActionMap ); + for( const auto& rEntry : aActionMap ) + if( rEntry.second->GetType() == SC_CAT_CONTENT ) + SetAddAction( new XclExpChTrCellContent( + *static_cast(rEntry.second), rRoot, rIdBuffer ) ); +} + +void XclExpChTrAction::SetIndex( sal_uInt32& rIndex ) +{ + nIndex = rIndex++; +} + +void XclExpChTrAction::SaveCont( XclExpStream& rStrm ) +{ + OSL_ENSURE( nOpCode != EXC_CHTR_OP_UNKNOWN, "XclExpChTrAction::SaveCont - unknown action" ); + rStrm << nLength + << nIndex + << nOpCode + << static_cast(bAccepted ? EXC_CHTR_ACCEPT : EXC_CHTR_NOTHING); + SaveActionData( rStrm ); +} + +void XclExpChTrAction::PrepareSaveAction( XclExpStream& /*rStrm*/ ) const +{ +} + +void XclExpChTrAction::CompleteSaveAction( XclExpStream& /*rStrm*/ ) const +{ +} + +void XclExpChTrAction::Save( XclExpStream& rStrm ) +{ + PrepareSaveAction( rStrm ); + ExcRecord::Save( rStrm ); + if( pAddAction ) + pAddAction->Save( rStrm ); + CompleteSaveAction( rStrm ); +} + +std::size_t XclExpChTrAction::GetLen() const +{ + return GetHeaderByteCount() + GetActionByteCount(); +} + +XclExpChTrData::XclExpChTrData() : + mpFormulaCell( nullptr ), + fValue( 0.0 ), + nRKValue( 0 ), + nType( EXC_CHTR_TYPE_EMPTY ), + nSize( 0 ) +{ +} + +XclExpChTrData::~XclExpChTrData() +{ + Clear(); +} + +void XclExpChTrData::Clear() +{ + pString.reset(); + mpFormulaCell = nullptr; + mxTokArr.reset(); + maRefLog.clear(); + fValue = 0.0; + nRKValue = 0; + nType = EXC_CHTR_TYPE_EMPTY; + nSize = 0; +} + +void XclExpChTrData::WriteFormula( XclExpStream& rStrm, const XclExpChTrTabIdBuffer& rTabIdBuffer ) +{ + OSL_ENSURE( mxTokArr && !mxTokArr->Empty(), "XclExpChTrData::Write - no formula" ); + rStrm << *mxTokArr; + + for( const auto& rLogEntry : maRefLog ) + { + if( rLogEntry.mpUrl && rLogEntry.mpFirstTab ) + { + rStrm << *rLogEntry.mpUrl << sal_uInt8(0x01) << *rLogEntry.mpFirstTab << sal_uInt8(0x02); + } + else + { + bool bSingleTab = rLogEntry.mnFirstXclTab == rLogEntry.mnLastXclTab; + rStrm.SetSliceSize( bSingleTab ? 6 : 8 ); + rStrm << sal_uInt8(0x01) << sal_uInt8(0x02) << sal_uInt8(0x00); + rStrm << rTabIdBuffer.GetId( rLogEntry.mnFirstXclTab ); + if( bSingleTab ) + rStrm << sal_uInt8(0x02); + else + rStrm << sal_uInt8(0x00) << rTabIdBuffer.GetId( rLogEntry.mnLastXclTab ); + } + } + rStrm.SetSliceSize( 0 ); + rStrm << sal_uInt8(0x00); +} + +void XclExpChTrData::Write( XclExpStream& rStrm, const XclExpChTrTabIdBuffer& rTabIdBuffer ) +{ + switch( nType ) + { + case EXC_CHTR_TYPE_RK: + rStrm << nRKValue; + break; + case EXC_CHTR_TYPE_DOUBLE: + rStrm << fValue; + break; + case EXC_CHTR_TYPE_STRING: + OSL_ENSURE( pString, "XclExpChTrData::Write - no string" ); + rStrm << *pString; + break; + case EXC_CHTR_TYPE_FORMULA: + WriteFormula( rStrm, rTabIdBuffer ); + break; + } +} + +XclExpChTrCellContent::XclExpChTrCellContent( + const ScChangeActionContent& rAction, + const XclExpRoot& rRoot, + const XclExpChTrTabIdBuffer& rTabIdBuffer ) : + XclExpChTrAction( rAction, rRoot, rTabIdBuffer, EXC_CHTR_OP_CELL ), + XclExpRoot( rRoot ), + aPosition( rAction.GetBigRange().MakeRange( rRoot.GetDoc()).aStart ) +{ + sal_uInt32 nDummy32; + sal_uInt16 nDummy16; + GetCellData( rRoot, rAction.GetOldCell(), pOldData, nDummy32, nOldLength ); + GetCellData( rRoot, rAction.GetNewCell(), pNewData, nLength, nDummy16 ); +} + +XclExpChTrCellContent::~XclExpChTrCellContent() +{ + pOldData.reset(); + pNewData.reset(); +} + +void XclExpChTrCellContent::MakeEmptyChTrData( std::unique_ptr& rpData ) +{ + if( rpData ) + rpData->Clear(); + else + rpData.reset( new XclExpChTrData ); +} + +void XclExpChTrCellContent::GetCellData( + const XclExpRoot& rRoot, const ScCellValue& rScCell, + std::unique_ptr& rpData, sal_uInt32& rXclLength1, sal_uInt16& rXclLength2 ) +{ + MakeEmptyChTrData( rpData ); + rXclLength1 = 0x0000003A; + rXclLength2 = 0x0000; + + if (rScCell.isEmpty()) + { + rpData.reset(); + return; + } + + switch (rScCell.meType) + { + case CELLTYPE_VALUE: + { + rpData->fValue = rScCell.mfValue; + if( XclTools::GetRKFromDouble( rpData->nRKValue, rpData->fValue ) ) + { + rpData->nType = EXC_CHTR_TYPE_RK; + rpData->nSize = 4; + rXclLength1 = 0x0000003E; + rXclLength2 = 0x0004; + } + else + { + rpData->nType = EXC_CHTR_TYPE_DOUBLE; + rpData->nSize = 8; + rXclLength1 = 0x00000042; + rXclLength2 = 0x0008; + } + } + break; + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + { + OUString sCellStr; + if (rScCell.meType == CELLTYPE_STRING) + { + sCellStr = rScCell.mpString->getString(); + rpData->mpFormattedString = XclExpStringHelper::CreateCellString( + rRoot, sCellStr, nullptr); + } + else + { + XclExpHyperlinkHelper aLinkHelper( rRoot, aPosition ); + if (rScCell.mpEditText) + { + sCellStr = ScEditUtil::GetString(*rScCell.mpEditText, &GetDoc()); + rpData->mpFormattedString = XclExpStringHelper::CreateCellString( + rRoot, *rScCell.mpEditText, nullptr, aLinkHelper); + } + else + { + rpData->mpFormattedString = XclExpStringHelper::CreateCellString( + rRoot, OUString(), nullptr); + } + } + rpData->pString.reset( new XclExpString( sCellStr, XclStrFlags::NONE, 32766 ) ); + rpData->nType = EXC_CHTR_TYPE_STRING; + rpData->nSize = 3 + rpData->pString->GetSize(); + rXclLength1 = 64 + (sCellStr.getLength() << 1); + rXclLength2 = 6 + static_cast(sCellStr.getLength() << 1); + } + break; + case CELLTYPE_FORMULA: + { + const ScFormulaCell* pFmlCell = rScCell.mpFormula; + rpData->mpFormulaCell = pFmlCell; + + const ScTokenArray* pTokenArray = pFmlCell->GetCode(); + if( pTokenArray ) + { + XclExpRefLog& rRefLog = rpData->maRefLog; + rpData->mxTokArr = GetFormulaCompiler().CreateFormula( + EXC_FMLATYPE_CELL, *pTokenArray, &pFmlCell->aPos, &rRefLog ); + rpData->nType = EXC_CHTR_TYPE_FORMULA; + std::size_t nSize = std::accumulate(rRefLog.begin(), rRefLog.end(), + static_cast(rpData->mxTokArr->GetSize() + 3), + [](const std::size_t& rSum, const XclExpRefLogEntry& rLogEntry) { + if( rLogEntry.mpUrl && rLogEntry.mpFirstTab ) + return rSum + rLogEntry.mpUrl->GetSize() + rLogEntry.mpFirstTab->GetSize() + 2; + else + return rSum + ((rLogEntry.mnFirstXclTab == rLogEntry.mnLastXclTab) ? 6 : 8); + }); + rpData->nSize = ::std::min< std::size_t >( nSize, 0xFFFF ); + rXclLength1 = 0x00000052; + rXclLength2 = 0x0018; + } + } + break; + default:; + } +} + +void XclExpChTrCellContent::SaveActionData( XclExpStream& rStrm ) const +{ + WriteTabId( rStrm, aPosition.Tab() ); + rStrm << static_cast((pOldData ? (pOldData->nType << 3) : 0x0000) | (pNewData ? pNewData->nType : 0x0000)) + << sal_uInt16(0x0000); + Write2DAddress( rStrm, aPosition ); + rStrm << nOldLength + << sal_uInt32(0x00000000); + if( pOldData ) + pOldData->Write( rStrm, rIdBuffer ); + if( pNewData ) + pNewData->Write( rStrm, rIdBuffer ); +} + +sal_uInt16 XclExpChTrCellContent::GetNum() const +{ + return 0x013B; +} + +std::size_t XclExpChTrCellContent::GetActionByteCount() const +{ + std::size_t nLen = 16; + if( pOldData ) + nLen += pOldData->nSize; + if( pNewData ) + nLen += pNewData->nSize; + return nLen; +} + +static const char* lcl_GetType( XclExpChTrData* pData ) +{ + switch( pData->nType ) + { + case EXC_CHTR_TYPE_RK: + case EXC_CHTR_TYPE_DOUBLE: + return "n"; + case EXC_CHTR_TYPE_FORMULA: + { + ScFormulaCell* pFormulaCell = const_cast< ScFormulaCell* >( pData->mpFormulaCell ); + const char* sType; + OUString sValue; + XclXmlUtils::GetFormulaTypeAndValue( *pFormulaCell, sType, sValue ); + return sType; + } + break; + case EXC_CHTR_TYPE_STRING: + return "inlineStr"; + default: + break; + } + return "*unknown*"; +} + +static void lcl_WriteCell( XclExpXmlStream& rStrm, sal_Int32 nElement, const ScAddress& rPosition, XclExpChTrData* pData ) +{ + sax_fastparser::FSHelperPtr pStream = rStrm.GetCurrentStream(); + + pStream->startElement(nElement, + XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), rPosition), + XML_s, nullptr, // OOXTODO: not supported + XML_t, lcl_GetType(pData), + XML_cm, nullptr, // OOXTODO: not supported + XML_vm, nullptr, // OOXTODO: not supported + XML_ph, nullptr); // OOXTODO: not supported + switch( pData->nType ) + { + case EXC_CHTR_TYPE_RK: + case EXC_CHTR_TYPE_DOUBLE: + pStream->startElement(XML_v); + pStream->write( pData->fValue ); + pStream->endElement( XML_v ); + break; + case EXC_CHTR_TYPE_FORMULA: + pStream->startElement( XML_f + // OOXTODO: other attributes? see XclExpFormulaCell::SaveXml() + ); + pStream->writeEscaped( XclXmlUtils::ToOUString( + rStrm.GetRoot().GetCompileFormulaContext(), + pData->mpFormulaCell->aPos, pData->mpFormulaCell->GetCode())); + pStream->endElement( XML_f ); + break; + case EXC_CHTR_TYPE_STRING: + pStream->startElement(XML_is); + if( pData->mpFormattedString ) + pData->mpFormattedString->WriteXml( rStrm ); + else + pData->pString->WriteXml( rStrm ); + pStream->endElement( XML_is ); + break; + default: + // ignore + break; + } + pStream->endElement( nElement ); +} + +void XclExpChTrCellContent::SaveXml( XclExpXmlStream& rRevisionLogStrm ) +{ + sax_fastparser::FSHelperPtr pStream = rRevisionLogStrm.GetCurrentStream(); + pStream->startElement( XML_rcc, + XML_rId, OString::number(GetActionNumber()), + XML_ua, ToPsz( GetAccepted () ), // OOXTODO? bAccepted == ua or ra; not sure. + XML_ra, nullptr, // OOXTODO: RRD.fUndoAction? Or RRD.fAccepted? + XML_sId, OString::number(GetTabId(aPosition.Tab())), + XML_odxf, nullptr, // OOXTODO: not supported + XML_xfDxf, nullptr, // OOXTODO: not supported + XML_s, nullptr, // OOXTODO: not supported + XML_dxf, nullptr, // OOXTODO: not supported + XML_numFmtId, nullptr, // OOXTODO: not supported + XML_quotePrefix, nullptr, // OOXTODO: not supported + XML_oldQuotePrefix, nullptr, // OOXTODO: not supported + XML_ph, nullptr, // OOXTODO: not supported + XML_oldPh, nullptr, // OOXTODO: not supported + XML_endOfListFormulaUpdate, nullptr); // OOXTODO: not supported + if( pOldData ) + { + lcl_WriteCell( rRevisionLogStrm, XML_oc, aPosition, pOldData.get() ); + if (!pNewData) + { + pStream->singleElement(XML_nc, XML_r, XclXmlUtils::ToOString(rRevisionLogStrm.GetRoot().GetDoc(), aPosition)); + } + } + if( pNewData ) + { + lcl_WriteCell( rRevisionLogStrm, XML_nc, aPosition, pNewData.get() ); + } + // OOXTODO: XML_odxf, XML_ndxf, XML_extLst elements + pStream->endElement( XML_rcc ); +} + +XclExpChTrInsert::XclExpChTrInsert( const XclExpChTrInsert& rCopy ) : + XclExpChTrAction(rCopy), + mbEndOfList(rCopy.mbEndOfList), + aRange(rCopy.aRange) {} + +XclExpChTrInsert::XclExpChTrInsert( + const ScChangeAction& rAction, + const XclExpRoot& rRoot, + const XclExpChTrTabIdBuffer& rTabIdBuffer, + const ScChangeTrack& rChangeTrack ) : + XclExpChTrAction( rAction, rRoot, rTabIdBuffer ), + mbEndOfList(false), + aRange( rAction.GetBigRange().MakeRange( rRoot.GetDoc()) ) +{ + nLength = 0x00000030; + switch( rAction.GetType() ) + { + case SC_CAT_INSERT_COLS: nOpCode = EXC_CHTR_OP_INSCOL; break; + case SC_CAT_INSERT_ROWS: + { + const ScChangeActionIns& rIns = static_cast(rAction); + mbEndOfList = rIns.IsEndOfList(); + nOpCode = EXC_CHTR_OP_INSROW; + } + break; + case SC_CAT_DELETE_COLS: nOpCode = EXC_CHTR_OP_DELCOL; break; + case SC_CAT_DELETE_ROWS: nOpCode = EXC_CHTR_OP_DELROW; break; + default: + OSL_FAIL( "XclExpChTrInsert::XclExpChTrInsert - unknown action" ); + } + + if( nOpCode & EXC_CHTR_OP_COLFLAG ) + { + aRange.aStart.SetRow( 0 ); + aRange.aEnd.SetRow( rRoot.GetXclMaxPos().Row() ); + } + else + { + aRange.aStart.SetCol( 0 ); + aRange.aEnd.SetCol( rRoot.GetXclMaxPos().Col() ); + } + + if( nOpCode & EXC_CHTR_OP_DELFLAG ) + { + SetAddAction( new XclExpChTr0x014A( *this ) ); + AddDependentContents( rAction, rRoot, rChangeTrack ); + } +} + +XclExpChTrInsert::~XclExpChTrInsert() +{ +} + +void XclExpChTrInsert::SaveActionData( XclExpStream& rStrm ) const +{ + WriteTabId( rStrm, aRange.aStart.Tab() ); + sal_uInt16 nFlagVal = mbEndOfList ? 0x0001 : 0x0000; + rStrm << nFlagVal; + Write2DRange( rStrm, aRange ); + rStrm << sal_uInt32(0x00000000); +} + +void XclExpChTrInsert::PrepareSaveAction( XclExpStream& rStrm ) const +{ + if( (nOpCode == EXC_CHTR_OP_DELROW) || (nOpCode == EXC_CHTR_OP_DELCOL) ) + XclExpChTrEmpty( 0x0150 ).Save( rStrm ); +} + +void XclExpChTrInsert::CompleteSaveAction( XclExpStream& rStrm ) const +{ + if( (nOpCode == EXC_CHTR_OP_DELROW) || (nOpCode == EXC_CHTR_OP_DELCOL) ) + XclExpChTrEmpty( 0x0151 ).Save( rStrm ); +} + +sal_uInt16 XclExpChTrInsert::GetNum() const +{ + return 0x0137; +} + +std::size_t XclExpChTrInsert::GetActionByteCount() const +{ + return 16; +} + +static const char* lcl_GetAction( sal_uInt16 nOpCode ) +{ + switch( nOpCode ) + { + case EXC_CHTR_OP_INSCOL: return "insertCol"; + case EXC_CHTR_OP_INSROW: return "insertRow"; + case EXC_CHTR_OP_DELCOL: return "deleteCol"; + case EXC_CHTR_OP_DELROW: return "deleteRow"; + default: return "*unknown*"; + } +} + +void XclExpChTrInsert::SaveXml( XclExpXmlStream& rRevisionLogStrm ) +{ + sax_fastparser::FSHelperPtr pStream = rRevisionLogStrm.GetCurrentStream(); + pStream->startElement( XML_rrc, + XML_rId, OString::number(GetActionNumber()), + XML_ua, ToPsz( GetAccepted () ), // OOXTODO? bAccepted == ua or ra; not sure. + XML_ra, nullptr, // OOXTODO: RRD.fUndoAction? Or RRD.fAccepted? + XML_sId, OString::number(GetTabId(aRange.aStart.Tab())), + XML_eol, ToPsz10(mbEndOfList), + XML_ref, XclXmlUtils::ToOString(rRevisionLogStrm.GetRoot().GetDoc(), aRange), + XML_action, lcl_GetAction( nOpCode ), + XML_edge, nullptr); // OOXTODO: ??? + + // OOXTODO: does this handle XML_rfmt, XML_undo? + XclExpChTrAction* pAction = GetAddAction(); + while( pAction != nullptr ) + { + pAction->SaveXml( rRevisionLogStrm ); + pAction = pAction->GetAddAction(); + } + pStream->endElement( XML_rrc ); +} + +XclExpChTrInsertTab::XclExpChTrInsertTab( + const ScChangeAction& rAction, + const XclExpRoot& rRoot, + const XclExpChTrTabIdBuffer& rTabIdBuffer ) : + XclExpChTrAction( rAction, rRoot, rTabIdBuffer, EXC_CHTR_OP_INSTAB ), + XclExpRoot( rRoot ), + nTab( static_cast(rAction.GetBigRange().aStart.Tab()) ) +{ + nLength = 0x0000021C; + bForceInfo = true; +} + +XclExpChTrInsertTab::~XclExpChTrInsertTab() +{ +} + +void XclExpChTrInsertTab::SaveActionData( XclExpStream& rStrm ) const +{ + WriteTabId( rStrm, nTab ); + rStrm << sal_uInt32( 0 ); + lcl_WriteFixedString( rStrm, XclExpString( GetTabInfo().GetScTabName( nTab ) ), 127 ); + lcl_WriteDateTime( rStrm, GetDateTime() ); + rStrm.WriteZeroBytes( 133 ); +} + +sal_uInt16 XclExpChTrInsertTab::GetNum() const +{ + return 0x014D; +} + +std::size_t XclExpChTrInsertTab::GetActionByteCount() const +{ + return 276; +} + +void XclExpChTrInsertTab::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr pStream = rStrm.GetCurrentStream(); + pStream->singleElement( XML_ris, + XML_rId, OString::number(GetActionNumber()), + XML_ua, ToPsz( GetAccepted () ), // OOXTODO? bAccepted == ua or ra; not sure. + XML_ra, nullptr, // OOXTODO: RRD.fUndoAction? Or RRD.fAccepted? + XML_sheetId, OString::number(GetTabId(nTab)), + XML_name, GetTabInfo().GetScTabName(nTab).toUtf8(), + XML_sheetPosition, OString::number(nTab) ); +} + +XclExpChTrMoveRange::XclExpChTrMoveRange( + const ScChangeActionMove& rAction, + const XclExpRoot& rRoot, + const XclExpChTrTabIdBuffer& rTabIdBuffer, + const ScChangeTrack& rChangeTrack ) : + XclExpChTrAction( rAction, rRoot, rTabIdBuffer, EXC_CHTR_OP_MOVE ), + aDestRange( rAction.GetBigRange().MakeRange( rRoot.GetDoc() ) ) +{ + nLength = 0x00000042; + aSourceRange = aDestRange; + sal_Int32 nDCols, nDRows, nDTabs; + rAction.GetDelta( nDCols, nDRows, nDTabs ); + aSourceRange.aStart.IncRow( static_cast(-nDRows) ); + aSourceRange.aStart.IncCol( static_cast(-nDCols) ); + aSourceRange.aStart.IncTab( static_cast(-nDTabs) ); + aSourceRange.aEnd.IncRow( static_cast(-nDRows) ); + aSourceRange.aEnd.IncCol( static_cast(-nDCols) ); + aSourceRange.aEnd.IncTab( static_cast(-nDTabs) ); + AddDependentContents( rAction, rRoot, rChangeTrack ); +} + +XclExpChTrMoveRange::~XclExpChTrMoveRange() +{ +} + +void XclExpChTrMoveRange::SaveActionData( XclExpStream& rStrm ) const +{ + WriteTabId( rStrm, aDestRange.aStart.Tab() ); + Write2DRange( rStrm, aSourceRange ); + Write2DRange( rStrm, aDestRange ); + WriteTabId( rStrm, aSourceRange.aStart.Tab() ); + rStrm << sal_uInt32(0x00000000); +} + +void XclExpChTrMoveRange::PrepareSaveAction( XclExpStream& rStrm ) const +{ + XclExpChTrEmpty( 0x014E ).Save( rStrm ); +} + +void XclExpChTrMoveRange::CompleteSaveAction( XclExpStream& rStrm ) const +{ + XclExpChTrEmpty( 0x014F ).Save( rStrm ); +} + +sal_uInt16 XclExpChTrMoveRange::GetNum() const +{ + return 0x0140; +} + +std::size_t XclExpChTrMoveRange::GetActionByteCount() const +{ + return 24; +} + +void XclExpChTrMoveRange::SaveXml( XclExpXmlStream& rRevisionLogStrm ) +{ + sax_fastparser::FSHelperPtr pStream = rRevisionLogStrm.GetCurrentStream(); + + pStream->startElement( XML_rm, + XML_rId, OString::number(GetActionNumber()), + XML_ua, ToPsz( GetAccepted () ), // OOXTODO? bAccepted == ua or ra; not sure. + XML_ra, nullptr, // OOXTODO: RRD.fUndoAction? Or RRD.fAccepted? + XML_sheetId, OString::number(GetTabId(aDestRange.aStart.Tab())), + XML_source, XclXmlUtils::ToOString(rRevisionLogStrm.GetRoot().GetDoc(), aSourceRange), + XML_destination, XclXmlUtils::ToOString(rRevisionLogStrm.GetRoot().GetDoc(), aDestRange), + XML_sourceSheetId, OString::number(GetTabId(aSourceRange.aStart.Tab())) ); + // OOXTODO: does this handle XML_rfmt, XML_undo? + XclExpChTrAction* pAction = GetAddAction(); + while( pAction != nullptr ) + { + pAction->SaveXml( rRevisionLogStrm ); + pAction = pAction->GetAddAction(); + } + pStream->endElement( XML_rm ); +} + +XclExpChTr0x014A::XclExpChTr0x014A( const XclExpChTrInsert& rAction ) : + XclExpChTrInsert( rAction ) +{ + nLength = 0x00000026; + nOpCode = EXC_CHTR_OP_FORMAT; +} + +XclExpChTr0x014A::~XclExpChTr0x014A() +{ +} + +void XclExpChTr0x014A::SaveActionData( XclExpStream& rStrm ) const +{ + WriteTabId( rStrm, aRange.aStart.Tab() ); + rStrm << sal_uInt16(0x0003) + << sal_uInt16(0x0001); + Write2DRange( rStrm, aRange ); +} + +sal_uInt16 XclExpChTr0x014A::GetNum() const +{ + return 0x014A; +} + +std::size_t XclExpChTr0x014A::GetActionByteCount() const +{ + return 14; +} + +void XclExpChTr0x014A::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr pStream = rStrm.GetCurrentStream(); + + pStream->startElement( XML_rfmt, + XML_sheetId, OString::number(GetTabId(aRange.aStart.Tab())), + XML_xfDxf, nullptr, // OOXTODO: not supported + XML_s, nullptr, // OOXTODO: style + XML_sqref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aRange), + XML_start, nullptr, // OOXTODO: for string changes + XML_length, nullptr); // OOXTODO: for string changes + // OOXTODO: XML_dxf, XML_extLst + + pStream->endElement( XML_rfmt ); +} + +std::size_t ExcXmlRecord::GetLen() const +{ + return 0; +} + +sal_uInt16 ExcXmlRecord::GetNum() const +{ + return 0; +} + +void ExcXmlRecord::Save( XclExpStream& ) +{ + // Do nothing; ignored for BIFF output. +} + +namespace { + +class EndXmlElement : public ExcXmlRecord +{ + sal_Int32 mnElement; +public: + explicit EndXmlElement( sal_Int32 nElement ) : mnElement( nElement) {} + virtual void SaveXml( XclExpXmlStream& rStrm ) override; +}; + +} + +void EndXmlElement::SaveXml( XclExpXmlStream& rStrm ) +{ + sax_fastparser::FSHelperPtr pStream = rStrm.GetCurrentStream(); + pStream->write("writeId(mnElement)->write(">"); +} + +XclExpChangeTrack::XclExpChangeTrack( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + pTabIdBuffer( nullptr ) +{ + OSL_ENSURE( GetOldRoot().pTabId, "XclExpChangeTrack::XclExpChangeTrack - root data incomplete" ); + if( !GetOldRoot().pTabId ) + return; + + ScChangeTrack* pTempChangeTrack = CreateTempChangeTrack(); + if (!pTempChangeTrack) + return; + + pTabIdBuffer = new XclExpChTrTabIdBuffer( GetTabInfo().GetXclTabCount() ); + maBuffers.push_back( std::unique_ptr(pTabIdBuffer) ); + + // calculate final table order (tab id list) + const ScChangeAction* pScAction; + for( pScAction = pTempChangeTrack->GetLast(); pScAction; pScAction = pScAction->GetPrev() ) + { + if( pScAction->GetType() == SC_CAT_INSERT_TABS ) + { + SCTAB nScTab = static_cast< SCTAB >( pScAction->GetBigRange().aStart.Tab() ); + pTabIdBuffer->InitFill( GetTabInfo().GetXclTab( nScTab ) ); + } + } + pTabIdBuffer->InitFillup(); + GetOldRoot().pTabId->Copy( *pTabIdBuffer ); + + // get actions in reverse order + pScAction = pTempChangeTrack->GetLast(); + while( pScAction ) + { + PushActionRecord( *pScAction ); + const ScChangeAction* pPrevAction = pScAction->GetPrev(); + pScAction = pPrevAction; + } + + // build record list + if (GetOutput() == EXC_OUTPUT_BINARY) + { + XclExpChTrHeader* pHeader = new XclExpChTrHeader; // header record for last GUID + maRecList.push_back( std::unique_ptr(pHeader) ); + maRecList.push_back( std::unique_ptr( new XclExpChTr0x0195 ) ); + maRecList.push_back( std::unique_ptr( new XclExpChTr0x0194( *pTempChangeTrack ) ) ); + + OUString sLastUsername; + DateTime aLastDateTime( DateTime::EMPTY ); + sal_uInt32 nIndex = 1; + sal_uInt8 aGUID[ 16 ]; // GUID for action info records + bool bValidGUID = false; + while( !aActionStack.empty() ) + { + XclExpChTrAction* pAction = aActionStack.top(); + aActionStack.pop(); + + if( (nIndex == 1) || pAction->ForceInfoRecord() || + (pAction->GetUsername() != sLastUsername) || + (pAction->GetDateTime() != aLastDateTime) ) + { + lcl_GenerateGUID( aGUID, bValidGUID ); + sLastUsername = pAction->GetUsername(); + aLastDateTime = pAction->GetDateTime(); + + maRecList.push_back( std::unique_ptr(new XclExpChTrInfo(sLastUsername, aLastDateTime, aGUID)) ); + maRecList.push_back( std::unique_ptr(new XclExpChTrTabId(pAction->GetTabIdBuffer())) ); + pHeader->SetGUID( aGUID ); + } + pAction->SetIndex( nIndex ); + maRecList.push_back( std::unique_ptr(pAction) ); + } + + pHeader->SetGUID( aGUID ); + pHeader->SetCount( nIndex - 1 ); + maRecList.push_back( std::unique_ptr(new ExcEof) ); + } + else + { + XclExpXmlChTrHeaders* pHeaders = new XclExpXmlChTrHeaders; + maRecList.push_back( std::unique_ptr(pHeaders)); + + OUString sLastUsername; + DateTime aLastDateTime(DateTime::EMPTY); + sal_uInt32 nIndex = 1; + sal_Int32 nLogNumber = 1; + XclExpXmlChTrHeader* pCurHeader = nullptr; + sal_uInt8 aGUID[ 16 ]; // GUID for action info records + bool bValidGUID = false; + + while (!aActionStack.empty()) + { + XclExpChTrAction* pAction = aActionStack.top(); + aActionStack.pop(); + + if( (nIndex == 1) || pAction->ForceInfoRecord() || + (pAction->GetUsername() != sLastUsername) || + (pAction->GetDateTime() != aLastDateTime) ) + { + lcl_GenerateGUID( aGUID, bValidGUID ); + sLastUsername = pAction->GetUsername(); + aLastDateTime = pAction->GetDateTime(); + + pCurHeader = new XclExpXmlChTrHeader(sLastUsername, aLastDateTime, aGUID, nLogNumber, pAction->GetTabIdBuffer()); + maRecList.push_back( std::unique_ptr(pCurHeader)); + nLogNumber++; + pHeaders->SetGUID(aGUID); + } + pAction->SetIndex(nIndex); + pCurHeader->AppendAction(std::unique_ptr(pAction)); + } + + pHeaders->SetGUID(aGUID); + maRecList.push_back( std::unique_ptr(new EndXmlElement(XML_headers))); + } +} + +XclExpChangeTrack::~XclExpChangeTrack() +{ + while( !aActionStack.empty() ) + { + delete aActionStack.top(); + aActionStack.pop(); + } +} + +ScChangeTrack* XclExpChangeTrack::CreateTempChangeTrack() +{ + // get original change track + ScChangeTrack* pOrigChangeTrack = GetDoc().GetChangeTrack(); + OSL_ENSURE( pOrigChangeTrack, "XclExpChangeTrack::CreateTempChangeTrack - no change track data" ); + if( !pOrigChangeTrack ) + return nullptr; + + assert(!xTempDoc); + // create empty document + xTempDoc.reset(new ScDocument); + + // adjust table count + SCTAB nOrigCount = GetDoc().GetTableCount(); + OUString sTabName; + for( sal_Int32 nIndex = 0; nIndex < nOrigCount; nIndex++ ) + { + xTempDoc->CreateValidTabName(sTabName); + xTempDoc->InsertTab(SC_TAB_APPEND, sTabName); + } + OSL_ENSURE(nOrigCount == xTempDoc->GetTableCount(), + "XclExpChangeTrack::CreateTempChangeTrack - table count mismatch"); + if(nOrigCount != xTempDoc->GetTableCount()) + return nullptr; + + return pOrigChangeTrack->Clone(xTempDoc.get()); +} + +void XclExpChangeTrack::PushActionRecord( const ScChangeAction& rAction ) +{ + XclExpChTrAction* pXclAction = nullptr; + ScChangeTrack* pTempChangeTrack = xTempDoc->GetChangeTrack(); + switch( rAction.GetType() ) + { + case SC_CAT_CONTENT: + pXclAction = new XclExpChTrCellContent( static_cast(rAction), GetRoot(), *pTabIdBuffer ); + break; + case SC_CAT_INSERT_ROWS: + case SC_CAT_INSERT_COLS: + case SC_CAT_DELETE_ROWS: + case SC_CAT_DELETE_COLS: + if (pTempChangeTrack) + pXclAction = new XclExpChTrInsert( rAction, GetRoot(), *pTabIdBuffer, *pTempChangeTrack ); + break; + case SC_CAT_INSERT_TABS: + { + pXclAction = new XclExpChTrInsertTab( rAction, GetRoot(), *pTabIdBuffer ); + XclExpChTrTabIdBuffer* pNewBuffer = new XclExpChTrTabIdBuffer( *pTabIdBuffer ); + pNewBuffer->Remove(); + maBuffers.push_back( std::unique_ptr(pNewBuffer) ); + pTabIdBuffer = pNewBuffer; + } + break; + case SC_CAT_MOVE: + if (pTempChangeTrack) + pXclAction = new XclExpChTrMoveRange( static_cast(rAction), GetRoot(), *pTabIdBuffer, *pTempChangeTrack ); + break; + default:; + } + if( pXclAction ) + aActionStack.push( pXclAction ); +} + +bool XclExpChangeTrack::WriteUserNamesStream() +{ + bool bRet = false; + tools::SvRef xSvStrm = OpenStream( EXC_STREAM_USERNAMES ); + OSL_ENSURE( xSvStrm.is(), "XclExpChangeTrack::WriteUserNamesStream - no stream" ); + if( xSvStrm.is() ) + { + XclExpStream aXclStrm( *xSvStrm, GetRoot() ); + XclExpChTr0x0191().Save( aXclStrm ); + XclExpChTr0x0198().Save( aXclStrm ); + XclExpChTr0x0192().Save( aXclStrm ); + XclExpChTr0x0197().Save( aXclStrm ); + xSvStrm->Commit(); + bRet = true; + } + return bRet; +} + +void XclExpChangeTrack::Write() +{ + if (maRecList.empty()) + return; + + if( !WriteUserNamesStream() ) + return; + + tools::SvRef xSvStrm = OpenStream( EXC_STREAM_REVLOG ); + OSL_ENSURE( xSvStrm.is(), "XclExpChangeTrack::Write - no stream" ); + if( xSvStrm.is() ) + { + XclExpStream aXclStrm( *xSvStrm, GetRoot(), EXC_MAXRECSIZE_BIFF8 + 8 ); + + for(const auto& rxRec : maRecList) + rxRec->Save(aXclStrm); + + xSvStrm->Commit(); + } +} + +static void lcl_WriteUserNamesXml( XclExpXmlStream& rWorkbookStrm ) +{ + sax_fastparser::FSHelperPtr pUserNames = rWorkbookStrm.CreateOutputStream( + "xl/revisions/userNames.xml", + u"revisions/userNames.xml", + rWorkbookStrm.GetCurrentStream()->getOutputStream(), + "application/vnd.openxmlformats-officedocument.spreadsheetml.userNames+xml", + CREATE_OFFICEDOC_RELATION_TYPE("usernames")); + pUserNames->startElement( XML_users, + XML_xmlns, rWorkbookStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(), + FSNS( XML_xmlns, XML_r ), rWorkbookStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(), + XML_count, "0" ); + // OOXTODO: XML_userinfo elements for each user editing the file + // Doesn't seem to be supported by .xls output either (based on + // contents of XclExpChangeTrack::WriteUserNamesStream()). + pUserNames->endElement( XML_users ); +} + +void XclExpChangeTrack::WriteXml( XclExpXmlStream& rWorkbookStrm ) +{ + if (maRecList.empty()) + return; + + lcl_WriteUserNamesXml( rWorkbookStrm ); + + sax_fastparser::FSHelperPtr pRevisionHeaders = rWorkbookStrm.CreateOutputStream( + "xl/revisions/revisionHeaders.xml", + u"revisions/revisionHeaders.xml", + rWorkbookStrm.GetCurrentStream()->getOutputStream(), + "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionHeaders+xml", + CREATE_OFFICEDOC_RELATION_TYPE("revisionHeaders")); + // OOXTODO: XML_userinfo elements for each user editing the file + // Doesn't seem to be supported by .xls output either (based on + // contents of XclExpChangeTrack::WriteUserNamesStream()). + rWorkbookStrm.PushStream( pRevisionHeaders ); + + for (const auto& rxRec : maRecList) + rxRec->SaveXml(rWorkbookStrm); + + rWorkbookStrm.PopStream(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3