tdf#158031 editeng SID_*_HYPERLINK: use AlsoCheckBeforeCursor

This fixes the GTK3 problem where only the first half of the link
successfully ran SID_EDIT_HYPERLINK against an editeng hyperlink.

Fixed for Writer and Calc editeng hyperlinks (i.e. in textboxes).

TODO: fix SID_COPY_HYPERLINK (which doesn't SelectFieldAtCursor)

The problem showed up when the menu option was added
because the mouse was over an unselected field,
but when the mouse moved to select the menu option,
the slot was not considered valid anymore
if the cursor had moved behind the field.

(The right-click to open the context menu puts the cursor
before or after the field.)

Now, by checking for a field-before-the-cursor during validation,
the field is found and the command is not prevented from running.

Once this is all in place, some older commits from Caolan
should be able to be reverted.

(Strangely, SID_OPEN_HYPERLINK and SID_REMOVE_HYPERLINK
seem to run before the slot is invalidated.
No idea why they are different.
For GEN, the popup usually either kept the pre-menu mouse position,
or else it always runs the command before the slot invalidates.)

Change-Id: If992b3fdc94d89162b60447458cabced2d5e3f19
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/158856
Reviewed-by: Justin Luth <jluth@mail.com>
Tested-by: Jenkins
diff --git a/editeng/source/editeng/editview.cxx b/editeng/source/editeng/editview.cxx
index bfa98d1..e1229b6 100644
--- a/editeng/source/editeng/editview.cxx
+++ b/editeng/source/editeng/editview.cxx
@@ -1424,11 +1424,11 @@ void EditView::SelectFieldAtCursor()
        assert(std::abs(aSel.nStartPos - aSel.nEndPos) == 1);
}

const SvxFieldData* EditView::GetFieldAtCursor() const
const SvxFieldData* EditView::GetFieldAtCursor(bool bAlsoCheckBeforeCursor) const
{
    const SvxFieldItem* pFieldItem = GetFieldUnderMousePointer();
    if (!pFieldItem)
        pFieldItem = GetFieldAtSelection();
        pFieldItem = GetFieldAtSelection(bAlsoCheckBeforeCursor);

    return pFieldItem ? pFieldItem->GetField() : nullptr;
}
diff --git a/editeng/source/misc/urlfieldhelper.cxx b/editeng/source/misc/urlfieldhelper.cxx
index 564bc54..1ed8647 100644
--- a/editeng/source/misc/urlfieldhelper.cxx
+++ b/editeng/source/misc/urlfieldhelper.cxx
@@ -25,7 +25,7 @@ void URLFieldHelper::RemoveURLField(EditView& pEditView)
    }
}

bool URLFieldHelper::IsCursorAtURLField(const EditView& pEditView)
bool URLFieldHelper::IsCursorAtURLField(const EditView& pEditView, bool bAlsoCheckBeforeCursor)
{
    // tdf#128666 Make sure only URL field (or nothing) is selected
    ESelection aSel = pEditView.GetSelection();
@@ -37,7 +37,7 @@ bool URLFieldHelper::IsCursorAtURLField(const EditView& pEditView)
    if (!bIsValidSelection)
        return false;

    const SvxFieldData* pField = pEditView.GetFieldAtCursor();
    const SvxFieldData* pField = pEditView.GetFieldAtCursor(bAlsoCheckBeforeCursor);
    if (dynamic_cast<const SvxURLField*>(pField))
        return true;

diff --git a/include/editeng/editview.hxx b/include/editeng/editview.hxx
index 4c18ac2..b54f6b4 100644
--- a/include/editeng/editview.hxx
+++ b/include/editeng/editview.hxx
@@ -333,8 +333,9 @@ public:
    const SvxFieldItem* GetFieldAtSelection(bool bAlsoCheckBeforeCursor = false) const;
    const SvxFieldItem* GetFieldAtSelection(bool* pIsBeforeCursor) const;

    /// Select and return the field at the current cursor position
    const SvxFieldData* GetFieldAtCursor() const;
    /// return field under mouse, at selection, or immediately after (or before) the current cursor
    const SvxFieldData* GetFieldAtCursor(bool bAlsoCheckBeforeCursor = false) const;
    /// if no selection, select the field immediately after or before the current cursor
    void SelectFieldAtCursor();
    /// Converts position in paragraph to logical position without unfolding fields
    sal_Int32       GetPosNoField(sal_Int32 nPara, sal_Int32 nPos) const;
diff --git a/include/editeng/urlfieldhelper.hxx b/include/editeng/urlfieldhelper.hxx
index 9a1d53d..0b9da3a 100644
--- a/include/editeng/urlfieldhelper.hxx
+++ b/include/editeng/urlfieldhelper.hxx
@@ -18,10 +18,10 @@ class EDITENG_DLLPUBLIC URLFieldHelper
{
public:
    static void RemoveURLField(EditView& pEditView);
    static bool IsCursorAtURLField(const EditView& pEditView);
    static bool IsCursorAtURLField(const OutlinerView* pOLV)
    static bool IsCursorAtURLField(const EditView& pEditView, bool bAlsoCheckBeforeCursor = false);
    static bool IsCursorAtURLField(const OutlinerView* pOLV, bool bAlsoCheckBeforeCursor = false)
    {
        return pOLV && IsCursorAtURLField(pOLV->GetEditView());
        return pOLV && IsCursorAtURLField(pOLV->GetEditView(), bAlsoCheckBeforeCursor);
    }
};

diff --git a/sc/source/ui/drawfunc/drtxtob.cxx b/sc/source/ui/drawfunc/drtxtob.cxx
index bc9a3b7..ec527db 100644
--- a/sc/source/ui/drawfunc/drtxtob.cxx
+++ b/sc/source/ui/drawfunc/drtxtob.cxx
@@ -407,8 +407,8 @@ void ScDrawTextObjectBar::GetState( SfxItemSet& rSet )
        || rSet.GetItemState(SID_COPY_HYPERLINK_LOCATION) != SfxItemState::UNKNOWN
        || rSet.GetItemState(SID_REMOVE_HYPERLINK) != SfxItemState::UNKNOWN)
    {
        SdrView* pView = mrViewData.GetScDrawView();
        if( !URLFieldHelper::IsCursorAtURLField(pView->GetTextEditOutlinerView()) )
        OutlinerView* pOutView = mrViewData.GetScDrawView()->GetTextEditOutlinerView();
        if (!URLFieldHelper::IsCursorAtURLField(pOutView, /*AlsoCheckBeforeCursor=*/true))
        {
            rSet.DisableItem( SID_OPEN_HYPERLINK );
            rSet.DisableItem( SID_EDIT_HYPERLINK );
diff --git a/sw/source/uibase/shells/drwtxtex.cxx b/sw/source/uibase/shells/drwtxtex.cxx
index 565cb9f..db76755 100644
--- a/sw/source/uibase/shells/drwtxtex.cxx
+++ b/sw/source/uibase/shells/drwtxtex.cxx
@@ -938,7 +938,7 @@ void SwDrawTextShell::GetState(SfxItemSet& rSet)
            case SID_OPEN_HYPERLINK:
            case SID_COPY_HYPERLINK_LOCATION:
            {
                if (!URLFieldHelper::IsCursorAtURLField(pOLV))
                if (!URLFieldHelper::IsCursorAtURLField(pOLV, /*AlsoCheckBeforeCursor=*/true))
                    rSet.DisableItem(nWhich);
                nSlotId = 0;
            }