[Libreoffice-commits] core.git: Branch 'distro/collabora/cp-5.3' - 8 commits - desktop/inc desktop/qa desktop/source include/LibreOfficeKit include/sfx2

Ashod Nakashian ashod.nakashian at collabora.co.uk
Fri Feb 17 15:45:42 UTC 2017


 desktop/inc/lib/init.hxx                     |    4 
 desktop/qa/desktop_lib/test_desktop_lib.cxx  |  229 ++++++++++++++++++++++--
 desktop/source/lib/init.cxx                  |  251 ++++++++++++++++++++-------
 include/LibreOfficeKit/LibreOfficeKit.h      |   10 +
 include/LibreOfficeKit/LibreOfficeKit.hxx    |   10 +
 include/LibreOfficeKit/LibreOfficeKitEnums.h |   64 +++---
 include/sfx2/lokhelper.hxx                   |    4 
 7 files changed, 463 insertions(+), 109 deletions(-)

New commits:
commit a1b16d9c84fae89f5f4b0d681fb695c80accdcc9
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Fri Jan 20 19:11:47 2017 -0500

    Lok: unittest batch API
    
    Change-Id: I33bc59756f46b51a69efd91b89813ba17eca6218
    Reviewed-on: https://gerrit.libreoffice.org/33403
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit f6c0e851eff8c22c0ab427245c5eb78e9f396b62)

diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx
index 833e801..85550c3 100644
--- a/desktop/qa/desktop_lib/test_desktop_lib.cxx
+++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx
@@ -99,6 +99,7 @@ public:
     void testContextMenuImpress();
     void testNotificationCompression();
     void testTileInvalidationCompression();
+    void testBatching();
     void testPartInInvalidation();
     void testRedlineWriter();
     void testTrackChanges();
@@ -133,6 +134,7 @@ public:
     CPPUNIT_TEST(testContextMenuImpress);
     CPPUNIT_TEST(testNotificationCompression);
     CPPUNIT_TEST(testTileInvalidationCompression);
+    CPPUNIT_TEST(testBatching);
     CPPUNIT_TEST(testPartInInvalidation);
     CPPUNIT_TEST(testRedlineWriter);
     CPPUNIT_TEST(testTrackChanges);
@@ -1584,6 +1586,49 @@ void DesktopLOKTest::testPartInInvalidation()
     }
 }
 
+void DesktopLOKTest::testBatching()
+{
+    LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+
+    comphelper::LibreOfficeKit::setPartInInvalidation(true);
+    comphelper::ScopeGuard aGuard([]()
+    {
+        comphelper::LibreOfficeKit::setPartInInvalidation(false);
+    });
+
+    std::vector<std::tuple<int, std::string>> notifs;
+    std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
+
+    // Enable Batch mode.
+    handler->setEventLatch(true);
+
+    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 239, 0");
+    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "EMPTY, 0");
+
+    Scheduler::ProcessEventsToIdle();
+
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), notifs.size());
+
+    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 240, 0");
+    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "-121, -121, 300, 300, 0");
+    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, -32767, -32767, 0");
+
+    Scheduler::ProcessEventsToIdle();
+
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), notifs.size());
+
+    // Disable Batch mode.
+    handler->setEventLatch(false);
+
+    Scheduler::ProcessEventsToIdle();
+
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs.size());
+
+    size_t i = 0;
+    CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_TILES, (int)std::get<0>(notifs[i]));
+    CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 1000000000, 1000000000, 0"), std::get<1>(notifs[i++]));
+}
+
 void DesktopLOKTest::testRedlineWriter()
 {
     // Load a Writer document, enable change recording and press a key.
commit 58c58b049dd79108ae1dc223b4a336c3655c616d
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Fri Jan 6 10:48:18 2017 -0500

    Lok: support for batch API calls
    
    Mouse and keyboard operations typically
    come in batches, and often each results in
    tile invalidations and/or layout modifications.
    
    Processing each input event on its own, then processing
    the resulting output event is very costly and unecessary
    when we know there is more of the same.
    
    The new API adds support for batching such related
    input events by disabling the output events generated
    by Core until the batch is done. The client can
    then process the resulting events, which will
    be compressed and deduplicated.
    
    Change-Id: Id381dab807186d010021a8778ee440074a739920
    Reviewed-on: https://gerrit.libreoffice.org/33402
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit 1c27286b9d5331634c073cd3e327bd941e61bbb6)

diff --git a/desktop/inc/lib/init.hxx b/desktop/inc/lib/init.hxx
index 302f54d..bfe9954 100644
--- a/desktop/inc/lib/init.hxx
+++ b/desktop/inc/lib/init.hxx
@@ -41,6 +41,10 @@ namespace desktop {
         void setEventLatch(const bool bEventLatch)
         {
             m_bEventLatch = bEventLatch;
+            if (!IsActive())
+            {
+                Start();
+            }
         }
 
         bool isEventLatchOn() const { return m_bEventLatch; }
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 268f372..92e3485 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -532,6 +532,8 @@ static unsigned char* doc_renderFont(LibreOfficeKitDocument* pThis,
                           int* pFontWidth,
                           int* pFontHeight);
 static char* doc_getPartHash(LibreOfficeKitDocument* pThis, int nPart);
+static void doc_beginBatch(LibreOfficeKitDocument* pThis);
+static void doc_endBatch(LibreOfficeKitDocument* pThis);
 
 LibLODocument_Impl::LibLODocument_Impl(const uno::Reference <css::lang::XComponent> &xComponent)
     : mxComponent(xComponent)
@@ -578,6 +580,8 @@ LibLODocument_Impl::LibLODocument_Impl(const uno::Reference <css::lang::XCompone
 
         m_pDocumentClass->renderFont = doc_renderFont;
         m_pDocumentClass->getPartHash = doc_getPartHash;
+        m_pDocumentClass->beginBatch = doc_beginBatch;
+        m_pDocumentClass->endBatch = doc_endBatch;
 
         gDocumentClass = m_pDocumentClass;
     }
@@ -2696,6 +2700,28 @@ unsigned char* doc_renderFont(LibreOfficeKitDocument* /*pThis*/,
     return nullptr;
 }
 
+static void doc_beginBatch(LibreOfficeKitDocument* pThis)
+{
+    SolarMutexGuard aGuard;
+
+    LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+    for (const auto& pair : pDocument->mpCallbackFlushHandlers)
+    {
+        pair.second->setEventLatch(true);
+    }
+}
+
+static void doc_endBatch(LibreOfficeKitDocument* pThis)
+{
+    SolarMutexGuard aGuard;
+
+    LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+    for (const auto& pair : pDocument->mpCallbackFlushHandlers)
+    {
+        pair.second->setEventLatch(false);
+    }
+}
+
 static char* lo_getError (LibreOfficeKit *pThis)
 {
     SolarMutexGuard aGuard;
diff --git a/include/LibreOfficeKit/LibreOfficeKit.h b/include/LibreOfficeKit/LibreOfficeKit.h
index c7a2130..55cff72 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.h
+++ b/include/LibreOfficeKit/LibreOfficeKit.h
@@ -250,6 +250,16 @@ struct _LibreOfficeKitDocumentClass
                        int* pArray,
                        size_t nSize);
 
+    /// Starts a batch of operations.
+    /// Events are emmitted only after ending the batch.
+    /// @see lok::Document::endBatch();
+    void (*beginBatch) (LibreOfficeKitDocument* pThis);
+
+    /// Ends a batch of operations.
+    /// @see lok::Document::beginBatch();
+    void (*endBatch) (LibreOfficeKitDocument* pThis);
+
+
 #endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY
 };
 
diff --git a/include/LibreOfficeKit/LibreOfficeKit.hxx b/include/LibreOfficeKit/LibreOfficeKit.hxx
index 447f44b..46ecb5f 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.hxx
+++ b/include/LibreOfficeKit/LibreOfficeKit.hxx
@@ -452,6 +452,16 @@ public:
         return mpDoc->pClass->getViewIds(mpDoc, pArray, nSize);
     }
 
+    inline void beginBatch()
+    {
+        mpDoc->pClass->beginBatch(mpDoc);
+    }
+
+    inline void endBatch()
+    {
+        mpDoc->pClass->endBatch(mpDoc);
+    }
+
 #endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY
 };
 
commit 9f3e57e5fa31ca5654ee9eeaba03c871a1c83150
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Tue Dec 6 00:42:58 2016 -0500

    Lok: improved tile invalidation compression
    
    Handle corner cases better and eliminate
    invalid rects and out-of-bounds coordinates.
    
    Change-Id: Ib9247ae4f0306cf68937cd2678f6386fe7710eec
    Reviewed-on: https://gerrit.libreoffice.org/31665
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
    (cherry picked from commit 3db1ce30ab235ad22aed71c22e4f6f52b7b88829)

diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx
index 9acd2c7..833e801 100644
--- a/desktop/qa/desktop_lib/test_desktop_lib.cxx
+++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx
@@ -1299,29 +1299,29 @@ void DesktopLOKTest::testNotificationCompression()
     std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
 
     handler->queue(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, ""); // 0
-    handler->queue(LOK_CALLBACK_TEXT_SELECTION, "15 25 15 10"); // Superseeded.
+    handler->queue(LOK_CALLBACK_TEXT_SELECTION, "15, 25, 15, 10"); // Superseeded.
     handler->queue(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, ""); // Should be dropped.
-    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "15 25 15 10"); // Superseeded.
-    handler->queue(LOK_CALLBACK_TEXT_SELECTION, "15 25 15 10"); // Should be dropped.
+    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "15, 25, 15, 10"); // 1
+    handler->queue(LOK_CALLBACK_TEXT_SELECTION, "15, 25, 15, 10"); // Should be dropped.
     handler->queue(LOK_CALLBACK_TEXT_SELECTION, ""); // Superseeded.
     handler->queue(LOK_CALLBACK_STATE_CHANGED, ""); // 2
     handler->queue(LOK_CALLBACK_STATE_CHANGED, ".uno:Bold"); // 3
     handler->queue(LOK_CALLBACK_STATE_CHANGED, ""); // 4
     handler->queue(LOK_CALLBACK_MOUSE_POINTER, "text"); // 5
-    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "15 25 15 10"); // 6
-    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "15 25 15 10"); // Should be dropped.
+    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "15, 25, 15, 10"); // Should be dropped.
+    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "15, 25, 15, 10"); // Should be dropped.
     handler->queue(LOK_CALLBACK_MOUSE_POINTER, "text"); // Should be dropped.
-    handler->queue(LOK_CALLBACK_TEXT_SELECTION_START, "15 25 15 10"); // Superseeded.
-    handler->queue(LOK_CALLBACK_TEXT_SELECTION_END, "15 25 15 10"); // Superseeded.
-    handler->queue(LOK_CALLBACK_TEXT_SELECTION, "15 25 15 10"); // Superseedd.
-    handler->queue(LOK_CALLBACK_TEXT_SELECTION_START, "15 25 15 10"); // Should be dropped.
-    handler->queue(LOK_CALLBACK_TEXT_SELECTION_END, "15 25 15 10"); // Should be dropped.
+    handler->queue(LOK_CALLBACK_TEXT_SELECTION_START, "15, 25, 15, 10"); // Superseeded.
+    handler->queue(LOK_CALLBACK_TEXT_SELECTION_END, "15, 25, 15, 10"); // Superseeded.
+    handler->queue(LOK_CALLBACK_TEXT_SELECTION, "15, 25, 15, 10"); // Superseedd.
+    handler->queue(LOK_CALLBACK_TEXT_SELECTION_START, "15, 25, 15, 10"); // Should be dropped.
+    handler->queue(LOK_CALLBACK_TEXT_SELECTION_END, "15, 25, 15, 10"); // Should be dropped.
     handler->queue(LOK_CALLBACK_TEXT_SELECTION, ""); // 7
-    handler->queue(LOK_CALLBACK_TEXT_SELECTION_START, "15 25 15 10"); // 8
-    handler->queue(LOK_CALLBACK_TEXT_SELECTION_END, "15 25 15 10"); // 9
-    handler->queue(LOK_CALLBACK_CELL_CURSOR, "15 25 15 10"); // 10
+    handler->queue(LOK_CALLBACK_TEXT_SELECTION_START, "15, 25, 15, 10"); // 8
+    handler->queue(LOK_CALLBACK_TEXT_SELECTION_END, "15, 25, 15, 10"); // 9
+    handler->queue(LOK_CALLBACK_CELL_CURSOR, "15, 25, 15, 10"); // 10
     handler->queue(LOK_CALLBACK_CURSOR_VISIBLE, ""); // 11
-    handler->queue(LOK_CALLBACK_CELL_CURSOR, "15 25 15 10"); // Should be dropped.
+    handler->queue(LOK_CALLBACK_CELL_CURSOR, "15, 25, 15, 10"); // Should be dropped.
     handler->queue(LOK_CALLBACK_CELL_FORMULA, "blah"); // 12
     handler->queue(LOK_CALLBACK_SET_PART, "1"); // 13
     handler->queue(LOK_CALLBACK_STATE_CHANGED, ".uno:AssignLayout=20"); // Superseeded
@@ -1338,6 +1338,9 @@ void DesktopLOKTest::testNotificationCompression()
     CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, (int)std::get<0>(notifs[i]));
     CPPUNIT_ASSERT_EQUAL(std::string(""), std::get<1>(notifs[i++]));
 
+    CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_TILES, (int)std::get<0>(notifs[i]));
+    CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get<1>(notifs[i++]));
+
     CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_STATE_CHANGED, (int)std::get<0>(notifs[i]));
     CPPUNIT_ASSERT_EQUAL(std::string(""), std::get<1>(notifs[i++]));
 
@@ -1350,20 +1353,17 @@ void DesktopLOKTest::testNotificationCompression()
     CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_MOUSE_POINTER, (int)std::get<0>(notifs[i]));
     CPPUNIT_ASSERT_EQUAL(std::string("text"), std::get<1>(notifs[i++]));
 
-    CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_TILES, (int)std::get<0>(notifs[i]));
-    CPPUNIT_ASSERT_EQUAL(std::string("15 25 15 10"), std::get<1>(notifs[i++]));
-
     CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_TEXT_SELECTION, (int)std::get<0>(notifs[i]));
     CPPUNIT_ASSERT_EQUAL(std::string(""), std::get<1>(notifs[i++]));
 
     CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_TEXT_SELECTION_START, (int)std::get<0>(notifs[i]));
-    CPPUNIT_ASSERT_EQUAL(std::string("15 25 15 10"), std::get<1>(notifs[i++]));
+    CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get<1>(notifs[i++]));
 
     CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_TEXT_SELECTION_END, (int)std::get<0>(notifs[i]));
-    CPPUNIT_ASSERT_EQUAL(std::string("15 25 15 10"), std::get<1>(notifs[i++]));
+    CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get<1>(notifs[i++]));
 
     CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_CELL_CURSOR, (int)std::get<0>(notifs[i]));
-    CPPUNIT_ASSERT_EQUAL(std::string("15 25 15 10"), std::get<1>(notifs[i++]));
+    CPPUNIT_ASSERT_EQUAL(std::string("15, 25, 15, 10"), std::get<1>(notifs[i++]));
 
     CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_CURSOR_VISIBLE, (int)std::get<0>(notifs[i]));
     CPPUNIT_ASSERT_EQUAL(std::string(""), std::get<1>(notifs[i++]));
@@ -1381,8 +1381,6 @@ void DesktopLOKTest::testNotificationCompression()
 void DesktopLOKTest::testTileInvalidationCompression()
 {
     LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
-    std::vector<std::tuple<int, std::string>> notifs;
-    std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
 
     comphelper::LibreOfficeKit::setPartInInvalidation(true);
     comphelper::ScopeGuard aGuard([]()
@@ -1390,23 +1388,129 @@ void DesktopLOKTest::testTileInvalidationCompression()
         comphelper::LibreOfficeKit::setPartInInvalidation(false);
     });
 
-    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 239, 0");
-    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 2147483767, 2147483767, 0");
-    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 239, 0");
-    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "-121, -121, 300, 300, 0");
-    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, -32767, -32767, 0");
+    // Single part merging
+    {
+        std::vector<std::tuple<int, std::string>> notifs;
+        std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
 
-    Scheduler::ProcessEventsToIdle();
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 239, 0");
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 239, 0");
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "-100, -50, 500, 650, 0");
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, -32767, -32767, 0");
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "100, 100, 200, 200, 0");
 
-/*
-    // Broken on Tinderbox, for whatever unreproducible reason.
+        Scheduler::ProcessEventsToIdle();
+
+        CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs.size());
 
-    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs.size());
+        size_t i = 0;
+        CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_TILES, (int)std::get<0>(notifs[i]));
+        CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 400, 600, 0"), std::get<1>(notifs[i++]));
+    }
 
-    size_t i = 0;
-    CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_TILES, (int)std::get<0>(notifs[i]));
-    CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 2147483767, 2147483767, 0"), std::get<1>(notifs[i++]));
-*/
+    // Part Number
+    {
+        std::vector<std::tuple<int, std::string>> notifs;
+        std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
+
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 239, 0");
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 200, 200, 1"); // Different part
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 0, 0, 2"); // Invalid
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "-121, -121, 200, 200, 0"); // Inside first
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, -32767, -32767, 1"); // Invalid
+
+        Scheduler::ProcessEventsToIdle();
+
+        CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs.size());
+
+        size_t i = 0;
+        CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_TILES, (int)std::get<0>(notifs[i]));
+        CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 200, 200, 1"), std::get<1>(notifs[i++]));
+
+        CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_TILES, (int)std::get<0>(notifs[i]));
+        CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 239, 239, 0"), std::get<1>(notifs[i++]));
+    }
+
+    // All Parts
+    {
+        std::vector<std::tuple<int, std::string>> notifs;
+        std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
+
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 239, 0"); // 0
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 200, 200, 1"); // 1: Different part
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 0, 0, -1"); // Invalid
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "-121, -121, 200, 200, -1"); // 0: All parts
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, -32767, -32767, -1"); // Invalid
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "-100, -100, 1200, 1200, -1"); // 0: All parts
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 239, 3"); // Overlapped
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "1000, 1000, 1239, 1239, 2"); // 1: Unique region
+
+        Scheduler::ProcessEventsToIdle();
+
+        CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), notifs.size());
+
+        size_t i = 0;
+        CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_TILES, (int)std::get<0>(notifs[i]));
+        CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 1100, 1100, -1"), std::get<1>(notifs[i++]));
+
+        CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_TILES, (int)std::get<0>(notifs[i]));
+        CPPUNIT_ASSERT_EQUAL(std::string("1000, 1000, 1239, 1239, 2"), std::get<1>(notifs[i++]));
+    }
+
+    // All Parts (partial)
+    {
+        std::vector<std::tuple<int, std::string>> notifs;
+        std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
+
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 200, 200, 0"); // 0
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 100, 100, 1"); // 1: Different part
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 0, 0, -1"); // Invalid
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "150, 150, 50, 50, -1"); // 2: All-parts
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, -32767, -32767, -1"); // Invalid
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "150, 150, 40, 40, 3"); // Overlapped w/ 2
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 200, 200, 4"); // 3: Unique
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "1000, 1000, 1239, 1239, 1"); // 4: Unique
+
+        Scheduler::ProcessEventsToIdle();
+
+        CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), notifs.size());
+
+        size_t i = 0;
+        CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_TILES, (int)std::get<0>(notifs[i]));
+        CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 200, 200, 0"), std::get<1>(notifs[i++]));
+
+        CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_TILES, (int)std::get<0>(notifs[i]));
+        CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 100, 100, 1"), std::get<1>(notifs[i++]));
+
+        CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_TILES, (int)std::get<0>(notifs[i]));
+        CPPUNIT_ASSERT_EQUAL(std::string("150, 150, 50, 50, -1"), std::get<1>(notifs[i++]));
+
+        CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_TILES, (int)std::get<0>(notifs[i]));
+        CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 200, 200, 4"), std::get<1>(notifs[i++]));
+
+        CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_TILES, (int)std::get<0>(notifs[i]));
+        CPPUNIT_ASSERT_EQUAL(std::string("1000, 1000, 1239, 1239, 1"), std::get<1>(notifs[i++]));
+    }
+
+    // Merge with "EMPTY"
+    {
+        std::vector<std::tuple<int, std::string>> notifs;
+        std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
+
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 239, 0");
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "EMPTY, 0");
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 240, 0");
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "-121, -121, 300, 300, 0");
+        handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, -32767, -32767, 0");
+
+        Scheduler::ProcessEventsToIdle();
+
+        CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs.size());
+
+        size_t i = 0;
+        CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_TILES, (int)std::get<0>(notifs[i]));
+        CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 1000000000, 1000000000, 0"), std::get<1>(notifs[i++]));
+    }
 }
 
 void DesktopLOKTest::testPartInInvalidation()
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index d383547..268f372 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -343,7 +343,7 @@ struct RectangleAndPart
     int m_nPart;
 
     RectangleAndPart()
-        : m_nPart(-1)
+        : m_nPart(INT_MIN)  // -1 is reserved to mean "all parts".
     {
     }
 
@@ -351,17 +351,23 @@ struct RectangleAndPart
     {
         std::stringstream ss;
         ss << m_aRectangle.toString().getStr();
-        if (m_nPart != -1)
+        if (m_nPart >= -1)
             ss << ", " << m_nPart;
         return ss.str().c_str();
     }
 
-    /// Infinite Rectangle is when both dimensions are >= 2e7.
-    // ~2 billion twips is INT_MAX, which is full-area.
+    /// Infinite Rectangle is both sides are
+    /// equal or longer than SfxLokHelper::MaxTwips.
     bool isInfinite() const
     {
-        return m_aRectangle.GetWidth() >= 2e7 &&
-               m_aRectangle.GetHeight() >= 2e7;
+        return m_aRectangle.GetWidth() >= SfxLokHelper::MaxTwips &&
+               m_aRectangle.GetHeight() >= SfxLokHelper::MaxTwips;
+    }
+
+    /// Empty Rectangle is when it has zero dimensions.
+    bool isEmpty() const
+    {
+        return m_aRectangle.IsEmpty();
     }
 
     static RectangleAndPart Create(const std::string& rPayload)
@@ -369,7 +375,7 @@ struct RectangleAndPart
         RectangleAndPart aRet;
         if (rPayload.find("EMPTY") == 0) // payload starts with "EMPTY"
         {
-            aRet.m_aRectangle = Rectangle(0, 0, INT_MAX, INT_MAX);
+            aRet.m_aRectangle = Rectangle(0, 0, SfxLokHelper::MaxTwips, SfxLokHelper::MaxTwips);
             if (comphelper::LibreOfficeKit::isPartInInvalidation())
                 aRet.m_nPart = std::stol(rPayload.substr(6));
 
@@ -377,15 +383,41 @@ struct RectangleAndPart
         }
 
         std::istringstream aStream(rPayload);
-        long nLeft, nTop, nRight, nBottom;
-        long nPart = -1;
+        long nLeft, nTop, nWidth, nHeight;
+        long nPart = INT_MIN;
         char nComma;
         if (comphelper::LibreOfficeKit::isPartInInvalidation())
-            aStream >> nLeft >> nComma >> nTop >> nComma >> nRight >> nComma >> nBottom >> nComma >> nPart;
+        {
+            aStream >> nLeft >> nComma >> nTop >> nComma >> nWidth >> nComma >> nHeight >> nComma >> nPart;
+        }
         else
-            aStream >> nLeft >> nComma >> nTop >> nComma >> nRight >> nComma >> nBottom;
+        {
+            aStream >> nLeft >> nComma >> nTop >> nComma >> nWidth >> nComma >> nHeight;
+        }
+
+        if (nWidth > 0 && nHeight > 0)
+        {
+            // The top-left corner starts at (0, 0).
+            // Anything negative is invalid.
+            if (nLeft < 0)
+            {
+                nWidth += nLeft;
+                nLeft = 0;
+            }
+
+            if (nTop < 0)
+            {
+                nHeight += nTop;
+                nTop = 0;
+            }
+
+            if (nWidth > 0 && nHeight > 0)
+            {
+                aRet.m_aRectangle = Rectangle(nLeft, nTop, nLeft + nWidth, nTop + nHeight);
+            }
+        }
+        // else leave empty rect.
 
-        aRet.m_aRectangle = Rectangle(nLeft, nTop, nLeft + nRight, nTop + nBottom);
         aRet.m_nPart = nPart;
         return aRet;
     }
@@ -602,6 +634,7 @@ void CallbackFlushHandler::queue(const int type, const char* data)
 {
     std::string payload(data ? data : "(nil)");
     //SAL_WARN("lok", "Queue: " << type << " : " << payload);
+
     if (m_bPartTilePainting)
     {
         // We drop notifications when this is set, except for important ones.
@@ -673,24 +706,6 @@ void CallbackFlushHandler::queue(const int type, const char* data)
         break;
     }
 
-    // if we have to invalidate all tiles, we can skip any new tile invalidation
-    if (type == LOK_CALLBACK_INVALIDATE_TILES)
-    {
-        const auto& pos = std::find_if(m_queue.rbegin(), m_queue.rend(),
-                [] (const queue_type::value_type& elem) { return (elem.first == LOK_CALLBACK_INVALIDATE_TILES); });
-
-        if (pos != m_queue.rend())
-        {
-            RectangleAndPart rcOld = RectangleAndPart::Create(pos->second);
-            RectangleAndPart rcNew = RectangleAndPart::Create(payload);
-            if (rcOld.isInfinite() && rcOld.m_nPart == rcNew.m_nPart)
-            {
-                SAL_WARN("lok", "Skipping queue [" << type << "]: [" << payload << "] since all tiles need to be invalidated.");
-                return;
-            }
-        }
-    }
-
     if (type == LOK_CALLBACK_TEXT_SELECTION && payload.empty())
     {
         const auto& posStart = std::find_if(m_queue.rbegin(), m_queue.rend(),
@@ -773,20 +788,50 @@ void CallbackFlushHandler::queue(const int type, const char* data)
             {
                 RectangleAndPart rcNew = RectangleAndPart::Create(payload);
                 //SAL_WARN("lok", "New: " << rcNew.toString());
+                if (rcNew.isEmpty())
+                {
+                    SAL_WARN("lok", "Skipping invalid event [" << type << "]: [" << payload << "].");
+                    return;
+                }
+
+                // If we have to invalidate all tiles, we can skip any new tile invalidation.
+                // Find the last INVALIDATE_TILES entry, if any to see if it's invalidate-all.
+                const auto& pos = std::find_if(m_queue.rbegin(), m_queue.rend(),
+                        [] (const queue_type::value_type& elem) { return (elem.first == LOK_CALLBACK_INVALIDATE_TILES); });
+                if (pos != m_queue.rend())
+                {
+                    RectangleAndPart rcOld = RectangleAndPart::Create(pos->second);
+                    if (rcOld.isInfinite() && (rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart))
+                    {
+                        SAL_WARN("lok", "Skipping queue [" << type << "]: [" << payload << "] since all tiles need to be invalidated.");
+                        return;
+                    }
+
+                    if (rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart)
+                    {
+                        // If fully overlapping.
+                        if (rcOld.m_aRectangle.IsInside(rcNew.m_aRectangle))
+                        {
+                            SAL_WARN("lok", "Skipping queue [" << type << "]: [" << payload << "] since overlaps existing all-parts.");
+                            return;
+                        }
+                    }
+                }
+
                 if (rcNew.isInfinite())
                 {
                     SAL_WARN("lok", "Have Empty [" << type << "]: [" << payload << "] so removing all with part " << rcNew.m_nPart << ".");
                     removeAll(
-                        [type, &rcNew] (const queue_type::value_type& elem) {
-                            if (elem.first == type)
+                        [&rcNew] (const queue_type::value_type& elem) {
+                            if (elem.first == LOK_CALLBACK_INVALIDATE_TILES)
                             {
+                                // Remove exiting if new is all-encompasing, or if of the same part.
                                 const RectangleAndPart rcOld = RectangleAndPart::Create(elem.second);
-                                return (rcOld.m_nPart == rcNew.m_nPart);
-                            }
-                            else
-                            {
-                                return false;
+                                return (rcNew.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart);
                             }
+
+                            // Keep others.
+                            return false;
                         }
                     );
                 }
@@ -794,30 +839,55 @@ void CallbackFlushHandler::queue(const int type, const char* data)
                 {
                     const auto rcOrig = rcNew;
 
-                    //SAL_WARN("lok", "Have [" << type << "]: [" << payload << "] so merging overlapping.");
+                    SAL_WARN("lok", "Have [" << type << "]: [" << payload << "] so merging overlapping.");
                     removeAll(
-                        [type, &rcNew] (const queue_type::value_type& elem) {
-                            if (elem.first == type)
+                        [&rcNew] (const queue_type::value_type& elem) {
+                            if (elem.first == LOK_CALLBACK_INVALIDATE_TILES)
                             {
                                 const RectangleAndPart rcOld = RectangleAndPart::Create(elem.second);
-                                if (rcOld.m_nPart != rcNew.m_nPart)
+                                if (rcNew.m_nPart != -1 && rcOld.m_nPart != -1 && rcOld.m_nPart != rcNew.m_nPart)
+                                {
+                                    SAL_WARN("lok", "Nothing to merge between new: " << rcNew.toString() << ", and old: " << rcOld.toString());
                                     return false;
+                                }
 
-                                const Rectangle rcOverlap = rcNew.m_aRectangle.GetIntersection(rcOld.m_aRectangle);
-                                bool bOverlap = (rcOverlap.GetWidth() > 0 && rcOverlap.GetHeight() > 0);
-                                SAL_WARN("lok", "Merging " << rcNew.toString() << " & " << rcOld.toString() << " => " <<
-                                         rcOverlap.toString() << " Overlap: " << bOverlap);
-                                if (bOverlap)
+                                if (rcNew.m_nPart == -1)
                                 {
-                                    rcNew.m_aRectangle.Union(rcOld.m_aRectangle);
-                                    SAL_WARN("lok", "Merged: " << rcNew.toString());
+                                    // Don't merge unless fully overlaped.
+                                    SAL_WARN("lok", "New " << rcNew.toString() << " has " << rcOld.toString() << "?");
+                                    if (rcNew.m_aRectangle.IsInside(rcOld.m_aRectangle))
+                                    {
+                                        SAL_WARN("lok", "New " << rcNew.toString() << " engulfs old " << rcOld.toString() << ".");
+                                        return true;
+                                    }
+                                }
+                                else if (rcOld.m_nPart == -1)
+                                {
+                                    // Don't merge unless fully overlaped.
+                                    SAL_WARN("lok", "Old " << rcOld.toString() << " has " << rcNew.toString() << "?");
+                                    if (rcOld.m_aRectangle.IsInside(rcNew.m_aRectangle))
+                                    {
+                                        SAL_WARN("lok", "New " << rcNew.toString() << " engulfs old " << rcOld.toString() << ".");
+                                        return true;
+                                    }
+                                }
+                                else
+                                {
+                                    const Rectangle rcOverlap = rcNew.m_aRectangle.GetIntersection(rcOld.m_aRectangle);
+                                    const bool bOverlap = !rcOverlap.IsEmpty();
+                                    SAL_WARN("lok", "Merging " << rcNew.toString() << " & " << rcOld.toString() << " => " <<
+                                            rcOverlap.toString() << " Overlap: " << bOverlap);
+                                    if (bOverlap)
+                                    {
+                                        rcNew.m_aRectangle.Union(rcOld.m_aRectangle);
+                                        SAL_WARN("lok", "Merged: " << rcNew.toString());
+                                        return true;
+                                    }
                                 }
-                                return bOverlap;
-                            }
-                            else
-                            {
-                                return false;
                             }
+
+                            // Keep others.
+                            return false;
                         }
                     );
 
@@ -829,10 +899,10 @@ void CallbackFlushHandler::queue(const int type, const char* data)
                         {
                             SAL_WARN("lok", "Error: merged rect smaller.");
                         }
-
-                        payload = rcNew.toString().getStr();
                     }
                 }
+
+                payload = rcNew.toString().getStr();
             }
             break;
 
@@ -857,7 +927,8 @@ void CallbackFlushHandler::queue(const int type, const char* data)
     }
 
     m_queue.emplace_back(type, payload);
-    SAL_WARN("lok", "Queued [" << type << "]: [" << payload << "] to have " << m_queue.size() << " entries.");
+    SAL_WARN("lok", "Queued #" << (m_queue.size() - 1) <<
+             " [" << type << "]: [" << payload << "] to have " << m_queue.size() << " entries.");
 
     lock.unlock();
     if (!IsActive())
diff --git a/include/sfx2/lokhelper.hxx b/include/sfx2/lokhelper.hxx
index 0c118fb..a6de41c 100644
--- a/include/sfx2/lokhelper.hxx
+++ b/include/sfx2/lokhelper.hxx
@@ -42,6 +42,10 @@ public:
     static void notifyOtherView(SfxViewShell* pThisView, SfxViewShell* pOtherView, int nType, const OString& rKey, const OString& rPayload);
     /// Emits a LOK_CALLBACK_INVALIDATE_TILES, but tweaks it according to setOptionalFeatures() if needed.
     static void notifyInvalidation(SfxViewShell* pThisView, const OString& rPayload);
+
+    /// A special value to signify 'infinity'.
+    /// This value is chosen such that sal_Int32 will not overflow when manipulated.
+    static const long MaxTwips = 1e9;
 };
 
 template<typename ViewShellType, typename FunctionType>
commit 6b8c3d6690445ffedefdbef56bc94c0aa7c267fc
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun Dec 4 11:00:48 2016 -0500

    Lok: better logging and warnings
    
    Change-Id: Ia933ae65197893592eeb04cb8a1a617a931ad623
    Reviewed-on: https://gerrit.libreoffice.org/31606
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit 2a5d8d7306776cc59a6311f20f32e1007dcac858)

diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 0539e97..d383547 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -601,6 +601,7 @@ void CallbackFlushHandler::callback(const int type, const char* payload, void* d
 void CallbackFlushHandler::queue(const int type, const char* data)
 {
     std::string payload(data ? data : "(nil)");
+    //SAL_WARN("lok", "Queue: " << type << " : " << payload);
     if (m_bPartTilePainting)
     {
         // We drop notifications when this is set, except for important ones.
@@ -617,7 +618,7 @@ void CallbackFlushHandler::queue(const int type, const char* data)
             type != LOK_CALLBACK_VIEW_CURSOR_VISIBLE &&
             type != LOK_CALLBACK_TEXT_SELECTION)
         {
-            SAL_WARN("lok", "Skipping while painting [" + std::to_string(type) + "]: [" + payload + "].");
+            SAL_WARN("lok", "Skipping while painting [" << type << "]: [" << payload << "].");
             return;
         }
 
@@ -634,7 +635,7 @@ void CallbackFlushHandler::queue(const int type, const char* data)
         // issuing it, instead of the absolute one that we expect.
         // This is temporary however, and, once the control is created and initialized
         // correctly, it eventually emits the correct absolute coordinates.
-        SAL_WARN("lok", "Skipping invalid event [" + std::to_string(type) + "]: [" + payload + "].");
+        SAL_WARN("lok", "Skipping invalid event [" << type << "]: [" << payload << "].");
         return;
     }
 
@@ -665,7 +666,7 @@ void CallbackFlushHandler::queue(const int type, const char* data)
 
             if (pos != m_queue.rend() && pos->second == payload)
             {
-                //SAL_WARN("lok", "Skipping queue duplicate [" + std::to_string(type) + "]: [" + payload + "].");
+                //SAL_WARN("lok", "Skipping queue duplicate [" << type << + "]: [" << payload << "].");
                 return;
             }
         }
@@ -714,6 +715,7 @@ void CallbackFlushHandler::queue(const int type, const char* data)
             case LOK_CALLBACK_GRAPHIC_SELECTION:
             case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
             case LOK_CALLBACK_INVALIDATE_TILES:
+                //SAL_WARN("lok", "Removing dups of [" << type << "]: [" << payload << "].");
                 removeAll([type] (const queue_type::value_type& elem) { return (elem.first == type); });
             break;
         }
@@ -773,7 +775,7 @@ void CallbackFlushHandler::queue(const int type, const char* data)
                 //SAL_WARN("lok", "New: " << rcNew.toString());
                 if (rcNew.isInfinite())
                 {
-                    SAL_WARN("lok", "Have Empty [" << type << "]: [" << payload << "] so removing all.");
+                    SAL_WARN("lok", "Have Empty [" << type << "]: [" << payload << "] so removing all with part " << rcNew.m_nPart << ".");
                     removeAll(
                         [type, &rcNew] (const queue_type::value_type& elem) {
                             if (elem.first == type)
@@ -792,6 +794,7 @@ void CallbackFlushHandler::queue(const int type, const char* data)
                 {
                     const auto rcOrig = rcNew;
 
+                    //SAL_WARN("lok", "Have [" << type << "]: [" << payload << "] so merging overlapping.");
                     removeAll(
                         [type, &rcNew] (const queue_type::value_type& elem) {
                             if (elem.first == type)
@@ -799,14 +802,15 @@ void CallbackFlushHandler::queue(const int type, const char* data)
                                 const RectangleAndPart rcOld = RectangleAndPart::Create(elem.second);
                                 if (rcOld.m_nPart != rcNew.m_nPart)
                                     return false;
-                                //SAL_WARN("lok", "#" << i << " Old: " << rcOld.toString());
+
                                 const Rectangle rcOverlap = rcNew.m_aRectangle.GetIntersection(rcOld.m_aRectangle);
-                                //SAL_WARN("lok", "#" << i << " Overlap: " << rcOverlap.toString());
                                 bool bOverlap = (rcOverlap.GetWidth() > 0 && rcOverlap.GetHeight() > 0);
+                                SAL_WARN("lok", "Merging " << rcNew.toString() << " & " << rcOld.toString() << " => " <<
+                                         rcOverlap.toString() << " Overlap: " << bOverlap);
                                 if (bOverlap)
                                 {
-                                    //SAL_WARN("lok", rcOld.toString() << " U " << rcNew.toString());
                                     rcNew.m_aRectangle.Union(rcOld.m_aRectangle);
+                                    SAL_WARN("lok", "Merged: " << rcNew.toString());
                                 }
                                 return bOverlap;
                             }
@@ -820,6 +824,12 @@ void CallbackFlushHandler::queue(const int type, const char* data)
                     if (rcNew.m_aRectangle != rcOrig.m_aRectangle)
                     {
                         SAL_WARN("lok", "Replacing: " << rcOrig.toString() << " by " << rcNew.toString());
+                        if (rcNew.m_aRectangle.GetWidth() < rcOrig.m_aRectangle.GetWidth() ||
+                            rcNew.m_aRectangle.GetHeight() < rcOrig.m_aRectangle.GetHeight())
+                        {
+                            SAL_WARN("lok", "Error: merged rect smaller.");
+                        }
+
                         payload = rcNew.toString().getStr();
                     }
                 }
@@ -847,6 +857,7 @@ void CallbackFlushHandler::queue(const int type, const char* data)
     }
 
     m_queue.emplace_back(type, payload);
+    SAL_WARN("lok", "Queued [" << type << "]: [" << payload << "] to have " << m_queue.size() << " entries.");
 
     lock.unlock();
     if (!IsActive())
@@ -861,6 +872,7 @@ void CallbackFlushHandler::Invoke()
     {
         std::unique_lock<std::mutex> lock(m_mutex);
 
+        //SAL_WARN("lok", "Flushing " << m_queue.size() << " elements.");
         for (auto& pair : m_queue)
         {
             const int type = pair.first;
@@ -875,7 +887,7 @@ void CallbackFlushHandler::Invoke()
                     // If the state didn't change, it's safe to ignore.
                     if (stateIt->second == payload)
                     {
-                        //SAL_WARN("lok", "Skipping duplicate [" + std::to_string(type) + "]: [" + payload + "].");
+                        //SAL_WARN("lok", "Skipping duplicate [" << type << "]: [" << payload << "].");
                         continue;
                     }
 
@@ -894,24 +906,26 @@ void CallbackFlushHandler::Invoke()
                         // If the state didn't change, it's safe to ignore.
                         if (stateIt->second == payload)
                         {
-                            //SAL_WARN("lok", "Skipping view duplicate [" + std::to_string(type) + "," + std::to_string(viewId) + "]: [" + payload + "].");
+                            //SAL_WARN("lok", "Skipping view duplicate [" << type << ',' << viewId << "]: [" << payload << "].");
                             continue;
                         }
 
                         stateIt->second = payload;
-                        //SAL_WARN("lok", "Replacing an element in view states [" + std::to_string(type) + "," + std::to_string(viewId) + "]: [" + payload + "].");
+                        //SAL_WARN("lok", "Replacing an element in view states [" << type << ',' << viewId << "]: [" << payload << "].");
                     }
                     else
                     {
                         states.emplace(type, payload);
-                        //SAL_WARN("lok", "Inserted a new element in view states: [" + std::to_string(type) + "," + std::to_string(viewId) + "]: [" + payload + "]");
+                        //SAL_WARN("lok", "Inserted a new element in view states: [" << type << ',' << viewId << "]: [" << payload << "]");
                     }
                 }
             }
 
+            //SAL_WARN("lok", "Emitting [" << type << "]: [" << payload << "].");
             m_pCallback(type, payload.c_str(), m_pData);
         }
 
+        //SAL_WARN("lok", "Done flushing.");
         m_queue.clear();
     }
 }
commit cc79b2de24f3b6dff0026fa3a8f08e14207c6e21
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sun Dec 4 17:21:55 2016 -0500

    Lok: disable DesktopLOKTest::testTileInvalidationCompression
    
    For some reason this test fails on TinderBox and for others
    while passing on Jenkins on all platforms.
    
    This might be related to the compiler version used.
    
    Disabling until resolved.
    
    Change-Id: Ic05dbb290818506c78ceaa4c416fb00e8c0dc8da
    Reviewed-on: https://gerrit.libreoffice.org/31607
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    Tested-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit a8b2e3e948c507d71fd01b219dc77838f6bf785f)

diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx
index e79866d..9acd2c7 100644
--- a/desktop/qa/desktop_lib/test_desktop_lib.cxx
+++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx
@@ -1398,11 +1398,15 @@ void DesktopLOKTest::testTileInvalidationCompression()
 
     Scheduler::ProcessEventsToIdle();
 
+/*
+    // Broken on Tinderbox, for whatever unreproducible reason.
+
     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs.size());
 
     size_t i = 0;
     CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_TILES, (int)std::get<0>(notifs[i]));
     CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 2147483767, 2147483767, 0"), std::get<1>(notifs[i++]));
+*/
 }
 
 void DesktopLOKTest::testPartInInvalidation()
commit 1b550f329c793f07ae8cac32a4c1895c6b25a513
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Thu Dec 1 00:53:11 2016 -0500

    Lok: correct tile invalidation merging
    
    Rectangles that are empty (i.e. IsEmpty() returns true)
    were incorrectly considered to mean equivalent to "EMPTY".
    The latter means full-area, while the former mean zero-area.
    
    This fixes the issue by restrict full-area to rectangles
    with 2 billion units on the each side (roughly INT_MAX) or
    more, and using this new check rather than IsEmpty().
    
    Change-Id: I12aca17267f5dd33b2932012d1d9db3545f9af6f
    Reviewed-on: https://gerrit.libreoffice.org/31458
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit 9c218858f1bd83ffdd72dd943a841cffa5a93b8c)

diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx
index 005a740..e79866d 100644
--- a/desktop/qa/desktop_lib/test_desktop_lib.cxx
+++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx
@@ -98,6 +98,7 @@ public:
     void testContextMenuWriter();
     void testContextMenuImpress();
     void testNotificationCompression();
+    void testTileInvalidationCompression();
     void testPartInInvalidation();
     void testRedlineWriter();
     void testTrackChanges();
@@ -131,6 +132,7 @@ public:
     CPPUNIT_TEST(testContextMenuWriter);
     CPPUNIT_TEST(testContextMenuImpress);
     CPPUNIT_TEST(testNotificationCompression);
+    CPPUNIT_TEST(testTileInvalidationCompression);
     CPPUNIT_TEST(testPartInInvalidation);
     CPPUNIT_TEST(testRedlineWriter);
     CPPUNIT_TEST(testTrackChanges);
@@ -1376,6 +1378,33 @@ void DesktopLOKTest::testNotificationCompression()
     CPPUNIT_ASSERT_EQUAL(std::string(".uno:AssignLayout=1"), std::get<1>(notifs[i++]));
 }
 
+void DesktopLOKTest::testTileInvalidationCompression()
+{
+    LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+    std::vector<std::tuple<int, std::string>> notifs;
+    std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, &notifs));
+
+    comphelper::LibreOfficeKit::setPartInInvalidation(true);
+    comphelper::ScopeGuard aGuard([]()
+    {
+        comphelper::LibreOfficeKit::setPartInInvalidation(false);
+    });
+
+    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 239, 0");
+    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 2147483767, 2147483767, 0");
+    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, 239, 239, 0");
+    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "-121, -121, 300, 300, 0");
+    handler->queue(LOK_CALLBACK_INVALIDATE_TILES, "0, 0, -32767, -32767, 0");
+
+    Scheduler::ProcessEventsToIdle();
+
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), notifs.size());
+
+    size_t i = 0;
+    CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_TILES, (int)std::get<0>(notifs[i]));
+    CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 2147483767, 2147483767, 0"), std::get<1>(notifs[i++]));
+}
+
 void DesktopLOKTest::testPartInInvalidation()
 {
     LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 362ee61..0539e97 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -356,11 +356,20 @@ struct RectangleAndPart
         return ss.str().c_str();
     }
 
+    /// Infinite Rectangle is when both dimensions are >= 2e7.
+    // ~2 billion twips is INT_MAX, which is full-area.
+    bool isInfinite() const
+    {
+        return m_aRectangle.GetWidth() >= 2e7 &&
+               m_aRectangle.GetHeight() >= 2e7;
+    }
+
     static RectangleAndPart Create(const std::string& rPayload)
     {
         RectangleAndPart aRet;
         if (rPayload.find("EMPTY") == 0) // payload starts with "EMPTY"
         {
+            aRet.m_aRectangle = Rectangle(0, 0, INT_MAX, INT_MAX);
             if (comphelper::LibreOfficeKit::isPartInInvalidation())
                 aRet.m_nPart = std::stol(rPayload.substr(6));
 
@@ -673,9 +682,9 @@ void CallbackFlushHandler::queue(const int type, const char* data)
         {
             RectangleAndPart rcOld = RectangleAndPart::Create(pos->second);
             RectangleAndPart rcNew = RectangleAndPart::Create(payload);
-            if (rcOld.m_aRectangle.IsEmpty() && rcOld.m_nPart == rcNew.m_nPart)
+            if (rcOld.isInfinite() && rcOld.m_nPart == rcNew.m_nPart)
             {
-                //SAL_WARN("lok", "Skipping queue [" + std::to_string(type) + "]: [" + payload + "] since all tiles need to be invalidated.");
+                SAL_WARN("lok", "Skipping queue [" << type << "]: [" << payload << "] since all tiles need to be invalidated.");
                 return;
             }
         }
@@ -762,8 +771,9 @@ void CallbackFlushHandler::queue(const int type, const char* data)
             {
                 RectangleAndPart rcNew = RectangleAndPart::Create(payload);
                 //SAL_WARN("lok", "New: " << rcNew.toString());
-                if (rcNew.m_aRectangle.IsEmpty())
+                if (rcNew.isInfinite())
                 {
+                    SAL_WARN("lok", "Have Empty [" << type << "]: [" << payload << "] so removing all.");
                     removeAll(
                         [type, &rcNew] (const queue_type::value_type& elem) {
                             if (elem.first == type)
commit 4ce103a9640508fcdea61306c2cc9305047205cf
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sat Nov 26 21:55:41 2016 -0500

    Lok: compress duplicate state-changed events
    
    STATE_CHANGED callback events that have the form
    of name=value are only removed when newer ones
    are identical. This is not very helpful since
    often the same name (i.e. state type) changes
    its value and we need to superseed older ones
    with new values.
    
    This patch makes sure that a STATE_CHANGED with
    a given name has its latest value and doesn't
    change multiple times while in the queue.
    
    Change-Id: Ibfa18359464d7137411e5846b1c6d415a0aad43d
    Reviewed-on: https://gerrit.libreoffice.org/31258
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit f80140bf3a4fa2d809167825ee04ffd0a0c19c6c)

diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx
index 7200967..005a740 100644
--- a/desktop/qa/desktop_lib/test_desktop_lib.cxx
+++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx
@@ -1322,13 +1322,15 @@ void DesktopLOKTest::testNotificationCompression()
     handler->queue(LOK_CALLBACK_CELL_CURSOR, "15 25 15 10"); // Should be dropped.
     handler->queue(LOK_CALLBACK_CELL_FORMULA, "blah"); // 12
     handler->queue(LOK_CALLBACK_SET_PART, "1"); // 13
+    handler->queue(LOK_CALLBACK_STATE_CHANGED, ".uno:AssignLayout=20"); // Superseeded
     handler->queue(LOK_CALLBACK_CURSOR_VISIBLE, ""); // Should be dropped.
     handler->queue(LOK_CALLBACK_CELL_FORMULA, "blah"); // Should be dropped.
     handler->queue(LOK_CALLBACK_SET_PART, "1"); // Should be dropped.
+    handler->queue(LOK_CALLBACK_STATE_CHANGED, ".uno:AssignLayout=1"); // 14
 
     Scheduler::ProcessEventsToIdle();
 
-    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(13), notifs.size());
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(14), notifs.size());
 
     size_t i = 0;
     CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, (int)std::get<0>(notifs[i]));
@@ -1369,6 +1371,9 @@ void DesktopLOKTest::testNotificationCompression()
 
     CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_SET_PART, (int)std::get<0>(notifs[i]));
     CPPUNIT_ASSERT_EQUAL(std::string("1"), std::get<1>(notifs[i++]));
+
+    CPPUNIT_ASSERT_EQUAL((int)LOK_CALLBACK_STATE_CHANGED, (int)std::get<0>(notifs[i]));
+    CPPUNIT_ASSERT_EQUAL(std::string(".uno:AssignLayout=1"), std::get<1>(notifs[i++]));
 }
 
 void DesktopLOKTest::testPartInInvalidation()
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 9df6471..362ee61 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -815,6 +815,24 @@ void CallbackFlushHandler::queue(const int type, const char* data)
                 }
             }
             break;
+
+            // State changes with same name override previous ones with a different value.
+            // Ex. ".uno:PageStatus=Slide 20 of 83" overwrites any previous PageStatus.
+            case LOK_CALLBACK_STATE_CHANGED:
+            {
+                // Compare the state name=value and overwrite earlier entries with same name.
+                const auto pos = payload.find('=');
+                if (pos != std::string::npos)
+                {
+                    const std::string name = payload.substr(0, pos + 1);
+                    removeAll(
+                        [type, &payload, &name] (const queue_type::value_type& elem) {
+                            return (elem.first == type) && (elem.second.compare(0, name.size(), name) == 0);
+                        }
+                    );
+                }
+            }
+            break;
         }
     }
 
commit 9d1b5591dc87210aaedae15e9ddf7f5f11de10c5
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date:   Sat Nov 26 21:12:58 2016 -0500

    Lok: number callback enum for easier debugging
    
    Since the entries and their order are part of the
    public API, and will not change, numbering them
    makes it easier to trap particular callbacks by
    their number (as that's what shows in logs and
    the debugger).
    
    Change-Id: Ife2fe3e601ce3dce0939363d748fcb54d3c85fd4
    Reviewed-on: https://gerrit.libreoffice.org/31257
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
    (cherry picked from commit 719f7cb94ce783349fb1cf366a78edd9996d3e37)

diff --git a/include/LibreOfficeKit/LibreOfficeKitEnums.h b/include/LibreOfficeKit/LibreOfficeKitEnums.h
index 187fa98..d2ccc0f 100644
--- a/include/LibreOfficeKit/LibreOfficeKitEnums.h
+++ b/include/LibreOfficeKit/LibreOfficeKitEnums.h
@@ -93,13 +93,13 @@ typedef enum
      *
      * @see LOK_FEATURE_PART_IN_INVALIDATION_CALLBACK.
      */
-    LOK_CALLBACK_INVALIDATE_TILES,
+    LOK_CALLBACK_INVALIDATE_TILES = 0,
     /**
      * The size and/or the position of the visible cursor changed.
      *
      * Rectangle format is the same as LOK_CALLBACK_INVALIDATE_TILES.
      */
-    LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR,
+    LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR = 1,
     /**
      * The list of rectangles representing the current text selection changed.
      *
@@ -108,7 +108,7 @@ typedef enum
      * LOK_CALLBACK_INVALIDATE_TILES. When there is no selection, an empty
      * string is provided.
      */
-    LOK_CALLBACK_TEXT_SELECTION,
+    LOK_CALLBACK_TEXT_SELECTION = 2,
     /**
      * The position and size of the cursor rectangle at the text
      * selection start. It is used to draw the selection handles.
@@ -118,7 +118,7 @@ typedef enum
      *
      * Rectangle format is the same as LOK_CALLBACK_INVALIDATE_TILES.
      */
-    LOK_CALLBACK_TEXT_SELECTION_START,
+    LOK_CALLBACK_TEXT_SELECTION_START = 3,
     /**
      * The position and size of the cursor rectangle at the text
      * selection end. It is used to draw the selection handles.
@@ -128,7 +128,7 @@ typedef enum
      *
      * Rectangle format is the same as LOK_CALLBACK_INVALIDATE_TILES.
      */
-    LOK_CALLBACK_TEXT_SELECTION_END,
+    LOK_CALLBACK_TEXT_SELECTION_END = 4,
     /**
      * The blinking text cursor is now visible or not.
      *
@@ -137,26 +137,26 @@ typedef enum
      * LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR once it becomes false. Payload is
      * either the "true" or the "false" string.
      */
-    LOK_CALLBACK_CURSOR_VISIBLE,
+    LOK_CALLBACK_CURSOR_VISIBLE = 5,
     /**
      * The size and/or the position of the graphic selection changed.
      *
      * Rectangle format is the same as LOK_CALLBACK_INVALIDATE_TILES.
      */
-    LOK_CALLBACK_GRAPHIC_SELECTION,
+    LOK_CALLBACK_GRAPHIC_SELECTION = 6,
 
     /**
      * User clicked on an hyperlink that should be handled by other
      * applications accordingly.
      */
-    LOK_CALLBACK_HYPERLINK_CLICKED,
+    LOK_CALLBACK_HYPERLINK_CLICKED = 7,
 
     /**
      * Emit state update to the client.
      * For example, when cursor is on bold text, this callback is triggered
      * with payload: ".uno:Bold=true"
      */
-    LOK_CALLBACK_STATE_CHANGED,
+    LOK_CALLBACK_STATE_CHANGED = 8,
 
     /**
      * Start a "status indicator" (here restricted to a progress bar type
@@ -172,25 +172,25 @@ typedef enum
      * loading a document and then constructing a LibreOfficeKitDocument
      * object.
      */
-    LOK_CALLBACK_STATUS_INDICATOR_START,
+    LOK_CALLBACK_STATUS_INDICATOR_START = 9,
 
     /**
      * Sets the numeric value of the status indicator.
      * The payload should be a percentage, an integer between 0 and 100.
      */
-    LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE,
+    LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE = 10,
 
     /**
      * Ends the status indicator.
      *
      * Not necessarily ever emitted.
      */
-    LOK_CALLBACK_STATUS_INDICATOR_FINISH,
+    LOK_CALLBACK_STATUS_INDICATOR_FINISH = 11,
 
     /**
      * No match was found for the search input
      */
-    LOK_CALLBACK_SEARCH_NOT_FOUND,
+    LOK_CALLBACK_SEARCH_NOT_FOUND = 12,
 
     /**
      * Size of the document changed.
@@ -198,14 +198,14 @@ typedef enum
      * Payload format is "width, height", i.e. clients get the new size without
      * having to do an explicit lok::Document::getDocumentSize() call.
      */
-    LOK_CALLBACK_DOCUMENT_SIZE_CHANGED,
+    LOK_CALLBACK_DOCUMENT_SIZE_CHANGED = 13,
 
     /**
      * The current part number is changed.
      *
      * Payload is a single 0-based integer.
      */
-    LOK_CALLBACK_SET_PART,
+    LOK_CALLBACK_SET_PART = 14,
 
     /**
      * Selection rectangles of the search result when find all is performed.
@@ -231,7 +231,7 @@ typedef enum
      * - searchResultSelection is an array of part-number and rectangle list
      *   pairs, in LOK_CALLBACK_SET_PART / LOK_CALLBACK_TEXT_SELECTION format.
      */
-    LOK_CALLBACK_SEARCH_RESULT_SELECTION,
+    LOK_CALLBACK_SEARCH_RESULT_SELECTION = 15,
 
     /**
      * Result of the UNO command execution when bNotifyWhenFinished was set
@@ -246,26 +246,26 @@ typedef enum
      *     // TODO "result": "..."  // UNO Any converted to JSON (not implemented yet)
      * }
      */
-    LOK_CALLBACK_UNO_COMMAND_RESULT,
+    LOK_CALLBACK_UNO_COMMAND_RESULT = 16,
 
     /**
      * The size and/or the position of the cell cursor changed.
      *
      * Rectangle format is the same as LOK_CALLBACK_INVALIDATE_TILES.
      */
-    LOK_CALLBACK_CELL_CURSOR,
+    LOK_CALLBACK_CELL_CURSOR = 17,
 
     /**
      * The current mouse pointer style.
      *
      * Payload is a css mouse pointer style.
      */
-    LOK_CALLBACK_MOUSE_POINTER,
+    LOK_CALLBACK_MOUSE_POINTER = 18,
 
     /**
      * The text content of the formula bar in Calc.
      */
-    LOK_CALLBACK_CELL_FORMULA,
+    LOK_CALLBACK_CELL_FORMULA = 19,
 
     /**
      * Loading a document requires a password.
@@ -274,7 +274,7 @@ typedef enum
      * lok::Office::setDocumentPassword().  The document cannot be loaded
      * without the password.
      */
-    LOK_CALLBACK_DOCUMENT_PASSWORD,
+    LOK_CALLBACK_DOCUMENT_PASSWORD = 20,
 
     /**
      * Editing a document requires a password.
@@ -282,7 +282,7 @@ typedef enum
      * Loading the document is blocked until the password is provided via
      * lok::Office::setDocumentPassword().
      */
-    LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY,
+    LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY = 21,
 
     /**
      * An error happened.
@@ -296,7 +296,7 @@ typedef enum
      *     "message": freeform description
      * }
      */
-    LOK_CALLBACK_ERROR,
+    LOK_CALLBACK_ERROR = 22,
 
     /**
      * Context menu structure
@@ -318,7 +318,7 @@ typedef enum
      *
      *     {"text": "label text3", "type": "command", "command": ".uno:Something3", "checktype": "checkmark|radio|auto", "checked": "true|false"}
      */
-    LOK_CALLBACK_CONTEXT_MENU,
+    LOK_CALLBACK_CONTEXT_MENU = 23,
 
     /**
      * The size and/or the position of the view cursor changed. A view cursor
@@ -334,7 +334,7 @@ typedef enum
      * - viewId is a value returned earlier by lok::Document::createView()
      * - rectangle uses the format of LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
      */
-    LOK_CALLBACK_INVALIDATE_VIEW_CURSOR,
+    LOK_CALLBACK_INVALIDATE_VIEW_CURSOR = 24,
 
     /**
      * The text selection in one of the other views has changed.
@@ -349,7 +349,7 @@ typedef enum
      * - viewId is a value returned earlier by lok::Document::createView()
      * - selection uses the format of LOK_CALLBACK_TEXT_SELECTION.
      */
-    LOK_CALLBACK_TEXT_VIEW_SELECTION,
+    LOK_CALLBACK_TEXT_VIEW_SELECTION = 25,
 
     /**
      * The cell cursor in one of the other views has changed.
@@ -364,7 +364,7 @@ typedef enum
      * - viewId is a value returned earlier by lok::Document::createView()
      * - rectangle uses the format of LOK_CALLBACK_CELL_CURSOR.
      */
-    LOK_CALLBACK_CELL_VIEW_CURSOR,
+    LOK_CALLBACK_CELL_VIEW_CURSOR = 26,
 
     /**
      * The size and/or the position of a graphic selection in one of the other
@@ -380,7 +380,7 @@ typedef enum
      * - viewId is a value returned earlier by lok::Document::createView()
      * - selection uses the format of LOK_CALLBACK_INVALIDATE_TILES.
      */
-    LOK_CALLBACK_GRAPHIC_VIEW_SELECTION,
+    LOK_CALLBACK_GRAPHIC_VIEW_SELECTION = 27,
 
     /**
      * The blinking text cursor in one of the other views is now visible or
@@ -396,7 +396,7 @@ typedef enum
      * - viewId is a value returned earlier by lok::Document::createView()
      * - visible uses the format of LOK_CALLBACK_CURSOR_VISIBLE.
      */
-    LOK_CALLBACK_VIEW_CURSOR_VISIBLE,
+    LOK_CALLBACK_VIEW_CURSOR_VISIBLE = 28,
 
     /**
      * The size and/or the position of a lock rectangle in one of the other
@@ -412,7 +412,7 @@ typedef enum
      * - viewId is a value returned earlier by lok::Document::createView()
      * - rectangle uses the format of LOK_CALLBACK_INVALIDATE_TILES.
      */
-    LOK_CALLBACK_VIEW_LOCK,
+    LOK_CALLBACK_VIEW_LOCK = 29,
 
     /**
      * The size of the change tracking table has changed.
@@ -437,7 +437,7 @@ typedef enum
      * - 'action' is either 'Add' or 'Remove', depending on if this is an
      *   insertion into the table or a removal.
      */
-    LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED,
+    LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED = 30,
 
     /**
      * An entry in the change tracking table has been modified.
@@ -461,7 +461,7 @@ typedef enum
      *
      * - 'action' is 'Modify'.
      */
-    LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED,
+    LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED = 31,
 }
 LibreOfficeKitCallbackType;
 


More information about the Libreoffice-commits mailing list