tdf#95378 Writer Navigator document content tracking

This patch adds document content tracking for tables, frames, images,
OLE objects and drawing objects.

Change-Id: I99af15d2da9f4f97d8702b5a9cad4fc21b676166
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/101253
Tested-by: Jenkins
Reviewed-by: Jim Raykowski <raykowj@gmail.com>
diff --git a/sw/source/uibase/utlui/content.cxx b/sw/source/uibase/utlui/content.cxx
index 88f9f09..ba62299 100644
--- a/sw/source/uibase/utlui/content.cxx
+++ b/sw/source/uibase/utlui/content.cxx
@@ -1882,11 +1882,8 @@ IMPL_LINK_NOARG(SwContentTree, ContentDoubleClickHdl, weld::TreeView&, bool)
            SwContent* pCnt = reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xEntry).toInt64());
            assert(pCnt && "no UserData");
            GotoContent(pCnt);
            const ContentTypeId nActType = pCnt->GetParent()->GetType();
            if (nActType == ContentTypeId::FRAME)
                m_pActiveShell->EnterStdMode();
            // fdo#36308 don't expand outlines on double-click
            bConsumed = nActType == ContentTypeId::OUTLINE;
            bConsumed = pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE;
        }
    }

@@ -3087,6 +3084,42 @@ void SwContentTree::HideTree()
    m_xTreeView->hide();
}

static void lcl_SelectByContentTypeAndName(SwContentTree* pThis, weld::TreeView& rContentTree,
                                           const OUString& rContentTypeName, const OUString& rName)
{
    if (!rName.isEmpty())
    {
        // find content type entry
        std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator());
        bool bFoundEntry = rContentTree.get_iter_first(*xIter);
        while (bFoundEntry && rContentTypeName != rContentTree.get_text(*xIter))
            bFoundEntry = rContentTree.iter_next_sibling(*xIter);
        // find content type content entry and select it
        if (bFoundEntry)
        {
            rContentTree.expand_row(*xIter); // assure content type entry is expanded
            while (rContentTree.iter_next(*xIter) && lcl_IsContent(*xIter, rContentTree))
            {
                if (rName == rContentTree.get_text(*xIter))
                {
                    // get first selected for comparison
                    std::unique_ptr<weld::TreeIter> xFirstSelected(rContentTree.make_iterator());
                    if (!rContentTree.get_selected(xFirstSelected.get()))
                        xFirstSelected.reset();
                    if (rContentTree.count_selected_rows() != 1 ||
                            rContentTree.iter_compare(*xIter, *xFirstSelected) != 0)
                    {
                        // unselect all entries and make passed entry visible and selected
                        rContentTree.set_cursor(*xIter);
                        pThis->Select();
                    }
                    break;
                }
            }
        }
    }
}

/** No idle with focus or while dragging */
IMPL_LINK_NOARG(SwContentTree, TimerUpdate, Timer *, void)
{
@@ -3136,72 +3169,131 @@ void SwContentTree::UpdateTracking()
    if (State::HIDDEN == m_eState)
        return;

    // track document outline position at cursor
    if (m_nOutlineTracking == 3) // no outline tracking
    // m_bIgnoreViewChange is set on delete
    if (m_bIgnoreViewChange)
    {
        m_bIgnoreViewChange = false;
        return;
    }

    const SwOutlineNodes::size_type nActPos = GetWrtShell()->GetOutlinePos(MAXLEVEL); // find out where the cursor is
    if (nActPos == SwOutlineNodes::npos)
        return;

    // only track if selection is already an outline
    std::unique_ptr<weld::TreeIter> xFirstSelected(m_xTreeView->make_iterator());
    if (!m_xTreeView->get_selected(xFirstSelected.get()))
        xFirstSelected.reset();
    if (xFirstSelected && lcl_IsContent(*xFirstSelected, *m_xTreeView) &&
            reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xFirstSelected).toInt64())->GetParent()->GetType() != ContentTypeId::OUTLINE)
        return;
    if (xFirstSelected && lcl_IsContentType(*xFirstSelected, *m_xTreeView) &&
            reinterpret_cast<SwContentType*>(m_xTreeView->get_id(*xFirstSelected).toInt64())->GetType() != ContentTypeId::OUTLINE)
        return;

    int nSelectedRows = m_xTreeView->count_selected_rows();

    // find the outline in the tree and select it
    m_xTreeView->all_foreach([this, nSelectedRows, nActPos, &xFirstSelected](weld::TreeIter& rEntry){
        bool bRet = false;

        if (lcl_IsContent(rEntry, *m_xTreeView) &&
                reinterpret_cast<SwContent*>(m_xTreeView->get_id(rEntry).toInt64())->GetParent()->GetType() == ContentTypeId::OUTLINE)
    // drawing
    if ((m_pActiveShell->GetSelectionType() & (SelectionType::DrawObject |
                                          SelectionType::DrawObjectEditMode)) &&
            !(m_bIsRoot && m_nRootType != ContentTypeId::DRAWOBJECT))
    {
        SdrView* pSdrView = m_pActiveShell->GetDrawView();
        if(pSdrView && 1 == pSdrView->GetMarkedObjectCount())
        {
            // might have been scrolled out of view by the user so leave it that way
            if (reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(rEntry).toInt64())->GetOutlinePos() == nActPos)
            SdrObject* pSelected = pSdrView->GetMarkedObjectByIndex(0);
            OUString aName(pSelected->GetName());
            lcl_SelectByContentTypeAndName(this, *m_xTreeView,
                                           SwResId(STR_CONTENT_TYPE_DRAWOBJECT), aName);
        }
        return;
    }
    // graphic, frame, and ole
    OUString aContentTypeName;
    if (m_pActiveShell->GetSelectionType() == SelectionType::Graphic &&
            !(m_bIsRoot && m_nRootType != ContentTypeId::GRAPHIC))
        aContentTypeName = SwResId(STR_CONTENT_TYPE_GRAPHIC);
    else if (m_pActiveShell->GetSelectionType() == SelectionType::Frame &&
             !(m_bIsRoot && m_nRootType != ContentTypeId::FRAME))
        aContentTypeName = SwResId(STR_CONTENT_TYPE_FRAME);
    else if (m_pActiveShell->GetSelectionType() == SelectionType::Ole &&
             !(m_bIsRoot && m_nRootType != ContentTypeId::OLE))
        aContentTypeName = SwResId(STR_CONTENT_TYPE_OLE);
    if (!aContentTypeName.isEmpty())
    {
        OUString aName(m_pActiveShell->GetFlyName());
        lcl_SelectByContentTypeAndName(this, *m_xTreeView, aContentTypeName, aName);
        return;
    }
    // table
    if (m_pActiveShell->IsCursorInTable()  &&
            !(m_bIsRoot && m_nRootType != ContentTypeId::TABLE))
    {
        if(m_pActiveShell->GetTableFormat())
        {
            OUString aName = m_pActiveShell->GetTableFormat()->GetName();
            lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_TABLE),
                                           aName);
        }
        return;
    }
    // outline
    // find out where the cursor is
    const SwOutlineNodes::size_type nActPos = GetWrtShell()->GetOutlinePos(MAXLEVEL);
    if (!((m_bIsRoot && m_nRootType != ContentTypeId::OUTLINE) ||
          m_nOutlineTracking == 3 || nActPos == SwOutlineNodes::npos))
    {
        // assure outline content type is expanded
        // this assumes outline content type is first in treeview
        std::unique_ptr<weld::TreeIter> xFirstEntry(m_xTreeView->make_iterator());
        m_xTreeView->get_iter_first(*xFirstEntry);
        m_xTreeView->expand_row(*xFirstEntry);

        m_xTreeView->all_foreach([this, nActPos](weld::TreeIter& rEntry){
            bool bRet = false;
            if (lcl_IsContent(rEntry, *m_xTreeView) && reinterpret_cast<SwContent*>(
                        m_xTreeView->get_id(rEntry).toInt64())->GetParent()->GetType() ==
                        ContentTypeId::OUTLINE)
            {
                // only select if not already selected or tree has multiple entries selected
                if (nSelectedRows != 1 || m_xTreeView->iter_compare(rEntry, *xFirstSelected) != 0)
                if (reinterpret_cast<SwOutlineContent*>(
                            m_xTreeView->get_id(rEntry).toInt64())->GetOutlinePos() == nActPos)
                {
                    if (m_nOutlineTracking == 2) // focused outline tracking
                    std::unique_ptr<weld::TreeIter> xFirstSelected(
                                m_xTreeView->make_iterator());
                    if (!m_xTreeView->get_selected(xFirstSelected.get()))
                        xFirstSelected.reset();
                    // only select if not already selected or tree has multiple entries selected
                    if (m_xTreeView->count_selected_rows() != 1 ||
                            m_xTreeView->iter_compare(rEntry, *xFirstSelected) != 0)
                    {
                        // collapse to children of root node
                        std::unique_ptr<weld::TreeIter> xChildEntry(m_xTreeView->make_iterator());
                        if (m_xTreeView->get_iter_first(*xChildEntry) && m_xTreeView->iter_children(*xChildEntry))
                        if (m_nOutlineTracking == 2) // focused outline tracking
                        {
                            do
                            // collapse to children of root node
                            std::unique_ptr<weld::TreeIter> xChildEntry(
                                        m_xTreeView->make_iterator());
                            if (m_xTreeView->get_iter_first(*xChildEntry) &&
                                    m_xTreeView->iter_children(*xChildEntry))
                            {
                                if (reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xChildEntry).toInt64())->GetParent()->GetType() == ContentTypeId::OUTLINE)
                                    m_xTreeView->collapse_row(*xChildEntry);
                                else
                                    break;
                                do
                                {
                                    if (reinterpret_cast<SwContent*>(
                                                m_xTreeView->get_id(*xChildEntry).toInt64())->
                                            GetParent()->GetType() == ContentTypeId::OUTLINE)
                                        m_xTreeView->collapse_row(*xChildEntry);
                                    else
                                        break;
                                }
                                while (m_xTreeView->iter_next(*xChildEntry));
                            }
                            while (m_xTreeView->iter_next(*xChildEntry));
                        }
                        // unselect all entries, make pEntry visible, and select
                        m_xTreeView->set_cursor(rEntry);
                        Select();
                    }
                    m_xTreeView->set_cursor(rEntry); // unselect all entries, make pEntry visible, and select
                    Select();
                    bRet = true;
                }
                bRet = true;
            }
        }
        else
        {
            // use of this break assumes outline content type is first in tree
            if (lcl_IsContentType(rEntry, *m_xTreeView) &&
                    reinterpret_cast<SwContentType*>(m_xTreeView->get_id(rEntry).toInt64())->GetType() != ContentTypeId::OUTLINE)
                bRet = true;
        }

        return bRet;
    });
            else
            {
                // use of this break assumes outline content type is first in tree
                if (lcl_IsContentType(rEntry, *m_xTreeView) &&
                        reinterpret_cast<SwContentType*>(
                            m_xTreeView->get_id(rEntry).toInt64())->GetType() !=
                        ContentTypeId::OUTLINE)
                    bRet = true;
            }
            return bRet;
        });
    }
    else
    {
        // clear treeview selections
        m_xTreeView->unselect_all();
        Select();
    }
}

void SwContentTree::SelectOutlinesWithSelection()
@@ -3940,6 +4032,9 @@ void SwContentTree::EditEntry(const weld::TreeIter& rEntry, EditEntryMode nMode)
    const ContentTypeId nType = pCnt->GetParent()->GetType();
    sal_uInt16 nSlot = 0;

    if(EditEntryMode::DELETE == nMode)
        m_bIgnoreViewChange = true;

    uno::Reference< container::XNameAccess >  xNameAccess, xSecond, xThird;
    switch(nType)
    {
@@ -4134,7 +4229,7 @@ void SwContentTree::EditEntry(const weld::TreeIter& rEntry, EditEntryMode nMode)
    }
    if(nSlot)
        m_pActiveShell->GetView().GetViewFrame()->
                    GetDispatcher()->Execute(nSlot, SfxCallMode::ASYNCHRON);
                    GetDispatcher()->Execute(nSlot, SfxCallMode::SYNCHRON);
    else if(xNameAccess.is())
    {
        uno::Any aObj = xNameAccess->getByName(pCnt->GetName());