diff options
-rw-r--r-- | include/svx/rubydialog.hxx | 3 | ||||
-rw-r--r-- | svx/source/dialog/rubydialog.cxx | 76 | ||||
-rw-r--r-- | svx/uiconfig/ui/asianphoneticguidedialog.ui | 669 | ||||
-rw-r--r-- | sw/inc/doc.hxx | 2 | ||||
-rw-r--r-- | sw/inc/rubylist.hxx | 1 | ||||
-rw-r--r-- | sw/qa/core/uwriter.cxx | 295 | ||||
-rw-r--r-- | sw/source/core/doc/docruby.cxx | 217 |
7 files changed, 865 insertions, 398 deletions
diff --git a/include/svx/rubydialog.hxx b/include/svx/rubydialog.hxx index 655b4f2ae049..30b284ee0fbd 100644 --- a/include/svx/rubydialog.hxx +++ b/include/svx/rubydialog.hxx @@ -83,6 +83,8 @@ class SvxRubyDialog final : public SfxModelessDialogController std::unique_ptr<weld::ComboBox> m_xCharStyleLB; std::unique_ptr<weld::Button> m_xStylistPB; + std::unique_ptr<weld::Button> m_xSelectionGroupPB; + std::unique_ptr<weld::Button> m_xApplyPB; std::unique_ptr<weld::Button> m_xClosePB; @@ -92,6 +94,7 @@ class SvxRubyDialog final : public SfxModelessDialogController std::unique_ptr<RubyPreview> m_xPreviewWin; std::unique_ptr<weld::CustomWeld> m_xPreview; + DECL_LINK(SelectionGroup_Impl, weld::Button&, void); DECL_LINK(ApplyHdl_Impl, weld::Button&, void); DECL_LINK(CloseHdl_Impl, weld::Button&, void); DECL_LINK(StylistHdl_Impl, weld::Button&, void); diff --git a/svx/source/dialog/rubydialog.cxx b/svx/source/dialog/rubydialog.cxx index e28fdb71d49e..f85a395029b0 100644 --- a/svx/source/dialog/rubydialog.cxx +++ b/svx/source/dialog/rubydialog.cxx @@ -43,6 +43,7 @@ #include <vcl/event.hxx> #include <vcl/settings.hxx> #include <vcl/svapp.hxx> +#include <rtl/ustrbuf.hxx> #include <svl/itemset.hxx> using namespace css::uno; @@ -119,6 +120,70 @@ public: virtual void SAL_CALL selectionChanged(const css::lang::EventObject& aEvent) override; virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override; + + bool IsSelectionGrouped() { return aRubyValues.getLength() < 2; } + + void MakeSelectionGrouped() + { + if (aRubyValues.getLength() < 2) + { + return; + } + + OUString sBaseTmp; + OUStringBuffer aBaseString; + for (const PropertyValues& pVals : aRubyValues) + { + sBaseTmp.clear(); + for (const PropertyValue& pVal : pVals) + { + if (pVal.Name == cRubyBaseText) + { + pVal.Value >>= sBaseTmp; + } + } + + aBaseString.append(sBaseTmp); + } + + Sequence<PropertyValues> aNewRubyValues{ 1 }; + PropertyValues* pNewRubyValues = aNewRubyValues.getArray(); + + // Copy some reasonable style values from the previous ruby array + pNewRubyValues[0] = aRubyValues[0]; + for (const PropertyValues& pVals : aRubyValues) + { + for (const PropertyValue& pVal : pVals) + { + if (pVal.Name == cRubyText) + { + pVal.Value >>= sBaseTmp; + if (!sBaseTmp.isEmpty()) + { + pNewRubyValues[0] = pVals; + break; + } + } + } + } + + PropertyValue* pNewValues = pNewRubyValues[0].getArray(); + for (sal_Int32 i = 0; i < pNewRubyValues[0].getLength(); ++i) + { + if (pNewValues[i].Name == cRubyBaseText) + { + sBaseTmp = aBaseString; + pNewValues[i].Value <<= sBaseTmp; + } + else if (pNewValues[i].Name == cRubyText) + { + sBaseTmp.clear(); + pNewValues[i].Value <<= sBaseTmp; + } + } + + aRubyValues = std::move(aNewRubyValues); + } }; SvxRubyData_Impl::SvxRubyData_Impl() @@ -207,6 +272,7 @@ SvxRubyDialog::SvxRubyDialog(SfxBindings* pBind, SfxChildWindow* pCW, weld::Wind , m_xCharStyleFT(m_xBuilder->weld_label(u"styleft"_ustr)) , m_xCharStyleLB(m_xBuilder->weld_combo_box(u"stylelb"_ustr)) , m_xStylistPB(m_xBuilder->weld_button(u"styles"_ustr)) + , m_xSelectionGroupPB(m_xBuilder->weld_button(u"selection-group"_ustr)) , m_xApplyPB(m_xBuilder->weld_button(u"ok"_ustr)) , m_xClosePB(m_xBuilder->weld_button(u"close"_ustr)) , m_xContentArea(m_xDialog->weld_content_area()) @@ -228,6 +294,7 @@ SvxRubyDialog::SvxRubyDialog(SfxBindings* pBind, SfxChildWindow* pCW, weld::Wind aEditArr[6] = m_xLeft4ED.get(); aEditArr[7] = m_xRight4ED.get(); + m_xSelectionGroupPB->connect_clicked(LINK(this, SvxRubyDialog, SelectionGroup_Impl)); m_xApplyPB->connect_clicked(LINK(this, SvxRubyDialog, ApplyHdl_Impl)); m_xClosePB->connect_clicked(LINK(this, SvxRubyDialog, CloseHdl_Impl)); m_xStylistPB->connect_clicked(LINK(this, SvxRubyDialog, StylistHdl_Impl)); @@ -405,6 +472,9 @@ void SvxRubyDialog::GetRubyText() void SvxRubyDialog::Update() { + // Only enable selection grouping options when they can be applied + m_xSelectionGroupPB->set_sensitive(!m_pImpl->IsSelectionGrouped()); + const Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues(); sal_Int32 nLen = aRubyValues.getLength(); m_xScrolledWindow->vadjustment_configure(0, 0, !nLen ? 1 : nLen, 1, 4, 4); @@ -505,6 +575,12 @@ IMPL_LINK(SvxRubyDialog, ScrollHdl_Impl, weld::ScrolledWindow&, rScroll, void) m_xPreviewWin->Invalidate(); } +IMPL_LINK_NOARG(SvxRubyDialog, SelectionGroup_Impl, weld::Button&, void) +{ + m_pImpl->MakeSelectionGrouped(); + Update(); +} + IMPL_LINK_NOARG(SvxRubyDialog, ApplyHdl_Impl, weld::Button&, void) { const Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues(); diff --git a/svx/uiconfig/ui/asianphoneticguidedialog.ui b/svx/uiconfig/ui/asianphoneticguidedialog.ui index b70aee74127b..66a045d26ba7 100644 --- a/svx/uiconfig/ui/asianphoneticguidedialog.ui +++ b/svx/uiconfig/ui/asianphoneticguidedialog.ui @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.38.2 --> +<!-- Generated with glade 3.40.0 --> <interface domain="svx"> <requires lib="gtk+" version="3.20"/> <object class="GtkDialog" id="AsianPhoneticGuideDialog"> @@ -72,423 +72,468 @@ </packing> </child> <child> - <object class="GtkBox" id="box1"> + <!-- n-columns=2 n-rows=2 --> + <object class="GtkGrid" id="grid4"> <property name="visible">True</property> <property name="can-focus">False</property> - <property name="orientation">vertical</property> - <property name="spacing">6</property> + <property name="row-spacing">6</property> + <property name="column-spacing">6</property> <child> - <!-- n-columns=2 n-rows=1 --> - <object class="GtkGrid" id="grid3"> + <object class="GtkBox" id="box1"> <property name="visible">True</property> <property name="can-focus">False</property> - <property name="column-spacing">12</property> - <property name="column-homogeneous">True</property> - <child> - <object class="GtkLabel" id="basetextft"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="label" translatable="yes" context="asianphoneticguidedialog|basetextft">Base text</property> - <property name="use-underline">True</property> - </object> - <packing> - <property name="left-attach">0</property> - <property name="top-attach">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="rubytextft"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="label" translatable="yes" context="asianphoneticguidedialog|rubytextft">Ruby text</property> - <property name="use-underline">True</property> - </object> - <packing> - <property name="left-attach">1</property> - <property name="top-attach">0</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkScrolledWindow" id="scrolledwindow"> - <property name="visible">True</property> - <property name="can-focus">True</property> <property name="hexpand">True</property> - <property name="hscrollbar-policy">never</property> - <property name="vscrollbar-policy">always</property> - <property name="shadow-type">in</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> <child> - <object class="GtkViewport" id="viewport1"> + <object class="GtkScrolledWindow" id="scrolledwindow"> <property name="visible">True</property> - <property name="can-focus">False</property> + <property name="can-focus">True</property> + <property name="hexpand">True</property> + <property name="hscrollbar-policy">never</property> + <property name="vscrollbar-policy">always</property> + <property name="shadow-type">in</property> <child> - <!-- n-columns=1 n-rows=1 --> - <object class="GtkGrid" id="grid"> + <object class="GtkViewport" id="viewport1"> <property name="visible">True</property> <property name="can-focus">False</property> <child> - <!-- n-columns=2 n-rows=4 --> - <object class="GtkGrid" id="grid1"> + <!-- n-columns=1 n-rows=1 --> + <object class="GtkGrid" id="grid"> <property name="visible">True</property> <property name="can-focus">False</property> - <property name="hexpand">True</property> - <property name="border-width">6</property> - <property name="row-spacing">6</property> - <property name="column-spacing">12</property> <child> - <object class="GtkEntry" id="Left2ED"> + <!-- n-columns=2 n-rows=4 --> + <object class="GtkGrid" id="grid1"> <property name="visible">True</property> - <property name="can-focus">True</property> + <property name="can-focus">False</property> <property name="hexpand">True</property> - <property name="activates-default">True</property> - <property name="truncate-multiline">True</property> - <child internal-child="accessible"> - <object class="AtkObject" id="Left2ED-atkobject"> - <property name="AtkObject::accessible-name" translatable="yes" context="asianphoneticguidedialog|Left2ED-atkobject">Base text</property> - <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|Left2ED">Displays the base text that you selected in the current file. If you want, you can modify the base text by entering new text here.</property> + <property name="border-width">6</property> + <property name="row-spacing">6</property> + <property name="column-spacing">12</property> + <child> + <object class="GtkEntry" id="Left2ED"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="hexpand">True</property> + <property name="activates-default">True</property> + <property name="truncate-multiline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="Left2ED-atkobject"> + <property name="AtkObject::accessible-name" translatable="yes" context="asianphoneticguidedialog|Left2ED-atkobject">Base text</property> + <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|Left2ED">Displays the base text that you selected in the current file. If you want, you can modify the base text by entering new text here.</property> + </object> + </child> </object> + <packing> + <property name="left-attach">0</property> + <property name="top-attach">1</property> + </packing> </child> - </object> - <packing> - <property name="left-attach">0</property> - <property name="top-attach">1</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="Left1ED"> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="activates-default">True</property> - <property name="truncate-multiline">True</property> - <child internal-child="accessible"> - <object class="AtkObject" id="Left1ED-atkobject"> - <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|Left1ED">Displays the base text that you selected in the current file. If you want, you can modify the base text by entering new text here.</property> + <child> + <object class="GtkEntry" id="Left1ED"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="activates-default">True</property> + <property name="truncate-multiline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="Left1ED-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|Left1ED">Displays the base text that you selected in the current file. If you want, you can modify the base text by entering new text here.</property> + </object> + </child> </object> + <packing> + <property name="left-attach">0</property> + <property name="top-attach">0</property> + </packing> </child> - </object> - <packing> - <property name="left-attach">0</property> - <property name="top-attach">0</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="Right1ED"> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="hexpand">True</property> - <property name="activates-default">True</property> - <property name="truncate-multiline">True</property> - <child internal-child="accessible"> - <object class="AtkObject" id="Right1ED-atkobject"> - <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|Right1ED">Enter the text that you want to use as a pronunciation guide for the base text.</property> + <child> + <object class="GtkEntry" id="Right1ED"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="hexpand">True</property> + <property name="activates-default">True</property> + <property name="truncate-multiline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="Right1ED-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|Right1ED">Enter the text that you want to use as a pronunciation guide for the base text.</property> + </object> + </child> </object> + <packing> + <property name="left-attach">1</property> + <property name="top-attach">0</property> + </packing> </child> - </object> - <packing> - <property name="left-attach">1</property> - <property name="top-attach">0</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="Right2ED"> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="activates-default">True</property> - <property name="truncate-multiline">True</property> - <child internal-child="accessible"> - <object class="AtkObject" id="Right2ED-atkobject"> - <property name="AtkObject::accessible-name" translatable="yes" context="asianphoneticguidedialog|Right2ED-atkobject">Ruby text</property> - <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|Right2ED">Enter the text that you want to use as a pronunciation guide for the base text.</property> + <child> + <object class="GtkEntry" id="Right2ED"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="activates-default">True</property> + <property name="truncate-multiline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="Right2ED-atkobject"> + <property name="AtkObject::accessible-name" translatable="yes" context="asianphoneticguidedialog|Right2ED-atkobject">Ruby text</property> + <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|Right2ED">Enter the text that you want to use as a pronunciation guide for the base text.</property> + </object> + </child> </object> + <packing> + <property name="left-attach">1</property> + <property name="top-attach">1</property> + </packing> </child> - </object> - <packing> - <property name="left-attach">1</property> - <property name="top-attach">1</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="Left3ED"> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="activates-default">True</property> - <property name="truncate-multiline">True</property> - <child internal-child="accessible"> - <object class="AtkObject" id="Left3ED-atkobject"> - <property name="AtkObject::accessible-name" translatable="yes" context="asianphoneticguidedialog|Left3ED-atkobject">Base text</property> - <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|Left3ED">Displays the base text that you selected in the current file. If you want, you can modify the base text by entering new text here.</property> + <child> + <object class="GtkEntry" id="Left3ED"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="activates-default">True</property> + <property name="truncate-multiline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="Left3ED-atkobject"> + <property name="AtkObject::accessible-name" translatable="yes" context="asianphoneticguidedialog|Left3ED-atkobject">Base text</property> + <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|Left3ED">Displays the base text that you selected in the current file. If you want, you can modify the base text by entering new text here.</property> + </object> + </child> </object> + <packing> + <property name="left-attach">0</property> + <property name="top-attach">2</property> + </packing> </child> - </object> - <packing> - <property name="left-attach">0</property> - <property name="top-attach">2</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="Right3ED"> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="activates-default">True</property> - <property name="truncate-multiline">True</property> - <child internal-child="accessible"> - <object class="AtkObject" id="Right3ED-atkobject"> - <property name="AtkObject::accessible-name" translatable="yes" context="asianphoneticguidedialog|Right3ED-atkobject">Ruby text</property> - <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|Right3ED">Enter the text that you want to use as a pronunciation guide for the base text.</property> + <child> + <object class="GtkEntry" id="Right3ED"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="activates-default">True</property> + <property name="truncate-multiline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="Right3ED-atkobject"> + <property name="AtkObject::accessible-name" translatable="yes" context="asianphoneticguidedialog|Right3ED-atkobject">Ruby text</property> + <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|Right3ED">Enter the text that you want to use as a pronunciation guide for the base text.</property> + </object> + </child> </object> + <packing> + <property name="left-attach">1</property> + <property name="top-attach">2</property> + </packing> </child> - </object> - <packing> - <property name="left-attach">1</property> - <property name="top-attach">2</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="Right4ED"> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="activates-default">True</property> - <property name="truncate-multiline">True</property> - <child internal-child="accessible"> - <object class="AtkObject" id="Right4ED-atkobject"> - <property name="AtkObject::accessible-name" translatable="yes" context="asianphoneticguidedialog|Right4ED-atkobject">Ruby text</property> - <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|Right4ED">Enter the text that you want to use as a pronunciation guide for the base text.</property> + <child> + <object class="GtkEntry" id="Right4ED"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="activates-default">True</property> + <property name="truncate-multiline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="Right4ED-atkobject"> + <property name="AtkObject::accessible-name" translatable="yes" context="asianphoneticguidedialog|Right4ED-atkobject">Ruby text</property> + <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|Right4ED">Enter the text that you want to use as a pronunciation guide for the base text.</property> + </object> + </child> </object> + <packing> + <property name="left-attach">1</property> + <property name="top-attach">3</property> + </packing> </child> - </object> - <packing> - <property name="left-attach">1</property> - <property name="top-attach">3</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="Left4ED"> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="activates-default">True</property> - <property name="truncate-multiline">True</property> - <child internal-child="accessible"> - <object class="AtkObject" id="Left4ED-atkobject"> - <property name="AtkObject::accessible-name" translatable="yes" context="asianphoneticguidedialog|Left4ED-atkobject">Base text</property> - <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|Left4ED">Displays the base text that you selected in the current file. If you want, you can modify the base text by entering new text here.</property> + <child> + <object class="GtkEntry" id="Left4ED"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="activates-default">True</property> + <property name="truncate-multiline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="Left4ED-atkobject"> + <property name="AtkObject::accessible-name" translatable="yes" context="asianphoneticguidedialog|Left4ED-atkobject">Base text</property> + <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|Left4ED">Displays the base text that you selected in the current file. If you want, you can modify the base text by entering new text here.</property> + </object> + </child> </object> + <packing> + <property name="left-attach">0</property> + <property name="top-attach">3</property> + </packing> </child> </object> <packing> <property name="left-attach">0</property> - <property name="top-attach">3</property> + <property name="top-attach">0</property> </packing> </child> </object> - <packing> - <property name="left-attach">0</property> - <property name="top-attach">0</property> - </packing> </child> </object> </child> </object> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <!-- n-columns=4 n-rows=2 --> - <object class="GtkGrid" id="grid2"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="margin-top">6</property> - <property name="row-spacing">6</property> - <property name="column-spacing">12</property> - <child> - <object class="GtkLabel" id="label4"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="label" translatable="yes" context="asianphoneticguidedialog|label4">Alignment:</property> - <property name="use-underline">True</property> - <property name="mnemonic-widget">adjustlb</property> - <property name="xalign">0</property> - </object> <packing> - <property name="left-attach">0</property> - <property name="top-attach">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> </packing> </child> <child> - <object class="GtkLabel" id="label5"> + <!-- n-columns=4 n-rows=2 --> + <object class="GtkGrid" id="grid2"> <property name="visible">True</property> <property name="can-focus">False</property> - <property name="label" translatable="yes" context="asianphoneticguidedialog|label5">Position:</property> - <property name="use-underline">True</property> - <property name="mnemonic-widget">positionlb</property> - <property name="xalign">0</property> + <property name="margin-top">6</property> + <property name="row-spacing">6</property> + <property name="column-spacing">12</property> + <child> + <object class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes" context="asianphoneticguidedialog|label4">Alignment:</property> + <property name="use-underline">True</property> + <property name="mnemonic-widget">adjustlb</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left-attach">0</property> + <property name="top-attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label5"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes" context="asianphoneticguidedialog|label5">Position:</property> + <property name="use-underline">True</property> + <property name="mnemonic-widget">positionlb</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left-attach">1</property> + <property name="top-attach">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="styleft"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes" context="asianphoneticguidedialog|styleft">Character style for ruby text:</property> + <property name="use-underline">True</property> + <property name="mnemonic-widget">stylelb</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="left-attach">2</property> + <property name="top-attach">0</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="stylelb"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="hexpand">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="stylelb-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|stylelb">Select a character style for the ruby text.</property> + </object> + </child> + </object> + <packing> + <property name="left-attach">2</property> + <property name="top-attach">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="styles"> + <property name="label" translatable="yes" context="asianphoneticguidedialog|styles">Styles</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="use-underline">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="styles-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|styles">Opens the Styles deck of the Sidebar where you can select a character style for the ruby text.</property> + </object> + </child> + </object> + <packing> + <property name="left-attach">3</property> + <property name="top-attach">1</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="adjustlb"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <items> + <item translatable="yes" context="asianphoneticguidedialog|adjustlb">Left</item> + <item translatable="yes" context="asianphoneticguidedialog|adjustlb">Center</item> + <item translatable="yes" context="asianphoneticguidedialog|adjustlb">Right</item> + <item translatable="yes" context="asianphoneticguidedialog|adjustlb">0 1 0</item> + <item translatable="yes" context="asianphoneticguidedialog|adjustlb">1 2 1</item> + </items> + <child internal-child="accessible"> + <object class="AtkObject" id="adjustlb-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|adjustlb">Select the horizontal alignment for the ruby text.</property> + </object> + </child> + </object> + <packing> + <property name="left-attach">0</property> + <property name="top-attach">1</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="positionlb"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <items> + <item translatable="yes" context="asianphoneticguidedialog|positionlb">Top</item> + <item translatable="yes" context="asianphoneticguidedialog|positionlb">Bottom</item> + <item translatable="yes" context="asianphoneticguidedialog|positionlb">Right</item> + </items> + <child internal-child="accessible"> + <object class="AtkObject" id="positionlb-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|positionlb">Select where you want to place the ruby text.</property> + </object> + </child> + </object> + <packing> + <property name="left-attach">1</property> + <property name="top-attach">1</property> + </packing> + </child> + <child> + <placeholder/> + </child> </object> <packing> - <property name="left-attach">1</property> - <property name="top-attach">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> </packing> </child> <child> - <object class="GtkLabel" id="styleft"> + <object class="GtkLabel" id="label1"> <property name="visible">True</property> <property name="can-focus">False</property> - <property name="label" translatable="yes" context="asianphoneticguidedialog|styleft">Character style for ruby text:</property> + <property name="margin-top">6</property> + <property name="label" translatable="yes" context="asianphoneticguidedialog|label1">Preview:</property> <property name="use-underline">True</property> - <property name="mnemonic-widget">stylelb</property> + <property name="mnemonic-widget">preview</property> <property name="xalign">0</property> </object> <packing> - <property name="left-attach">2</property> - <property name="top-attach">0</property> - </packing> - </child> - <child> - <object class="GtkComboBoxText" id="stylelb"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="hexpand">True</property> - <child internal-child="accessible"> - <object class="AtkObject" id="stylelb-atkobject"> - <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|stylelb">Select a character style for the ruby text.</property> - </object> - </child> - </object> - <packing> - <property name="left-attach">2</property> - <property name="top-attach">1</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> </packing> </child> <child> - <object class="GtkButton" id="styles"> - <property name="label" translatable="yes" context="asianphoneticguidedialog|styles">Styles</property> + <object class="GtkScrolledWindow" id="ctlFavoriteswin"> <property name="visible">True</property> <property name="can-focus">True</property> - <property name="receives-default">True</property> - <property name="use-underline">True</property> - <child internal-child="accessible"> - <object class="AtkObject" id="styles-atkobject"> - <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|styles">Opens the Styles deck of the Sidebar where you can select a character style for the ruby text.</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="hscrollbar-policy">never</property> + <property name="vscrollbar-policy">never</property> + <property name="shadow-type">in</property> + <child> + <object class="GtkViewport"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <child> + <object class="GtkDrawingArea" id="preview"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + </object> + </child> </object> </child> </object> <packing> - <property name="left-attach">3</property> - <property name="top-attach">1</property> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">4</property> </packing> </child> + </object> + <packing> + <property name="left-attach">0</property> + <property name="top-attach">1</property> + </packing> + </child> + <child> + <!-- n-columns=2 n-rows=1 --> + <object class="GtkGrid" id="grid3"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="valign">start</property> + <property name="column-spacing">12</property> + <property name="column-homogeneous">True</property> <child> - <object class="GtkComboBoxText" id="adjustlb"> + <object class="GtkLabel" id="basetextft"> <property name="visible">True</property> <property name="can-focus">False</property> - <items> - <item translatable="yes" context="asianphoneticguidedialog|adjustlb">Left</item> - <item translatable="yes" context="asianphoneticguidedialog|adjustlb">Center</item> - <item translatable="yes" context="asianphoneticguidedialog|adjustlb">Right</item> - <item translatable="yes" context="asianphoneticguidedialog|adjustlb">0 1 0</item> - <item translatable="yes" context="asianphoneticguidedialog|adjustlb">1 2 1</item> - </items> - <child internal-child="accessible"> - <object class="AtkObject" id="adjustlb-atkobject"> - <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|adjustlb">Select the horizontal alignment for the ruby text.</property> - </object> - </child> + <property name="label" translatable="yes" context="asianphoneticguidedialog|basetextft">Base text</property> + <property name="use-underline">True</property> </object> <packing> <property name="left-attach">0</property> - <property name="top-attach">1</property> + <property name="top-attach">0</property> </packing> </child> <child> - <object class="GtkComboBoxText" id="positionlb"> + <object class="GtkLabel" id="rubytextft"> <property name="visible">True</property> <property name="can-focus">False</property> - <items> - <item translatable="yes" context="asianphoneticguidedialog|positionlb">Top</item> - <item translatable="yes" context="asianphoneticguidedialog|positionlb">Bottom</item> - <item translatable="yes" context="asianphoneticguidedialog|positionlb">Right</item> - </items> - <child internal-child="accessible"> - <object class="AtkObject" id="positionlb-atkobject"> - <property name="AtkObject::accessible-description" translatable="yes" context="asianphoneticguidedialog|extended_tip|positionlb">Select where you want to place the ruby text.</property> - </object> - </child> + <property name="label" translatable="yes" context="asianphoneticguidedialog|rubytextft">Ruby text</property> + <property name="use-underline">True</property> </object> <packing> <property name="left-attach">1</property> - <property name="top-attach">1</property> + <property name="top-attach">0</property> </packing> </child> - <child> - <placeholder/> - </child> </object> <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> + <property name="left-attach">0</property> + <property name="top-attach">0</property> </packing> </child> <child> - <object class="GtkLabel" id="label1"> + <object class="GtkButtonBox" id="dialog-action_area2"> <property name="visible">True</property> <property name="can-focus">False</property> - <property name="margin-top">6</property> - <property name="label" translatable="yes" context="asianphoneticguidedialog|label1">Preview:</property> - <property name="use-underline">True</property> - <property name="mnemonic-widget">preview</property> - <property name="xalign">0</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">4</property> - </packing> - </child> - <child> - <object class="GtkScrolledWindow" id="ctlFavoriteswin"> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="hexpand">True</property> - <property name="vexpand">True</property> - <property name="hscrollbar-policy">never</property> - <property name="vscrollbar-policy">never</property> - <property name="shadow-type">in</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <property name="layout-style">start</property> <child> - <object class="GtkViewport"> + <object class="GtkButton" id="selection-group"> + <property name="label" translatable="yes" context="asianphoneticguidedialog|selectiongroup">_Group</property> <property name="visible">True</property> - <property name="can-focus">False</property> - <child> - <object class="GtkDrawingArea" id="preview"> - <property name="visible">True</property> - <property name="can-focus">False</property> - <property name="hexpand">True</property> - <property name="vexpand">True</property> - </object> - </child> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="use-underline">True</property> </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> </child> </object> <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">5</property> + <property name="left-attach">1</property> + <property name="top-attach">1</property> </packing> </child> + <child> + <placeholder/> + </child> </object> <packing> <property name="expand">True</property> <property name="fill">True</property> - <property name="position">1</property> + <property name="position">0</property> </packing> </child> </object> diff --git a/sw/inc/doc.hxx b/sw/inc/doc.hxx index 1a4fff14b4c2..0274dd4976be 100644 --- a/sw/inc/doc.hxx +++ b/sw/inc/doc.hxx @@ -1591,7 +1591,7 @@ public: // Interface for the list of Ruby - texts/attributes static sal_uInt16 FillRubyList( const SwPaM& rPam, SwRubyList& rList ); - void SetRubyList( const SwPaM& rPam, const SwRubyList& rList ); + void SetRubyList( SwPaM& rPam, const SwRubyList& rList ); void ReadLayoutCache( SvStream& rStream ); void WriteLayoutCache( SvStream& rStream ); diff --git a/sw/inc/rubylist.hxx b/sw/inc/rubylist.hxx index b025686daae3..3542cbcb3738 100644 --- a/sw/inc/rubylist.hxx +++ b/sw/inc/rubylist.hxx @@ -27,7 +27,6 @@ class SwRubyListEntry SwFormatRuby m_aRubyAttr; public: SwRubyListEntry() : m_aRubyAttr( OUString() ) {} - ~SwRubyListEntry(); const OUString& GetText() const { return m_sText; } void SetText( const OUString& rStr ) { m_sText = rStr; } diff --git a/sw/qa/core/uwriter.cxx b/sw/qa/core/uwriter.cxx index 3504a11130df..2e10ae43aa1c 100644 --- a/sw/qa/core/uwriter.cxx +++ b/sw/qa/core/uwriter.cxx @@ -132,6 +132,7 @@ public: void testTdf92308(); void testTableCellComparison(); void testTdf156211(); + void testFillRubyList(); void testSetRubyList(); CPPUNIT_TEST_SUITE(SwDocTest); @@ -171,6 +172,7 @@ public: CPPUNIT_TEST(testTdf92308); CPPUNIT_TEST(testTableCellComparison); CPPUNIT_TEST(testTdf156211); + CPPUNIT_TEST(testFillRubyList); CPPUNIT_TEST(testSetRubyList); CPPUNIT_TEST_SUITE_END(); @@ -1985,6 +1987,165 @@ void SwDocTest::testTdf156211() CPPUNIT_ASSERT(!oSI.IsKashidaLine(TextFrameIndex{ 95 })); } +void SwDocTest::testFillRubyList() +{ + SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -1); + SwPaM aPaM(aIdx); + + SwTextNode* pTextNode = aPaM.GetPointNode().GetTextNode(); + CPPUNIT_ASSERT(pTextNode); + + auto& rOps = m_pDoc->getIDocumentContentOperations(); + + auto fnAppendJapanese = [&](const OUString& rText) + { + rOps.AppendTextNode(*aPaM.GetPoint()); + + SvxLanguageItem aCJKLangItem(LANGUAGE_JAPANESE, RES_CHRATR_CJK_LANGUAGE); + SvxLanguageItem aWestLangItem(LANGUAGE_ENGLISH_US, RES_CHRATR_LANGUAGE); + rOps.InsertPoolItem(aPaM, aCJKLangItem); + rOps.InsertPoolItem(aPaM, aWestLangItem); + + rOps.InsertString(aPaM, rText); + + aPaM.SetMark(); + aPaM.GetPoint()->nContent = 0; + + CPPUNIT_ASSERT_EQUAL(rText, aPaM.GetText()); + }; + + auto fnAppendRuby = [](SwRubyList* rList, OUString aBase, OUString aRuby) + { + auto pEnt = std::make_unique<SwRubyListEntry>(); + pEnt->SetText(std::move(aBase)); + pEnt->SetRubyAttr(SwFormatRuby{ std::move(aRuby) }); + + rList->push_back(std::move(pEnt)); + }; + + auto fnGetCombinedString = [&] + { + SwRubyList aRubies; + SwDoc::FillRubyList(aPaM, aRubies); + + OUStringBuffer aTemp; + + for (auto const& rRuby : aRubies) + { + aTemp.append(rRuby->GetText() + u"["_ustr + rRuby->GetRubyAttr().GetText() + u"]"_ustr); + } + + return aTemp.toString(); + }; + + // Single word without existing rubies + { + fnAppendJapanese(u"学校"_ustr); + CPPUNIT_ASSERT_EQUAL(u"学校[]"_ustr, fnGetCombinedString()); + } + + // Compound word without existing rubies + { + fnAppendJapanese(u"自動販売機"_ustr); + CPPUNIT_ASSERT_EQUAL(u"自動[]販売[]機[]"_ustr, fnGetCombinedString()); + } + + // Single word with existing rubies + { + fnAppendJapanese(u"学校"_ustr); + + SwRubyList rList; + fnAppendRuby(&rList, u"学校"_ustr, u"がっこう"_ustr); + + m_pDoc->SetRubyList(aPaM, rList); + + CPPUNIT_ASSERT_EQUAL(u"学校[がっこう]"_ustr, fnGetCombinedString()); + } + + // Compound word with existing rubies + { + fnAppendJapanese(u"自動販売機"_ustr); + + SwRubyList rList; + fnAppendRuby(&rList, u"自動"_ustr, u"じどう"_ustr); + fnAppendRuby(&rList, u"販売"_ustr, u"はんばい"_ustr); + fnAppendRuby(&rList, u"機"_ustr, u"き"_ustr); + + m_pDoc->SetRubyList(aPaM, rList); + + CPPUNIT_ASSERT_EQUAL(u"自動[じどう]販売[はんばい]機[き]"_ustr, fnGetCombinedString()); + } + + // Compound word with existing rubies treated as a single word + { + fnAppendJapanese(u"自動販売機"_ustr); + + SwRubyList rList; + fnAppendRuby(&rList, u"自動販売機"_ustr, u"じどうはんばいき"_ustr); + fnAppendRuby(&rList, u""_ustr, u""_ustr); + fnAppendRuby(&rList, u""_ustr, u""_ustr); + + m_pDoc->SetRubyList(aPaM, rList); + + CPPUNIT_ASSERT_EQUAL(u"自動販売機[じどうはんばいき]"_ustr, fnGetCombinedString()); + } + + // tdf#141466: Characteristic test from bug + { + fnAppendJapanese(u"学校に行きます。"_ustr); + + SwRubyList rList; + fnAppendRuby(&rList, u"学校"_ustr, u"がっこう"_ustr); + fnAppendRuby(&rList, u"に"_ustr, u""_ustr); + fnAppendRuby(&rList, u"行"_ustr, u"い"_ustr); + fnAppendRuby(&rList, u"きます"_ustr, u""_ustr); + fnAppendRuby(&rList, u"。"_ustr, u""_ustr); + + m_pDoc->SetRubyList(aPaM, rList); + + CPPUNIT_ASSERT_EQUAL(u"学校[がっこう]に[]行[い]き[]ます[]。[]"_ustr, fnGetCombinedString()); + } + + // tdf#107184: Characteristic test for ruby group mode editing + { + fnAppendJapanese(u"学校に行きます"_ustr); + + SwRubyList rList; + fnAppendRuby(&rList, u"学校に行きます"_ustr, u"がっこうにいきます"_ustr); + + m_pDoc->SetRubyList(aPaM, rList); + + CPPUNIT_ASSERT_EQUAL(u"学校に行きます[がっこうにいきます]"_ustr, fnGetCombinedString()); + } + + // tdf#156543: Characteristic test for ruby mono mode editing + { + fnAppendJapanese(u"学校に行きます"_ustr); + + SwRubyList rList; + fnAppendRuby(&rList, u"学"_ustr, u"がっ"_ustr); + fnAppendRuby(&rList, u"校"_ustr, u"こう"_ustr); + fnAppendRuby(&rList, u"に"_ustr, u""_ustr); + fnAppendRuby(&rList, u"行"_ustr, u"い"_ustr); + fnAppendRuby(&rList, u"き"_ustr, u""_ustr); + fnAppendRuby(&rList, u"ま"_ustr, u""_ustr); + fnAppendRuby(&rList, u"す"_ustr, u""_ustr); + + m_pDoc->SetRubyList(aPaM, rList); + + CPPUNIT_ASSERT_EQUAL(u"学[がっ]校[こう]に[]行[い]き[]ます[]"_ustr, fnGetCombinedString()); + } + + // Empty PaM + { + fnAppendJapanese(u"学校"_ustr); + + aPaM.DeleteMark(); + + CPPUNIT_ASSERT_EQUAL(u"学校[]"_ustr, fnGetCombinedString()); + } +} + void SwDocTest::testSetRubyList() { SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -1); @@ -2057,7 +2218,11 @@ void SwDocTest::testSetRubyList() SwRubyList rList; fnAppendRuby(&rList, u"学校"_ustr, u"がっこう"_ustr); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aPaM.GetMark()->GetContentIndex()); m_pDoc->SetRubyList(aPaM, rList); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aPaM.GetMark()->GetContentIndex()); CPPUNIT_ASSERT_EQUAL(u"学校[がっこう]"_ustr, fnGetCombinedString()); } @@ -2073,7 +2238,11 @@ void SwDocTest::testSetRubyList() fnAppendRuby(&rList, u"きます"_ustr, u""_ustr); fnAppendRuby(&rList, u"。"_ustr, u""_ustr); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(8), aPaM.GetMark()->GetContentIndex()); m_pDoc->SetRubyList(aPaM, rList); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(8), aPaM.GetMark()->GetContentIndex()); CPPUNIT_ASSERT_EQUAL(u"学校[がっこう]に行[い]きます。"_ustr, fnGetCombinedString()); } @@ -2089,7 +2258,11 @@ void SwDocTest::testSetRubyList() fnAppendRuby(&rList, u"きます"_ustr, u""_ustr); fnAppendRuby(&rList, u"。"_ustr, u""_ustr); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(8), aPaM.GetMark()->GetContentIndex()); m_pDoc->SetRubyList(aPaM, rList); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(8), aPaM.GetMark()->GetContentIndex()); CPPUNIT_ASSERT_EQUAL(u"学校[がっこう]に行[い]きます。"_ustr, fnGetCombinedString()); @@ -2101,10 +2274,132 @@ void SwDocTest::testSetRubyList() fnAppendRuby(&rList2, u"ます"_ustr, u""_ustr); fnAppendRuby(&rList2, u"。"_ustr, u""_ustr); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(8), aPaM.GetMark()->GetContentIndex()); m_pDoc->SetRubyList(aPaM, rList2); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(7), aPaM.GetMark()->GetContentIndex()); CPPUNIT_ASSERT_EQUAL(u"学校[がっこう]に来[き]ます。"_ustr, fnGetCombinedString()); } + + // tdf#107184: Characteristic test for ruby group mode editing + { + fnAppendJapanese(u"学校に行きます"_ustr); + + SwRubyList rList; + fnAppendRuby(&rList, u"学校に行きます"_ustr, u"がっこうにいきます"_ustr); + + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(7), aPaM.GetMark()->GetContentIndex()); + m_pDoc->SetRubyList(aPaM, rList); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(7), aPaM.GetMark()->GetContentIndex()); + + CPPUNIT_ASSERT_EQUAL(u"学校に行きます[がっこうにいきます]"_ustr, fnGetCombinedString()); + } + + // tdf#107184: Delete ruby in group mode after populating + { + fnAppendJapanese(u"学校に行きます"_ustr); + + SwRubyList rList; + fnAppendRuby(&rList, u"学校に行きます"_ustr, u"がっこうにいきます"_ustr); + + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(7), aPaM.GetMark()->GetContentIndex()); + m_pDoc->SetRubyList(aPaM, rList); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(7), aPaM.GetMark()->GetContentIndex()); + + CPPUNIT_ASSERT_EQUAL(u"学校に行きます[がっこうにいきます]"_ustr, fnGetCombinedString()); + + SwRubyList rList2; + fnAppendRuby(&rList2, u"学校に行きます"_ustr, u""_ustr); + + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(7), aPaM.GetMark()->GetContentIndex()); + m_pDoc->SetRubyList(aPaM, rList2); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(7), aPaM.GetMark()->GetContentIndex()); + + CPPUNIT_ASSERT_EQUAL(u"学校に行きます"_ustr, fnGetCombinedString()); + } + + // tdf#156543: Characteristic test for ruby mono mode editing + { + fnAppendJapanese(u"学校に行きます"_ustr); + + SwRubyList rList; + fnAppendRuby(&rList, u"学"_ustr, u"がっ"_ustr); + fnAppendRuby(&rList, u"校"_ustr, u"こう"_ustr); + fnAppendRuby(&rList, u"に"_ustr, u""_ustr); + fnAppendRuby(&rList, u"行"_ustr, u"い"_ustr); + fnAppendRuby(&rList, u"き"_ustr, u""_ustr); + fnAppendRuby(&rList, u"ま"_ustr, u""_ustr); + fnAppendRuby(&rList, u"す"_ustr, u"す"_ustr); + + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(7), aPaM.GetMark()->GetContentIndex()); + m_pDoc->SetRubyList(aPaM, rList); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(7), aPaM.GetMark()->GetContentIndex()); + + CPPUNIT_ASSERT_EQUAL(u"学[がっ]校[こう]に行[い]きます[す]"_ustr, fnGetCombinedString()); + } + + // Offset PaM - Combination of insert and replace + { + fnAppendJapanese(u"学校員"_ustr); + + SwPosition aNewPos{ aPaM.GetPoint()->nNode, aPaM.GetPoint()->nContent }; + const SwTextNode* pTNd = aNewPos.GetNode().GetTextNode(); + pTNd->GoNext(&aNewPos, SwCursorSkipMode::Chars); + + SwPaM aEmptyPaM{ aNewPos }; + aEmptyPaM.SetMark(); + aEmptyPaM.GetMark()->AdjustContent(1); + + SwRubyList rList; + fnAppendRuby(&rList, u"森林"_ustr, u"しんりん"_ustr); + fnAppendRuby(&rList, u"海上"_ustr, u"かいじょう"_ustr); + fnAppendRuby(&rList, u"地面"_ustr, u"じめん"_ustr); + + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aEmptyPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aEmptyPaM.GetMark()->GetContentIndex()); + m_pDoc->SetRubyList(aEmptyPaM, rList); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aEmptyPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(7), aEmptyPaM.GetMark()->GetContentIndex()); + + CPPUNIT_ASSERT_EQUAL(u"学森林[しんりん]海上[かいじょう]地面[じめん]員"_ustr, + fnGetCombinedString()); + } + + // Empty PaM - Should insert + { + fnAppendJapanese(u"学校"_ustr); + + SwPosition aNewPos{ aPaM.GetPoint()->nNode, aPaM.GetPoint()->nContent }; + const SwTextNode* pTNd = aNewPos.GetNode().GetTextNode(); + pTNd->GoNext(&aNewPos, SwCursorSkipMode::Chars); + + SwPaM aEmptyPaM{ aNewPos }; + aEmptyPaM.SetMark(); + + SwRubyList rList; + fnAppendRuby(&rList, u"森林"_ustr, u"しんりん"_ustr); + fnAppendRuby(&rList, u"海上"_ustr, u"かいじょう"_ustr); + fnAppendRuby(&rList, u"地面"_ustr, u"じめん"_ustr); + + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aEmptyPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aEmptyPaM.GetMark()->GetContentIndex()); + m_pDoc->SetRubyList(aEmptyPaM, rList); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aEmptyPaM.GetPoint()->GetContentIndex()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(7), aEmptyPaM.GetMark()->GetContentIndex()); + + CPPUNIT_ASSERT_EQUAL(u"学森林[しんりん]海上[かいじょう]地面[じめん]校"_ustr, + fnGetCombinedString()); + } } CPPUNIT_TEST_SUITE_REGISTRATION(SwDocTest); diff --git a/sw/source/core/doc/docruby.cxx b/sw/source/core/doc/docruby.cxx index 90f4e771d993..15629c70325e 100644 --- a/sw/source/core/doc/docruby.cxx +++ b/sw/source/core/doc/docruby.cxx @@ -40,6 +40,8 @@ using namespace ::com::sun::star::i18n; +constexpr int nMaxBaseTexts = 30; + /* * Members in the list: * - String - the orig text @@ -78,9 +80,9 @@ sal_uInt16 SwDoc::FillRubyList( const SwPaM& rPam, SwRubyList& rList ) else break; } - } while( 30 > rList.size() && *aPam.GetPoint() < *pEnd ); + } while (nMaxBaseTexts > rList.size() && *aPam.GetPoint() < *pEnd); } - if( 30 <= rList.size() ) + if (nMaxBaseTexts <= rList.size()) break; _pStartCursor = _pStartCursor->GetNext(); } while( _pStartCursor != _pStartCursor2 ); @@ -88,95 +90,146 @@ sal_uInt16 SwDoc::FillRubyList( const SwPaM& rPam, SwRubyList& rList ) return rList.size(); } -void SwDoc::SetRubyList( const SwPaM& rPam, const SwRubyList& rList ) +void SwDoc::SetRubyList(SwPaM& rPam, const SwRubyList& rList) { - GetIDocumentUndoRedo().StartUndo( SwUndoId::SETRUBYATTR, nullptr ); + SwPaM aOrigPam{ *rPam.GetPoint(), *rPam.GetMark() }; + aOrigPam.Normalize(); + + GetIDocumentUndoRedo().StartUndo(SwUndoId::SETRUBYATTR, nullptr); const o3tl::sorted_vector<sal_uInt16> aDelArr{ RES_TXTATR_CJK_RUBY }; SwRubyList::size_type nListEntry = 0; + int nCurrBaseTexts = 0; - const SwPaM *_pStartCursor = rPam.GetNext(), - *_pStartCursor2 = _pStartCursor; - bool bCheckEmpty = &rPam != _pStartCursor; - do { - auto [pStt, pEnd] = _pStartCursor->StartEnd(); // SwPosition* - if( !bCheckEmpty || ( pStt != pEnd && *pStt != *pEnd )) + const SwPaM* pStartCursor = rPam.GetNext(); + auto [pStt, pEnd] = pStartCursor->StartEnd(); + + bool bCheckEmpty = (&rPam == pStartCursor) || (pStt != pEnd && *pStt != *pEnd); + + // Sequentially replace as many spans as possible + SwPaM aPam(*pStt); + while (bCheckEmpty && nListEntry < rList.size() && nCurrBaseTexts < nMaxBaseTexts) + { + if (pEnd != pStt) { + aPam.SetMark(); + *aPam.GetMark() = *pEnd; + } - SwPaM aPam( *pStt ); - do { - SwRubyListEntry aCheckEntry; - if( pEnd != pStt ) - { - aPam.SetMark(); - *aPam.GetMark() = *pEnd; - } - if( SelectNextRubyChars( aPam, aCheckEntry )) - { - const SwRubyListEntry* pEntry = rList[ nListEntry++ ].get(); - if( aCheckEntry.GetRubyAttr() != pEntry->GetRubyAttr() ) - { - // set/reset the attribute - if( !pEntry->GetRubyAttr().GetText().isEmpty() ) - { - getIDocumentContentOperations().InsertPoolItem( aPam, pEntry->GetRubyAttr() ); - } - else - { - ResetAttrs( aPam, true, aDelArr ); - } - } + SwRubyListEntry aCheckEntry; + if (!SelectNextRubyChars(aPam, aCheckEntry)) + { + // goto next paragraph + aPam.DeleteMark(); + aPam.Move(fnMoveForward, GoInNode); - if (aCheckEntry.GetText() != pEntry->GetText()) - { - if (pEntry->GetText().isEmpty()) - { - ResetAttrs(aPam, true, aDelArr); - } - - // text is changed, so replace the original - getIDocumentContentOperations().ReplaceRange(aPam, pEntry->GetText(), - false); - std::swap(*aPam.GetMark(), *aPam.GetPoint()); - } + if (*aPam.GetPoint() >= *pEnd) + { + break; + } - aPam.DeleteMark(); - } - else - { - if( *aPam.GetPoint() < *pEnd ) - { - // goto next paragraph - aPam.DeleteMark(); - aPam.Move( fnMoveForward, GoInNode ); - } - else - { - const SwRubyListEntry* pEntry = rList[ nListEntry++ ].get(); - - // set/reset the attribute - if( !pEntry->GetRubyAttr().GetText().isEmpty() && - !pEntry->GetText().isEmpty() ) - { - getIDocumentContentOperations().InsertString( aPam, pEntry->GetText() ); - aPam.SetMark(); - aPam.GetMark()->AdjustContent( -pEntry->GetText().getLength() ); - getIDocumentContentOperations().InsertPoolItem( - aPam, pEntry->GetRubyAttr(), SetAttrMode::DONTEXPAND ); - } - else - break; - aPam.DeleteMark(); - } - } - } while( nListEntry < rList.size() && *aPam.GetPoint() < *pEnd ); + continue; } - if( 30 <= rList.size() ) - break; - _pStartCursor = _pStartCursor->GetNext(); - } while( _pStartCursor != _pStartCursor2 ); - GetIDocumentUndoRedo().EndUndo( SwUndoId::SETRUBYATTR, nullptr ); + ++nCurrBaseTexts; + + const SwRubyListEntry* pEntry = rList[nListEntry++].get(); + if (aCheckEntry.GetRubyAttr() != pEntry->GetRubyAttr()) + { + // set/reset the attribute + if (!pEntry->GetRubyAttr().GetText().isEmpty()) + { + getIDocumentContentOperations().InsertPoolItem(aPam, pEntry->GetRubyAttr()); + } + else + { + ResetAttrs(aPam, true, aDelArr); + } + } + + if (aCheckEntry.GetText() != pEntry->GetText()) + { + if (pEntry->GetText().isEmpty()) + { + ResetAttrs(aPam, true, aDelArr); + } + + // text is changed, so replace the original + getIDocumentContentOperations().ReplaceRange(aPam, pEntry->GetText(), false); + aPam.Exchange(); + } + + aPam.DeleteMark(); + } + + // Delete any spans past the end of the ruby list + while (nListEntry == rList.size() && nCurrBaseTexts < nMaxBaseTexts) + { + if (pEnd != pStt) + { + aPam.SetMark(); + *aPam.GetMark() = *pEnd; + } + + SwRubyListEntry aCheckEntry; + if (!SelectNextRubyChars(aPam, aCheckEntry)) + { + // goto next paragraph + aPam.DeleteMark(); + aPam.Move(fnMoveForward, GoInNode); + + if (*aPam.GetPoint() >= *pEnd) + { + break; + } + + continue; + } + + ++nCurrBaseTexts; + + ResetAttrs(aPam, true, aDelArr); + getIDocumentContentOperations().DeleteRange(aPam); + aPam.Exchange(); + + aPam.DeleteMark(); + } + + // Insert any spans past the end of the base text list + sal_Int32 nTotalContentGrowth = 0; + while (nListEntry < rList.size()) + { + const SwRubyListEntry* pEntry = rList[nListEntry++].get(); + + if (pEnd != pStt) + { + aPam.SetMark(); + *aPam.GetMark() = *pEnd; + } + + aPam.SetMark(); + getIDocumentContentOperations().InsertString(aPam, pEntry->GetText()); + nTotalContentGrowth += pEntry->GetText().getLength(); + + if (!pEntry->GetRubyAttr().GetText().isEmpty()) + { + getIDocumentContentOperations().InsertPoolItem(aPam, pEntry->GetRubyAttr()); + } + + aPam.DeleteMark(); + } + + // Expand selection to account for insertion + rPam.Normalize(); + rPam = SwPaM{ *aOrigPam.GetPoint(), *rPam.GetMark() }; + if (*rPam.GetPoint() == *rPam.GetMark()) + { + rPam.GetPoint()->AdjustContent(-nTotalContentGrowth); + } + + rPam.Normalize(); + + GetIDocumentUndoRedo().EndUndo(SwUndoId::SETRUBYATTR, nullptr); } bool SwDoc::SelectNextRubyChars( SwPaM& rPam, SwRubyListEntry& rEntry ) @@ -320,8 +373,4 @@ bool SwDoc::SelectNextRubyChars( SwPaM& rPam, SwRubyListEntry& rEntry ) return rPam.HasMark(); } -SwRubyListEntry::~SwRubyListEntry() -{ -} - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |