summaryrefslogtreecommitdiff
path: root/editeng/source
diff options
context:
space:
mode:
authorMichael Stahl <michael.stahl@allotropia.de>2024-10-25 15:02:34 +0200
committerMichael Stahl <michael.stahl@allotropia.de>2025-05-07 13:36:01 +0200
commit8f8034177b9efcfaa52b3dc0980aec58e75fca5b (patch)
tree42d859a1b6ba3abbb3196c9dc800960c66b2aba8 /editeng/source
parent0a677d53a6c0eba6f859eaa658d4aecf78bee631 (diff)
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>
Diffstat (limited to 'editeng/source')
-rw-r--r--editeng/source/editeng/editdoc.cxx2434
-rw-r--r--editeng/source/editeng/impedit.hxx3
-rw-r--r--editeng/source/editeng/impedit5.cxx6
3 files changed, 2443 insertions, 0 deletions
diff --git a/editeng/source/editeng/editdoc.cxx b/editeng/source/editeng/editdoc.cxx
index a34ca78d810d..235708d6c93a 100644
--- a/editeng/source/editeng/editdoc.cxx
+++ b/editeng/source/editeng/editdoc.cxx
@@ -42,6 +42,16 @@
#include <editeng/lrspitem.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/lspcitem.hxx>
+#if defined(YRS)
+#include <editeng/frmdiritem.hxx>
+#include <editeng/hngpnctitem.hxx>
+#include <editeng/forbiddenruleitem.hxx>
+#include <editeng/scriptspaceitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/numdef.hxx>
+#include <svl/itemiter.hxx>
+#endif
#include <editdoc.hxx>
#include <editeng/eerdll.hxx>
@@ -714,6 +724,2301 @@ void EditSelection::Adjust( const EditDoc& rNodes )
}
}
+#if defined(YRS)
+#include <editeng/yrs.hxx>
+
+namespace {
+
+struct YrsReplayGuard
+{
+ IYrsTransactionSupplier *const m_pYrsSupplier;
+ IYrsTransactionSupplier::Mode m_Mode;
+
+ explicit YrsReplayGuard(IYrsTransactionSupplier *const pYrsSupplier)
+ : m_pYrsSupplier(pYrsSupplier)
+ {
+ if (m_pYrsSupplier)
+ {
+ m_Mode = m_pYrsSupplier->SetMode(IYrsTransactionSupplier::Mode::Replay);
+ }
+ }
+ ~YrsReplayGuard()
+ {
+ if (m_pYrsSupplier)
+ {
+ m_pYrsSupplier->SetMode(m_Mode);
+ }
+ }
+};
+
+struct YrsWrite
+{
+ YTransaction *const pTxn;
+ Branch *const pProps;
+ Branch *const pText;
+};
+
+constexpr char CH_PARA = 0x0d;
+
+YrsWrite GetYrsWrite(IYrsTransactionSupplier *const pYrsSupplier,
+ OString const& rId, YTransaction *const pInTxn = nullptr)
+{
+ if (!pYrsSupplier)
+ {
+ return { nullptr, nullptr, nullptr };
+ }
+ YDoc *const pDoc{pYrsSupplier->GetYDoc()};
+ YTransaction *const pTxn{pInTxn ? pInTxn : pYrsSupplier->GetWriteTransaction()};
+ // write is disabled when receiving edits from peers
+ if (!pTxn)
+ {
+ return { nullptr, nullptr, nullptr };
+ }
+ assert(pDoc);
+ Branch *const pComments{pYrsSupplier->GetCommentMap()};
+ ::std::unique_ptr<YOutput, YOutputDeleter> const pComment{ymap_get(pComments, pTxn, rId.getStr())};
+ yvalidate(pComment->tag == Y_ARRAY);
+ yvalidate(pComment->len == 1);
+ Branch *const pCommentArray{pComment->value.y_type};
+ ::std::unique_ptr<YOutput, YOutputDeleter> const pProps{yarray_get(pCommentArray, pTxn, 1)};
+ yvalidate(pProps->tag == Y_MAP);
+ yvalidate(pProps->len == 1);
+ ::std::unique_ptr<YOutput, YOutputDeleter> const pText{yarray_get(pCommentArray, pTxn, 2)};
+ yvalidate(pText->tag == Y_TEXT);
+ yvalidate(pText->len == 1);
+ return { pTxn, pProps->value.y_type, pText->value.y_type };
+}
+
+void YrsSetVertical(IYrsTransactionSupplier *const pYrsSupplier,
+ OString const& rCommentId, bool const isVertical)
+{
+ YrsWrite const yw{GetYrsWrite(pYrsSupplier, rCommentId)};
+ if (yw.pTxn == nullptr)
+ {
+ return;
+ }
+ YInput const input{yinput_bool(isVertical ? Y_TRUE : Y_FALSE)};
+ ymap_insert(yw.pProps, yw.pTxn, "is-vertical", &input);
+}
+
+void YrsSetRotation(IYrsTransactionSupplier *const pYrsSupplier,
+ OString const& rCommentId, TextRotation const nRotation)
+{
+ YrsWrite const yw{GetYrsWrite(pYrsSupplier, rCommentId)};
+ if (yw.pTxn == nullptr)
+ {
+ return;
+ }
+ YInput const input{yinput_long(static_cast<int64_t>(nRotation))};
+ ymap_insert(yw.pProps, yw.pTxn, "rotation", &input);
+}
+
+void YrsSetDefTab(IYrsTransactionSupplier *const pYrsSupplier,
+ OString const& rCommentId, sal_uInt16 const nDefTab)
+{
+ YrsWrite const yw{GetYrsWrite(pYrsSupplier, rCommentId)};
+ if (yw.pTxn == nullptr)
+ {
+ return;
+ }
+ YInput const input{yinput_long(static_cast<int64_t>(nDefTab))};
+ ymap_insert(yw.pProps, yw.pTxn, "def-tab", &input);
+}
+
+void YrsInsertAttribImplImpl(YrsWrite const& yw, SfxPoolItem const& rItm,
+ uint32_t const nStart, uint32_t const nLen)
+{
+ ::std::vector<YInput> tabStops;
+ ::std::vector<::std::vector<YInput>> tabStopValues;
+ ::std::vector<OString> tempStrings;
+ ::std::vector<YInput> itemArray;
+ ::std::vector<char const*> itemNames;
+ YInput attr;
+ char const* attrName;
+ switch (rItm.Which())
+ {
+ case EE_CHAR_COLOR:
+ case EE_CHAR_BKGCOLOR:
+ {
+ sal_uInt32 const nColor{static_cast<SvxColorItem const&>(rItm).getColor()};
+ attr = yinput_long(nColor);
+ attrName = rItm.Which() == EE_CHAR_COLOR ? "EE_CHAR_COLOR" : "EE_CHAR_BKGCOLOR";
+ break;
+ }
+ case EE_CHAR_FONTINFO:
+ case EE_CHAR_FONTINFO_CJK:
+ case EE_CHAR_FONTINFO_CTL:
+ {
+ SvxFontItem const& rItem{static_cast<SvxFontItem const&>(rItm)};
+ tempStrings.reserve(2); // prevent realloc
+ tempStrings.emplace_back(OUStringToOString(rItem.GetFamilyName(), RTL_TEXTENCODING_UTF8));
+ itemArray.emplace_back(yinput_string(tempStrings.back().getStr()));
+ itemNames.emplace_back("familyname");
+ tempStrings.emplace_back(OUStringToOString(rItem.GetStyleName(), RTL_TEXTENCODING_UTF8));
+ itemArray.emplace_back(yinput_string(tempStrings.back().getStr()));
+ itemNames.emplace_back("style");
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetFamily())));
+ itemNames.emplace_back("family");
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetPitch())));
+ itemNames.emplace_back("pitch");
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetCharSet())));
+ itemNames.emplace_back("charset");
+ attr = yinput_json_map(const_cast<char**>(itemNames.data()), itemArray.data(), itemArray.size());
+ attrName = rItm.Which() == EE_CHAR_FONTINFO
+ ? "EE_CHAR_FONTINFO"
+ : rItm.Which() == EE_CHAR_FONTINFO_CJK ? "EE_CHAR_FONTINFO_CJK" : "EE_CHAR_FONTINFO_CTL";
+ break;
+ }
+ case EE_CHAR_FONTHEIGHT:
+ case EE_CHAR_FONTHEIGHT_CJK:
+ case EE_CHAR_FONTHEIGHT_CTL:
+ {
+ SvxFontHeightItem const& rItem{static_cast<SvxFontHeightItem const&>(rItm)};
+ itemNames.emplace_back("height");
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetHeight())));
+ itemNames.emplace_back("prop");
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetProp())));
+ itemNames.emplace_back("propunit");
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetPropUnit())));
+ attr = yinput_json_map(const_cast<char**>(itemNames.data()), itemArray.data(), itemArray.size());
+ attrName = rItm.Which() == EE_CHAR_FONTHEIGHT
+ ? "EE_CHAR_FONTHEIGHT"
+ : rItm.Which() == EE_CHAR_FONTHEIGHT_CJK ? "EE_CHAR_FONTHEIGHT_CJK" : "EE_CHAR_FONTHEIGHT_CTL";
+ break;
+ }
+ case EE_CHAR_FONTWIDTH:
+ {
+ SvxCharScaleWidthItem const& rItem{static_cast<SvxCharScaleWidthItem const&>(rItm)};
+ attr = yinput_long(rItem.GetValue());
+ attrName = "EE_CHAR_FONTWIDTH";
+ break;
+ }
+ case EE_CHAR_WEIGHT:
+ case EE_CHAR_WEIGHT_CJK:
+ case EE_CHAR_WEIGHT_CTL:
+ {
+ SvxWeightItem const& rItem{static_cast<SvxWeightItem const&>(rItm)};
+ attr = yinput_long(uint64_t(rItem.GetWeight()));
+ attrName = rItm.Which() == EE_CHAR_WEIGHT
+ ? "EE_CHAR_WEIGHT"
+ : rItm.Which() == EE_CHAR_WEIGHT_CJK ? "EE_CHAR_WEIGHT_CJK" : "EE_CHAR_WEIGHT_CTL";
+ break;
+ }
+ case EE_CHAR_UNDERLINE:
+ case EE_CHAR_OVERLINE:
+ {
+ SvxTextLineItem const& rItem{static_cast<SvxTextLineItem const&>(rItm)};
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetLineStyle())));
+ itemNames.emplace_back("style");
+ itemArray.emplace_back(yinput_long(uint64_t(sal_uInt32(rItem.GetColor()))));
+ itemNames.emplace_back("color");
+ attr = yinput_json_map(const_cast<char**>(itemNames.data()), itemArray.data(), itemArray.size());
+ attrName = rItm.Which() == EE_CHAR_UNDERLINE ? "EE_CHAR_UNDERLINE" : "EE_CHAR_OVERLINE";
+ break;
+ }
+ case EE_CHAR_STRIKEOUT:
+ {
+ SvxCrossedOutItem const& rItem{static_cast<SvxCrossedOutItem const&>(rItm)};
+ attr = yinput_long(uint64_t(rItem.GetStrikeout()));
+ attrName = "EE_CHAR_STRIKEOUT";
+ break;
+ }
+ case EE_CHAR_ITALIC:
+ case EE_CHAR_ITALIC_CJK:
+ case EE_CHAR_ITALIC_CTL:
+ {
+ SvxPostureItem const& rItem{static_cast<SvxPostureItem const&>(rItm)};
+ attr = yinput_long(uint64_t(rItem.GetPosture()));
+ attrName = rItm.Which() == EE_CHAR_ITALIC
+ ? "EE_CHAR_ITALIC"
+ : rItm.Which() == EE_CHAR_ITALIC_CJK ? "EE_CHAR_ITALIC_CJK" : "EE_CHAR_ITALIC_CTL";
+ break;
+ }
+ case EE_CHAR_OUTLINE:
+ {
+ SvxContourItem const& rItem{static_cast<SvxContourItem const&>(rItm)};
+ attr = yinput_bool(rItem.GetValue() ? Y_TRUE : Y_FALSE);
+ attrName = "EE_CHAR_OUTLINE";
+ break;
+ }
+ case EE_CHAR_SHADOW:
+ {
+ SvxShadowedItem const& rItem{static_cast<SvxShadowedItem const&>(rItm)};
+ attr = yinput_bool(rItem.GetValue() ? Y_TRUE : Y_FALSE);
+ attrName = "EE_CHAR_SHADOW";
+ break;
+ }
+ case EE_CHAR_ESCAPEMENT:
+ {
+ SvxEscapementItem const& rItem{static_cast<SvxEscapementItem const&>(rItm)};
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetEsc())));
+ itemNames.emplace_back("esc");
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetProportionalHeight())));
+ itemNames.emplace_back("prop");
+ attr = yinput_json_map(const_cast<char**>(itemNames.data()), itemArray.data(), itemArray.size());
+ attrName = "EE_CHAR_ESCAPEMENT";
+ break;
+ }
+ case EE_CHAR_PAIRKERNING:
+ {
+ SvxAutoKernItem const& rItem{static_cast<SvxAutoKernItem const&>(rItm)};
+ attr = yinput_bool(rItem.GetValue() ? Y_TRUE : Y_FALSE);
+ attrName = "EE_CHAR_PAIRKERNING";
+ break;
+ }
+ case EE_CHAR_KERNING:
+ {
+ SvxKerningItem const& rItem{static_cast<SvxKerningItem const&>(rItm)};
+ attr = yinput_long(uint64_t(rItem.GetValue()));
+ attrName = "EE_CHAR_KERNING";
+ break;
+ }
+ case EE_CHAR_WLM:
+ {
+ SvxWordLineModeItem const& rItem{static_cast<SvxWordLineModeItem const&>(rItm)};
+ attr = yinput_bool(rItem.GetValue() ? Y_TRUE : Y_FALSE);
+ attrName = "EE_CHAR_WLM";
+ break;
+ }
+ case EE_CHAR_LANGUAGE:
+ case EE_CHAR_LANGUAGE_CJK:
+ case EE_CHAR_LANGUAGE_CTL:
+ {
+ SvxLanguageItem const& rItem{static_cast<SvxLanguageItem const&>(rItm)};
+ attr = yinput_long(uint64_t(rItem.GetValue().get()));
+ attrName = rItm.Which() == EE_CHAR_LANGUAGE
+ ? "EE_CHAR_LANGUAGE"
+ : rItm.Which() == EE_CHAR_LANGUAGE_CJK ? "EE_CHAR_LANGUAGE_CJK" : "EE_CHAR_LANGUAGE_CTL";
+ break;
+ }
+ case EE_CHAR_EMPHASISMARK:
+ {
+ SvxEmphasisMarkItem const& rItem{static_cast<SvxEmphasisMarkItem const&>(rItm)};
+ attr = yinput_long(uint64_t(rItem.GetValue()));
+ attrName = "EE_CHAR_EMPHASISMARK";
+ break;
+ }
+ case EE_CHAR_RELIEF:
+ {
+ SvxCharReliefItem const& rItem{static_cast<SvxCharReliefItem const&>(rItm)};
+ attr = yinput_long(uint64_t(rItem.GetValue()));
+ attrName = "EE_CHAR_RELIEF";
+ break;
+ }
+ case EE_CHAR_CASEMAP:
+ {
+ SvxCaseMapItem const& rItem{static_cast<SvxCaseMapItem const&>(rItm)};
+ attr = yinput_long(uint64_t(rItem.GetValue()));
+ attrName = "EE_CHAR_CASEMAP";
+ break;
+ }
+ case EE_PARA_WRITINGDIR:
+ {
+ SvxFrameDirectionItem const& rItem{static_cast<SvxFrameDirectionItem const&>(rItm)};
+ attr = yinput_long(uint64_t(rItem.GetValue()));
+ attrName = "EE_PARA_WRITINGDIR";
+ break;
+ }
+ case EE_PARA_HANGINGPUNCTUATION:
+ {
+ SvxHangingPunctuationItem const& rItem{static_cast<SvxHangingPunctuationItem const&>(rItm)};
+ attr = yinput_bool(rItem.GetValue() ? Y_TRUE : Y_FALSE);
+ attrName = "EE_PARA_HANGINGPUNCTUATION";
+ break;
+ }
+ case EE_PARA_FORBIDDENRULES:
+ {
+ SvxForbiddenRuleItem const& rItem{static_cast<SvxForbiddenRuleItem const&>(rItm)};
+ attr = yinput_bool(rItem.GetValue() ? Y_TRUE : Y_FALSE);
+ attrName = "EE_PARA_FORBIDDENRULES";
+ break;
+ }
+ case EE_PARA_ASIANCJKSPACING:
+ {
+ SvxScriptSpaceItem const& rItem{static_cast<SvxScriptSpaceItem const&>(rItm)};
+ attr = yinput_bool(rItem.GetValue() ? Y_TRUE : Y_FALSE);
+ attrName = "EE_PARA_ASIANCJKSPACING";
+ break;
+ }
+//TODO complex, but apparently no way to set this in comment? inline constexpr TypedWhichId<SvxNumBulletItem> EE_PARA_NUMBULLET (EE_PARA_START+5);
+ case EE_PARA_HYPHENATE:
+ case EE_PARA_HYPHENATE_NO_CAPS:
+ case EE_PARA_HYPHENATE_NO_LAST_WORD:
+ case EE_PARA_BULLETSTATE:
+ {
+ SfxBoolItem const& rItem{static_cast<SfxBoolItem const&>(rItm)};
+ attr = yinput_bool(rItem.GetValue() ? Y_TRUE : Y_FALSE);
+ attrName = rItm.Which() == EE_PARA_HYPHENATE
+ ? "EE_PARA_HYPHENATE"
+ : rItm.Which() == EE_PARA_HYPHENATE_NO_CAPS
+ ? "EE_PARA_HYPHENATE_NO_CAPS"
+ : rItm.Which() == EE_PARA_HYPHENATE_NO_LAST_WORD
+ ? "EE_PARA_HYPHENATE_NO_LAST_WORD"
+ : "EE_PARA_BULLETSTATE";
+ break;
+ }
+//TODO no way to set this in comment? inline constexpr TypedWhichId<SvxLRSpaceItem> EE_PARA_OUTLLRSPACE (EE_PARA_START+10);
+ case EE_PARA_OUTLLEVEL:
+ {
+ SfxInt16Item const& rItem{static_cast<SfxInt16Item const&>(rItm)};
+ attr = yinput_long(uint64_t(rItem.GetValue()));
+ attrName = "EE_PARA_OUTLLEVEL";
+ break;
+ }
+//TODO complex, but apparently no way to set this in comment? inline constexpr TypedWhichId<SvxBulletItem> EE_PARA_BULLET (EE_PARA_START+12);
+ case EE_PARA_LRSPACE:
+ {
+ SvxLRSpaceItem const& rItem{static_cast<SvxLRSpaceItem const&>(rItm)};
+ itemArray.emplace_back(yinput_float(rItem.GetTextFirstLineOffset().m_dValue));
+ itemNames.emplace_back("first-line-offset");
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetTextFirstLineOffset().m_nUnit)));
+ itemNames.emplace_back("first-line-offset-unit");
+ itemArray.emplace_back(yinput_float(rItem.GetLeft().m_dValue));
+ itemNames.emplace_back("left-margin");
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetLeft().m_nUnit)));
+ itemNames.emplace_back("left-margin-unit");
+ itemArray.emplace_back(yinput_float(rItem.GetRight().m_dValue));
+ itemNames.emplace_back("right-margin");
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetRight().m_nUnit)));
+ itemNames.emplace_back("right-margin-unit");
+ itemArray.emplace_back(yinput_bool(rItem.IsAutoFirst() ? Y_TRUE : Y_FALSE));
+ itemNames.emplace_back("auto-first");
+ attr = yinput_json_map(const_cast<char**>(itemNames.data()), itemArray.data(), itemArray.size());
+ attrName = "EE_PARA_LRSPACE";
+ break;
+ }
+ case EE_PARA_ULSPACE:
+ {
+ SvxULSpaceItem const& rItem{static_cast<SvxULSpaceItem const&>(rItm)};
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetUpper())));
+ itemNames.emplace_back("upper-margin");
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetLower())));
+ itemNames.emplace_back("lower-margin");
+ // TODO what does EE support here?
+ attr = yinput_json_map(const_cast<char**>(itemNames.data()), itemArray.data(), itemArray.size());
+ attrName = "EE_PARA_ULSPACE";
+ break;
+ }
+ case EE_PARA_SBL:
+ {
+ SvxLineSpacingItem const& rItem{static_cast<SvxLineSpacingItem const&>(rItm)};
+ switch (rItem.GetLineSpaceRule())
+ {
+ case SvxLineSpaceRule::Auto:
+ break;
+ case SvxLineSpaceRule::Fix:
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetLineHeight())));
+ itemNames.emplace_back("line-space-fix");
+ break;
+ case SvxLineSpaceRule::Min:
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetLineHeight())));
+ itemNames.emplace_back("line-space-min");
+ break;
+ }
+ switch (rItem.GetInterLineSpaceRule())
+ {
+ case SvxInterLineSpaceRule::Off:
+ break;
+ case SvxInterLineSpaceRule::Prop:
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetPropLineSpace())));
+ itemNames.emplace_back("inter-line-space-prop");
+ break;
+ case SvxInterLineSpaceRule::Fix:
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetInterLineSpace())));
+ itemNames.emplace_back("inter-line-space-fix");
+ break;
+ }
+ attr = yinput_json_map(const_cast<char**>(itemNames.data()), itemArray.data(), itemArray.size());
+ attrName = "EE_PARA_SBL";
+ break;
+ }
+ case EE_PARA_JUST:
+ {
+ SvxAdjustItem const& rItem{static_cast<SvxAdjustItem const&>(rItm)};
+ switch (rItem.GetAdjust())
+ {
+ case SvxAdjust::Left:
+ case SvxAdjust::Right:
+ case SvxAdjust::Center:
+ attr = yinput_long(uint64_t(rItem.GetAdjust()));
+ break;
+ case SvxAdjust::Block:
+ switch (rItem.GetLastBlock())
+ {
+ case SvxAdjust::Left:
+ attr = yinput_long(uint64_t(SvxAdjust::Block));
+ break;
+ case SvxAdjust::Center:
+ attr = yinput_long(uint64_t(5));
+ break;
+ case SvxAdjust::Block:
+ attr = yinput_long(uint64_t(rItem.GetOneWord() == SvxAdjust::Block ? 7 : 6));
+ break;
+ default:
+ assert(false);
+ }
+ break;
+ default:
+ assert(false);
+ }
+ attrName = "EE_PARA_JUST";
+ break;
+ }
+ case EE_PARA_TABS:
+ {
+ SvxTabStopItem const& rItem{static_cast<SvxTabStopItem const&>(rItm)};
+ itemNames.emplace_back("default-distance");
+ itemArray.emplace_back(yinput_long(uint64_t(rItem.GetDefaultDistance())));
+ tabStopValues.reserve(rItem.Count()); // prevent realloc
+ for (decltype(rItem.Count()) i{0}; i < rItem.Count(); ++i)
+ {
+ SvxTabStop const& rTab{rItem.At(i)};
+ char const*const names[]{"pos", "adjustment", "decimal", "fill"};
+ tabStopValues.emplace_back();
+ tabStopValues.back().emplace_back(yinput_long(uint64_t(rTab.GetTabPos())));
+ tabStopValues.back().emplace_back(yinput_long(uint64_t(rTab.GetAdjustment())));
+ tabStopValues.back().emplace_back(yinput_long(uint64_t(rTab.GetDecimal())));
+ tabStopValues.back().emplace_back(yinput_long(uint64_t(rTab.GetFill())));
+ tabStops.emplace_back(yinput_json_map(const_cast<char**>(names), tabStopValues.back().data(), 4));
+ }
+ itemNames.emplace_back("tab-stops");
+ itemArray.emplace_back(yinput_json_array(tabStops.data(), tabStops.size()));
+ attr = yinput_json_map(const_cast<char**>(itemNames.data()), itemArray.data(), itemArray.size());
+ attrName = "EE_PARA_TABS";
+ break;
+ }
+ case EE_PARA_JUST_METHOD:
+ {
+ SvxJustifyMethodItem const& rItem{static_cast<SvxJustifyMethodItem const&>(rItm)};
+ attr = yinput_long(uint64_t(rItem.GetValue()));
+ attrName = "EE_PARA_JUST_METHOD";
+ break;
+ }
+ case EE_PARA_VER_JUST:
+ {
+ SvxVerJustifyItem const& rItem{static_cast<SvxVerJustifyItem const&>(rItm)};
+ attr = yinput_long(uint64_t(rItem.GetValue()));
+ attrName = "EE_PARA_VER_JUST";
+ break;
+ }
+ // these aren't editable?
+//constexpr TypedWhichId<SvXMLAttrContainerItem> EE_CHAR_XMLATTRIBS (EE_CHAR_START+27);
+//constexpr TypedWhichId<SfxGrabBagItem> EE_CHAR_GRABBAG (EE_CHAR_START+30);
+
+ default:
+ assert(false);
+ }
+ assert(itemNames.size() == itemArray.size());
+ YInput const attrs{yinput_json_map(const_cast<char**>(&attrName), &attr, 1)};
+ ytext_format(yw.pText, yw.pTxn, nStart, nLen, &attrs);
+}
+
+void YrsInsertAttribImpl(YrsWrite const& yw, uint32_t const offset, EditCharAttrib const*const pAttr)
+{
+ auto const start{offset + pAttr->GetStart()};
+ auto const len{pAttr->GetEnd() - pAttr->GetStart()};
+ YrsInsertAttribImplImpl(yw, *pAttr->GetItem(), start, len);
+}
+
+void YrsInsertFeature(IYrsTransactionSupplier *const pYrsSupplier, OString const& rCommentId,
+ EditDoc const& rDoc, uint32_t const index, EditCharAttrib const*const pAttr)
+{
+ YrsWrite const yw{GetYrsWrite(pYrsSupplier, rCommentId)};
+ if (yw.pTxn == nullptr)
+ {
+ return;
+ }
+ uint32_t i{0};
+ for (auto paras{index}; paras != 0; --paras)
+ {
+ i += rDoc.GetObject(paras-1)->Len() + 1;
+ }
+ i += pAttr->GetStart();
+ char const feature[]{ CH_FEATURE, '\0' };
+ switch (pAttr->Which())
+ {
+ case EE_FEATURE_TAB:
+ case EE_FEATURE_LINEBR:
+ {
+ YInput const type{yinput_string(pAttr->Which() == EE_FEATURE_TAB ? "tab" : "line")};
+ YInput attrArray[]{ type };
+ char const*const attrNames[]{ "feature" };
+ YInput const attrs{yinput_json_map(const_cast<char**>(attrNames), attrArray, 1)};
+ ytext_insert(yw.pText, yw.pTxn, i, feature, &attrs);
+ break;
+ }
+ case EE_FEATURE_FIELD:
+ {
+ SvxURLField const*const pURLField{dynamic_cast<SvxURLField const*>(dynamic_cast<SvxFieldItem const*>(pAttr->GetItem())->GetField())};
+ assert(pURLField);
+ YInput const type{yinput_string("url")};
+ // ??? somehow this comes out as Y_JSON_NUM at the other end?
+ YInput const format{yinput_long(static_cast<int64_t>(pURLField->GetFormat()))};
+ OString const urlStr{OUStringToOString(pURLField->GetURL(), RTL_TEXTENCODING_UTF8)};
+ YInput const url{yinput_string(urlStr.getStr())};
+ OString const reprStr{OUStringToOString(pURLField->GetRepresentation(), RTL_TEXTENCODING_UTF8)};
+ YInput const representation{yinput_string(reprStr.getStr())};
+ OString const targetStr{OUStringToOString(pURLField->GetTargetFrame(), RTL_TEXTENCODING_UTF8)};
+ YInput const targetframe{yinput_string(targetStr.getStr())};
+ YInput attrArray[]{ type, format, url, representation, targetframe };
+ char const*const attrNames[]{ "feature", "url-format", "url-url", "url-representation", "url-targetframe" };
+ // don't use yinput_ymap for this!
+ YInput const attrs{yinput_json_map(const_cast<char**>(attrNames), attrArray, 5)};
+ ytext_insert(yw.pText, yw.pTxn, i, feature, &attrs);
+
+ break;
+ }
+ default: // EE_FEATURE_NOTCONV appears unused?
+ assert(false);
+ }
+}
+
+void YrsAddPara(IYrsTransactionSupplier *const pYrsSupplier,
+ OString const& rCommentId, EditDoc const& rDoc, uint32_t const index)
+{
+ YrsWrite const yw{GetYrsWrite(pYrsSupplier, rCommentId)};
+ if (yw.pTxn == nullptr)
+ {
+ return;
+ }
+ SAL_DEBUG("YRS YrsAddPara");
+ // need to encode into 1 YText
+ char const para[]{ CH_PARA, '\0' };
+ uint32_t i{0};
+ // UTF-16 index should be equal to EditDoc one
+ for (auto paras{index}; paras != 0; --paras)
+ {
+ i += rDoc.GetObject(paras-1)->Len() + 1;
+ }
+ ContentAttribs const& rParaAttribs{rDoc.GetObject(index)->GetContentAttribs()};
+ auto const pStyle{rParaAttribs.GetStyleSheet()};
+ if (pStyle)
+ {
+ OString const styleName{OUStringToOString(pStyle->GetName(), RTL_TEXTENCODING_UTF8)};
+ YInput const style{yinput_string(styleName.getStr())};
+ YInput attrArray[]{ style };
+ char const*const attrNames[]{ "para-style" };
+ YInput const attrs{yinput_json_map(const_cast<char**>(attrNames), attrArray, 1)};
+ ytext_insert(yw.pText, yw.pTxn, i, para, &attrs);
+ }
+ else
+ {
+ ytext_insert(yw.pText, yw.pTxn, i, para, nullptr);
+ }
+ for (SfxItemIter it{rParaAttribs.GetItems()}; !it.IsAtEnd(); it.NextItem())
+ {
+ YrsInsertAttribImplImpl(yw, *it.GetCurItem(), i, 1);
+ }
+}
+
+void YrsRemovePara(IYrsTransactionSupplier *const pYrsSupplier,
+ OString const& rCommentId, EditDoc const& rDoc, uint32_t const index)
+{
+ YrsWrite const yw{GetYrsWrite(pYrsSupplier, rCommentId)};
+ if (yw.pTxn == nullptr)
+ {
+ return;
+ }
+ uint32_t i{0};
+ if (index != 0)
+ {
+ for (auto paras{index}; paras != 0; --paras)
+ {
+ i += rDoc.GetObject(paras-1)->Len() + 1;
+ }
+ }
+ uint32_t const len(rDoc.GetObject(index)->Len() + 1);
+ ytext_remove_range(yw.pText, yw.pTxn, i, len);
+}
+
+void YrsClear(IYrsTransactionSupplier *const pYrsSupplier, OString const& rCommentId)
+{
+ YrsWrite const yw{GetYrsWrite(pYrsSupplier, rCommentId)};
+ if (yw.pTxn == nullptr)
+ {
+ return;
+ }
+ auto const len{ytext_len(yw.pText, yw.pTxn)};
+ ytext_remove_range(yw.pText, yw.pTxn, 0, len);
+}
+
+void YrsInsertParaBreak(IYrsTransactionSupplier *const pYrsSupplier, OString const& rCommentId,
+ EditDoc const& rDoc, uint32_t const index, uint32_t const content)
+{
+ YrsWrite const yw{GetYrsWrite(pYrsSupplier, rCommentId)};
+ if (yw.pTxn == nullptr)
+ {
+ return;
+ }
+ // need to encode into 1 YText
+ char const para[]{ CH_PARA, '\0' };
+ uint32_t i{0};
+ for (auto paras{index}; paras != 0; --paras)
+ {
+ i += rDoc.GetObject(paras-1)->Len() + 1;
+ }
+ i += content;
+ ContentAttribs const& rParaAttribs{rDoc.GetObject(index)->GetContentAttribs()};
+ OString const styleName{OUStringToOString(rParaAttribs.GetStyleSheet()->GetName(), RTL_TEXTENCODING_UTF8)};
+ YInput const style{yinput_string(styleName.getStr())};
+ YInput attrArray[]{ style };
+ char const*const attrNames[]{ "para-style" };
+ YInput const attrs{yinput_json_map(const_cast<char**>(attrNames), attrArray, 1)};
+ ytext_insert(yw.pText, yw.pTxn, i, para, &attrs);
+ for (SfxItemIter it{rParaAttribs.GetItems()}; !it.IsAtEnd(); it.NextItem())
+ {
+ YrsInsertAttribImplImpl(yw, *it.GetCurItem(), i, 1);
+ }
+}
+
+void YrsInsertText(IYrsTransactionSupplier *const pYrsSupplier, OString const& rCommentId,
+ EditDoc const& rDoc, uint32_t const index, uint32_t const content, ::std::u16string_view const rText)
+{
+ YrsWrite const yw{GetYrsWrite(pYrsSupplier, rCommentId)};
+ if (yw.pTxn == nullptr)
+ {
+ return;
+ }
+ uint32_t i{0};
+ for (auto paras{index}; paras != 0; --paras)
+ {
+ i += rDoc.GetObject(paras-1)->Len() + 1;
+ }
+ i += content;
+ OString const text{::rtl::OUStringToOString(rText, RTL_TEXTENCODING_UTF8)};
+ ytext_insert(yw.pText, yw.pTxn, i, text.getStr(), nullptr);
+}
+
+void YrsConnectPara(IYrsTransactionSupplier *const pYrsSupplier, OString const& rCommentId,
+ EditDoc const& rDoc, uint32_t const index, uint32_t const pos)
+{
+ YrsWrite const yw{GetYrsWrite(pYrsSupplier, rCommentId)};
+ if (yw.pTxn == nullptr)
+ {
+ return;
+ }
+ uint32_t i{0};
+ for (auto paras{index}; paras != 0; --paras)
+ {
+ i += rDoc.GetObject(paras-1)->Len() + 1;
+ }
+ i += pos;
+ ytext_remove_range(yw.pText, yw.pTxn, i, 1);
+}
+
+void YrsRemoveChars(IYrsTransactionSupplier *const pYrsSupplier, OString const& rCommentId,
+ EditDoc const& rDoc, uint32_t const index, uint32_t const content, uint32_t const length)
+{
+ YrsWrite const yw{GetYrsWrite(pYrsSupplier, rCommentId)};
+ if (yw.pTxn == nullptr)
+ {
+ return;
+ }
+ uint32_t i{0};
+ for (auto paras{index}; paras != 0; --paras)
+ {
+ i += rDoc.GetObject(paras-1)->Len() + 1;
+ }
+ i += content;
+ ytext_remove_range(yw.pText, yw.pTxn, i, length);
+}
+
+void YrsSetStyle(IYrsTransactionSupplier *const pYrsSupplier, OString const& rCommentId,
+ EditDoc const& rDoc, uint32_t const index, ::std::u16string_view const rStyle)
+{
+ YrsWrite const yw{GetYrsWrite(pYrsSupplier, rCommentId)};
+ if (yw.pTxn == nullptr)
+ {
+ return;
+ }
+ uint32_t i{0};
+ for (auto paras{index}; paras != 0; --paras)
+ {
+ i += rDoc.GetObject(paras-1)->Len() + 1;
+ }
+ i += rDoc.GetObject(index)->Len();
+ OString const styleName{OUStringToOString(rStyle, RTL_TEXTENCODING_UTF8)};
+ YInput const style{yinput_string(styleName.getStr())};
+ YInput attrArray[]{ style };
+ char const*const attrNames[]{ "para-style" };
+ YInput const attrs{yinput_json_map(const_cast<char**>(attrNames), attrArray, 1)};
+ ytext_format(yw.pText, yw.pTxn, i, 1, &attrs);
+}
+
+void YrsSetParaAttr(IYrsTransactionSupplier *const pYrsSupplier, OString const& rCommentId,
+ EditDoc const& rDoc, uint32_t const index, SfxPoolItem const& rItem)
+{
+ YrsWrite const yw{GetYrsWrite(pYrsSupplier, rCommentId)};
+ if (yw.pTxn == nullptr)
+ {
+ return;
+ }
+ uint32_t i{0};
+ for (auto paras{index}; paras != 0; --paras)
+ {
+ i += rDoc.GetObject(paras-1)->Len() + 1;
+ }
+ i += rDoc.GetObject(index)->Len();
+ YrsInsertAttribImplImpl(yw, rItem, i, 1);
+}
+
+char const* YrsWhichToAttrName(sal_Int16 const nWhich)
+{
+ switch (nWhich)
+ {
+ case EE_CHAR_COLOR:
+ return "EE_CHAR_COLOR";
+ case EE_CHAR_BKGCOLOR:
+ return "EE_CHAR_BKGCOLOR";
+ case EE_CHAR_FONTINFO:
+ return "EE_CHAR_FONTINFO";
+ case EE_CHAR_FONTINFO_CJK:
+ return "EE_CHAR_FONTINFO_CJK";
+ case EE_CHAR_FONTINFO_CTL:
+ return "EE_CHAR_FONTINFO_CTL";
+ case EE_CHAR_FONTHEIGHT:
+ return "EE_CHAR_FONTHEIGHT";
+ case EE_CHAR_FONTHEIGHT_CJK:
+ return "EE_CHAR_FONTHEIGHT_CJK";
+ case EE_CHAR_FONTHEIGHT_CTL:
+ return "EE_CHAR_FONTHEIGHT_CTL";
+ case EE_CHAR_FONTWIDTH:
+ return "EE_CHAR_FONTWIDTH";
+ case EE_CHAR_WEIGHT:
+ return "EE_CHAR_WEIGHT";
+ case EE_CHAR_WEIGHT_CJK:
+ return "EE_CHAR_WEIGHT_CJK";
+ case EE_CHAR_WEIGHT_CTL:
+ return "EE_CHAR_WEIGHT_CTL";
+ case EE_CHAR_UNDERLINE:
+ return "EE_CHAR_UNDERLINE";
+ case EE_CHAR_OVERLINE:
+ return "EE_CHAR_OVERLINE";
+ case EE_CHAR_STRIKEOUT:
+ return "EE_CHAR_STRIKEOUT";
+ case EE_CHAR_ITALIC:
+ return "EE_CHAR_ITALIC";
+ case EE_CHAR_ITALIC_CJK:
+ return "EE_CHAR_ITALIC_CJK";
+ case EE_CHAR_ITALIC_CTL:
+ return "EE_CHAR_ITALIC_CTL";
+ case EE_CHAR_OUTLINE:
+ return "EE_CHAR_OUTLINE";
+ case EE_CHAR_SHADOW:
+ return "EE_CHAR_SHADOW";
+ case EE_CHAR_ESCAPEMENT:
+ return "EE_CHAR_ESCAPEMENT";
+ case EE_CHAR_PAIRKERNING:
+ return "EE_CHAR_PAIRKERNING";
+ case EE_CHAR_KERNING:
+ return "EE_CHAR_KERNING";
+ case EE_CHAR_WLM:
+ return "EE_CHAR_WLM";
+ case EE_CHAR_LANGUAGE:
+ return "EE_CHAR_LANGUAGE";
+ case EE_CHAR_LANGUAGE_CJK:
+ return "EE_CHAR_LANGUAGE_CJK";
+ case EE_CHAR_LANGUAGE_CTL:
+ return "EE_CHAR_LANGUAGE_CTL";
+ case EE_CHAR_EMPHASISMARK:
+ return "EE_CHAR_EMPHASISMARK";
+ case EE_CHAR_RELIEF:
+ return "EE_CHAR_RELIEF";
+ case EE_CHAR_CASEMAP:
+ return "EE_CHAR_CASEMAP";
+ case EE_PARA_WRITINGDIR:
+ return "EE_PARA_WRITINGDIR";
+ case EE_PARA_HANGINGPUNCTUATION:
+ return "EE_PARA_HANGINGPUNCTUATION";
+ case EE_PARA_FORBIDDENRULES:
+ return "EE_PARA_FORBIDDENRULES";
+ case EE_PARA_ASIANCJKSPACING:
+ return "EE_PARA_ASIANCJKSPACING";
+//TODO complex, but apparently no way to set this in comment? inline constexpr TypedWhichId<SvxNumBulletItem> EE_PARA_NUMBULLET (EE_PARA_START+5);
+ case EE_PARA_HYPHENATE:
+ return "EE_PARA_HYPHENATE";
+ case EE_PARA_HYPHENATE_NO_CAPS:
+ return "EE_PARA_HYPHENATE_NO_CAPS";
+ case EE_PARA_HYPHENATE_NO_LAST_WORD:
+ return "EE_PARA_HYPHENATE_NO_LAST_WORD";
+ case EE_PARA_BULLETSTATE:
+ return "EE_PARA_BULLETSTATE";
+//TODO no way to set this in comment? inline constexpr TypedWhichId<SvxLRSpaceItem> EE_PARA_OUTLLRSPACE (EE_PARA_START+10);
+ case EE_PARA_OUTLLEVEL:
+ return "EE_PARA_OUTLLEVEL";
+//TODO complex, but apparently no way to set this in comment? inline constexpr TypedWhichId<SvxBulletItem> EE_PARA_BULLET (EE_PARA_START+12);
+ case EE_PARA_LRSPACE:
+ return "EE_PARA_LRSPACE";
+ case EE_PARA_ULSPACE:
+ return "EE_PARA_ULSPACE";
+ case EE_PARA_SBL:
+ return "EE_PARA_SBL";
+ case EE_PARA_JUST:
+ return "EE_PARA_JUST";
+ case EE_PARA_TABS:
+ return "EE_PARA_TABS";
+ case EE_PARA_JUST_METHOD:
+ return "EE_PARA_JUST_METHOD";
+ case EE_PARA_VER_JUST:
+ return "EE_PARA_VER_JUST";
+ default:
+ assert(false);
+ }
+}
+
+void YrsRemoveAttrib(IYrsTransactionSupplier *const pYrsSupplier, OString const& rCommentId,
+ EditDoc const& rDoc, uint32_t const index, sal_uInt16 const nWhich, sal_Int32 const nStart, sal_Int32 const nEnd)
+{
+ YrsWrite const yw{GetYrsWrite(pYrsSupplier, rCommentId)};
+ if (yw.pTxn == nullptr)
+ {
+ return;
+ }
+ uint32_t i{0};
+ for (auto paras{index}; paras != 0; --paras)
+ {
+ i += rDoc.GetObject(paras-1)->Len() + 1;
+ }
+ YInput const attr{yinput_null()};
+ char const*const attrName{YrsWhichToAttrName(nWhich)};
+ YInput const attrs{yinput_json_map(const_cast<char**>(&attrName), const_cast<YInput*>(&attr), 1)};
+ ytext_format(yw.pText, yw.pTxn, i + nStart, nEnd - nStart, &attrs);
+}
+
+void YrsInsertAttrib(IYrsTransactionSupplier *const pYrsSupplier, OString const& rCommentId, EditDoc const& rDoc, uint32_t const index, EditCharAttrib const*const pAttr)
+{
+ YrsWrite const yw{GetYrsWrite(pYrsSupplier, rCommentId)};
+ if (yw.pTxn == nullptr)
+ {
+ return;
+ }
+ uint32_t i{0};
+ for (auto paras{index}; paras != 0; --paras)
+ {
+ i += rDoc.GetObject(paras-1)->Len() + 1;
+ }
+ YrsInsertAttribImpl(yw, i, pAttr);
+}
+
+uint64_t YrsReadInt(YOutput const& rValue)
+{
+ // with the v1 encoding, JSON is being sent apparently (like "family":2) , which has issues with integers being sometimes read as floats so workaround here
+ if (rValue.tag == Y_JSON_INT)
+ {
+ return rValue.value.integer;
+ }
+ else
+ {
+ yvalidate(rValue.tag == Y_JSON_NUM);
+ return ::std::lround(rValue.value.num);
+ }
+}
+
+void YrsImplInsertAttr(SfxItemSet & rSet, ::std::vector<sal_uInt16> *const pRemoved,
+ char const*const pKey, YOutput const& rValue)
+{
+ sal_uInt16 nWhich{0};
+ if (strcmp(pKey, "EE_CHAR_COLOR") == 0)
+ {
+ nWhich = EE_CHAR_COLOR;
+ }
+ else if (strcmp(pKey, "EE_CHAR_BKGCOLOR") == 0)
+ {
+ nWhich = EE_CHAR_BKGCOLOR;
+ }
+ else if (strcmp(pKey, "EE_CHAR_FONTINFO") == 0)
+ {
+ nWhich = EE_CHAR_FONTINFO;
+ }
+ else if (strcmp(pKey, "EE_CHAR_FONTINFO_CJK") == 0)
+ {
+ nWhich = EE_CHAR_FONTINFO_CJK;
+ }
+ else if (strcmp(pKey, "EE_CHAR_FONTINFO_CTL") == 0)
+ {
+ nWhich = EE_CHAR_FONTINFO_CTL;
+ }
+ else if (strcmp(pKey, "EE_CHAR_FONTHEIGHT") == 0)
+ {
+ nWhich = EE_CHAR_FONTHEIGHT;
+ }
+ else if (strcmp(pKey, "EE_CHAR_FONTHEIGHT_CJK") == 0)
+ {
+ nWhich = EE_CHAR_FONTHEIGHT_CJK;
+ }
+ else if (strcmp(pKey, "EE_CHAR_FONTHEIGHT_CTL") == 0)
+ {
+ nWhich = EE_CHAR_FONTHEIGHT_CTL;
+ }
+ else if (strcmp(pKey, "EE_CHAR_FONTWIDTH") == 0)
+ {
+ nWhich = EE_CHAR_FONTWIDTH;
+ }
+ else if (strcmp(pKey, "EE_CHAR_WEIGHT") == 0)
+ {
+ nWhich = EE_CHAR_WEIGHT;
+ }
+ else if (strcmp(pKey, "EE_CHAR_WEIGHT_CJK") == 0)
+ {
+ nWhich = EE_CHAR_WEIGHT_CJK;
+ }
+ else if (strcmp(pKey, "EE_CHAR_WEIGHT_CTL") == 0)
+ {
+ nWhich = EE_CHAR_WEIGHT_CTL;
+ }
+ else if (strcmp(pKey, "EE_CHAR_UNDERLINE") == 0)
+ {
+ nWhich = EE_CHAR_UNDERLINE;
+ }
+ else if (strcmp(pKey, "EE_CHAR_OVERLINE") == 0)
+ {
+ nWhich = EE_CHAR_OVERLINE;
+ }
+ else if (strcmp(pKey, "EE_CHAR_STRIKEOUT") == 0)
+ {
+ nWhich = EE_CHAR_STRIKEOUT;
+ }
+ else if (strcmp(pKey, "EE_CHAR_ITALIC") == 0)
+ {
+ nWhich = EE_CHAR_ITALIC;
+ }
+ else if (strcmp(pKey, "EE_CHAR_ITALIC_CJK") == 0)
+ {
+ nWhich = EE_CHAR_ITALIC_CJK;
+ }
+ else if (strcmp(pKey, "EE_CHAR_ITALIC_CTL") == 0)
+ {
+ nWhich = EE_CHAR_ITALIC_CTL;
+ }
+ else if (strcmp(pKey, "EE_CHAR_OUTLINE") == 0)
+ {
+ nWhich = EE_CHAR_OUTLINE;
+ }
+ else if (strcmp(pKey, "EE_CHAR_SHADOW") == 0)
+ {
+ nWhich = EE_CHAR_SHADOW;
+ }
+ else if (strcmp(pKey, "EE_CHAR_ESCAPEMENT") == 0)
+ {
+ nWhich = EE_CHAR_ESCAPEMENT;
+ }
+ else if (strcmp(pKey, "EE_CHAR_PAIRKERNING") == 0)
+ {
+ nWhich = EE_CHAR_PAIRKERNING;
+ }
+ else if (strcmp(pKey, "EE_CHAR_KERNING") == 0)
+ {
+ nWhich = EE_CHAR_KERNING;
+ }
+ else if (strcmp(pKey, "EE_CHAR_WLM") == 0)
+ {
+ nWhich = EE_CHAR_WLM;
+ }
+ else if (strcmp(pKey, "EE_CHAR_LANGUAGE") == 0)
+ {
+ nWhich = EE_CHAR_LANGUAGE;
+ }
+ else if (strcmp(pKey, "EE_CHAR_LANGUAGE_CJK") == 0)
+ {
+ nWhich = EE_CHAR_LANGUAGE_CJK;
+ }
+ else if (strcmp(pKey, "EE_CHAR_LANGUAGE_CTL") == 0)
+ {
+ nWhich = EE_CHAR_LANGUAGE_CTL;
+ }
+ else if (strcmp(pKey, "EE_CHAR_EMPHASISMARK") == 0)
+ {
+ nWhich = EE_CHAR_EMPHASISMARK;
+ }
+ else if (strcmp(pKey, "EE_CHAR_RELIEF") == 0)
+ {
+ nWhich = EE_CHAR_RELIEF;
+ }
+ else if (strcmp(pKey, "EE_CHAR_CASEMAP") == 0)
+ {
+ nWhich = EE_CHAR_CASEMAP;
+ }
+ else if (strcmp(pKey, "EE_PARA_WRITINGDIR") == 0)
+ {
+ nWhich = EE_PARA_WRITINGDIR;
+ }
+ else if (strcmp(pKey, "EE_PARA_HANGINGPUNCTUATION") == 0)
+ {
+ nWhich = EE_PARA_HANGINGPUNCTUATION;
+ }
+ else if (strcmp(pKey, "EE_PARA_FORBIDDENRULES") == 0)
+ {
+ nWhich = EE_PARA_FORBIDDENRULES;
+ }
+ else if (strcmp(pKey, "EE_PARA_ASIANCJKSPACING") == 0)
+ {
+ nWhich = EE_PARA_ASIANCJKSPACING;
+ }
+ else if (strcmp(pKey, "EE_PARA_HYPHENATE") == 0)
+ {
+ nWhich = EE_PARA_HYPHENATE;
+ }
+ else if (strcmp(pKey, "EE_PARA_HYPHENATE_NO_CAPS") == 0)
+ {
+ nWhich = EE_PARA_HYPHENATE_NO_CAPS;
+ }
+ else if (strcmp(pKey, "EE_PARA_HYPHENATE_NO_LAST_WORD") == 0)
+ {
+ nWhich = EE_PARA_HYPHENATE_NO_LAST_WORD;
+ }
+ else if (strcmp(pKey, "EE_PARA_BULLETSTATE") == 0)
+ {
+ nWhich = EE_PARA_BULLETSTATE;
+ }
+ else if (strcmp(pKey, "EE_PARA_OUTLLEVEL") == 0)
+ {
+ nWhich = EE_PARA_OUTLLEVEL;
+ }
+ else if (strcmp(pKey, "EE_PARA_LRSPACE") == 0)
+ {
+ nWhich = EE_PARA_LRSPACE;
+ }
+ else if (strcmp(pKey, "EE_PARA_ULSPACE") == 0)
+ {
+ nWhich = EE_PARA_ULSPACE;
+ }
+ else if (strcmp(pKey, "EE_PARA_SBL") == 0)
+ {
+ nWhich = EE_PARA_SBL;
+ }
+ else if (strcmp(pKey, "EE_PARA_JUST") == 0)
+ {
+ nWhich = EE_PARA_JUST;
+ }
+ else if (strcmp(pKey, "EE_PARA_TABS") == 0)
+ {
+ nWhich = EE_PARA_TABS;
+ }
+ else if (strcmp(pKey, "EE_PARA_JUST_METHOD") == 0)
+ {
+ nWhich = EE_PARA_JUST_METHOD;
+ }
+ else if (strcmp(pKey, "EE_PARA_VER_JUST") == 0)
+ {
+ nWhich = EE_PARA_VER_JUST;
+ }
+ else if (pKey[0] == 'E' && pKey[1] == 'E' && pKey[2] == '_')
+ {
+ abort();
+ }
+ else
+ {
+ return;
+ }
+
+ if (rValue.tag == Y_JSON_NULL)
+ {
+ assert(pRemoved);
+ if (pRemoved)
+ {
+ pRemoved->emplace_back(nWhich);
+ }
+ return;
+ }
+ else switch (nWhich)
+ {
+ case EE_CHAR_COLOR:
+ case EE_CHAR_BKGCOLOR:
+ {
+ Color const c(ColorTransparency, YrsReadInt(rValue));
+ SvxColorItem const item(c, nWhich);
+ rSet.Put(item);
+ break;
+ }
+ case EE_CHAR_FONTINFO:
+ case EE_CHAR_FONTINFO_CJK:
+ case EE_CHAR_FONTINFO_CTL:
+ {
+ yvalidate(rValue.tag == Y_JSON_MAP);
+ ::std::optional<OUString> oFamilyName;
+ ::std::optional<OUString> oStyle;
+ ::std::optional<FontFamily> oFamily;
+ ::std::optional<FontPitch> oPitch;
+ ::std::optional<rtl_TextEncoding> oCharset;
+ for (decltype(rValue.len) i = 0; i < rValue.len; ++i)
+ {
+ const char*const pEntry{rValue.value.map[i].key};
+ if (strcmp(pEntry, "familyname") == 0)
+ {
+ yvalidate(rValue.value.map[i].value->tag == Y_JSON_STR);
+ oFamilyName.emplace(OStringToOUString(rValue.value.map[i].value->value.str, RTL_TEXTENCODING_UTF8));
+ }
+ else if (strcmp(pEntry, "style") == 0)
+ {
+ yvalidate(rValue.value.map[i].value->tag == Y_JSON_STR);
+ oStyle.emplace(OStringToOUString(rValue.value.map[i].value->value.str, RTL_TEXTENCODING_UTF8));
+ }
+ else if (strcmp(pEntry, "family") == 0)
+ {
+ auto const value{YrsReadInt(*rValue.value.map[i].value)};
+ yvalidate(FAMILY_DONTKNOW <= value && value <= FAMILY_SYSTEM);
+ oFamily.emplace(FontFamily(value));
+ }
+ else if (strcmp(pEntry, "pitch") == 0)
+ {
+ auto const value{YrsReadInt(*rValue.value.map[i].value)};
+ yvalidate(PITCH_DONTKNOW <= value && value <= PITCH_VARIABLE);
+ oPitch.emplace(FontPitch(value));
+ }
+ else if (strcmp(pEntry, "charset") == 0)
+ {
+ auto const value{YrsReadInt(*rValue.value.map[i].value)};
+ yvalidate(/*RTL_TEXTENCODING_DONTKNOW <= value &&*/ value <= RTL_TEXTENCODING_UNICODE);
+ oCharset.emplace(rtl_TextEncoding(value));
+ }
+ else yvalidate(false);
+ }
+ if (oFamilyName && oStyle && oFamily && oPitch && oCharset)
+ {
+ SvxFontItem const item{
+ *oFamily, *oFamilyName, *oStyle, *oPitch, *oCharset, nWhich};
+ rSet.Put(item);
+ }
+ else yvalidate(false);
+ break;
+ }
+ case EE_CHAR_FONTHEIGHT:
+ case EE_CHAR_FONTHEIGHT_CJK:
+ case EE_CHAR_FONTHEIGHT_CTL:
+ {
+ yvalidate(rValue.tag == Y_JSON_MAP);
+ ::std::optional<sal_uInt32> oHeight;
+ ::std::optional<sal_uInt16> oProp;
+ ::std::optional<MapUnit> oMapUnit;
+ for (decltype(rValue.len) i = 0; i < rValue.len; ++i)
+ {
+ const char*const pEntry{rValue.value.map[i].key};
+ if (strcmp(pEntry, "height") == 0)
+ {
+ auto const value{YrsReadInt(*rValue.value.map[i].value)};
+ yvalidate(/*0 <= value && */value <= SAL_MAX_INT32);
+ oHeight.emplace(value);
+ }
+ else if (strcmp(pEntry, "prop") == 0)
+ {
+ auto const value{YrsReadInt(*rValue.value.map[i].value)};
+ yvalidate(/*0 <= value && */value <= SAL_MAX_INT16);
+ oProp.emplace(value);
+ }
+ else if (strcmp(pEntry, "propunit") == 0)
+ {
+ auto const value{YrsReadInt(*rValue.value.map[i].value)};
+ yvalidate(/*0 <= value && */value <= ::std::underlying_type_t<MapUnit>(MapUnit::LAST));
+ oMapUnit.emplace(MapUnit(value));
+ }
+ else yvalidate(false);
+ }
+ if (oHeight && oProp && oMapUnit)
+ {
+ SvxFontHeightItem item{*oHeight, 100, nWhich};
+ item.SetProp(*oProp, *oMapUnit);
+ rSet.Put(item);
+ }
+ else yvalidate(false);
+ break;
+ }
+ case EE_CHAR_FONTWIDTH:
+ {
+ auto const value{YrsReadInt(rValue)};
+ yvalidate(/*0 <= value && */value <= SAL_MAX_INT16);
+ SvxCharScaleWidthItem const item{sal_uInt16(value), TypedWhichId<SvxCharScaleWidthItem>(nWhich)};
+ rSet.Put(item);
+ break;
+ }
+ case EE_CHAR_WEIGHT:
+ case EE_CHAR_WEIGHT_CJK:
+ case EE_CHAR_WEIGHT_CTL:
+ {
+ auto const value{YrsReadInt(rValue)};
+ yvalidate(WEIGHT_DONTKNOW <= value && value <= WEIGHT_BLACK);
+ SvxWeightItem const item{FontWeight(value), nWhich};
+ rSet.Put(item);
+ break;
+ }
+ case EE_CHAR_UNDERLINE:
+ case EE_CHAR_OVERLINE:
+ {
+ yvalidate(rValue.tag == Y_JSON_MAP);
+ ::std::optional<FontLineStyle> oStyle;
+ ::std::optional<Color> oColor;
+ for (decltype(rValue.len) i = 0; i < rValue.len; ++i)
+ {
+ const char*const pEntry{rValue.value.map[i].key};
+ if (strcmp(pEntry, "style") == 0)
+ {
+ auto const value{YrsReadInt(*rValue.value.map[i].value)};
+ yvalidate(LINESTYLE_NONE <= value && value <= LINESTYLE_BOLDWAVE);
+ oStyle.emplace(FontLineStyle(value));
+ }
+ else if (strcmp(pEntry, "color") == 0)
+ {
+ oColor.emplace(ColorTransparency, YrsReadInt(*rValue.value.map[i].value));
+ }
+ else yvalidate(false);
+ }
+ if (oStyle && oColor)
+ {
+ if (nWhich == EE_CHAR_UNDERLINE)
+ {
+ SvxUnderlineItem item{*oStyle, EE_CHAR_UNDERLINE};
+ item.SetColor(*oColor);
+ rSet.Put(item);
+ }
+ else
+ {
+ SvxOverlineItem item{*oStyle, EE_CHAR_OVERLINE};
+ item.SetColor(*oColor);
+ rSet.Put(item);
+ }
+ }
+ else yvalidate(false);
+ break;
+ }
+ case EE_CHAR_STRIKEOUT:
+ {
+ auto const value{YrsReadInt(rValue)};
+ yvalidate(STRIKEOUT_NONE <= value && value <= STRIKEOUT_X);
+ SvxCrossedOutItem const item{FontStrikeout(value), nWhich};
+ rSet.Put(item);
+ break;
+ }
+ case EE_CHAR_ITALIC:
+ case EE_CHAR_ITALIC_CJK:
+ case EE_CHAR_ITALIC_CTL:
+ {
+ auto const value{YrsReadInt(rValue)};
+ yvalidate(ITALIC_NONE <= value && value <= ITALIC_DONTKNOW);
+ SvxPostureItem const item{FontItalic(value), nWhich};
+ rSet.Put(item);
+ break;
+ }
+ case EE_CHAR_OUTLINE:
+ {
+ yvalidate(rValue.tag == Y_JSON_BOOL);
+ SvxContourItem const item{rValue.value.flag == Y_TRUE, nWhich};
+ rSet.Put(item);
+ break;
+ }
+ case EE_CHAR_SHADOW:
+ {
+ yvalidate(rValue.tag == Y_JSON_BOOL);
+ SvxShadowedItem const item{rValue.value.flag == Y_TRUE, nWhich};
+ rSet.Put(item);
+ break;
+ }
+ case EE_CHAR_ESCAPEMENT:
+ {
+ yvalidate(rValue.tag == Y_JSON_MAP);
+ ::std::optional<short> oEsc;
+ ::std::optional<sal_uInt8> oProp;
+ for (decltype(rValue.len) i = 0; i < rValue.len; ++i)
+ {
+ const char*const pEntry{rValue.value.map[i].key};
+ if (strcmp(pEntry, "esc") == 0)
+ {
+ int64_t const value{static_cast<int64_t>(YrsReadInt(*rValue.value.map[i].value))};
+ yvalidate(SAL_MIN_INT16 <= value && value <= SAL_MAX_INT16);
+ oEsc.emplace(short(value));
+ }
+ else if (strcmp(pEntry, "prop") == 0)
+ {
+ auto const value{YrsReadInt(*rValue.value.map[i].value)};
+ yvalidate(/*0 <= value && */value <= SAL_MAX_UINT8);
+ oProp.emplace(sal_uInt8(value));
+ }
+ else yvalidate(false);
+ }
+ if (oEsc && oProp)
+ {
+ SvxEscapementItem const item{*oEsc, *oProp, EE_CHAR_ESCAPEMENT};
+ rSet.Put(item);
+ }
+ else yvalidate(false);
+ break;
+ }
+ case EE_CHAR_PAIRKERNING:
+ {
+ yvalidate(rValue.tag == Y_JSON_BOOL);
+ SvxAutoKernItem const item{rValue.value.flag == Y_TRUE, nWhich};
+ rSet.Put(item);
+ break;
+ }
+ case EE_CHAR_KERNING:
+ {
+ int64_t const value{static_cast<int64_t>(YrsReadInt(rValue))};
+ yvalidate(SAL_MIN_INT16 <= value && value <= SAL_MAX_INT16);
+ SvxKerningItem const item{short(value), nWhich};
+ rSet.Put(item);
+ break;
+ }
+ case EE_CHAR_WLM:
+ {
+ yvalidate(rValue.tag == Y_JSON_BOOL);
+ SvxWordLineModeItem const item{rValue.value.flag == Y_TRUE, nWhich};
+ rSet.Put(item);
+ break;
+ }
+ case EE_CHAR_LANGUAGE:
+ case EE_CHAR_LANGUAGE_CJK:
+ case EE_CHAR_LANGUAGE_CTL:
+ {
+ auto const value{YrsReadInt(rValue)};
+ yvalidate(/*0 <= value && */value <= SAL_MAX_UINT16);
+ SvxLanguageItem const item{LanguageType(value), nWhich};
+ rSet.Put(item);
+ break;
+ }
+ case EE_CHAR_EMPHASISMARK:
+ {
+ auto const value{YrsReadInt(rValue)};
+ yvalidate((value & 0x300f) == value);
+ SvxEmphasisMarkItem const item{FontEmphasisMark(value), TypedWhichId<SvxEmphasisMarkItem>(nWhich)};
+ rSet.Put(item);
+ break;
+ }
+ case EE_CHAR_RELIEF:
+ {
+ auto const value{YrsReadInt(rValue)};
+ yvalidate(::std::underlying_type_t<FontRelief>(FontRelief::NONE) <= value && value <= ::std::underlying_type_t<FontRelief>(FontRelief::Engraved));
+ SvxCharReliefItem const item{FontRelief(value), nWhich};
+ rSet.Put(item);
+ break;
+ }
+ case EE_CHAR_CASEMAP:
+ {
+ auto const value{YrsReadInt(rValue)};
+ yvalidate(::std::underlying_type_t<SvxCaseMap>(SvxCaseMap::NotMapped) <= value && value < ::std::underlying_type_t<SvxCaseMap>(SvxCaseMap::End));
+ SvxCaseMapItem const item{SvxCaseMap(value), nWhich};
+ rSet.Put(item);
+ break;
+ }
+ case EE_PARA_WRITINGDIR:
+ {
+ auto const value{YrsReadInt(rValue)};
+ yvalidate(::std::underlying_type_t<SvxFrameDirection>(SvxFrameDirection::Horizontal_LR_TB) <= value && value < ::std::underlying_type_t<SvxFrameDirection>(SvxFrameDirection::Stacked));
+ SvxFrameDirectionItem const item{SvxFrameDirection(value), nWhich};
+ rSet.Put(item);
+ break;
+ }
+ case EE_PARA_HANGINGPUNCTUATION:
+ {
+ yvalidate(rValue.tag == Y_JSON_BOOL);
+ SvxHangingPunctuationItem const item{rValue.value.flag == Y_TRUE, nWhich};
+ rSet.Put(item);
+ break;
+ }
+ case EE_PARA_FORBIDDENRULES:
+ {
+ yvalidate(rValue.tag == Y_JSON_BOOL);
+ SvxForbiddenRuleItem const item{rValue.value.flag == Y_TRUE, nWhich};
+ rSet.Put(item);
+ break;
+ }
+ case EE_PARA_ASIANCJKSPACING:
+ {
+ yvalidate(rValue.tag == Y_JSON_BOOL);
+ SvxScriptSpaceItem const item{rValue.value.flag == Y_TRUE, nWhich};
+ rSet.Put(item);
+ break;
+ }
+ case EE_PARA_HYPHENATE:
+ case EE_PARA_HYPHENATE_NO_CAPS:
+ case EE_PARA_HYPHENATE_NO_LAST_WORD:
+ case EE_PARA_BULLETSTATE:
+ {
+ yvalidate(rValue.tag == Y_JSON_BOOL);
+ SfxBoolItem const item{nWhich, rValue.value.flag == Y_TRUE};
+ rSet.Put(item);
+ break;
+ }
+ case EE_PARA_OUTLLEVEL:
+ {
+ int64_t const value{static_cast<int64_t>(YrsReadInt(rValue))};
+ yvalidate(-1 <= value && value < SVX_MAX_NUM);
+ SfxInt16Item const item{nWhich, sal_Int16(value)};
+ rSet.Put(item);
+ break;
+ }
+ case EE_PARA_LRSPACE:
+ {
+ yvalidate(rValue.tag == Y_JSON_MAP);
+ ::std::optional<double> oFirstLineIndent;
+ ::std::optional<sal_Int16> oFirstLineIndentUnit;
+ ::std::optional<double> oLeft;
+ ::std::optional<sal_Int16> oLeftUnit;
+ ::std::optional<double> oRight;
+ ::std::optional<sal_Int16> oRightUnit;
+ ::std::optional<bool> oAutoFirst;
+ for (decltype(rValue.len) i = 0; i < rValue.len; ++i)
+ {
+ const char*const pEntry{rValue.value.map[i].key};
+ if (strcmp(pEntry, "first-line-offset") == 0)
+ {
+ yvalidate(rValue.value.map[i].value->tag == Y_JSON_NUM);
+ oFirstLineIndent.emplace(rValue.value.map[i].value->value.num);
+ }
+ else if (strcmp(pEntry, "first-line-offset-unit") == 0)
+ {
+ auto const value{YrsReadInt(*rValue.value.map[i].value)};
+ yvalidate(css::util::MeasureUnit::MM_100TH <= value && value <= css::util::MeasureUnit::FONT_CJK_ADVANCE);
+ oFirstLineIndentUnit.emplace(sal_Int16(value));
+ }
+ else if (strcmp(pEntry, "left-margin") == 0)
+ {
+ yvalidate(rValue.value.map[i].value->tag == Y_JSON_NUM);
+ oLeft.emplace(rValue.value.map[i].value->value.num);
+ }
+ else if (strcmp(pEntry, "left-margin-unit") == 0)
+ {
+ auto const value{YrsReadInt(*rValue.value.map[i].value)};
+ yvalidate(css::util::MeasureUnit::MM_100TH <= value && value <= css::util::MeasureUnit::FONT_CJK_ADVANCE);
+ oLeftUnit.emplace(sal_Int16(value));
+ }
+ else if (strcmp(pEntry, "right-margin") == 0)
+ {
+ yvalidate(rValue.value.map[i].value->tag == Y_JSON_NUM);
+ oRight.emplace(rValue.value.map[i].value->value.num);
+ }
+ else if (strcmp(pEntry, "right-margin-unit") == 0)
+ {
+ auto const value{YrsReadInt(*rValue.value.map[i].value)};
+ yvalidate(css::util::MeasureUnit::MM_100TH <= value && value <= css::util::MeasureUnit::FONT_CJK_ADVANCE);
+ oRightUnit.emplace(sal_Int16(value));
+ }
+ else if (strcmp(pEntry, "auto-first") == 0)
+ {
+ yvalidate(rValue.value.map[i].value->tag == Y_JSON_BOOL);
+ oAutoFirst.emplace(rValue.value.map[i].value->value.flag == Y_TRUE);
+ }
+ else yvalidate(false);
+ }
+ if (oFirstLineIndent && oFirstLineIndentUnit && oAutoFirst.has_value()
+ && oLeft && oLeftUnit && oRight && oRightUnit)
+ {
+ SvxLRSpaceItem item{{*oLeft, *oLeftUnit}, {*oRight, *oRightUnit}, {*oFirstLineIndent, *oFirstLineIndentUnit}, EE_PARA_LRSPACE};
+ item.SetAutoFirst(*oAutoFirst);
+ rSet.Put(item);
+ }
+ else yvalidate(false);
+ break;
+ }
+ case EE_PARA_ULSPACE:
+ {
+ yvalidate(rValue.tag == Y_JSON_MAP);
+ ::std::optional<sal_uInt16> oUpper;
+ ::std::optional<sal_uInt16> oLower;
+ for (decltype(rValue.len) i = 0; i < rValue.len; ++i)
+ {
+ const char*const pEntry{rValue.value.map[i].key};
+ if (strcmp(pEntry, "upper-margin") == 0)
+ {
+ auto const value{YrsReadInt(*rValue.value.map[i].value)};
+ yvalidate(/*0 <= value && */value <= SAL_MAX_UINT16);
+ oUpper.emplace(sal_uInt16(value));
+ }
+ else if (strcmp(pEntry, "lower-margin") == 0)
+ {
+ auto const value{YrsReadInt(*rValue.value.map[i].value)};
+ yvalidate(/*0 <= value && */value <= SAL_MAX_UINT16);
+ oLower.emplace(sal_uInt16(value));
+ }
+ else yvalidate(false);
+ }
+ if (oUpper && oLower)
+ {
+ SvxULSpaceItem const item{*oUpper, *oLower, EE_PARA_ULSPACE};
+ rSet.Put(item);
+ }
+ else yvalidate(false);
+ break;
+ }
+ case EE_PARA_SBL:
+ {
+ yvalidate(rValue.tag == Y_JSON_MAP);
+ ::std::optional<sal_uInt16> oLineSpaceFix;
+ ::std::optional<sal_uInt16> oLineSpaceMin;
+ ::std::optional<sal_uInt16> oInterLineSpaceProp;
+ ::std::optional<sal_Int16> oInterLineSpaceFix;
+ for (decltype(rValue.len) i = 0; i < rValue.len; ++i)
+ {
+ const char*const pEntry{rValue.value.map[i].key};
+ if (strcmp(pEntry, "line-space-fix") == 0)
+ {
+ auto const value{YrsReadInt(*rValue.value.map[i].value)};
+ yvalidate(/*0 <= value && */value <= SAL_MAX_UINT16);
+ oLineSpaceFix.emplace(sal_uInt16(value));
+ }
+ else if (strcmp(pEntry, "line-space-min") == 0)
+ {
+ auto const value{YrsReadInt(*rValue.value.map[i].value)};
+ yvalidate(/*0 <= value && */value <= SAL_MAX_UINT16);
+ oLineSpaceMin.emplace(sal_uInt16(value));
+ }
+ else if (strcmp(pEntry, "inter-line-space-prop") == 0)
+ {
+ auto const value{YrsReadInt(*rValue.value.map[i].value)};
+ yvalidate(/*0 <= value && */value <= SAL_MAX_UINT16);
+ oInterLineSpaceProp.emplace(sal_uInt16(value));
+ }
+ else if (strcmp(pEntry, "inter-line-space-fix") == 0)
+ {
+ auto const value{YrsReadInt(*rValue.value.map[i].value)};
+ yvalidate(/*0 <= value && */value <= SAL_MAX_UINT16);
+ oInterLineSpaceFix.emplace(sal_Int16(value));
+ }
+ else yvalidate(false);
+ }
+ SvxLineSpacingItem item{0, EE_PARA_SBL};
+ if (oLineSpaceFix)
+ {
+ item.SetLineHeight(*oLineSpaceFix);
+ item.SetLineSpaceRule(SvxLineSpaceRule::Fix);
+ rSet.Put(item);
+ }
+ else if (oLineSpaceMin)
+ {
+ item.SetLineHeight(*oLineSpaceMin);
+ rSet.Put(item);
+ }
+ else if (oInterLineSpaceProp)
+ {
+ item.SetPropLineSpace(*oInterLineSpaceProp);
+ rSet.Put(item);
+ }
+ else if (oInterLineSpaceFix)
+ {
+ item.SetInterLineSpace(*oInterLineSpaceFix);
+ rSet.Put(item);
+ }
+ else yvalidate(false);
+ break;
+ }
+ case EE_PARA_JUST:
+ {
+ auto const value{YrsReadInt(rValue)};
+ SvxAdjustItem item{SvxAdjust(), nWhich};
+ switch (value)
+ {
+ case uint64_t(SvxAdjust::Left):
+ case uint64_t(SvxAdjust::Right):
+ case uint64_t(SvxAdjust::Center):
+ item.SetAdjust(SvxAdjust(value));
+ break;
+ case uint64_t(SvxAdjust::Block):
+ item.SetAdjust(SvxAdjust(value));
+ item.SetLastBlock(SvxAdjust::Left);
+ break;
+ case 5:
+ item.SetAdjust(SvxAdjust::Block);
+ item.SetLastBlock(SvxAdjust::Center);
+ break;
+ case 6:
+ item.SetAdjust(SvxAdjust::Block);
+ item.SetLastBlock(SvxAdjust::Block);
+ item.SetOneWord(SvxAdjust::Left);
+ break;
+ case 7:
+ item.SetAdjust(SvxAdjust::Block);
+ item.SetLastBlock(SvxAdjust::Block);
+ item.SetOneWord(SvxAdjust::Block);
+ break;
+ default:
+ abort();
+ }
+ rSet.Put(item);
+ break;
+ }
+ case EE_PARA_TABS:
+ {
+ yvalidate(rValue.tag == Y_JSON_MAP);
+ ::std::optional<sal_Int32> oDefault;
+ ::std::optional<::std::vector<SvxTabStop>> oTabs;
+ for (decltype(rValue.len) i = 0; i < rValue.len; ++i)
+ {
+ const char*const pEntry{rValue.value.map[i].key};
+ if (strcmp(pEntry, "default-distance") == 0)
+ {
+ auto const value{YrsReadInt(*rValue.value.map[i].value)};
+ yvalidate(/*0 <= value && */value <= SAL_MAX_INT32);
+ oDefault.emplace(sal_Int32(value));
+ }
+ else if (strcmp(pEntry, "tab-stops") == 0)
+ {
+ oTabs.emplace();
+ yvalidate(rValue.value.map[i].value->tag == Y_JSON_ARR);
+ YOutput const& rArray{*rValue.value.map[i].value};
+ for (decltype(rArray.len) j = 0; j < rArray.len; ++j)
+ {
+ YOutput const& rMap{rArray.value.array[j]};
+ yvalidate(rMap.tag == Y_JSON_MAP);
+ ::std::optional<sal_Int32> oPos;
+ ::std::optional<SvxTabAdjust> oAdjust;
+ ::std::optional<sal_Unicode> oDecimal;
+ ::std::optional<sal_Unicode> oFill;
+ for (decltype(rMap.len) k = 0; k < rMap.len; ++k)
+ {
+ const char*const pE{rMap.value.map[k].key};
+ if (strcmp(pE, "pos") == 0)
+ {
+ auto const value{YrsReadInt(*rMap.value.map[k].value)};
+ yvalidate(/*0 <= value && */value <= SAL_MAX_INT32);
+ oPos.emplace(sal_Int32(value));
+ }
+ else if (strcmp(pE, "adjustment") == 0)
+ {
+ auto const value{YrsReadInt(*rMap.value.map[k].value)};
+ yvalidate(::std::underlying_type_t<SvxTabAdjust>(SvxTabAdjust::Left) <= value && value < ::std::underlying_type_t<SvxTabAdjust>(SvxTabAdjust::End));
+ oAdjust.emplace(SvxTabAdjust(value));
+ }
+ else if (strcmp(pE, "decimal") == 0)
+ {
+ auto const value{YrsReadInt(*rMap.value.map[k].value)};
+ yvalidate(/*0 <= value && */value < SAL_MAX_UINT16);
+ oDecimal.emplace(sal_Unicode(value));
+ }
+ else if (strcmp(pE, "fill") == 0)
+ {
+ auto const value{YrsReadInt(*rMap.value.map[k].value)};
+ yvalidate(/*0 <= value && */value < SAL_MAX_UINT16);
+ oFill.emplace(sal_Unicode(value));
+ }
+ else yvalidate(false);
+ }
+ if (oPos && oAdjust && oDecimal && oFill)
+ {
+ oTabs->emplace_back(*oPos, *oAdjust, *oDecimal, *oFill);
+ }
+ else yvalidate(false);
+ }
+ }
+ }
+ if (oDefault && oTabs)
+ {
+ SvxTabStopItem item{nWhich};
+ item.SetDefaultDistance(*oDefault);
+ for (SvxTabStop const& it : *oTabs)
+ {
+ item.Insert(it);
+ }
+ rSet.Put(item);
+ }
+ else yvalidate(false);
+ break;
+ }
+ case EE_PARA_JUST_METHOD:
+ {
+ auto const value{YrsReadInt(rValue)};
+ yvalidate(::std::underlying_type_t<SvxCellJustifyMethod>(SvxCellJustifyMethod::Auto) <= value && value <= ::std::underlying_type_t<SvxCellJustifyMethod>(SvxCellJustifyMethod::Distribute));
+ SvxJustifyMethodItem const item{SvxCellJustifyMethod(value), nWhich};
+ rSet.Put(item);
+ break;
+ }
+ case EE_PARA_VER_JUST:
+ {
+ auto const value{YrsReadInt(rValue)};
+ yvalidate(::std::underlying_type_t<SvxCellVerJustify>(SvxCellVerJustify::Standard) <= value && value <= ::std::underlying_type_t<SvxCellVerJustify>(SvxCellVerJustify::Block));
+ SvxVerJustifyItem const item{SvxCellVerJustify(value), nWhich};
+ rSet.Put(item);
+ break;
+ }
+
+ default:
+ assert(false);
+ }
+}
+
+// TODO: this could be a lot simpler if feature were a nested json-map like attr
+template<typename T> void
+YrsImplInsertFeature(ImpEditEngine & rIEE,
+ EditPaM const& rPam,
+ T const*const elements, uint32_t const len,
+ ::std::function<::std::pair<char const*, YOutput const&>(T const&)> pGetAttr)
+{
+ ::std::optional<OUString> oFeature;
+ ::std::optional<SvxURLFormat> oFormat;
+ ::std::optional<OUString> oURL;
+ ::std::optional<OUString> oRepresentation;
+ ::std::optional<OUString> oTargetFrame;
+ for (::std::remove_const_t<decltype(len)> k = 0; k < len; ++k)
+ {
+ T const& rAttr{elements[k]};
+ auto [pKey, rValue]{pGetAttr(rAttr)};
+ if (strcmp(pKey, "feature") == 0)
+ {
+ yvalidate(rValue.tag == Y_JSON_STR);
+ oFeature.emplace(OStringToOUString(rValue.value.str, RTL_TEXTENCODING_UTF8));
+ }
+ else if (strcmp(pKey, "url-format") == 0)
+ {
+ uint64_t const value{YrsReadInt(rValue)};
+ switch (value)
+ {
+ case ::std::underlying_type_t<SvxURLFormat>(SvxURLFormat::AppDefault):
+ case ::std::underlying_type_t<SvxURLFormat>(SvxURLFormat::Url):
+ case ::std::underlying_type_t<SvxURLFormat>(SvxURLFormat::Repr):
+ oFormat.emplace(SvxURLFormat(value));
+ break;
+ default:
+ yvalidate(false);
+ }
+ }
+ else if (strcmp(pKey, "url-url") == 0)
+ {
+ yvalidate(rValue.tag == Y_JSON_STR);
+ oURL.emplace(OStringToOUString(rValue.value.str, RTL_TEXTENCODING_UTF8));
+ }
+ else if (strcmp(pKey, "url-representation") == 0)
+ {
+ yvalidate(rValue.tag == Y_JSON_STR);
+ oRepresentation.emplace(OStringToOUString(rValue.value.str, RTL_TEXTENCODING_UTF8));
+ }
+ else if (strcmp(pKey, "url-targetframe") == 0)
+ {
+ yvalidate(rValue.tag == Y_JSON_STR);
+ oTargetFrame.emplace(OStringToOUString(rValue.value.str, RTL_TEXTENCODING_UTF8));
+ }
+ }
+ if (oFeature && *oFeature == "tab")
+ {
+ EditSelection const sel{rPam};
+ rIEE.InsertTab(sel);
+ }
+ else if (oFeature && *oFeature == "line")
+ {
+ EditSelection const sel{rPam};
+ rIEE.InsertLineBreak(sel);
+ }
+ else if (oFeature && *oFeature == "url")
+ {
+ if (oFormat && oURL && oRepresentation)
+ {
+ SvxURLField field{*oURL, *oRepresentation, *oFormat};
+ if (oTargetFrame)
+ {
+ field.SetTargetFrame(*oTargetFrame);
+ }
+ SvxFieldItem const item{field, EE_FEATURE_FIELD};
+ EditSelection const sel{rPam};
+ rIEE.InsertField(sel, item);
+ }
+ else yvalidate(false);
+ }
+ else
+ {
+ yvalidate(false);
+ }
+}
+
+} // namespace
+
+void EditDoc::YrsSetStyle(sal_Int32 const index, ::std::u16string_view const rStyle)
+{
+ ::YrsSetStyle(m_pYrsSupplier, m_CommentId, *this, index, rStyle);
+}
+
+void EditDoc::YrsSetParaAttr(sal_Int32 const index, SfxPoolItem const& rItem)
+{
+ ::YrsSetParaAttr(m_pYrsSupplier, m_CommentId, *this, index, rItem);
+}
+
+void EditDoc::YrsWriteEEState()
+{
+ assert(m_pYrsSupplier);
+
+ YrsWrite const yw{GetYrsWrite(m_pYrsSupplier, m_CommentId)};
+ assert(yw.pTxn && yw.pProps && yw.pText);
+
+ YInput const vertical{yinput_bool(mbIsVertical ? Y_TRUE : Y_FALSE)};
+ ymap_insert(yw.pProps, yw.pTxn, "is-vertical", &vertical);
+ YInput const rotation{yinput_long(static_cast<int64_t>(mnRotation))};
+ ymap_insert(yw.pProps, yw.pTxn, "rotation", &rotation);
+ YInput const deftab{yinput_long(static_cast<int64_t>(mnDefTab))};
+ ymap_insert(yw.pProps, yw.pTxn, "def-tab", &deftab);
+
+ for (::std::unique_ptr<ContentNode> const& rpNode : maContents)
+ {
+ auto const start{ytext_len(yw.pText, yw.pTxn)};
+ sal_Int32 pos{0};
+ for (sal_Int32 i = rpNode->GetString().indexOf(CH_FEATURE, pos);
+ i != -1; i = rpNode->GetString().indexOf(CH_FEATURE, pos))
+ {
+ if (i != pos)
+ {
+ OString const content{OUStringToOString(rpNode->GetString().subView(pos, i - pos), RTL_TEXTENCODING_UTF8)};
+ ytext_insert(yw.pText, yw.pTxn, start + pos, content.getStr(), nullptr);
+ }
+ EditCharAttrib const*const pAttrib{rpNode->GetCharAttribs().FindFeature(i)};
+ assert(pAttrib);
+ // this will insert the CH_FEATURE too
+ YrsInsertFeature(m_pYrsSupplier, m_CommentId, *this, GetPos(rpNode.get()), pAttrib);
+ pos = i+1;
+ }
+ if (pos != rpNode->GetString().getLength())
+ {
+ OString const content{OUStringToOString(rpNode->GetString().subView(pos, rpNode->GetString().getLength() - pos), RTL_TEXTENCODING_UTF8)};
+ ytext_insert(yw.pText, yw.pTxn, start + pos, content.getStr(), nullptr);
+ }
+ for (::std::unique_ptr<EditCharAttrib> const& rpAttr : rpNode->GetCharAttribs().GetAttribs())
+ {
+ if (!rpAttr->IsFeature())
+ {
+ YrsInsertAttribImpl(yw, start, rpAttr.get());
+ }
+ }
+ char const para[]{ CH_PARA, '\0' };
+ ContentAttribs const& rParaAttribs{rpNode->GetContentAttribs()};
+ OString const styleName{OUStringToOString(rpNode->GetStyleSheet()->GetName(), RTL_TEXTENCODING_UTF8)};
+ YInput const style{yinput_string(styleName.getStr())};
+ YInput attrArray[]{ style };
+ char const*const attrNames[]{ "para-style" };
+ YInput const attrs{yinput_json_map(const_cast<char**>(attrNames), attrArray, 1)};
+ auto const end{ytext_len(yw.pText, yw.pTxn)};
+ ytext_insert(yw.pText, yw.pTxn, end, para, &attrs);
+ for (SfxItemIter it{rParaAttribs.GetItems()}; !it.IsAtEnd(); it.NextItem())
+ {
+ YrsInsertAttribImplImpl(yw, *it.GetCurItem(), end, 1);
+ }
+ }
+}
+
+void EditDoc::YrsReadEEState(YTransaction *const pTxn, ImpEditEngine & rIEE)
+{
+ assert(m_pYrsSupplier);
+ YrsWrite const yw{GetYrsWrite(m_pYrsSupplier, m_CommentId, pTxn)};
+ assert(yw.pTxn && yw.pProps && yw.pText);
+
+ ::std::unique_ptr<YOutput, YOutputDeleter> const pVertical{ymap_get(yw.pProps, yw.pTxn, "is-vertical")};
+ yvalidate(!pVertical || pVertical->tag == Y_JSON_BOOL);
+ if (pVertical && pVertical->tag == Y_JSON_BOOL)
+ {
+ SetVertical(pVertical->value.flag == Y_TRUE);
+ }
+ ::std::unique_ptr<YOutput, YOutputDeleter> const pRotation{ymap_get(yw.pProps, yw.pTxn, "rotation")};
+ yvalidate(pRotation);
+ {
+ auto const value{YrsReadInt(*pRotation)};
+ switch (value)
+ {
+ case int64_t(TextRotation::NONE):
+ case int64_t(TextRotation::TOPTOBOTTOM):
+ case int64_t(TextRotation::BOTTOMTOTOP):
+ SetRotation(TextRotation(value));
+ break;
+ default:
+ yvalidate(false);
+ }
+ }
+ ::std::unique_ptr<YOutput, YOutputDeleter> const pDefTab{ymap_get(yw.pProps, yw.pTxn, "def-tab")};
+ yvalidate(pDefTab);
+ {
+ auto const value{YrsReadInt(*pDefTab)};
+ yvalidate(/*0 <= value && */value <= SAL_MAX_INT16);
+ SetDefTab(value);
+ }
+
+ assert(maContents.size() == 1);
+#if 0
+ // there is a getImpl().GetEditSelection() too, who knows what else... try to reuse existing node?
+ rIEE.GetParaPortions().Reset();
+ maContents.clear();
+ // the last paragraph cannot be removed, as cursors and a11y need it
+ rIEE.RemoveParagraph(0); // remove pre-existing one from InitDoc()
+#endif
+ uint32_t chunks{0};
+ YChunk *const pChunks{ytext_chunks(yw.pText, yw.pTxn, &chunks)};
+ sal_Int32 nodes{0};
+ ContentNode * pNode{nullptr};
+ for (decltype(chunks) i = 0; i < chunks; ++i)
+ {
+ decltype(nodes) const nodeStart{nodes};
+ sal_Int32 const posStart{pNode ? pNode->Len() : 0};
+
+ yvalidate(pChunks[i].data.tag == Y_JSON_STR);
+ OString const str(pChunks[i].data.value.str);
+ sal_Int32 strStart{0};
+
+ for (sal_Int32 j = 0; j < str.getLength(); ++j)
+ {
+ if (!pNode)
+ {
+ //pNode = new ContentNode(GetItemPool());
+ //rIEE.InsertContent(pNode, nodes); // does not set DefFont?
+ pNode = rIEE.ImpFastInsertParagraph(nodes).GetNode();
+// rIEE.GetParaPortions().Insert(nodes, ::std::make_unique<ParaPortion>(pNode));
+//does not call ParagraphInserted so no a11y event Insert(nodes, ::std::unique_ptr<ContentNode>(pNode));
+ // calling UpdateSelections() is pointless, it only handles deletes
+ }
+ if (str[j] == CH_PARA)
+ {
+ if (j != 0)
+ {
+ OString const portion{str.copy(strStart, j - strStart)};
+ pNode->Append(OStringToOUString(portion, RTL_TEXTENCODING_UTF8));
+ }
+ for (decltype(pChunks[i].fmt_len) k = 0; k < pChunks[i].fmt_len; ++k)
+ {
+ if (strcmp(pChunks[i].fmt[k].key, "para-style") == 0)
+ {
+ yvalidate(pChunks[i].fmt[k].value->tag == Y_JSON_STR);
+ OUString const style{OStringToOUString(pChunks[i].fmt[k].value->value.str, RTL_TEXTENCODING_UTF8)};
+ SfxStyleSheet *const pStyle{dynamic_cast<SfxStyleSheet *>(
+ rIEE.GetStyleSheetPool()->Find(style, SfxStyleFamily::Para))};
+ if (!pStyle) { abort(); }
+ pNode->SetStyleSheet(pStyle);
+ }
+ }
+ pNode = nullptr;
+ ++nodes;
+ strStart = j+1;
+ }
+ else if (str[j] == CH_FEATURE)
+ {
+ if (j != strStart)
+ {
+ OString const portion{str.copy(strStart, j - strStart)};
+ pNode->Append(OStringToOUString(portion, RTL_TEXTENCODING_UTF8));
+ }
+ auto GetAttr = [](YMapEntry const& rEntry) -> ::std::pair<char const*, YOutput const&>
+ { return {rEntry.key, *rEntry.value}; };
+ EditPaM const pam{pNode, pNode->Len()};
+ YrsImplInsertFeature<YMapEntry>(rIEE, pam, pChunks[i].fmt, pChunks[i].fmt_len, GetAttr);
+ strStart = j+1;
+ }
+ }
+ if (strStart != str.getLength())
+ {
+ OString const portion{str.copy(strStart, str.getLength() - strStart)};
+ pNode->Append(OStringToOUString(portion, RTL_TEXTENCODING_UTF8));
+ }
+ assert((pNode == nullptr) == (str[str.getLength()-1] == CH_PARA));
+ ContentNode *const pEndNode{pNode ? pNode : maContents[nodes-1].get()};
+ EditSelection const sel{EditPaM(maContents[nodeStart].get(), posStart), EditPaM(pEndNode, pEndNode->Len())};
+ SfxItemSet set{rIEE.GetEmptyItemSet()};
+ for (decltype(pChunks[i].fmt_len) j = 0; j < pChunks[i].fmt_len; ++j)
+ {
+ YrsImplInsertAttr(set, nullptr, pChunks[i].fmt[j].key, *pChunks[i].fmt[j].value);
+ }
+ if (set.Count())
+ {
+ rIEE.SetAttribs(sel, set);
+ }
+ }
+ ychunks_destroy(pChunks, chunks);
+ rIEE.RemoveParagraph(nodes); // remove pre-existing one from InitDoc()
+}
+
+static void YrsAdjustCursors(ImpEditEngine & rIEE, EditDoc & rDoc,
+ sal_Int32 const node, sal_Int32 const pos, ContentNode *const pNewNode, sal_Int32 const delta)
+{
+ for (EditView *const pView : rIEE.GetEditViews())
+ {
+ bool bSet{false};
+ EditSelection sel{pView->getImpl().GetEditSelection()};
+ ContentNode const*const pNode{rDoc.GetObject(node)};
+ if (sel.Min().GetNode() == pNode
+ && pos <= sel.Min().GetIndex())
+ {
+ sel.Min().SetNode(pNewNode);
+ sel.Min().SetIndex(sel.Min().GetIndex() + delta);
+ bSet = true;
+ }
+ if (sel.Max().GetNode() == pNode
+ && pos <= sel.Max().GetIndex())
+ {
+ sel.Max().SetNode(pNewNode);
+ sel.Max().SetIndex(sel.Max().GetIndex() + delta);
+ bSet = true;
+ }
+ if (bSet)
+ {
+ pView->getImpl().SetEditSelection(sel);
+ }
+ }
+}
+
+// TODO test this
+static void YrsAdjustCursorsDel(ImpEditEngine & rIEE, EditDoc & rDoc,
+ sal_Int32 const startNode, sal_Int32 const startPos,
+ sal_Int32 const endNode, sal_Int32 const endPos)
+{
+ for (EditView *const pView : rIEE.GetEditViews())
+ {
+ bool bSet{false};
+ EditSelection sel{pView->getImpl().GetEditSelection()};
+ ContentNode *const pStartNode{rDoc.GetObject(startNode)};
+ ContentNode const*const pEndNode{rDoc.GetObject(endNode)};
+ if ((sel.Min().GetNode() == pStartNode && startPos < sel.Min().GetIndex())
+ || (startNode < rDoc.GetPos(sel.Min().GetNode()) && rDoc.GetPos(sel.Min().GetNode()) < endNode)
+ || (sel.Min().GetNode() == pEndNode && sel.Min().GetIndex() < endPos))
+ {
+ sel.Min().SetNode(pStartNode);
+ sel.Min().SetIndex(startPos);
+ bSet = true;
+ }
+ else if (sel.Min().GetNode() == pEndNode)
+ {
+ sel.Min().SetNode(pStartNode);
+ sel.Min().SetIndex(startPos + sel.Min().GetIndex() - endPos);
+ bSet = true;
+ }
+ if ((sel.Max().GetNode() == pStartNode && startPos < sel.Max().GetIndex())
+ || (startNode < rDoc.GetPos(sel.Max().GetNode()) && rDoc.GetPos(sel.Max().GetNode()) < endNode)
+ || (sel.Max().GetNode() == pEndNode && sel.Max().GetIndex() < endPos))
+ {
+ sel.Max().SetNode(pStartNode);
+ sel.Max().SetIndex(startPos);
+ bSet = true;
+ }
+ else if (sel.Max().GetNode() == pEndNode)
+ {
+ sel.Max().SetNode(pStartNode);
+ sel.Max().SetIndex(startPos + sel.Max().GetIndex() - endPos);
+ bSet = true;
+ }
+ if (bSet)
+ {
+ pView->getImpl().SetEditSelection(sel);
+ }
+ }
+}
+
+void EditDoc::YrsApplyEEDelta(YTransaction *const /*pTxn*/, YTextEvent const*const pEvent, ImpEditEngine & rIEE)
+{
+ uint32_t lenC{0};
+ YDeltaOut *const pChange{ytext_event_delta(pEvent, &lenC)};
+
+ sal_Int32 node{0};
+ sal_Int32 pos{0};
+
+ for (decltype(lenC) i = 0; i < lenC; ++i)
+ {
+ switch (pChange[i].tag)
+ {
+ case Y_EVENT_CHANGE_ADD:
+ {
+ decltype(node) const nodeStart{node};
+ decltype(pos) const posStart{pos};
+
+ SfxStyleSheet * pStyle{nullptr};
+ SfxItemSet set{rIEE.GetEmptyItemSet()};
+ for (decltype(pChange[i].attributes_len) j = 0; j < pChange[i].attributes_len; ++j)
+ {
+ YrsImplInsertAttr(set, nullptr, pChange[i].attributes[j].key, pChange[i].attributes[j].value);
+ if (strcmp(pChange[i].attributes[j].key, "para-style") == 0)
+ {
+ yvalidate(pChange[i].attributes[j].value.tag == Y_JSON_STR);
+ OUString const style{OStringToOUString(pChange[i].attributes[j].value.value.str, RTL_TEXTENCODING_UTF8)};
+ pStyle = dynamic_cast<SfxStyleSheet *>(
+ rIEE.GetStyleSheetPool()->Find(style, SfxStyleFamily::Para));
+ if (!pStyle) { abort(); }
+ }
+ }
+
+ for (decltype(pChange[i].len) j = 0; j < pChange[i].len; ++j)
+ {
+ switch (pChange[i].insert[j].tag)
+ {
+ case Y_JSON_STR:
+ {
+ OUString const str{OStringToOUString(::std::string_view(pChange[i].insert[j].value.str), RTL_TEXTENCODING_UTF8)};
+ if (str.getLength() == 1 && str[0] == CH_FEATURE)
+ {
+ auto GetAttr = [](YDeltaAttr const& rEntry) -> ::std::pair<char const*, YOutput const&>
+ { return {rEntry.key, rEntry.value}; };
+ EditPaM const pam{maContents[node].get(), pos};
+ YrsImplInsertFeature<YDeltaAttr>(rIEE, pam, pChange[i].attributes, pChange[i].attributes_len, GetAttr);
+ YrsAdjustCursors(rIEE, *this, node, pos, maContents[node].get(), 1);
+ ++pos;
+ break;
+ }
+ sal_Int32 index{0};
+ sal_Int32 iPara{str.indexOf(CH_PARA)};
+ if (iPara != -1)
+ {
+ if (index != iPara)
+ {
+ EditSelection const sel{EditPaM{maContents[node].get(), pos}};
+ rIEE.InsertText(sel, str.copy(index, iPara));
+ }
+ EditPaM const newPos{rIEE.SplitContent(node, pos + iPara)};
+ rIEE.SetStyleSheet(node, pStyle);
+ YrsAdjustCursors(rIEE, *this, node, pos, const_cast<ContentNode*>(newPos.GetNode()), -pos);
+ index = iPara + 1;
+ pos = 0;
+ ++node;
+ iPara = str.indexOf(CH_PARA, index);
+ }
+ while (iPara != -1)
+ {
+// EditSelection const sel{EditPaM{maContents[node].get(), pos}};
+// rIEE.InsertText(sel, str);
+ rIEE.InsertParagraph(node, str.copy(index, iPara - index));
+ rIEE.SetStyleSheet(node, pStyle);
+ index = iPara + 1;
+ ++node;
+ iPara = str.indexOf(CH_PARA, index);
+ }
+ assert(iPara == -1);
+ if (index != str.getLength())
+ {
+ EditSelection const sel{EditPaM{maContents[node].get(), pos}};
+ rIEE.InsertText(sel, str.copy(index, str.getLength() - index));
+ YrsAdjustCursors(rIEE, *this, node, pos, maContents[node].get(), str.getLength() - index);
+ pos += str.getLength() - index;
+ }
+ break;
+ }
+ default:
+ assert(false);
+ }
+ }
+
+ EditPaM const start{maContents[nodeStart].get(), posStart};
+ EditPaM const end{maContents[node].get(), pos};
+ EditSelection const sel{start, end};
+ if (set.Count())
+ {
+ rIEE.SetAttribs(sel, set);
+ }
+ }
+ break;
+ case Y_EVENT_CHANGE_DELETE:
+ case Y_EVENT_CHANGE_RETAIN:
+ {
+ decltype(node) const nodeStart{node};
+ decltype(pos) const posStart{pos};
+
+ // len should be UTF16 via Y_OFFSET_UTF16
+ sal_Int32 len{static_cast<sal_Int32>(pChange[i].len)};
+ while (0 < len)
+ {
+ yvalidate(o3tl::make_unsigned(node) < maContents.size());
+ ContentNode & rNode{*maContents[node]};
+ if (pos + len <= rNode.Len())
+ {
+ pos += len;
+ len = 0;
+ }
+ else
+ {
+ len -= rNode.Len() - pos + 1;
+ pos = 0;
+ ++node;
+ }
+ }
+ ::std::optional<EditSelection> oSel;
+ // setting attribute on node special case - there may be
+ // EE_PARA_* obviously and possibly also EE_CHAR_* but
+ // unclear how those would be inserted - and a "para-style"
+ if (pos == 0 && pChange[i].len == 1 && pChange[i].tag == Y_EVENT_CHANGE_RETAIN)
+ {
+ assert(o3tl::make_unsigned(node) <= maContents.size());
+ EditPaM const start{maContents[node - 1].get(), 0};
+ oSel.emplace(start);
+ }
+ else
+ {
+ if (pos == 0 && static_cast<size_t>(node) == maContents.size())
+ { // adjust past-the-end positon (formatting change)
+ --node;
+ pos = maContents[node]->Len();
+ }
+ assert(o3tl::make_unsigned(node) < maContents.size() && pos <= maContents[node]->Len());
+ EditPaM const start{maContents[nodeStart].get(), posStart};
+ EditPaM const end{maContents[node].get(), pos};
+ oSel.emplace(start, end);
+ }
+ if (pChange[i].tag == Y_EVENT_CHANGE_DELETE)
+ {
+ YrsAdjustCursorsDel(rIEE, *this, nodeStart, posStart, node, pos);
+ rIEE.DeleteSelected(*oSel);
+ node = nodeStart;
+ pos = posStart;
+ }
+ else if (0 < pChange[i].attributes_len)
+ {
+ assert(pChange[i].tag == Y_EVENT_CHANGE_RETAIN);
+ SfxItemSet set{rIEE.GetEmptyItemSet()};
+ ::std::vector<sal_uInt16> removed;
+ for (decltype(pChange[i].attributes_len) j = 0; j < pChange[i].attributes_len; ++j)
+ {
+ if (pos == 0 && pChange[i].len == 1
+ && strcmp(pChange[i].attributes[j].key, "para-style") == 0)
+ {
+ yvalidate(pChange[i].attributes[j].value.tag == Y_JSON_STR);
+ OUString const style{OStringToOUString(pChange[i].attributes[j].value.value.str, RTL_TEXTENCODING_UTF8)};
+ SfxStyleSheet *const pStyle{dynamic_cast<SfxStyleSheet *>(
+ rIEE.GetStyleSheetPool()->Find(style, SfxStyleFamily::Para))};
+ if (!pStyle) { abort(); }
+ rIEE.SetStyleSheet(node - 1, pStyle);
+ }
+ else
+ {
+ YrsImplInsertAttr(set, &removed, pChange[i].attributes[j].key, pChange[i].attributes[j].value);
+ }
+ }
+ if (set.Count())
+ {
+ rIEE.SetAttribs(*oSel, set);
+ }
+ for (auto const nWhich : removed)
+ {
+ rIEE.RemoveCharAttribs(*oSel, EERemoveParaAttribsMode::RemoveAll, nWhich);
+ }
+ }
+ }
+ break;
+ default:
+ assert(false);
+ }
+ }
+
+ ytext_delta_destroy(pChange, lenC);
+}
+
+void EditDoc::SetYrsCommentId(IYrsTransactionSupplier *const pYrsSupplier, OString const& rId)
+{
+ assert(!m_pYrsSupplier);
+ m_pYrsSupplier = pYrsSupplier;
+ m_CommentId = rId;
+}
+
+OString EditDoc::GetYrsCommentId() const
+{
+ return m_CommentId;
+}
+
+void EditView::SetYrsCommentId(IYrsTransactionSupplier *const pYrsSupplier, OString const& rId)
+{
+ getEditEngine().GetEditDoc().SetYrsCommentId(pYrsSupplier, rId);
+}
+
+OString EditView::GetYrsCommentId() const
+{
+ return getEditEngine().GetEditDoc().GetYrsCommentId();
+}
+
+void EditView::YrsWriteEEState()
+{
+ return getEditEngine().GetEditDoc().YrsWriteEEState();
+}
+
+void EditView::YrsReadEEState(YTransaction *const pTxn)
+{
+ return getEditEngine().GetEditDoc().YrsReadEEState(pTxn, getImpEditEngine());
+}
+
+void EditView::YrsApplyEEDelta(YTransaction *const pTxn, YTextEvent const*const pEvent)
+{
+ return getEditEngine().GetEditDoc().YrsApplyEEDelta(pTxn, pEvent, getImpEditEngine());
+}
+#endif
+
EditDoc::EditDoc( SfxItemPool* pPool ) :
mnLastCache(0),
mpItemPool(pPool ? pPool : new EditEngineItemPool()),
@@ -724,14 +3029,46 @@ EditDoc::EditDoc( SfxItemPool* pPool ) :
mbModified(false),
mbDisableAttributeExpanding(false)
{
+#if defined(YRS)
+ SAL_DEBUG("YRS +EditDoc");
+#endif
// Don't create an empty node, Clear() will be called in EditEngine-CTOR
};
EditDoc::~EditDoc()
{
+#if defined(YRS)
+ SAL_DEBUG("YRS -EditDoc");
+#endif
maContents.clear();
}
+// not sure which of the members make sense to sync - if its only a cache for
+// some value elsewhere in the model then probably not?
+void EditDoc::SetVertical(bool const bVertical)
+{
+ mbIsVertical = bVertical;
+#if defined(YRS)
+ YrsSetVertical(m_pYrsSupplier, m_CommentId, mbIsVertical);
+#endif
+}
+
+void EditDoc::SetRotation(TextRotation const nRotation)
+{
+ mnRotation = nRotation;
+#if defined(YRS)
+ YrsSetRotation(m_pYrsSupplier, m_CommentId, mnRotation);
+#endif
+}
+
+void EditDoc::SetDefTab(sal_uInt16 const nTab)
+{
+ mnDefTab = nTab ? nTab : DEFTAB;
+#if defined(YRS)
+ YrsSetDefTab(m_pYrsSupplier, m_CommentId, mnDefTab);
+#endif
+}
+
void CreateFont( SvxFont& rFont, const SfxItemSet& rSet, bool bSearchInParent, SvtScriptType nScriptType )
{
vcl::Font aPrevFont( rFont );
@@ -813,6 +3150,7 @@ void CreateFont( SvxFont& rFont, const SfxItemSet& rSet, bool bSearchInParent, S
void EditDoc::CreateDefFont( bool bUseStyles )
{
SfxItemSet aTmpSet(SfxItemSet::makeFixedSfxItemSet<EE_PARA_START, EE_CHAR_END>(GetItemPool()));
+ // maDefFont depends only on items and flags, no need to sync
CreateFont(maDefFont, aTmpSet);
maDefFont.SetVertical( IsEffectivelyVertical() );
maDefFont.SetOrientation( Degree10(IsEffectivelyVertical() ? (IsTopToBottom() ? 2700 : 900) : 0) );
@@ -865,6 +3203,9 @@ void EditDoc::Insert(sal_Int32 nPos, std::unique_ptr<ContentNode> pNode)
return;
}
maContents.insert(maContents.begin()+nPos, std::move(pNode));
+#if defined(YRS)
+ YrsAddPara(m_pYrsSupplier, m_CommentId, *this, nPos);
+#endif
}
void EditDoc::Remove(sal_Int32 nPos)
@@ -874,6 +3215,9 @@ void EditDoc::Remove(sal_Int32 nPos)
SAL_WARN( "editeng", "EditDoc::Remove - out of bounds pos " << nPos);
return;
}
+#if defined(YRS)
+ YrsRemovePara(m_pYrsSupplier, m_CommentId, *this, nPos);
+#endif
maContents.erase(maContents.begin() + nPos);
}
@@ -885,6 +3229,9 @@ std::unique_ptr<ContentNode> EditDoc::Release(sal_Int32 nPos)
return nullptr;
}
+#if defined(YRS)
+ YrsRemovePara(m_pYrsSupplier, m_CommentId, *this, nPos);
+#endif
std::unique_ptr<ContentNode> pNode = std::move(maContents[nPos]);
maContents.erase(maContents.begin() + nPos);
return pNode;
@@ -971,6 +3318,11 @@ EditPaM EditDoc::Clear()
{
maContents.clear();
+#if defined(YRS)
+ // Insert will call YrsAddPara()
+ YrsClear(m_pYrsSupplier, m_CommentId);
+#endif
+
ContentNode* pNode = new ContentNode(GetItemPool());
Insert(0, std::unique_ptr<ContentNode>(pNode));
@@ -1014,6 +3366,11 @@ EditPaM EditDoc::RemoveText()
maContents.clear();
+#if defined(YRS)
+ // Insert will call YrsAddPara()
+ YrsClear(m_pYrsSupplier, m_CommentId);
+#endif
+
ContentNode* pNode = new ContentNode(GetItemPool());
Insert(0, std::unique_ptr<ContentNode>(pNode));
@@ -1040,6 +3397,10 @@ EditPaM EditDoc::InsertText( EditPaM aPaM, const OUString& rStr )
SetModified( true );
+#if defined(YRS)
+ YrsInsertText(m_pYrsSupplier, m_CommentId, *this, GetPos(aPaM.GetNode()), aPaM.GetIndex() - rStr.getLength(), rStr);
+#endif
+
return aPaM;
}
@@ -1077,10 +3438,22 @@ EditPaM EditDoc::InsertParaBreak( EditPaM aPaM, bool bKeepEndingAttribs )
// Character attributes may need to be copied or trimmed:
pNode->CopyAndCutAttribs( aPaM.GetNode(), GetItemPool(), bKeepEndingAttribs );
+#if defined(YRS)
+ {
+ // skip the YrsAddPara in Insert
+ YrsReplayGuard const g{m_pYrsSupplier};
+#endif
Insert(nPos+1, std::unique_ptr<ContentNode>(pNode));
+#if defined(YRS)
+ }
+#endif
SetModified(true);
+#if defined(YRS)
+ YrsInsertParaBreak(m_pYrsSupplier, m_CommentId, *this, nPos, aPaM.GetIndex());
+#endif
+
aPaM.SetNode( pNode );
aPaM.SetIndex( 0 );
return aPaM;
@@ -1100,6 +3473,10 @@ EditPaM EditDoc::InsertFeature( EditPaM aPaM, const SfxPoolItem& rItem )
SetModified( true );
+#if defined(YRS)
+ YrsInsertFeature(m_pYrsSupplier, m_CommentId, *this, GetPos(aPaM.GetNode()), pAttrib);
+#endif
+
aPaM.SetIndex( aPaM.GetIndex() + 1 );
return aPaM;
}
@@ -1115,10 +3492,23 @@ EditPaM EditDoc::ConnectParagraphs( ContentNode* pLeft, ContentNode* pRight )
// the one to the right disappears.
sal_Int32 nRight = GetPos( pRight );
+#if defined(YRS)
+ {
+ // skip the YrsRemovePara in Remove, the node even still has the text...
+ YrsReplayGuard const g{m_pYrsSupplier};
+#endif
Remove( nRight );
+#if defined(YRS)
+ }
+#endif
SetModified(true);
+#if defined(YRS)
+ assert(nRight != 0);
+ YrsConnectPara(m_pYrsSupplier, m_CommentId, *this, nRight - 1, aPaM.GetIndex());
+#endif
+
return aPaM;
}
@@ -1129,6 +3519,10 @@ void EditDoc::RemoveChars( EditPaM aPaM, sal_Int32 nChars )
aPaM.GetNode()->CollapseAttribs( aPaM.GetIndex(), nChars );
SetModified( true );
+
+#if defined(YRS)
+ YrsRemoveChars(m_pYrsSupplier, m_CommentId, *this, GetPos(aPaM.GetNode()), aPaM.GetIndex(), nChars);
+#endif
}
void EditDoc::InsertAttribInSelection( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd, const SfxPoolItem& rPoolItem )
@@ -1162,11 +3556,24 @@ void EditDoc::InsertAttribInSelection( ContentNode* pNode, sal_Int32 nStart, sal
// Will become a large Attribute.
pEndingAttrib->GetEnd() = pStartingAttrib->GetEnd();
pNode->GetCharAttribs().Remove(pStartingAttrib);
+#if defined(YRS)
+ YrsInsertAttrib(m_pYrsSupplier, m_CommentId, *this, GetPos(pNode), pEndingAttrib);
+#endif
}
else if ( pStartingAttrib && ( *(pStartingAttrib->GetItem()) == rPoolItem ) )
+ {
pStartingAttrib->GetStart() = nStart;
+#if defined(YRS)
+ YrsInsertAttrib(m_pYrsSupplier, m_CommentId, *this, GetPos(pNode), pStartingAttrib);
+#endif
+ }
else if ( pEndingAttrib && ( *(pEndingAttrib->GetItem()) == rPoolItem ) )
+ {
pEndingAttrib->GetEnd() = nEnd;
+#if defined(YRS)
+ YrsInsertAttrib(m_pYrsSupplier, m_CommentId, *this, GetPos(pNode), pEndingAttrib);
+#endif
+ }
else
InsertAttrib( rPoolItem, pNode, nStart, nEnd );
@@ -1220,6 +3627,9 @@ bool EditDoc::RemoveAttribs( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEn
if ( pAttr->GetEnd() > nEnd )
{
bNeedsSorting = true;
+#if defined(YRS)
+ YrsRemoveAttrib(m_pYrsSupplier, m_CommentId, *this, GetPos(pNode), pAttr->Which(), pAttr->GetStart(), nEnd);
+#endif
pAttr->GetStart() = nEnd; // then it starts after this
rpStarting = pAttr;
if ( nWhich )
@@ -1238,6 +3648,9 @@ bool EditDoc::RemoveAttribs( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEn
bChanged = true;
if ( ( pAttr->GetStart() < nStart ) && !pAttr->IsFeature() )
{
+#if defined(YRS)
+ YrsRemoveAttrib(m_pYrsSupplier, m_CommentId, *this, GetPos(pNode), pAttr->Which(), nStart, pAttr->GetEnd());
+#endif
pAttr->GetEnd() = nStart; // then it ends here
rpEnding = pAttr;
}
@@ -1255,6 +3668,9 @@ bool EditDoc::RemoveAttribs( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEn
{
bNeedsSorting = true;
pAttr->GetStart() = nEnd;
+#if defined(YRS)
+ YrsRemoveAttrib(m_pYrsSupplier, m_CommentId, *this, GetPos(pNode), pAttr->Which(), nStart, nEnd);
+#endif
rpStarting = pAttr;
if ( nWhich )
break; // There can be further attributes!
@@ -1262,6 +3678,9 @@ bool EditDoc::RemoveAttribs( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEn
else if ( pAttr->GetEnd() == nEnd )
{
pAttr->GetEnd() = nStart;
+#if defined(YRS)
+ YrsRemoveAttrib(m_pYrsSupplier, m_CommentId, *this, GetPos(pNode), pAttr->Which(), nStart, nEnd);
+#endif
rpEnding = pAttr;
if ( nWhich )
break; // There can be further attributes!
@@ -1272,7 +3691,15 @@ bool EditDoc::RemoveAttribs( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEn
sal_Int32 nOldEnd = pAttr->GetEnd();
pAttr->GetEnd() = nStart;
rpEnding = pAttr;
+#if defined(YRS)
+ YrsRemoveAttrib(m_pYrsSupplier, m_CommentId, *this, GetPos(pNode), pAttr->Which(), nStart, nEnd);
+ {
+ YrsReplayGuard const g{m_pYrsSupplier};
+#endif
InsertAttrib( *pAttr->GetItem(), pNode, nEnd, nOldEnd );
+#if defined(YRS)
+ }
+#endif
if ( nWhich )
break; // There can be further attributes!
}
@@ -1280,6 +3707,9 @@ bool EditDoc::RemoveAttribs( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEn
}
if ( bRemoveAttrib )
{
+#if defined(YRS)
+ YrsRemoveAttrib(m_pYrsSupplier, m_CommentId, *this, GetPos(pNode), pAttr->Which(), pAttr->GetStart(), pAttr->GetEnd());
+#endif
DBG_ASSERT( ( pAttr != rpStarting ) && ( pAttr != rpEnding ), "Delete and retain the same attribute?" );
DBG_ASSERT( !pAttr->IsFeature(), "RemoveAttribs: Remove a feature?!" );
rAttribs.erase(rAttribs.begin()+nAttr);
@@ -1315,6 +3745,10 @@ void EditDoc::InsertAttrib( const SfxPoolItem& rPoolItem, ContentNode* pNode, sa
pNode->GetCharAttribs().InsertAttrib( pAttrib );
SetModified( true );
+
+#if defined(YRS)
+ YrsInsertAttrib(m_pYrsSupplier, m_CommentId, *this, GetPos(pNode), pAttrib);
+#endif
}
void EditDoc::InsertAttrib( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd, const SfxPoolItem& rPoolItem )
diff --git a/editeng/source/editeng/impedit.hxx b/editeng/source/editeng/impedit.hxx
index f725ffbb170b..de80aa861af7 100644
--- a/editeng/source/editeng/impedit.hxx
+++ b/editeng/source/editeng/impedit.hxx
@@ -524,6 +524,9 @@ public:
class ImpEditEngine : public SfxListener, public svl::StyleSheetUser
{
friend class EditEngine;
+#if defined(YRS)
+ friend class EditDoc;
+#endif
typedef EditEngine::ViewsType ViewsType;
diff --git a/editeng/source/editeng/impedit5.cxx b/editeng/source/editeng/impedit5.cxx
index cbbd830d23e4..31683f08825d 100644
--- a/editeng/source/editeng/impedit5.cxx
+++ b/editeng/source/editeng/impedit5.cxx
@@ -90,6 +90,9 @@ void ImpEditEngine::SetStyleSheet( sal_Int32 nPara, SfxStyleSheet* pStyle )
if ( pCurStyle )
EndListening( *pCurStyle );
pNode->SetStyleSheet( pStyle, maStatus.UseCharAttribs() );
+#if defined(YRS)
+ maEditDoc.YrsSetStyle(nPara, pStyle ? pStyle->GetName() : OUString());
+#endif
if ( pStyle )
StartListening(*pStyle, DuplicateHandling::Allow);
@@ -550,6 +553,9 @@ void ImpEditEngine::SetAttribs( EditSelection aSel, const SfxItemSet& rSet, SetA
{
pNode->GetContentAttribs().GetItems().Put( rItem );
bParaAttribFound = true;
+#if defined(YRS)
+ maEditDoc.YrsSetParaAttr(nNode, rItem);
+#endif
}
else
{