tdf#137838 SW: Add ability to create a hyperlink to a drawing object

within a document

Change-Id: Ie51ad6a0a9da1a2478a4a4abc73ba793ea92704c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/105164
Tested-by: Jenkins
Reviewed-by: Jim Raykowski <raykowj@gmail.com>
diff --git a/sw/inc/unotxdoc.hxx b/sw/inc/unotxdoc.hxx
index 1f814a9..48f49a2 100644
--- a/sw/inc/unotxdoc.hxx
+++ b/sw/inc/unotxdoc.hxx
@@ -480,6 +480,7 @@ class SwXLinkTargetSupplier final : public cppu::WeakImplHelper
    OUString m_sSections;
    OUString m_sOutlines;
    OUString m_sBookmarks;
    OUString m_sDrawingObjects;

public:
    SwXLinkTargetSupplier(SwXTextDocument& rxDoc);
@@ -579,6 +580,35 @@ public:
    virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
};

class SwXDrawingObjectTarget final : public cppu::WeakImplHelper
<
    css::beans::XPropertySet,
    css::lang::XServiceInfo
>
{
    const SfxItemPropertySet*   m_pPropSet;
    OUString                    m_sDrawingObjectText;

public:
    SwXDrawingObjectTarget(const OUString& rDrawingObjectText);
    virtual ~SwXDrawingObjectTarget() override;

    //XPropertySet
    virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo(  ) override;
    virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
    virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
    virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override;
    virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override;
    virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
    virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;

    //XServiceInfo
    virtual OUString SAL_CALL getImplementationName() override;
    virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
    virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
};


enum class SwCreateDrawTable {
    Dash = 1, Gradient, Hatch, Bitmap, TransGradient, Marker, Defaults
};
diff --git a/sw/source/uibase/docvw/edtwin2.cxx b/sw/source/uibase/docvw/edtwin2.cxx
index ae16890..5770afb 100644
--- a/sw/source/uibase/docvw/edtwin2.cxx
+++ b/sw/source/uibase/docvw/edtwin2.cxx
@@ -171,7 +171,8 @@ void SwEditWin::RequestHelp(const HelpEvent &rEvt)
                        sSuffix == "outline" ||
                        sSuffix == "text" ||
                        sSuffix == "graphic" ||
                        sSuffix == "ole" )
                        sSuffix == "ole" ||
                        sSuffix == "drawingobject" )
                        sText = sText.copy( 0, nFound - 1);
                }
                // #i104300#
diff --git a/sw/source/uibase/inc/wrtsh.hxx b/sw/source/uibase/inc/wrtsh.hxx
index 4499c0c..d4c9a6b 100644
--- a/sw/source/uibase/inc/wrtsh.hxx
+++ b/sw/source/uibase/inc/wrtsh.hxx
@@ -482,6 +482,7 @@ typedef bool (SwWrtShell::*FNSimpleMove)();
    bool GotoTable( const OUString& rName );
    void GotoFormatField( const SwFormatField& rField );
    const SwRangeRedline* GotoRedline( SwRedlineTable::size_type nArrPos, bool bSelect);
    bool GotoDrawingObject(std::u16string_view rName);

    void ChangeHeaderOrFooter(std::u16string_view rStyleName, bool bHeader, bool bOn, bool bShowWarning);
    virtual void SetShowHeaderFooterSeparator( FrameControlType eControl, bool bShow ) override;
diff --git a/sw/source/uibase/uiview/view2.cxx b/sw/source/uibase/uiview/view2.cxx
index 1071ed4..cd8e948 100644
--- a/sw/source/uibase/uiview/view2.cxx
+++ b/sw/source/uibase/uiview/view2.cxx
@@ -2066,7 +2066,9 @@ bool SwView::JumpToSwMark( const OUString& rMark )
            sCmp = sCmp.toAsciiLowerCase();
            FlyCntType eFlyType = FLYCNTTYPE_ALL;

            if( sCmp == "region" )
            if (sCmp == "drawingobject")
                bRet = m_pWrtShell->GotoDrawingObject(sName);
            else if( sCmp == "region" )
            {
                m_pWrtShell->EnterStdMode();
                bRet = m_pWrtShell->GotoRegion( sName );
@@ -2159,6 +2161,9 @@ bool SwView::JumpToSwMark( const OUString& rMark )
        // reset ViewStatus
        SetCursorAtTop( bSaveCT, bSaveCC );

        if(!m_pWrtShell->IsFrameSelected() && !m_pWrtShell->IsObjSelected())
            m_pWrtShell->ShowCursor();

        if( !bHasShFocus )
            m_pWrtShell->ShellLoseFocus();
    }
diff --git a/sw/source/uibase/uno/unotxdoc.cxx b/sw/source/uibase/uno/unotxdoc.cxx
index 2a723e1..18f84f8 100644
--- a/sw/source/uibase/uno/unotxdoc.cxx
+++ b/sw/source/uibase/uno/unotxdoc.cxx
@@ -160,6 +160,8 @@
#include <xmloff/odffields.hxx>
#include <tools/json_writer.hxx>

#include <svx/svdpage.hxx>

#define TWIPS_PER_PIXEL 15

using namespace ::com::sun::star;
@@ -3870,6 +3872,7 @@ SwXLinkTargetSupplier::SwXLinkTargetSupplier(SwXTextDocument& rxDoc) :
    m_sSections   = SwResId(STR_CONTENT_TYPE_REGION);
    m_sOutlines   = SwResId(STR_CONTENT_TYPE_OUTLINE);
    m_sBookmarks  = SwResId(STR_CONTENT_TYPE_BOOKMARK);
    m_sDrawingObjects = SwResId(STR_CONTENT_TYPE_DRAWOBJECT);
}

SwXLinkTargetSupplier::~SwXLinkTargetSupplier()
@@ -3932,6 +3935,13 @@ Any SwXLinkTargetSupplier::getByName(const OUString& rName)
                                        m_pxDoc->getBookmarks(), rName, sSuffix );
        aRet <<= Reference< XPropertySet >(xBkms, UNO_QUERY);
    }
    else if(rName == m_sDrawingObjects)
    {
        sSuffix += "drawingobject";
        Reference<XNameAccess> xDrawingObjects = new SwXLinkNameAccessWrapper(
                    *m_pxDoc, rName, sSuffix);
        aRet <<= Reference<XPropertySet>(xDrawingObjects, UNO_QUERY);
    }
    else
        throw NoSuchElementException();
    return aRet;
@@ -3945,7 +3955,8 @@ Sequence< OUString > SwXLinkTargetSupplier::getElementNames()
             m_sOLEs,
             m_sSections,
             m_sOutlines,
             m_sBookmarks };
             m_sBookmarks,
             m_sDrawingObjects };
}

sal_Bool SwXLinkTargetSupplier::hasByName(const OUString& rName)
@@ -3956,7 +3967,8 @@ sal_Bool SwXLinkTargetSupplier::hasByName(const OUString& rName)
        rName == m_sOLEs   ||
        rName == m_sSections ||
        rName == m_sOutlines ||
        rName == m_sBookmarks    )
        rName == m_sBookmarks ||
        rName == m_sDrawingObjects )
        return true;
    return false;
}
@@ -4030,17 +4042,39 @@ Any SwXLinkNameAccessWrapper::getByName(const OUString& rName)
                if(!m_pxDoc->GetDocShell())
                    throw RuntimeException("No document shell available");
                SwDoc* pDoc = m_pxDoc->GetDocShell()->GetDoc();
                const size_t nOutlineCount = pDoc->GetNodes().GetOutLineNds().size();

                for (size_t i = 0; i < nOutlineCount && !bFound; ++i)
                if (sSuffix == "|outline")
                {
                    const SwOutlineNodes& rOutlineNodes = pDoc->GetNodes().GetOutLineNds();
                    const SwNumRule* pOutlRule = pDoc->GetOutlineNumRule();
                    if(sParam == lcl_CreateOutlineString(i, rOutlineNodes, pOutlRule))
                    const size_t nOutlineCount = pDoc->GetNodes().GetOutLineNds().size();

                    for (size_t i = 0; i < nOutlineCount && !bFound; ++i)
                    {
                        Reference< XPropertySet >  xOutline = new SwXOutlineTarget(sParam);
                        aRet <<= xOutline;
                        bFound = true;
                        const SwOutlineNodes& rOutlineNodes = pDoc->GetNodes().GetOutLineNds();
                        const SwNumRule* pOutlRule = pDoc->GetOutlineNumRule();
                        if(sParam == lcl_CreateOutlineString(i, rOutlineNodes, pOutlRule))
                        {
                            Reference< XPropertySet >  xOutline = new SwXOutlineTarget(sParam);
                            aRet <<= xOutline;
                            bFound = true;
                        }
                    }
                }
                else if (sSuffix == "|drawingobject")
                {
                    SwDrawModel* pModel = pDoc->getIDocumentDrawModelAccess().GetDrawModel();
                    if (pModel)
                    {
                        SdrPage* pPage = pModel->GetPage(0);
                        for (size_t i = 0; i < pPage->GetObjCount() && !bFound; ++i)
                        {
                            SdrObject* pObj = pPage->GetObj(i);
                            if (sParam == pObj->GetName())
                            {
                                Reference<XPropertySet> xDrawingObject = new SwXDrawingObjectTarget(sParam);
                                aRet <<= xDrawingObject;
                                bFound = true;
                            }
                        }
                    }
                }
            }
@@ -4068,17 +4102,37 @@ Sequence< OUString > SwXLinkNameAccessWrapper::getElementNames()
    {
        if(!m_pxDoc->GetDocShell())
            throw RuntimeException("No document shell available");

        SwDoc* pDoc = m_pxDoc->GetDocShell()->GetDoc();
        const SwOutlineNodes& rOutlineNodes = pDoc->GetNodes().GetOutLineNds();
        const size_t nOutlineCount = rOutlineNodes.size();
        aRet.realloc(nOutlineCount);
        OUString* pResArr = aRet.getArray();
        const SwNumRule* pOutlRule = pDoc->GetOutlineNumRule();
        for (size_t i = 0; i < nOutlineCount; ++i)
        if (m_sLinkSuffix == "|outline")
        {
            OUString sEntry = lcl_CreateOutlineString(i, rOutlineNodes, pOutlRule) + "|outline";
            pResArr[i] = sEntry;
            const SwOutlineNodes& rOutlineNodes = pDoc->GetNodes().GetOutLineNds();
            const size_t nOutlineCount = rOutlineNodes.size();
            aRet.realloc(nOutlineCount);
            OUString* pResArr = aRet.getArray();
            const SwNumRule* pOutlRule = pDoc->GetOutlineNumRule();
            for (size_t i = 0; i < nOutlineCount; ++i)
            {
                OUString sEntry = lcl_CreateOutlineString(i, rOutlineNodes, pOutlRule) + "|outline";
                pResArr[i] = sEntry;
            }
        }
        else if (m_sLinkSuffix == "|drawingobject")
        {
            SwDrawModel* pModel = pDoc->getIDocumentDrawModelAccess().GetDrawModel();
            if(pModel)
            {
                SdrPage* pPage = pModel->GetPage(0);
                const size_t nObjCount = pPage->GetObjCount();
                aRet.realloc(nObjCount);
                OUString* pResArr = aRet.getArray();
                auto j = 0;
                for (size_t i = 0; i < nObjCount; ++i)
                {
                    SdrObject* pObj = pPage->GetObj(i);
                    if (!pObj->GetName().isEmpty())
                        pResArr[j++] = pObj->GetName() + "|drawingobject";
                }
            }
        }
    }
    else
@@ -4107,16 +4161,33 @@ sal_Bool SwXLinkNameAccessWrapper::hasByName(const OUString& rName)
                if(!m_pxDoc->GetDocShell())
                    throw RuntimeException("No document shell available");
                SwDoc* pDoc = m_pxDoc->GetDocShell()->GetDoc();
                const size_t nOutlineCount = pDoc->GetNodes().GetOutLineNds().size();

                for (size_t i = 0; i < nOutlineCount && !bRet; ++i)
                if (m_sLinkSuffix == "|outline")
                {
                    const SwOutlineNodes& rOutlineNodes = pDoc->GetNodes().GetOutLineNds();
                    const SwNumRule* pOutlRule = pDoc->GetOutlineNumRule();
                    if(sParam ==
                        lcl_CreateOutlineString(i, rOutlineNodes, pOutlRule))
                    const size_t nOutlineCount = pDoc->GetNodes().GetOutLineNds().size();

                    for (size_t i = 0; i < nOutlineCount && !bRet; ++i)
                    {
                        bRet = true;
                        const SwOutlineNodes& rOutlineNodes = pDoc->GetNodes().GetOutLineNds();
                        const SwNumRule* pOutlRule = pDoc->GetOutlineNumRule();
                        if(sParam ==
                            lcl_CreateOutlineString(i, rOutlineNodes, pOutlRule))
                        {
                            bRet = true;
                        }
                    }
                }
                else if (m_sLinkSuffix == "|drawingobject")
                {
                    SwDrawModel* pModel = pDoc->getIDocumentDrawModelAccess().GetDrawModel();
                    if (pModel)
                    {
                        SdrPage* pPage = pModel->GetPage(0);
                        const size_t nObjCount = pPage->GetObjCount();
                        for (size_t i = 0; i < nObjCount && !bRet; ++i)
                        {
                            if (sParam == pPage->GetObj(i)->GetName())
                                bRet = true;
                        }
                    }
                }
            }
@@ -4182,6 +4253,8 @@ static Any lcl_GetDisplayBitmap(const OUString& _sLinkSuffix)
        sImgId = RID_BMP_NAVI_BOOKMARK;
    else if(sLinkSuffix == "region")
        sImgId = RID_BMP_NAVI_REGION;
    else if(sLinkSuffix == "drawingobject")
        sImgId = RID_BMP_NAVI_DRAWOBJECT;

    if (!sImgId.isEmpty())
    {
@@ -4310,6 +4383,73 @@ Sequence< OUString > SwXOutlineTarget::getSupportedServiceNames()
    return aRet;
}

SwXDrawingObjectTarget::SwXDrawingObjectTarget(const OUString& rDrawingObjectText) :
    m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_LINK_TARGET)),
    m_sDrawingObjectText(rDrawingObjectText)
{
}

SwXDrawingObjectTarget::~SwXDrawingObjectTarget()
{
}

Reference< XPropertySetInfo >  SwXDrawingObjectTarget::getPropertySetInfo()
{
    static Reference< XPropertySetInfo >  xRet = m_pPropSet->getPropertySetInfo();
    return xRet;
}

void SwXDrawingObjectTarget::setPropertyValue(
    const OUString& rPropertyName, const Any& /*aValue*/)
{
    throw UnknownPropertyException(rPropertyName);
}

Any SwXDrawingObjectTarget::getPropertyValue(const OUString& rPropertyName)
{
    if(rPropertyName != UNO_LINK_DISPLAY_NAME)
        throw UnknownPropertyException(rPropertyName);

    return Any(m_sDrawingObjectText);
}

void SwXDrawingObjectTarget::addPropertyChangeListener(
    const OUString& /*PropertyName*/, const Reference< XPropertyChangeListener > & /*aListener*/)
{
}

void SwXDrawingObjectTarget::removePropertyChangeListener(
    const OUString& /*PropertyName*/, const Reference< XPropertyChangeListener > & /*aListener*/)
{
}

void SwXDrawingObjectTarget::addVetoableChangeListener(
    const OUString& /*PropertyName*/, const Reference< XVetoableChangeListener > & /*aListener*/)
{
}

void SwXDrawingObjectTarget::removeVetoableChangeListener(
    const OUString& /*PropertyName*/, const Reference< XVetoableChangeListener > & /*aListener*/)
{
}

OUString SwXDrawingObjectTarget::getImplementationName()
{
    return "SwXDrawingObjectTarget";
}

sal_Bool SwXDrawingObjectTarget::supportsService(const OUString& ServiceName)
{
    return cppu::supportsService(this, ServiceName);
}

Sequence< OUString > SwXDrawingObjectTarget::getSupportedServiceNames()
{
    Sequence<OUString> aRet { "com.sun.star.document.LinkTarget" };

    return aRet;
}

SwXDocumentPropertyHelper::SwXDocumentPropertyHelper(SwDoc& rDoc) :
SvxUnoForbiddenCharsTable ( rDoc.getIDocumentSettingAccess().getForbiddenCharacterTable() )
,m_pDoc(&rDoc)
diff --git a/sw/source/uibase/utlui/content.cxx b/sw/source/uibase/utlui/content.cxx
index 51a4bbd..57ed667 100644
--- a/sw/source/uibase/utlui/content.cxx
+++ b/sw/source/uibase/utlui/content.cxx
@@ -4296,8 +4296,6 @@ static void lcl_AssureStdModeAtShell(SwWrtShell* pWrtShell)
void SwContentTree::GotoContent(const SwContent* pCnt)
{
    lcl_AssureStdModeAtShell(m_pActiveShell);

    bool bSel = false;
    switch(pCnt->GetParent()->GetType())
    {
        case ContentTypeId::OUTLINE   :
@@ -4314,8 +4312,7 @@ void SwContentTree::GotoContent(const SwContent* pCnt)
        case ContentTypeId::GRAPHIC   :
        case ContentTypeId::OLE       :
        {
            if(m_pActiveShell->GotoFly(pCnt->GetName()))
                bSel = true;
            m_pActiveShell->GotoFly(pCnt->GetName());
        }
        break;
        case ContentTypeId::BOOKMARK:
@@ -4336,7 +4333,6 @@ void SwContentTree::GotoContent(const SwContent* pCnt)
                m_pActiveShell->Right( CRSR_SKIP_CHARS, true, 1, false);
                m_pActiveShell->SwCursorShell::SelectTextAttr( RES_TXTATR_INETFMT, true );
            }

        }
        break;
        case ContentTypeId::REFERENCE:
@@ -4356,47 +4352,26 @@ void SwContentTree::GotoContent(const SwContent* pCnt)
        break;
        case ContentTypeId::DRAWOBJECT:
        {
            SwPosition aPos = *m_pActiveShell->GetCursor()->GetPoint();
            SdrView* pDrawView = m_pActiveShell->GetDrawView();
            if (pDrawView)
            {
                pDrawView->SdrEndTextEdit();
                pDrawView->UnmarkAll();
                SwDrawModel* _pModel = m_pActiveShell->getIDocumentDrawModelAccess().GetDrawModel();
                SdrPage* pPage = _pModel->GetPage(0);
                const size_t nCount = pPage->GetObjCount();
                for( size_t i=0; i<nCount; ++i )
                {
                    SdrObject* pTemp = pPage->GetObj(i);
                    if (pTemp->GetName() == pCnt->GetName())
                    {
                        SdrPageView* pPV = pDrawView->GetSdrPageView();
                        if( pPV )
                        {
                            pDrawView->MarkObj( pTemp, pPV );
                        }
                    }
                }
                m_pActiveShell->GetNavigationMgr().addEntry(aPos);
                m_pActiveShell->EnterStdMode();
                bSel = true;
            }
            m_pActiveShell->GotoDrawingObject(pCnt->GetName());
        }
        break;
        default: break;
    }
    if(bSel)

    if (m_pActiveShell->IsFrameSelected() || m_pActiveShell->IsObjSelected())
    {
        m_pActiveShell->HideCursor();
        m_pActiveShell->EnterSelFrameMode();
    }

    SwView& rView = m_pActiveShell->GetView();
    rView.StopShellTimer();
    rView.GetPostItMgr()->SetActiveSidebarWin(nullptr);
    rView.GetEditWin().GrabFocus();

    // force scroll to cursor position when navigating to inactive document
    if(!bSel)
    // assure visible view area is at cursor position
    if (!m_pActiveShell->IsCursorVisible() && !m_pActiveShell->IsFrameSelected() &&
            !m_pActiveShell->IsObjSelected())
    {
        Point rPoint = m_pActiveShell->GetCursorDocPos();
        rPoint.setX(0);
diff --git a/sw/source/uibase/wrtsh/move.cxx b/sw/source/uibase/wrtsh/move.cxx
index bd07683..c187396 100644
--- a/sw/source/uibase/wrtsh/move.cxx
+++ b/sw/source/uibase/wrtsh/move.cxx
@@ -23,6 +23,10 @@
#include <view.hxx>
#include <viewopt.hxx>
#include <drawbase.hxx>
#include <IDocumentDrawModelAccess.hxx>
#include <drawdoc.hxx>
#include <svx/svdpage.hxx>
#include <svx/svdview.hxx>

/**
   Always:
@@ -624,6 +628,39 @@ bool SwWrtShell::GotoOutline( const OUString& rName )
    return bRet;
}

bool SwWrtShell::GotoDrawingObject(std::u16string_view rName)
{
    SwPosition aPos = *GetCursor()->GetPoint();
    bool bRet = false;
    SdrView* pDrawView = GetDrawView();
    if (pDrawView)
    {
        pDrawView->SdrEndTextEdit();
        pDrawView->UnmarkAll();
        SdrPage* pPage = getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
        const size_t nCount = pPage->GetObjCount();
        for (size_t i = 0; i < nCount; ++i)
        {
            SdrObject* pObj = pPage->GetObj(i);
            if (pObj->GetName() == rName)
            {
                SdrPageView* pPageView = pDrawView->GetSdrPageView();
                if(pPageView)
                {
                    pDrawView->MarkObj(pObj, pPageView);
                    m_aNavigationMgr.addEntry(aPos);
                    EnterStdMode();
                    HideCursor();
                    EnterSelFrameMode();
                    bRet = true;
                }
                break;
            }
        }
    }
    return bRet;
}

bool SwWrtShell::GotoRegion( std::u16string_view rName )
{
    SwPosition aPos = *GetCursor()->GetPoint();