Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  content.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <svx/svditer.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/string.hxx>
#include <editeng/frmdiritem.hxx>
#include <svl/urlbmk.hxx>
#include <osl/thread.h>
#include <sal/log.hxx>
#include <tools/urlobj.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/event.hxx>
#include <sfx2/viewfrm.hxx>
#include <o3tl/enumrange.hxx>
#include <o3tl/sorted_vector.hxx>
#include <utility>
#include <vcl/commandevent.hxx>
#include <vcl/weldutils.hxx>
#include <sot/formats.hxx>
#include <o3tl/string_view.hxx>
#include <uiitems.hxx>
#include <fmtanchr.hxx>
#include <fmtinfmt.hxx>
#include <txtinet.hxx>
#include <fmtfld.hxx>
#include <swmodule.hxx>
#include <wrtsh.hxx>
#include <view.hxx>
#include <docsh.hxx>
#include <drawdoc.hxx>
#include <content.hxx>
#include <frmatr.hxx>
#include <frmfmt.hxx>
#include <fldbas.hxx>
#include <IMark.hxx>
#include <section.hxx>
#include <tox.hxx>
#include <navipi.hxx>
#include <navicont.hxx>
#include <navicfg.hxx>
#include <edtwin.hxx>
#include <doc.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentDrawModelAccess.hxx>
#include <IDocumentOutlineNodes.hxx>
#include <unotxvw.hxx>
#include <cmdid.h>
#include <helpids.h>
#include <strings.hrc>
#include <com/sun/star/text/XTextSectionsSupplier.hpp>
#include <com/sun/star/text/XTextGraphicObjectsSupplier.hpp>
#include <com/sun/star/text/XTextTablesSupplier.hpp>
#include <com/sun/star/text/XDocumentIndexesSupplier.hpp>
#include <com/sun/star/text/XDocumentIndex.hpp>
#include <com/sun/star/text/XBookmarksSupplier.hpp>
#include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp>
#include <com/sun/star/text/XTextFramesSupplier.hpp>
#include <com/sun/star/ui/XSidebarProvider.hpp>
#include <com/sun/star/ui/XDecks.hpp>
#include <com/sun/star/ui/XDeck.hpp>
#include <com/sun/star/ui/XPanels.hpp>
#include <com/sun/star/ui/XPanel.hpp>
#include <svx/svdpage.hxx>
#include <svx/svdview.hxx>
#include <SwRewriter.hxx>
#include <hints.hxx>
#include <numrule.hxx>
#include <swundo.hxx>
#include <ndtxt.hxx>
#include <PostItMgr.hxx>
#include <postithelper.hxx>

#include <swabstdlg.hxx>
#include <bitmaps.hlst>

#include <AnnotationWin.hxx>
#include <memory>

#include <fmtcntnt.hxx>
#include <docstat.hxx>

#include <viewopt.hxx>

#include <IDocumentFieldsAccess.hxx>
#include <txtfld.hxx>
#include <fldmgr.hxx>

#include <frameformats.hxx>

#include <ftnidx.hxx>
#include <txtftn.hxx>
#include <fmtftn.hxx>

#include <txtannotationfld.hxx>
#include <txtfrm.hxx>
#include <txtrfmrk.hxx>
#include <svx/sdr/overlay/overlayselection.hxx>
#include <svx/sdr/overlay/overlayobject.hxx>
#include <svx/sdr/overlay/overlaymanager.hxx>
#include <svx/sdrpaintwindow.hxx>
#include <node2lay.hxx>

#include <sectfrm.hxx>

#include <docufld.hxx>

#include <svl/fstathelper.hxx>

#include <expfld.hxx>
#include <unotxdoc.hxx>
#include <unoidxcoll.hxx>

#include <rootfrm.hxx>

#define CTYPE_CNT   0
#define CTYPE_CTT   1

using namespace ::com::sun::star;
using namespace ::com::sun::star::text;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::container;

namespace {

/*
    Symbolic name representations of numeric values used for the Outline Content Visibility popup
    menu item ids. The numbers are chosen arbitrarily to not over overlap other menu item ids.
    see: SwContentTree::ExecuteContextMenuAction, navigatorcontextmenu.ui

    1512 toggle outline content visibility of the selected outline entry
    1513 make the outline content of the selected outline entry and children not visible
    1514 make the outline content of the selected entry and children visible
*/

const sal_uInt32 TOGGLE_OUTLINE_CONTENT_VISIBILITY = 1512;
const sal_uInt32 HIDE_OUTLINE_CONTENT_VISIBILITY = 1513;
const sal_uInt32 SHOW_OUTLINE_CONTENT_VISIBILITY = 1514;

constexpr char NAVI_BOOKMARK_DELIM = '\x01';

}

class SwContentArr
    : public o3tl::sorted_vector<std::unique_ptr<SwContent>, o3tl::less_ptr_to,
                o3tl::find_partialorder_ptrequals>
{
};

namespace
{
    std::map<OUString, std::map<void*, bool>> lcl_DocOutLineExpandStateMap;

    bool lcl_IsContent(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView)
    {
        return weld::fromId<const SwTypeNumber*>(rTreeView.get_id(rEntry))->GetTypeId() == CTYPE_CNT;
    }

    bool lcl_IsContentType(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView)
    {
        return weld::fromId<const SwTypeNumber*>(rTreeView.get_id(rEntry))->GetTypeId() == CTYPE_CTT;
    }

    bool lcl_IsLowerOutlineContent(const weld::TreeIter& rEntry, const weld::TreeView&&nbsp;rTreeView, sal_uInt8 nLevel)
    {
        return weld::fromId<const SwOutlineContent*>(rTreeView.get_id(rEntry))->GetOutlineLevel() < nLevel;
    }

    bool lcl_FindShell(SwWrtShell const * pShell)
    {
        bool bFound = false;
        SwView *pView = SwModule::GetFirstView();
        while (pView)
        {
            if(pShell == &pView->GetWrtShell())
            {
                bFound = true;
                break;
            }
            pView = SwModule::GetNextView(pView);
        }
        return bFound;
    }

    bool lcl_IsUiVisibleBookmark(const ::sw::mark::MarkBase* pMark)
    {
        return IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::BOOKMARK;
    }

    OUString lcl_GetFootnoteText(const SwTextFootnote& rTextFootnote)
    {
        SwNodeIndex aIdx(*rTextFootnote.GetStartNode(), 1);
        SwContentNode* pCNd = aIdx.GetNode().GetTextNode();
        if(!pCNd)
            pCNd = SwNodes::GoNext(&aIdx);
        return pCNd->IsTextNode() ? static_cast<SwTextNode*>(pCNd)->GetText() : OUString();
    }

    void getAnchorPos(SwPosition& rPos)
    {
        // get the top most anchor position of the position
        if (SwFrameFormat* pFlyFormat = rPos.GetNode().GetFlyFormat())
        {
            SwNode* pAnchorNode;
            SwFrameFormat* pTmp = pFlyFormat;
            while (pTmp && (pAnchorNode = pTmp->GetAnchor().GetAnchorNode()) &&
                   (pTmp = pAnchorNode->GetFlyFormat()))
            {
                pFlyFormat = pTmp;
            }
            if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor())
                rPos = *pPos;
        }
    }

    bool lcl_IsLowerRegionContent(const weld::TreeIter& rEntry, const weld::TreeView&&nbsp;rTreeView, sal_uInt8 nLevel)
    {
        return weld::fromId<const SwRegionContent*>(rTreeView.get_id(rEntry))->GetRegionLevel() < nLevel;
    }

    void lcl_SelectAllFootnotesOrEndnotes(SwWrtShell& rWrtShell, SwContentType* pContentType)
    {
        const auto nCount = pContentType->GetMemberCount();
        if (nCount == 0)
            return;

        rWrtShell.AssureStdMode();
        SwCursor* pCursor = rWrtShell.getShellCursor(true);

        rWrtShell.StartAction();
        rWrtShell.EnterAddMode();
        for (size_t i = 0; i < nCount; i++)
        {
            const SwTextFootnoteContent* pTextFootnoteCnt
                    = static_cast<const SwTextFootnoteContent*>(pContentType->GetMember(i));
            if (pTextFootnoteCnt && !pTextFootnoteCnt->IsInvisible())
            {
                if (const SwTextAttr* pTextAttr = pTextFootnoteCnt->GetTextFootnote())
                {
                    const SwTextFootnote* pTextFootnote
                            = pTextAttr->GetFootnote().GetTextFootnote();
                    if (!pTextFootnote)
                        continue;
                    const SwTextNode& rTextNode = pTextFootnote->GetTextNode();
                    auto nStart = pTextAttr->GetStart();
                    pCursor->GetPoint()->Assign(rTextNode, nStart + 1);
                    rWrtShell.SetMark();
                    rWrtShell.SttSelect();
                    pCursor->GetPoint()->Assign(rTextNode, nStart);
                    rWrtShell.EndSelect();
                }
            }
        }
        rWrtShell.LeaveAddMode();
        rWrtShell.EndAction();
    }
}

// Content, contains names and reference at the content type.

SwContent::SwContent(const SwContentType* pCnt, OUString aName, double nYPos) :
    SwTypeNumber(CTYPE_CNT),
    m_pParent(pCnt),
    m_sContentName(std::move(aName)),
    m_nYPosition(nYPos),
    m_bInvisible(false)
{
}


SwTypeNumber::~SwTypeNumber()
{
}

bool SwContent::IsProtect() const
{
    return false;
}

bool SwTextFieldContent::IsProtect() const
{
    return m_pFormatField->IsProtect();
}

SwPostItField const* SwPostItContent::GetPostItField() const
{
    return m_pField
        ? static_cast<SwPostItField const*>(m_pField->GetField())
        : nullptr;
}

bool SwPostItContent::IsProtect() const
{
    return m_pField->IsProtect();
}

bool SwURLFieldContent::IsProtect() const
{
    return m_pINetAttr->IsProtect();
}

bool SwRegionContent::IsProtect() const
{
    return m_pSectionFormat->GetSection()->IsProtect();
}

SwGraphicContent::~SwGraphicContent()
{
}

SwTOXBaseContent::~SwTOXBaseContent()
{
}

const TranslateId STR_CONTENT_TYPE_ARY[] =
{
    STR_CONTENT_TYPE_OUTLINE,
    STR_CONTENT_TYPE_TABLE,
    STR_CONTENT_TYPE_FRAME,
    STR_CONTENT_TYPE_GRAPHIC,
    STR_CONTENT_TYPE_OLE,
    STR_CONTENT_TYPE_BOOKMARK,
    STR_CONTENT_TYPE_REGION,
    STR_CONTENT_TYPE_URLFIELD,
    STR_CONTENT_TYPE_REFERENCE,
    STR_CONTENT_TYPE_INDEX,
    STR_CONTENT_TYPE_POSTIT,
    STR_CONTENT_TYPE_DRAWOBJECT,
    STR_CONTENT_TYPE_TEXTFIELD,
    STR_CONTENT_TYPE_FOOTNOTE,
    STR_CONTENT_TYPE_ENDNOTE
};

const TranslateId STR_CONTENT_TYPE_SINGLE_ARY[] =
{
    STR_CONTENT_TYPE_SINGLE_OUTLINE,
    STR_CONTENT_TYPE_SINGLE_TABLE,
    STR_CONTENT_TYPE_SINGLE_FRAME,
    STR_CONTENT_TYPE_SINGLE_GRAPHIC,
    STR_CONTENT_TYPE_SINGLE_OLE,
    STR_CONTENT_TYPE_SINGLE_BOOKMARK,
    STR_CONTENT_TYPE_SINGLE_REGION,
    STR_CONTENT_TYPE_SINGLE_URLFIELD,
    STR_CONTENT_TYPE_SINGLE_REFERENCE,
    STR_CONTENT_TYPE_SINGLE_INDEX,
    STR_CONTENT_TYPE_SINGLE_POSTIT,
    STR_CONTENT_TYPE_SINGLE_DRAWOBJECT,
    STR_CONTENT_TYPE_SINGLE_TEXTFIELD,
    STR_CONTENT_TYPE_SINGLE_FOOTNOTE,
    STR_CONTENT_TYPE_SINGLE_ENDNOTE
};

namespace
{
    bool checkVisibilityChanged(
        const SwContentArr& rSwContentArrA,
        const SwContentArr& rSwContentArrB)
    {
        if(rSwContentArrA.size() != rSwContentArrB.size())
        {
            return true;
        }

        for(size_t a(0); a < rSwContentArrA.size(); a++)
        {
            if(rSwContentArrA[a]->IsInvisible() != rSwContentArrB[a]->IsInvisible())
            {
                return true;
            }
        }

        return false;
    }
// Gets "YPos" for content, i.e. a number used to sort content members in Navigator's list
sal_Int32 getYPos(const SwNode& rNode)
{
    SwNodeOffset nIndex = rNode.GetIndex();
    if (rNode.GetNodes().GetEndOfExtras().GetIndex() >= nIndex)
    {
        // Not a node of BodyText
        // Are we in a fly?
        if (const auto pFlyFormat = rNode.GetFlyFormat())
        {
            // Get node index of anchor
            if (SwNode* pAnchorNode = pFlyFormat->GetAnchor().GetAnchorNode())
            {
                return getYPos(*pAnchorNode);
            }
        }
    }
    return sal_Int32(nIndex);
}
// end of anonymous namespace

SwContentType::SwContentType(SwWrtShell* pShell, ContentTypeId nType, sal_uInt8 nLevel) :
    SwTypeNumber(CTYPE_CTT),
    m_pWrtShell(pShell),
    m_sContentTypeName(SwResId(STR_CONTENT_TYPE_ARY[static_cast<int>(nType)])),
    m_sSingleContentTypeName(SwResId(STR_CONTENT_TYPE_SINGLE_ARY[static_cast<int>(nType)])),
    m_nMemberCount(0),
    m_nContentType(nType),
    m_nOutlineLevel(nLevel),
    m_bDataValid(false),
    m_bEdit(false),
    m_bDelete(true)
{
    switch(m_nContentType)
    {
        case ContentTypeId::OUTLINE:
            m_sTypeToken = "outline";
        break;
        case ContentTypeId::TABLE:
            m_sTypeToken = "table";
            m_bEdit = true;
            m_bRenamable = true;
        break;
        case ContentTypeId::FRAME:
            m_sTypeToken = "frame";
            m_bEdit = true;
            m_bRenamable = true;
        break;
        case ContentTypeId::GRAPHIC:
            m_sTypeToken = "graphic";
            m_bEdit = true;
            m_bRenamable = true;
        break;
        case ContentTypeId::OLE:
            m_sTypeToken = "ole";
            m_bEdit = true;
            m_bRenamable = true;
        break;
        case ContentTypeId::TEXTFIELD:
            m_bEdit = true;
        break;
        case ContentTypeId::FOOTNOTE:
        case ContentTypeId::ENDNOTE:
            m_bEdit = true;
        break;
        case ContentTypeId::BOOKMARK:
        {
            const bool bProtectedBM = m_pWrtShell->getIDocumentSettingAccess().get(
                        DocumentSettingId::PROTECT_BOOKMARKS);
            m_bEdit = true;
            m_bDelete = !bProtectedBM;
            m_bRenamable = !bProtectedBM;
        }
        break;
        case ContentTypeId::REGION:
            m_sTypeToken = "region";
            m_bEdit = true;
            m_bRenamable = true;
        break;
        case ContentTypeId::INDEX:
            m_bEdit = true;
            m_bRenamable = true;
        break;
        case ContentTypeId::REFERENCE:
            m_bEdit = false;
        break;
        case ContentTypeId::URLFIELD:
            m_bEdit = true;
        break;
        case ContentTypeId::POSTIT:
            m_bEdit = true;
        break;
        case ContentTypeId::DRAWOBJECT:
            m_sTypeToken = "drawingobject";
            m_bEdit = true;
            m_bRenamable = true;
        break;
        defaultbreak;
    }

    const int nShift = static_cast<int>(m_nContentType);
    assert(nShift > -1);
    const sal_Int32 nMask = 1 << nShift;
    const sal_Int32 nBlock = SwModule::get()->GetNavigationConfig()->GetSortAlphabeticallyBlock();
    m_bAlphabeticSort = nBlock & nMask;

    FillMemberList();
}

SwContentType::~SwContentType()
{
}

const SwContent* SwContentType::GetMember(size_t nIndex)
{
    if(!m_bDataValid || !m_pMember)
    {
        FillMemberList();
    }
    if(nIndex < m_pMember->size())
        return (*m_pMember)[nIndex].get();

    return nullptr;
}

void SwContentType::Invalidate()
{
    m_bDataValid = false;
}

void SwContentType::FillMemberList(bool* pbContentChanged)
{
    std::unique_ptr<SwContentArr> pOldMember;
    size_t nOldMemberCount = 0;
    if(m_pMember && pbContentChanged)
    {
        pOldMember = std::move(m_pMember);
        nOldMemberCount = pOldMember->size();
        m_pMember.reset( new SwContentArr );
        *pbContentChanged = false;
    }
    else if(!m_pMember)
        m_pMember.reset( new SwContentArr );
    else
        m_pMember->clear();
    switch(m_nContentType)
    {
        case ContentTypeId::OUTLINE   :
        {
            const SwNodeOffset nEndOfExtrasIndex = m_pWrtShell->GetNodes().GetEndOfExtras().GetIndex();
            // provide for up to 99999 outline nodes in frames to be sorted in document layout order
            double nOutlinesInFramesIndexAdjustment = 0.00001;
            const SwOutlineNodes& rOutlineNodes(m_pWrtShell->GetNodes().GetOutLineNds());
            const size_t nOutlineCount = rOutlineNodes.size();

            for (size_t i = 0; i < nOutlineCount; ++i)
            {
                SwTextNode* pNode = rOutlineNodes[i]->GetTextNode();

                OUString aEntry(comphelper::string::stripStart(
                                m_pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText(
                                i, m_pWrtShell->GetLayout(), truefalsefalse), ' '));
                aEntry = SwNavigationPI::CleanEntry(aEntry);

                // tdf#157250 Show deleted headings tracked changes in the Navigator only when show
                // tracked changes is on
                if (m_pWrtShell->GetLayout()->IsHideRedlines()
                    && pNode->GetRedlineMergeFlag() != SwNode::Merge::None && aEntry.isEmpty())
                    continue;

                const sal_uInt8 nLevel = pNode->GetAttrOutlineLevel() - 1;
                if (nLevel >= m_nOutlineLevel)
                    continue;
                double nYPos = m_bAlphabeticSort ? 0 : static_cast<double>(getYPos(*pNode));
                if (nEndOfExtrasIndex >= pNode->GetIndex() && pNode->GetFlyFormat())
                {
                    nYPos += nOutlinesInFramesIndexAdjustment;
                    nOutlinesInFramesIndexAdjustment += 0.00001;
                }

                auto pCnt(std::make_unique<SwOutlineContent>(this, aEntry, i, nLevel,
                                                        m_pWrtShell->IsOutlineMovable(i), nYPos));
                if (!pNode->getLayoutFrame(m_pWrtShell->GetLayout()))
                    pCnt->SetInvisible();
                m_pMember->insert(std::move(pCnt));
            }

            // need to check level and equal entry number after creation due to possible outline
            // nodes in frames, headers, footers
            if (pOldMember)
            {
                assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
                if (pOldMember->size() != m_pMember->size())
                {
                    *pbContentChanged = true;
                    break;
                }
                for (size_t i = 0; i < pOldMember->size(); i++)
                {
                    if (static_cast<SwOutlineContent*>((*pOldMember)[i].get())->GetOutlineLevel() !=
                            static_cast<SwOutlineContent*>((*m_pMember)[i].get())->GetOutlineLevel())
                    {
                        *pbContentChanged = true;
                        break;
                    }
                }
            }
        }
        break;
        case ContentTypeId::TABLE     :
        {
            const size_t nCount = m_pWrtShell->GetTableFrameFormatCount(true);
            const sw::TableFrameFormats* pFrameFormats = m_pWrtShell->GetDoc()->GetTableFrameFormats();
            for(size_t n = 0, i = 0; i < nCount + n; ++i)
            {
                const SwTableFormat& rTableFormat = *(*pFrameFormats)[i];
                if(!rTableFormat.IsUsed())  // skip deleted tables
                {
                    n++;
                    continue;
                }
                tools::Long nYPos = 0;
                if (!m_bAlphabeticSort)
                {
                    if (SwTable* pTable = SwTable::FindTable(&rTableFormat))
                        nYPos = getYPos(*pTable->GetTableNode());
                }
                auto pCnt = std::make_unique<SwContent>(this, rTableFormat.GetName().toString(), nYPos);
                if(!rTableFormat.IsVisible())
                    pCnt->SetInvisible();
                m_pMember->insert(std::move(pCnt));
            }

            if (pOldMember)
            {
                // need to check visibility (and equal entry number) after
                // creation due to a sorted list being used here (before,
                // entries with same index were compared already at creation
                // time what worked before a sorted list was used)
                *pbContentChanged = checkVisibilityChanged(
                    *pOldMember,
                    *m_pMember);
            }
        }
        break;
        case ContentTypeId::OLE       :
        case ContentTypeId::FRAME     :
        case ContentTypeId::GRAPHIC   :
        {
            FlyCntType eType = FLYCNTTYPE_FRM;
            if(m_nContentType == ContentTypeId::OLE)
                eType = FLYCNTTYPE_OLE;
            else if(m_nContentType == ContentTypeId::GRAPHIC)
                eType = FLYCNTTYPE_GRF;
            Point aNullPt;
            size_t nCount = m_pWrtShell->GetFlyCount(eType, /*bIgnoreTextBoxes=*/true);
            std::vector<SwFrameFormat const*> formats(m_pWrtShell->GetFlyFrameFormats(eType, /*bIgnoreTextBoxes=*/true));
            SAL_WARN_IF(nCount != formats.size(), "sw.ui""Count differs");
            nCount = formats.size();
            for (size_t i = 0; i < nCount; ++i)
            {
                SwFrameFormat const*const pFrameFormat = formats[i];
                const UIName sFrameName = pFrameFormat->GetName();

                SwContent* pCnt;
                tools::Long nYPos =
                        m_bAlphabeticSort ? 0 : pFrameFormat->FindLayoutRect(false, &aNullPt).Top();
                if(ContentTypeId::GRAPHIC == m_nContentType)
                {
                    OUString sLink;
                    SwDoc::GetGrfNms(*static_cast<const SwFlyFrameFormat*>(pFrameFormat), &sLink,
                                     nullptr);
                    pCnt = new SwGraphicContent(this, sFrameName.toString(), INetURLObject::decode(sLink,
                                           INetURLObject::DecodeMechanism::Unambiguous), nYPos);
                }
                else
                {
                    pCnt = new SwContent(this, sFrameName.toString(), nYPos);
                }
                if(!pFrameFormat->IsVisible())
                    pCnt->SetInvisible();
                m_pMember->insert(std::unique_ptr<SwContent>(pCnt));
            }

            if (pOldMember)
            {
                // need to check visibility (and equal entry number) after
                // creation due to a sorted list being used here (before,
                // entries with same index were compared already at creation
                // time what worked before a sorted list was used)
                assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
                *pbContentChanged = checkVisibilityChanged(
                    *pOldMember,
                    *m_pMember);
            }
        }
        break;
        case ContentTypeId::BOOKMARK:
        {
            tools::Long nYPos = 0;
            IDocumentMarkAccess* const pMarkAccess = m_pWrtShell->getIDocumentMarkAccess();
            for(auto ppBookmark = pMarkAccess->getBookmarksBegin();
                ppBookmark != pMarkAccess->getBookmarksEnd();
                ++ppBookmark)
            {
                if(lcl_IsUiVisibleBookmark(*ppBookmark))
                {
                    const SwMarkName& rBkmName = (*ppBookmark)->GetName();
                    //nYPos from 0 -> text::Bookmarks will be sorted alphabetically
                    auto pCnt(std::make_unique<SwContent>(this, rBkmName.toString(),
                                                          m_bAlphabeticSort ? 0 : nYPos++));
                    m_pMember->insert(std::move(pCnt));
                }
            }
        }
        break;
        case ContentTypeId::TEXTFIELD:
        {
            std::vector<SwTextField*> aArr;
            const SwFieldTypes& rFieldTypes =
                    *m_pWrtShell->GetDoc()->getIDocumentFieldsAccess().GetFieldTypes();
            const size_t nSize = rFieldTypes.size();
            for (size_t i = 0; i < nSize; ++i)
            {
                const SwFieldType* pFieldType = rFieldTypes[i].get();
                if (pFieldType->Which() == SwFieldIds::Postit)
                    continue;
                std::vector<SwFormatField*> vFields;
                pFieldType->GatherFields(vFields);
                for (SwFormatField* pFormatField: vFields)
                {
                    if (SwTextField* pTextField = pFormatField->GetTextField())
                    {
                        // fields in header footer don't behave well, skip them
                        if (m_pWrtShell->GetDoc()->IsInHeaderFooter(pTextField->GetTextNode()))
                            continue;
                        aArr.emplace_back(pTextField);
                    }
                }
            }
            if (!m_bAlphabeticSort)
            {
                const SwNodeOffset nEndOfExtrasIndex =
                        m_pWrtShell->GetNodes().GetEndOfExtras().GetIndex();
                // use stable sort array to list fields in document model order
                std::stable_sort(aArr.begin(), aArr.end(),
                                 [&nEndOfExtrasIndex, this](
                                 const SwTextField* a, const SwTextField* b){
                    SwPosition aPos(a->GetTextNode(), a->GetStart());
                    SwPosition bPos(b->GetTextNode(), b->GetStart());
                    // use anchor position for entries that are located in flys
                    if (nEndOfExtrasIndex >= aPos.GetNodeIndex())
                        getAnchorPos(aPos);
                    if (nEndOfExtrasIndex >= bPos.GetNodeIndex())
                        getAnchorPos(bPos);
                    if (aPos == bPos)
                    {
                        // probably in same or nested fly frame
                        // sort using layout position
                        SwRect aCharRect, bCharRect;
                        std::shared_ptr<SwPaM> pPamForTextField;
                        if (SwTextFrame* pFrame = static_cast<SwTextFrame*>(
                                    a->GetTextNode().getLayoutFrame(m_pWrtShell->GetLayout())))
                        {
                            SwTextField::GetPamForTextField(*a, pPamForTextField);
                            if (pPamForTextField)
                                pFrame->GetCharRect(aCharRect, *pPamForTextField->GetPoint());
                        }
                        if (SwTextFrame* pFrame = static_cast<SwTextFrame*>(
                                    b->GetTextNode().getLayoutFrame(m_pWrtShell->GetLayout())))
                        {
                            SwTextField::GetPamForTextField(*b, pPamForTextField);
                            if (pPamForTextField)
                                pFrame->GetCharRect(bCharRect, *pPamForTextField->GetPoint());
                        }
                        return aCharRect.Top() < bCharRect.Top();
                    }
                    return aPos < bPos;});
            }
            std::vector<OUString> aDocumentStatisticsSubTypesList;
            tools::Long nYPos = 0;
            for (SwTextField* pTextField : aArr)
            {
                const SwField* pField = pTextField->GetFormatField().GetField();
                OUString sExpandField = pField->ExpandField(true, m_pWrtShell->GetLayout());
                if (!sExpandField.isEmpty())
                    sExpandField = u" - " + sExpandField;
                OUString sText;
                if (pField->GetTypeId() == SwFieldTypesEnum::DocumentStatistics)
                {
                    if (aDocumentStatisticsSubTypesList.empty())
                        SwFieldMgr(m_pWrtShell).GetSubTypes(SwFieldTypesEnum::DocumentStatistics,
                                                            aDocumentStatisticsSubTypesList);
                    OUString sSubType;
                    if (pField->GetSubType() < aDocumentStatisticsSubTypesList.size())
                        sSubType = u" - " + aDocumentStatisticsSubTypesList[pField->GetSubType()];
                    sText = pField->GetDescription() + u" - " + pField->GetFieldName() + sSubType +
                            sExpandField;
                }
                else if (pField->GetTypeId() == SwFieldTypesEnum::GetRef)
                {
                    assert(dynamic_cast<const SwGetRefField*>(pField));
                    const SwGetRefField* pRefField(static_cast<const SwGetRefField*>(pField));
                    if (pRefField->IsRefToHeadingCrossRefBookmark() ||
                            pRefField->IsRefToNumItemCrossRefBookmark())
                    {
                        OUString sExpandedTextOfReferencedTextNode =
                                pRefField->GetExpandedTextOfReferencedTextNode(
                                    *m_pWrtShell->GetLayout());
                        if (sExpandedTextOfReferencedTextNode.getLength() > 80)
                        {
                            sExpandedTextOfReferencedTextNode = OUString::Concat(
                                        sExpandedTextOfReferencedTextNode.subView(0, 80)) + u"...";
                        }
                        sText = pField->GetDescription() + u" - "
                                + sExpandedTextOfReferencedTextNode + sExpandField;
                    }
                    else
                    {
                        OUString sFieldSubTypeOrName;
                        auto nSubType = pField->GetSubType();
                        if (nSubType == REF_FOOTNOTE)
                            sFieldSubTypeOrName = SwResId(STR_FLDREF_FOOTNOTE);
                        else if (nSubType == REF_ENDNOTE)
                            sFieldSubTypeOrName = SwResId(STR_FLDREF_ENDNOTE);
                        else
                            sFieldSubTypeOrName = pField->GetFieldName();
                        sText = pField->GetDescription() + u" - " + sFieldSubTypeOrName
                                + sExpandField;
                    }
                }
                else
                    sText = pField->GetDescription() + u" - " + pField->GetFieldName()
                            + sExpandField;
                auto pCnt(std::make_unique<SwTextFieldContent>(this, sText,
                                                               &pTextField->GetFormatField(),
                                                               m_bAlphabeticSort ? 0 : nYPos++));
                if (!pTextField->GetTextNode().getLayoutFrame(m_pWrtShell->GetLayout()))
                    pCnt->SetInvisible();
                m_pMember->insert(std::move(pCnt));
            }
        }
        break;
        // We will separate footnotes and endnotes here.
        case ContentTypeId::FOOTNOTE:
        case ContentTypeId::ENDNOTE:
        {
            const SwFootnoteIdxs& rFootnoteIdxs = m_pWrtShell->GetDoc()->GetFootnoteIdxs();
            if (rFootnoteIdxs.empty())
                break;
            // insert footnotes and endnotes
            tools::Long nPos = 0;
            for (const SwTextFootnote* pTextFootnote : rFootnoteIdxs)
            {
                if ((!pTextFootnote->GetFootnote().IsEndNote()
                     && m_nContentType == ContentTypeId::FOOTNOTE)
                    || (pTextFootnote->GetFootnote().IsEndNote()
                        && m_nContentType == ContentTypeId::ENDNOTE))
                {
                    const SwFormatFootnote& rFormatFootnote = pTextFootnote->GetFootnote();
                    const OUString sText
                        = rFormatFootnote.GetViewNumStr(*m_pWrtShell->GetDoc(),
                                                        m_pWrtShell->GetLayout(), true)
                          + " " + lcl_GetFootnoteText(*pTextFootnote);
                    auto pCnt(std::make_unique<SwTextFootnoteContent>(
                        this, sText, pTextFootnote, ++nPos));
                    if (!pTextFootnote->GetTextNode().getLayoutFrame(m_pWrtShell->GetLayout()))
                        pCnt->SetInvisible();
                    m_pMember->insert(std::move(pCnt));
                }
            }
        }
        break;
        case ContentTypeId::REGION    :
        {
            size_t nCount = m_pWrtShell->GetSectionFormatCount();
            for (size_t i = 0; i < nCount; ++i)
            {
                const SwSectionFormat* pFormat = &m_pWrtShell->GetSectionFormat(i);
                if (!pFormat->IsInNodesArr())
                    continue;
                const SwSection* pSection = pFormat->GetSection();
                if (SectionType eTmpType = pSection->GetType();
                    eTmpType == SectionType::ToxContent || eTmpType == SectionType::ToxHeader)
                    continue;
                const SwNodeIndex* pNodeIndex = pFormat->GetContent().GetContentIdx();
                if (pNodeIndex)
                {
                    const UIName& sSectionName = pSection->GetSectionName();

                    sal_uInt8 nLevel = 0;
                    SwSectionFormat* pParentFormat = pFormat->GetParent();
                    while(pParentFormat)
                    {
                        nLevel++;
                        pParentFormat = pParentFormat->GetParent();
                    }

                    auto pCnt(std::make_unique<SwRegionContent>(this, sSectionName.toString(), nLevel,
                                            m_bAlphabeticSort ? 0 : getYPos(pNodeIndex->GetNode()),
                                                                pFormat));

                    if (!pFormat->IsVisible() || pSection->IsHidden())
                        pCnt->SetInvisible();
                    m_pMember->insert(std::move(pCnt));
                }

                if (pOldMember)
                {
                    // need to check visibility (and equal entry number) after
                    // creation due to a sorted list being used here (before,
                    // entries with same index were compared already at creation
                    // time what worked before a sorted list was used)
                    assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
                    *pbContentChanged = checkVisibilityChanged(
                        *pOldMember,
                        *m_pMember);
                }
            }
        }
        break;
        case ContentTypeId::REFERENCE:
        {
            std::vector<OUString> aRefMarks;
            m_pWrtShell->GetRefMarks( &aRefMarks );

            tools::Long nYPos = 0;
            for (const auto& rRefMark : aRefMarks)
            {
                m_pMember->insert(std::make_unique<SwContent>(this, rRefMark,
                                                              m_bAlphabeticSort ? 0 : nYPos++));
            }
        }
        break;
        case ContentTypeId::URLFIELD:
        {
            SwGetINetAttrs aArr;
            m_pWrtShell->GetINetAttrs(aArr, false);

            if (m_bAlphabeticSort)
            {
                for (auto& r : aArr)
                {
                    auto pCnt(std::make_unique<SwURLFieldContent>(this, r.sText, INetURLObject::decode(
                                                    r.rINetAttr.GetINetFormat().GetValue(),
                                                    INetURLObject::DecodeMechanism::Unambiguous),
                                                             &r.rINetAttr, 0));
                    m_pMember->insert(std::move(pCnt));
                }
                break;
            }

            // use stable sort array to list hyperlinks in document order
            const SwNodeOffset nEndOfExtrasIndex = m_pWrtShell->GetNodes().GetEndOfExtras().GetIndex();
            bool bHasEntryInFly = false;
            std::vector<SwGetINetAttr*> aStableSortINetAttrsArray;

            for (SwGetINetAttr& r : aArr)
            {
                aStableSortINetAttrsArray.emplace_back(&r);
                if (!bHasEntryInFly)
                {
                    if (nEndOfExtrasIndex >= r.rINetAttr.GetTextNode().GetIndex())
                    {
                        // Not a node of BodyText
                        // Are we in a fly?
                        if (r.rINetAttr.GetTextNode().GetFlyFormat())
                            bHasEntryInFly = true;
                    }
                }
            }

            std::stable_sort(aStableSortINetAttrsArray.begin(), aStableSortINetAttrsArray.end(),
                             [](const SwGetINetAttr* a, const SwGetINetAttr* b){
                SwPosition aSwPos(a->rINetAttr.GetTextNode(),
                                  a->rINetAttr.GetStart());
                SwPosition bSwPos(b->rINetAttr.GetTextNode(),
                                  b->rINetAttr.GetStart());
                return aSwPos < bSwPos;});

            // When there are hyperlinks in text frames do an additional sort using the text frame
            // anchor position to place entries in the order of document layout appearance.
            if (bHasEntryInFly)
            {
                std::stable_sort(aStableSortINetAttrsArray.begin(), aStableSortINetAttrsArray.end(),
                                 [nEndOfExtrasIndex](const SwGetINetAttr* a, const SwGetINetAttr* b){
                    const SwTextNode& aTextNode = a->rINetAttr.GetTextNode();
                    const SwTextNode& bTextNode = b->rINetAttr.GetTextNode();
                    SwPosition aPos(aTextNode, a->rINetAttr.GetStart());
                    SwPosition bPos(bTextNode, b->rINetAttr.GetStart());
                    // use anchor position for entries that are located in flys
                    if (nEndOfExtrasIndex >= aTextNode.GetIndex())
                        if (auto pFlyFormat = aTextNode.GetFlyFormat())
                            if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor())
                                aPos = *pPos;
                    if (nEndOfExtrasIndex >= bTextNode.GetIndex())
                        if (auto pFlyFormat = bTextNode.GetFlyFormat())
                            if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor())
                                bPos = *pPos;
                    return aPos < bPos;});
            }

            SwGetINetAttrs::size_type n = 0;
            for (auto p : aStableSortINetAttrsArray)
            {
                auto pCnt = std::make_unique<SwURLFieldContent>(this, p->sText,
                            INetURLObject::decode(p->rINetAttr.GetINetFormat().GetValue(),
                                                  INetURLObject::DecodeMechanism::Unambiguous),
                            &p->rINetAttr, ++n);
                m_pMember->insert(std::move(pCnt));
            }
        }
        break;
        case ContentTypeId::INDEX:
        {
            const sal_uInt16 nCount = m_pWrtShell->GetTOXCount();

            for ( sal_uInt16 nTox = 0; nTox < nCount; nTox++ )
            {
                const SwTOXBase* pBase = m_pWrtShell->GetTOX( nTox );
                UIName sTOXNm( pBase->GetTOXName() );

                SwContent* pCnt = new SwTOXBaseContent(
                        this, sTOXNm.toString(), m_bAlphabeticSort ? 0 : nTox, *pBase);

                if(pBase && !pBase->IsVisible())
                    pCnt->SetInvisible();

                m_pMember->insert( std::unique_ptr<SwContent>(pCnt) );
                const size_t nPos = m_pMember->size() - 1;
                if (pOldMember)
                {
                    assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
                    if (!*pbContentChanged && nOldMemberCount > nPos &&
                            (*pOldMember)[nPos]->IsInvisible() != pCnt->IsInvisible())
                        *pbContentChanged = true;
                }
            }
        }
        break;
        case ContentTypeId::POSTIT:
        {
            SwPostItMgr* aMgr = m_pWrtShell->GetView().GetPostItMgr();
            if (aMgr)
            {
                tools::Long nYPos = 0;
                for(SwPostItMgr::const_iterator i = aMgr->begin(); i != aMgr->end(); ++i)
                {
                    if (const SwFormatField* pFormatField = dynamic_cast<const SwFormatField *>((*i)->GetBroadcaster())) // SwPostit
                    {
                        if (pFormatField->GetTextField() && pFormatField->IsFieldInDoc())
                        {
                            OUString sEntry = pFormatField->GetField()->GetPar2();
                            sEntry = RemoveNewline(sEntry);
                            std::unique_ptr<SwPostItContent> pCnt(new SwPostItContent(
                                                this,
                                                sEntry,
                                                pFormatField,
                                                nYPos));
                            if (!pFormatField->GetTextField()->GetTextNode().getLayoutFrame(
                                        m_pWrtShell->GetLayout()))
                                pCnt->SetInvisible();
                            if (pOldMember)
                            {
                                assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
                                if (!*pbContentChanged &&
                                        nOldMemberCount > o3tl::make_unsigned(nYPos) &&
                                        (*pOldMember)[nYPos]->IsInvisible() != pCnt->IsInvisible())
                                    *pbContentChanged = true;
                            }
                            m_pMember->insert(std::move(pCnt));
                            nYPos++;
                        }
                    }
                }
            }
        }
        break;
        case ContentTypeId::DRAWOBJECT:
        {
            IDocumentDrawModelAccess& rIDDMA = m_pWrtShell->getIDocumentDrawModelAccess();
            SwDrawModel* pModel = rIDDMA.GetDrawModel();
            if(pModel)
            {
                SdrPage* pPage = pModel->GetPage(0);
                for (const rtl::Reference<SdrObject>& pTemp : *pPage)
                {
                    // #i51726# - all drawing objects can be named now
                    if (!pTemp->IsVirtualObj() && !pTemp->GetName().isEmpty())
                    {
                        tools::Long nYPos = LONG_MIN;
                        const bool bIsVisible = rIDDMA.IsVisibleLayerId(pTemp->GetLayer());
                        if (bIsVisible)
                            nYPos = m_bAlphabeticSort ? 0 : pTemp->GetLogicRect().Top();
                        auto pCnt(std::make_unique<SwContent>(this, pTemp->GetName(), nYPos));
                        if (!bIsVisible)
                            pCnt->SetInvisible();
                        m_pMember->insert(std::move(pCnt));
                    }
                }

                if (pOldMember)
                {
                    // need to check visibility (and equal entry number) after
                    // creation due to a sorted list being used here (before,
                    // entries with same index were compared already at creation
                    // time what worked before a sorted list was used)
                    assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
                    *pbContentChanged = checkVisibilityChanged(
                        *pOldMember,
                        *m_pMember);
                }
            }
        }
        break;
        defaultbreak;
    }
    m_nMemberCount = m_pMember->size();
    if (pOldMember)
    {
        assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
        if (!*pbContentChanged && pOldMember->size() != m_nMemberCount)
            *pbContentChanged = true;
    }

    m_bDataValid = true;
}

namespace {

enum STR_CONTEXT_IDX
{
    IDX_STR_OUTLINE_LEVEL = 0,
    IDX_STR_DISPLAY = 1,
    IDX_STR_ACTIVE_VIEW = 2,
    IDX_STR_HIDDEN = 3,
    IDX_STR_ACTIVE = 4,
    IDX_STR_INACTIVE = 5,
    IDX_STR_EDIT_ENTRY = 6,
    IDX_STR_DELETE_ENTRY = 7,
    IDX_STR_SEND_OUTLINE_TO_CLIPBOARD_ENTRY = 8,
    IDX_STR_OUTLINE_TRACKING = 9,
    IDX_STR_OUTLINE_TRACKING_DEFAULT = 10,
    IDX_STR_OUTLINE_TRACKING_FOCUS = 11,
    IDX_STR_OUTLINE_TRACKING_OFF = 12
};

}

const TranslateId STR_CONTEXT_ARY[] =
{
    STR_OUTLINE_LEVEL,
    STR_DISPLAY,
    STR_ACTIVE_VIEW,
    STR_HIDDEN,
    STR_ACTIVE,
    STR_INACTIVE,
    STR_EDIT_ENTRY,
    STR_DELETE_ENTRY,
    STR_SEND_OUTLINE_TO_CLIPBOARD_ENTRY,
    STR_OUTLINE_TRACKING,
    STR_OUTLINE_TRACKING_DEFAULT,
    STR_OUTLINE_TRACKING_FOCUS,
    STR_OUTLINE_TRACKING_OFF
};

SwContentTree::SwContentTree(std::unique_ptr<weld::TreeView> xTreeView, SwNavigationPI* pDialog)
    : m_xTreeView(std::move(xTreeView))
    , m_aDropTargetHelper(*this)
    , m_pDialog(pDialog)
    , m_sSpace(u" "_ustr)
    , m_aUpdTimer("SwContentTree m_aUpdTimer")
    , m_aOverlayObjectDelayTimer("SwContentTree m_aOverlayObjectDelayTimer")
    , m_sInvisible(SwResId(STR_INVISIBLE))
    , m_pHiddenShell(nullptr)
    , m_pActiveShell(nullptr)
    , m_pConfig(SwModule::get()->GetNavigationConfig())
    , m_nActiveBlock(0)
    , m_nHiddenBlock(0)
    , m_nEntryCount(0)
    , m_nRootType(ContentTypeId::UNKNOWN)
    , m_nLastSelType(ContentTypeId::UNKNOWN)
    , m_nOutlineLevel(MAXLEVEL)
    , m_eState(State::ACTIVE)
    , m_bIsRoot(false)
    , m_bIsIdleClear(false)
    , m_bIsLastReadOnly(false)
    , m_bIsOutlineMoveable(true)
    , m_bViewHasChanged(false)
{
    m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 30,
                                  m_xTreeView->get_text_height() * 14);

    m_xTreeView->set_help_id(HID_NAVIGATOR_TREELIST);

    m_xTreeView->connect_expanding(LINK(this, SwContentTree, ExpandHdl));
    m_xTreeView->connect_collapsing(LINK(this, SwContentTree, CollapseHdl));
    m_xTreeView->connect_row_activated(LINK(this, SwContentTree, ContentDoubleClickHdl));
    m_xTreeView->connect_selection_changed(LINK(this, SwContentTree, SelectHdl));
    m_xTreeView->connect_focus_in(LINK(this, SwContentTree, FocusInHdl));
    m_xTreeView->connect_key_press(LINK(this, SwContentTree, KeyInputHdl));
    m_xTreeView->connect_popup_menu(LINK(this, SwContentTree, CommandHdl));
    m_xTreeView->connect_query_tooltip(LINK(this, SwContentTree, QueryTooltipHdl));
    m_xTreeView->connect_drag_begin(LINK(this, SwContentTree, DragBeginHdl));
    m_xTreeView->connect_mouse_move(LINK(this, SwContentTree, MouseMoveHdl));
    m_xTreeView->connect_mouse_press(LINK(this, SwContentTree, MousePressHdl));
    m_xTreeView->set_column_editables({true});
    m_xTreeView->connect_editing(LINK(this, SwContentTree, EditingEntryHdl),
                                 LINK(this, SwContentTree, EditedEntryHdl));

    for (ContentTypeId i : o3tl::enumrange<ContentTypeId>())
    {
        if (i != ContentTypeId::OUTLINE)
            mTrackContentType[i] = true;
        m_aActiveContentArr[i] = nullptr;
        m_aHiddenContentArr[i] = nullptr;
    }
    for (int i = 0; i < CONTEXT_COUNT; ++i)
    {
        m_aContextStrings[i] = SwResId(STR_CONTEXT_ARY[i]);
    }
    m_nActiveBlock = m_pConfig->GetActiveBlock();

    // Restore outline headings expand state (same session persistence only)
    if (SwView* pView = GetActiveView(); pView && pView->GetDocShell())
    {
        OUString sDocTitle = pView->GetDocShell()->GetTitle();
        auto it = lcl_DocOutLineExpandStateMap.find(sDocTitle);
        if (it != lcl_DocOutLineExpandStateMap.end())
            mOutLineNodeMap = it->second;
        if (comphelper::LibreOfficeKit::isActive()) {
            if (pView->m_nNaviExpandedStatus < 0)
                m_nActiveBlock = 1;
            else
                m_nActiveBlock = pView->m_nNaviExpandedStatus;
        }
    }

    m_aUpdTimer.SetInvokeHandler(LINK(this, SwContentTree, TimerUpdate));
    m_aUpdTimer.SetTimeout(1000);
    m_aOverlayObjectDelayTimer.SetInvokeHandler(LINK(this, SwContentTree, OverlayObjectDelayTimerHdl));
    m_aOverlayObjectDelayTimer.SetTimeout(500);
}

SwContentTree::~SwContentTree()
{
    if (SwView* pView = GetActiveView(); pView && pView->GetDocShell())
    {
        OUString sDocTitle = pView->GetDocShell()->GetTitle();
        lcl_DocOutLineExpandStateMap[sDocTitle] = mOutLineNodeMap;
        if (comphelper::LibreOfficeKit::isActive())
            pView->m_nNaviExpandedStatus = m_nActiveBlock;
    }
    clear(); // If applicable erase content types previously.
    m_aUpdTimer.Stop();
    SetActiveShell(nullptr);
}

IMPL_LINK(SwContentTree, MousePressHdl, const MouseEvent&, rMEvt, bool)
{
    m_bSelectTo = rMEvt.IsShift() && (m_pConfig->IsNavigateOnSelect() || rMEvt.GetClicks() == 2);
    return false;
}

IMPL_LINK(SwContentTree, MouseMoveHdl, const MouseEvent&, rMEvt, bool)
{
    // Prevent trying to bring entry to attention when handling document change. The mouse over
    // entry might not be valid, for example, when the mouse pointer is on an entry that is deleted
    // in the document by an undo/redo.
    if (m_bDocHasChanged)
        return false;
    if (m_eState == State::HIDDEN)
        return false;
    if (std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
            m_xTreeView->get_dest_row_at_pos(rMEvt.GetPosPixel(), xEntry.get(), falsefalse) &&
            !rMEvt.IsLeaveWindow())
    {
        if (!m_xOverlayCompareEntry)
            m_xOverlayCompareEntry.reset(m_xTreeView->make_iterator().release());
        else if (m_xTreeView->iter_compare(*xEntry, *m_xOverlayCompareEntry) == 0)
            return false// The entry under the mouse has not changed.
        m_xTreeView->copy_iterator(*xEntry, *m_xOverlayCompareEntry);
        BringEntryToAttention(*xEntry);
    }
    else
    {
        if (m_xOverlayCompareEntry)
            m_xOverlayCompareEntry.reset();
        m_aOverlayObjectDelayTimer.Stop();
        if (m_xOverlayObject && m_xOverlayObject->getOverlayManager())
        {
            m_xOverlayObject->getOverlayManager()->remove(*m_xOverlayObject);
            m_xOverlayObject.reset();
        }
    }
    return false;
}

// Drag&Drop methods
IMPL_LINK(SwContentTree, DragBeginHdl, bool&, rUnsetDragIcon, bool)
{
    rUnsetDragIcon = true;

    bool bDisallow = true;

    // don't allow if tree root is selected
    std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
    bool bEntry = m_xTreeView->get_selected(xEntry.get());
    if (!bEntry || lcl_IsContentType(*xEntry, *m_xTreeView))
    {
        return true// disallow
    }

    if (m_bIsRoot && m_nRootType == ContentTypeId::OUTLINE)
    {
        // Only move drag entry and continuous selected siblings:
        m_aDndOutlinesSelected.clear();

        std::unique_ptr<weld::TreeIter> xScratch(m_xTreeView->make_iterator(xEntry.get()));

        // Find first selected of continuous siblings
        while (m_xTreeView->iter_previous_sibling(*xScratch) && m_xTreeView->is_selected(*xScratch))
        {
            m_xTreeView->copy_iterator(*xScratch, *xEntry);
        }
        // Record continuous selected siblings
        do
        {
            SwOutlineContent* pOutlineContent
                = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xEntry));
            if (!pOutlineContent) // shouldn't happen
                continue;
            m_aDndOutlinesSelected.push_back(pOutlineContent->GetOutlinePos());
        } while (m_xTreeView->iter_next_sibling(*xEntry) && m_xTreeView->is_selected(*xEntry));

        bDisallow = false;
    }

    rtl::Reference<TransferDataContainer> xContainer = new TransferDataContainer;

    if (FillTransferData(*xContainer))
    {
        bDisallow = false;
        m_xTreeView->enable_drag_source(xContainer, DND_ACTION_COPY);
    }

    return bDisallow;
}

SwContentTreeDropTarget::SwContentTreeDropTarget(SwContentTree& rTreeView)
    : DropTargetHelper(rTreeView.get_widget().get_drop_target())
    , m_rTreeView(rTreeView)
{
}

sal_Int8 SwContentTreeDropTarget::AcceptDrop(const AcceptDropEvent& rEvt)
{
    sal_Int8 nAccept = m_rTreeView.AcceptDrop(rEvt);

    if (nAccept != DND_ACTION_NONE)
    {
        // to enable the autoscroll when we're close to the edges
        weld::TreeView& rWidget = m_rTreeView.get_widget();
        rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true);
    }

    return nAccept;
}

bool SwContentTree::IsInDrag() const
{
    return m_xTreeView->get_drag_source() == m_xTreeView.get();
}

bool SwContentTree::HasHeadings() const
{
    const std::unique_ptr<SwContentType>& rpContentT = m_aActiveContentArr[ContentTypeId::OUTLINE];
    if (rpContentT && rpContentT->GetMemberCount() > 0)
        return true;
    return false;
}

// QueryDrop will be executed in the navigator
sal_Int8 SwContentTree::AcceptDrop(const AcceptDropEvent& rEvt)
{
    sal_Int8 nRet = DND_ACTION_NONE;
    if( m_bIsRoot )
    {
        if( m_bIsOutlineMoveable )
            nRet = rEvt.mnAction;
    }
    else if (!IsInDrag())
        nRet = GetParentWindow()->AcceptDrop();
    return nRet;
}

// Drop will be executed in the navigator
static void* lcl_GetOutlineKey(SwContentTree& rTree, SwOutlineContent const * pContent)
{
    void* key = nullptr;
    if (pContent)
    {
        SwWrtShell* pShell = rTree.GetWrtShell();
        auto const nPos = pContent->GetOutlinePos();

        key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos ));
    }
    return key;
}

sal_Int8 SwContentTreeDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt)
{
    return m_rTreeView.ExecuteDrop(rEvt);
}

sal_Int8 SwContentTree::ExecuteDrop(const ExecuteDropEvent& rEvt)
{
    std::unique_ptr<weld::TreeIter> xDropEntry(m_xTreeView->make_iterator());
    if (!m_xTreeView->get_dest_row_at_pos(rEvt.maPosPixel, xDropEntry.get(), true))
        xDropEntry.reset();

    if (m_nRootType == ContentTypeId::OUTLINE)
    {
        if (xDropEntry && lcl_IsContent(*xDropEntry, *m_xTreeView))
        {
            assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xDropEntry))));
            SwOutlineContent* pOutlineContent = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xDropEntry));
            assert(pOutlineContent);

            void* key = lcl_GetOutlineKey(*this, pOutlineContent);
            assert(key);
            if (!mOutLineNodeMap[key])
            {
                while (m_xTreeView->iter_has_child(*xDropEntry))
                {
                    std::unique_ptr<weld::TreeIter> xChildEntry(m_xTreeView->make_iterator(xDropEntry.get()));
                    bool bChildEntry = m_xTreeView->iter_children(*xChildEntry);
                    while (bChildEntry)
                    {
                        m_xTreeView->copy_iterator(*xChildEntry, *xDropEntry);
                        bChildEntry = m_xTreeView->iter_next_sibling(*xChildEntry);
                    }
                }
            }
        }

        SwOutlineNodes::size_type nTargetPos = 0;
        if (!xDropEntry)
        {
            // dropped in blank space -> move to bottom
            nTargetPos = GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineNodesCount() - 1;
        }
        else if (!lcl_IsContent(*xDropEntry, *m_xTreeView))
        {
            // dropped on "heading" parent -> move to start
            nTargetPos = SwOutlineNodes::npos;
        }
        else
        {
            assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xDropEntry))));
            nTargetPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xDropEntry))->GetOutlinePos();
        }

        if( MAXLEVEL > m_nOutlineLevel && // Not all layers are displayed.
                        nTargetPos != SwOutlineNodes::npos)
        {
            std::unique_ptr<weld::TreeIter> xNext(m_xTreeView->make_iterator(xDropEntry.get()));
            bool bNext = m_xTreeView->iter_next(*xNext);
            if (bNext)
            {
                assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xNext))));
                nTargetPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xNext))->GetOutlinePos() - 1;
            }
            else
                nTargetPos = GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineNodesCount() - 1;
        }

        // remove the drop highlight before we change the contents of the tree so we don't
        // try and dereference a removed entry in post-processing drop
        m_xTreeView->unset_drag_dest_row();
        MoveOutline(nTargetPos);

    }
    return IsInDrag() ? DND_ACTION_NONE : GetParentWindow()->ExecuteDrop(rEvt);
}

namespace
{
    bool IsAllExpanded(const weld::TreeView& rContentTree, const weld::TreeIter& rEntry)
    {
        if (!rContentTree.get_row_expanded(rEntry))
            return false;

        if (!rContentTree.iter_has_child(rEntry))
            return false;

        std::unique_ptr<weld::TreeIter> xChild(rContentTree.make_iterator(&rEntry));
        (void)rContentTree.iter_children(*xChild);

        do
        {
            if (rContentTree.iter_has_child(*xChild) || rContentTree.get_children_on_demand(*xChild))
            {
                if (!IsAllExpanded(rContentTree, *xChild))
                    return false;
            }
        }
        while (rContentTree.iter_next_sibling(*xChild));
        return true;
    }

    void ExpandOrCollapseAll(weld::TreeView& rContentTree, weld::TreeIter& rEntry)
    {
        bool bExpand = !IsAllExpanded(rContentTree, rEntry);
        bExpand ? rContentTree.expand_row(rEntry) : rContentTree.collapse_row(rEntry);
        int nRefDepth = rContentTree.get_iter_depth(rEntry);
        while (rContentTree.iter_next(rEntry) && rContentTree.get_iter_depth(rEntry) > nRefDepth)
        {
            if (rContentTree.iter_has_child(rEntry))
                bExpand ? rContentTree.expand_row(rEntry) : rContentTree.collapse_row(rEntry);
        }
    }
}

// Handler for Dragging and ContextMenu
static bool lcl_InsertExpandCollapseAllItem(const weld::TreeView& rContentTree, const weld::TreeIter& rEntry, weld::Menu& rPop)
{
    if (rContentTree.iter_has_child(rEntry) || rContentTree.get_children_on_demand(rEntry))
    {
        rPop.set_label(OUString::number(800), IsAllExpanded(rContentTree, rEntry) ? SwResId(STR_COLLAPSEALL) : SwResId(STR_EXPANDALL));
        return false;
    }
    return true;
}

static void lcl_SetOutlineContentEntriesSensitivities(SwContentTree* pThis, const weld::TreeView& rContentTree, const weld::TreeIter& rEntry, weld::Menu& rPop)
{
    rPop.set_sensitive(OUString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY), false);
    rPop.set_sensitive(OUString::number(HIDE_OUTLINE_CONTENT_VISIBILITY), false);
    rPop.set_sensitive(OUString::number(SHOW_OUTLINE_CONTENT_VISIBILITY), false);

    // todo: multi selection
    if (rContentTree.count_selected_rows() > 1)
        return;

    bool bIsRoot = lcl_IsContentType(rEntry, rContentTree);

    const SwNodes& rNodes = pThis->GetWrtShell()->GetNodes();
    const SwOutlineNodes& rOutlineNodes = rNodes.GetOutLineNds();
    size_t nOutlinePos = weld::GetAbsPos(rContentTree, rEntry);

    if (!bIsRoot)
        --nOutlinePos;

    if (nOutlinePos >= rOutlineNodes.size())
         return;

    int nFirstLevel = pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineLevel(nOutlinePos);
    {
        // determine if any concerned outline node has content
        bool bHasContent(false);
        size_t nPos = nOutlinePos;
        SwNode* pSttNd = rOutlineNodes[nPos];
        SwNode* pEndNd = &rNodes.GetEndOfContent();
        if (rOutlineNodes.size() > nPos + 1)
            pEndNd = rOutlineNodes[nPos + 1];

        // selected
        SwNodeIndex aIdx(*pSttNd);
        if (SwNodes::GoNext(&aIdx) != pEndNd)
            bHasContent = true;

        // descendants
        if (!bHasContent && (rContentTree.iter_has_child(rEntry) || rContentTree.get_children_on_demand(rEntry)))
        {
            while (++nPos < rOutlineNodes.size() &&
                  (bIsRoot || pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos) > nFirstLevel))
            {
                pSttNd = rOutlineNodes[nPos];
                pEndNd = &rNodes.GetEndOfContent();
                if (rOutlineNodes.size() > nPos + 1)
                    pEndNd = rOutlineNodes[nPos + 1];

                // test for content in outline node
                aIdx.Assign(*pSttNd);
                if (SwNodes::GoNext(&aIdx) != pEndNd)
                {
                    bHasContent = true;
                    break;
                }
            }
        }

        if (!bHasContent)
            return// no content in any of the concerned outline nodes
    }

    // determine for subs if all are folded or unfolded or if they are mixed
    if (rContentTree.iter_has_child(rEntry) || rContentTree.get_children_on_demand(rEntry))
    {
        // skip no content nodes
        // we know there is content from results above so this is presumably safe
        size_t nPos = nOutlinePos;
        while (true)
        {
            SwNode* pSttNd = rOutlineNodes[nPos];
            SwNode* pEndNd = rOutlineNodes.back();
            if (!bIsRoot && rOutlineNodes.size() > nPos + 1)
                pEndNd = rOutlineNodes[nPos + 1];

            SwNodeIndex aIdx(*pSttNd);
            if (SwNodes::GoNext(&aIdx) != pEndNd)
                break;
            nPos++;
        }

        bool bHasFolded(!pThis->GetWrtShell()->IsOutlineContentVisible(nPos));
        bool bHasUnfolded(!bHasFolded);

        while ((++nPos < pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineNodesCount()) &&
               (bIsRoot || pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos) > nFirstLevel))
        {

            SwNode* pSttNd = rOutlineNodes[nPos];
            SwNode* pEndNd = &rNodes.GetEndOfContent();
            if (rOutlineNodes.size() > nPos + 1)
                pEndNd = rOutlineNodes[nPos + 1];

            SwNodeIndex aIdx(*pSttNd);
            if (SwNodes::GoNext(&aIdx) == pEndNd)
                continue// skip if no content

            if (!pThis->GetWrtShell()->IsOutlineContentVisible(nPos))
                bHasFolded = true;
            else
                bHasUnfolded = true;

            if (bHasFolded && bHasUnfolded)
                break// mixed so no need to continue
        }

        rPop.set_sensitive(OUString::number(HIDE_OUTLINE_CONTENT_VISIBILITY), bHasUnfolded);
        rPop.set_sensitive(OUString::number(SHOW_OUTLINE_CONTENT_VISIBILITY), bHasFolded);
    }

    rPop.set_sensitive(OUString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY), !bIsRoot);
}

IMPL_LINK(SwContentTree, CommandHdl, const CommandEvent&, rCEvt, bool)
{
    if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
        return false;

    grab_focus();

    // select clicked entry or limit selection to root entry if needed
    if (std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
            rCEvt.IsMouseEvent() &&  m_xTreeView->get_dest_row_at_pos(
                rCEvt.GetMousePosPixel(), xEntry.get(), false))
    {
        // if clicked entry is not currently selected then clear selections and select it
        if (!m_xTreeView->is_selected(*xEntry))
            m_xTreeView->set_cursor(*xEntry);
        // if root entry is selected then clear selections and select it
        else if (m_xTreeView->is_selected(0))
            m_xTreeView->set_cursor(0);
    }

    UpdateContentFunctionsToolbar();

    std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), u"modules/swriter/ui/navigatorcontextmenu.ui"_ustr));
    std::unique_ptr<weld::Menu> xPop = xBuilder->weld_menu(u"navmenu"_ustr);

    bool bOutline(false);
    std::unique_ptr<weld::Menu> xSubPop1 = xBuilder->weld_menu(u"outlinelevel"_ustr);
    std::unique_ptr<weld::Menu> xSubPop2 = xBuilder->weld_menu(u"dragmodemenu"_ustr);
    std::unique_ptr<weld::Menu> xSubPop3 = xBuilder->weld_menu(u"displaymenu"_ustr);
    std::unique_ptr<weld::Menu> xSubPopOutlineTracking = xBuilder->weld_menu(u"outlinetracking"_ustr);

    std::unique_ptr<weld::Menu> xSubPopOutlineContent = xBuilder->weld_menu(u"outlinecontent"_ustr);

    xSubPopOutlineContent->append(OUString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY),
                                  SwResId(STR_OUTLINE_CONTENT_VISIBILITY_TOGGLE));
    xSubPopOutlineContent->append(OUString::number(HIDE_OUTLINE_CONTENT_VISIBILITY),
                                  SwResId(STR_OUTLINE_CONTENT_VISIBILITY_HIDE_ALL));
    xSubPopOutlineContent->append(OUString::number(SHOW_OUTLINE_CONTENT_VISIBILITY),
                                  SwResId(STR_OUTLINE_CONTENT_VISIBILITY_SHOW_ALL));

    xSubPopOutlineContent->set_item_help_id(OUString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY),
                                            HID_NAVIGATOR_TREELIST);
    xSubPopOutlineContent->set_item_help_id(OUString::number(HIDE_OUTLINE_CONTENT_VISIBILITY),
                                            HID_NAVIGATOR_TREELIST);
    xSubPopOutlineContent->set_item_help_id(OUString::number(SHOW_OUTLINE_CONTENT_VISIBILITY),
                                            HID_NAVIGATOR_TREELIST);

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=91 G=92

¤ Dauer der Verarbeitung: 0.22 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge