diff options
author | Ashod Nakashian <ashod.nakashian@collabora.co.uk> | 2016-12-06 00:42:58 -0500 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.co.uk> | 2017-01-16 09:09:33 +0000 |
commit | 3db1ce30ab235ad22aed71c22e4f6f52b7b88829 (patch) | |
tree | edd0a7e399dc0fd07f27ef187a4c5ab1e9e06318 | |
parent | 2abe0df63c59637ea1b00703458edf71dff8167c (diff) |
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@libreoffice.org>
Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk>
-rw-r--r-- | desktop/qa/desktop_lib/test_desktop_lib.cxx | 174 | ||||
-rw-r--r-- | desktop/source/lib/init.cxx | 183 | ||||
-rw-r--r-- | include/sfx2/lokhelper.hxx | 4 |
3 files changed, 270 insertions, 91 deletions
diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx index 9219a171dc67..59bf62079b4b 100644 --- a/desktop/qa/desktop_lib/test_desktop_lib.cxx +++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx @@ -1295,29 +1295,29 @@ void DesktopLOKTest::testNotificationCompression() std::unique_ptr<CallbackFlushHandler> handler(new CallbackFlushHandler(pDocument, callbackCompressionTest, ¬ifs)); 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 @@ -1334,6 +1334,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++])); @@ -1346,20 +1349,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++])); @@ -1377,8 +1377,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, ¬ifs)); comphelper::LibreOfficeKit::setPartInInvalidation(true); comphelper::ScopeGuard aGuard([]() @@ -1386,23 +1384,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, ¬ifs)); - 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, ¬ifs)); + + 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, ¬ifs)); + + 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, ¬ifs)); + + 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, ¬ifs)); + + 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 e3f517288150..79e284a2e7b8 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -348,7 +348,7 @@ struct RectangleAndPart int m_nPart; RectangleAndPart() - : m_nPart(-1) + : m_nPart(INT_MIN) // -1 is reserved to mean "all parts". { } @@ -356,17 +356,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) @@ -374,7 +380,7 @@ struct RectangleAndPart RectangleAndPart aRet; if (rPayload.compare(0, 5, "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)); @@ -382,15 +388,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; } @@ -607,6 +639,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. @@ -678,24 +711,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(), @@ -778,20 +793,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; } ); } @@ -799,30 +844,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; } ); @@ -834,10 +904,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; @@ -862,7 +932,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 0c118fbca896..a6de41cb880d 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> |