officecfg/registry/data/org/openoffice/Office/UI/WriterCommands.xcu | 8 + sw/inc/cmdid.h | 3 sw/inc/swcrsr.hxx | 2 sw/sdi/_textsh.sdi | 6 + sw/sdi/swriter.sdi | 17 ++++ sw/source/core/crsr/swcrsr.cxx | 15 +++ sw/source/core/inc/txtfrm.hxx | 2 sw/source/core/text/atrstck.cxx | 24 +++++ sw/source/core/text/frmcrsr.cxx | 42 ++++++++++ sw/source/uibase/shells/txtattr.cxx | 26 ++++++ sw/uiconfig/swriter/popupmenu/table.xml | 2 sw/uiconfig/swriter/popupmenu/text.xml | 2 12 files changed, 147 insertions(+), 2 deletions(-)
New commits: commit 2f0c7d5691acd4010443856788a54b0abc03098b Author: László Németh <nem...@numbertext.org> AuthorDate: Thu Jun 13 20:25:57 2024 +0200 Commit: László Németh <nem...@numbertext.org> CommitDate: Fri Jun 14 10:10:38 2024 +0200 tdf#161563 tdf#161565 sw: add No Break to word context menu & visualize Add No Break option to context menu of words hyphenated automatically, giving as easy access to fix paragraph layout, as context menu of misspelled words – like DTP software do. Also add this option to context menu of words with enabled "No Break" to disable it. To avoid unwanted paragraph layout during further text editing or formatting, visualize words excluded from hyphenation with a light gray dotted underline, when Formatting Marks is enabled. Follow-up to commit b5e275f47a54bd7fee39dad516a433fde5be872d "tdf#106733 sw: implement CharNoHyphenation" and commit 73bd04a71e741788a2f2f3b26cc46ddb6a361372 "tdf#106733 xmloff: keep fo:hyphenate in character formatting". Change-Id: I81bb410abcf999c8d9a3dca28acfc5c21aa0f260 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/168827 Tested-by: Jenkins Reviewed-by: László Németh <nem...@numbertext.org> diff --git a/officecfg/registry/data/org/openoffice/Office/UI/WriterCommands.xcu b/officecfg/registry/data/org/openoffice/Office/UI/WriterCommands.xcu index b383eb189ec6..c0fdc8887338 100644 --- a/officecfg/registry/data/org/openoffice/Office/UI/WriterCommands.xcu +++ b/officecfg/registry/data/org/openoffice/Office/UI/WriterCommands.xcu @@ -3039,6 +3039,14 @@ <value>9</value> </prop> </node> + <node oor:name=".uno:NoBreak" oor:op="replace"> + <prop oor:name="Label" oor:type="xs:string"> + <value xml:lang="en-US">No Break</value> + </prop> + <prop oor:name="Properties" oor:type="xs:int"> + <value>8</value> + </prop> + </node> <node oor:name=".uno:VScroll" oor:op="replace"> <prop oor:name="Label" oor:type="xs:string"> <value xml:lang="en-US">Vertical Scroll Bar</value> diff --git a/sw/inc/cmdid.h b/sw/inc/cmdid.h index 2a7df327eca6..c99e8da6d3f7 100644 --- a/sw/inc/cmdid.h +++ b/sw/inc/cmdid.h @@ -526,7 +526,8 @@ class SwUINumRuleItem; // Region: Extras #define FN_LINE_NUMBERING_DLG (FN_EXTRA + 2 ) /* */ #define FN_HYPHENATE_OPT_DLG (FN_EXTRA + 5 ) /* */ -#define FN_ADD_UNKNOWN (FN_EXTRA + 6 ) /* learn words */ +#define FN_NO_BREAK (FN_EXTRA + 6 ) /* do not hyphenate */ +#define FN_ADD_UNKNOWN (FN_EXTRA + 7 ) /* learn words */ #define FN_NUMBERING_OUTLINE_DLG (FN_EXTRA + 12) /* */ #define FN_SORTING_DLG (FN_EXTRA + 14) /* */ #define FN_CALCULATE (FN_EXTRA + 15) /* */ diff --git a/sw/inc/swcrsr.hxx b/sw/inc/swcrsr.hxx index 33b5ea8d226c..2b424db27de0 100644 --- a/sw/inc/swcrsr.hxx +++ b/sw/inc/swcrsr.hxx @@ -223,6 +223,8 @@ public: const SwCursor* GetNext() const { return dynamic_cast<SwCursor const *>(GetNextInRing()); } SwCursor* GetPrev() { return dynamic_cast<SwCursor *>(GetPrevInRing()); } const SwCursor* GetPrev() const { return dynamic_cast<SwCursor const *>(GetPrevInRing()); } + + bool IsInHyphenatedWord( SwRootFrame const& rLayout ) const; }; /** diff --git a/sw/sdi/_textsh.sdi b/sw/sdi/_textsh.sdi index 4c5a48b7d697..fa5809bd29c0 100644 --- a/sw/sdi/_textsh.sdi +++ b/sw/sdi/_textsh.sdi @@ -1412,6 +1412,12 @@ interface BaseText StateMethod = GetTextCtrlState; DisableFlags="SfxDisableFlags::SwOnProtectedCursor"; ] + FN_NO_BREAK // status(final|play) + [ + ExecMethod = ExecCharAttr; + StateMethod = GetAttrState ; + DisableFlags="SfxDisableFlags::SwOnProtectedCursor"; + ] SID_ATTR_CHAR_RELIEF [ ExecMethod = ExecTextCtrl ; diff --git a/sw/sdi/swriter.sdi b/sw/sdi/swriter.sdi index 03c949ad6ae9..ed49dc200ed9 100644 --- a/sw/sdi/swriter.sdi +++ b/sw/sdi/swriter.sdi @@ -2415,6 +2415,23 @@ SfxVoidItem Hyphenate FN_HYPHENATE_OPT_DLG GroupId = SfxGroupId::Options; ] +SfxVoidItem NoBreak FN_NO_BREAK +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = FALSE, + Toggle = TRUE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::Format; +] + SfxVoidItem IncrementIndentValue FN_INC_INDENT_OFFSET () [ diff --git a/sw/source/core/crsr/swcrsr.cxx b/sw/source/core/crsr/swcrsr.cxx index d0584fc97887..3a986d37ddc0 100644 --- a/sw/source/core/crsr/swcrsr.cxx +++ b/sw/source/core/crsr/swcrsr.cxx @@ -2368,6 +2368,21 @@ void SwCursor::RestoreSavePos() GetPoint()->SetContent( nIdx ); } +bool SwCursor::IsInHyphenatedWord(SwRootFrame const& rLayout) const +{ + bool bRet = false; + Point aPt; + std::pair<Point, bool> const tmp(aPt, true); + SwContentFrame const*const pFrame = GetPointContentNode()->getLayoutFrame( + &rLayout, GetPoint(), &tmp); + if( pFrame && pFrame->IsTextFrame() ) + { + SwPaM aPam( *GetPoint(), *GetMark() ); + bRet = static_cast<SwTextFrame const*>(pFrame)->IsInHyphenatedWord( &aPam, HasMark() ); + } + return bRet; +} + SwTableCursor::SwTableCursor( const SwPosition &rPos ) : SwCursor( rPos, nullptr ) { diff --git a/sw/source/core/inc/txtfrm.hxx b/sw/source/core/inc/txtfrm.hxx index 2ce060b5cf8a..8d9892b0d0c0 100644 --- a/sw/source/core/inc/txtfrm.hxx +++ b/sw/source/core/inc/txtfrm.hxx @@ -508,6 +508,8 @@ public: * @return found */ bool Hyphenate(SwInterHyphInfoTextFrame & rInf); + /// Is a hyphenated word? At selection, Point can be at the end of the word + virtual bool IsInHyphenatedWord(SwPaM *, bool bSelection) const; /// Test grow inline SwTwips GrowTst( const SwTwips nGrow ); diff --git a/sw/source/core/text/atrstck.cxx b/sw/source/core/text/atrstck.cxx index 82a3da7fbf17..eafce865e9ed 100644 --- a/sw/source/core/text/atrstck.cxx +++ b/sw/source/core/text/atrstck.cxx @@ -42,6 +42,7 @@ #include <editeng/twolinesitem.hxx> #include <editeng/charhiddenitem.hxx> #include <editeng/boxitem.hxx> +#include <editeng/nhypitem.hxx> #include <editeng/shaditem.hxx> #include <viewopt.hxx> #include <charfmt.hxx> @@ -629,8 +630,16 @@ void SwAttrHandler::FontChg(const SfxPoolItem& rItem, SwFont& rFnt, bool bPush ) CharFormat::GetItem( *pTopAt, RES_CHRATR_HIDDEN ) : m_pDefaultArray[ nStackPos ]; + const sal_uInt16 nStackPos2 = StackPos[ RES_CHRATR_NOHYPHEN ]; + const SwTextAttr* pTopAt2 = GetTop(nStackPos2); + + const SfxPoolItem* pTmpItem2 = pTopAt2 ? + CharFormat::GetItem( *pTopAt2, RES_CHRATR_NOHYPHEN ) : + m_pDefaultArray[ nStackPos2 ]; + if ((m_pShell && !m_pShell->GetWin()) || - (pTmpItem && !pTmpItem->StaticWhichCast(RES_CHRATR_HIDDEN).GetValue()) ) + (pTmpItem && !pTmpItem->StaticWhichCast(RES_CHRATR_HIDDEN).GetValue()) || + (pTmpItem2 && !pTmpItem2->StaticWhichCast(RES_CHRATR_NOHYPHEN).GetValue()) ) { rFnt.SetUnderline( rItem.StaticWhichCast(RES_CHRATR_UNDERLINE).GetLineStyle() ); rFnt.SetUnderColor( rItem.StaticWhichCast(RES_CHRATR_UNDERLINE).GetColor() ); @@ -685,6 +694,19 @@ void SwAttrHandler::FontChg(const SfxPoolItem& rItem, SwFont& rFnt, bool bPush ) case RES_CHRATR_HIGHLIGHT : rFnt.SetHighlightColor( rItem.StaticWhichCast(RES_CHRATR_HIGHLIGHT).GetColor() ); break; + case RES_CHRATR_NOHYPHEN : + if ( m_pShell && m_pShell->GetWin() && + m_pShell->GetViewOptions()->IsShowHiddenChar() ) + { + if ( rItem.StaticWhichCast(RES_CHRATR_NOHYPHEN).GetValue() ) + { + rFnt.SetUnderline( LINESTYLE_DOTTED ); + rFnt.SetUnderColor( COL_LIGHTGRAY ); + } + else + ActivateTop( rFnt, RES_CHRATR_UNDERLINE ); + } + break; case RES_CHRATR_CJK_FONT : { auto& rFontItem = rItem.StaticWhichCast(RES_CHRATR_CJK_FONT); diff --git a/sw/source/core/text/frmcrsr.cxx b/sw/source/core/text/frmcrsr.cxx index d01f9e20eb92..882573f7cb96 100644 --- a/sw/source/core/text/frmcrsr.cxx +++ b/sw/source/core/text/frmcrsr.cxx @@ -697,6 +697,48 @@ bool SwTextFrame::LeftMargin(SwPaM *pPam) const return true; } +bool SwTextFrame::IsInHyphenatedWord(SwPaM *pPam, bool bSelection) const +{ + assert(GetMergedPara() || &pPam->GetPointNode() == static_cast<SwContentNode const*>(GetDep())); + + SwTextFrame *pFrame = GetAdjFrameAtPos( const_cast<SwTextFrame*>(this), *pPam->GetPoint(), + SwTextCursor::IsRightMargin() ); + pFrame->GetFormatted(); + if (!IsEmpty()) + { + SwTextSizeInfo aInf( pFrame ); + SwTextCursor aLine( pFrame, &aInf ); + TextFrameIndex const nCursorPos(MapModelToViewPos(*pPam->GetPoint())); + aLine.CharCursorToLine(nCursorPos); + if ( aLine.GetCurr()->IsEndHyph() ) + { + TextFrameIndex nPos(aLine.GetStart() + aLine.GetCurr()->GetLen()); + while( nPos > nCursorPos && ' ' != aInf.GetText()[sal_Int32(nPos) - 1] ) + --nPos; + if ( nPos == nCursorPos && ( bSelection || + // without selection, the cursor must be inside the word, not before that + // to apply the character formatting, as usual + ( nPos > aLine.GetStart() && ' ' != aInf.GetText()[sal_Int32(nPos) - 1] ) ) ) + return true; + } + // the hyphenated word starts in the previous line + if ( aLine.GetStart() > TextFrameIndex(0) ) + { + TextFrameIndex nPos(aLine.GetStart()); + aLine.CharCursorToLine(nPos - TextFrameIndex(1)); + if ( aLine.GetCurr()->IsEndHyph() ) + { + while( nPos < nCursorPos && ' ' != aInf.GetText()[sal_Int32(nPos)] ) + ++nPos; + if ( nPos == nCursorPos && + ( bSelection || ' ' != aInf.GetText()[sal_Int32(nPos)] ) ) + return true; + } + } + } + return false; +} + /* * To the line end: That's the position before the last char of the line. * Exception: In the last line, it should be able to place the cursor after diff --git a/sw/source/uibase/shells/txtattr.cxx b/sw/source/uibase/shells/txtattr.cxx index 1fee6fde1987..119f4618a120 100644 --- a/sw/source/uibase/shells/txtattr.cxx +++ b/sw/source/uibase/shells/txtattr.cxx @@ -37,6 +37,7 @@ #include <editeng/scripttypeitem.hxx> #include <editeng/frmdiritem.hxx> #include <editeng/cmapitem.hxx> +#include <editeng/nhypitem.hxx> #include <osl/diagnose.h> #include <paratr.hxx> @@ -191,6 +192,13 @@ void SwTextShell::ExecCharAttr(SfxRequest &rReq) if( !rSh.HasReadonlySel() && rSh.IsEndPara()) rSh.DontExpandFormat(); break; + case FN_NO_BREAK: + { + bool bNoHyphen = aSet.Get(RES_CHRATR_NOHYPHEN).GetValue(); + SvxNoHyphenItem aNoHyphen( !bNoHyphen, RES_CHRATR_NOHYPHEN ); + rSh.SetAttrItem( aNoHyphen ); + } + break; default: OSL_FAIL("wrong dispatcher"); return; @@ -827,7 +835,25 @@ void SwTextShell::GetAttrState(SfxItemSet &rSet) nSlot = 0; } break; + case FN_NO_BREAK: + { + SfxItemSetFixed<RES_CHRATR_NOHYPHEN, RES_CHRATR_NOHYPHEN> aSet(GetPool()); + rSh.GetCurAttr(aSet); + const SfxPoolItem& rItem = aSet.Get(RES_CHRATR_NOHYPHEN); + SwWrtShell& rWrtSh = GetShell(); + // add "No Break" menu item to the context menu, if the word + // has "no break" setting, or it is hyphenated + if ( static_cast<const SvxNoHyphenItem&>(rItem).GetValue() || ( rWrtSh.GetCursor() + && rWrtSh.GetCursor()->IsInHyphenatedWord(*rWrtSh.GetLayout()) ) ) + { + rSet.Put(rItem); + } + else + rSet.DisableItem(nSlot); + nSlot = 0; + } + break; default: // Do nothing nSlot = 0; diff --git a/sw/uiconfig/swriter/popupmenu/table.xml b/sw/uiconfig/swriter/popupmenu/table.xml index 791312faa27d..c40c3f7ad193 100644 --- a/sw/uiconfig/swriter/popupmenu/table.xml +++ b/sw/uiconfig/swriter/popupmenu/table.xml @@ -8,6 +8,8 @@ * --> <menu:menupopup xmlns:menu="http://openoffice.org/2001/menu"> + <menu:menuitem menu:id=".uno:NoBreak"/> + <menu:menuseparator/> <menu:menuitem menu:id=".uno:Cut"/> <menu:menuitem menu:id=".uno:Copy"/> <menu:menuitem menu:id=".uno:Paste"/> diff --git a/sw/uiconfig/swriter/popupmenu/text.xml b/sw/uiconfig/swriter/popupmenu/text.xml index 75ccb4e6db14..4c68f99524f2 100644 --- a/sw/uiconfig/swriter/popupmenu/text.xml +++ b/sw/uiconfig/swriter/popupmenu/text.xml @@ -8,6 +8,8 @@ * --> <menu:menupopup xmlns:menu="http://openoffice.org/2001/menu"> + <menu:menuitem menu:id=".uno:NoBreak"/> + <menu:menuseparator/> <menu:menuitem menu:id=".uno:Cut"/> <menu:menuitem menu:id=".uno:Copy"/> <menu:menuitem menu:id=".uno:Paste"/>