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:

Reply via email to