LOCRDT editeng,sfx2,sw: experimental yrs collab

This can be enabled in configure via --with-yrs=...; see also README.yrs

Currently this is hardcoded to run over a local named pipe.

Everything related to editengine is implemented in EditDoc and made
accessible via its wrapper classes; everything related to communication
and accessing sw's comment is implemented in sw::DocumentStateManager.

The acceptor starts in SwView::SwView(), once SwPostItMgr exists.

There is a hack in SfxFrameLoader_Impl::load() to fetch the document
that the accepting soffice has loaded, and load it in the connecting
soffice as well.

Change-Id: I89476b5864b70f479bcf15989374c1c65b5da9ea
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/175652
Tested-by: Jenkins
Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
This commit is contained in:
Michael Stahl 2024-10-25 15:02:34 +02:00
parent 0a677d53a6
commit 8f8034177b
30 changed files with 3940 additions and 10 deletions

46
README.yrs Normal file
View File

@ -0,0 +1,46 @@
## Experimental Writer comments editing collaboration with yrs
### How to build
First, build yrs C FFI bindings:
```
git clone https://github.com/y-crdt/y-crdt.git
cd y-crdt
git checkout v0.23.1
cargo build -p yffi
```
Then, put the yrs build directory in autogen.input:
`--with-yrs=/path/to/y-crdt`
### How to run
To prevent crashes at runtime, set the environment variable
EDIT_COMMENT_IN_READONLY_MODE=1 and open documents in read-only mode: only
inserting/deleting comments, and editing inside comments will be enabled.
Currently, communication happens over a hard-coded pipe:
* start an soffice with YRSACCEPT=1 load a Writer document and it will listen
and block until connect
(you can also create a new Writer document but that will be boring if all
you can do is insert comments into empty doc)
* start another soffice with a different user profile, create new Writer
document, and it will connect and load the document from the other side
All sorts of paragraph and character formattings should work inside comments.
Inserting hyperlinks also works, although sadly i wasn't able to figure out
how to enable the menu items in read-only mode, so it only works in editable
mode.
Undo/Redo doesn't work at all, it's disabled in readonly mode anyway.
Switching to editable mode is also possible, but only comment-related editing
is synced via yrs, so if other editing operations change the positions of
comments, a crash will be inevitable.

View File

@ -4449,4 +4449,21 @@ $(call gb_LinkTarget_set_include,$(1),\
endef endef
endif endif
ifneq ($(WITH_YRS),)
define gb_LinkTarget__use_yrs
$(call gb_LinkTarget_set_include,$(1),\
$$(INCLUDE) \
-I$(WITH_YRS)/tests-ffi/include \
)
$(call gb_LinkTarget_add_defs,$(1),-DYRS)
$(call gb_LinkTarget_add_libs,$(1),$(WITH_YRS)/target/debug/libyrs.a)
endef
else
gb_LinkTarget__use_yrs :=
endif
# vim: set noet sw=4 ts=4: # vim: set noet sw=4 ts=4:

View File

@ -784,6 +784,7 @@ export WITH_LOCALES=@WITH_LOCALES@
export WITH_MYSPELL_DICTS=@WITH_MYSPELL_DICTS@ export WITH_MYSPELL_DICTS=@WITH_MYSPELL_DICTS@
export WITH_THEMES=@WITH_THEMES@ export WITH_THEMES=@WITH_THEMES@
export WITH_WEBDAV=@WITH_WEBDAV@ export WITH_WEBDAV=@WITH_WEBDAV@
WITH_YRS=@WITH_YRS@
export WORKDIR=@WORKDIR@ export WORKDIR=@WORKDIR@
export WORKDIR_FOR_BUILD=@WORKDIR_FOR_BUILD@ export WORKDIR_FOR_BUILD=@WORKDIR_FOR_BUILD@
export WPD_CFLAGS=$(gb_SPACE)@WPD_CFLAGS@ export WPD_CFLAGS=$(gb_SPACE)@WPD_CFLAGS@

View File

@ -2830,6 +2830,12 @@ AC_ARG_WITH(hamcrest,
--without-junit disables those tests. Not relevant in the --without-java case.]), --without-junit disables those tests. Not relevant in the --without-java case.]),
,with_hamcrest=yes) ,with_hamcrest=yes)
AC_ARG_WITH(yrs,
AS_HELP_STRING([--with-yrs=<absolute path to yrs build>],
[Specifies the built yrs git repo for very experimental experiments.]),
WITH_YRS=$withval)
AC_SUBST(WITH_YRS)
AC_ARG_WITH(perl-home, AC_ARG_WITH(perl-home,
AS_HELP_STRING([--with-perl-home=<abs. path to Perl 5 home>], AS_HELP_STRING([--with-perl-home=<abs. path to Perl 5 home>],
[If you have installed Perl 5 Distribution, on your system, please [If you have installed Perl 5 Distribution, on your system, please

View File

@ -53,6 +53,7 @@ $(eval $(call gb_CppunitTest_use_externals,editeng_core,\
boost_headers \ boost_headers \
icuuc \ icuuc \
libxml2 \ libxml2 \
yrs \
)) ))
$(eval $(call gb_CppunitTest_set_include,editeng_core,\ $(eval $(call gb_CppunitTest_set_include,editeng_core,\

View File

@ -47,6 +47,7 @@ $(eval $(call gb_CppunitTest_use_externals,editeng_editeng,\
boost_headers \ boost_headers \
icuuc \ icuuc \
libxml2 \ libxml2 \
yrs \
)) ))
$(eval $(call gb_CppunitTest_set_include,editeng_editeng,\ $(eval $(call gb_CppunitTest_set_include,editeng_editeng,\

View File

@ -166,6 +166,7 @@ $(eval $(call gb_Library_use_externals,editeng,\
icuuc \ icuuc \
icu_headers \ icu_headers \
libxml2 \ libxml2 \
yrs \
)) ))
# vim: set noet sw=4 ts=4: # vim: set noet sw=4 ts=4:

View File

@ -46,6 +46,13 @@
enum class TextRotation; enum class TextRotation;
#if defined(YRS)
class ImpEditEngine;
class IYrsTransactionSupplier;
typedef struct TransactionInner YTransaction;
typedef struct YTextEvent YTextEvent;
#endif
#define CHARPOSGROW 16 #define CHARPOSGROW 16
#define DEFTAB 720 #define DEFTAB 720
@ -119,6 +126,19 @@ private:
bool mbModified:1; bool mbModified:1;
bool mbDisableAttributeExpanding:1; bool mbDisableAttributeExpanding:1;
#if defined(YRS)
OString m_CommentId;
IYrsTransactionSupplier * m_pYrsSupplier{nullptr};
public:
void SetYrsCommentId(IYrsTransactionSupplier *, OString const& rId);
void YrsWriteEEState();
void YrsReadEEState(YTransaction *, ImpEditEngine & rIEE);
void YrsApplyEEDelta(YTransaction *, YTextEvent const* pEvent, ImpEditEngine & rIEE);
void YrsSetStyle(sal_Int32 nPara, ::std::u16string_view rStyle);
void YrsSetParaAttr(sal_Int32 nPara, SfxPoolItem const& rItem);
OString GetYrsCommentId() const;
#endif
public: public:
EditDoc( SfxItemPool* pItemPool ); EditDoc( SfxItemPool* pItemPool );
~EditDoc(); ~EditDoc();
@ -139,21 +159,19 @@ public:
void CreateDefFont( bool bUseStyles ); void CreateDefFont( bool bUseStyles );
const SvxFont& GetDefFont() const { return maDefFont; } const SvxFont& GetDefFont() const { return maDefFont; }
void SetDefTab(sal_uInt16 nTab) void SetDefTab(sal_uInt16 nTab);
{
mnDefTab = nTab ? nTab : DEFTAB;
}
sal_uInt16 GetDefTab() const sal_uInt16 GetDefTab() const
{ {
return mnDefTab; return mnDefTab;
} }
void SetVertical( bool bVertical ) { mbIsVertical = bVertical; } void SetVertical(bool bVertical);
bool IsEffectivelyVertical() const; bool IsEffectivelyVertical() const;
bool IsTopToBottom() const; bool IsTopToBottom() const;
bool GetVertical() const; bool GetVertical() const;
void SetRotation( TextRotation nRotation ) { mnRotation = nRotation; } void SetRotation(TextRotation nRotation);
TextRotation GetRotation() const { return mnRotation; } TextRotation GetRotation() const { return mnRotation; }
void SetFixedCellHeight( bool bUseFixedCellHeight ) void SetFixedCellHeight( bool bUseFixedCellHeight )

File diff suppressed because it is too large Load Diff

View File

@ -524,6 +524,9 @@ public:
class ImpEditEngine : public SfxListener, public svl::StyleSheetUser class ImpEditEngine : public SfxListener, public svl::StyleSheetUser
{ {
friend class EditEngine; friend class EditEngine;
#if defined(YRS)
friend class EditDoc;
#endif
typedef EditEngine::ViewsType ViewsType; typedef EditEngine::ViewsType ViewsType;

View File

@ -90,6 +90,9 @@ void ImpEditEngine::SetStyleSheet( sal_Int32 nPara, SfxStyleSheet* pStyle )
if ( pCurStyle ) if ( pCurStyle )
EndListening( *pCurStyle ); EndListening( *pCurStyle );
pNode->SetStyleSheet( pStyle, maStatus.UseCharAttribs() ); pNode->SetStyleSheet( pStyle, maStatus.UseCharAttribs() );
#if defined(YRS)
maEditDoc.YrsSetStyle(nPara, pStyle ? pStyle->GetName() : OUString());
#endif
if ( pStyle ) if ( pStyle )
StartListening(*pStyle, DuplicateHandling::Allow); StartListening(*pStyle, DuplicateHandling::Allow);
@ -550,6 +553,9 @@ void ImpEditEngine::SetAttribs( EditSelection aSel, const SfxItemSet& rSet, SetA
{ {
pNode->GetContentAttribs().GetItems().Put( rItem ); pNode->GetContentAttribs().GetItems().Put( rItem );
bParaAttribFound = true; bParaAttribFound = true;
#if defined(YRS)
maEditDoc.YrsSetParaAttr(nNode, rItem);
#endif
} }
else else
{ {

View File

@ -37,6 +37,12 @@
#include <editeng/editengdllapi.h> #include <editeng/editengdllapi.h>
#if defined(YRS)
class IYrsTransactionSupplier;
typedef struct TransactionInner YTransaction;
typedef struct YTextEvent YTextEvent;
#endif
class EditTextObject; class EditTextObject;
class EditEngine; class EditEngine;
class ImpEditEngine; class ImpEditEngine;
@ -403,6 +409,14 @@ public:
/// To inform editeng that negated x document coordinates are in use. /// To inform editeng that negated x document coordinates are in use.
void SetNegativeX(bool bSet); void SetNegativeX(bool bSet);
bool IsNegativeX() const; bool IsNegativeX() const;
#if defined(YRS)
void SetYrsCommentId(IYrsTransactionSupplier *, OString const& rId);
void YrsWriteEEState();
void YrsReadEEState(YTransaction *);
void YrsApplyEEDelta(YTransaction *, YTextEvent const* pEvent);
OString GetYrsCommentId() const;
#endif
}; };
#endif // INCLUDED_EDITENG_EDITVIEW_HXX #endif // INCLUDED_EDITENG_EDITVIEW_HXX

69
include/editeng/yrs.hxx Normal file
View File

@ -0,0 +1,69 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once
extern "C" {
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wextern-c-compat"
#endif
#include <libyrs.h>
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
}
#include <rtl/string.hxx>
// check input is valid values to find encoding bugs early
#define yvalidate(cond) \
if (!(cond)) \
{ \
std::abort(); \
}
struct YOutputDeleter
{
void operator()(YOutput* const p) const { youtput_destroy(p); }
};
class IYrsTransactionSupplier
{
public:
enum class Mode
{
Edit,
Replay
};
protected:
Mode m_Mode{ Mode::Edit };
public:
IYrsTransactionSupplier() = default;
virtual ~IYrsTransactionSupplier() = default;
Mode SetMode(Mode const mode)
{
Mode ret = mode;
std::swap(ret, m_Mode);
return ret;
}
virtual YDoc* GetYDoc() = 0;
virtual Branch* GetCommentMap() = 0;
virtual Branch* GetCursorMap() = 0;
virtual YTransaction* GetReadTransaction() = 0;
virtual YTransaction* GetWriteTransaction() = 0;
virtual bool CommitTransaction(bool isForce = false) = 0;
virtual OString GenNewCommentId() = 0;
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */

View File

@ -44,6 +44,10 @@ $(eval $(call gb_Library_set_include,sfx,\
$(eval $(call gb_Library_add_defs,sfx,-DSFX2_DLLIMPLEMENTATION)) $(eval $(call gb_Library_add_defs,sfx,-DSFX2_DLLIMPLEMENTATION))
ifneq ($(WITH_YRS),)
$(eval $(call gb_Library_add_defs,sfx,-DYRS))
endif
$(eval $(call gb_Library_use_libraries,sfx,\ $(eval $(call gb_Library_use_libraries,sfx,\
basegfx \ basegfx \
comphelper \ comphelper \

View File

@ -46,6 +46,10 @@
#include <com/sun/star/lang/XInitialization.hpp> #include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/uno/XComponentContext.hpp> #include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/util/XCloseable.hpp> #include <com/sun/star/util/XCloseable.hpp>
#if defined(YRS)
#include <com/sun/star/io/SequenceInputStream.hpp>
#include <com/sun/star/connection/Connector.hpp>
#endif
#include <comphelper/getexpandeduri.hxx> #include <comphelper/getexpandeduri.hxx>
#include <comphelper/interaction.hxx> #include <comphelper/interaction.hxx>
@ -627,6 +631,45 @@ sal_Bool SAL_CALL SfxFrameLoader_Impl::load( const Sequence< PropertyValue >& rA
Reference< XModel2 > xModel = aDescriptor.getOrDefault( u"Model"_ustr, Reference< XModel2 >() ); Reference< XModel2 > xModel = aDescriptor.getOrDefault( u"Model"_ustr, Reference< XModel2 >() );
const bool bExternalModel = xModel.is(); const bool bExternalModel = xModel.is();
#if defined(YRS)
uno::Reference<connection::XConnection> xConnection;
if (!xModel.is() && aDescriptor.getOrDefault(u"URL"_ustr, OUString()) == "private:factory/swriter" && !getenv("YRSACCEPT"))
{
SAL_DEBUG("YRS connect sfx2");
// must read this SYNC
auto const conn = u"pipe,name=ytest"_ustr;
auto const xConnector = css::connection::Connector::create(m_aContext);
xConnection = xConnector->connect(conn);
uno::Sequence<sal_Int8> buf;
if (xConnection->read(buf, 4) != 4)
{
abort();
}
sal_Int32 const size{static_cast<sal_uInt8>(buf[0])
| static_cast<sal_uInt8>(buf[1]) << 8
| static_cast<sal_uInt8>(buf[2]) << 16
| static_cast<sal_uInt8>(buf[3]) << 24};
if (size != 0)
{
SAL_DEBUG("YRS connect reading file of size " << size);
uno::Sequence<sal_Int8> buff(size);
if (xConnection->read(buff, size) != size)
{
abort();
}
uno::Reference<io::XInputStream> const xInStream{
io::SequenceInputStream::createStreamFromSequence(m_aContext, buff)};
assert(xInStream.is());
aDescriptor.put(u"URL"_ustr, u"private:stream"_ustr);
aDescriptor.put(u"InputStream"_ustr, uno::Any(xInStream));
}
aDescriptor.put(u"ReadOnly"_ustr, uno::Any(true));
aDescriptor.put(u"YrsConnect"_ustr, uno::Any(xConnection));
}
#endif
// check for factory URLs to create a new doc, instead of loading one // check for factory URLs to create a new doc, instead of loading one
const OUString sURL = aDescriptor.getOrDefault( u"URL"_ustr, OUString() ); const OUString sURL = aDescriptor.getOrDefault( u"URL"_ustr, OUString() );
const bool bIsFactoryURL = sURL.startsWith( "private:factory/" ); const bool bIsFactoryURL = sURL.startsWith( "private:factory/" );

View File

@ -94,6 +94,7 @@ $(eval $(call gb_Library_use_externals,sw,\
icuuc \ icuuc \
icu_headers \ icu_headers \
libxml2 \ libxml2 \
yrs \
)) ))
ifneq ($(ENABLE_WASM_STRIP_ACCESSIBILITY),TRUE) ifneq ($(ENABLE_WASM_STRIP_ACCESSIBILITY),TRUE)

View File

@ -19,6 +19,14 @@
#pragma once #pragma once
#if defined(YRS)
#include <com/sun/star/uno/Any.h>
#include <editeng/yrs.hxx>
#include <optional>
struct SwPosition;
class SwPostItField;
#endif
/** Get information about the current document state /** Get information about the current document state
*/ */
class IDocumentState class IDocumentState
@ -47,6 +55,21 @@ public:
virtual bool IsEnableSetModified() const = 0; virtual bool IsEnableSetModified() const = 0;
virtual void SetEnableSetModified(bool bEnableSetModified) = 0; virtual void SetEnableSetModified(bool bEnableSetModified) = 0;
#if defined(YRS)
virtual void YrsInitAcceptor() = 0;
virtual void YrsInitConnector(css::uno::Any const& raConnector) = 0;
virtual IYrsTransactionSupplier::Mode SetYrsMode(IYrsTransactionSupplier::Mode mode) = 0;
virtual void YrsCommitModified() = 0;
virtual void YrsNotifySetResolved(OString const& rCommentId, SwPostItField const& rField) = 0;
virtual void YrsAddCommentImpl(SwPosition const& rPos, OString const& rCommentId) = 0;
virtual void YrsAddComment(SwPosition const& rPos, ::std::optional<SwPosition> oAnchorStart,
SwPostItField const& rField, bool isInsert)
= 0;
virtual void YrsRemoveCommentImpl(rtl::OString const& rCommentId) = 0;
virtual void YrsRemoveComment(SwPosition const& rPos, rtl::OString const& rCommentId) = 0;
#endif
protected: protected:
virtual ~IDocumentState(){}; virtual ~IDocumentState(){};
}; };

View File

@ -131,6 +131,9 @@ class SAL_DLLPUBLIC_RTTI SwPostItMgr final : public SfxListener,
SwAnnotationItem* InsertItem( SfxBroadcaster* pItem, bool bCheckExistence, bool bFocus); SwAnnotationItem* InsertItem( SfxBroadcaster* pItem, bool bCheckExistence, bool bFocus);
void RemoveItem( SfxBroadcaster* pBroadcast ); void RemoveItem( SfxBroadcaster* pBroadcast );
#if defined(YRS)
public:
#endif
VclPtr<sw::annotation::SwAnnotationWin> GetOrCreateAnnotationWindow(SwAnnotationItem& rItem, bool& rCreated); VclPtr<sw::annotation::SwAnnotationWin> GetOrCreateAnnotationWindow(SwAnnotationItem& rItem, bool& rCreated);
public: public:

File diff suppressed because it is too large Load Diff

View File

@ -21,16 +21,29 @@
#include <IDocumentState.hxx> #include <IDocumentState.hxx>
#if defined(YRS)
#include <rtl/ref.hxx>
#include <com/sun/star/connection/XAcceptor.hpp>
#include <com/sun/star/connection/XConnector.hpp>
struct SwPosition;
#endif
class SwDoc; class SwDoc;
namespace sw { namespace sw {
#if defined(YRS)
class YrsThread;
class YrsTransactionSupplier;
#endif
class DocumentStateManager final : public IDocumentState class DocumentStateManager final : public IDocumentState
{ {
public: public:
DocumentStateManager( SwDoc& i_rSwdoc ); DocumentStateManager( SwDoc& i_rSwdoc );
~DocumentStateManager();
void SetModified() override; void SetModified() override;
void ResetModified() override; void ResetModified() override;
@ -55,6 +68,26 @@ private:
bool mbUpdateExpField; //< TRUE: Update expression fields. bool mbUpdateExpField; //< TRUE: Update expression fields.
bool mbNewDoc ; //< TRUE: new Doc. bool mbNewDoc ; //< TRUE: new Doc.
bool mbInCallModified; //< TRUE: in Set/Reset-Modified link. bool mbInCallModified; //< TRUE: in Set/Reset-Modified link.
#if defined(YRS)
friend class YrsThread;
::rtl::Reference<YrsThread> m_pYrsReader;
css::uno::Reference<css::connection::XAcceptor> m_xAcceptor;
::std::unique_ptr<YrsTransactionSupplier> m_pYrsSupplier;
public:
void YrsInitAcceptor() override;
void YrsInitConnector(css::uno::Any const& raConnector) override;
IYrsTransactionSupplier::Mode SetYrsMode(IYrsTransactionSupplier::Mode mode) override;
void YrsCommitModified() override;
void YrsNotifySetResolved(OString const& rCommentId, SwPostItField const& rField) override;
void YrsAddCommentImpl(SwPosition const& rPos, OString const& rCommentId) override;
void YrsAddComment(SwPosition const& rPos, ::std::optional<SwPosition> oAnchorStart,
SwPostItField const& rField, bool isInsert) override;
void YrsRemoveCommentImpl(OString const& rCommentId) override;
void YrsRemoveComment(SwPosition const& rPos, OString const& rCommentId) override;
#endif
}; };
} }

View File

@ -1209,6 +1209,22 @@ void SwDocShell::LoadingFinished()
// before <FinishedLoading(..)> is called. // before <FinishedLoading(..)> is called.
const bool bHasDocToStayModified( m_xDoc->getIDocumentState().IsModified() && m_xDoc->getIDocumentLinksAdministration().LinksUpdated() ); const bool bHasDocToStayModified( m_xDoc->getIDocumentState().IsModified() && m_xDoc->getIDocumentLinksAdministration().LinksUpdated() );
#if defined(YRS)
#if 0
// this doesn't even filter as advertised!
auto const args{GetBaseModel()->getArgs2({u"YrsConnect"_ustr})};
#endif
// when loading, it is only available from SfxMedium, not SfxBaseModel
for (auto const& rArg : GetMedium()->GetArgs())
{
if (rArg.Name == "YrsConnect")
{
m_xDoc->getIDocumentState().YrsInitConnector(rArg.Value);
break;
}
}
#endif
FinishedLoading(); FinishedLoading();
SfxViewFrame* pVFrame = SfxViewFrame::GetFirst(this); SfxViewFrame* pVFrame = SfxViewFrame::GetFirst(this);
if(pVFrame) if(pVFrame)

View File

@ -61,6 +61,9 @@
#include <docsh.hxx> #include <docsh.hxx>
#include <doc.hxx> #include <doc.hxx>
#include <IDocumentUndoRedo.hxx> #include <IDocumentUndoRedo.hxx>
#if defined(YRS)
#include <IDocumentState.hxx>
#endif
#include <SwUndoField.hxx> #include <SwUndoField.hxx>
#include <edtwin.hxx> #include <edtwin.hxx>
#include "ShadowOverlayObject.hxx" #include "ShadowOverlayObject.hxx"
@ -206,6 +209,9 @@ void SwAnnotationWin::SetPostItText()
// get text from SwPostItField and insert into our textview // get text from SwPostItField and insert into our textview
mpOutliner->SetModifyHdl( Link<LinkParamNone*,void>() ); mpOutliner->SetModifyHdl( Link<LinkParamNone*,void>() );
mpOutliner->EnableUndo( false ); mpOutliner->EnableUndo( false );
#if defined(YRS)
auto const mode = mrView.GetDocShell()->GetDoc()->getIDocumentState().SetYrsMode(IYrsTransactionSupplier::Mode::Replay);
#endif
if( mpField->GetTextObject() ) if( mpField->GetTextObject() )
mpOutliner->SetText( *mpField->GetTextObject() ); mpOutliner->SetText( *mpField->GetTextObject() );
else else
@ -214,6 +220,9 @@ void SwAnnotationWin::SetPostItText()
GetOutlinerView()->SetStyleSheet(SwResId(STR_POOLCOLL_COMMENT)); GetOutlinerView()->SetStyleSheet(SwResId(STR_POOLCOLL_COMMENT));
GetOutlinerView()->InsertText(sNewText); GetOutlinerView()->InsertText(sNewText);
} }
#if defined(YRS)
mrView.GetDocShell()->GetDoc()->getIDocumentState().SetYrsMode(mode);
#endif
mpOutliner->ClearModifyFlag(); mpOutliner->ClearModifyFlag();
mpOutliner->GetUndoManager().Clear(); mpOutliner->GetUndoManager().Clear();
@ -256,6 +265,11 @@ void SwAnnotationWin::SetResolved(bool resolved)
UpdateData(); UpdateData();
Invalidate(); Invalidate();
collectUIInformation(u"SETRESOLVED"_ustr,get_id()); collectUIInformation(u"SETRESOLVED"_ustr,get_id());
#if defined(YRS)
mrView.GetDocShell()->GetDoc()->getIDocumentState().YrsNotifySetResolved(
GetOutlinerView()->GetEditView().GetYrsCommentId(),
*static_cast<SwPostItField const*>(mpFormatField->GetField()));
#endif
} }
void SwAnnotationWin::ToggleResolved() void SwAnnotationWin::ToggleResolved()

View File

@ -45,6 +45,9 @@
#include <doc.hxx> #include <doc.hxx>
#include <IDocumentSettingAccess.hxx> #include <IDocumentSettingAccess.hxx>
#include <IDocumentFieldsAccess.hxx> #include <IDocumentFieldsAccess.hxx>
#if defined(YRS)
#include <IDocumentState.hxx>
#endif
#include <docstyle.hxx> #include <docstyle.hxx>
#include <fldbas.hxx> #include <fldbas.hxx>
#include <fmtfld.hxx> #include <fmtfld.hxx>
@ -517,6 +520,11 @@ void SwPostItMgr::RemoveItem( SfxBroadcaster* pBroadcast )
[&pBroadcast](const std::unique_ptr<SwAnnotationItem>& pField) { return pField->GetBroadcaster() == pBroadcast; }); [&pBroadcast](const std::unique_ptr<SwAnnotationItem>& pField) { return pField->GetBroadcaster() == pBroadcast; });
if (i != mvPostItFields.end()) if (i != mvPostItFields.end())
{ {
#if defined(YRS)
mpView->GetDocShell()->GetDoc()->getIDocumentState().YrsRemoveComment(
(*i)->GetAnchorPosition(),
(*i)->mpPostIt->GetOutlinerView()->GetEditView().GetYrsCommentId());
#endif
std::unique_ptr<SwAnnotationItem> p = std::move(*i); std::unique_ptr<SwAnnotationItem> p = std::move(*i);
// tdf#120487 remove from list before dispose, so comment window // tdf#120487 remove from list before dispose, so comment window
// won't be recreated due to the entry still in the list if focus // won't be recreated due to the entry still in the list if focus
@ -900,6 +908,9 @@ VclPtr<SwAnnotationWin> SwPostItMgr::GetOrCreateAnnotationWindow(SwAnnotationIte
pPostIt->InitControls(); pPostIt->InitControls();
pPostIt->SetReadonly(mbReadOnly); pPostIt->SetReadonly(mbReadOnly);
rItem.mpPostIt = pPostIt; rItem.mpPostIt = pPostIt;
#if defined(YRS)
SAL_DEBUG("YRS GetOrCreateAnnotationWindow " << rItem.mpPostIt);
#endif
if (mpAnswer) if (mpAnswer)
{ {
if (pPostIt->GetPostItField()->GetParentPostItId() != 0) //do we really have another note in front of this one if (pPostIt->GetPostItField()->GetParentPostItId() != 0) //do we really have another note in front of this one

View File

@ -55,6 +55,9 @@
#include <wrtsh.hxx> #include <wrtsh.hxx>
#include <AnnotationWin.hxx> #include <AnnotationWin.hxx>
#include <IDocumentDeviceAccess.hxx> #include <IDocumentDeviceAccess.hxx>
#if defined(YRS)
#include <IDocumentState.hxx>
#endif
#include <redline.hxx> #include <redline.hxx>
#include <memory> #include <memory>
@ -199,6 +202,14 @@ OUString SidebarTextControl::RequestHelp(tools::Rectangle& rHelpRect)
return OUString(); return OUString();
} }
#if defined(YRS)
void SidebarTextControl::EditViewInvalidate(const tools::Rectangle& rRect)
{
mrDocView.GetDocShell()->GetDoc()->getIDocumentState().YrsCommitModified();
return WeldEditView::EditViewInvalidate(rRect);
}
#endif
void SidebarTextControl::EditViewScrollStateChange() void SidebarTextControl::EditViewScrollStateChange()
{ {
mrSidebarWin.SetScrollbar(); mrSidebarWin.SetScrollbar();

View File

@ -57,6 +57,9 @@ class SidebarTextControl : public WeldEditView
virtual EditEngine* GetEditEngine() const override; virtual EditEngine* GetEditEngine() const override;
#if defined(YRS)
virtual void EditViewInvalidate(const tools::Rectangle& rRect) override;
#endif
virtual void EditViewScrollStateChange() override; virtual void EditViewScrollStateChange() override;
void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;

View File

@ -75,6 +75,10 @@
#include <txmsrt.hxx> #include <txmsrt.hxx>
#include <unotools/useroptions.hxx> #include <unotools/useroptions.hxx>
#include <IDocumentContentOperations.hxx> #include <IDocumentContentOperations.hxx>
#if defined(YRS)
#include <IDocumentState.hxx>
#include <txtfld.hxx>
#endif
#include <translatehelper.hxx> #include <translatehelper.hxx>
using namespace com::sun::star::uno; using namespace com::sun::star::uno;
@ -1524,7 +1528,8 @@ bool SwFieldMgr::InsertField(
// insert // insert
pCurShell->StartAllAction(); pCurShell->StartAllAction();
bool const isSuccess = pCurShell->InsertField2(*pField, rData.m_oAnnotationRange ? &*rData.m_oAnnotationRange : nullptr); ::std::optional<SwPosition> oAnchorStart;
bool const isSuccess = pCurShell->InsertField2(*pField, rData.m_oAnnotationRange ? &*rData.m_oAnnotationRange : nullptr, &oAnchorStart);
if (isSuccess) if (isSuccess)
{ {
@ -1566,6 +1571,21 @@ bool SwFieldMgr::InsertField(
pField.reset(); pField.reset();
pCurShell->EndAllAction(); pCurShell->EndAllAction();
#if defined(YRS)
if (isSuccess)
{
// now the SwAnnotationWin are created
// shell cursor is behind field
SwPosition const pos{pCurShell->GetCursor()->GetPoint()->nContent, -1};
pCurShell->GetDoc()->getIDocumentState().YrsAddComment(
pos, oAnchorStart,
static_cast<SwPostItField const&>(*SwCursorShell::GetTextFieldAtPos(&pos, ::sw::GetTextAttrMode::Default)->GetFormatField().GetField()),
true);
pCurShell->GetDoc()->getIDocumentState().YrsCommitModified();
}
#endif
return isSuccess; return isSuccess;
} }

View File

@ -317,7 +317,10 @@ typedef bool (SwWrtShell::*FNSimpleMove)();
int IntelligentCut(SelectionType nSelectionType, bool bCut = true); int IntelligentCut(SelectionType nSelectionType, bool bCut = true);
// edit // edit
SW_DLLPUBLIC bool InsertField2(SwField const &, SwPaM* pAnnotationRange = nullptr); bool InsertField2Impl(SwField const &, SwPaM* pAnnotationRange,
::std::optional<SwPosition> *const poAnchorStart);
SW_DLLPUBLIC bool InsertField2(SwField const &, SwPaM* pAnnotationRange = nullptr,
::std::optional<SwPosition> *const poAnchorStart = nullptr);
SW_DLLPUBLIC void Insert(const OUString &); SW_DLLPUBLIC void Insert(const OUString &);
// graphic // graphic
void InsertGraphic( const OUString &rPath, const OUString &rFilter, void InsertGraphic( const OUString &rPath, const OUString &rFilter,

View File

@ -1027,6 +1027,9 @@ SwView::SwView(SfxViewFrame& _rFrame, SfxViewShell* pOldSh)
// Set DocShell // Set DocShell
m_xGlueDocShell.reset(new SwViewGlueDocShell(*this, rDocSh)); m_xGlueDocShell.reset(new SwViewGlueDocShell(*this, rDocSh));
m_pPostItMgr.reset(new SwPostItMgr(this)); m_pPostItMgr.reset(new SwPostItMgr(this));
#if defined(YRS)
m_pWrtShell->GetDoc()->getIDocumentState().YrsInitAcceptor();
#endif
// Check and process the DocSize. Via the handler, the shell could not // Check and process the DocSize. Via the handler, the shell could not
// be found, because the shell is not known in the SFX management // be found, because the shell is not known in the SFX management

View File

@ -523,6 +523,17 @@ Reference< XInterface > SwXTextDocument::getCurrentSelection()
sal_Bool SwXTextDocument::attachResource(const OUString& aURL, const Sequence< beans::PropertyValue >& aArgs) sal_Bool SwXTextDocument::attachResource(const OUString& aURL, const Sequence< beans::PropertyValue >& aArgs)
{ {
#if defined(YRS)
// this is for new document
for (auto const& rArg : aArgs)
{
if (rArg.Name == "YrsConnect")
{
m_pDocShell->GetDoc()->getIDocumentState().YrsInitConnector(rArg.Value);
break;
}
}
#endif
return SfxBaseModel::attachResource(aURL, aArgs); return SfxBaseModel::attachResource(aURL, aArgs);
} }

View File

@ -66,11 +66,18 @@
#include <strings.hrc> #include <strings.hrc>
#include <officecfg/Office/Common.hxx> #include <officecfg/Office/Common.hxx>
bool SwWrtShell::InsertField2(SwField const& rField, SwPaM* pAnnotationRange) bool SwWrtShell::InsertField2(SwField const& rField,
SwPaM* pAnnotationRange, ::std::optional<SwPosition> *const poAnchorStart)
{ {
ResetCursorStack(); ResetCursorStack();
if(!CanInsert()) if(!CanInsert())
return false; return false;
return InsertField2Impl(rField, pAnnotationRange, poAnchorStart);
}
bool SwWrtShell::InsertField2Impl(SwField const& rField,
SwPaM* pAnnotationRange, ::std::optional<SwPosition> *const poAnchorStart)
{
StartAllAction(); StartAllAction();
SwRewriter aRewriter; SwRewriter aRewriter;
@ -135,7 +142,11 @@ bool SwWrtShell::InsertField2(SwField const& rField, SwPaM* pAnnotationRange)
pAnnotationTextRange->Start()->AdjustContent(-1); pAnnotationTextRange->Start()->AdjustContent(-1);
} }
IDocumentMarkAccess* pMarksAccess = GetDoc()->getIDocumentMarkAccess(); IDocumentMarkAccess* pMarksAccess = GetDoc()->getIDocumentMarkAccess();
pMarksAccess->makeAnnotationMark( *pAnnotationTextRange, SwMarkName() ); auto pMark{pMarksAccess->makeAnnotationMark(*pAnnotationTextRange, SwMarkName())};
if (poAnchorStart)
{
poAnchorStart->emplace(pMark->GetMarkStart());
}
} }
pAnnotationTextRange.reset(); pAnnotationTextRange.reset();
} }