editeng/inc/editdoc.hxx | 3 editeng/source/editeng/editdoc.cxx | 95 +++++++++++++++++++++++++-- editeng/source/editeng/editview.cxx | 7 + include/editeng/editview.hxx | 2 include/editeng/yrs.hxx | 1 include/sal/log-areas.dox | 3 sfx2/source/view/frmload.cxx | 4 - sw/source/core/crsr/viscrs.cxx | 2 sw/source/core/doc/DocumentStateManager.cxx | 79 ++++++++++++++++------ sw/source/uibase/docvw/PostItMgr.cxx | 2 sw/source/uibase/docvw/SidebarTxtControl.cxx | 4 - 11 files changed, 171 insertions(+), 31 deletions(-)
New commits: commit 4727305dd2e6fa1e92408d9fd7fef70b8d1fb913 Author: Michael Stahl <[email protected]> AuthorDate: Mon May 12 09:20:29 2025 +0200 Commit: Michael Stahl <[email protected]> CommitDate: Tue May 13 10:00:31 2025 +0200 LOCRCT editeng,sfx2,sw: replace temporary SAL_DEBUG Change-Id: Ib6f3b732a7eb6f2dbf423ffd2206b0474ff6c681 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/185200 Tested-by: Jenkins Reviewed-by: Michael Stahl <[email protected]> diff --git a/editeng/source/editeng/editdoc.cxx b/editeng/source/editeng/editdoc.cxx index 86a523ca9f6a..7073723bda16 100644 --- a/editeng/source/editeng/editdoc.cxx +++ b/editeng/source/editeng/editdoc.cxx @@ -1281,7 +1281,7 @@ void YrsAddPara(IYrsTransactionSupplier *const pYrsSupplier, { return; } - SAL_DEBUG("YRS YrsAddPara"); + SAL_INFO("editeng.yrs", "YRS YrsAddPara"); // need to encode into 1 YText char const para[]{ CH_PARA, ' uint32_t i{0}; @@ -3146,7 +3146,7 @@ EditDoc::EditDoc( SfxItemPool* pPool ) : mbDisableAttributeExpanding(false) { #if defined(YRS) - SAL_DEBUG("YRS +EditDoc"); + SAL_INFO("editeng.yrs", "YRS +EditDoc"); #endif // Don't create an empty node, Clear() will be called in EditEngine-CTOR }; @@ -3154,7 +3154,7 @@ EditDoc::EditDoc( SfxItemPool* pPool ) : EditDoc::~EditDoc() { #if defined(YRS) - SAL_DEBUG("YRS -EditDoc"); + SAL_INFO("editeng.yrs", "YRS -EditDoc"); #endif maContents.clear(); } diff --git a/editeng/source/editeng/editview.cxx b/editeng/source/editeng/editview.cxx index 982b17d3ab30..4a59be87ae2b 100644 --- a/editeng/source/editeng/editview.cxx +++ b/editeng/source/editeng/editview.cxx @@ -384,7 +384,7 @@ void EditView::YrsApplyEECursor(OString const& rPeerId, OUString const& rAuthor, ::std::optional<EditSelection> const oSel{getEditEngine().GetEditDoc().YrsReadEECursor(point, oMark)}; if (!oSel) { - SAL_DEBUG("YRS ignoring invalid cursor position"); + SAL_INFO("editeng.yrs", "YRS ignoring invalid cursor position"); return; } ESelection const esel{getEditEngine().getImpl().CreateESel(*oSel)}; diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox index 425edb201d41..7b4fc359997a 100644 --- a/include/sal/log-areas.dox +++ b/include/sal/log-areas.dox @@ -199,6 +199,7 @@ certain functionality. @li @c editeng.chaining @li @c editeng.items @li @c editeng.quicktextsize +@li @c editeng.yrs @section embeddedobj @@ -373,6 +374,7 @@ certain functionality. @li @c sfx.notify @li @c sfx.sidebar @li @c sfx.view +@li @c sfx.yrs @section slideshow @@ -566,6 +568,7 @@ certain functionality. @li @c sw.ww8 - .doc/.docx export filter, .doc import filter (not writerfilter) @li @c sw.ww8.level2 - further info for sw.ww8 @li @c sw.xml - Writer .odt import/export +@li @c sw.yrs @section writerfilter diff --git a/sfx2/source/view/frmload.cxx b/sfx2/source/view/frmload.cxx index 80615abe165f..16137f020a23 100644 --- a/sfx2/source/view/frmload.cxx +++ b/sfx2/source/view/frmload.cxx @@ -635,7 +635,7 @@ sal_Bool SAL_CALL SfxFrameLoader_Impl::load( const Sequence< PropertyValue >& rA uno::Reference<connection::XConnection> xConnection; if (!xModel.is() && aDescriptor.getOrDefault(u"URL"_ustr, OUString()) == "private:factory/swriter" && !getenv("YRSACCEPT")) { - SAL_DEBUG("YRS connect sfx2"); + SAL_INFO("sfx.yrs", "YRS connect sfx2"); // must read this SYNC auto const conn = u"pipe,name=ytest"_ustr; @@ -652,7 +652,7 @@ sal_Bool SAL_CALL SfxFrameLoader_Impl::load( const Sequence< PropertyValue >& rA | static_cast<sal_uInt8>(buf[3]) << 24}; if (size != 0) { - SAL_DEBUG("YRS connect reading file of size " << size); + SAL_INFO("sfx.yrs", "YRS connect reading file of size " << size); uno::Sequence<sal_Int8> buff(size); if (xConnection->read(buff, size) != size) { diff --git a/sw/source/core/crsr/viscrs.cxx b/sw/source/core/crsr/viscrs.cxx index 2e640750dcb3..10acc7ab7c64 100644 --- a/sw/source/core/crsr/viscrs.cxx +++ b/sw/source/core/crsr/viscrs.cxx @@ -977,7 +977,7 @@ void SwShellCursor::FillRects() { cursorRect.Width(20); } - SAL_DEBUG("YRS FillRects extra rect " << cursorRect); + SAL_INFO("sw.yrs", "YRS FillRects extra rect " << cursorRect); emplace_back(cursorRect); SwRect const temp{Point{cursorRect.Left() - cursorRect.Height()/2, cursorRect.Bottom()}, Size{cursorRect.Height() + 20, 20}}; diff --git a/sw/source/core/doc/DocumentStateManager.cxx b/sw/source/core/doc/DocumentStateManager.cxx index 323b82a82440..0e883ab21ebd 100644 --- a/sw/source/core/doc/DocumentStateManager.cxx +++ b/sw/source/core/doc/DocumentStateManager.cxx @@ -214,10 +214,10 @@ public: | static_cast<sal_uInt8>(buf[3]) << 24}; if (size == 0) { - SAL_DEBUG("YRS 0"); + SAL_INFO("sw.yrs", "YRS 0"); break; } - SAL_DEBUG("YRS receive " << size); + SAL_INFO("sw.yrs", "YRS receive " << size); ::std::unique_ptr<uno::Sequence<sal_Int8>> pBuf{new uno::Sequence<sal_Int8>(size)}; m_xConnection->read(*pBuf, size); Application::PostUserEvent(LINK(this, YrsThread, HandleMessage), pBuf.release()); @@ -238,7 +238,7 @@ struct ObserveState extern "C" void observe_comments(void *const pState, uint32_t count, YEvent const*const events) { - SAL_DEBUG("YRS observe_comments"); + SAL_INFO("sw.yrs", "YRS observe_comments"); ObserveState & rState{*static_cast<ObserveState*>(pState)}; // DO NOT call rState.rYrsSupplier.GetWriteTransaction()! YTransaction *const pTxn{rState.pTxn}; @@ -566,7 +566,7 @@ extern "C" void observe_comments(void *const pState, uint32_t count, YEvent cons yvalidate(rNode.IsTextNode()); yvalidate(it.first.second <= o3tl::make_unsigned(rNode.GetTextNode()->Len())); SwPosition anchorPos{*rNode.GetTextNode(), static_cast<sal_Int32>(it.first.second)}; - SAL_DEBUG("YRS " << anchorPos); + SAL_INFO("sw.yrs", "YRS " << anchorPos); SwWrtShell *const pShell{dynamic_cast<SwWrtShell*>(rState.rDoc.getIDocumentLayoutAccess().GetCurrentViewShell())}; SwPostItFieldType* pType = static_cast<SwPostItFieldType*>(pShell->GetFieldType(0, SwFieldIds::Postit)); auto pField{ @@ -824,7 +824,7 @@ void YrsReadCursor(ObserveCursorState & rState, OString const& rPeerId, extern "C" void observe_cursors(void *const pState, uint32_t count, YEvent const*const events) { - SAL_DEBUG("YRS observe_cursors"); + SAL_INFO("sw.yrs", "YRS observe_cursors"); // note: it (very rarely) happens that observe_cursors will be called // when a cursor is moved into a comment that is newly inserted, but // observe_comments hasn't been called to actually insert the comment yet @@ -952,11 +952,11 @@ void writeLength(sal_Int8 *& rpBuf, sal_Int32 const len) IMPL_LINK(YrsThread, HandleMessage, void*, pVoid, void) { - SAL_DEBUG("YRS HandleMessage"); + SAL_INFO("sw.yrs", "YRS HandleMessage"); DBG_TESTSOLARMUTEX(); if (!m_pDSM) { - SAL_DEBUG("m_pDSM died"); + SAL_INFO("sw.yrs", "m_pDSM died"); return; } // wrap this, not strictly needed but can't hurt? @@ -968,7 +968,7 @@ IMPL_LINK(YrsThread, HandleMessage, void*, pVoid, void) { case ::std::underlying_type_t<Message>(Message::RequestStateVector): { - SAL_DEBUG("sending state vector"); + SAL_INFO("sw.yrs", "sending state vector"); YTransaction *const pTxn{m_pDSM->m_pYrsSupplier->GetWriteTransaction()}; uint32_t len{0}; char * pSV = ytransaction_state_vector_v1(pTxn, &len); @@ -992,7 +992,7 @@ IMPL_LINK(YrsThread, HandleMessage, void*, pVoid, void) } case ::std::underlying_type_t<Message>(Message::SendStateVector): { - SAL_DEBUG("received state vector"); + SAL_INFO("sw.yrs", "received state vector"); YTransaction *const pTxn{m_pDSM->m_pYrsSupplier->GetWriteTransaction()}; uint32_t len{0}; char * pUpdate = ytransaction_state_diff_v1(pTxn, @@ -1017,13 +1017,13 @@ IMPL_LINK(YrsThread, HandleMessage, void*, pVoid, void) } case ::std::underlying_type_t<Message>(Message::SendStateDiff): { - SAL_DEBUG("apply update: " << yupdate_debug_v1(reinterpret_cast<char const*>(pBuf->begin()) + 1, length - 1)); + SAL_INFO("sw.yrs", "apply update: " << yupdate_debug_v1(reinterpret_cast<char const*>(pBuf->begin()) + 1, length - 1)); YTransaction *const pTxn{m_pDSM->m_pYrsSupplier->GetWriteTransaction()}; m_pDSM->m_pYrsSupplier->SetMode(IYrsTransactionSupplier::Mode::Replay); auto const err = ytransaction_apply(pTxn, reinterpret_cast<char const*>(pBuf->begin()) + 1, length - 1); if (err != 0) { - SAL_DEBUG("ytransaction_apply error " << err); + SAL_INFO("sw.yrs", "ytransaction_apply error " << err); abort(); } // let's have one observe_deep instead of a observe on every @@ -1066,7 +1066,7 @@ void DocumentStateManager::YrsNotifySetResolved(OString const& rCommentId, SwPos void DocumentStateManager::YrsAddCommentImpl(SwPosition const& rAnchorPos, OString const& commentId) { - SAL_DEBUG("YRS AddCommentImpl"); + SAL_INFO("sw.yrs", "YRS AddCommentImpl"); ::std::vector<SwAnnotationItem *> items; // ??? TODO how should this work for multiple viewshells? every shell has its own EditEngine? unclear. for (SwViewShell & rShell : m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()->GetRingContainer()) @@ -1092,7 +1092,7 @@ void DocumentStateManager::YrsAddComment(SwPosition const& rPos, ::std::optional<SwPosition> const oAnchorStart, SwPostItField const& rField, bool const isInsert) { - SAL_DEBUG("YRS AddComment " << rPos); + SAL_INFO("sw.yrs", "YRS AddComment " << rPos); OString const commentId{m_pYrsSupplier->GenNewCommentId()}; // this calls EditViewInvalidate so prevent destroying pTxn YrsAddCommentImpl(rPos, commentId); @@ -1199,7 +1199,7 @@ void DocumentStateManager::YrsRemoveCommentImpl(OString const& rCommentId) void DocumentStateManager::YrsRemoveComment(SwPosition const& rPos, OString const& rCommentId) { - SAL_DEBUG("YRS RemoveComment"); + SAL_INFO("sw.yrs", "YRS RemoveComment"); YrsRemoveCommentImpl(rCommentId); YTransaction *const pTxn{m_pYrsSupplier->GetWriteTransaction()}; if (!pTxn) @@ -1270,7 +1270,7 @@ void DocumentStateManager::YrsNotifyCursorUpdate() { return; } - SAL_DEBUG("YRS NotifyCursorUpdate"); + SAL_INFO("sw.yrs", "YRS NotifyCursorUpdate"); YDoc *const pYDoc{m_pYrsSupplier->GetYDoc()}; auto const id{ydoc_id(pYDoc)}; ::std::unique_ptr<YOutput, YOutputDeleter> pEntry{ymap_get(m_pYrsSupplier->m_pCursors, pTxn, OString::number(id).getStr())}; @@ -1403,7 +1403,7 @@ void DocumentStateManager::YrsInitAcceptor() { auto const conn = u"pipe,name=ytest"_ustr; auto const xContext{comphelper::getProcessComponentContext()}; - SAL_DEBUG("YRS accept"); + SAL_INFO("sw.yrs", "YRS accept"); m_xAcceptor = css::connection::Acceptor::create(xContext); // TODO move to thread? uno::Reference<connection::XConnection> xConnection = m_xAcceptor->accept(conn); @@ -1416,7 +1416,7 @@ void DocumentStateManager::YrsInitAcceptor() { try { - SAL_DEBUG("YRS send file: " << url); + SAL_INFO("sw.yrs", "YRS send file: " << url); ::ucbhelper::Content temp{url, {}, xContext}; uno::Reference<io::XInputStream> const xInStream{temp.openStreamNoLock()}; uno::Reference<io::XSeekable> const xSeekable{xInStream, uno::UNO_QUERY}; @@ -1467,7 +1467,7 @@ void DocumentStateManager::YrsInitAcceptor() m_pYrsReader = new YrsThread(xConnection, *this); //m_xAcceptor->stopAccepting(); m_pYrsReader->launch(); - SAL_DEBUG("YRS started"); + SAL_INFO("sw.yrs", "YRS started"); // inserting comments needs sidebar wins so needs a view shell first SwFieldType & rType{*m_rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Postit, OUString(), false)}; std::vector<SwFormatField*> fields; @@ -1521,7 +1521,7 @@ void DocumentStateManager::YrsInitConnector(uno::Any const& raConnection) m_pYrsReader = new YrsThread(xConnection, *this); m_pYrsReader->launch(); - SAL_DEBUG("YRS started (InitConnector)"); + SAL_INFO("sw.yrs", "YRS started (InitConnector)"); } #endif @@ -1570,7 +1570,7 @@ void DocumentStateManager::SetModified() m_rDoc.DeleteAutoCorrExceptWord(); #if defined(YRS) - SAL_DEBUG("YRS SetModified"); + SAL_INFO("sw.yrs", "YRS SetModified"); YrsCommitModified(); #endif } diff --git a/sw/source/uibase/docvw/PostItMgr.cxx b/sw/source/uibase/docvw/PostItMgr.cxx index 70c30cf1e6e4..dd9c68e4668c 100644 --- a/sw/source/uibase/docvw/PostItMgr.cxx +++ b/sw/source/uibase/docvw/PostItMgr.cxx @@ -909,7 +909,7 @@ VclPtr<SwAnnotationWin> SwPostItMgr::GetOrCreateAnnotationWindow(SwAnnotationIte pPostIt->SetReadonly(mbReadOnly); rItem.mpPostIt = pPostIt; #if defined(YRS) - SAL_DEBUG("YRS GetOrCreateAnnotationWindow " << rItem.mpPostIt); + SAL_INFO("sw.yrs", "YRS GetOrCreateAnnotationWindow " << rItem.mpPostIt); #endif if (mpAnswer) { diff --git a/sw/source/uibase/docvw/SidebarTxtControl.cxx b/sw/source/uibase/docvw/SidebarTxtControl.cxx index ebc8cde72761..a1580a1924be 100644 --- a/sw/source/uibase/docvw/SidebarTxtControl.cxx +++ b/sw/source/uibase/docvw/SidebarTxtControl.cxx @@ -206,7 +206,7 @@ OUString SidebarTextControl::RequestHelp(tools::Rectangle& rHelpRect) #if defined(YRS) void SidebarTextControl::EditViewInvalidate(const tools::Rectangle& rRect) { - SAL_DEBUG("YRS EditViewInvalidate"); + SAL_INFO("sw.yrs", "YRS EditViewInvalidate"); mrDocView.GetDocShell()->GetDoc()->getIDocumentState().YrsNotifyCursorUpdate(); mrDocView.GetDocShell()->GetDoc()->getIDocumentState().YrsCommitModified(); return WeldEditView::EditViewInvalidate(rRect); @@ -214,7 +214,7 @@ void SidebarTextControl::EditViewInvalidate(const tools::Rectangle& rRect) void SidebarTextControl::EditViewSelectionChange() { - SAL_DEBUG("YRS EditViewSelectionChange"); + SAL_INFO("sw.yrs", "YRS EditViewSelectionChange"); mrDocView.GetDocShell()->GetDoc()->getIDocumentState().YrsNotifyCursorUpdate(); return WeldEditView::EditViewSelectionChange(); } commit ee7ab3d4634ae916f04811d0ce033637ea22578c Author: Michael Stahl <[email protected]> AuthorDate: Thu Mar 20 14:47:02 2025 +0100 Commit: Michael Stahl <[email protected]> CommitDate: Tue May 13 10:00:20 2025 +0200 LOCRDT editeng,sw: yrs weak link peer cursors for editengine Replace unreliable integer cursors with WeakRef cursors which maintain their position automatically. This requires yrs v0.23.1 with the yweak_read() function. Unfortunately this crashes currently, so hide it behind #if YRS_WEAK. https://github.com/y-crdt/y-crdt/issues/536 Change-Id: I74ec1eaa25c7cb554efd38ee632df041ac92d5c2 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183311 Tested-by: Jenkins Reviewed-by: Michael Stahl <[email protected]> diff --git a/editeng/inc/editdoc.hxx b/editeng/inc/editdoc.hxx index cb252ee0c877..f348f17199e1 100644 --- a/editeng/inc/editdoc.hxx +++ b/editeng/inc/editdoc.hxx @@ -51,6 +51,8 @@ class ImpEditEngine; class IYrsTransactionSupplier; typedef struct TransactionInner YTransaction; typedef struct YTextEvent YTextEvent; +typedef struct Branch Branch; +typedef struct YOutput YOutput; #endif @@ -134,6 +136,7 @@ public: void YrsWriteEEState(); void YrsReadEEState(YTransaction *, ImpEditEngine & rIEE); void YrsApplyEEDelta(YTransaction *, YTextEvent const* pEvent, ImpEditEngine & rIEE); + bool YrsWriteEECursor(YTransaction *, Branch const& rArray, YOutput const* pCurrent, EditSelection const& rSelection); ::std::optional<EditSelection> YrsReadEECursor(::std::pair<int64_t, int64_t> point, ::std::optional<::std::pair<int64_t, int64_t>> oMark); void YrsSetStyle(sal_Int32 nPara, ::std::u16string_view rStyle); diff --git a/editeng/source/editeng/editdoc.cxx b/editeng/source/editeng/editdoc.cxx index 45c89ef0dcfd..86a523ca9f6a 100644 --- a/editeng/source/editeng/editdoc.cxx +++ b/editeng/source/editeng/editdoc.cxx @@ -2712,12 +2712,98 @@ void EditDoc::YrsReadEEState(YTransaction *const pTxn, ImpEditEngine & rIEE) rIEE.RemoveParagraph(nodes); // remove pre-existing one from InitDoc() } +bool EditDoc::YrsWriteEECursor(YTransaction *const pTxn, Branch const& rArray, + YOutput const*const pCurrent, EditSelection const& rSelection) +{ + EditSelection sel{rSelection}; + sel.Adjust(*this); // unfortunately min/max naming may be misleading... + YrsWrite const yw{GetYrsWrite(m_pYrsSupplier, m_CommentId)}; + uint32_t start{0}; + for (auto paras{GetPos(sel.Min().GetNode())}; paras != 0; --paras) + { + start += GetObject(paras-1)->Len() + 1; + } + uint32_t end{start}; + start += sel.Min().GetIndex(); + if (sel.HasRange()) + { + auto const nStartNode{GetPos(sel.Min().GetNode())}; + for (auto paras{GetPos(sel.Max().GetNode()) - nStartNode}; + paras != 0; --paras) + { + end += GetObject(nStartNode + paras-1)->Len() + 1; + } + end += sel.Max().GetIndex(); + } + else + { + end = start; + } + if (pCurrent != nullptr && pCurrent->tag == Y_ARRAY + && yarray_len(pCurrent->value.y_type) == 2 + // could do without the comment id? no, empty-text leaves pBranch null! + && yarray_get(pCurrent->value.y_type, pTxn, 0)->tag == Y_JSON_STR + && strcmp(yarray_get(pCurrent->value.y_type, pTxn, 0)->value.str, m_CommentId.getStr()) == 0) + { + ::std::unique_ptr<YOutput, YOutputDeleter> const pWeak{yarray_get(pCurrent->value.y_type, pTxn, 1)}; + yvalidate(pWeak->tag == Y_WEAK_LINK); + Branch * pBranch{nullptr}; + uint32_t oldStart{SAL_MAX_UINT32}; + uint32_t oldEnd{SAL_MAX_UINT32}; + yweak_read(pWeak->value.y_type, pTxn, &pBranch, &oldStart, &oldEnd); + assert(oldStart != SAL_MAX_UINT32 && oldEnd != SAL_MAX_UINT32); + if (oldStart == start && oldEnd == end) + { + return false; + } + } + + Weak const*const pWeak{ytext_quote(yw.pText, pTxn, start, end, Y_FALSE, Y_TRUE)}; + ::std::vector<YInput> positions; + positions.push_back(yinput_string(m_CommentId.getStr())); + positions.push_back(yinput_weak(pWeak)); + YInput const input{yinput_yarray(positions.data(), 2)}; + yarray_remove_range(&rArray, pTxn, 1, 1); + yarray_insert_range(&rArray, pTxn, 1, &input, 1); +// yweak_destroy(pWeak); // NO! UAF + return true; +} + ::std::optional<EditSelection> EditDoc::YrsReadEECursor( ::std::pair<int64_t, int64_t> const i_point, ::std::optional<::std::pair<int64_t, int64_t>> const i_oMark) { - // TODO should not use ints here? ::std::optional<EditPaM> oMark; +#if defined(YRS_WEAK) + auto start{i_point.first}; // returned from yweak_read(), need to convert + ContentNode * pNode{nullptr}; + for (decltype(Count()) i = 0; i < Count(); ++i) + { + pNode = GetObject(i); + if (start <= o3tl::make_unsigned(pNode->Len())) + { + break; + } + start -= pNode->Len() + 1; + } + yvalidate(start <= o3tl::make_unsigned(pNode->Len())); + EditPaM point{pNode, static_cast<sal_Int32>(start)}; + if (i_oMark) + { + auto end{i_oMark->first}; + for (decltype(Count()) i = 0; i < Count(); ++i) + { + pNode = GetObject(i); + if (end <= o3tl::make_unsigned(pNode->Len())) + { + break; + } + end -= pNode->Len() + 1; + } + yvalidate(end <= o3tl::make_unsigned(pNode->Len())); + oMark.emplace(pNode, static_cast<sal_Int32>(end)); + } +#else if (i_oMark) { // yvalidate(i_oMark->first < Count()); @@ -2750,6 +2836,7 @@ void EditDoc::YrsReadEEState(YTransaction *const pTxn, ImpEditEngine & rIEE) return {}; } EditPaM point{&rNode, static_cast<sal_Int32>(i_point.second)}; +#endif return EditSelection{point, oMark ? *oMark : point}; } diff --git a/editeng/source/editeng/editview.cxx b/editeng/source/editeng/editview.cxx index 65152d487577..982b17d3ab30 100644 --- a/editeng/source/editeng/editview.cxx +++ b/editeng/source/editeng/editview.cxx @@ -372,6 +372,11 @@ void EditView::YrsGetSelectionRectangles( } } +bool EditView::YrsWriteEECursor(YTransaction *const pTxn, Branch const& rArray, YOutput const*const pCurrent) +{ + return getEditEngine().GetEditDoc().YrsWriteEECursor(pTxn, rArray, pCurrent, getImpl().GetEditSelection()); +} + void EditView::YrsApplyEECursor(OString const& rPeerId, OUString const& rAuthor, ::std::pair<int64_t, int64_t> const point, ::std::optional<::std::pair<int64_t, int64_t>> const oMark) diff --git a/include/editeng/editview.hxx b/include/editeng/editview.hxx index 4e1ee56ef7ce..d31ee35ff81e 100644 --- a/include/editeng/editview.hxx +++ b/include/editeng/editview.hxx @@ -42,6 +42,7 @@ class IYrsTransactionSupplier; typedef struct TransactionInner YTransaction; typedef struct YTextEvent YTextEvent; typedef struct Branch Branch; +typedef struct YOutput YOutput; #endif class EditTextObject; @@ -417,6 +418,7 @@ public: void YrsReadEEState(YTransaction *); void YrsApplyEEDelta(YTransaction *, YTextEvent const* pEvent); OString GetYrsCommentId() const; + bool YrsWriteEECursor(YTransaction *, Branch const& rArray, YOutput const* pCurrent); void YrsApplyEECursor(OString const& rPeerId, OUString const& rAuthor, ::std::pair<int64_t, int64_t> point, ::std::optional<::std::pair<int64_t, int64_t>> oMark); diff --git a/include/editeng/yrs.hxx b/include/editeng/yrs.hxx index 4842d47540c4..17f41179c3bf 100644 --- a/include/editeng/yrs.hxx +++ b/include/editeng/yrs.hxx @@ -9,6 +9,7 @@ #pragma once +#define YRS_WEAK extern "C" { #if defined(__clang__) #pragma clang diagnostic push diff --git a/sw/source/core/doc/DocumentStateManager.cxx b/sw/source/core/doc/DocumentStateManager.cxx index c0735e01de94..323b82a82440 100644 --- a/sw/source/core/doc/DocumentStateManager.cxx +++ b/sw/source/core/doc/DocumentStateManager.cxx @@ -740,7 +740,12 @@ void YrsReadCursor(ObserveCursorState & rState, OString const& rPeerId, { Branch const*const pArray{rCursor.value.y_type}; auto const len{yarray_len(pArray)}; +#if defined(YRS_WEAK) + if (len == 2 + && yarray_get(pArray, rState.pTxn, 0)->tag == Y_JSON_STR) +#else if (len == 3 || len == 5) +#endif { ::std::unique_ptr<YOutput, YOutputDeleter> const pComment{yarray_get(pArray, rState.pTxn, 0)}; yvalidate(pComment->tag == Y_JSON_STR && pComment->len < SAL_MAX_INT32); @@ -748,6 +753,20 @@ void YrsReadCursor(ObserveCursorState & rState, OString const& rPeerId, YrsInvalidateEECursors(rState, rPeerId, &commentId); YrsInvalidateSwCursors(rState, rPeerId, rAuthor, false); ::std::optional<::std::pair<int64_t, int64_t>> oMark; +#if defined(YRS_WEAK) + ::std::unique_ptr<YOutput, YOutputDeleter> const pWeak{yarray_get(pArray, rState.pTxn, 1)}; + yvalidate(pWeak->tag == Y_WEAK_LINK); + Branch * pBranch{nullptr}; + uint32_t start{SAL_MAX_UINT32}; + uint32_t end{SAL_MAX_UINT32}; + yweak_read(pWeak->value.y_type, rState.pTxn, &pBranch, &start, &end); + yvalidate(start < SAL_MAX_INT32 && end < SAL_MAX_INT32); + if (start != end) + { + oMark.emplace(end, -1); + } + ::std::pair<int64_t, int64_t> const pos{start, -1}; +#else if (len == 5) { ::std::unique_ptr<YOutput, YOutputDeleter> const pNode{ @@ -763,6 +782,7 @@ void YrsReadCursor(ObserveCursorState & rState, OString const& rPeerId, ::std::unique_ptr<YOutput, YOutputDeleter> const pContent{yarray_get(pArray, rState.pTxn, 2)}; yvalidate(pContent->tag == Y_JSON_INT); ::std::pair<int64_t, int64_t> const pos{pNode->value.integer, pContent->value.integer}; +#endif rState.CursorUpdates.emplace_back(rPeerId, ::std::optional<OString>{commentId}, ::std::optional<OUString>{rAuthor}, pos, oMark); } else if (len == 2 || len == 4) @@ -901,6 +921,15 @@ extern "C" void observe_cursors(void *const pState, uint32_t count, YEvent const YrsReadCursor(rState, peerId, pChange[2].values[0], author, false); break; } +#if defined(YRS_WEAK) + case Y_WEAK_LINK: + { + // not sure what this is, but yffi doesn't have any API + // for it, let's hope we can just ignore it +// YWeakLinkEvent const*const pEvent{&events[i].content.weak}; + break; + } +#endif default: assert(false); } @@ -1263,6 +1292,12 @@ void DocumentStateManager::YrsNotifyCursorUpdate() pShell->GetView().GetPostItMgr()->GetActiveSidebarWin()}) { // TODO StickyIndex cannot be inserted into YDoc ? +#if defined(YRS_WEAK) + if (pWin->GetOutlinerView()->GetEditView().YrsWriteEECursor(pTxn, *pEntry->value.y_type, pCurrent.get())) + { + YrsCommitModified(); + } +#else ESelection const sel{pWin->GetOutlinerView()->GetSelection()}; ::std::vector<YInput> positions; // the ID of the comment @@ -1301,6 +1336,7 @@ void DocumentStateManager::YrsNotifyCursorUpdate() YrsCommitModified(); } } +#endif } else if (!pShell->IsStdMode() || pShell->GetSelectedObjCount() != 0 @@ -1342,6 +1378,9 @@ void DocumentStateManager::YrsNotifyCursorUpdate() { if (pCurrent == nullptr || pCurrent->tag != Y_ARRAY || yarray_len(pCurrent->value.y_type) != 2 +#if defined(YRS_WEAK) + || yarray_get(pCurrent->value.y_type, pTxn, 0)->tag != Y_JSON_INT +#endif || yarray_get(pCurrent->value.y_type, pTxn, 0)->value.integer != positions[0].value.integer || yarray_get(pCurrent->value.y_type, pTxn, 1)->value.integer != positions[1].value.integer) {
