sw/qa/extras/uiwriter/uiwriter9.cxx | 27 ++++++++ sw/source/uibase/shells/textsh1.cxx | 115 +++++++++++++++++++++++++++++------- 2 files changed, 122 insertions(+), 20 deletions(-)
New commits: commit 41452912e8ec11ecfead11588e7c70ad08903fd9 Author: Justin Luth <jl...@mail.com> AuthorDate: Sat Dec 23 21:20:04 2023 -0500 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Wed Jan 3 08:49:35 2024 +0100 tdf#111969 sw: right-click menu recognizes start of hyperlink Prior to these fixes, the right-click building a menu for a hyperlink started half-a-character to late, because it doesn't activate at the start of a hyperlink, since a start == end cursor doesn't register character attributes. So this kind of fix may be needed for any kind of field or similar attribute that uses GetContentAtPos that interacts with the mouse cursor/menus etc. make CppunitTest_sw_uiwriter9 CPPUNIT_TEST_NAME=testTdf158785 Change-Id: I735c0085cb9658d299fe8896dfe1507bbe115667 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/161265 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/sw/qa/extras/uiwriter/uiwriter9.cxx b/sw/qa/extras/uiwriter/uiwriter9.cxx index a22ce3df306e..ef48220bc983 100644 --- a/sw/qa/extras/uiwriter/uiwriter9.cxx +++ b/sw/qa/extras/uiwriter/uiwriter9.cxx @@ -66,6 +66,33 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf158785) aLogicR.AdjustX(1); pWrtShell->GetContentAtPos(aLogicR, aContentAtPos); CPPUNIT_ASSERT_EQUAL(IsAttrAtPos::NONE, aContentAtPos.eContentAtPos); + + /* + * tdf#111969: the beginning of the hyperlink should allow the right-click menu to remove it + */ + // move cursor (with no selection) to the start of the hyperlink - after the N-dash + pWrtShell->SttEndDoc(/*bStart=*/true); + pWrtShell->Right(SwCursorSkipMode::Chars, /*bSelect=*/false, 1, /*bBasicCall=*/false); + aLogicL = pWrtShell->GetCharRect().Center(); + aLogicR = aLogicL; + + // sanity check - we really are right in front of the hyperlink + aLogicL.AdjustX(-1); + aContentAtPos = IsAttrAtPos::InetAttr; + pWrtShell->GetContentAtPos(aLogicL, aContentAtPos); + CPPUNIT_ASSERT_EQUAL(IsAttrAtPos::NONE, aContentAtPos.eContentAtPos); + aLogicR.AdjustX(1); + aContentAtPos = IsAttrAtPos::InetAttr; + pWrtShell->GetContentAtPos(aLogicR, aContentAtPos); + CPPUNIT_ASSERT_EQUAL(IsAttrAtPos::InetAttr, aContentAtPos.eContentAtPos); + + // Remove the hyperlink + dispatchCommand(mxComponent, ".uno:RemoveHyperlink", {}); + + // The test: was the hyperlink actually removed? + aContentAtPos = IsAttrAtPos::InetAttr; + pWrtShell->GetContentAtPos(aLogicR, aContentAtPos); + CPPUNIT_ASSERT_EQUAL(IsAttrAtPos::NONE, aContentAtPos.eContentAtPos); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest9, testTdf111969) diff --git a/sw/source/uibase/shells/textsh1.cxx b/sw/source/uibase/shells/textsh1.cxx index 23e968ffd524..f23d5a229ec9 100644 --- a/sw/source/uibase/shells/textsh1.cxx +++ b/sw/source/uibase/shells/textsh1.cxx @@ -74,7 +74,9 @@ #include <cellatr.hxx> #include <edtwin.hxx> #include <fldmgr.hxx> +#include <ndtxt.hxx> #include <strings.hrc> +#include <txatbase.hxx> #include <paratr.hxx> #include <vcl/svapp.hxx> #include <sfx2/app.hxx> @@ -1389,7 +1391,27 @@ void SwTextShell::Execute(SfxRequest &rReq) } break; case SID_EDIT_HYPERLINK: + { + if (!rWrtSh.HasSelection()) + { + SfxItemSetFixed<RES_TXTATR_INETFMT, RES_TXTATR_INETFMT> aSet(GetPool()); + rWrtSh.GetCurAttr(aSet); + if (SfxItemState::SET > aSet.GetItemState(RES_TXTATR_INETFMT)) + { + // Didn't find a hyperlink to edit yet. + + // If the cursor is just before an unselected hyperlink, + // the dialog will not know that it should edit that hyperlink, + // so in this case, first select it so the dialog will find the hyperlink. + // The dialog would leave the hyperlink selected anyway after a successful edit + // (although it isn't normally selected after a cancel, but oh well). + if (!rWrtSh.SelectTextAttr(RES_TXTATR_INETFMT)) + break; + } + } + GetView().GetViewFrame().SetChildWindow(SID_HYPERLINK_DIALOG, true); + } break; case SID_REMOVE_HYPERLINK: { @@ -1837,17 +1859,32 @@ void SwTextShell::Execute(SfxRequest &rReq) { SfxItemSetFixed<RES_TXTATR_INETFMT, RES_TXTATR_INETFMT> aSet(GetPool()); rWrtSh.GetCurAttr(aSet); + + const SwFormatINetFormat* pINetFormat = nullptr; if(SfxItemState::SET <= aSet.GetItemState( RES_TXTATR_INETFMT )) + pINetFormat = &aSet.Get(RES_TXTATR_INETFMT); + else if (!rWrtSh.HasSelection()) { - const SwFormatINetFormat& rINetFormat = aSet.Get(RES_TXTATR_INETFMT); + // is the cursor at the beginning of a hyperlink? + const SwTextNode* pTextNd = rWrtSh.GetCursor()->GetPointNode().GetTextNode(); + if (pTextNd) + { + const sal_Int32 nIndex = rWrtSh.GetCursor()->Start()->GetContentIndex(); + const SwTextAttr* pINetFmt = pTextNd->GetTextAttrAt(nIndex, RES_TXTATR_INETFMT); + if (pINetFmt && !pINetFmt->GetINetFormat().GetValue().isEmpty()) + pINetFormat = &pINetFmt->GetINetFormat(); + } + } + if (pINetFormat) + { if (nSlot == SID_OPEN_HYPERLINK) { - rWrtSh.ClickToINetAttr(rINetFormat); + rWrtSh.ClickToINetAttr(*pINetFormat); } else if (nSlot == SID_COPY_HYPERLINK_LOCATION) { - OUString hyperlinkLocation = rINetFormat.GetValue(); + OUString hyperlinkLocation = pINetFormat->GetValue(); ::uno::Reference< datatransfer::clipboard::XClipboard > xClipboard = GetView().GetEditWin().GetClipboard(); vcl::unohelper::TextDataObject::CopyStringTo(hyperlinkLocation, xClipboard, SfxViewShell::Current()); } @@ -2541,26 +2578,53 @@ void SwTextShell::GetState( SfxItemSet &rSet ) case SID_EDIT_HYPERLINK: { - SfxItemSetFixed<RES_TXTATR_INETFMT, RES_TXTATR_INETFMT> aSet(GetPool()); - rSh.GetCurAttr(aSet); - if(SfxItemState::SET > aSet.GetItemState( RES_TXTATR_INETFMT ) || rSh.HasReadonlySel()) + if (!rSh.HasReadonlySel()) { - rSet.DisableItem(nWhich); + SfxItemSetFixed<RES_TXTATR_INETFMT, RES_TXTATR_INETFMT> aSet(GetPool()); + rSh.GetCurAttr(aSet); + if (SfxItemState::SET <= aSet.GetItemState(RES_TXTATR_INETFMT)) + break; + + // is the cursor at the beginning of a hyperlink? + const SwTextNode* pTextNd = rSh.GetCursor()->GetPointNode().GetTextNode(); + if (pTextNd && !rSh.HasSelection()) + { + const sal_Int32 nIndex = rSh.GetCursor()->Start()->GetContentIndex(); + const SwTextAttr* pINetFmt + = pTextNd->GetTextAttrAt(nIndex, RES_TXTATR_INETFMT); + if (pINetFmt && !pINetFmt->GetINetFormat().GetValue().isEmpty()) + break; + } } + rSet.DisableItem(nWhich); } break; case SID_REMOVE_HYPERLINK: { - SfxItemSetFixed<RES_TXTATR_INETFMT, RES_TXTATR_INETFMT> aSet(GetPool()); - rSh.GetCurAttr(aSet); - - // If a hyperlink is selected, either alone or along with other text... - if ((aSet.GetItemState(RES_TXTATR_INETFMT) < SfxItemState::SET && - aSet.GetItemState(RES_TXTATR_INETFMT) != SfxItemState::DONTCARE) || - rSh.HasReadonlySel()) + if (!rSh.HasReadonlySel()) { - rSet.DisableItem(nWhich); + SfxItemSetFixed<RES_TXTATR_INETFMT, RES_TXTATR_INETFMT> aSet(GetPool()); + rSh.GetCurAttr(aSet); + + // If a hyperlink is selected, either alone or along with other text... + if (SfxItemState::SET <= aSet.GetItemState(RES_TXTATR_INETFMT) + || aSet.GetItemState(RES_TXTATR_INETFMT) == SfxItemState::DONTCARE) + { + break; + } + + // is the cursor at the beginning of a hyperlink? + const SwTextNode* pTextNd = rSh.GetCursor()->GetPointNode().GetTextNode(); + if (pTextNd && !rSh.HasSelection()) + { + const sal_Int32 nIndex = rSh.GetCursor()->Start()->GetContentIndex(); + const SwTextAttr* pINetFmt + = pTextNd->GetTextAttrAt(nIndex, RES_TXTATR_INETFMT); + if (pINetFmt && !pINetFmt->GetINetFormat().GetValue().isEmpty()) + break; + } } + rSet.DisableItem(nWhich); } break; case SID_TRANSLITERATE_HALFWIDTH: @@ -2594,8 +2658,19 @@ void SwTextShell::GetState( SfxItemSet &rSet ) { SfxItemSetFixed<RES_TXTATR_INETFMT, RES_TXTATR_INETFMT> aSet(GetPool()); rSh.GetCurAttr(aSet); + if (SfxItemState::SET <= aSet.GetItemState(RES_TXTATR_INETFMT, false)) + break; + + // is the cursor at the beginning of a hyperlink? + const SwTextNode* pTextNd = rSh.GetCursor()->GetPointNode().GetTextNode(); + if (pTextNd && !rSh.HasSelection()) + { + const sal_Int32 nIndex = rSh.GetCursor()->Start()->GetContentIndex(); + const SwTextAttr* pINetFmt = pTextNd->GetTextAttrAt(nIndex, RES_TXTATR_INETFMT); + if (pINetFmt && !pINetFmt->GetINetFormat().GetValue().isEmpty()) + break; + } - bool bAuthorityFieldURL = false; SwField* pField = rSh.GetCurField(); if (pField && pField->GetTyp()->Which() == SwFieldIds::TableOfAuthorities) { @@ -2605,12 +2680,12 @@ void SwTextShell::GetState( SfxItemSet &rSet ) || targetType == SwAuthorityField::TargetType::UseTargetURL) { // Check if the Bibliography entry has a target URL - bAuthorityFieldURL = rAuthorityField.GetAbsoluteURL().getLength() > 0; + if (rAuthorityField.GetAbsoluteURL().getLength() > 0) + break; } } - if (SfxItemState::SET > aSet.GetItemState(RES_TXTATR_INETFMT, false) - && !bAuthorityFieldURL) - rSet.DisableItem(nWhich); + + rSet.DisableItem(nWhich); } break; case FN_OPEN_LOCAL_URL: