[Libreoffice-commits] core.git: 3 commits - sc/inc sc/Library_scfilt.mk sc/qa sc/source

Kohei Yoshida kohei.yoshida at collabora.com
Fri Jul 18 10:14:06 PDT 2014


 sc/Library_scfilt.mk                                        |    1 
 sc/inc/chgtrack.hxx                                         |    2 
 sc/qa/unit/data/xlsx/track-changes/simple-cell-changes.xlsx |binary
 sc/qa/unit/subsequent_export-test.cxx                       |   28 
 sc/source/filter/excel/xestream.cxx                         |    7 
 sc/source/filter/inc/XclExpChangeTrack.hxx                  |   70 +
 sc/source/filter/inc/revisionfragment.hxx                   |   79 ++
 sc/source/filter/inc/xestream.hxx                           |   10 
 sc/source/filter/oox/revisionfragment.cxx                   |  462 ++++++++++++
 sc/source/filter/oox/workbookfragment.cxx                   |   11 
 sc/source/filter/xcl97/XclExpChangeTrack.cxx                |  313 +++++---
 11 files changed, 853 insertions(+), 130 deletions(-)

New commits:
commit 4edbaa38af4025564c0154985705c2785b7ff345
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Fri Jul 18 11:30:07 2014 -0400

    bnc#885548: Add test for import / export of revisions for xlsx.
    
    It's the same test as with the xls variant.
    
    Change-Id: Ice3d3c34213cc521e2782ef0f98d28d135c72fbe

diff --git a/sc/qa/unit/data/xlsx/track-changes/simple-cell-changes.xlsx b/sc/qa/unit/data/xlsx/track-changes/simple-cell-changes.xlsx
new file mode 100755
index 0000000..29e8027
Binary files /dev/null and b/sc/qa/unit/data/xlsx/track-changes/simple-cell-changes.xlsx differ
diff --git a/sc/qa/unit/subsequent_export-test.cxx b/sc/qa/unit/subsequent_export-test.cxx
index 270c7dc..f91a2de 100644
--- a/sc/qa/unit/subsequent_export-test.cxx
+++ b/sc/qa/unit/subsequent_export-test.cxx
@@ -93,7 +93,7 @@ public:
 
     void testCellBordersXLS();
     void testCellBordersXLSX();
-    void testTrackChangesSimpleXLS();
+    void testTrackChangesSimpleXLSX();
     void testSheetTabColorsXLSX();
 
     void testSharedFormulaExportXLS();
@@ -132,7 +132,7 @@ public:
     CPPUNIT_TEST(testSheetProtectionXLSX);
     CPPUNIT_TEST(testCellBordersXLS);
     CPPUNIT_TEST(testCellBordersXLSX);
-    CPPUNIT_TEST(testTrackChangesSimpleXLS);
+    CPPUNIT_TEST(testTrackChangesSimpleXLSX);
     CPPUNIT_TEST(testSheetTabColorsXLSX);
     CPPUNIT_TEST(testSharedFormulaExportXLS);
     CPPUNIT_TEST(testSharedFormulaExportXLSX);
@@ -1327,7 +1327,7 @@ OUString toString( const ScBigRange& rRange )
     return aBuf.makeStringAndClear();
 }
 
-void ScExportTest::testTrackChangesSimpleXLS()
+void ScExportTest::testTrackChangesSimpleXLSX()
 {
     struct CheckItem
     {
@@ -1450,17 +1450,19 @@ void ScExportTest::testTrackChangesSimpleXLS()
 
     } aTest;
 
+    // First, test the xls variant.
+
     ScDocShellRef xDocSh = loadDoc("track-changes/simple-cell-changes.", XLS);
     CPPUNIT_ASSERT(xDocSh.Is());
     ScDocument* pDoc = &xDocSh->GetDocument();
     bool bGood = aTest.check(*pDoc);
-    CPPUNIT_ASSERT_MESSAGE("Initial check failed.", bGood);
+    CPPUNIT_ASSERT_MESSAGE("Initial check failed (xls).", bGood);
 
     ScDocShellRef xDocSh2 = saveAndReload(xDocSh, XLS);
     xDocSh->DoClose();
     pDoc = &xDocSh2->GetDocument();
     bGood = aTest.check(*pDoc);
-    CPPUNIT_ASSERT_MESSAGE("Check after reload failed.", bGood);
+    CPPUNIT_ASSERT_MESSAGE("Check after reload failed (xls).", bGood);
 
     // fdo#81445 : Check the blank value string to make sure it's "<empty>".
     ScChangeTrack* pCT = pDoc->GetChangeTrack();
@@ -1472,6 +1474,22 @@ void ScExportTest::testTrackChangesSimpleXLS()
     CPPUNIT_ASSERT_EQUAL(OUString("Cell B2 changed from '<empty>' to '1'"), aDesc);
 
     xDocSh2->DoClose();
+
+    // Now, test the xlsx variant the same way.
+
+    xDocSh = loadDoc("track-changes/simple-cell-changes.", XLSX);
+    CPPUNIT_ASSERT(xDocSh.Is());
+    pDoc = &xDocSh->GetDocument();
+    aTest.check(*pDoc);
+    CPPUNIT_ASSERT_MESSAGE("Initial check failed (xlsx).", bGood);
+
+    xDocSh2 = saveAndReload(xDocSh, XLSX);
+    xDocSh->DoClose();
+    pDoc = &xDocSh2->GetDocument();
+    bGood = aTest.check(*pDoc);
+    CPPUNIT_ASSERT_MESSAGE("Check after reload failed (xlsx).", bGood);
+
+    xDocSh2->DoClose();
 }
 
 void ScExportTest::testSheetTabColorsXLSX()
commit fa44673e154ed4fb0b518b8850e2f6e4b9069531
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Thu Jul 17 20:20:16 2014 -0400

    bnc#885548: Adjust xlsx export of revisions to get it to work in Excel.
    
    Change-Id: I0058d9ddfea545390e615a3030171a366e333c85

diff --git a/sc/source/filter/excel/xestream.cxx b/sc/source/filter/excel/xestream.cxx
index 362ba34..7aaf648 100644
--- a/sc/source/filter/excel/xestream.cxx
+++ b/sc/source/filter/excel/xestream.cxx
@@ -831,6 +831,13 @@ const char* XclXmlUtils::ToPsz( bool b )
     return b ? "true" : "false";
 }
 
+const char* XclXmlUtils::ToPsz10( bool b )
+{
+    // xlsx seems to use "1" or "0" for boolean values.  I wonder it ever uses
+    // the "true" "false" variant.
+    return b ? "1" : "0";
+}
+
 sax_fastparser::FSHelperPtr XclXmlUtils::WriteElement( sax_fastparser::FSHelperPtr pStream, sal_Int32 nElement, sal_Int32 nValue )
 {
     pStream->startElement( nElement, FSEND );
diff --git a/sc/source/filter/inc/XclExpChangeTrack.hxx b/sc/source/filter/inc/XclExpChangeTrack.hxx
index 2ef29b7..12b249f 100644
--- a/sc/source/filter/inc/XclExpChangeTrack.hxx
+++ b/sc/source/filter/inc/XclExpChangeTrack.hxx
@@ -31,6 +31,14 @@
 
 #include <boost/ptr_container/ptr_vector.hpp>
 
+class ExcXmlRecord : public ExcRecord
+{
+public:
+    virtual sal_Size    GetLen() const SAL_OVERRIDE;
+    virtual sal_uInt16  GetNum() const SAL_OVERRIDE;
+    virtual void        Save( XclExpStream& rStrm ) SAL_OVERRIDE;
+};
+
 // XclExpUserBView - one UserBView record for each user
 
 class XclExpUserBView : public ExcRecord
@@ -228,40 +236,61 @@ public:
     virtual void                SaveXml( XclExpXmlStream& rStrm ) SAL_OVERRIDE;
 };
 
+class XclExpXmlChTrHeaders : public ExcXmlRecord
+{
+    sal_uInt8 maGUID[16];
+public:
+    void SetGUID( const sal_uInt8* pGUID );
+
+    virtual void SaveXml( XclExpXmlStream& rStrm ) SAL_OVERRIDE;
+};
+
+class XclExpChTrTabIdBuffer;
+class XclExpChTrAction;
+
+class XclExpXmlChTrHeader : public ExcXmlRecord
+{
+    OUString maUserName;
+    DateTime maDateTime;
+    sal_uInt8 maGUID[16];
+    sal_Int32 mnLogNumber;
+    sal_uInt32 mnMinAction;
+    sal_uInt32 mnMaxAction;
+
+    std::vector<sal_uInt16> maTabBuffer;
+    std::vector<XclExpChTrAction*> maActions;
+
+public:
+    XclExpXmlChTrHeader(
+        const OUString& rUserName, const DateTime& rDateTime, const sal_uInt8* pGUID,
+        sal_Int32 nLogNumber, const XclExpChTrTabIdBuffer& rBuf );
+
+    virtual void SaveXml( XclExpXmlStream& rStrm ) SAL_OVERRIDE;
+
+    void AppendAction( XclExpChTrAction* pAction );
+};
+
 // XclExpChTrInfo - header of action group of a user
 
 class XclExpChTrInfo : public ExcRecord
 {
 private:
     XclExpString                sUsername;
-    sal_Int32                   mnLogNumber;
     DateTime                    aDateTime;
     sal_uInt8                   aGUID[ 16 ];
 
     virtual void                SaveCont( XclExpStream& rStrm ) SAL_OVERRIDE;
 
 public:
-    inline                      XclExpChTrInfo(
-                                    const OUString& rUsername,
-                                    const DateTime& rDateTime,
-                                    const sal_uInt8* pGUID,
-                                    sal_Int32 nLogNumber );
+    XclExpChTrInfo( const OUString& rUsername, const DateTime& rDateTime,
+                    const sal_uInt8* pGUID );
+
     virtual                     ~XclExpChTrInfo();
 
     virtual sal_uInt16              GetNum() const SAL_OVERRIDE;
     virtual sal_Size            GetLen() const SAL_OVERRIDE;
-
-    virtual void                SaveXml( XclExpXmlStream& rStrm ) SAL_OVERRIDE;
 };
 
-inline XclExpChTrInfo::XclExpChTrInfo( const OUString& rUsername, const DateTime& rDateTime, const sal_uInt8* pGUID, sal_Int32 nLogNumber ) :
-    sUsername( rUsername ),
-    mnLogNumber( nLogNumber ),
-    aDateTime( rDateTime )
-{
-    memcpy( aGUID, pGUID, 16 );
-}
-
 // XclExpChTrTabIdBuffer - buffer for tab id's
 
 class XclExpChTrTabIdBuffer
@@ -296,7 +325,6 @@ class XclExpChTrTabId : public ExcRecord
 private:
     sal_uInt16*                 pBuffer;
     sal_uInt16                  nTabCount;
-    bool                        mbInRevisionHeaders;
 
     inline void                 Clear() { if( pBuffer ) delete[] pBuffer; pBuffer = NULL; }
 
@@ -304,16 +332,14 @@ private:
 
 public:
     inline                      XclExpChTrTabId( sal_uInt16 nCount ) :
-                                    pBuffer( NULL ), nTabCount( nCount ), mbInRevisionHeaders( false ) {}
-                                XclExpChTrTabId( const XclExpChTrTabIdBuffer& rBuffer, bool bInRevisionHeaders = false );
+                                    pBuffer( NULL ), nTabCount( nCount ) {}
+                                XclExpChTrTabId( const XclExpChTrTabIdBuffer& rBuffer );
     virtual                     ~XclExpChTrTabId();
 
     void                        Copy( const XclExpChTrTabIdBuffer& rBuffer );
 
     virtual sal_uInt16              GetNum() const SAL_OVERRIDE;
     virtual sal_Size            GetLen() const SAL_OVERRIDE;
-
-    virtual void                SaveXml( XclExpXmlStream& rStrm ) SAL_OVERRIDE;
 };
 
 // XclExpChTrAction - base class for action records
@@ -361,7 +387,6 @@ protected:
                                 // do something after writing the record
     virtual void                CompleteSaveAction( XclExpStream& rStrm ) const;
 
-    inline sal_uInt32           GetActionNumber() const { return nIndex; }
     inline bool                 GetAccepted() const { return bAccepted; }
 
 public:
@@ -385,6 +410,7 @@ public:
     virtual sal_Size            GetLen() const SAL_OVERRIDE;
 
     inline XclExpChTrAction*    GetAddAction() { return pAddAction; }
+    inline sal_uInt32           GetActionNumber() const { return nIndex; }
 };
 
 inline void XclExpChTrAction::Write2DAddress( XclExpStream& rStrm, const ScAddress& rAddress ) const
diff --git a/sc/source/filter/inc/xestream.hxx b/sc/source/filter/inc/xestream.hxx
index e69e0c7..c705e45 100644
--- a/sc/source/filter/inc/xestream.hxx
+++ b/sc/source/filter/inc/xestream.hxx
@@ -277,8 +277,18 @@ public:
     static OUString ToOUString( const ScfUInt16Vec& rBuffer, sal_Int32 nStart = 0, sal_Int32 nLength = -1 );
     static OUString ToOUString( ScDocument& rDocument, const ScAddress& rAddress, const ScTokenArray* pTokenArray );
     static OUString ToOUString( const XclExpString& s );
+
+    /**
+     * @return const char* literal "true" for true value, or literal "false"
+     *         for false value.
+     */
     static const char* ToPsz( bool b );
 
+    /**
+     * @return literal "1" for true value, or literal "0" for false value.
+     */
+    static const char* ToPsz10( bool b );
+
     static sax_fastparser::FSHelperPtr  WriteElement( sax_fastparser::FSHelperPtr pStream, sal_Int32 nElement, sal_Int32 nValue );
     static sax_fastparser::FSHelperPtr  WriteElement( sax_fastparser::FSHelperPtr pStream, sal_Int32 nElement, sal_Int64 nValue );
     static sax_fastparser::FSHelperPtr  WriteElement( sax_fastparser::FSHelperPtr pStream, sal_Int32 nElement, const char* sValue );
diff --git a/sc/source/filter/xcl97/XclExpChangeTrack.cxx b/sc/source/filter/xcl97/XclExpChangeTrack.cxx
index 3aa2a74..b24073f 100644
--- a/sc/source/filter/xcl97/XclExpChangeTrack.cxx
+++ b/sc/source/filter/xcl97/XclExpChangeTrack.cxx
@@ -36,7 +36,7 @@ static OString lcl_GuidToOString( sal_uInt8 aGuid[ 16 ] )
 {
     char sBuf[ 40 ];
     snprintf( sBuf, sizeof( sBuf ),
-            "{%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
+            "{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
             aGuid[ 0 ], aGuid[ 1 ], aGuid[ 2 ], aGuid[ 3 ], aGuid[ 4 ], aGuid[ 5 ], aGuid[ 6 ], aGuid[ 7 ],
             aGuid[ 8 ], aGuid[ 9 ], aGuid[ 10 ], aGuid[ 11 ], aGuid[ 12 ], aGuid[ 13 ], aGuid[ 14 ], aGuid[ 15 ] );
     return OString( sBuf );
@@ -365,6 +365,150 @@ void XclExpChTrHeader::SaveXml( XclExpXmlStream& rRevisionHeadersStrm )
     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);
+
+    rStrm.WriteAttributes(
+        XML_xmlns,              "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
+        FSNS(XML_xmlns, XML_r), "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
+        XML_guid,               lcl_GuidToOString(maGUID).getStr(),
+        XML_lastGuid,           NULL,   // OOXTODO
+        XML_shared,             NULL,   // OOXTODO
+        XML_diskRevisions,      NULL,   // OOXTODO
+        XML_history,            NULL,   // OOXTODO
+        XML_trackRevisions,     NULL,   // OOXTODO
+        XML_exclusive,          NULL,   // OOXTODO
+        XML_revisionId,         NULL,   // OOXTODO
+        XML_version,            NULL,   // OOXTODO
+        XML_keepChangeHistory,  NULL,   // OOXTODO
+        XML_protected,          NULL,   // OOXTODO
+        XML_preserveHistory,    NULL,   // OOXTODO
+        FSEND);
+
+    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[0]);
+    }
+}
+
+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(NULL, "revisionLog", mnLogNumber),
+            rStrm.GetCurrentStream()->getOutputStream(),
+            "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml",
+            CREATE_OFFICEDOC_RELATION_TYPE("revisionLog"),
+            &aRelId);
+
+    rStrm.WriteAttributes(
+        XML_guid, lcl_GuidToOString(maGUID).getStr(),
+        XML_dateTime, lcl_DateTimeToOString(maDateTime).getStr(),
+        XML_userName, XclXmlUtils::ToOString(maUserName).getStr(),
+        FSNS(XML_r, XML_id),  XclXmlUtils::ToOString(aRelId).getStr(),
+        FSEND);
+
+    if (mnMinAction)
+        rStrm.WriteAttributes(XML_minRId, OString::number(mnMinAction).getStr(), FSEND);
+
+    if (mnMaxAction)
+        rStrm.WriteAttributes(XML_maxRId, OString::number(mnMaxAction).getStr(), FSEND);
+
+    if (!maTabBuffer.empty())
+        // next available sheet index.
+        rStrm.WriteAttributes(XML_maxSheetId, OString::number(maTabBuffer.back()+1).getStr(), FSEND);
+
+    pHeader->write(">");
+
+    if (!maTabBuffer.empty())
+    {
+        // Write sheet index map.
+        size_t n = maTabBuffer.size();
+        pHeader->startElement(
+            XML_sheetIdMap,
+            XML_count, OString::number(n).getStr(),
+            FSEND);
+
+        for (size_t i = 0; i < n; ++i)
+        {
+            pHeader->singleElement(
+                XML_sheetId,
+                XML_val, OString::number(maTabBuffer[i]).getStr(),
+                FSEND);
+        }
+        pHeader->endElement(XML_sheetIdMap);
+    }
+
+    // Write all revision logs in a separate stream.
+
+    rStrm.PushStream(pRevLogStrm);
+
+    pRevLogStrm->write("<")->writeId(XML_revisions);
+
+    rStrm.WriteAttributes(
+        XML_xmlns,              "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
+        FSNS(XML_xmlns, XML_r), "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
+        FSEND);
+
+    pRevLogStrm->write(">");
+
+    std::vector<XclExpChTrAction*>::iterator it = maActions.begin(), itEnd = maActions.end();
+    for (; it != itEnd; ++it)
+    {
+        XclExpChTrAction* p = *it;
+        p->SaveXml(rStrm);
+    }
+
+    pRevLogStrm->write("</")->writeId(XML_revisions)->write(">");
+
+    rStrm.PopStream();
+
+    pHeader->write("</")->writeId(XML_header)->write(">");
+}
+
+void XclExpXmlChTrHeader::AppendAction( XclExpChTrAction* pAction )
+{
+    sal_uInt32 nActionNum = pAction->GetActionNumber();
+    if (!mnMinAction || mnMinAction > nActionNum)
+        mnMinAction = nActionNum;
+
+    if (!mnMaxAction || mnMaxAction < nActionNum)
+        mnMaxAction = nActionNum;
+
+    maActions.push_back(pAction);
+}
+
+XclExpChTrInfo::XclExpChTrInfo( const OUString& rUsername, const DateTime& rDateTime, const sal_uInt8* pGUID ) :
+    sUsername( rUsername ),
+    aDateTime( rDateTime )
+{
+    memcpy( aGUID, pGUID, 16 );
+}
+
 XclExpChTrInfo::~XclExpChTrInfo()
 {
 }
@@ -393,33 +537,6 @@ sal_Size XclExpChTrInfo::GetLen() const
     return 158;
 }
 
-void XclExpChTrInfo::SaveXml( XclExpXmlStream& rRevisionHeadersStrm )
-{
-    sax_fastparser::FSHelperPtr pHeader = rRevisionHeadersStrm.GetCurrentStream();
-
-    OUString sRelationshipId;
-    sax_fastparser::FSHelperPtr pRevisionLog = rRevisionHeadersStrm.CreateOutputStream(
-            XclXmlUtils::GetStreamName( "xl/revisions/", "revisionLog", mnLogNumber ),
-            XclXmlUtils::GetStreamName( NULL, "revisionLog", mnLogNumber ),
-            rRevisionHeadersStrm.GetCurrentStream()->getOutputStream(),
-            "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml",
-            CREATE_OFFICEDOC_RELATION_TYPE("revisionLog"),
-            &sRelationshipId );
-
-    rRevisionHeadersStrm.WriteAttributes(
-            XML_guid,               lcl_GuidToOString( aGUID ).getStr(),
-            XML_dateTime,           lcl_DateTimeToOString( aDateTime ).getStr(),
-            XML_maxSheetId,         NULL,   // OOXTODO
-            XML_userName,           XclXmlUtils::ToOString( sUsername ).getStr(),
-            FSNS( XML_r, XML_id ),  XclXmlUtils::ToOString( sRelationshipId ).getStr(),
-            XML_minRId,             NULL,   // OOXTODO
-            XML_maxRId,             NULL,   // OOXTODO
-            FSEND );
-    pHeader->write( ">" );
-
-    rRevisionHeadersStrm.PushStream( pRevisionLog );
-}
-
 XclExpChTrTabIdBuffer::XclExpChTrTabIdBuffer( sal_uInt16 nCount ) :
     nBufSize( nCount ),
     nLastId( nCount )
@@ -490,9 +607,8 @@ void XclExpChTrTabIdBuffer::Remove()
     nLastId--;
 }
 
-XclExpChTrTabId::XclExpChTrTabId( const XclExpChTrTabIdBuffer& rBuffer, bool bInRevisionHeaders )
+XclExpChTrTabId::XclExpChTrTabId( const XclExpChTrTabIdBuffer& rBuffer )
     : nTabCount( rBuffer.GetBufferCount() )
-    , mbInRevisionHeaders( bInRevisionHeaders )
 {
     pBuffer = new sal_uInt16[ nTabCount ];
     rBuffer.GetBufferCopy( pBuffer );
@@ -532,29 +648,6 @@ sal_Size XclExpChTrTabId::GetLen() const
     return nTabCount << 1;
 }
 
-void XclExpChTrTabId::SaveXml( XclExpXmlStream& rRevisionLogStrm )
-{
-    if( !mbInRevisionHeaders )
-        return;
-
-    sax_fastparser::FSHelperPtr pRevisionLog = rRevisionLogStrm.GetCurrentStream();
-    rRevisionLogStrm.PopStream();
-
-    sax_fastparser::FSHelperPtr pHeader = rRevisionLogStrm.GetCurrentStream();
-    pHeader->startElement( XML_sheetIdMap,
-            XML_count,  OString::number( nTabCount ).getStr(),
-            FSEND );
-    for( int i = 0; i < nTabCount; ++i )
-    {
-        pHeader->singleElement( XML_sheetId,
-                XML_val,    OString::number( pBuffer[ i ] ).getStr(),
-                FSEND );
-    }
-    pHeader->endElement( XML_sheetIdMap );
-
-    rRevisionLogStrm.PushStream( pRevisionLog );
-}
-
 // ! does not copy additional actions
 XclExpChTrAction::XclExpChTrAction( const XclExpChTrAction& rCopy ) :
     ExcRecord( rCopy ),
@@ -1093,11 +1186,12 @@ void XclExpChTrInsert::SaveXml( XclExpXmlStream& rRevisionLogStrm )
             XML_ua,     XclXmlUtils::ToPsz( GetAccepted () ),   // OOXTODO? bAccepted == ua or ra; not sure.
             XML_ra,     NULL,       // OOXTODO: RRD.fUndoAction?  Or RRD.fAccepted?
             XML_sId,    OString::number(  GetTabId( aRange.aStart.Tab() ) ).getStr(),
-            XML_eol,    NULL,       // OOXTODO: not supported?
+            XML_eol,    XclXmlUtils::ToPsz10(mbEndOfList),
             XML_ref,    XclXmlUtils::ToOString( aRange ).getStr(),
             XML_action, lcl_GetAction( nOpCode ),
             XML_edge,   NULL,       // OOXTODO: ???
             FSEND );
+
     // OOXTODO: does this handle XML_rfmt, XML_undo?
     XclExpChTrAction* pAction = GetAddAction();
     while( pAction != NULL )
@@ -1279,14 +1373,6 @@ void XclExpChTr0x014A::SaveXml( XclExpXmlStream& rStrm )
     pStream->endElement( XML_rfmt );
 }
 
-class ExcXmlRecord : public ExcRecord
-{
-public:
-    virtual sal_Size    GetLen() const SAL_OVERRIDE;
-    virtual sal_uInt16  GetNum() const SAL_OVERRIDE;
-    virtual void        Save( XclExpStream& rStrm ) SAL_OVERRIDE;
-};
-
 sal_Size ExcXmlRecord::GetLen() const
 {
     return 0;
@@ -1406,54 +1492,79 @@ XclExpChangeTrack::XclExpChangeTrack( const XclExpRoot& rRoot ) :
     }
 
     // build record list
-    pHeader = new XclExpChTrHeader;
-    maRecList.push_back( new StartXmlElement( XML_headers, StartXmlElement::WRITE_NAMESPACES ) );
-    maRecList.push_back( pHeader );
-    maRecList.push_back( new XclExpChTr0x0195 );
-    maRecList.push_back( new XclExpChTr0x0194( *pTempChangeTrack ) );
-
-    OUString sLastUsername;
-    DateTime aLastDateTime( DateTime::EMPTY );
-    sal_uInt32 nIndex = 1;
-    sal_Int32 nLogNumber = 1;
-    while( !aActionStack.empty() )
+    if (GetOutput() == EXC_OUTPUT_BINARY)
     {
-        XclExpChTrAction* pAction = aActionStack.top();
-        aActionStack.pop();
-
-        if( (nIndex == 1) || pAction->ForceInfoRecord() ||
-            (pAction->GetUsername() != sLastUsername) ||
-            (pAction->GetDateTime() != aLastDateTime) )
+        pHeader = new XclExpChTrHeader;
+        maRecList.push_back( pHeader );
+        maRecList.push_back( new XclExpChTr0x0195 );
+        maRecList.push_back( new XclExpChTr0x0194( *pTempChangeTrack ) );
+
+        OUString sLastUsername;
+        DateTime aLastDateTime( DateTime::EMPTY );
+        sal_uInt32 nIndex = 1;
+        sal_Int32 nLogNumber = 1;
+        while( !aActionStack.empty() )
         {
-            if( nIndex != 1 )
+            XclExpChTrAction* pAction = aActionStack.top();
+            aActionStack.pop();
+
+            if( (nIndex == 1) || pAction->ForceInfoRecord() ||
+                (pAction->GetUsername() != sLastUsername) ||
+                (pAction->GetDateTime() != aLastDateTime) )
             {
-                maRecList.push_back( new EndXmlElement( XML_revisions ) );
-                maRecList.push_back( new EndHeaderElement() );
+                lcl_GenerateGUID( aGUID, bValidGUID );
+                sLastUsername = pAction->GetUsername();
+                aLastDateTime = pAction->GetDateTime();
+
+                nLogNumber++;
+                maRecList.push_back( new XclExpChTrInfo(sLastUsername, aLastDateTime, aGUID) );
+                maRecList.push_back( new XclExpChTrTabId(pAction->GetTabIdBuffer()) );
+                pHeader->SetGUID( aGUID );
             }
+            pAction->SetIndex( nIndex );
+            maRecList.push_back( pAction );
+        }
+
+        pHeader->SetGUID( aGUID );
+        pHeader->SetCount( nIndex - 1 );
+        maRecList.push_back( new ExcEof );
+    }
+    else
+    {
+        XclExpXmlChTrHeaders* pHeaders = new XclExpXmlChTrHeaders;
+        maRecList.push_back(pHeaders);
 
-            lcl_GenerateGUID( aGUID, bValidGUID );
-            sLastUsername = pAction->GetUsername();
-            aLastDateTime = pAction->GetDateTime();
+        OUString sLastUsername;
+        DateTime aLastDateTime(DateTime::EMPTY);
+        sal_uInt32 nIndex = 1;
+        sal_Int32 nLogNumber = 1;
+        XclExpXmlChTrHeader* pCurHeader = NULL;
 
-            maRecList.push_back( new StartXmlElement( XML_header, 0 ) );
-            maRecList.push_back( new XclExpChTrInfo( sLastUsername, aLastDateTime, aGUID, nLogNumber++ ) );
-            maRecList.push_back( new XclExpChTrTabId( pAction->GetTabIdBuffer(), true ) );
-            maRecList.push_back( new StartXmlElement( XML_revisions, StartXmlElement::WRITE_NAMESPACES | StartXmlElement::CLOSE_ELEMENT ) );
-            pHeader->SetGUID( aGUID );
+        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(pCurHeader);
+                nLogNumber++;
+                pHeaders->SetGUID(aGUID);
+            }
+            pAction->SetIndex(nIndex);
+            pCurHeader->AppendAction(pAction);
         }
-        pAction->SetIndex( nIndex );
-        maRecList.push_back( pAction );
-    }
 
-    pHeader->SetGUID( aGUID );
-    pHeader->SetCount( nIndex - 1 );
-    if( nLogNumber > 1 )
-    {
-        maRecList.push_back( new EndXmlElement( XML_revisions ) );
-        maRecList.push_back( new EndHeaderElement() );
+        pHeaders->SetGUID(aGUID);
+        maRecList.push_back(new EndXmlElement(XML_headers));
     }
-    maRecList.push_back( new EndXmlElement( XML_headers ) );
-    maRecList.push_back( new ExcEof );
 }
 
 XclExpChangeTrack::~XclExpChangeTrack()
commit b92fdaa1a40bd54d279b4ac2faf19bf15468cc2f
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Wed Jul 16 19:59:35 2014 -0400

    bnc#885548: Initial work on importing revisions from xlsx.
    
    Change-Id: Ie0528490d024093cbabf38541fe70be96a9caa2e

diff --git a/sc/Library_scfilt.mk b/sc/Library_scfilt.mk
index d5ca533..f01e317 100644
--- a/sc/Library_scfilt.mk
+++ b/sc/Library_scfilt.mk
@@ -199,6 +199,7 @@ $(eval $(call gb_Library_add_exception_objects,scfilt,\
 	sc/source/filter/oox/pivottablefragment \
 	sc/source/filter/oox/querytablebuffer \
 	sc/source/filter/oox/querytablefragment \
+	sc/source/filter/oox/revisionfragment \
 	sc/source/filter/oox/richstringcontext \
 	sc/source/filter/oox/richstring \
 	sc/source/filter/oox/scenariobuffer \
diff --git a/sc/inc/chgtrack.hxx b/sc/inc/chgtrack.hxx
index f036f79..f898368 100644
--- a/sc/inc/chgtrack.hxx
+++ b/sc/inc/chgtrack.hxx
@@ -1099,7 +1099,7 @@ public:
                         sal_uLong nOldFormat, ScDocument* pRefDoc = NULL );
                         // after new value was set in the document,
                         // old value from pOldCell, format from Doc
-    void AppendContent( const ScAddress& rPos, const ScCellValue& rOldCell );
+    SC_DLLPUBLIC void AppendContent( const ScAddress& rPos, const ScCellValue& rOldCell );
                         // after new values were set in the document,
                         // old values from RefDoc/UndoDoc.
                         // All contents with a cell in RefDoc
diff --git a/sc/source/filter/inc/revisionfragment.hxx b/sc/source/filter/inc/revisionfragment.hxx
new file mode 100644
index 0000000..473549e
--- /dev/null
+++ b/sc/source/filter/inc/revisionfragment.hxx
@@ -0,0 +1,79 @@
+/* -*- 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/.
+ */
+
+#ifndef INCLUDED_SC_OOX_XLS_REVISIONFRAGMENT_HXX
+#define INCLUDED_SC_OOX_XLS_REVISIONFRAGMENT_HXX
+
+#include <excelhandlers.hxx>
+
+class ScChangeTrack;
+
+namespace oox { namespace xls {
+
+class RevisionHeadersFragment : public WorkbookFragmentBase
+{
+    struct Impl;
+    Impl* mpImpl;
+
+public:
+    explicit RevisionHeadersFragment(
+        const WorkbookHelper& rHelper, const OUString& rFragmentPath );
+
+    virtual ~RevisionHeadersFragment();
+
+protected:
+    virtual oox::core::ContextHandlerRef onCreateContext(
+        sal_Int32 nElement, const AttributeList& rAttribs ) SAL_OVERRIDE;
+
+    virtual void onStartElement( const AttributeList& rAttribs ) SAL_OVERRIDE;
+    virtual void onCharacters( const OUString& rChars ) SAL_OVERRIDE;
+    virtual void onEndElement() SAL_OVERRIDE;
+
+    virtual void finalizeImport() SAL_OVERRIDE;
+
+private:
+    void importHeaders( const AttributeList& rAttribs );
+    void importHeader( const AttributeList& rAttribs );
+};
+
+class RevisionLogFragment : public WorkbookFragmentBase
+{
+    struct Impl;
+    Impl* mpImpl;
+
+public:
+    explicit RevisionLogFragment(
+        const WorkbookHelper& rHelper, const OUString& rFragmentPath, ScChangeTrack& rChangeTrack );
+
+    virtual ~RevisionLogFragment();
+
+protected:
+    virtual oox::core::ContextHandlerRef onCreateContext(
+        sal_Int32 nElement, const AttributeList& rAttribs ) SAL_OVERRIDE;
+
+    virtual void onStartElement( const AttributeList& rAttribs ) SAL_OVERRIDE;
+    virtual void onCharacters( const OUString& rChars ) SAL_OVERRIDE;
+    virtual void onEndElement() SAL_OVERRIDE;
+
+    virtual void finalizeImport() SAL_OVERRIDE;
+
+private:
+    void importCommon( const AttributeList& rAttribs );
+    void importRcc( const AttributeList& rAttribs );
+    void importRrc( const AttributeList& rAttribs );
+
+    void pushRevision();
+};
+
+}}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+
diff --git a/sc/source/filter/oox/revisionfragment.cxx b/sc/source/filter/oox/revisionfragment.cxx
new file mode 100644
index 0000000..dd8cc4b
--- /dev/null
+++ b/sc/source/filter/oox/revisionfragment.cxx
@@ -0,0 +1,462 @@
+/* -*- 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/.
+ */
+
+#include <revisionfragment.hxx>
+#include <oox/core/relations.hxx>
+#include <oox/core/xmlfilterbase.hxx>
+#include <oox/core/fastparser.hxx>
+#include <o3tl/heap_ptr.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sax/tools/converter.hxx>
+#include <editeng/editobj.hxx>
+
+#include <chgtrack.hxx>
+#include <document.hxx>
+#include <compiler.hxx>
+#include <editutil.hxx>
+#include <formulacell.hxx>
+#include <chgviset.hxx>
+#include <richstringcontext.hxx>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <com/sun/star/util/DateTime.hpp>
+
+using namespace com::sun::star;
+
+namespace oox { namespace xls {
+
+namespace {
+
+enum RevisionType
+{
+    REV_UNKNOWN = 0,
+    REV_CELLCHANGE,
+    REV_INSERTROW,
+    REV_DELETEROW,
+    REV_INSERTCOL,
+    REV_DELETECOL
+};
+
+/**
+ * For nc (new cell) or oc (old cell) elements under rcc (cell content
+ * revision).
+ */
+class RCCCellValueContext : public WorkbookContextBase
+{
+    sal_Int32 mnSheetIndex;
+    ScAddress& mrPos;
+    ScCellValue& mrCellValue;
+    sal_Int32 mnType;
+
+    RichStringRef mxRichString;
+
+public:
+    RCCCellValueContext(
+        RevisionLogFragment& rParent, sal_Int32 nSheetIndex, ScAddress& rPos, ScCellValue& rCellValue ) :
+        WorkbookContextBase(rParent),
+        mnSheetIndex(nSheetIndex),
+        mrPos(rPos),
+        mrCellValue(rCellValue),
+        mnType(-1) {}
+
+protected:
+    virtual oox::core::ContextHandlerRef onCreateContext(
+        sal_Int32 nElement, const AttributeList& /*rAttribs*/ ) SAL_OVERRIDE
+    {
+        if (nElement == XLS_TOKEN(is))
+        {
+            mxRichString.reset(new RichString(*this));
+            return new RichStringContext(*this, mxRichString);
+        }
+
+        return this;
+    }
+
+    virtual void onStartElement( const AttributeList& rAttribs ) SAL_OVERRIDE
+    {
+        switch (getCurrentElement())
+        {
+            case XLS_TOKEN(nc):
+            case XLS_TOKEN(oc):
+                importCell(rAttribs);
+            break;
+            default:
+                ;
+        }
+    }
+
+    virtual void onCharacters( const OUString& rChars ) SAL_OVERRIDE
+    {
+        switch (getCurrentElement())
+        {
+            case XLS_TOKEN(v):
+            {
+                if (mnType == XML_n || mnType == XML_b)
+                    mrCellValue.set(rChars.toDouble());
+            }
+            break;
+            case XLS_TOKEN(t):
+            {
+                if (mnType == XML_inlineStr)
+                {
+                    ScDocument& rDoc = getScDocument();
+                    svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
+                    mrCellValue.set(rPool.intern(rChars));
+                }
+            }
+            break;
+            case XLS_TOKEN(f):
+            {
+                // formula string
+                ScDocument& rDoc = getScDocument();
+                ScCompiler aComp(&rDoc, mrPos);
+                aComp.SetGrammar(formula::FormulaGrammar::GRAM_OOXML);
+                ScTokenArray* pArray = aComp.CompileString(rChars);
+                if (!pArray)
+                    break;
+
+                mrCellValue.set(new ScFormulaCell(&rDoc, mrPos, pArray));
+            }
+            break;
+            default:
+                ;
+        }
+    }
+
+    virtual void onEndElement() SAL_OVERRIDE
+    {
+        switch (getCurrentElement())
+        {
+            case XLS_TOKEN(nc):
+            case XLS_TOKEN(oc):
+            {
+                if (mrCellValue.isEmpty() && mxRichString)
+                {
+                    // The value is a rich text string.
+                    ScDocument& rDoc = getScDocument();
+                    EditTextObject* pTextObj = mxRichString->convert(rDoc.GetEditEngine(), NULL);
+                    if (pTextObj)
+                    {
+                        svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
+                        pTextObj->NormalizeString(rPool);
+                        mrCellValue.set(pTextObj);
+                    }
+                }
+            }
+            break;
+            default:
+                ;
+        }
+    }
+
+private:
+    void importCell( const AttributeList& rAttribs )
+    {
+        mnType = rAttribs.getToken(XML_t, XML_n);
+        OUString aRefStr = rAttribs.getString(XML_r, OUString());
+        if (!aRefStr.isEmpty())
+        {
+            mrPos.Parse(aRefStr, NULL, formula::FormulaGrammar::CONV_XL_OOX);
+            if (mnSheetIndex != -1)
+                mrPos.SetTab(mnSheetIndex-1);
+        }
+    }
+};
+
+struct RevisionMetadata
+{
+    OUString maUserName;
+    DateTime maDateTime;
+
+    RevisionMetadata() : maDateTime(DateTime::EMPTY) {}
+    RevisionMetadata( const RevisionMetadata& r ) :
+        maUserName(r.maUserName), maDateTime(r.maDateTime) {}
+};
+
+}
+
+typedef std::map<OUString, RevisionMetadata> RevDataType;
+
+struct RevisionHeadersFragment::Impl
+{
+    RevDataType maRevData;
+
+    Impl() {}
+};
+
+RevisionHeadersFragment::RevisionHeadersFragment(
+    const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
+    WorkbookFragmentBase(rHelper, rFragmentPath),
+    mpImpl(new Impl) {}
+
+RevisionHeadersFragment::~RevisionHeadersFragment()
+{
+    delete mpImpl;
+}
+
+oox::core::ContextHandlerRef RevisionHeadersFragment::onCreateContext(
+    sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ )
+{
+    return this;
+}
+
+void RevisionHeadersFragment::onStartElement( const AttributeList& rAttribs )
+{
+    switch (getCurrentElement())
+    {
+        case XLS_TOKEN(headers):
+            importHeaders(rAttribs);
+        break;
+        case XLS_TOKEN(header):
+            importHeader(rAttribs);
+        break;
+        case XLS_TOKEN(sheetIdMap):
+        break;
+        case XLS_TOKEN(sheetId):
+        break;
+        default:
+            ;
+    }
+}
+
+void RevisionHeadersFragment::onCharacters( const OUString& /*rChars*/ ) {}
+
+void RevisionHeadersFragment::onEndElement()
+{
+    switch (getCurrentElement())
+    {
+        case XLS_TOKEN(headers):
+        break;
+        case XLS_TOKEN(header):
+        break;
+        case XLS_TOKEN(sheetIdMap):
+        break;
+        case XLS_TOKEN(sheetId):
+        break;
+        default:
+            ;
+    }
+}
+
+void RevisionHeadersFragment::finalizeImport()
+{
+    ScDocument& rDoc = getScDocument();
+    o3tl::heap_ptr<ScChangeTrack> pCT(new ScChangeTrack(&rDoc));
+    pCT->SetUseFixDateTime(true);
+
+    const oox::core::Relations& rRels = getRelations();
+    RevDataType::const_iterator it = mpImpl->maRevData.begin(), itEnd = mpImpl->maRevData.end();
+    for (; it != itEnd; ++it)
+    {
+        OUString aPath = rRels.getFragmentPathFromRelId(it->first);
+        if (aPath.isEmpty())
+            continue;
+
+        // Parse each reivison log fragment.
+        const RevisionMetadata& rData = it->second;
+        pCT->SetUser(rData.maUserName);
+        pCT->SetFixDateTimeLocal(rData.maDateTime);
+        boost::scoped_ptr<oox::core::FastParser> xParser(getOoxFilter().createParser());
+        rtl::Reference<oox::core::FragmentHandler> xFragment(new RevisionLogFragment(*this, aPath, *pCT));
+        importOoxFragment(xFragment, *xParser);
+    }
+
+    rDoc.SetChangeTrack(pCT.release());
+
+    // Turn on visibility of tracked changes.
+    ScChangeViewSettings aSettings;
+    aSettings.SetShowChanges(true);
+    rDoc.SetChangeViewSettings(aSettings);
+}
+
+void RevisionHeadersFragment::importHeaders( const AttributeList& /*rAttribs*/ )
+{
+    // Nothing for now.
+}
+
+void RevisionHeadersFragment::importHeader( const AttributeList& rAttribs )
+{
+    OUString aRId = rAttribs.getString(R_TOKEN(id), OUString());
+    if (aRId.isEmpty())
+        // All bets are off if we don't have a relation ID.
+        return;
+
+    RevisionMetadata aMetadata;
+    OUString aDateTimeStr = rAttribs.getString(XML_dateTime, OUString());
+    if (!aDateTimeStr.isEmpty())
+    {
+        util::DateTime aDateTime;
+        sax::Converter::parseDateTime(aDateTime, 0, aDateTimeStr);
+        Date aDate(aDateTime.Day, aDateTime.Month, aDateTime.Year);
+        Time aTime(aDateTime.Hours, aDateTime.Minutes, aDateTime.Seconds, aDateTime.NanoSeconds);
+        aMetadata.maDateTime.SetDate(aDate.GetDate());
+        aMetadata.maDateTime.SetTime(aTime.GetTime());
+    }
+
+    aMetadata.maUserName = rAttribs.getString(XML_userName, OUString());
+
+    mpImpl->maRevData.insert(RevDataType::value_type(aRId, aMetadata));
+}
+
+struct RevisionLogFragment::Impl
+{
+    ScChangeTrack& mrChangeTrack;
+
+    sal_Int32 mnRevIndex;
+    sal_Int32 mnSheetIndex;
+
+    RevisionType meType;
+
+    // rcc
+    ScAddress maOldCellPos;
+    ScAddress maNewCellPos;
+    ScCellValue maOldCellValue;
+    ScCellValue maNewCellValue;
+
+    // rrc
+    ScRange maRange;
+
+    bool mbEndOfList;
+
+    Impl( ScChangeTrack& rChangeTrack ) :
+        mrChangeTrack(rChangeTrack),
+        mnRevIndex(-1),
+        mnSheetIndex(-1),
+        meType(REV_UNKNOWN),
+        mbEndOfList(false) {}
+};
+
+RevisionLogFragment::RevisionLogFragment(
+    const WorkbookHelper& rHelper, const OUString& rFragmentPath, ScChangeTrack& rChangeTrack ) :
+    WorkbookFragmentBase(rHelper, rFragmentPath),
+    mpImpl(new Impl(rChangeTrack)) {}
+
+RevisionLogFragment::~RevisionLogFragment()
+{
+    delete mpImpl;
+}
+
+oox::core::ContextHandlerRef RevisionLogFragment::onCreateContext(
+    sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
+{
+    switch (nElement)
+    {
+        case XLS_TOKEN(nc):
+            return new RCCCellValueContext(*this, mpImpl->mnSheetIndex, mpImpl->maNewCellPos, mpImpl->maNewCellValue);
+        case XLS_TOKEN(oc):
+            return new RCCCellValueContext(*this, mpImpl->mnSheetIndex, mpImpl->maOldCellPos, mpImpl->maOldCellValue);
+        default:
+            ;
+    }
+    return this;
+}
+
+void RevisionLogFragment::onStartElement( const AttributeList& rAttribs )
+{
+    switch (getCurrentElement())
+    {
+        case XLS_TOKEN(rcc):
+            mpImpl->maNewCellPos.SetInvalid();
+            mpImpl->maOldCellPos.SetInvalid();
+            mpImpl->maNewCellValue.clear();
+            mpImpl->maOldCellValue.clear();
+            importRcc(rAttribs);
+        break;
+        case XLS_TOKEN(rrc):
+            importRrc(rAttribs);
+        break;
+        default:
+            ;
+    }
+}
+
+void RevisionLogFragment::onCharacters( const OUString& /*rChars*/ ) {}
+
+void RevisionLogFragment::onEndElement()
+{
+    switch (getCurrentElement())
+    {
+        case XLS_TOKEN(rcc):
+        case XLS_TOKEN(rrc):
+            pushRevision();
+        break;
+        default:
+            ;
+    }
+}
+
+void RevisionLogFragment::finalizeImport() {}
+
+void RevisionLogFragment::importCommon( const AttributeList& rAttribs )
+{
+    mpImpl->mnRevIndex   = rAttribs.getInteger(XML_rId, -1);
+    mpImpl->mnSheetIndex = rAttribs.getInteger(XML_sId, -1);
+}
+
+void RevisionLogFragment::importRcc( const AttributeList& rAttribs )
+{
+    importCommon(rAttribs);
+
+    mpImpl->meType = REV_CELLCHANGE;
+}
+
+void RevisionLogFragment::importRrc( const AttributeList& rAttribs )
+{
+    importCommon(rAttribs);
+
+    if (mpImpl->mnSheetIndex == -1)
+        // invalid sheet index, or sheet index not given.
+        return;
+
+    mpImpl->meType = REV_UNKNOWN;
+    sal_Int32 nAction = rAttribs.getToken(XML_action, -1);
+    if (nAction == -1)
+        return;
+
+    OUString aRefStr = rAttribs.getString(XML_ref, OUString());
+    mpImpl->maRange.Parse(aRefStr, &getScDocument(), formula::FormulaGrammar::CONV_XL_OOX);
+    if (!mpImpl->maRange.IsValid())
+        return;
+
+    switch (nAction)
+    {
+        case XML_insertRow:
+            mpImpl->meType = REV_INSERTROW;
+            mpImpl->maRange.aEnd.SetCol(MAXCOL);
+            mpImpl->maRange.aStart.SetTab(mpImpl->mnSheetIndex-1);
+            mpImpl->maRange.aEnd.SetTab(mpImpl->mnSheetIndex-1);
+        break;
+        default:
+            // Unknown action type.  Ignore it.
+            return;
+    }
+
+    mpImpl->mbEndOfList = rAttribs.getBool(XML_eol, false);
+}
+
+void RevisionLogFragment::pushRevision()
+{
+    switch (mpImpl->meType)
+    {
+        case REV_CELLCHANGE:
+            mpImpl->mrChangeTrack.AppendContentOnTheFly(
+                mpImpl->maNewCellPos, mpImpl->maOldCellValue, mpImpl->maNewCellValue);
+        break;
+        case REV_INSERTROW:
+            mpImpl->mrChangeTrack.AppendInsert(mpImpl->maRange, mpImpl->mbEndOfList);
+        break;
+        default:
+            ;
+    }
+}
+
+}}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/workbookfragment.cxx b/sc/source/filter/oox/workbookfragment.cxx
index 088cebc..d7ffaab 100644
--- a/sc/source/filter/oox/workbookfragment.cxx
+++ b/sc/source/filter/oox/workbookfragment.cxx
@@ -36,6 +36,7 @@
 #include "pivotcachebuffer.hxx"
 #include "sharedstringsbuffer.hxx"
 #include "sharedstringsfragment.hxx"
+#include "revisionfragment.hxx"
 #include "stylesfragment.hxx"
 #include "tablebuffer.hxx"
 #include "themebuffer.hxx"
@@ -293,7 +294,7 @@ public:
     }
 };
 
-static void importSheetFragments( WorkbookFragment& rWorkbookHandler, SheetFragmentVector& rSheets )
+void importSheetFragments( WorkbookFragment& rWorkbookHandler, SheetFragmentVector& rSheets )
 {
     sal_Int32 nThreads = std::min( rSheets.size(), (size_t) 4 /* FIXME: ncpus/2 */ );
 
@@ -490,6 +491,14 @@ void WorkbookFragment::finalizeImport()
 
     // final conversions, e.g. calculation settings and view settings
     finalizeWorkbookImport();
+
+    OUString aRevHeadersPath = getFragmentPathFromFirstType(CREATE_OFFICEDOC_RELATION_TYPE("revisionHeaders"));
+    if (!aRevHeadersPath.isEmpty())
+    {
+        boost::scoped_ptr<oox::core::FastParser> xParser(getOoxFilter().createParser());
+        rtl::Reference<oox::core::FragmentHandler> xFragment(new RevisionHeadersFragment(*this, aRevHeadersPath));
+        importOoxFragment(xFragment, *xParser);
+    }
 }
 
 // private --------------------------------------------------------------------


More information about the Libreoffice-commits mailing list