cui/source/inc/chardlg.hxx | 1 cui/source/tabpages/chardlg.cxx | 25 ++++++++ cui/uiconfig/ui/positionpage.ui | 49 ++++++++++++++++ editeng/source/items/textitem.cxx | 14 +++- include/editeng/editrids.hrc | 1 include/editeng/nhypitem.hxx | 4 + sw/qa/uitest/writer_tests2/formatCharacter.py | 5 + sw/qa/uitest/writer_tests8/tdf106733.py | 78 ++++++++++++++++++++++++++ sw/source/core/access/AccessibilityCheck.cxx | 6 ++ sw/source/core/bastyp/init.cxx | 2 sw/source/core/layout/wsfrm.cxx | 1 sw/source/core/unocore/unomap.cxx | 2 sw/source/core/unocore/unomapproperties.hxx | 2 13 files changed, 182 insertions(+), 8 deletions(-)
New commits: commit 03c5a31a0f374a90fbc821718c14dc5f8a385adf Author: László Németh <nem...@numbertext.org> AuthorDate: Wed Jan 24 13:25:21 2024 +0100 Commit: László Németh <nem...@numbertext.org> CommitDate: Wed Jan 24 20:57:37 2024 +0100 tdf#106733 sw cui: add CharNoHyphenation checkbox On Position tab of Character formatting dialog window as a new checkbox "Exclude from hyphenation" (UX design by Heiko Tietze). With this, it's possible to disable hyphenation with direct character formatting (e.g. combined with Find All), or using character styles, and setting "Exclude from hyphenation" in them. This feature is conformant to the OpenDocument standard, and unlike the previous locale=None workaround, it keeps spell checking and locale dependent text layout. Note: Clear direct formatting (Ctrl-M) is an alternative way to remove the enabled CharNoHyphenation (e.g. in version 24.2, where this commit won't be back-ported). Follow-up to commit b5e275f47a54bd7fee39dad516a433fde5be872d "tdf#106733 sw: implement CharNoHyphenation". Change-Id: I26823e6ec2a3ca39dcf0f7c051d96e638921c589 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162514 Tested-by: Jenkins Reviewed-by: László Németh <nem...@numbertext.org> diff --git a/cui/source/inc/chardlg.hxx b/cui/source/inc/chardlg.hxx index 6d6bf958c2e2..b484c7419b83 100644 --- a/cui/source/inc/chardlg.hxx +++ b/cui/source/inc/chardlg.hxx @@ -269,6 +269,7 @@ private: std::unique_ptr<weld::MetricSpinButton> m_xKerningMF; std::unique_ptr<weld::CheckButton> m_xPairKerningBtn; + std::unique_ptr<weld::CheckButton> m_xNoHyphenationBtn; void Initialize(); void UpdatePreview_Impl( sal_uInt8 nProp, sal_uInt8 nEscProp, short nEsc ); diff --git a/cui/source/tabpages/chardlg.cxx b/cui/source/tabpages/chardlg.cxx index e851816c1787..86b183fe4160 100644 --- a/cui/source/tabpages/chardlg.cxx +++ b/cui/source/tabpages/chardlg.cxx @@ -41,6 +41,7 @@ #include <editeng/kernitem.hxx> #include <editeng/flstitem.hxx> #include <editeng/autokernitem.hxx> +#include <editeng/nhypitem.hxx> #include <editeng/colritem.hxx> #include <dialmgr.hxx> #include <sfx2/htmlmode.hxx> @@ -2429,6 +2430,7 @@ SvxCharPositionPage::SvxCharPositionPage(weld::Container* pPage, weld::DialogCon , m_xScaleWidthMF(m_xBuilder->weld_metric_spin_button("scalewidthsb", FieldUnit::PERCENT)) , m_xKerningMF(m_xBuilder->weld_metric_spin_button("kerningsb", FieldUnit::POINT)) , m_xPairKerningBtn(m_xBuilder->weld_check_button("pairkerning")) + , m_xNoHyphenationBtn(m_xBuilder->weld_check_button("nohyphenation")) { m_xPreviewWin.reset(new weld::CustomWeld(*m_xBuilder, "preview", m_aPreviewWin)); #ifdef IOS @@ -2794,6 +2796,16 @@ void SvxCharPositionPage::Reset( const SfxItemSet* rSet ) else m_xPairKerningBtn->set_active(false); + // No hyphenation + nWhich = GetWhich( sal_uInt16(19) ); // number borrowed from RES_CHRATR_NOHYPHEN + if ( rSet->GetItemState( nWhich ) >= SfxItemState::DEFAULT ) + { + const SvxNoHyphenItem& rItem = static_cast<const SvxNoHyphenItem&>(rSet->Get( nWhich )); + m_xNoHyphenationBtn->set_active(rItem.GetValue()); + } + else + m_xNoHyphenationBtn->set_active(false); + // Scale Width nWhich = GetWhich( SID_ATTR_CHAR_SCALEWIDTH ); if ( rSet->GetItemState( nWhich ) >= SfxItemState::DEFAULT ) @@ -2874,6 +2886,7 @@ void SvxCharPositionPage::ChangesApplied() m_xScaleWidthMF->save_value(); m_xKerningMF->save_value(); m_xPairKerningBtn->save_state(); + m_xNoHyphenationBtn->save_state(); } bool SvxCharPositionPage::FillItemSet( SfxItemSet* rSet ) @@ -2963,6 +2976,18 @@ bool SvxCharPositionPage::FillItemSet( SfxItemSet* rSet ) else if ( SfxItemState::DEFAULT == rOldSet.GetItemState( nWhich, false ) ) rSet->InvalidateItem(nWhich); + // No hyphenation + + nWhich = GetWhich( sal_uInt16(19) ); // number borrowed from RES_CHRATR_NOHYPHEN + + if (m_xNoHyphenationBtn->get_state_changed_from_saved()) + { + rSet->Put( SvxNoHyphenItem( m_xNoHyphenationBtn->get_active(), nWhich ) ); + bModified = true; + } + else if ( SfxItemState::DEFAULT == rOldSet.GetItemState( nWhich, false ) ) + rSet->InvalidateItem(nWhich); + // Scale Width nWhich = GetWhich( SID_ATTR_CHAR_SCALEWIDTH ); if (m_xScaleWidthMF->get_value_changed_from_saved()) diff --git a/cui/uiconfig/ui/positionpage.ui b/cui/uiconfig/ui/positionpage.ui index d093aa1deea3..fdafbc7134b2 100644 --- a/cui/uiconfig/ui/positionpage.ui +++ b/cui/uiconfig/ui/positionpage.ui @@ -481,6 +481,53 @@ <property name="position">2</property> </packing> </child> + <child> + <object class="GtkFrame" id="frame8"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <object class="GtkBox" id="box8"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="spacing">12</property> + <property name="margin-start">12</property> + <property name="margin-top">6</property> + <child> + <object class="GtkCheckButton" id="nohyphenation"> + <property name="label" translatable="yes" context="positionpage|nohyphenation">Exclude from hyphenation</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label23"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" context="positionpage|label23">Hyphenation</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> <child> <object class="GtkScrolledWindow"> <property name="visible">True</property> @@ -516,7 +563,7 @@ <property name="expand">False</property> <property name="fill">True</property> <property name="pack_type">end</property> - <property name="position">3</property> + <property name="position">4</property> </packing> </child> </object> diff --git a/editeng/source/items/textitem.cxx b/editeng/source/items/textitem.cxx index 3be26195b52d..0bed0a7d077d 100644 --- a/editeng/source/items/textitem.cxx +++ b/editeng/source/items/textitem.cxx @@ -107,7 +107,7 @@ SfxPoolItem* SvxEmphasisMarkItem::CreateDefault() {return new SvxEmphasisMarkIte SfxPoolItem* SvxCharRotateItem::CreateDefault() {return new SvxCharRotateItem(0_deg10, false, TypedWhichId<SvxCharRotateItem>(0));} SfxPoolItem* SvxCharScaleWidthItem::CreateDefault() {return new SvxCharScaleWidthItem(100, TypedWhichId<SvxCharScaleWidthItem>(0));} SfxPoolItem* SvxCharReliefItem::CreateDefault() {return new SvxCharReliefItem(FontRelief::NONE, 0);} - +SfxPoolItem* SvxNoHyphenItem::CreateDefault() {return new SvxNoHyphenItem(false, 0);} // class SvxFontListItem ------------------------------------------------- @@ -2166,8 +2166,8 @@ bool SvxLanguageItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId ) // class SvxNoHyphenItem ------------------------------------------------- -SvxNoHyphenItem::SvxNoHyphenItem( const sal_uInt16 nId ) : - SfxBoolItem( nId , true ) +SvxNoHyphenItem::SvxNoHyphenItem( const bool bNoHyphen, const sal_uInt16 nId ) : + SfxBoolItem( nId, bNoHyphen ) { } @@ -2184,8 +2184,12 @@ bool SvxNoHyphenItem::GetPresentation OUString& rText, const IntlWrapper& /*rIntl*/ ) const { - rText.clear(); - return false; + if ( GetValue() ) + rText = EditResId(RID_SVXITEMS_NOHYPHENATION_TRUE); + else + rText.clear(); + + return GetValue(); } /* diff --git a/include/editeng/editrids.hrc b/include/editeng/editrids.hrc index 9cbe225d730e..8d45a7c8a9da 100644 --- a/include/editeng/editrids.hrc +++ b/include/editeng/editrids.hrc @@ -176,6 +176,7 @@ #define RID_SVXITEMS_BLINK_FALSE NC_("RID_SVXITEMS_BLINK_FALSE", "Not Blinking") #define RID_SVXITEMS_AUTOKERN_TRUE NC_("RID_SVXITEMS_AUTOKERN_TRUE", "Pair Kerning") #define RID_SVXITEMS_AUTOKERN_FALSE NC_("RID_SVXITEMS_AUTOKERN_FALSE", "No pair kerning") +#define RID_SVXITEMS_NOHYPHENATION_TRUE NC_("RID_SVXITEMS_NOHYPHENATION_TRUE", "No hyphenation") #define RID_SVXITEMS_WORDLINE_TRUE NC_("RID_SVXITEMS_WORDLINE_TRUE", "Individual words") #define RID_SVXITEMS_WORDLINE_FALSE NC_("RID_SVXITEMS_WORDLINE_FALSE", "Not Words Only") #define RID_SVXITEMS_CONTOUR_TRUE NC_("RID_SVXITEMS_CONTOUR_TRUE", "Outline") diff --git a/include/editeng/nhypitem.hxx b/include/editeng/nhypitem.hxx index 1078a055b769..e8ad29dbc625 100644 --- a/include/editeng/nhypitem.hxx +++ b/include/editeng/nhypitem.hxx @@ -26,7 +26,9 @@ class EDITENG_DLLPUBLIC SvxNoHyphenItem final : public SfxBoolItem { public: - SvxNoHyphenItem( const sal_uInt16 nId ); + static SfxPoolItem* CreateDefault(); + + SvxNoHyphenItem( const bool bNoHyphen, const sal_uInt16 nId ); // "pure virtual Methods" from SfxPoolItem virtual SvxNoHyphenItem* Clone( SfxItemPool *pPool = nullptr ) const override; diff --git a/sw/qa/uitest/writer_tests2/formatCharacter.py b/sw/qa/uitest/writer_tests2/formatCharacter.py index 86efd640dbb4..265f7b4df92e 100644 --- a/sw/qa/uitest/writer_tests2/formatCharacter.py +++ b/sw/qa/uitest/writer_tests2/formatCharacter.py @@ -188,6 +188,8 @@ class formatCharacter(UITestCase): xKerning = xDialog.getChild("kerningsb") xPairKerning = xDialog.getChild("pairkerning") xFitToLine = xDialog.getChild("fittoline") + xNoHyphenation = xDialog.getChild("nohyphenation") + self.assertEqual(get_state_as_dict(xNoHyphenation)["Selected"], "false") xSuperscript.executeAction("CLICK", tuple()) xRelFontSize.executeAction("UP", tuple()) @@ -196,6 +198,7 @@ class formatCharacter(UITestCase): xKerning.executeAction("UP", tuple()) xPairKerning.executeAction("CLICK", tuple()) xFitToLine.executeAction("CLICK", tuple()) + xNoHyphenation.executeAction("CLICK", tuple()) with self.ui_test.execute_dialog_through_command(".uno:FontDialog", close_button="cancel") as xDialog: @@ -208,6 +211,7 @@ class formatCharacter(UITestCase): xKerning = xDialog.getChild("kerningsb") xPairKerning = xDialog.getChild("pairkerning") xFitToLine = xDialog.getChild("fittoline") + xNoHyphenation = xDialog.getChild("nohyphenation") self.assertEqual(get_state_as_dict(xSuperscript)["Checked"], "true") self.assertEqual(get_state_as_dict(x90deg)["Checked"], "true") @@ -215,6 +219,7 @@ class formatCharacter(UITestCase): self.assertEqual(get_state_as_dict(xKerning)["Text"], "0.1 pt") self.assertEqual(get_state_as_dict(xPairKerning)["Selected"], "false") self.assertEqual(get_state_as_dict(xFitToLine)["Selected"], "true") + self.assertEqual(get_state_as_dict(xNoHyphenation)["Selected"], "true") diff --git a/sw/qa/uitest/writer_tests8/tdf106733.py b/sw/qa/uitest/writer_tests8/tdf106733.py index 201bba1c56ff..ade3c0da5440 100644 --- a/sw/qa/uitest/writer_tests8/tdf106733.py +++ b/sw/qa/uitest/writer_tests8/tdf106733.py @@ -11,6 +11,7 @@ from uitest.uihelper.common import get_state_as_dict, get_url_for_data_file from libreoffice.uno.propertyvalue import mkPropertyValues from libreoffice.linguistic.linguservice import get_lingu_service_manager from com.sun.star.lang import Locale +from uitest.uihelper.common import select_pos # handle tdf#106733 hyphenation of words disabled by character formatting @@ -110,3 +111,80 @@ class tdf106733(UITestCase): # This was False (the line started with "cept", because of the enabled hyphenation) self.assertEqual(True, para1.String.startswith(" except")) + def set_format_character(self, check): + with self.ui_test.execute_dialog_through_command(".uno:FontDialog") as xDialog: + xTabs = xDialog.getChild("tabcontrol") + select_pos(xTabs, "2") + xNoHyphenation = xDialog.getChild("nohyphenation") + checkValues = { False: "false", True: "true" } + self.assertEqual(get_state_as_dict(xNoHyphenation)["Selected"], checkValues[check]) + xNoHyphenation.executeAction("CLICK", tuple()) + self.assertEqual(get_state_as_dict(xNoHyphenation)["Selected"], checkValues[not check]) + + def test_tdf106733_format_character(self): + supported_locale = self.is_supported_locale("en", "US") + if not supported_locale: + self.skipTest("no hyphenation patterns for en_US available") + + with self.ui_test.load_file(get_url_for_data_file("tdf106733.fodt")) as writer_doc: + + # enable NoHyphenation on first "except" + for i in range(38): + self.xUITest.executeCommand(".uno:GoToNextWord") + self.xUITest.executeCommand(".uno:WordRightSel") + self.set_format_character(False) + + # disable NoHyphenation on second "except" + for i in range(50): + self.xUITest.executeCommand(".uno:GoToNextWord") + self.xUITest.executeCommand(".uno:GoRight") + self.xUITest.executeCommand(".uno:SelectWord") + self.set_format_character(True) + + # disable NoHyphenation on third "except" set by its character style + for i in range(51): + self.xUITest.executeCommand(".uno:GoToNextWord") + self.xUITest.executeCommand(".uno:GoRight") + self.xUITest.executeCommand(".uno:SelectWord") + + # remove character style "Strong Emphasis" to disable NoHyphenation + self.xUITest.executeCommand(".uno:StyleApply?Style:string=No%20Character%20Style&FamilyName:string=CharacterStyles") + # and restore the bold formatting to hyphenate the last word + self.xUITest.executeCommand('.uno:Bold') + + self.xUITest.executeCommand('.uno:GoToStartOfDoc') + + self.set_custom_hyphenation() + # delete the text of the first line + for i in range(5): + self.xUITest.executeCommand(".uno:GoDown") + self.xUITest.executeCommand(".uno:GoToEndOfLine") + self.xUITest.executeCommand('.uno:StartOfDocumentSel') + self.xUITest.executeCommand('.uno:Delete') + paragraphs = writer_doc.Text.createEnumeration() + para1 = paragraphs.nextElement() + # disabled hyphenation on the first "except" + self.assertEqual(True, para1.String.startswith(" except")) + + # check disabled hyphenations (by direct character formatting) + for i in range(6): + self.xUITest.executeCommand(".uno:GoDown") + self.xUITest.executeCommand(".uno:GoToEndOfLine") + self.xUITest.executeCommand('.uno:StartOfDocumentSel') + self.xUITest.executeCommand('.uno:Delete') + paragraphs = writer_doc.Text.createEnumeration() + para1 = paragraphs.nextElement() + # enabled hyphenation on the second "except" + self.assertEqual(True, para1.String.startswith("cept")) + + # check disabled hyphenations (by character style) + for i in range(6): + self.xUITest.executeCommand(".uno:GoDown") + self.xUITest.executeCommand(".uno:GoToEndOfLine") + self.xUITest.executeCommand('.uno:StartOfDocumentSel') + self.xUITest.executeCommand('.uno:Delete') + paragraphs = writer_doc.Text.createEnumeration() + para1 = paragraphs.nextElement() + # enabled hyphenation on the third "except" + self.assertEqual(True, para1.String.startswith("cept")) + diff --git a/sw/source/core/access/AccessibilityCheck.cxx b/sw/source/core/access/AccessibilityCheck.cxx index f1ec39b05949..5d98f7bd731e 100644 --- a/sw/source/core/access/AccessibilityCheck.cxx +++ b/sw/source/core/access/AccessibilityCheck.cxx @@ -699,6 +699,11 @@ public: case RES_CHRATR_CONTOUR: sFormattingType = "Outline"; break; + + case RES_CHRATR_NOHYPHEN: + sFormattingType = "No Hyphenation"; + break; + default: break; } @@ -758,6 +763,7 @@ public: || aSwAttrSet.GetItem(RES_CHRATR_OVERLINE, false) || aSwAttrSet.GetItem(RES_CHRATR_CROSSEDOUT, false) || aSwAttrSet.GetItem(RES_CHRATR_RELIEF, false) + || aSwAttrSet.GetItem(RES_CHRATR_NOHYPHEN, false) || aSwAttrSet.GetItem(RES_CHRATR_CONTOUR, false)) { auto pIssue diff --git a/sw/source/core/bastyp/init.cxx b/sw/source/core/bastyp/init.cxx index 0fe5cb710857..d453c2ddc9a5 100644 --- a/sw/source/core/bastyp/init.cxx +++ b/sw/source/core/bastyp/init.cxx @@ -483,7 +483,7 @@ void InitCore() aAttrTab[ RES_CHRATR_WORDLINEMODE- POOLATTR_BEGIN ] = new SvxWordLineModeItem( false, RES_CHRATR_WORDLINEMODE ); aAttrTab[ RES_CHRATR_AUTOKERN- POOLATTR_BEGIN ] = new SvxAutoKernItem( false, RES_CHRATR_AUTOKERN ); aAttrTab[ RES_CHRATR_BLINK - POOLATTR_BEGIN ] = new SvxBlinkItem( false, RES_CHRATR_BLINK ); - aAttrTab[ RES_CHRATR_NOHYPHEN - POOLATTR_BEGIN ] = new SvxNoHyphenItem( RES_CHRATR_NOHYPHEN ); + aAttrTab[ RES_CHRATR_NOHYPHEN - POOLATTR_BEGIN ] = new SvxNoHyphenItem( false, RES_CHRATR_NOHYPHEN ); aAttrTab[ RES_CHRATR_UNUSED2- POOLATTR_BEGIN ] = new SfxVoidItem( RES_CHRATR_UNUSED2 ); aAttrTab[ RES_CHRATR_BACKGROUND - POOLATTR_BEGIN ] = new SvxBrushItem( RES_CHRATR_BACKGROUND ); diff --git a/sw/source/core/layout/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx index aa108e16b6ec..0cda5673219e 100644 --- a/sw/source/core/layout/wsfrm.cxx +++ b/sw/source/core/layout/wsfrm.cxx @@ -2590,6 +2590,7 @@ void SwContentFrame::UpdateAttr_( const SfxPoolItem* pOld, const SfxPoolItem* pN case RES_CHRATR_FONTSIZE: case RES_CHRATR_ESCAPEMENT: case RES_CHRATR_CONTOUR: + case RES_CHRATR_NOHYPHEN: case RES_PARATR_NUMRULE: rInvFlags |= SwContentFrameInvFlags::SetCompletePaint; break; diff --git a/sw/source/core/unocore/unomap.cxx b/sw/source/core/unocore/unomap.cxx index c8b1c1ed26be..1565ce7ea8c2 100644 --- a/sw/source/core/unocore/unomap.cxx +++ b/sw/source/core/unocore/unomap.cxx @@ -1495,6 +1495,8 @@ std::span<const SfxItemPropertyMapEntry> SwUnoPropertyMapProvider::GetPropertyMa { UNO_NAME_CHAR_SHADOWED, RES_CHRATR_SHADOWED, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 }, // SvxContouredItem { UNO_NAME_CHAR_CONTOURED, RES_CHRATR_CONTOUR, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 }, + // SvxNoHyphenItem + { UNO_NAME_CHAR_NO_HYPHENATION, RES_CHRATR_NOHYPHEN, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 }, // SvxCrossedOutItem { UNO_NAME_CHAR_STRIKEOUT, RES_CHRATR_CROSSEDOUT, cppu::UnoType<sal_Int16>::get(),PropertyAttribute::MAYBEVOID, MID_CROSS_OUT }, // SvxUnderlineItem diff --git a/sw/source/core/unocore/unomapproperties.hxx b/sw/source/core/unocore/unomapproperties.hxx index 2330ba8608e6..6d7af3e735bd 100644 --- a/sw/source/core/unocore/unomapproperties.hxx +++ b/sw/source/core/unocore/unomapproperties.hxx @@ -543,6 +543,8 @@ { UNO_NAME_PARA_LINE_SPACING, RES_PARATR_LINESPACING, cppu::UnoType<css::style::LineSpacing>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS}, \ { UNO_NAME_PARA_RIGHT_MARGIN, RES_MARGIN_RIGHT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_R_MARGIN|CONVERT_TWIPS}, \ { UNO_NAME_TABSTOPS, RES_PARATR_TABSTOP, cppu::UnoType< cppu::UnoSequenceType<css::style::TabStop> >::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS}, \ + { UNO_NAME_CHAR_NO_HYPHENATION, RES_CHRATR_NOHYPHEN, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, \ + #define FILL_PROPERTIES_SW_BMP \ { UNO_NAME_FILLBMP_LOGICAL_SIZE, XATTR_FILLBMP_SIZELOG, cppu::UnoType<bool>::get(), 0, 0}, \