summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--desktop/inc/lib/init.hxx23
-rw-r--r--desktop/source/lib/init.cxx144
-rw-r--r--include/editeng/outliner.hxx2
-rw-r--r--include/sfx2/lokcallback.hxx11
-rw-r--r--include/sfx2/lokhelper.hxx18
-rw-r--r--include/sfx2/viewsh.hxx5
-rw-r--r--include/test/lokcallback.hxx24
-rw-r--r--sc/qa/unit/tiledrendering/tiledrendering.cxx15
-rw-r--r--sd/qa/unit/tiledrendering/tiledrendering.cxx15
-rw-r--r--sfx2/source/view/lokhelper.cxx69
-rw-r--r--sfx2/source/view/viewsh.cxx33
-rw-r--r--sw/inc/view.hxx1
-rw-r--r--sw/inc/viscrs.hxx3
-rw-r--r--sw/qa/core/txtnode/txtnode.cxx2
-rw-r--r--sw/qa/extras/tiledrendering/tiledrendering.cxx72
-rw-r--r--sw/source/core/crsr/viscrs.cxx85
-rw-r--r--sw/source/uibase/inc/wrtsh.hxx2
-rw-r--r--sw/source/uibase/uiview/view.cxx7
-rw-r--r--sw/source/uibase/wrtsh/wrtsh4.cxx13
-rw-r--r--test/source/lokcallback.cxx121
20 files changed, 584 insertions, 81 deletions
diff --git a/desktop/inc/lib/init.hxx b/desktop/inc/lib/init.hxx
index bc090fc208a9..01620a4f02ac 100644
--- a/desktop/inc/lib/init.hxx
+++ b/desktop/inc/lib/init.hxx
@@ -104,6 +104,8 @@ namespace desktop {
virtual void libreOfficeKitViewCallback(int nType, const char* pPayload) override;
virtual void libreOfficeKitViewCallbackWithViewId(int nType, const char* pPayload, int nViewId) override;
virtual void libreOfficeKitViewInvalidateTilesCallback(const tools::Rectangle* pRect, int nPart) override;
+ virtual void libreOfficeKitViewUpdatedCallback(int nType) override;
+ virtual void libreOfficeKitViewUpdatedCallbackPerViewId(int nType, int nViewId, int nSourceViewId) override;
private:
struct CallbackData
@@ -171,6 +173,8 @@ namespace desktop {
queue_type2::iterator toQueue2(queue_type1::iterator);
queue_type2::reverse_iterator toQueue2(queue_type1::reverse_iterator);
void queue(const int type, CallbackData& data);
+ void enqueueUpdatedTypes();
+ void enqueueUpdatedType( int type, SfxViewShell* sourceViewShell, int viewId );
/** we frequently want to scan the queue, and mostly when we do so, we only care about the element type
so we split the queue in 2 to make the scanning cache friendly. */
@@ -178,6 +182,25 @@ namespace desktop {
queue_type2 m_queue2;
std::map<int, std::string> m_states;
std::unordered_map<int, std::unordered_map<int, std::string>> m_viewStates;
+
+ // For some types only the last message matters (see isUpdatedType()) or only the last message
+ // per each viewId value matters (see isUpdatedTypePerViewId()), so instead of using push model
+ // where we'd get flooded by repeated messages (which might be costly to generate and process),
+ // the preferred way is that libreOfficeKitViewUpdatedCallback()
+ // or libreOfficeKitViewUpdatedCallbackPerViewId() get called to notify about such a message being
+ // needed, and we'll set a flag here to fetch the actual message before flushing.
+ void setUpdatedType( int nType, bool value );
+ void setUpdatedTypePerViewId( int nType, int nViewId, int nSourceViewId, bool value );
+ void resetUpdatedType( int nType);
+ void resetUpdatedTypePerViewId( int nType, int nViewId );
+ std::vector<bool> m_updatedTypes; // index is type, value is if set
+ struct PerViewIdData
+ {
+ bool set; // value is if set
+ int sourceViewId;
+ };
+ std::unordered_map<int, std::vector<PerViewIdData>> m_updatedTypesPerViewId; // key is view, index is type
+
LibreOfficeKitDocument* m_pDocument;
int m_viewId = -1; // view id of the associated SfxViewShell
LibreOfficeKitCallback m_pCallback;
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index ba3315306464..c38a6dd8d754 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -698,6 +698,23 @@ static bool lcl_isViewCallbackType(const int type)
}
}
+static bool isUpdatedType(int /*type*/)
+{
+ return false;
+}
+
+static bool isUpdatedTypePerViewId(int type)
+{
+ switch (type)
+ {
+ case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
+ case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int lcl_getViewId(const std::string& payload)
{
// this is a cheap way how to get the viewId from a JSON message; proper
@@ -1455,6 +1472,48 @@ CallbackFlushHandler::queue_type2::reverse_iterator CallbackFlushHandler::toQueu
return m_queue2.rbegin() + delta;
}
+void CallbackFlushHandler::setUpdatedType( int nType, bool value )
+{
+ assert(isUpdatedType(nType));
+ if( m_updatedTypes.size() <= o3tl::make_unsigned( nType ))
+ m_updatedTypes.resize( nType + 1 ); // new are default-constructed, i.e. false
+ m_updatedTypes[ nType ] = value;
+}
+
+void CallbackFlushHandler::resetUpdatedType( int nType )
+{
+ setUpdatedType( nType, false );
+}
+
+void CallbackFlushHandler::setUpdatedTypePerViewId( int nType, int nViewId, int nSourceViewId, bool value )
+{
+ assert(isUpdatedTypePerViewId(nType));
+ std::vector<PerViewIdData>& types = m_updatedTypesPerViewId[ nViewId ];
+ if( types.size() <= o3tl::make_unsigned( nType ))
+ types.resize( nType + 1 ); // new are default-constructed, i.e. false
+ types[ nType ] = PerViewIdData{ value, nSourceViewId };
+}
+
+void CallbackFlushHandler::resetUpdatedTypePerViewId( int nType, int nViewId )
+{
+ assert(isUpdatedTypePerViewId(nType));
+ bool allViewIds = false;
+ // Handle specially messages that do not have viewId for backwards compatibility.
+ if( nType == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR && !comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
+ allViewIds = true;
+ if( !allViewIds )
+ {
+ setUpdatedTypePerViewId( nType, nViewId, -1, false );
+ return;
+ }
+ for( auto& it : m_updatedTypesPerViewId )
+ {
+ std::vector<PerViewIdData>& types = it.second;
+ if( types.size() >= o3tl::make_unsigned( nType ))
+ types[ nType ].set = false;
+ }
+}
+
void CallbackFlushHandler::libreOfficeKitViewCallback(int nType, const char* pPayload)
{
CallbackData callbackData(pPayload);
@@ -1473,6 +1532,22 @@ void CallbackFlushHandler::libreOfficeKitViewInvalidateTilesCallback(const tools
queue(LOK_CALLBACK_INVALIDATE_TILES, callbackData);
}
+void CallbackFlushHandler::libreOfficeKitViewUpdatedCallback(int nType)
+{
+ assert(isUpdatedType( nType ));
+ std::unique_lock<std::mutex> lock(m_mutex);
+ SAL_INFO("lok", "Updated: [" << nType << "]");
+ setUpdatedType(nType, true);
+}
+
+void CallbackFlushHandler::libreOfficeKitViewUpdatedCallbackPerViewId(int nType, int nViewId, int nSourceViewId)
+{
+ assert(isUpdatedTypePerViewId( nType ));
+ std::unique_lock<std::mutex> lock(m_mutex);
+ SAL_INFO("lok", "Updated: [" << nType << "]");
+ setUpdatedTypePerViewId(nType, nViewId, nSourceViewId, true);
+}
+
void CallbackFlushHandler::queue(const int type, const char* data)
{
CallbackData callbackData(data);
@@ -1536,6 +1611,20 @@ void CallbackFlushHandler::queue(const int type, CallbackData& aCallbackData)
std::unique_lock<std::mutex> lock(m_mutex);
+ // Update types should be received via the updated callbacks for performance,
+ // getting them as normal callbacks is technically not wrong, but probably should be avoided.
+ // Reset the updated flag if we get a normal message.
+ if(isUpdatedType(type))
+ {
+ SAL_INFO("lok", "Received event with updated type [" << type << "] as normal callback");
+ resetUpdatedType(type);
+ }
+ if(isUpdatedTypePerViewId(type))
+ {
+ SAL_INFO("lok", "Received event with updated type [" << type << "] as normal callback");
+ resetUpdatedTypePerViewId(type, aCallbackData.getViewId());
+ }
+
// drop duplicate callbacks for the listed types
switch (type)
{
@@ -2028,6 +2117,58 @@ bool CallbackFlushHandler::processWindowEvent(int type, CallbackData& aCallbackD
return false;
}
+void CallbackFlushHandler::enqueueUpdatedTypes()
+{
+ if( m_updatedTypes.empty() && m_updatedTypesPerViewId.empty())
+ return;
+ SfxViewShell* viewShell = SfxViewShell::GetFirst( false,
+ [this](const SfxViewShell* shell) { return shell->GetViewShellId().get() == m_viewId; } );
+ assert(viewShell != nullptr);
+ for( size_t type = 0; type < m_updatedTypes.size(); ++type )
+ {
+ if(m_updatedTypes[ type ])
+ {
+ assert(isUpdatedType( type ));
+ enqueueUpdatedType( type, viewShell, m_viewId );
+ }
+ }
+ for( const auto& it : m_updatedTypesPerViewId )
+ {
+ int viewId = it.first;
+ const std::vector<PerViewIdData>& types = it.second;
+ for( size_t type = 0; type < types.size(); ++type )
+ {
+ if(types[ type ].set)
+ {
+ assert(isUpdatedTypePerViewId( type ));
+ SfxViewShell* sourceViewShell = viewShell;
+ const int sourceViewId = types[ type ].sourceViewId;
+ if( sourceViewId != m_viewId )
+ sourceViewShell = SfxViewShell::GetFirst( false,
+ [sourceViewId](const SfxViewShell* shell) { return shell->GetViewShellId().get() == sourceViewId; } );
+ if(sourceViewShell == nullptr)
+ {
+ SAL_INFO("lok", "View #" << sourceViewId << " no longer found for updated event [" << type << "]");
+ continue; // View removed, probably cleaning up.
+ }
+ enqueueUpdatedType( type, sourceViewShell, viewId );
+ }
+ }
+ }
+ m_updatedTypes.clear();
+ m_updatedTypesPerViewId.clear();
+}
+
+void CallbackFlushHandler::enqueueUpdatedType( int type, SfxViewShell* viewShell, int viewId )
+{
+ OString payload = viewShell->getLOKPayload( type, viewId );
+ CallbackData callbackData(payload.getStr(), viewId);
+ m_queue1.emplace_back(type);
+ m_queue2.emplace_back(callbackData);
+ SAL_INFO("lok", "Queued updated [" << type << "]: [" << callbackData.getPayload()
+ << "] to have " << m_queue1.size() << " entries.");
+}
+
void CallbackFlushHandler::Invoke()
{
comphelper::ProfileZone aZone("CallbackFlushHandler::Invoke");
@@ -2045,6 +2186,9 @@ void CallbackFlushHandler::Invoke()
std::scoped_lock<std::mutex> lock(m_mutex);
+ // Append messages for updated types, fetch them only now.
+ enqueueUpdatedTypes();
+
SAL_INFO("lok", "Flushing " << m_queue1.size() << " elements.");
auto it1 = m_queue1.begin();
auto it2 = m_queue2.begin();
diff --git a/include/editeng/outliner.hxx b/include/editeng/outliner.hxx
index c269bf49046f..5003a671d35e 100644
--- a/include/editeng/outliner.hxx
+++ b/include/editeng/outliner.hxx
@@ -366,6 +366,8 @@ public:
virtual void libreOfficeKitViewCallback(int nType, const char* pPayload) const = 0;
virtual void libreOfficeKitViewCallbackWithViewId(int nType, const char* pPayload, int nViewId) const = 0;
virtual void libreOfficeKitViewInvalidateTilesCallback(const tools::Rectangle* pRect, int nPart) const = 0;
+ virtual void libreOfficeKitViewUpdatedCallback(int nType) const = 0;
+ virtual void libreOfficeKitViewUpdatedCallbackPerViewId(int nType, int nViewId, int nSourceViewId) const = 0;
virtual ViewShellId GetViewShellId() const = 0;
virtual ViewShellDocId GetDocId() const = 0;
/// Wrapper around SfxLokHelper::notifyOtherViews().
diff --git a/include/sfx2/lokcallback.hxx b/include/sfx2/lokcallback.hxx
index d01e7203205e..6f59402d0cec 100644
--- a/include/sfx2/lokcallback.hxx
+++ b/include/sfx2/lokcallback.hxx
@@ -11,6 +11,8 @@
#include <sal/types.h>
+#include <vector>
+
namespace tools
{
class Rectangle;
@@ -37,6 +39,15 @@ public:
// comphelper::LibreOfficeKit::isPartInInvalidation() is not set
virtual void libreOfficeKitViewInvalidateTilesCallback(const tools::Rectangle* pRect, int nPart)
= 0;
+ // A message of the given type should be sent, for performance purpose only a notification
+ // is given here, details about the message should be queried from SfxViewShell when necessary.
+ // This is used for messages that are generated often but only the last one is needed.
+ virtual void libreOfficeKitViewUpdatedCallback(int nType) = 0;
+ // Like libreOfficeKitViewUpdatedCallback(), but a last message is needed for each nViewId value.
+ // SfxViewShell:getLOKPayload() will be called on nSourceViewId view.
+ virtual void libreOfficeKitViewUpdatedCallbackPerViewId(int nType, int nViewId,
+ int nSourceViewId)
+ = 0;
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/sfx2/lokhelper.hxx b/include/sfx2/lokhelper.hxx
index cf2c1c9c72bb..4ac50a19cc69 100644
--- a/include/sfx2/lokhelper.hxx
+++ b/include/sfx2/lokhelper.hxx
@@ -110,14 +110,28 @@ public:
static void notifyDocumentSizeChangedAllViews(vcl::ITiledRenderable* pDoc, bool bInvalidateAll = true);
/// Emits a LOK_CALLBACK_INVALIDATE_TILES, but tweaks it according to setOptionalFeatures() if needed.
static void notifyInvalidation(SfxViewShell const* pThisView, tools::Rectangle const *);
- /// Emits a LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, but tweaks it according to setOptionalFeatures() if needed.
- static void notifyVisCursorInvalidation(OutlinerViewShell const* pThisView, const OString& rRectangle, bool bMispelledWord = false, const OString& rHyperlink = "");
/// Notifies all views with the given type and payload.
static void notifyAllViews(int nType, const OString& rPayload);
/// Notify about the editing context change.
static void notifyContextChange(SfxViewShell const* pViewShell, const OUString& aApplication, const OUString& aContext);
+ // Notify about the given type needing an update.
+ static void notifyUpdate(SfxViewShell const* pViewShell, int nType);
+ // Notify about the given type needing a per-viewid update.
+ static void notifyUpdatePerViewId(SfxViewShell const* pViewShell, int nType);
+ /// Same as notifyUpdatePerViewId(), pTargetShell will be notified, relevant viewId in pViewShell,
+ /// pSourceView->getLOKPayload() will be called to get the data.
+ static void notifyUpdatePerViewId(SfxViewShell const* pTargetShell, SfxViewShell const* pViewShell,
+ SfxViewShell const* pSourceShell, int nType);
+ // Notify other views about the given type needing a per-viewid update.
+ static void notifyOtherViewsUpdatePerViewId(SfxViewShell const* pViewShell, int nType);
+
+ static OString makePayloadJSON(const SfxViewShell* pThisView, int nViewId, std::string_view rKey, const OString& rPayload);
+ /// Makes a LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR payload, but tweaks it according to setOptionalFeatures() if needed.
+ static OString makeVisCursorInvalidation(int nViewId, const OString& rRectangle,
+ bool bMispelledWord = false, const OString& rHyperlink = "");
+
/// Helper for posting async key event
static void postKeyEventAsync(const VclPtr<vcl::Window> &xWindow,
int nType, int nCharCode, int nKeyCode, int nRepeat = 0);
diff --git a/include/sfx2/viewsh.hxx b/include/sfx2/viewsh.hxx
index 89c9cd6c0c7c..e35c6848c9e1 100644
--- a/include/sfx2/viewsh.hxx
+++ b/include/sfx2/viewsh.hxx
@@ -343,6 +343,11 @@ public:
virtual void libreOfficeKitViewInvalidateTilesCallback(const tools::Rectangle* pRect, int nPart) const override;
// Performs any pending calls to libreOfficeKitViewInvalidateTilesCallback() as necessary.
virtual void flushPendingLOKInvalidateTiles();
+ virtual void libreOfficeKitViewUpdatedCallback(int nType) const override;
+ virtual void libreOfficeKitViewUpdatedCallbackPerViewId(int nType, int nViewId, int nSourceViewId) const override;
+ // Returns current payload for nType, after libreOfficeKitViewUpdatedCallback() or
+ // libreOfficeKitViewUpdatedCallbackPerViewId() were called.
+ virtual OString getLOKPayload(int nType, int nViewId) const;
/// Set if we are doing tiled searching.
void setTiledSearching(bool bTiledSearching);
diff --git a/include/test/lokcallback.hxx b/include/test/lokcallback.hxx
index f7372bc7ec80..a3f383bcec5e 100644
--- a/include/test/lokcallback.hxx
+++ b/include/test/lokcallback.hxx
@@ -15,28 +15,48 @@
#include <sfx2/lokcallback.hxx>
#include <vcl/idle.hxx>
+#include <vector>
+
/**
A helper to convert SfxLokCallbackInterface to a LIbreOfficeKitCallback for tests.
It reimplements the specialized callbacks and converts them to the generic type/payload
callback.
*/
-
class OOO_DLLPUBLIC_TEST TestLokCallbackWrapper final : public SfxLokCallbackInterface, public Idle
{
public:
TestLokCallbackWrapper(LibreOfficeKitCallback callback, void* data);
+ /// Discard all possibly still held events.
+ void clear();
+ /// Set the view id of the associated SfxViewShell.
+ void setLOKViewId(int viewId) { m_viewId = viewId; }
virtual void libreOfficeKitViewCallback(int nType, const char* pPayload) override;
virtual void libreOfficeKitViewCallbackWithViewId(int nType, const char* pPayload,
int nViewId) override;
virtual void libreOfficeKitViewInvalidateTilesCallback(const tools::Rectangle* pRect,
int nPart) override;
+ virtual void libreOfficeKitViewUpdatedCallback(int nType) override;
+ virtual void libreOfficeKitViewUpdatedCallbackPerViewId(int nType, int nViewId,
+ int nSourceViewId) override;
virtual void Invoke() override;
private:
- void callCallback(int nType, const char* pPayload);
+ void callCallback(int nType, const char* pPayload, int nViewId);
+ void startTimer();
+ void flushLOKData();
+ void discardUpdatedTypes(int nType, int nViewId);
LibreOfficeKitCallback m_callback;
void* m_data;
+ int m_viewId = -1; // the associated SfxViewShell
+ std::vector<int> m_updatedTypes; // value is type
+ struct PerViewIdData
+ {
+ int type;
+ int viewId;
+ int sourceViewId;
+ };
+ std::vector<PerViewIdData> m_updatedTypesPerViewId;
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sc/qa/unit/tiledrendering/tiledrendering.cxx b/sc/qa/unit/tiledrendering/tiledrendering.cxx
index 351f1f12644f..d8aef459e4d5 100644
--- a/sc/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sc/qa/unit/tiledrendering/tiledrendering.cxx
@@ -179,6 +179,7 @@ public:
private:
ScModelObj* createDoc(const char* pName);
+ void setupLibreOfficeKitViewCallback(SfxViewShell* pViewShell);
static void callback(int nType, const char* pPayload, void* pData);
void callbackImpl(int nType, const char* pPayload);
@@ -220,6 +221,7 @@ void ScTiledRenderingTest::tearDown()
}
mxComponent->dispose();
}
+ m_callbackWrapper.clear();
comphelper::LibreOfficeKit::setActive(false);
test::BootstrapFixture::tearDown();
@@ -236,6 +238,12 @@ ScModelObj* ScTiledRenderingTest::createDoc(const char* pName)
return pModelObj;
}
+void ScTiledRenderingTest::setupLibreOfficeKitViewCallback(SfxViewShell* pViewShell)
+{
+ pViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ m_callbackWrapper.setLOKViewId(SfxLokHelper::getView(pViewShell));
+}
+
void ScTiledRenderingTest::callback(int nType, const char* pPayload, void* pData)
{
static_cast<ScTiledRenderingTest*>(pData)->callbackImpl(nType, pPayload);
@@ -398,7 +406,7 @@ void ScTiledRenderingTest::testDocumentSize()
ScTabViewShell* pViewShell = pDocSh->GetBestViewShell(false);
CPPUNIT_ASSERT(pViewShell);
- pViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pViewShell);
// check initial document size
Size aDocSize = pModelObj->getDocumentSize();
@@ -598,6 +606,7 @@ public:
mpViewShell = SfxViewShell::Current();
mpViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper);
mnView = SfxLokHelper::getView();
+ m_callbackWrapper.setLOKViewId( mnView );
if (!bDeleteListenerOnDestruct)
mpViewShell = nullptr;
}
@@ -781,7 +790,7 @@ void ScTiledRenderingTest::testDocumentSizeChanged()
// Load a document that doesn't have much content.
createDoc("small.ods");
- SfxViewShell::Current()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(SfxViewShell::Current());
// Go to the A30 cell -- that will extend the document size.
uno::Sequence<beans::PropertyValue> aPropertyValues =
@@ -883,7 +892,7 @@ void ScTiledRenderingTest::testColRowResize()
ScTabViewShell* pViewShell = pDocSh->GetBestViewShell(false);
CPPUNIT_ASSERT(pViewShell);
- pViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pViewShell);
ScDocument& rDoc = pDocSh->GetDocument();
diff --git a/sd/qa/unit/tiledrendering/tiledrendering.cxx b/sd/qa/unit/tiledrendering/tiledrendering.cxx
index e2a0e8a46501..ea7bfc5c11bb 100644
--- a/sd/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sd/qa/unit/tiledrendering/tiledrendering.cxx
@@ -203,6 +203,7 @@ public:
private:
SdXImpressDocument* createDoc(const char* pName, const uno::Sequence<beans::PropertyValue>& rArguments = uno::Sequence<beans::PropertyValue>());
+ void setupLibreOfficeKitViewCallback(SfxViewShell& pViewShell);
static void callback(int nType, const char* pPayload, void* pData);
void callbackImpl(int nType, const char* pPayload);
xmlDocUniquePtr parseXmlDump();
@@ -252,6 +253,7 @@ void SdTiledRenderingTest::tearDown()
if (m_pXmlBuffer)
xmlBufferFree(m_pXmlBuffer);
+ m_callbackWrapper.clear();
comphelper::LibreOfficeKit::setActive(false);
test::BootstrapFixture::tearDown();
@@ -268,6 +270,12 @@ SdXImpressDocument* SdTiledRenderingTest::createDoc(const char* pName, const uno
return pImpressDocument;
}
+void SdTiledRenderingTest::setupLibreOfficeKitViewCallback(SfxViewShell& pViewShell)
+{
+ pViewShell.setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ m_callbackWrapper.setLOKViewId(SfxLokHelper::getView(&pViewShell));
+}
+
void SdTiledRenderingTest::callback(int nType, const char* pPayload, void* pData)
{
static_cast<SdTiledRenderingTest*>(pData)->callbackImpl(nType, pPayload);
@@ -404,7 +412,7 @@ void SdTiledRenderingTest::testRegisterCallback()
{
SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
- pViewShell->GetViewShellBase().setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pViewShell->GetViewShellBase());
// Start text edit of the empty title shape.
SdPage* pActualPage = pViewShell->GetActualPage();
@@ -634,7 +642,7 @@ void SdTiledRenderingTest::testInsertDeletePage()
{
SdXImpressDocument* pXImpressDocument = createDoc("insert-delete.odp");
sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
- pViewShell->GetViewShellBase().setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pViewShell->GetViewShellBase());
SdDrawDocument* pDoc = pXImpressDocument->GetDocShell()->GetDoc();
CPPUNIT_ASSERT(pDoc);
@@ -921,6 +929,7 @@ public:
mpViewShell = SfxViewShell::Current();
mpViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper);
mnView = SfxLokHelper::getView();
+ m_callbackWrapper.setLOKViewId( mnView );
}
~ViewCallback()
@@ -2534,7 +2543,7 @@ void SdTiledRenderingTest::testCutSelectionChange()
CPPUNIT_ASSERT(pXImpressDocument);
sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
- pViewShell->GetViewShellBase().setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pViewShell->GetViewShellBase());
Scheduler::ProcessEventsToIdle();
// Select first text object
diff --git a/sfx2/source/view/lokhelper.cxx b/sfx2/source/view/lokhelper.cxx
index 35b61798c272..cd93f9b4dd55 100644
--- a/sfx2/source/view/lokhelper.cxx
+++ b/sfx2/source/view/lokhelper.cxx
@@ -357,15 +357,21 @@ static OString lcl_generateJSON(const SfxViewShell* pView, const boost::property
return OString(aString.c_str(), aString.size()).trim();
}
-static inline OString lcl_generateJSON(const SfxViewShell* pView, std::string_view rKey,
+static inline OString lcl_generateJSON(const SfxViewShell* pView, int nViewId, std::string_view rKey,
const OString& rPayload)
{
assert(pView != nullptr && "pView must be valid");
- return OString::Concat("{ \"viewId\": \"") + OString::number(SfxLokHelper::getView(pView))
+ return OString::Concat("{ \"viewId\": \"") + OString::number(nViewId)
+ "\", \"part\": \"" + OString::number(pView->getPart()) + "\", \"" + rKey + "\": \""
+ lcl_sanitizeJSONAsValue(rPayload) + "\" }";
}
+static inline OString lcl_generateJSON(const SfxViewShell* pView, std::string_view rKey,
+ const OString& rPayload)
+{
+ return lcl_generateJSON(pView, SfxLokHelper::getView(pView), rKey, rPayload);
+}
+
void SfxLokHelper::notifyOtherView(const SfxViewShell* pThisView, SfxViewShell const* pOtherView,
int nType, std::string_view rKey, const OString& rPayload)
{
@@ -451,6 +457,11 @@ void SfxLokHelper::notifyOtherViews(const SfxViewShell* pThisView, int nType,
}
}
+OString SfxLokHelper::makePayloadJSON(const SfxViewShell* pThisView, int nViewId, std::string_view rKey, const OString& rPayload)
+{
+ return lcl_generateJSON(pThisView, nViewId, rKey, rPayload);
+}
+
namespace {
OUString lcl_getNameForSlot(const SfxViewShell* pShell, sal_uInt16 nWhich)
{
@@ -565,25 +576,20 @@ void SfxLokHelper::notifyDocumentSizeChangedAllViews(vcl::ITiledRenderable* pDoc
}
}
-void SfxLokHelper::notifyVisCursorInvalidation(OutlinerViewShell const* pThisView, const OString& rRectangle, bool bMispelledWord, const OString& rHyperlink)
+OString SfxLokHelper::makeVisCursorInvalidation(int nViewId, const OString& rRectangle,
+ bool bMispelledWord, const OString& rHyperlink)
{
- if (DisableCallbacks::disabled())
- return;
-
if (comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
{
OString sHyperlink = rHyperlink.isEmpty() ? "{}" : rHyperlink;
- OString sPayload = OString::Concat("{ \"viewId\": \"") + OString::number(SfxLokHelper::getView()) +
+ return OString::Concat("{ \"viewId\": \"") + OString::number(nViewId) +
"\", \"rectangle\": \"" + rRectangle +
"\", \"mispelledWord\": \"" + OString::number(bMispelledWord ? 1 : 0) +
"\", \"hyperlink\": " + sHyperlink + " }";
- const int viewId = SfxLokHelper::getView();
- pThisView->libreOfficeKitViewCallbackWithViewId(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, sPayload.getStr(), viewId);
}
else
{
- OString sPayload = rRectangle;
- pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, sPayload.getStr());
+ return rRectangle;
}
}
@@ -615,6 +621,47 @@ void SfxLokHelper::notifyContextChange(SfxViewShell const* pViewShell, const OUS
pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CONTEXT_CHANGED, aBuffer.getStr());
}
+void SfxLokHelper::notifyUpdate(SfxViewShell const* pThisView, int nType)
+{
+ if (DisableCallbacks::disabled())
+ return;
+
+ pThisView->libreOfficeKitViewUpdatedCallback(nType);
+}
+
+void SfxLokHelper::notifyUpdatePerViewId(SfxViewShell const* pThisView, int nType)
+{
+ notifyUpdatePerViewId(pThisView, pThisView, pThisView, nType);
+}
+
+void SfxLokHelper::notifyUpdatePerViewId(SfxViewShell const* pTargetShell, SfxViewShell const* pViewShell,
+ SfxViewShell const* pSourceShell, int nType)
+{
+ if (DisableCallbacks::disabled())
+ return;
+
+ int viewId = SfxLokHelper::getView(pViewShell);
+ int sourceViewId = SfxLokHelper::getView(pSourceShell);
+ pTargetShell->libreOfficeKitViewUpdatedCallbackPerViewId(nType, viewId, sourceViewId);
+}
+
+void SfxLokHelper::notifyOtherViewsUpdatePerViewId(SfxViewShell const* pThisView, int nType)
+{
+ assert(pThisView != nullptr && "pThisView must be valid");
+ if (DisableCallbacks::disabled())
+ return;
+
+ int viewId = SfxLokHelper::getView(pThisView);
+ const ViewShellDocId nCurrentDocId = pThisView->GetDocId();
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pViewShell != pThisView && nCurrentDocId == pViewShell->GetDocId())
+ pViewShell->libreOfficeKitViewUpdatedCallbackPerViewId(nType, viewId, viewId);
+
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
namespace
{
diff --git a/sfx2/source/view/viewsh.cxx b/sfx2/source/view/viewsh.cxx
index 5766fb48205c..ecfd3f97bba4 100644
--- a/sfx2/source/view/viewsh.cxx
+++ b/sfx2/source/view/viewsh.cxx
@@ -1522,6 +1522,32 @@ void SfxViewShell::libreOfficeKitViewCallback(int nType, const char* pPayload) c
<< lokCallbackTypeToString(nType) << ": [" << pPayload << ']');
}
+void SfxViewShell::libreOfficeKitViewUpdatedCallback(int nType) const
+{
+ if (ignoreLibreOfficeKitViewCallback(nType, pImpl.get()))
+ return;
+ if (pImpl->m_pLibreOfficeKitViewCallback)
+ pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewUpdatedCallback(nType);
+ else
+ SAL_INFO(
+ "sfx.view",
+ "SfxViewShell::libreOfficeKitViewUpdatedCallback no callback set! Dropped payload of type "
+ << lokCallbackTypeToString(nType));
+}
+
+void SfxViewShell::libreOfficeKitViewUpdatedCallbackPerViewId(int nType, int nViewId, int nSourceViewId) const
+{
+ if (ignoreLibreOfficeKitViewCallback(nType, pImpl.get()))
+ return;
+ if (pImpl->m_pLibreOfficeKitViewCallback)
+ pImpl->m_pLibreOfficeKitViewCallback->libreOfficeKitViewUpdatedCallbackPerViewId(nType, nViewId, nSourceViewId);
+ else
+ SAL_INFO(
+ "sfx.view",
+ "SfxViewShell::libreOfficeKitViewUpdatedCallbackPerViewId no callback set! Dropped payload of type "
+ << lokCallbackTypeToString(nType));
+}
+
void SfxViewShell::afterCallbackRegistered()
{
}
@@ -1531,6 +1557,13 @@ void SfxViewShell::flushPendingLOKInvalidateTiles()
// SfxViewShell itself does not delay any tile invalidations.
}
+OString SfxViewShell::getLOKPayload(int nType, int /*nViewId*/) const
+{
+ // SfxViewShell itself currently doesn't handle any updated-payload types.
+ SAL_WARN("sfx.view", "SfxViewShell::getLOKPayload unhandled type " << lokCallbackTypeToString(nType));
+ abort();
+}
+
vcl::Window* SfxViewShell::GetEditWindowForActiveOLEObj() const
{
vcl::Window* pEditWin = nullptr;
diff --git a/sw/inc/view.hxx b/sw/inc/view.hxx
index 7a5423732a6e..2f6381469d34 100644
--- a/sw/inc/view.hxx
+++ b/sw/inc/view.hxx
@@ -693,6 +693,7 @@ public:
virtual tools::Rectangle getLOKVisibleArea() const override;
virtual void flushPendingLOKInvalidateTiles() override;
+ virtual OString getLOKPayload(int nType, int nViewId) const override;
};
inline tools::Long SwView::GetXScroll() const
diff --git a/sw/inc/viscrs.hxx b/sw/inc/viscrs.hxx
index 9ad0753f0bc0..c8b8b356f7e3 100644
--- a/sw/inc/viscrs.hxx
+++ b/sw/inc/viscrs.hxx
@@ -43,6 +43,7 @@ class SW_DLLPUBLIC SwVisibleCursor
/// For LibreOfficeKit only - remember what page we were at the last time.
sal_uInt16 m_nPageLastTime;
+ SwRect m_aLastLOKRect;
bool m_bIsVisible;
bool m_bIsDragCursor;
@@ -58,6 +59,8 @@ public:
void SetDragCursor( bool bFlag = true ) { m_bIsDragCursor = bFlag; }
void SetPosAndShow(SfxViewShell const * pViewShell);
const vcl::Cursor& GetTextCursor() const;
+
+ OString getLOKPayload(int nType, int nViewId) const;
};
// From here classes/methods for selections.
diff --git a/sw/qa/core/txtnode/txtnode.cxx b/sw/qa/core/txtnode/txtnode.cxx
index 6dc5b55cc28f..72763909122f 100644
--- a/sw/qa/core/txtnode/txtnode.cxx
+++ b/sw/qa/core/txtnode/txtnode.cxx
@@ -14,6 +14,7 @@
#include <sfx2/viewsh.hxx>
#include <vcl/gdimtf.hxx>
#include <vcl/scheduler.hxx>
+#include <sfx2/lokhelper.hxx>
#include <test/lokcallback.hxx>
#include <IDocumentStatistics.hxx>
@@ -134,6 +135,7 @@ CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, testTitleFieldInvalidate)
ViewCallback aCallback;
TestLokCallbackWrapper aCallbackWrapper(&ViewCallback::callback, &aCallback);
pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&aCallbackWrapper);
+ aCallbackWrapper.setLOKViewId(SfxLokHelper::getView(pWrtShell->GetSfxViewShell()));
Scheduler::ProcessEventsToIdle();
aCallback.m_nInvalidations = 0;
diff --git a/sw/qa/extras/tiledrendering/tiledrendering.cxx b/sw/qa/extras/tiledrendering/tiledrendering.cxx
index 7dab608c6323..4ad4159a295c 100644
--- a/sw/qa/extras/tiledrendering/tiledrendering.cxx
+++ b/sw/qa/extras/tiledrendering/tiledrendering.cxx
@@ -246,6 +246,7 @@ public:
private:
SwXTextDocument* createDoc(const char* pName = nullptr);
+ void setupLibreOfficeKitViewCallback(SfxViewShell* pViewShell);
static void callback(int nType, const char* pPayload, void* pData);
void callbackImpl(int nType, const char* pPayload);
// First invalidation.
@@ -305,6 +306,7 @@ void SwTiledRenderingTest::tearDown()
mxComponent->dispose();
mxComponent.clear();
}
+ m_callbackWrapper.clear();
comphelper::LibreOfficeKit::setActive(false);
test::BootstrapFixture::tearDown();
@@ -323,6 +325,12 @@ SwXTextDocument* SwTiledRenderingTest::createDoc(const char* pName)
return pTextDocument;
}
+void SwTiledRenderingTest::setupLibreOfficeKitViewCallback(SfxViewShell* pViewShell)
+{
+ pViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ m_callbackWrapper.setLOKViewId(SfxLokHelper::getView(pViewShell));
+}
+
void SwTiledRenderingTest::callback(int nType, const char* pPayload, void* pData)
{
static_cast<SwTiledRenderingTest*>(pData)->callbackImpl(nType, pPayload);
@@ -441,7 +449,7 @@ void SwTiledRenderingTest::testRegisterCallback()
{
SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
// Insert a character at the beginning of the document.
pWrtShell->Insert("x");
Scheduler::ProcessEventsToIdle();
@@ -625,7 +633,7 @@ void SwTiledRenderingTest::testSearch()
{
SwXTextDocument* pXTextDocument = createDoc("search.odt");
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
SwNodeOffset nNode = pWrtShell->getShellCursor(false)->Start()->nNode.GetNode().GetIndex();
// First hit, in the second paragraph, before the shape.
@@ -690,7 +698,7 @@ void SwTiledRenderingTest::testSearchTextFrame()
{
SwXTextDocument* pXTextDocument = createDoc("search.odt");
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
{
{"SearchItem.SearchString", uno::makeAny(OUString("TextFrame"))},
@@ -705,7 +713,7 @@ void SwTiledRenderingTest::testSearchTextFrameWrapAround()
{
SwXTextDocument* pXTextDocument = createDoc("search.odt");
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
{
{"SearchItem.SearchString", uno::makeAny(OUString("TextFrame"))},
@@ -723,7 +731,7 @@ void SwTiledRenderingTest::testDocumentSizeChanged()
// Get the current document size.
SwXTextDocument* pXTextDocument = createDoc("2-pages.odt");
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
Size aSize = pXTextDocument->getDocumentSize();
// Delete the second page and see how the size changes.
@@ -739,7 +747,7 @@ void SwTiledRenderingTest::testSearchAll()
{
SwXTextDocument* pXTextDocument = createDoc("search.odt");
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
{
{"SearchItem.SearchString", uno::makeAny(OUString("shape"))},
@@ -757,7 +765,7 @@ void SwTiledRenderingTest::testSearchAllNotifications()
{
SwXTextDocument* pXTextDocument = createDoc("search.odt");
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
// Reset notification counter before search.
m_nSelectionBeforeSearchResult = 0;
uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
@@ -784,7 +792,7 @@ void SwTiledRenderingTest::testPageDownInvalidation()
}));
pXTextDocument->initializeForTiledRendering(aPropertyValues);
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
comphelper::dispatchCommand(".uno:PageDown", uno::Sequence<beans::PropertyValue>());
// This was 2.
@@ -855,6 +863,7 @@ namespace {
mpViewShell = pViewShell ? pViewShell : SfxViewShell::Current();
mpViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper);
mnView = SfxLokHelper::getView();
+ m_callbackWrapper.setLOKViewId( mnView );
}
~ViewCallback()
@@ -1056,6 +1065,7 @@ void SwTiledRenderingTest::testViewCursors()
SfxLokHelper::createView();
ViewCallback aView2;
+ Scheduler::ProcessEventsToIdle();
CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
@@ -1074,6 +1084,7 @@ void SwTiledRenderingTest::testViewCursors()
pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 5, /*bBasicCall=*/false);
// Create a selection on the word.
pWrtShell->SelWrd();
+ Scheduler::ProcessEventsToIdle();
SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
// Did we indeed manage to select the second word?
CPPUNIT_ASSERT_EQUAL(OUString("bbb"), pShellCursor->GetText());
@@ -1535,7 +1546,7 @@ void SwTiledRenderingTest::testTrackChangesCallback()
// Load a document.
SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
// Turn on track changes and type "x".
uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
@@ -1562,7 +1573,7 @@ void SwTiledRenderingTest::testRedlineUpdateCallback()
// Load a document.
SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
// Turn on track changes, type "xx" and delete the second one.
uno::Reference<beans::XPropertySet> xPropertySet(mxComponent, uno::UNO_QUERY);
@@ -2351,7 +2362,7 @@ void SwTiledRenderingTest::testSplitNodeRedlineCallback()
// Load a document.
SwXTextDocument* pXTextDocument = createDoc("splitnode_redline_callback.fodt");
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
// 1. test case
// Move cursor between the two tracked changes
@@ -2409,7 +2420,7 @@ void SwTiledRenderingTest::testDeleteNodeRedlineCallback()
// Load a document.
SwXTextDocument* pXTextDocument = createDoc("removenode_redline_callback.fodt");
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
// 1. test case
// Move cursor between the two tracked changes
@@ -2474,7 +2485,6 @@ void SwTiledRenderingTest::testVisCursorInvalidation()
ViewCallback aView2;
Scheduler::ProcessEventsToIdle();
-
// Move visible cursor in the first view
SfxLokHelper::setView(nView1);
Scheduler::ProcessEventsToIdle();
@@ -2496,6 +2506,7 @@ void SwTiledRenderingTest::testVisCursorInvalidation()
// Insert text in the second view which moves the other view's cursor too
SfxLokHelper::setView(nView2);
+ Scheduler::ProcessEventsToIdle();
aView1.m_bOwnCursorInvalidated = false;
aView1.m_bViewCursorInvalidated = false;
aView2.m_bOwnCursorInvalidated = false;
@@ -2509,12 +2520,19 @@ void SwTiledRenderingTest::testVisCursorInvalidation()
CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
+ // Check that views have correct location for the other's cursor.
+ CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor, aView2.m_aViewCursor);
+ CPPUNIT_ASSERT_EQUAL(aView2.m_aOwnCursor, aView1.m_aViewCursor);
+ // Their cursors should be on the same line, first view's more to the right.
+ CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor.getY(), aView2.m_aOwnCursor.getY());
+ CPPUNIT_ASSERT_GREATER(aView2.m_aOwnCursor.getX(), aView1.m_aOwnCursor.getX());
// Do the same as before, but set the related compatibility flag first
SfxLokHelper::setView(nView2);
comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(true);
+ Scheduler::ProcessEventsToIdle();
aView1.m_bOwnCursorInvalidated = false;
aView1.m_bViewCursorInvalidated = false;
aView2.m_bOwnCursorInvalidated = false;
@@ -2530,6 +2548,11 @@ void SwTiledRenderingTest::testVisCursorInvalidation()
CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
CPPUNIT_ASSERT_EQUAL(nView2, aView2.m_nOwnCursorInvalidatedBy);
+ CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor, aView2.m_aViewCursor);
+ CPPUNIT_ASSERT_EQUAL(aView2.m_aOwnCursor, aView1.m_aViewCursor);
+ // Their cursors should be on the same line, first view's more to the right.
+ CPPUNIT_ASSERT_EQUAL(aView1.m_aOwnCursor.getY(), aView2.m_aOwnCursor.getY());
+ CPPUNIT_ASSERT_GREATER(aView2.m_aOwnCursor.getX(), aView1.m_aOwnCursor.getX());
comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(false);
}
@@ -2786,7 +2809,7 @@ void SwTiledRenderingTest::testRedlineNotificationDuringSave()
// It's an empty document, just settings.xml and content.xml are custom.
SwXTextDocument* pXTextDocument = createDoc("redline-notification-during-save.odt");
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
// Save the document.
utl::MediaDescriptor aMediaDescriptor;
@@ -2802,7 +2825,8 @@ void SwTiledRenderingTest::testHyperlink()
comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(true);
SwXTextDocument* pXTextDocument = createDoc("hyperlink.odt");
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
+ m_callbackWrapper.setLOKViewId(SfxLokHelper::getView(pWrtShell->GetSfxViewShell()));
SwShellCursor* pShellCursor = pWrtShell->getShellCursor(false);
Point aStart = pShellCursor->GetSttPos();
@@ -2829,7 +2853,7 @@ void SwTiledRenderingTest::testDropDownFormFieldButton()
pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
// Move the cursor to trigger displaying of the field button.
pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
@@ -2902,7 +2926,7 @@ void SwTiledRenderingTest::testDropDownFormFieldButtonEditing()
pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
// Move the cursor to trigger displaying of the field button.
pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
@@ -2959,7 +2983,7 @@ void SwTiledRenderingTest::testDropDownFormFieldButtonNoSelection()
pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
// Move the cursor to trigger displaying of the field button.
pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
@@ -3012,7 +3036,7 @@ void SwTiledRenderingTest::testMoveShapeHandle()
SwXTextDocument* pXTextDocument = createDoc("shape.fodt");
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
SdrObject* pObject = pPage->GetObj(0);
pWrtShell->SelectObj(Point(), 0, pObject);
@@ -3045,7 +3069,7 @@ void SwTiledRenderingTest::testDropDownFormFieldButtonNoItem()
pXTextDocument->setClientVisibleArea(tools::Rectangle(0, 0, 10000, 4000));
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
// Move the cursor to trigger displaying of the field button.
pWrtShell->Right(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false);
@@ -3082,7 +3106,7 @@ void SwTiledRenderingTest::testTablePaintInvalidate()
// Load a document with a table in it.
SwXTextDocument* pXTextDocument = createDoc("table-paint-invalidate.odt");
SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
// Enter the table.
pWrtShell->Down(/*bSelect=*/false);
Scheduler::ProcessEventsToIdle();
@@ -3180,7 +3204,7 @@ void SwTiledRenderingTest::testBulletDeleteInvalidation()
pWrtShell->GetLayout()->PaintSwFrame(*pWrtShell->GetOut(),
pWrtShell->GetLayout()->getFrameArea());
Scheduler::ProcessEventsToIdle();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
m_aInvalidations = tools::Rectangle();
// When pressing backspace in the last paragraph.
@@ -3210,7 +3234,7 @@ void SwTiledRenderingTest::testBulletNoNumInvalidation()
pWrtShell->GetLayout()->PaintSwFrame(*pWrtShell->GetOut(),
pWrtShell->GetLayout()->getFrameArea());
Scheduler::ProcessEventsToIdle();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
m_aInvalidations = tools::Rectangle();
// When pressing backspace in the last paragraph to turn bullets off.
@@ -3247,7 +3271,7 @@ void SwTiledRenderingTest::testBulletMultiDeleteInvalidation()
pWrtShell->GetLayout()->PaintSwFrame(*pWrtShell->GetOut(),
pWrtShell->GetLayout()->getFrameArea());
Scheduler::ProcessEventsToIdle();
- pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(&m_callbackWrapper);
+ setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell());
m_aInvalidations = tools::Rectangle();
// When selecting and deleting several bullets: select till the end of the 2nd para and delete.
diff --git a/sw/source/core/crsr/viscrs.cxx b/sw/source/core/crsr/viscrs.cxx
index c756d3da3586..2c77e6d4dd87 100644
--- a/sw/source/core/crsr/viscrs.cxx
+++ b/sw/source/core/crsr/viscrs.cxx
@@ -225,12 +225,64 @@ void SwVisibleCursor::SetPosAndShow(SfxViewShell const * pViewShell)
m_pCursorShell->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload.getStr());
}
+ // This may get called often, so instead of sending data on each update, just notify
+ // that there's been an update, and the other side will pull the data using
+ // getLOKPayload() when it decides to.
+ m_aLastLOKRect = aRect;
+ if (pViewShell)
+ {
+ if (pViewShell == m_pCursorShell->GetSfxViewShell())
+ {
+ SfxLokHelper::notifyUpdatePerViewId(pViewShell, LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR);
+ }
+ else
+ {
+ SfxLokHelper::notifyUpdatePerViewId(pViewShell, m_pCursorShell->GetSfxViewShell(), pViewShell,
+ LOK_CALLBACK_INVALIDATE_VIEW_CURSOR);
+ }
+ }
+ else
+ {
+ SfxLokHelper::notifyUpdatePerViewId(m_pCursorShell->GetSfxViewShell(), SfxViewShell::Current(),
+ m_pCursorShell->GetSfxViewShell(), LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR);
+ SfxLokHelper::notifyOtherViewsUpdatePerViewId(m_pCursorShell->GetSfxViewShell(), LOK_CALLBACK_INVALIDATE_VIEW_CURSOR);
+ }
+ }
+
+ if ( m_pCursorShell->IsCursorReadonly() && !m_pCursorShell->GetViewOptions()->IsSelectionInReadonly() )
+ return;
+
+ if ( m_pCursorShell->GetDrawView() )
+ const_cast<SwDrawView*>(static_cast<const SwDrawView*>(m_pCursorShell->GetDrawView()))->SetAnimationEnabled(
+ !m_pCursorShell->IsSelection() );
+
+ sal_uInt16 nStyle = m_bIsDragCursor ? CURSOR_SHADOW : 0;
+ if( nStyle != m_aTextCursor.GetStyle() )
+ {
+ m_aTextCursor.SetStyle( nStyle );
+ m_aTextCursor.SetWindow( m_bIsDragCursor ? m_pCursorShell->GetWin() : nullptr );
+ }
+
+ m_aTextCursor.Show();
+}
+
+OString SwVisibleCursor::getLOKPayload(int nType, int nViewId) const
+{
+ assert(nType == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR || nType == LOK_CALLBACK_INVALIDATE_VIEW_CURSOR);
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ SwRect aRect = m_aLastLOKRect;
+
// notify about the cursor position & size
tools::Rectangle aSVRect(aRect.Pos().getX(), aRect.Pos().getY(), aRect.Pos().getX() + aRect.SSize().Width(), aRect.Pos().getY() + aRect.SSize().Height());
OString sRect = aSVRect.toString();
+ if(nType == LOK_CALLBACK_INVALIDATE_VIEW_CURSOR)
+ return SfxLokHelper::makePayloadJSON(m_pCursorShell->GetSfxViewShell(), nViewId, "rectangle", sRect);
+
// is cursor at a misspelled word ?
bool bIsWrong = false;
+ SwView* pView = dynamic_cast<SwView*>(m_pCursorShell->GetSfxViewShell());
if (pView && pView->GetWrtShellPtr())
{
const SwViewOption* pVOpt = pView->GetWrtShell().GetViewOptions();
@@ -283,37 +335,10 @@ void SwVisibleCursor::SetPosAndShow(SfxViewShell const * pViewShell)
}
}
- if (pViewShell)
- {
- if (pViewShell == m_pCursorShell->GetSfxViewShell())
- {
- SfxLokHelper::notifyVisCursorInvalidation(pViewShell, sRect, bIsWrong, sHyperlink);
- }
- else
- SfxLokHelper::notifyOtherView(m_pCursorShell->GetSfxViewShell(), pViewShell, LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, "rectangle", sRect);
- }
- else
- {
- SfxLokHelper::notifyVisCursorInvalidation(m_pCursorShell->GetSfxViewShell(), sRect, bIsWrong, sHyperlink);
- SfxLokHelper::notifyOtherViews(m_pCursorShell->GetSfxViewShell(), LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, "rectangle", sRect);
- }
+ return SfxLokHelper::makeVisCursorInvalidation(nViewId, sRect, bIsWrong, sHyperlink);
}
-
- if ( m_pCursorShell->IsCursorReadonly() && !m_pCursorShell->GetViewOptions()->IsSelectionInReadonly() )
- return;
-
- if ( m_pCursorShell->GetDrawView() )
- const_cast<SwDrawView*>(static_cast<const SwDrawView*>(m_pCursorShell->GetDrawView()))->SetAnimationEnabled(
- !m_pCursorShell->IsSelection() );
-
- sal_uInt16 nStyle = m_bIsDragCursor ? CURSOR_SHADOW : 0;
- if( nStyle != m_aTextCursor.GetStyle() )
- {
- m_aTextCursor.SetStyle( nStyle );
- m_aTextCursor.SetWindow( m_bIsDragCursor ? m_pCursorShell->GetWin() : nullptr );
- }
-
- m_aTextCursor.Show();
+ else
+ abort();
}
const vcl::Cursor& SwVisibleCursor::GetTextCursor() const
diff --git a/sw/source/uibase/inc/wrtsh.hxx b/sw/source/uibase/inc/wrtsh.hxx
index 8e2573916e8b..a47e68cd2045 100644
--- a/sw/source/uibase/inc/wrtsh.hxx
+++ b/sw/source/uibase/inc/wrtsh.hxx
@@ -499,6 +499,8 @@ typedef bool (SwWrtShell::*FNSimpleMove)();
void InvalidateOutlineContentVisibility();
bool GetAttrOutlineContentVisible(const size_t nPos);
+ OString getLOKPayload(int nType, int nViewId) const;
+
private:
SAL_DLLPRIVATE void OpenMark();
diff --git a/sw/source/uibase/uiview/view.cxx b/sw/source/uibase/uiview/view.cxx
index 9a4b83a28566..7755afb107ea 100644
--- a/sw/source/uibase/uiview/view.cxx
+++ b/sw/source/uibase/uiview/view.cxx
@@ -1912,6 +1912,13 @@ void SwView::flushPendingLOKInvalidateTiles()
pSh->FlushPendingLOKInvalidateTiles();
}
+OString SwView::getLOKPayload(int nType, int nViewId) const
+{
+ SwWrtShell* pSh = GetWrtShellPtr();
+ assert(pSh);
+ return pSh->getLOKPayload(nType, nViewId);
+}
+
OUString SwView::GetDataSourceName() const
{
uno::Reference<lang::XMultiServiceFactory> xFactory(GetDocShell()->GetModel(), uno::UNO_QUERY);
diff --git a/sw/source/uibase/wrtsh/wrtsh4.cxx b/sw/source/uibase/wrtsh/wrtsh4.cxx
index 8009ce98037b..b80b41b41e54 100644
--- a/sw/source/uibase/wrtsh/wrtsh4.cxx
+++ b/sw/source/uibase/wrtsh/wrtsh4.cxx
@@ -19,6 +19,8 @@
#include <wrtsh.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+
// Private methods, which move the cursor over search.
// The removal of the selection must be made on the level above.
@@ -232,4 +234,15 @@ bool SwWrtShell::BwdPara_()
return bRet;
}
+OString SwWrtShell::getLOKPayload(int nType, int nViewId) const
+{
+ switch(nType)
+ {
+ case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
+ case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
+ return GetVisibleCursor()->getLOKPayload(nType, nViewId);
+ }
+ abort();
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/test/source/lokcallback.cxx b/test/source/lokcallback.cxx
index 13d381f0b46a..6a39b9064470 100644
--- a/test/source/lokcallback.cxx
+++ b/test/source/lokcallback.cxx
@@ -25,22 +25,37 @@ TestLokCallbackWrapper::TestLokCallbackWrapper(LibreOfficeKitCallback callback,
SetPriority(TaskPriority::LOWEST);
}
-inline void TestLokCallbackWrapper::callCallback(int nType, const char* pPayload)
+void TestLokCallbackWrapper::clear()
+{
+ m_viewId = -1;
+ m_updatedTypes.clear();
+ m_updatedTypesPerViewId.clear();
+}
+
+inline void TestLokCallbackWrapper::startTimer()
{
- m_callback(nType, pPayload, m_data);
if (!IsActive())
Start();
}
+constexpr int NO_VIEWID = -1;
+
+inline void TestLokCallbackWrapper::callCallback(int nType, const char* pPayload, int nViewId)
+{
+ discardUpdatedTypes(nType, nViewId);
+ m_callback(nType, pPayload, m_data);
+ startTimer();
+}
+
void TestLokCallbackWrapper::libreOfficeKitViewCallback(int nType, const char* pPayload)
{
- callCallback(nType, pPayload);
+ callCallback(nType, pPayload, NO_VIEWID);
}
void TestLokCallbackWrapper::libreOfficeKitViewCallbackWithViewId(int nType, const char* pPayload,
- int /*nViewId*/)
+ int nViewId)
{
- callCallback(nType, pPayload); // the view id is also included in payload
+ callCallback(nType, pPayload, nViewId);
}
void TestLokCallbackWrapper::libreOfficeKitViewInvalidateTilesCallback(
@@ -56,7 +71,100 @@ void TestLokCallbackWrapper::libreOfficeKitViewInvalidateTilesCallback(
buf.append(", ");
buf.append(static_cast<sal_Int32>(nPart));
}
- callCallback(LOK_CALLBACK_INVALIDATE_TILES, buf.makeStringAndClear().getStr());
+ callCallback(LOK_CALLBACK_INVALIDATE_TILES, buf.makeStringAndClear().getStr(), NO_VIEWID);
+}
+
+// TODO This is probably a pointless code duplication with CallbackFlushHandler,
+// and using this in unittests also means that CallbackFlushHandler does not get
+// tested as thoroughly as it could. On the other hand, this class is simpler,
+// so debugging those unittests should also be simpler. The proper solution
+// is presumably this class using CallbackFlushHandler internally by default,
+// but having an option to use this simpler code when needed.
+
+void TestLokCallbackWrapper::libreOfficeKitViewUpdatedCallback(int nType)
+{
+ if (std::find(m_updatedTypes.begin(), m_updatedTypes.end(), nType) == m_updatedTypes.end())
+ {
+ m_updatedTypes.push_back(nType);
+ startTimer();
+ }
+}
+
+void TestLokCallbackWrapper::libreOfficeKitViewUpdatedCallbackPerViewId(int nType, int nViewId,
+ int nSourceViewId)
+{
+ const PerViewIdData data{ nType, nViewId, nSourceViewId };
+ auto& l = m_updatedTypesPerViewId;
+ // The source view doesn't matter for uniqueness, just keep the latest one.
+ auto it = std::find_if(l.begin(), l.end(), [data](const PerViewIdData& other) {
+ return data.type == other.type && data.viewId == other.viewId;
+ });
+ if (it != l.end())
+ *it = data;
+ else
+ l.push_back(data);
+ startTimer();
+}
+
+void TestLokCallbackWrapper::discardUpdatedTypes(int nType, int nViewId)
+{
+ // If a callback is called directly with an event, drop the updated flag for it, since
+ // the direct event replaces it.
+ for (auto it = m_updatedTypes.begin(); it != m_updatedTypes.end();)
+ {
+ if (*it == nType)
+ it = m_updatedTypes.erase(it);
+ else
+ ++it;
+ }
+ // If we do not have a specific view id, drop flag for all views.
+ bool allViewIds = false;
+ if (nViewId < 0)
+ allViewIds = true;
+ if (nType == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR
+ && !comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
+ allViewIds = true;
+ for (auto it = m_updatedTypesPerViewId.begin(); it != m_updatedTypesPerViewId.end();)
+ {
+ if (it->type == nType && (allViewIds || it->viewId == nViewId))
+ it = m_updatedTypesPerViewId.erase(it);
+ else
+ ++it;
+ }
+}
+
+void TestLokCallbackWrapper::flushLOKData()
+{
+ if (m_updatedTypes.empty() && m_updatedTypesPerViewId.empty())
+ return;
+ // Ask for payloads of all the pending types that need updating, and call the generic callback with that data.
+ assert(m_viewId >= 0);
+ SfxViewShell* viewShell = SfxViewShell::GetFirst(false, [this](const SfxViewShell* shell) {
+ return shell->GetViewShellId().get() == m_viewId;
+ });
+ assert(viewShell != nullptr);
+ // First move data to local structures, so that notifyFromLOKCallback() doesn't modify it.
+ std::vector<int> updatedTypes;
+ std::swap(updatedTypes, m_updatedTypes);
+ std::vector<PerViewIdData> updatedTypesPerViewId;
+ std::swap(updatedTypesPerViewId, m_updatedTypesPerViewId);
+
+ for (int type : updatedTypes)
+ {
+ OString payload = viewShell->getLOKPayload(type, m_viewId);
+ if (!payload.isEmpty())
+ libreOfficeKitViewCallback(type, payload.getStr());
+ }
+ for (const PerViewIdData& data : updatedTypesPerViewId)
+ {
+ viewShell = SfxViewShell::GetFirst(false, [data](const SfxViewShell* shell) {
+ return shell->GetViewShellId().get() == data.sourceViewId;
+ });
+ assert(viewShell != nullptr);
+ OString payload = viewShell->getLOKPayload(data.type, data.viewId);
+ if (!payload.isEmpty())
+ libreOfficeKitViewCallbackWithViewId(data.type, payload.getStr(), data.viewId);
+ }
}
void TestLokCallbackWrapper::Invoke()
@@ -67,6 +175,7 @@ void TestLokCallbackWrapper::Invoke()
{
viewShell->flushPendingLOKInvalidateTiles();
}
+ flushLOKData();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */