use insert function instead of for loop
[LibreOffice.git] / sc / source / filter / xcl97 / XclExpChangeTrack.cxx
blob0e11e542d0a25ae7e2b7512e29e38a9e1e943ede
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 <memory>
21 #include <numeric>
22 #include <stdio.h>
23 #include <sot/storage.hxx>
24 #include <XclExpChangeTrack.hxx>
25 #include <utility>
26 #include <xeformula.hxx>
27 #include <xehelper.hxx>
28 #include <xltools.hxx>
29 #include <formulacell.hxx>
30 #include <document.hxx>
31 #include <editutil.hxx>
32 #include <root.hxx>
33 #include <tools/Guid.hxx>
35 #include <oox/export/utils.hxx>
36 #include <oox/token/namespaces.hxx>
37 #include <oox/token/tokens.hxx>
38 #include <rtl/uuid.h>
39 #include <svl/sharedstring.hxx>
40 #include <unotools/securityoptions.hxx>
42 #include <com/sun/star/util/DateTime.hpp>
44 using namespace oox;
46 static OString lcl_DateTimeToOString( const DateTime& rDateTime )
48 char sBuf[ 200 ];
49 snprintf( sBuf, sizeof( sBuf ),
50 "%d-%02d-%02dT%02d:%02d:%02d.%09" SAL_PRIuUINT32 "Z",
51 rDateTime.GetYear(), rDateTime.GetMonth(), rDateTime.GetDay(),
52 rDateTime.GetHour(), rDateTime.GetMin(), rDateTime.GetSec(),
53 rDateTime.GetNanoSec() );
54 return sBuf;
57 // local functions
59 static void lcl_WriteDateTime( XclExpStream& rStrm, const DateTime& rDateTime )
61 rStrm.SetSliceSize( 7 );
62 rStrm << static_cast<sal_uInt16>(rDateTime.GetYear())
63 << static_cast<sal_uInt8>(rDateTime.GetMonth())
64 << static_cast<sal_uInt8>(rDateTime.GetDay())
65 << static_cast<sal_uInt8>(rDateTime.GetHour())
66 << static_cast<sal_uInt8>(rDateTime.GetMin())
67 << static_cast<sal_uInt8>(rDateTime.GetSec());
68 rStrm.SetSliceSize( 0 );
71 // write string and fill rest of <nLength> with zero bytes
72 // <nLength> is without string header
73 static void lcl_WriteFixedString( XclExpStream& rStrm, const XclExpString& rString, std::size_t nLength )
75 std::size_t nStrBytes = rString.GetBufferSize();
76 OSL_ENSURE( nLength >= nStrBytes, "lcl_WriteFixedString - String too long" );
77 if( rString.Len() > 0 )
78 rStrm << rString;
79 if( nLength > nStrBytes )
80 rStrm.WriteZeroBytes( nLength - nStrBytes );
83 static void lcl_GenerateGUID( sal_uInt8* pGUID, bool& rValidGUID )
85 rtl_createUuid( pGUID, rValidGUID ? pGUID : nullptr, false );
86 rValidGUID = true;
89 static void lcl_WriteGUID( XclExpStream& rStrm, const sal_uInt8* pGUID )
91 rStrm.SetSliceSize( 16 );
92 for( std::size_t nIndex = 0; nIndex < 16; nIndex++ )
93 rStrm << pGUID[ nIndex ];
94 rStrm.SetSliceSize( 0 );
97 XclExpUserBView::XclExpUserBView( const OUString& rUsername, const sal_uInt8* pGUID ) :
98 sUsername( rUsername )
100 memcpy( aGUID, pGUID, 16 );
103 void XclExpUserBView::SaveCont( XclExpStream& rStrm )
105 rStrm << sal_uInt32(0xFF078014)
106 << sal_uInt32(0x00000001);
107 lcl_WriteGUID( rStrm, aGUID );
108 rStrm.WriteZeroBytes( 8 );
109 rStrm << sal_uInt32(1200)
110 << sal_uInt32(1000)
111 << sal_uInt16(1000)
112 << sal_uInt16(0x0CF7)
113 << sal_uInt16(0x0000)
114 << sal_uInt16(0x0001)
115 << sal_uInt16(0x0000);
116 if( sUsername.Len() > 0 )
117 rStrm << sUsername;
120 sal_uInt16 XclExpUserBView::GetNum() const
122 return 0x01A9;
125 std::size_t XclExpUserBView::GetLen() const
127 return 50 + ((sUsername.Len() > 0) ? sUsername.GetSize() : 0);
130 XclExpUserBViewList::XclExpUserBViewList( const ScChangeTrack& rChangeTrack )
132 sal_uInt8 aGUID[ 16 ];
133 bool bValidGUID = false;
134 const std::set<OUString>& rStrColl = rChangeTrack.GetUserCollection();
135 aViews.reserve(rStrColl.size());
136 for (const auto& rStr : rStrColl)
138 lcl_GenerateGUID( aGUID, bValidGUID );
139 aViews.emplace_back( rStr, aGUID );
143 XclExpUserBViewList::~XclExpUserBViewList()
147 void XclExpUserBViewList::Save( XclExpStream& rStrm )
149 for( XclExpUserBView& rView : aViews )
150 rView.Save( rStrm );
153 XclExpUsersViewBegin::XclExpUsersViewBegin( const sal_uInt8* pGUID, sal_uInt32 nTab ) :
154 nCurrTab( nTab )
156 memcpy( aGUID, pGUID, 16 );
159 void XclExpUsersViewBegin::SaveCont( XclExpStream& rStrm )
161 lcl_WriteGUID( rStrm, aGUID );
162 rStrm << nCurrTab
163 << sal_uInt32(100)
164 << sal_uInt32(64)
165 << sal_uInt32(3)
166 << sal_uInt32(0x0000003C)
167 << sal_uInt16(0)
168 << sal_uInt16(3)
169 << sal_uInt16(0)
170 << sal_uInt16(3)
171 << double(0)
172 << double(0)
173 << sal_Int16(-1)
174 << sal_Int16(-1);
177 sal_uInt16 XclExpUsersViewBegin::GetNum() const
179 return 0x01AA;
182 std::size_t XclExpUsersViewBegin::GetLen() const
184 return 64;
187 void XclExpUsersViewEnd::SaveCont( XclExpStream& rStrm )
189 rStrm << sal_uInt16(0x0001);
192 sal_uInt16 XclExpUsersViewEnd::GetNum() const
194 return 0x01AB;
197 std::size_t XclExpUsersViewEnd::GetLen() const
199 return 2;
202 void XclExpChTr0x0191::SaveCont( XclExpStream& rStrm )
204 rStrm << sal_uInt16(0x0000);
207 sal_uInt16 XclExpChTr0x0191::GetNum() const
209 return 0x0191;
212 std::size_t XclExpChTr0x0191::GetLen() const
214 return 2;
217 void XclExpChTr0x0198::SaveCont( XclExpStream& rStrm )
219 rStrm << sal_uInt16(0x0006)
220 << sal_uInt16(0x0000);
223 sal_uInt16 XclExpChTr0x0198::GetNum() const
225 return 0x0198;
228 std::size_t XclExpChTr0x0198::GetLen() const
230 return 4;
233 void XclExpChTr0x0192::SaveCont( XclExpStream& rStrm )
235 rStrm << sal_uInt16( 0x0022 );
236 rStrm.WriteZeroBytes( 510 );
239 sal_uInt16 XclExpChTr0x0192::GetNum() const
241 return 0x0192;
244 std::size_t XclExpChTr0x0192::GetLen() const
246 return 512;
249 void XclExpChTr0x0197::SaveCont( XclExpStream& rStrm )
251 rStrm << sal_uInt16(0x0000);
254 sal_uInt16 XclExpChTr0x0197::GetNum() const
256 return 0x0197;
259 std::size_t XclExpChTr0x0197::GetLen() const
261 return 2;
264 XclExpChTrEmpty::~XclExpChTrEmpty()
268 sal_uInt16 XclExpChTrEmpty::GetNum() const
270 return nRecNum;
273 std::size_t XclExpChTrEmpty::GetLen() const
275 return 0;
278 XclExpChTr0x0195::~XclExpChTr0x0195()
282 void XclExpChTr0x0195::SaveCont( XclExpStream& rStrm )
284 rStrm.WriteZeroBytes( 162 );
287 sal_uInt16 XclExpChTr0x0195::GetNum() const
289 return 0x0195;
292 std::size_t XclExpChTr0x0195::GetLen() const
294 return 162;
297 XclExpChTr0x0194::~XclExpChTr0x0194()
301 void XclExpChTr0x0194::SaveCont( XclExpStream& rStrm )
303 rStrm << sal_uInt32(0);
304 lcl_WriteDateTime( rStrm, aDateTime );
305 rStrm << sal_uInt8(0);
306 lcl_WriteFixedString( rStrm, sUsername, 147 );
309 sal_uInt16 XclExpChTr0x0194::GetNum() const
311 return 0x0194;
314 std::size_t XclExpChTr0x0194::GetLen() const
316 return 162;
319 XclExpChTrHeader::~XclExpChTrHeader()
323 void XclExpChTrHeader::SaveCont( XclExpStream& rStrm )
325 rStrm << sal_uInt16(0x0006)
326 << sal_uInt16(0x0000)
327 << sal_uInt16(0x000D);
328 lcl_WriteGUID( rStrm, aGUID );
329 lcl_WriteGUID( rStrm, aGUID );
330 rStrm << nCount
331 << sal_uInt16(0x0001)
332 << sal_uInt32(0x00000000)
333 << sal_uInt16(0x001E);
336 sal_uInt16 XclExpChTrHeader::GetNum() const
338 return 0x0196;
341 std::size_t XclExpChTrHeader::GetLen() const
343 return 50;
346 void XclExpChTrHeader::SaveXml( XclExpXmlStream& rRevisionHeadersStrm )
348 sax_fastparser::FSHelperPtr pHeaders = rRevisionHeadersStrm.GetCurrentStream();
349 tools::Guid aGuid(aGUID);
350 rRevisionHeadersStrm.WriteAttributes(
351 XML_guid, aGuid.getString(),
352 XML_lastGuid, nullptr, // OOXTODO
353 XML_shared, nullptr, // OOXTODO
354 XML_diskRevisions, nullptr, // OOXTODO
355 XML_history, nullptr, // OOXTODO
356 XML_trackRevisions, nullptr, // OOXTODO
357 XML_exclusive, nullptr, // OOXTODO
358 XML_revisionId, nullptr, // OOXTODO
359 XML_version, nullptr, // OOXTODO
360 XML_keepChangeHistory, nullptr, // OOXTODO
361 XML_protected, nullptr, // OOXTODO
362 XML_preserveHistory, nullptr); // OOXTODO
363 pHeaders->write( ">" );
366 void XclExpXmlChTrHeaders::SetGUID( const sal_uInt8* pGUID )
368 memcpy(maGUID, pGUID, 16);
371 void XclExpXmlChTrHeaders::SaveXml( XclExpXmlStream& rStrm )
373 sax_fastparser::FSHelperPtr pHeaders = rStrm.GetCurrentStream();
375 pHeaders->write("<")->writeId(XML_headers);
377 tools::Guid aGuid(maGUID);
378 rStrm.WriteAttributes(
379 XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
380 FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)),
381 XML_guid, aGuid.getString(),
382 XML_lastGuid, nullptr, // OOXTODO
383 XML_shared, nullptr, // OOXTODO
384 XML_diskRevisions, nullptr, // OOXTODO
385 XML_history, nullptr, // OOXTODO
386 XML_trackRevisions, nullptr, // OOXTODO
387 XML_exclusive, nullptr, // OOXTODO
388 XML_revisionId, nullptr, // OOXTODO
389 XML_version, nullptr, // OOXTODO
390 XML_keepChangeHistory, nullptr, // OOXTODO
391 XML_protected, nullptr, // OOXTODO
392 XML_preserveHistory, nullptr); // OOXTODO
394 pHeaders->write(">");
397 XclExpXmlChTrHeader::XclExpXmlChTrHeader(
398 OUString aUserName, const DateTime& rDateTime, const sal_uInt8* pGUID,
399 sal_Int32 nLogNumber, const XclExpChTrTabIdBuffer& rBuf ) :
400 maUserName(std::move(aUserName)), maDateTime(rDateTime), mnLogNumber(nLogNumber),
401 mnMinAction(0), mnMaxAction(0), mpAuthorIDs(new SvtSecurityMapPersonalInfo)
403 memcpy(maGUID, pGUID, 16);
404 if (rBuf.GetBufferCount())
406 maTabBuffer.resize(rBuf.GetBufferCount());
407 rBuf.GetBufferCopy(maTabBuffer.data());
411 void XclExpXmlChTrHeader::SaveXml( XclExpXmlStream& rStrm )
413 sax_fastparser::FSHelperPtr pHeader = rStrm.GetCurrentStream();
415 pHeader->write("<")->writeId(XML_header);
417 OUString aRelId;
418 sax_fastparser::FSHelperPtr pRevLogStrm = rStrm.CreateOutputStream(
419 XclXmlUtils::GetStreamName("xl/revisions/", "revisionLog", mnLogNumber),
420 XclXmlUtils::GetStreamName(nullptr, "revisionLog", mnLogNumber),
421 rStrm.GetCurrentStream()->getOutputStream(),
422 "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml",
423 CREATE_OFFICEDOC_RELATION_TYPE("revisionLog"),
424 &aRelId);
426 tools::Guid aGuid(maGUID);
427 bool bRemovePersonalInfo
428 = SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo)
429 && !SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::DocWarnKeepRedlineInfo);
430 if (bRemovePersonalInfo)
432 maDateTime = css::util::DateTime(0, 0, 0, 12, 1, 1, 1970, true);
433 maUserName = "Author" + OUString::number(mpAuthorIDs->GetInfoID(maUserName));
435 rStrm.WriteAttributes(
436 XML_guid, aGuid.getString(),
437 XML_dateTime, lcl_DateTimeToOString(maDateTime),
438 XML_userName, maUserName,
439 FSNS(XML_r, XML_id), aRelId);
441 if (mnMinAction)
442 rStrm.WriteAttributes(XML_minRId, OUString::number(mnMinAction));
444 if (mnMaxAction)
445 rStrm.WriteAttributes(XML_maxRId, OUString::number(mnMaxAction));
447 if (!maTabBuffer.empty())
448 // next available sheet index.
449 rStrm.WriteAttributes(XML_maxSheetId, OUString::number(maTabBuffer.back()+1));
451 pHeader->write(">");
453 if (!maTabBuffer.empty())
455 // Write sheet index map.
456 size_t n = maTabBuffer.size();
457 pHeader->startElement(XML_sheetIdMap, XML_count, OString::number(n));
459 for (size_t i = 0; i < n; ++i)
461 pHeader->singleElement(XML_sheetId, XML_val, OString::number(maTabBuffer[i]));
463 pHeader->endElement(XML_sheetIdMap);
466 // Write all revision logs in a separate stream.
468 rStrm.PushStream(pRevLogStrm);
470 pRevLogStrm->write("<")->writeId(XML_revisions);
472 rStrm.WriteAttributes(
473 XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
474 FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)));
476 pRevLogStrm->write(">");
478 for (const auto& rxAction : maActions)
480 rxAction->SaveXml(rStrm);
483 pRevLogStrm->write("</")->writeId(XML_revisions)->write(">");
485 rStrm.PopStream();
487 pHeader->write("</")->writeId(XML_header)->write(">");
490 void XclExpXmlChTrHeader::AppendAction( std::unique_ptr<XclExpChTrAction> pAction )
492 sal_uInt32 nActionNum = pAction->GetActionNumber();
493 if (!mnMinAction || mnMinAction > nActionNum)
494 mnMinAction = nActionNum;
496 if (!mnMaxAction || mnMaxAction < nActionNum)
497 mnMaxAction = nActionNum;
499 maActions.push_back(std::move(pAction));
502 XclExpChTrInfo::XclExpChTrInfo( const OUString& rUsername, const DateTime& rDateTime, const sal_uInt8* pGUID ) :
503 sUsername( rUsername ),
504 aDateTime( rDateTime )
506 memcpy( aGUID, pGUID, 16 );
509 XclExpChTrInfo::~XclExpChTrInfo()
513 void XclExpChTrInfo::SaveCont( XclExpStream& rStrm )
515 rStrm << sal_uInt32(0xFFFFFFFF)
516 << sal_uInt32(0x00000000)
517 << sal_uInt32(0x00000020)
518 << sal_uInt16(0xFFFF);
519 lcl_WriteGUID( rStrm, aGUID );
520 rStrm << sal_uInt16(0x04B0);
521 lcl_WriteFixedString( rStrm, sUsername, 113 );
522 lcl_WriteDateTime( rStrm, aDateTime );
523 rStrm << sal_uInt8(0x0000)
524 << sal_uInt16(0x0002);
527 sal_uInt16 XclExpChTrInfo::GetNum() const
529 return 0x0138;
532 std::size_t XclExpChTrInfo::GetLen() const
534 return 158;
537 XclExpChTrTabIdBuffer::XclExpChTrTabIdBuffer( sal_uInt16 nCount ) :
538 nBufSize( nCount ),
539 nLastId( nCount )
541 pBuffer.reset( new sal_uInt16[ nBufSize ] );
542 memset( pBuffer.get(), 0, sizeof(sal_uInt16) * nBufSize );
543 pLast = pBuffer.get() + nBufSize - 1;
546 XclExpChTrTabIdBuffer::XclExpChTrTabIdBuffer( const XclExpChTrTabIdBuffer& rCopy ) :
547 nBufSize( rCopy.nBufSize ),
548 nLastId( rCopy.nLastId )
550 pBuffer.reset( new sal_uInt16[ nBufSize ] );
551 memcpy( pBuffer.get(), rCopy.pBuffer.get(), sizeof(sal_uInt16) * nBufSize );
552 pLast = pBuffer.get() + nBufSize - 1;
555 XclExpChTrTabIdBuffer::~XclExpChTrTabIdBuffer()
559 void XclExpChTrTabIdBuffer::InitFill( sal_uInt16 nIndex )
561 OSL_ENSURE( nIndex < nLastId, "XclExpChTrTabIdBuffer::Insert - out of range" );
563 sal_uInt16 nFreeCount = 0;
564 for( sal_uInt16* pElem = pBuffer.get(); pElem <= pLast; pElem++ )
566 if( !*pElem )
567 nFreeCount++;
568 if( nFreeCount > nIndex )
570 *pElem = nLastId--;
571 return;
576 void XclExpChTrTabIdBuffer::InitFillup()
578 sal_uInt16 nFreeCount = 1;
579 for( sal_uInt16* pElem = pBuffer.get(); pElem <= pLast; pElem++ )
580 if( !*pElem )
581 *pElem = nFreeCount++;
582 nLastId = nBufSize;
585 sal_uInt16 XclExpChTrTabIdBuffer::GetId( sal_uInt16 nIndex ) const
587 assert(nIndex < nBufSize && "XclExpChTrTabIdBuffer::GetId - out of range");
588 return pBuffer[ nIndex ];
591 bool XclExpChTrTabIdBuffer::HasId( sal_uInt16 nIndex ) const
593 return nIndex < nBufSize;
596 void XclExpChTrTabIdBuffer::Remove()
598 OSL_ENSURE( pBuffer.get() <= pLast, "XclExpChTrTabIdBuffer::Remove - buffer empty" );
599 sal_uInt16* pElem = pBuffer.get();
600 while( (pElem <= pLast) && (*pElem != nLastId) )
601 pElem++;
602 while( pElem < pLast )
604 *pElem = *(pElem + 1);
605 pElem++;
607 pLast--;
608 nLastId--;
611 XclExpChTrTabId::XclExpChTrTabId( const XclExpChTrTabIdBuffer& rBuffer )
612 : nTabCount( rBuffer.GetBufferCount() )
614 pBuffer.reset( new sal_uInt16[ nTabCount ] );
615 rBuffer.GetBufferCopy( pBuffer.get() );
618 XclExpChTrTabId::~XclExpChTrTabId()
620 Clear();
623 void XclExpChTrTabId::Copy( const XclExpChTrTabIdBuffer& rBuffer )
625 Clear();
626 nTabCount = rBuffer.GetBufferCount();
627 pBuffer.reset( new sal_uInt16[ nTabCount ] );
628 rBuffer.GetBufferCopy( pBuffer.get() );
631 void XclExpChTrTabId::SaveCont( XclExpStream& rStrm )
633 rStrm.EnableEncryption();
634 if( pBuffer )
635 rStrm.Write(pBuffer.get(), nTabCount);
636 else
637 for( sal_uInt16 nIndex = 1; nIndex <= nTabCount; nIndex++ )
638 rStrm << nIndex;
641 sal_uInt16 XclExpChTrTabId::GetNum() const
643 return 0x013D;
646 std::size_t XclExpChTrTabId::GetLen() const
648 return nTabCount << 1;
651 // ! does not copy additional actions
652 XclExpChTrAction::XclExpChTrAction( const XclExpChTrAction& rCopy ) :
653 ExcRecord( rCopy ),
654 sUsername( rCopy.sUsername ),
655 aDateTime( rCopy.aDateTime ),
656 nIndex( 0 ),
657 bAccepted( rCopy.bAccepted ),
658 rTabInfo( rCopy.rTabInfo ),
659 rIdBuffer( rCopy.rIdBuffer ),
660 nLength( rCopy.nLength ),
661 nOpCode( rCopy.nOpCode ),
662 bForceInfo( rCopy.bForceInfo )
666 XclExpChTrAction::XclExpChTrAction(
667 const ScChangeAction& rAction,
668 const XclExpRoot& rRoot,
669 const XclExpChTrTabIdBuffer& rTabIdBuffer,
670 sal_uInt16 nNewOpCode ) :
671 sUsername( rAction.GetUser() ),
672 aDateTime( rAction.GetDateTime() ),
673 nIndex( 0 ),
674 bAccepted( rAction.IsAccepted() ),
675 rTabInfo( rRoot.GetTabInfo() ),
676 rIdBuffer( rTabIdBuffer ),
677 nLength( 0 ),
678 nOpCode( nNewOpCode ),
679 bForceInfo( false )
681 aDateTime.SetSec( 0 );
682 aDateTime.SetNanoSec( 0 );
685 XclExpChTrAction::~XclExpChTrAction()
689 void XclExpChTrAction::SetAddAction( XclExpChTrAction* pAction )
691 if( pAddAction )
692 pAddAction->SetAddAction( pAction );
693 else
694 pAddAction.reset( pAction );
697 void XclExpChTrAction::AddDependentContents(
698 const ScChangeAction& rAction,
699 const XclExpRoot& rRoot,
700 const ScChangeTrack& rChangeTrack )
702 ScChangeActionMap aActionMap;
704 rChangeTrack.GetDependents( const_cast<ScChangeAction*>(&rAction), aActionMap );
705 for( const auto& rEntry : aActionMap )
706 if( rEntry.second->GetType() == SC_CAT_CONTENT )
707 SetAddAction( new XclExpChTrCellContent(
708 *static_cast<const ScChangeActionContent*>(rEntry.second), rRoot, rIdBuffer ) );
711 void XclExpChTrAction::SetIndex( sal_uInt32& rIndex )
713 nIndex = rIndex++;
716 void XclExpChTrAction::SaveCont( XclExpStream& rStrm )
718 OSL_ENSURE( nOpCode != EXC_CHTR_OP_UNKNOWN, "XclExpChTrAction::SaveCont - unknown action" );
719 rStrm << nLength
720 << nIndex
721 << nOpCode
722 << static_cast<sal_uInt16>(bAccepted ? EXC_CHTR_ACCEPT : EXC_CHTR_NOTHING);
723 SaveActionData( rStrm );
726 void XclExpChTrAction::PrepareSaveAction( XclExpStream& /*rStrm*/ ) const
730 void XclExpChTrAction::CompleteSaveAction( XclExpStream& /*rStrm*/ ) const
734 void XclExpChTrAction::Save( XclExpStream& rStrm )
736 if (UsesDeletedTab())
738 SAL_WARN("sc", "XclExpChTrAction : unable to export position with tab of EXC_TAB_DELETED");
739 return;
741 PrepareSaveAction( rStrm );
742 ExcRecord::Save( rStrm );
743 if( pAddAction )
744 pAddAction->Save( rStrm );
745 CompleteSaveAction( rStrm );
748 std::size_t XclExpChTrAction::GetLen() const
750 return GetHeaderByteCount() + GetActionByteCount();
753 XclExpChTrData::XclExpChTrData() :
754 mpFormulaCell( nullptr ),
755 fValue( 0.0 ),
756 nRKValue( 0 ),
757 nType( EXC_CHTR_TYPE_EMPTY ),
758 nSize( 0 )
762 XclExpChTrData::~XclExpChTrData()
764 Clear();
767 void XclExpChTrData::Clear()
769 pString.reset();
770 mpFormulaCell = nullptr;
771 mxTokArr.reset();
772 maRefLog.clear();
773 fValue = 0.0;
774 nRKValue = 0;
775 nType = EXC_CHTR_TYPE_EMPTY;
776 nSize = 0;
779 void XclExpChTrData::WriteFormula( XclExpStream& rStrm, const XclExpChTrTabIdBuffer& rTabIdBuffer )
781 OSL_ENSURE( mxTokArr && !mxTokArr->Empty(), "XclExpChTrData::Write - no formula" );
782 rStrm << *mxTokArr;
784 for( const auto& rLogEntry : maRefLog )
786 if( rLogEntry.mpUrl && rLogEntry.mpFirstTab )
788 rStrm << *rLogEntry.mpUrl << sal_uInt8(0x01) << *rLogEntry.mpFirstTab << sal_uInt8(0x02);
790 else
792 bool bSingleTab = rLogEntry.mnFirstXclTab == rLogEntry.mnLastXclTab;
793 rStrm.SetSliceSize( bSingleTab ? 6 : 8 );
794 rStrm << sal_uInt8(0x01) << sal_uInt8(0x02) << sal_uInt8(0x00);
795 rStrm << rTabIdBuffer.GetId( rLogEntry.mnFirstXclTab );
796 if( bSingleTab )
797 rStrm << sal_uInt8(0x02);
798 else
799 rStrm << sal_uInt8(0x00) << rTabIdBuffer.GetId( rLogEntry.mnLastXclTab );
802 rStrm.SetSliceSize( 0 );
803 rStrm << sal_uInt8(0x00);
806 void XclExpChTrData::Write( XclExpStream& rStrm, const XclExpChTrTabIdBuffer& rTabIdBuffer )
808 switch( nType )
810 case EXC_CHTR_TYPE_RK:
811 rStrm << nRKValue;
812 break;
813 case EXC_CHTR_TYPE_DOUBLE:
814 rStrm << fValue;
815 break;
816 case EXC_CHTR_TYPE_STRING:
817 OSL_ENSURE( pString, "XclExpChTrData::Write - no string" );
818 rStrm << *pString;
819 break;
820 case EXC_CHTR_TYPE_FORMULA:
821 WriteFormula( rStrm, rTabIdBuffer );
822 break;
826 static bool lcl_IsDeletedTab(const XclExpChTrTabIdBuffer& rTabIdBuffer, sal_uInt16 nIndex)
828 return !rTabIdBuffer.HasId(nIndex);
831 bool XclExpChTrData::UsesDeletedTab(const XclExpChTrTabIdBuffer& rTabIdBuffer) const
833 if (nType != EXC_CHTR_TYPE_FORMULA)
834 return false;
836 for( const auto& rLogEntry : maRefLog )
838 if (rLogEntry.mpUrl && rLogEntry.mpFirstTab)
839 continue;
840 if (lcl_IsDeletedTab(rTabIdBuffer, rLogEntry.mnFirstXclTab))
841 return true;
842 bool bSingleTab = rLogEntry.mnFirstXclTab == rLogEntry.mnLastXclTab;
843 if (!bSingleTab)
844 continue;
845 if (lcl_IsDeletedTab(rTabIdBuffer, rLogEntry.mnLastXclTab))
846 return true;
848 return false;
851 XclExpChTrCellContent::XclExpChTrCellContent(
852 const ScChangeActionContent& rAction,
853 const XclExpRoot& rRoot,
854 const XclExpChTrTabIdBuffer& rTabIdBuffer ) :
855 XclExpChTrAction( rAction, rRoot, rTabIdBuffer, EXC_CHTR_OP_CELL ),
856 XclExpRoot( rRoot ),
857 aPosition( rAction.GetBigRange().MakeRange( rRoot.GetDoc()).aStart )
859 sal_uInt32 nDummy32;
860 sal_uInt16 nDummy16;
861 GetCellData( rRoot, rAction.GetOldCell(), pOldData, nDummy32, nOldLength );
862 GetCellData( rRoot, rAction.GetNewCell(), pNewData, nLength, nDummy16 );
865 XclExpChTrCellContent::~XclExpChTrCellContent()
867 pOldData.reset();
868 pNewData.reset();
871 void XclExpChTrCellContent::MakeEmptyChTrData( std::unique_ptr<XclExpChTrData>& rpData )
873 if( rpData )
874 rpData->Clear();
875 else
876 rpData.reset( new XclExpChTrData );
879 void XclExpChTrCellContent::GetCellData(
880 const XclExpRoot& rRoot, const ScCellValue& rScCell,
881 std::unique_ptr<XclExpChTrData>& rpData, sal_uInt32& rXclLength1, sal_uInt16& rXclLength2 )
883 MakeEmptyChTrData( rpData );
884 rXclLength1 = 0x0000003A;
885 rXclLength2 = 0x0000;
887 if (rScCell.isEmpty())
889 rpData.reset();
890 return;
893 switch (rScCell.getType())
895 case CELLTYPE_VALUE:
897 rpData->fValue = rScCell.getDouble();
898 if( XclTools::GetRKFromDouble( rpData->nRKValue, rpData->fValue ) )
900 rpData->nType = EXC_CHTR_TYPE_RK;
901 rpData->nSize = 4;
902 rXclLength1 = 0x0000003E;
903 rXclLength2 = 0x0004;
905 else
907 rpData->nType = EXC_CHTR_TYPE_DOUBLE;
908 rpData->nSize = 8;
909 rXclLength1 = 0x00000042;
910 rXclLength2 = 0x0008;
913 break;
914 case CELLTYPE_STRING:
915 case CELLTYPE_EDIT:
917 OUString sCellStr;
918 if (rScCell.getType() == CELLTYPE_STRING)
920 sCellStr = rScCell.getSharedString()->getString();
921 rpData->mpFormattedString = XclExpStringHelper::CreateCellString(
922 rRoot, sCellStr, nullptr);
924 else
926 XclExpHyperlinkHelper aLinkHelper( rRoot, aPosition );
927 if (rScCell.getEditText())
929 sCellStr = ScEditUtil::GetString(*rScCell.getEditText(), &GetDoc());
930 rpData->mpFormattedString = XclExpStringHelper::CreateCellString(
931 rRoot, *rScCell.getEditText(), nullptr, aLinkHelper);
933 else
935 rpData->mpFormattedString = XclExpStringHelper::CreateCellString(
936 rRoot, OUString(), nullptr);
939 rpData->pString.reset( new XclExpString( sCellStr, XclStrFlags::NONE, 32766 ) );
940 rpData->nType = EXC_CHTR_TYPE_STRING;
941 rpData->nSize = 3 + rpData->pString->GetSize();
942 rXclLength1 = 64 + (sCellStr.getLength() << 1);
943 rXclLength2 = 6 + static_cast<sal_uInt16>(sCellStr.getLength() << 1);
945 break;
946 case CELLTYPE_FORMULA:
948 const ScFormulaCell* pFmlCell = rScCell.getFormula();
949 rpData->mpFormulaCell = pFmlCell;
951 const ScTokenArray* pTokenArray = pFmlCell->GetCode();
952 if( pTokenArray )
954 XclExpRefLog& rRefLog = rpData->maRefLog;
955 rpData->mxTokArr = GetFormulaCompiler().CreateFormula(
956 EXC_FMLATYPE_CELL, *pTokenArray, &pFmlCell->aPos, &rRefLog );
957 rpData->nType = EXC_CHTR_TYPE_FORMULA;
958 std::size_t nSize = std::accumulate(rRefLog.begin(), rRefLog.end(),
959 static_cast<std::size_t>(rpData->mxTokArr->GetSize() + 3),
960 [](const std::size_t& rSum, const XclExpRefLogEntry& rLogEntry) {
961 if( rLogEntry.mpUrl && rLogEntry.mpFirstTab )
962 return rSum + rLogEntry.mpUrl->GetSize() + rLogEntry.mpFirstTab->GetSize() + 2;
963 else
964 return rSum + ((rLogEntry.mnFirstXclTab == rLogEntry.mnLastXclTab) ? 6 : 8);
966 rpData->nSize = ::std::min< std::size_t >( nSize, 0xFFFF );
967 rXclLength1 = 0x00000052;
968 rXclLength2 = 0x0018;
971 break;
972 default:;
976 bool XclExpChTrCellContent::UsesDeletedTab() const
978 if (IsDeletedTab(aPosition.Tab()))
979 return true;
980 if (pOldData && pOldData->UsesDeletedTab(rIdBuffer))
981 return true;
982 return pNewData && pNewData->UsesDeletedTab(rIdBuffer);
985 void XclExpChTrCellContent::SaveActionData( XclExpStream& rStrm ) const
987 WriteTabId( rStrm, aPosition.Tab() );
988 rStrm << static_cast<sal_uInt16>((pOldData ? (pOldData->nType << 3) : 0x0000) | (pNewData ? pNewData->nType : 0x0000))
989 << sal_uInt16(0x0000);
990 Write2DAddress( rStrm, aPosition );
991 rStrm << nOldLength
992 << sal_uInt32(0x00000000);
993 if( pOldData )
994 pOldData->Write( rStrm, rIdBuffer );
995 if( pNewData )
996 pNewData->Write( rStrm, rIdBuffer );
999 sal_uInt16 XclExpChTrCellContent::GetNum() const
1001 return 0x013B;
1004 std::size_t XclExpChTrCellContent::GetActionByteCount() const
1006 std::size_t nLen = 16;
1007 if( pOldData )
1008 nLen += pOldData->nSize;
1009 if( pNewData )
1010 nLen += pNewData->nSize;
1011 return nLen;
1014 static const char* lcl_GetType( XclExpChTrData* pData )
1016 switch( pData->nType )
1018 case EXC_CHTR_TYPE_RK:
1019 case EXC_CHTR_TYPE_DOUBLE:
1020 return "n";
1021 case EXC_CHTR_TYPE_FORMULA:
1023 ScFormulaCell* pFormulaCell = const_cast< ScFormulaCell* >( pData->mpFormulaCell );
1024 const char* sType;
1025 OUString sValue;
1026 XclXmlUtils::GetFormulaTypeAndValue( *pFormulaCell, sType, sValue );
1027 return sType;
1029 break;
1030 case EXC_CHTR_TYPE_STRING:
1031 return "inlineStr";
1032 default:
1033 break;
1035 return "*unknown*";
1038 static void lcl_WriteCell( XclExpXmlStream& rStrm, sal_Int32 nElement, const ScAddress& rPosition, XclExpChTrData* pData )
1040 sax_fastparser::FSHelperPtr pStream = rStrm.GetCurrentStream();
1042 pStream->startElement(nElement,
1043 XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), ScRange(rPosition)),
1044 XML_s, nullptr, // OOXTODO: not supported
1045 XML_t, lcl_GetType(pData),
1046 XML_cm, nullptr, // OOXTODO: not supported
1047 XML_vm, nullptr, // OOXTODO: not supported
1048 XML_ph, nullptr); // OOXTODO: not supported
1049 switch( pData->nType )
1051 case EXC_CHTR_TYPE_RK:
1052 case EXC_CHTR_TYPE_DOUBLE:
1053 pStream->startElement(XML_v);
1054 pStream->write( pData->fValue );
1055 pStream->endElement( XML_v );
1056 break;
1057 case EXC_CHTR_TYPE_FORMULA:
1058 pStream->startElement( XML_f
1059 // OOXTODO: other attributes? see XclExpFormulaCell::SaveXml()
1061 pStream->writeEscaped( XclXmlUtils::ToOUString(
1062 rStrm.GetRoot().GetCompileFormulaContext(),
1063 pData->mpFormulaCell->aPos, pData->mpFormulaCell->GetCode()));
1064 pStream->endElement( XML_f );
1065 break;
1066 case EXC_CHTR_TYPE_STRING:
1067 pStream->startElement(XML_is);
1068 if( pData->mpFormattedString )
1069 pData->mpFormattedString->WriteXml( rStrm );
1070 else
1071 pData->pString->WriteXml( rStrm );
1072 pStream->endElement( XML_is );
1073 break;
1074 default:
1075 // ignore
1076 break;
1078 pStream->endElement( nElement );
1081 void XclExpChTrCellContent::SaveXml( XclExpXmlStream& rRevisionLogStrm )
1083 if (IsDeletedTab(aPosition.Tab()))
1085 // seen on attempt to export tdf66241-1.ods to xlsx
1086 SAL_WARN("sc", "XclExpChTrCellContent: unable to export position with tab of EXC_TAB_DELETED");
1087 return;
1090 sax_fastparser::FSHelperPtr pStream = rRevisionLogStrm.GetCurrentStream();
1091 pStream->startElement( XML_rcc,
1092 XML_rId, OString::number(GetActionNumber()),
1093 XML_ua, ToPsz( GetAccepted () ), // OOXTODO? bAccepted == ua or ra; not sure.
1094 XML_ra, nullptr, // OOXTODO: RRD.fUndoAction? Or RRD.fAccepted?
1095 XML_sId, OString::number(GetTabId(aPosition.Tab())),
1096 XML_odxf, nullptr, // OOXTODO: not supported
1097 XML_xfDxf, nullptr, // OOXTODO: not supported
1098 XML_s, nullptr, // OOXTODO: not supported
1099 XML_dxf, nullptr, // OOXTODO: not supported
1100 XML_numFmtId, nullptr, // OOXTODO: not supported
1101 XML_quotePrefix, nullptr, // OOXTODO: not supported
1102 XML_oldQuotePrefix, nullptr, // OOXTODO: not supported
1103 XML_ph, nullptr, // OOXTODO: not supported
1104 XML_oldPh, nullptr, // OOXTODO: not supported
1105 XML_endOfListFormulaUpdate, nullptr); // OOXTODO: not supported
1106 if( pOldData )
1108 lcl_WriteCell( rRevisionLogStrm, XML_oc, aPosition, pOldData.get() );
1109 if (!pNewData)
1111 pStream->singleElement(XML_nc, XML_r, XclXmlUtils::ToOString(rRevisionLogStrm.GetRoot().GetDoc(), ScRange(aPosition)));
1114 if( pNewData )
1116 lcl_WriteCell( rRevisionLogStrm, XML_nc, aPosition, pNewData.get() );
1118 // OOXTODO: XML_odxf, XML_ndxf, XML_extLst elements
1119 pStream->endElement( XML_rcc );
1122 XclExpChTrInsert::XclExpChTrInsert( const XclExpChTrInsert& rCopy ) :
1123 XclExpChTrAction(rCopy),
1124 mbEndOfList(rCopy.mbEndOfList),
1125 aRange(rCopy.aRange) {}
1127 XclExpChTrInsert::XclExpChTrInsert(
1128 const ScChangeAction& rAction,
1129 const XclExpRoot& rRoot,
1130 const XclExpChTrTabIdBuffer& rTabIdBuffer,
1131 const ScChangeTrack& rChangeTrack ) :
1132 XclExpChTrAction( rAction, rRoot, rTabIdBuffer ),
1133 mbEndOfList(false),
1134 aRange( rAction.GetBigRange().MakeRange( rRoot.GetDoc()) )
1136 nLength = 0x00000030;
1137 switch( rAction.GetType() )
1139 case SC_CAT_INSERT_COLS: nOpCode = EXC_CHTR_OP_INSCOL; break;
1140 case SC_CAT_INSERT_ROWS:
1142 const ScChangeActionIns& rIns = static_cast<const ScChangeActionIns&>(rAction);
1143 mbEndOfList = rIns.IsEndOfList();
1144 nOpCode = EXC_CHTR_OP_INSROW;
1146 break;
1147 case SC_CAT_DELETE_COLS: nOpCode = EXC_CHTR_OP_DELCOL; break;
1148 case SC_CAT_DELETE_ROWS: nOpCode = EXC_CHTR_OP_DELROW; break;
1149 default:
1150 OSL_FAIL( "XclExpChTrInsert::XclExpChTrInsert - unknown action" );
1153 if( nOpCode & EXC_CHTR_OP_COLFLAG )
1155 aRange.aStart.SetRow( 0 );
1156 aRange.aEnd.SetRow( rRoot.GetXclMaxPos().Row() );
1158 else
1160 aRange.aStart.SetCol( 0 );
1161 aRange.aEnd.SetCol( rRoot.GetXclMaxPos().Col() );
1164 if( nOpCode & EXC_CHTR_OP_DELFLAG )
1166 SetAddAction( new XclExpChTr0x014A( *this ) );
1167 AddDependentContents( rAction, rRoot, rChangeTrack );
1171 XclExpChTrInsert::~XclExpChTrInsert()
1175 bool XclExpChTrInsert::UsesDeletedTab() const
1177 return IsDeletedTab(aRange.aStart.Tab());
1180 void XclExpChTrInsert::SaveActionData( XclExpStream& rStrm ) const
1182 WriteTabId( rStrm, aRange.aStart.Tab() );
1183 sal_uInt16 nFlagVal = mbEndOfList ? 0x0001 : 0x0000;
1184 rStrm << nFlagVal;
1185 Write2DRange( rStrm, aRange );
1186 rStrm << sal_uInt32(0x00000000);
1189 void XclExpChTrInsert::PrepareSaveAction( XclExpStream& rStrm ) const
1191 if( (nOpCode == EXC_CHTR_OP_DELROW) || (nOpCode == EXC_CHTR_OP_DELCOL) )
1192 XclExpChTrEmpty( 0x0150 ).Save( rStrm );
1195 void XclExpChTrInsert::CompleteSaveAction( XclExpStream& rStrm ) const
1197 if( (nOpCode == EXC_CHTR_OP_DELROW) || (nOpCode == EXC_CHTR_OP_DELCOL) )
1198 XclExpChTrEmpty( 0x0151 ).Save( rStrm );
1201 sal_uInt16 XclExpChTrInsert::GetNum() const
1203 return 0x0137;
1206 std::size_t XclExpChTrInsert::GetActionByteCount() const
1208 return 16;
1211 static const char* lcl_GetAction( sal_uInt16 nOpCode )
1213 switch( nOpCode )
1215 case EXC_CHTR_OP_INSCOL: return "insertCol";
1216 case EXC_CHTR_OP_INSROW: return "insertRow";
1217 case EXC_CHTR_OP_DELCOL: return "deleteCol";
1218 case EXC_CHTR_OP_DELROW: return "deleteRow";
1219 default: return "*unknown*";
1223 void XclExpChTrInsert::SaveXml( XclExpXmlStream& rRevisionLogStrm )
1225 if (IsDeletedTab(aRange.aStart.Tab()))
1227 // seen on attempt to export tdf66241-1.ods to xlsx
1228 SAL_WARN("sc", "XclExpChTrCellContent: unable to export position with tab of EXC_TAB_DELETED");
1229 return;
1232 sax_fastparser::FSHelperPtr pStream = rRevisionLogStrm.GetCurrentStream();
1233 pStream->startElement( XML_rrc,
1234 XML_rId, OString::number(GetActionNumber()),
1235 XML_ua, ToPsz( GetAccepted () ), // OOXTODO? bAccepted == ua or ra; not sure.
1236 XML_ra, nullptr, // OOXTODO: RRD.fUndoAction? Or RRD.fAccepted?
1237 XML_sId, OString::number(GetTabId(aRange.aStart.Tab())),
1238 XML_eol, ToPsz10(mbEndOfList),
1239 XML_ref, XclXmlUtils::ToOString(rRevisionLogStrm.GetRoot().GetDoc(), aRange),
1240 XML_action, lcl_GetAction( nOpCode ),
1241 XML_edge, nullptr); // OOXTODO: ???
1243 // OOXTODO: does this handle XML_rfmt, XML_undo?
1244 XclExpChTrAction* pAction = GetAddAction();
1245 while( pAction != nullptr )
1247 pAction->SaveXml( rRevisionLogStrm );
1248 pAction = pAction->GetAddAction();
1250 pStream->endElement( XML_rrc );
1253 XclExpChTrInsertTab::XclExpChTrInsertTab(
1254 const ScChangeAction& rAction,
1255 const XclExpRoot& rRoot,
1256 const XclExpChTrTabIdBuffer& rTabIdBuffer ) :
1257 XclExpChTrAction( rAction, rRoot, rTabIdBuffer, EXC_CHTR_OP_INSTAB ),
1258 XclExpRoot( rRoot ),
1259 nTab( static_cast<SCTAB>(rAction.GetBigRange().aStart.Tab()) )
1261 nLength = 0x0000021C;
1262 bForceInfo = true;
1265 XclExpChTrInsertTab::~XclExpChTrInsertTab()
1269 bool XclExpChTrInsertTab::UsesDeletedTab() const
1271 return IsDeletedTab(nTab);
1274 void XclExpChTrInsertTab::SaveActionData( XclExpStream& rStrm ) const
1276 WriteTabId( rStrm, nTab );
1277 rStrm << sal_uInt32( 0 );
1278 lcl_WriteFixedString( rStrm, XclExpString( GetTabInfo().GetScTabName( nTab ) ), 127 );
1279 lcl_WriteDateTime( rStrm, GetDateTime() );
1280 rStrm.WriteZeroBytes( 133 );
1283 sal_uInt16 XclExpChTrInsertTab::GetNum() const
1285 return 0x014D;
1288 std::size_t XclExpChTrInsertTab::GetActionByteCount() const
1290 return 276;
1293 void XclExpChTrInsertTab::SaveXml( XclExpXmlStream& rStrm )
1295 sax_fastparser::FSHelperPtr pStream = rStrm.GetCurrentStream();
1296 pStream->singleElement( XML_ris,
1297 XML_rId, OString::number(GetActionNumber()),
1298 XML_ua, ToPsz( GetAccepted () ), // OOXTODO? bAccepted == ua or ra; not sure.
1299 XML_ra, nullptr, // OOXTODO: RRD.fUndoAction? Or RRD.fAccepted?
1300 XML_sheetId, OString::number(GetTabId(nTab)),
1301 XML_name, GetTabInfo().GetScTabName(nTab).toUtf8(),
1302 XML_sheetPosition, OString::number(nTab) );
1305 XclExpChTrMoveRange::XclExpChTrMoveRange(
1306 const ScChangeActionMove& rAction,
1307 const XclExpRoot& rRoot,
1308 const XclExpChTrTabIdBuffer& rTabIdBuffer,
1309 const ScChangeTrack& rChangeTrack ) :
1310 XclExpChTrAction( rAction, rRoot, rTabIdBuffer, EXC_CHTR_OP_MOVE ),
1311 aDestRange( rAction.GetBigRange().MakeRange( rRoot.GetDoc() ) )
1313 nLength = 0x00000042;
1314 aSourceRange = aDestRange;
1315 sal_Int32 nDCols, nDRows, nDTabs;
1316 rAction.GetDelta( nDCols, nDRows, nDTabs );
1317 aSourceRange.aStart.IncRow( static_cast<SCROW>(-nDRows) );
1318 aSourceRange.aStart.IncCol( static_cast<SCCOL>(-nDCols) );
1319 aSourceRange.aStart.IncTab( static_cast<SCTAB>(-nDTabs) );
1320 aSourceRange.aEnd.IncRow( static_cast<SCROW>(-nDRows) );
1321 aSourceRange.aEnd.IncCol( static_cast<SCCOL>(-nDCols) );
1322 aSourceRange.aEnd.IncTab( static_cast<SCTAB>(-nDTabs) );
1323 AddDependentContents( rAction, rRoot, rChangeTrack );
1326 XclExpChTrMoveRange::~XclExpChTrMoveRange()
1330 bool XclExpChTrMoveRange::UsesDeletedTab() const
1332 return IsDeletedTab(aDestRange.aStart.Tab()) ||
1333 IsDeletedTab(aSourceRange.aStart.Tab());
1336 void XclExpChTrMoveRange::SaveActionData( XclExpStream& rStrm ) const
1338 WriteTabId( rStrm, aDestRange.aStart.Tab() );
1339 Write2DRange( rStrm, aSourceRange );
1340 Write2DRange( rStrm, aDestRange );
1341 WriteTabId( rStrm, aSourceRange.aStart.Tab() );
1342 rStrm << sal_uInt32(0x00000000);
1345 void XclExpChTrMoveRange::PrepareSaveAction( XclExpStream& rStrm ) const
1347 XclExpChTrEmpty( 0x014E ).Save( rStrm );
1350 void XclExpChTrMoveRange::CompleteSaveAction( XclExpStream& rStrm ) const
1352 XclExpChTrEmpty( 0x014F ).Save( rStrm );
1355 sal_uInt16 XclExpChTrMoveRange::GetNum() const
1357 return 0x0140;
1360 std::size_t XclExpChTrMoveRange::GetActionByteCount() const
1362 return 24;
1365 void XclExpChTrMoveRange::SaveXml( XclExpXmlStream& rRevisionLogStrm )
1367 if (IsDeletedTab(aDestRange.aStart.Tab()) || IsDeletedTab(aSourceRange.aStart.Tab()))
1369 // seen on attempt to export tdf66241-1.ods to xlsx
1370 SAL_WARN("sc", "XclExpChTrCellContent: unable to export position with tab of EXC_TAB_DELETED");
1371 return;
1374 sax_fastparser::FSHelperPtr pStream = rRevisionLogStrm.GetCurrentStream();
1376 pStream->startElement( XML_rm,
1377 XML_rId, OString::number(GetActionNumber()),
1378 XML_ua, ToPsz( GetAccepted () ), // OOXTODO? bAccepted == ua or ra; not sure.
1379 XML_ra, nullptr, // OOXTODO: RRD.fUndoAction? Or RRD.fAccepted?
1380 XML_sheetId, OString::number(GetTabId(aDestRange.aStart.Tab())),
1381 XML_source, XclXmlUtils::ToOString(rRevisionLogStrm.GetRoot().GetDoc(), aSourceRange),
1382 XML_destination, XclXmlUtils::ToOString(rRevisionLogStrm.GetRoot().GetDoc(), aDestRange),
1383 XML_sourceSheetId, OString::number(GetTabId(aSourceRange.aStart.Tab())) );
1384 // OOXTODO: does this handle XML_rfmt, XML_undo?
1385 XclExpChTrAction* pAction = GetAddAction();
1386 while( pAction != nullptr )
1388 pAction->SaveXml( rRevisionLogStrm );
1389 pAction = pAction->GetAddAction();
1391 pStream->endElement( XML_rm );
1394 XclExpChTr0x014A::XclExpChTr0x014A( const XclExpChTrInsert& rAction ) :
1395 XclExpChTrInsert( rAction )
1397 nLength = 0x00000026;
1398 nOpCode = EXC_CHTR_OP_FORMAT;
1401 XclExpChTr0x014A::~XclExpChTr0x014A()
1405 void XclExpChTr0x014A::SaveActionData( XclExpStream& rStrm ) const
1407 WriteTabId( rStrm, aRange.aStart.Tab() );
1408 rStrm << sal_uInt16(0x0003)
1409 << sal_uInt16(0x0001);
1410 Write2DRange( rStrm, aRange );
1413 sal_uInt16 XclExpChTr0x014A::GetNum() const
1415 return 0x014A;
1418 std::size_t XclExpChTr0x014A::GetActionByteCount() const
1420 return 14;
1423 void XclExpChTr0x014A::SaveXml( XclExpXmlStream& rStrm )
1425 sax_fastparser::FSHelperPtr pStream = rStrm.GetCurrentStream();
1427 pStream->startElement( XML_rfmt,
1428 XML_sheetId, OString::number(GetTabId(aRange.aStart.Tab())),
1429 XML_xfDxf, nullptr, // OOXTODO: not supported
1430 XML_s, nullptr, // OOXTODO: style
1431 XML_sqref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aRange),
1432 XML_start, nullptr, // OOXTODO: for string changes
1433 XML_length, nullptr); // OOXTODO: for string changes
1434 // OOXTODO: XML_dxf, XML_extLst
1436 pStream->endElement( XML_rfmt );
1439 std::size_t ExcXmlRecord::GetLen() const
1441 return 0;
1444 sal_uInt16 ExcXmlRecord::GetNum() const
1446 return 0;
1449 void ExcXmlRecord::Save( XclExpStream& )
1451 // Do nothing; ignored for BIFF output.
1454 namespace {
1456 class EndXmlElement : public ExcXmlRecord
1458 sal_Int32 mnElement;
1459 public:
1460 explicit EndXmlElement( sal_Int32 nElement ) : mnElement( nElement) {}
1461 virtual void SaveXml( XclExpXmlStream& rStrm ) override;
1466 void EndXmlElement::SaveXml( XclExpXmlStream& rStrm )
1468 sax_fastparser::FSHelperPtr pStream = rStrm.GetCurrentStream();
1469 pStream->write("</")->writeId(mnElement)->write(">");
1472 XclExpChangeTrack::XclExpChangeTrack( const XclExpRoot& rRoot ) :
1473 XclExpRoot( rRoot ),
1474 pTabIdBuffer( nullptr )
1476 OSL_ENSURE( GetOldRoot().pTabId, "XclExpChangeTrack::XclExpChangeTrack - root data incomplete" );
1477 if( !GetOldRoot().pTabId )
1478 return;
1480 ScChangeTrack* pTempChangeTrack = CreateTempChangeTrack();
1481 if (!pTempChangeTrack)
1482 return;
1484 pTabIdBuffer = new XclExpChTrTabIdBuffer( GetTabInfo().GetXclTabCount() );
1485 maBuffers.push_back( std::unique_ptr<XclExpChTrTabIdBuffer>(pTabIdBuffer) );
1487 // calculate final table order (tab id list)
1488 const ScChangeAction* pScAction;
1489 for( pScAction = pTempChangeTrack->GetLast(); pScAction; pScAction = pScAction->GetPrev() )
1491 if( pScAction->GetType() == SC_CAT_INSERT_TABS )
1493 SCTAB nScTab = static_cast< SCTAB >( pScAction->GetBigRange().aStart.Tab() );
1494 pTabIdBuffer->InitFill( GetTabInfo().GetXclTab( nScTab ) );
1497 pTabIdBuffer->InitFillup();
1498 GetOldRoot().pTabId->Copy( *pTabIdBuffer );
1500 // get actions in reverse order
1501 pScAction = pTempChangeTrack->GetLast();
1502 while( pScAction )
1504 PushActionRecord( *pScAction );
1505 const ScChangeAction* pPrevAction = pScAction->GetPrev();
1506 pScAction = pPrevAction;
1509 // build record list
1510 if (GetOutput() == EXC_OUTPUT_BINARY)
1512 XclExpChTrHeader* pHeader = new XclExpChTrHeader; // header record for last GUID
1513 maRecList.push_back( std::unique_ptr<ExcRecord>(pHeader) );
1514 maRecList.push_back( std::unique_ptr<ExcRecord>( new XclExpChTr0x0195 ) );
1515 maRecList.push_back( std::unique_ptr<ExcRecord>( new XclExpChTr0x0194( *pTempChangeTrack ) ) );
1517 OUString sLastUsername;
1518 DateTime aLastDateTime( DateTime::EMPTY );
1519 sal_uInt32 nIndex = 1;
1520 sal_uInt8 aGUID[ 16 ]; // GUID for action info records
1521 bool bValidGUID = false;
1522 while( !aActionStack.empty() )
1524 XclExpChTrAction* pAction = aActionStack.top();
1525 aActionStack.pop();
1527 if( (nIndex == 1) || pAction->ForceInfoRecord() ||
1528 (pAction->GetUsername() != sLastUsername) ||
1529 (pAction->GetDateTime() != aLastDateTime) )
1531 lcl_GenerateGUID( aGUID, bValidGUID );
1532 sLastUsername = pAction->GetUsername();
1533 aLastDateTime = pAction->GetDateTime();
1535 maRecList.push_back( std::unique_ptr<ExcRecord>(new XclExpChTrInfo(sLastUsername, aLastDateTime, aGUID)) );
1536 maRecList.push_back( std::unique_ptr<ExcRecord>(new XclExpChTrTabId(pAction->GetTabIdBuffer())) );
1537 pHeader->SetGUID( aGUID );
1539 pAction->SetIndex( nIndex );
1540 maRecList.push_back( std::unique_ptr<ExcRecord>(pAction) );
1543 pHeader->SetGUID( aGUID );
1544 pHeader->SetCount( nIndex - 1 );
1545 maRecList.push_back( std::unique_ptr<ExcRecord>(new ExcEof) );
1547 else
1549 XclExpXmlChTrHeaders* pHeaders = new XclExpXmlChTrHeaders;
1550 maRecList.push_back( std::unique_ptr<ExcRecord>(pHeaders));
1552 OUString sLastUsername;
1553 DateTime aLastDateTime(DateTime::EMPTY);
1554 sal_uInt32 nIndex = 1;
1555 sal_Int32 nLogNumber = 1;
1556 XclExpXmlChTrHeader* pCurHeader = nullptr;
1557 sal_uInt8 aGUID[ 16 ]; // GUID for action info records
1558 bool bValidGUID = false;
1560 while (!aActionStack.empty())
1562 XclExpChTrAction* pAction = aActionStack.top();
1563 aActionStack.pop();
1565 if( (nIndex == 1) || pAction->ForceInfoRecord() ||
1566 (pAction->GetUsername() != sLastUsername) ||
1567 (pAction->GetDateTime() != aLastDateTime) )
1569 lcl_GenerateGUID( aGUID, bValidGUID );
1570 sLastUsername = pAction->GetUsername();
1571 aLastDateTime = pAction->GetDateTime();
1573 pCurHeader = new XclExpXmlChTrHeader(sLastUsername, aLastDateTime, aGUID, nLogNumber, pAction->GetTabIdBuffer());
1574 maRecList.push_back( std::unique_ptr<ExcRecord>(pCurHeader));
1575 nLogNumber++;
1576 pHeaders->SetGUID(aGUID);
1578 pAction->SetIndex(nIndex);
1579 pCurHeader->AppendAction(std::unique_ptr<XclExpChTrAction>(pAction));
1582 pHeaders->SetGUID(aGUID);
1583 maRecList.push_back( std::unique_ptr<ExcRecord>(new EndXmlElement(XML_headers)));
1587 XclExpChangeTrack::~XclExpChangeTrack()
1589 while( !aActionStack.empty() )
1591 delete aActionStack.top();
1592 aActionStack.pop();
1596 ScChangeTrack* XclExpChangeTrack::CreateTempChangeTrack()
1598 // get original change track
1599 ScChangeTrack* pOrigChangeTrack = GetDoc().GetChangeTrack();
1600 OSL_ENSURE( pOrigChangeTrack, "XclExpChangeTrack::CreateTempChangeTrack - no change track data" );
1601 if( !pOrigChangeTrack )
1602 return nullptr;
1604 assert(!xTempDoc);
1605 // create empty document
1606 xTempDoc.reset(new ScDocument);
1608 // adjust table count
1609 SCTAB nOrigCount = GetDoc().GetTableCount();
1610 OUString sTabName;
1611 for( sal_Int32 nIndex = 0; nIndex < nOrigCount; nIndex++ )
1613 xTempDoc->CreateValidTabName(sTabName);
1614 xTempDoc->InsertTab(SC_TAB_APPEND, sTabName);
1616 OSL_ENSURE(nOrigCount == xTempDoc->GetTableCount(),
1617 "XclExpChangeTrack::CreateTempChangeTrack - table count mismatch");
1618 if(nOrigCount != xTempDoc->GetTableCount())
1619 return nullptr;
1621 return pOrigChangeTrack->Clone(xTempDoc.get());
1624 void XclExpChangeTrack::PushActionRecord( const ScChangeAction& rAction )
1626 XclExpChTrAction* pXclAction = nullptr;
1627 ScChangeTrack* pTempChangeTrack = xTempDoc->GetChangeTrack();
1628 switch( rAction.GetType() )
1630 case SC_CAT_CONTENT:
1631 pXclAction = new XclExpChTrCellContent( static_cast<const ScChangeActionContent&>(rAction), GetRoot(), *pTabIdBuffer );
1632 break;
1633 case SC_CAT_INSERT_ROWS:
1634 case SC_CAT_INSERT_COLS:
1635 case SC_CAT_DELETE_ROWS:
1636 case SC_CAT_DELETE_COLS:
1637 if (pTempChangeTrack)
1638 pXclAction = new XclExpChTrInsert( rAction, GetRoot(), *pTabIdBuffer, *pTempChangeTrack );
1639 break;
1640 case SC_CAT_INSERT_TABS:
1642 pXclAction = new XclExpChTrInsertTab( rAction, GetRoot(), *pTabIdBuffer );
1643 XclExpChTrTabIdBuffer* pNewBuffer = new XclExpChTrTabIdBuffer( *pTabIdBuffer );
1644 pNewBuffer->Remove();
1645 maBuffers.push_back( std::unique_ptr<XclExpChTrTabIdBuffer>(pNewBuffer) );
1646 pTabIdBuffer = pNewBuffer;
1648 break;
1649 case SC_CAT_MOVE:
1650 if (pTempChangeTrack)
1651 pXclAction = new XclExpChTrMoveRange( static_cast<const ScChangeActionMove&>(rAction), GetRoot(), *pTabIdBuffer, *pTempChangeTrack );
1652 break;
1653 default:;
1655 if( pXclAction )
1656 aActionStack.push( pXclAction );
1659 bool XclExpChangeTrack::WriteUserNamesStream()
1661 bool bRet = false;
1662 rtl::Reference<SotStorageStream> xSvStrm = OpenStream(EXC_STREAM_USERNAMES);
1663 OSL_ENSURE( xSvStrm.is(), "XclExpChangeTrack::WriteUserNamesStream - no stream" );
1664 if( xSvStrm.is() )
1666 XclExpStream aXclStrm( *xSvStrm, GetRoot() );
1667 XclExpChTr0x0191().Save( aXclStrm );
1668 XclExpChTr0x0198().Save( aXclStrm );
1669 XclExpChTr0x0192().Save( aXclStrm );
1670 XclExpChTr0x0197().Save( aXclStrm );
1671 xSvStrm->Commit();
1672 bRet = true;
1674 return bRet;
1677 void XclExpChangeTrack::Write()
1679 if (maRecList.empty())
1680 return;
1682 if( !WriteUserNamesStream() )
1683 return;
1685 rtl::Reference<SotStorageStream> xSvStrm = OpenStream(EXC_STREAM_REVLOG);
1686 OSL_ENSURE( xSvStrm.is(), "XclExpChangeTrack::Write - no stream" );
1687 if( xSvStrm.is() )
1689 XclExpStream aXclStrm( *xSvStrm, GetRoot(), EXC_MAXRECSIZE_BIFF8 + 8 );
1691 for(const auto& rxRec : maRecList)
1692 rxRec->Save(aXclStrm);
1694 xSvStrm->Commit();
1698 static void lcl_WriteUserNamesXml( XclExpXmlStream& rWorkbookStrm )
1700 sax_fastparser::FSHelperPtr pUserNames = rWorkbookStrm.CreateOutputStream(
1701 u"xl/revisions/userNames.xml"_ustr,
1702 u"revisions/userNames.xml",
1703 rWorkbookStrm.GetCurrentStream()->getOutputStream(),
1704 "application/vnd.openxmlformats-officedocument.spreadsheetml.userNames+xml",
1705 CREATE_OFFICEDOC_RELATION_TYPE("usernames"));
1706 pUserNames->startElement( XML_users,
1707 XML_xmlns, rWorkbookStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
1708 FSNS( XML_xmlns, XML_r ), rWorkbookStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
1709 XML_count, "0" );
1710 // OOXTODO: XML_userinfo elements for each user editing the file
1711 // Doesn't seem to be supported by .xls output either (based on
1712 // contents of XclExpChangeTrack::WriteUserNamesStream()).
1713 pUserNames->endElement( XML_users );
1716 void XclExpChangeTrack::WriteXml( XclExpXmlStream& rWorkbookStrm )
1718 if (maRecList.empty())
1719 return;
1721 lcl_WriteUserNamesXml( rWorkbookStrm );
1723 sax_fastparser::FSHelperPtr pRevisionHeaders = rWorkbookStrm.CreateOutputStream(
1724 u"xl/revisions/revisionHeaders.xml"_ustr,
1725 u"revisions/revisionHeaders.xml",
1726 rWorkbookStrm.GetCurrentStream()->getOutputStream(),
1727 "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionHeaders+xml",
1728 CREATE_OFFICEDOC_RELATION_TYPE("revisionHeaders"));
1729 // OOXTODO: XML_userinfo elements for each user editing the file
1730 // Doesn't seem to be supported by .xls output either (based on
1731 // contents of XclExpChangeTrack::WriteUserNamesStream()).
1732 rWorkbookStrm.PushStream( pRevisionHeaders );
1734 for (const auto& rxRec : maRecList)
1735 rxRec->SaveXml(rWorkbookStrm);
1737 rWorkbookStrm.PopStream();
1740 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */