diff options
Diffstat (limited to 'sw/source/uibase/utlui/content.cxx')
-rw-r--r-- | sw/source/uibase/utlui/content.cxx | 5314 |
1 files changed, 5314 insertions, 0 deletions
diff --git a/sw/source/uibase/utlui/content.cxx b/sw/source/uibase/utlui/content.cxx new file mode 100644 index 000000000..2c5356cd0 --- /dev/null +++ b/sw/source/uibase/utlui/content.cxx @@ -0,0 +1,5314 @@ +/* -*- 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 <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 <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 <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> + +#define CTYPE_CNT 0 +#define CTYPE_CTT 1 + +using namespace ::std; +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_uniqueptr_to<SwContent>, + o3tl::find_partialorder_ptrequals> +{ +}; + +namespace +{ + 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& 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::IMark* 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 = aIdx.GetNodes().GoNext(&aIdx); + return pCNd->IsTextNode() ? static_cast<SwTextNode*>(pCNd)->GetText() : OUString(); + } +} + +// Content, contains names and reference at the content type. + +SwContent::SwContent(const SwContentType* pCnt, const OUString& rName, double nYPos) : + SwTypeNumber(CTYPE_CNT), + m_pParent(pCnt), + m_sContentName(rName), + m_nYPosition(nYPos), + m_bInvisible(false) +{ +} + + +SwTypeNumber::~SwTypeNumber() +{ +} + +bool SwContent::IsProtect() const +{ + return false; +} + +bool SwTextFieldContent::IsProtect() const +{ + return m_pFormatField->IsProtect(); +} + +bool SwPostItContent::IsProtect() const +{ + return m_pField->IsProtect(); +} + +bool SwURLFieldContent::IsProtect() const +{ + return m_pINetAttr->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 SwNodeIndex& rNodeIndex) +{ + SwNodeOffset nIndex = rNodeIndex.GetIndex(); + if (rNodeIndex.GetNodes().GetEndOfExtras().GetIndex() >= nIndex) + { + // Not a node of BodyText + // Are we in a fly? + if (const auto pFlyFormat = rNodeIndex.GetNode().GetFlyFormat()) + { + // Get node index of anchor + if (auto pSwPosition = pFlyFormat->GetAnchor().GetContentAnchor()) + { + return getYPos(pSwPosition->nNode); + } + } + } + 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; + break; + case ContentTypeId::FRAME: + m_sTypeToken = "frame"; + m_bEdit = true; + break; + case ContentTypeId::GRAPHIC: + m_sTypeToken = "graphic"; + m_bEdit = true; + break; + case ContentTypeId::OLE: + m_sTypeToken = "ole"; + m_bEdit = true; + break; + case ContentTypeId::TEXTFIELD: + m_bEdit = true; + m_bDelete = true; + break; + case ContentTypeId::FOOTNOTE: + case ContentTypeId::ENDNOTE: + m_bEdit = true; + m_bDelete = false; + break; + case ContentTypeId::BOOKMARK: + { + const bool bProtectedBM = m_pWrtShell->getIDocumentSettingAccess().get( + DocumentSettingId::PROTECT_BOOKMARKS); + m_bEdit = !bProtectedBM; + m_bDelete = !bProtectedBM; + } + break; + case ContentTypeId::REGION: + m_sTypeToken = "region"; + m_bEdit = true; + m_bDelete = false; + break; + case ContentTypeId::INDEX: + m_bEdit = true; + m_bDelete = true; + break; + case ContentTypeId::REFERENCE: + m_bEdit = false; + m_bDelete = false; + break; + case ContentTypeId::URLFIELD: + m_bEdit = true; + m_bDelete = true; + break; + case ContentTypeId::POSTIT: + m_bEdit = true; + break; + case ContentTypeId::DRAWOBJECT: + m_bEdit = true; + break; + default: break; + } + 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; + SwPtrMsgPoolItem aAskItem( RES_CONTENT_VISIBLE, nullptr ); + 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(); + const sal_uInt8 nLevel = pNode->GetAttrOutlineLevel() - 1; + if (nLevel >= m_nOutlineLevel || !pNode->getLayoutFrame(m_pWrtShell->GetLayout())) + continue; + double nYPos = m_bAlphabeticSort ? 0 : static_cast<double>(getYPos(*pNode)); + if (nEndOfExtrasIndex >= pNode->GetIndex() && pNode->GetFlyFormat()) + { + nYPos += nOutlinesInFramesIndexAdjustment; + nOutlinesInFramesIndexAdjustment += 0.00001; + } + OUString aEntry(comphelper::string::stripStart( + m_pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText( + i, m_pWrtShell->GetLayout(), true, false, false), ' ')); + aEntry = SwNavigationPI::CleanEntry(aEntry); + auto pCnt(make_unique<SwOutlineContent>(this, aEntry, i, nLevel, + m_pWrtShell->IsOutlineMovable(i), nYPos)); + 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 SwFrameFormats* pFrameFormats = m_pWrtShell->GetDoc()->GetTableFrameFormats(); + SwAutoFormatGetDocNode aGetHt(&m_pWrtShell->GetNodes()); + for(size_t n = 0, i = 0; i < nCount + n; ++i) + { + const SwTableFormat& rTableFormat = + *static_cast<SwTableFormat*>(pFrameFormats->GetFormat(i)); + if (rTableFormat.GetInfo(aGetHt)) // skip deleted tables + { + n++; + continue; + } + tools::Long nYPos = 0; + if (!m_bAlphabeticSort) + { + if (SwTable* pTable = SwTable::FindTable(&rTableFormat)) + nYPos = getYPos(*pTable->GetTableNode()); + } + auto pCnt = make_unique<SwContent>(this, rTableFormat.GetName(), nYPos); + if( !rTableFormat.GetInfo( aAskItem ) && + !aAskItem.pObject ) // not visible + 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 OUString sFrameName = pFrameFormat->GetName(); + + SwContent* pCnt; + tools::Long nYPos = + m_bAlphabeticSort ? 0 : pFrameFormat->FindLayoutRect(false, &aNullPt).Top(); + if(ContentTypeId::GRAPHIC == m_nContentType) + { + OUString sLink; + m_pWrtShell->GetGrfNms( &sLink, nullptr, static_cast<const SwFlyFrameFormat*>( pFrameFormat)); + pCnt = new SwGraphicContent(this, sFrameName, INetURLObject::decode(sLink, + INetURLObject::DecodeMechanism::Unambiguous), nYPos); + } + else + { + pCnt = new SwContent(this, sFrameName, nYPos); + } + if( !pFrameFormat->GetInfo( aAskItem ) && + !aAskItem.pObject ) // not visible + 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(IDocumentMarkAccess::const_iterator_t ppBookmark = pMarkAccess->getBookmarksBegin(); + ppBookmark != pMarkAccess->getBookmarksEnd(); + ++ppBookmark) + { + if(lcl_IsUiVisibleBookmark(*ppBookmark)) + { + const OUString& rBkmName = (*ppBookmark)->GetName(); + //nYPos from 0 -> text::Bookmarks will be sorted alphabetically + auto pCnt(std::make_unique<SwContent>(this, rBkmName, + 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(); + bool bHasEntryInFly = false; + + // use stable sort array to list fields in document model order + std::stable_sort(aArr.begin(), aArr.end(), + [](const SwTextField* a, const SwTextField* b){ + SwPosition aPos(a->GetTextNode(), a->GetStart()); + SwPosition bPos(b->GetTextNode(), b->GetStart()); + return aPos < bPos;}); + + // determine if there is a text field in a fly frame + for (SwTextField* pTextField : aArr) + { + if (!bHasEntryInFly) + { + if (nEndOfExtrasIndex >= pTextField->GetTextNode().GetIndex()) + { + // Not a node of BodyText + // Are we in a fly? + if (pTextField->GetTextNode().GetFlyFormat()) + { + bHasEntryInFly = true; + break; + } + } + } + } + + // When there are fields in fly frames do an additional sort using the fly frame + // anchor position to place field entries in order of document layout appearance. + if (bHasEntryInFly) + { + std::stable_sort(aArr.begin(), aArr.end(), + [nEndOfExtrasIndex](const SwTextField* a, const SwTextField* b){ + SwTextNode& aTextNode = a->GetTextNode(); + SwTextNode& bTextNode = b->GetTextNode(); + SwPosition aPos(aTextNode, a->GetStart()); + SwPosition bPos(bTextNode, b->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;}); + } + } + 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) + { + 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.size() == 0) + 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(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 OUString& sSectionName = pSection->GetSectionName(); + + sal_uInt8 nLevel = 0; + SwSectionFormat* pParentFormat = pFormat->GetParent(); + while(pParentFormat) + { + nLevel++; + pParentFormat = pParentFormat->GetParent(); + } + + std::unique_ptr<SwContent> pCnt(new SwRegionContent(this, sSectionName, + nLevel, m_bAlphabeticSort ? 0 : getYPos(*pNodeIndex))); + if( !pFormat->GetInfo( aAskItem ) && + !aAskItem.pObject ) // not visible + 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(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(const_cast<SwTextNode&>(a->rINetAttr.GetTextNode()), + a->rINetAttr.GetStart()); + SwPosition bSwPos(const_cast<SwTextNode&>(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(const_cast<SwTextNode&>(aTextNode), a->rINetAttr.GetStart()); + SwPosition bPos(const_cast<SwTextNode&>(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 = 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 ); + OUString sTOXNm( pBase->GetTOXName() ); + + SwContent* pCnt = new SwTOXBaseContent( + this, sTOXNm, 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); + const size_t nCount = pPage->GetObjCount(); + for( size_t i=0; i<nCount; ++i ) + { + SdrObject* pTemp = pPage->GetObj(i); + // #i51726# - all drawing objects can be named now + if (!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; + default: break; + } + 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_DRAGMODE = 1, + IDX_STR_HYPERLINK = 2, + IDX_STR_LINK_REGION = 3, + IDX_STR_COPY_REGION = 4, + IDX_STR_DISPLAY = 5, + IDX_STR_ACTIVE_VIEW = 6, + IDX_STR_HIDDEN = 7, + IDX_STR_ACTIVE = 8, + IDX_STR_INACTIVE = 9, + IDX_STR_EDIT_ENTRY = 10, + IDX_STR_DELETE_ENTRY = 11, + IDX_STR_SEND_OUTLINE_TO_CLIPBOARD_ENTRY = 12, + IDX_STR_OUTLINE_TRACKING = 13, + IDX_STR_OUTLINE_TRACKING_DEFAULT = 14, + IDX_STR_OUTLINE_TRACKING_FOCUS = 15, + IDX_STR_OUTLINE_TRACKING_OFF = 16 +}; + +} + +const TranslateId STR_CONTEXT_ARY[] = +{ + STR_OUTLINE_LEVEL, + STR_DRAGMODE, + STR_HYPERLINK, + STR_LINK_REGION, + STR_COPY_REGION, + 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_xScratchIter(m_xTreeView->make_iterator()) + , m_aDropTargetHelper(*this) + , m_pDialog(pDialog) + , m_sSpace(OUString(" ")) + , m_aUpdTimer("SwContentTree m_aUpdTimer") + , m_sInvisible(SwResId(STR_INVISIBLE)) + , m_pHiddenShell(nullptr) + , m_pActiveShell(nullptr) + , m_pConfig(SW_MOD()->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_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)); + + 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(); + m_aUpdTimer.SetInvokeHandler(LINK(this, SwContentTree, TimerUpdate)); + m_aUpdTimer.SetTimeout(1000); +} + +SwContentTree::~SwContentTree() +{ + clear(); // If applicable erase content types previously. + m_aUpdTimer.Stop(); + SetActiveShell(nullptr); +} + +// 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 + } + + rtl::Reference<TransferDataContainer> xContainer = new TransferDataContainer; + sal_Int8 nDragMode = DND_ACTION_COPYMOVE | DND_ACTION_LINK; + + if (FillTransferData(*xContainer, nDragMode)) + bDisallow = false; + + 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()); + + // Find first selected of continuous siblings + while (true) + { + m_xTreeView->copy_iterator(*xEntry, *xScratch); + if (!m_xTreeView->iter_previous_sibling(*xScratch)) + break; + if (!m_xTreeView->is_selected(*xScratch)) + break; + m_xTreeView->copy_iterator(*xScratch, *xEntry); + } + // Record continuous selected siblings + do + { + m_aDndOutlinesSelected.push_back(m_xTreeView->make_iterator(xEntry.get())); + } + while (m_xTreeView->iter_next_sibling(*xEntry) && m_xTreeView->is_selected(*xEntry)); + bDisallow = false; + } + + if (!bDisallow) + m_xTreeView->enable_drag_source(xContainer, nDragMode); + 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(); +} + +// 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(OString::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(OString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY), false); + rPop.set_sensitive(OString::number(HIDE_OUTLINE_CONTENT_VISIBILITY), false); + rPop.set_sensitive(OString::number(SHOW_OUTLINE_CONTENT_VISIBILITY), false); + + // todo: multi selection + if (rContentTree.count_selected_rows() > 1) + return; + + bool bIsRoot = lcl_IsContentType(rEntry, rContentTree); + + if (const SwWrtShell* pSh = pThis->GetActiveWrtShell()) + { + if (pSh->GetViewOptions()->IsTreatSubOutlineLevelsAsContent()) + { + if (!bIsRoot) + rPop.set_sensitive(OString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY), true); + return; + } + } + + 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 (rNodes.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 (rNodes.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 (rNodes.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 (rNodes.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(OString::number(HIDE_OUTLINE_CONTENT_VISIBILITY), bHasUnfolded); + rPop.set_sensitive(OString::number(SHOW_OUTLINE_CONTENT_VISIBILITY), bHasFolded); + } + + rPop.set_sensitive(OString::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); + } + + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), "modules/swriter/ui/navigatorcontextmenu.ui")); + std::unique_ptr<weld::Menu> xPop = xBuilder->weld_menu("navmenu"); + + bool bOutline(false); + std::unique_ptr<weld::Menu> xSubPop1 = xBuilder->weld_menu("outlinelevel"); + std::unique_ptr<weld::Menu> xSubPop2 = xBuilder->weld_menu("dragmodemenu"); + std::unique_ptr<weld::Menu> xSubPop3 = xBuilder->weld_menu("displaymenu"); + std::unique_ptr<weld::Menu> xSubPopOutlineTracking = xBuilder->weld_menu("outlinetracking"); + + std::unique_ptr<weld::Menu> xSubPopOutlineContent = xBuilder->weld_menu("outlinecontent"); + + 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)); + + for(int i = 1; i <= 3; ++i) + xSubPopOutlineTracking->append_radio(OUString::number(i + 10), m_aContextStrings[IDX_STR_OUTLINE_TRACKING + i]); + xSubPopOutlineTracking->set_active(OString::number(10 + m_nOutlineTracking), true); + + for (int i = 1; i <= MAXLEVEL; ++i) + xSubPop1->append_radio(OUString::number(i + 100), OUString::number(i)); + xSubPop1->set_active(OString::number(100 + m_nOutlineLevel), true); + + for (int i=0; i < 3; ++i) + xSubPop2->append_radio(OUString::number(i + 201), m_aContextStrings[IDX_STR_HYPERLINK + i]); + xSubPop2->set_active(OString::number(201 + static_cast<int>(GetParentWindow()->GetRegionDropMode())), true); + + // Insert the list of the open files + { + sal_uInt16 nId = 301; + SwView *pView = SwModule::GetFirstView(); + while (pView) + { + OUString sInsert = pView->GetDocShell()->GetTitle() + " (" + + m_aContextStrings[pView == GetActiveView() ? IDX_STR_ACTIVE : + IDX_STR_INACTIVE] + ")"; + xSubPop3->append_radio(OUString::number(nId), sInsert); + if (State::CONSTANT == m_eState && m_pActiveShell == &pView->GetWrtShell()) + xSubPop3->set_active(OString::number(nId), true); + pView = SwModule::GetNextView(pView); + nId++; + } + xSubPop3->append_radio(OUString::number(nId++), m_aContextStrings[IDX_STR_ACTIVE_VIEW]); + if (m_pHiddenShell) // can have only one hidden shell + { + OUString sHiddenEntry = m_pHiddenShell->GetView().GetDocShell()->GetTitle() + + " (" + + m_aContextStrings[IDX_STR_HIDDEN] + + ")"; + xSubPop3->append_radio(OUString::number(nId), sHiddenEntry); + } + if (State::ACTIVE == m_eState) + xSubPop3->set_active(OString::number(--nId), true); + else if (State::HIDDEN == m_eState) + xSubPop3->set_active(OString::number(nId), true); + } + + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_selected(xEntry.get())) + xEntry.reset(); + + bool bRemoveGotoEntry = false; + if (State::HIDDEN == m_eState || !xEntry || !lcl_IsContent(*xEntry, *m_xTreeView) || + weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->IsInvisible()) + bRemoveGotoEntry = true; + + bool bRemovePostItEntries = true; + bool bRemoveIndexEntries = true; + bool bRemoveCopyEntry = true; + bool bRemoveEditEntry = true; + bool bRemoveUnprotectEntry = true; + bool bRemoveDeleteEntry = true; + bool bRemoveRenameEntry = true; + bool bRemoveSelectEntry = true; + bool bRemoveToggleExpandEntry = true; + bool bRemoveChapterEntries = true; + bool bRemoveSendOutlineEntry = true; + + bool bRemoveTableTracking = true; + bool bRemoveSectionTracking = true; + bool bRemoveFrameTracking = true; + bool bRemoveImageTracking = true; + bool bRemoveOLEobjectTracking = true; + bool bRemoveBookmarkTracking = true; + bool bRemoveHyperlinkTracking = true; + bool bRemoveReferenceTracking = true; + bool bRemoveIndexTracking = true; + bool bRemoveCommentTracking = true; + bool bRemoveDrawingObjectTracking = true; + bool bRemoveFieldTracking = true; + bool bRemoveFootnoteTracking = true; + bool bRemoveEndnoteTracking = true; + + bool bRemoveSortEntry = true; + + if (xEntry) + { + const SwContentType* pType; + if (lcl_IsContentType(*xEntry, *m_xTreeView)) + pType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry)); + else + pType = weld::fromId<SwContent*>( + m_xTreeView->get_id(*xEntry))->GetParent(); + const ContentTypeId nContentType = pType->GetType(); + + if (nContentType != ContentTypeId::FOOTNOTE && nContentType != ContentTypeId::ENDNOTE + && nContentType != ContentTypeId::POSTIT) + { + bRemoveSortEntry = false; + xPop->set_active("sort", pType->GetSortType()); + } + + OString aIdent; + switch (nContentType) + { + case ContentTypeId::TABLE: + aIdent = "tabletracking"; + bRemoveTableTracking = false; + break; + case ContentTypeId::REGION: + aIdent = "sectiontracking"; + bRemoveSectionTracking = false; + break; + case ContentTypeId::FRAME: + aIdent = "frametracking"; + bRemoveFrameTracking = false; + break; + case ContentTypeId::GRAPHIC: + aIdent = "imagetracking"; + bRemoveImageTracking = false; + break; + case ContentTypeId::OLE: + aIdent = "oleobjecttracking"; + bRemoveOLEobjectTracking = false; + break; + case ContentTypeId::BOOKMARK: + aIdent = "bookmarktracking"; + bRemoveBookmarkTracking = false; + break; + case ContentTypeId::URLFIELD: + aIdent = "hyperlinktracking"; + bRemoveHyperlinkTracking = false; + break; + case ContentTypeId::REFERENCE: + aIdent = "referencetracking"; + bRemoveReferenceTracking = false; + break; + case ContentTypeId::INDEX: + aIdent = "indextracking"; + bRemoveIndexTracking = false; + break; + case ContentTypeId::POSTIT: + aIdent = "commenttracking"; + bRemoveCommentTracking = false; + break; + case ContentTypeId::DRAWOBJECT: + aIdent = "drawingobjecttracking"; + bRemoveDrawingObjectTracking = false; + break; + case ContentTypeId::TEXTFIELD: + aIdent = "fieldtracking"; + bRemoveFieldTracking = false; + break; + case ContentTypeId::FOOTNOTE: + aIdent = "footnotetracking"; + bRemoveFootnoteTracking = false; + break; + case ContentTypeId::ENDNOTE: + aIdent = "endnotetracking"; + bRemoveEndnoteTracking = false; + break; + default: break; + } + if (!aIdent.isEmpty()) + xPop->set_active(aIdent, mTrackContentType[nContentType]); + + // Edit only if the shown content is coming from the current view. + if (State::HIDDEN != m_eState && + (State::ACTIVE == m_eState || (GetActiveView() && m_pActiveShell == GetActiveView()->GetWrtShellPtr())) + && lcl_IsContent(*xEntry, *m_xTreeView)) + { + const bool bReadonly = m_pActiveShell->GetView().GetDocShell()->IsReadOnly(); + const bool bVisible = !weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->IsInvisible(); + const bool bProtected = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->IsProtect(); + const bool bProtectBM = (ContentTypeId::BOOKMARK == nContentType) + && m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS); + const bool bEditable = pType->IsEditable() && + ((bVisible && !bProtected && !bProtectBM) || ContentTypeId::REGION == nContentType); + const bool bDeletable = pType->IsDeletable() && + ((bVisible && !bProtected && !bProtectBM) || ContentTypeId::REGION == nContentType); + const bool bRenamable = bEditable && !bReadonly && + (ContentTypeId::TABLE == nContentType || + ContentTypeId::FRAME == nContentType || + ContentTypeId::GRAPHIC == nContentType || + ContentTypeId::OLE == nContentType || + (ContentTypeId::BOOKMARK == nContentType && !bProtectBM) || + ContentTypeId::REGION == nContentType || + ContentTypeId::INDEX == nContentType || + ContentTypeId::DRAWOBJECT == nContentType); + + if (ContentTypeId::FOOTNOTE == nContentType || ContentTypeId::ENDNOTE == nContentType) + { + void* pUserData = weld::fromId<void*>(m_xTreeView->get_id(*xEntry)); + const SwTextFootnote* pFootnote = + static_cast<const SwTextFootnoteContent*>(pUserData)->GetTextFootnote(); + if (!pFootnote) + bRemoveGotoEntry = true; + } + else if(ContentTypeId::OUTLINE == nContentType) + { + bOutline = true; + lcl_SetOutlineContentEntriesSensitivities(this, *m_xTreeView, *xEntry, *xSubPopOutlineContent); + bRemoveToggleExpandEntry = lcl_InsertExpandCollapseAllItem(*m_xTreeView, *xEntry, *xPop); + if (!bReadonly) + { + bRemoveSelectEntry = false; + bRemoveChapterEntries = false; + } + bRemoveCopyEntry = false; + } + else if (!bReadonly && (bEditable || bDeletable)) + { + if(ContentTypeId::INDEX == nContentType) + { + bRemoveIndexEntries = false; + + const SwTOXBase* pBase = weld::fromId<SwTOXBaseContent*>(m_xTreeView->get_id(*xEntry))->GetTOXBase(); + if (!pBase->IsTOXBaseInReadonly()) + bRemoveEditEntry = false; + + xPop->set_active(OString::number(405), SwEditShell::IsTOXBaseReadonly(*pBase)); + bRemoveDeleteEntry = false; + } + else if(ContentTypeId::TABLE == nContentType) + { + bRemoveSelectEntry = false; + bRemoveEditEntry = false; + bRemoveUnprotectEntry = false; + bool bFull = false; + OUString sTableName = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->GetName(); + bool bProt = m_pActiveShell->HasTableAnyProtection( &sTableName, &bFull ); + xPop->set_sensitive(OString::number(403), !bFull); + xPop->set_sensitive(OString::number(404), bProt); + bRemoveDeleteEntry = false; + } + else if(ContentTypeId::DRAWOBJECT == nContentType) + { + bRemoveDeleteEntry = false; + } + else if(ContentTypeId::REGION == nContentType) + { + bRemoveSelectEntry = false; + bRemoveEditEntry = false; + } + else + { + if (bEditable && bDeletable) + { + bRemoveEditEntry = false; + bRemoveDeleteEntry = false; + } + else if (bEditable) + bRemoveEditEntry = false; + else if (bDeletable) + { + bRemoveDeleteEntry = false; + } + } + //Rename object + if (bRenamable) + bRemoveRenameEntry = false; + } + } + else + { + if (lcl_IsContentType(*xEntry, *m_xTreeView)) + pType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry)); + else + pType = weld::fromId<SwContent*>( + m_xTreeView->get_id(*xEntry))->GetParent(); + if (pType) + { + if (ContentTypeId::OUTLINE == nContentType) + { + bOutline = true; + if (State::HIDDEN != m_eState) + { + lcl_SetOutlineContentEntriesSensitivities(this, *m_xTreeView, *xEntry, + *xSubPopOutlineContent); + bRemoveSendOutlineEntry = false; + } + bRemoveToggleExpandEntry = lcl_InsertExpandCollapseAllItem(*m_xTreeView, *xEntry, + *xPop); + } + else if (State::HIDDEN != m_eState && + nContentType == ContentTypeId::POSTIT && + !m_pActiveShell->GetView().GetDocShell()->IsReadOnly() && + pType->GetMemberCount() > 0) + bRemovePostItEntries = false; + } + } + } + + if (bRemoveToggleExpandEntry) + xPop->remove(OString::number(800)); + + if (bRemoveGotoEntry) + xPop->remove(OString::number(900)); + + if (bRemoveSelectEntry) + xPop->remove(OString::number(805)); + + if (bRemoveChapterEntries) + { + xPop->remove(OString::number(806)); + xPop->remove(OString::number(801)); + xPop->remove(OString::number(802)); + xPop->remove(OString::number(803)); + xPop->remove(OString::number(804)); + } + + if (bRemoveSendOutlineEntry) + xPop->remove(OString::number(700)); + + if (bRemovePostItEntries) + { + xPop->remove(OString::number(600)); + xPop->remove(OString::number(601)); + xPop->remove(OString::number(602)); + } + + if (bRemoveDeleteEntry) + xPop->remove(OString::number(501)); + + if (bRemoveRenameEntry) + xPop->remove(OString::number(502)); + + if (bRemoveIndexEntries) + { + xPop->remove(OString::number(401)); + xPop->remove(OString::number(402)); + xPop->remove(OString::number(405)); + } + + if (bRemoveUnprotectEntry) + xPop->remove(OString::number(404)); + + if (bRemoveEditEntry) + xPop->remove(OString::number(403)); + + if (bRemoveToggleExpandEntry && + bRemoveSendOutlineEntry) + xPop->remove("separator1"); + + if (bRemoveCopyEntry) + xPop->remove("copy"); + + if (bRemoveGotoEntry && + bRemoveCopyEntry && + bRemoveSelectEntry && + bRemoveDeleteEntry && + bRemoveChapterEntries && + bRemovePostItEntries && + bRemoveRenameEntry && + bRemoveIndexEntries && + bRemoveUnprotectEntry && + bRemoveEditEntry) + xPop->remove("separator2"); + + if (!bOutline) + { + xSubPop1.reset(); + xPop->remove(OString::number(1)); // outline level menu + } + if (!bOutline || State::HIDDEN == m_eState) + { + xSubPopOutlineTracking.reset(); + xPop->remove(OString::number(4)); // outline tracking menu + } + if (!bOutline || State::HIDDEN == m_eState || + !m_pActiveShell->GetViewOptions()->IsShowOutlineContentVisibilityButton() || + m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount() == 0) + { + xSubPopOutlineContent.reset(); + xPop->remove(OString::number(5)); // outline folding menu + xPop->remove("separator3"); + } + + if (bRemoveTableTracking) + xPop->remove("tabletracking"); + if (bRemoveSectionTracking) + xPop->remove("sectiontracking"); + if (bRemoveFrameTracking) + xPop->remove("frametracking"); + if (bRemoveImageTracking) + xPop->remove("imagetracking"); + if (bRemoveOLEobjectTracking) + xPop->remove("oleobjecttracking"); + if (bRemoveBookmarkTracking) + xPop->remove("bookmarktracking"); + if (bRemoveHyperlinkTracking) + xPop->remove("hyperlinktracking"); + if (bRemoveReferenceTracking) + xPop->remove("referencetracking"); + if (bRemoveIndexTracking) + xPop->remove("indextracking"); + if (bRemoveCommentTracking) + xPop->remove("commenttracking"); + if (bRemoveDrawingObjectTracking) + xPop->remove("drawingobjecttracking"); + if (bRemoveFieldTracking) + xPop->remove("fieldtracking"); + if (bRemoveFootnoteTracking) + xPop->remove("footnotetracking"); + if (bRemoveEndnoteTracking) + xPop->remove("endnotetracking"); + if (bRemoveSortEntry) + xPop->remove("sort"); + + bool bSetSensitiveCollapseAllCategories = false; + if (!m_bIsRoot && xEntry) + { + bool bEntry = m_xTreeView->get_iter_first(*xEntry); + while (bEntry) + { + if (m_xTreeView->get_row_expanded(*xEntry)) + { + bSetSensitiveCollapseAllCategories = true; + break; + } + bEntry = m_xTreeView->iter_next_sibling(*xEntry); + } + } + xPop->set_sensitive("collapseallcategories", bSetSensitiveCollapseAllCategories); + + OString sCommand = xPop->popup_at_rect(m_xTreeView.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))); + if (!sCommand.isEmpty()) + ExecuteContextMenuAction(sCommand); + + return true; +} + +void SwContentTree::insert(const weld::TreeIter* pParent, const OUString& rStr, const OUString& rId, + bool bChildrenOnDemand, weld::TreeIter* pRet) +{ + m_xTreeView->insert(pParent, -1, &rStr, &rId, nullptr, nullptr, bChildrenOnDemand, pRet); + ++m_nEntryCount; +} + +void SwContentTree::remove(const weld::TreeIter& rIter) +{ + if (m_xTreeView->iter_has_child(rIter)) + { + std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator(&rIter); + (void)m_xTreeView->iter_children(*xChild); + remove(*xChild); + } + m_xTreeView->remove(rIter); + --m_nEntryCount; +} + +// Content will be integrated into the Box only on demand. +bool SwContentTree::RequestingChildren(const weld::TreeIter& rParent) +{ + bool bChild = m_xTreeView->iter_has_child(rParent); + if (bChild || !m_xTreeView->get_children_on_demand(rParent)) + return bChild; + + // Is this a content type? + if (lcl_IsContentType(rParent, *m_xTreeView)) + { + std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator(); + + assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent)))); + SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent)); + + const size_t nCount = pCntType->GetMemberCount(); + // Add for outline plus/minus + if (pCntType->GetType() == ContentTypeId::OUTLINE) + { + std::vector<std::unique_ptr<weld::TreeIter>> aParentCandidates; + for(size_t i = 0; i < nCount; ++i) + { + const SwContent* pCnt = pCntType->GetMember(i); + if(pCnt) + { + const auto nLevel = static_cast<const SwOutlineContent*>(pCnt)->GetOutlineLevel(); + OUString sEntry = pCnt->GetName(); + if(sEntry.isEmpty()) + sEntry = m_sSpace; + OUString sId(weld::toId(pCnt)); + + auto lamba = [nLevel, this](const std::unique_ptr<weld::TreeIter>& entry) + { + return lcl_IsLowerOutlineContent(*entry, *m_xTreeView, nLevel); + }; + + // if there is a preceding outline node candidate with a lower outline level use + // that as a parent, otherwise use the root node + auto aFind = std::find_if(aParentCandidates.rbegin(), aParentCandidates.rend(), lamba); + if (aFind != aParentCandidates.rend()) + insert(aFind->get(), sEntry, sId, false, xChild.get()); + else + insert(&rParent, sEntry, sId, false, xChild.get()); + m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible()); + m_xTreeView->set_extra_row_indent(*xChild, nLevel + 1 - m_xTreeView->get_iter_depth(*xChild)); + + // remove any parent candidates equal to or higher than this node + aParentCandidates.erase(std::remove_if(aParentCandidates.begin(), aParentCandidates.end(), + std::not_fn(lamba)), aParentCandidates.end()); + + // add this node as a parent candidate for any following nodes at a higher outline level + aParentCandidates.emplace_back(m_xTreeView->make_iterator(xChild.get())); + + bChild = true; + } + } + } + else + { + bool bRegion = pCntType->GetType() == ContentTypeId::REGION; + for(size_t i = 0; i < nCount; ++i) + { + const SwContent* pCnt = pCntType->GetMember(i); + if (pCnt) + { + OUString sEntry = pCnt->GetName(); + if (sEntry.isEmpty()) + sEntry = m_sSpace; + OUString sId(weld::toId(pCnt)); + insert(&rParent, sEntry, sId, false, xChild.get()); + m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible()); + if (bRegion) + m_xTreeView->set_extra_row_indent(*xChild, static_cast<const SwRegionContent*>(pCnt)->GetRegionLevel()); + bChild = true; + } + } + } + } + + return bChild; +} + +SdrObject* SwContentTree::GetDrawingObjectsByContent(const SwContent *pCnt) +{ + SdrObject *pRetObj = nullptr; + switch(pCnt->GetParent()->GetType()) + { + case ContentTypeId::DRAWOBJECT: + { + SdrView* pDrawView = m_pActiveShell->GetDrawView(); + if (pDrawView) + { + SwDrawModel* pDrawModel = m_pActiveShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel(); + SdrPage* pPage = pDrawModel->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()) + { + pRetObj = pTemp; + break; + } + } + } + break; + } + default: + pRetObj = nullptr; + } + return pRetObj; +} + +void SwContentTree::Expand(const weld::TreeIter& rParent, std::vector<std::unique_ptr<weld::TreeIter>>* pNodesToExpand) +{ + if (!(m_xTreeView->iter_has_child(rParent) || m_xTreeView->get_children_on_demand(rParent))) + return; + + if (!m_bIsRoot + || (lcl_IsContentType(rParent, *m_xTreeView) && + weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent))->GetType() == ContentTypeId::OUTLINE) + || (m_nRootType == ContentTypeId::OUTLINE)) + { + if (lcl_IsContentType(rParent, *m_xTreeView)) + { + SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent)); + const sal_Int32 nOr = 1 << static_cast<int>(pCntType->GetType()); //linear -> Bitposition + if (State::HIDDEN != m_eState) + { + m_nActiveBlock |= nOr; + m_pConfig->SetActiveBlock(m_nActiveBlock); + } + else + m_nHiddenBlock |= nOr; + if (pCntType->GetType() == ContentTypeId::OUTLINE) + { + std::map< void*, bool > aCurrOutLineNodeMap; + + SwWrtShell* pShell = GetWrtShell(); + bool bParentHasChild = RequestingChildren(rParent); + if (pNodesToExpand) + pNodesToExpand->emplace_back(m_xTreeView->make_iterator(&rParent)); + if (bParentHasChild) + { + std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(&rParent)); + bool bChild = m_xTreeView->iter_next(*xChild); + while (bChild && lcl_IsContent(*xChild, *m_xTreeView)) + { + if (m_xTreeView->iter_has_child(*xChild)) + { + assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xChild)))); + auto const nPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xChild))->GetOutlinePos(); + void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos )); + aCurrOutLineNodeMap.emplace( key, false ); + std::map<void*, bool>::iterator iter = mOutLineNodeMap.find( key ); + if( iter != mOutLineNodeMap.end() && mOutLineNodeMap[key]) + { + aCurrOutLineNodeMap[key] = true; + RequestingChildren(*xChild); + if (pNodesToExpand) + pNodesToExpand->emplace_back(m_xTreeView->make_iterator(xChild.get())); + m_xTreeView->set_children_on_demand(*xChild, false); + } + } + bChild = m_xTreeView->iter_next(*xChild); + } + } + mOutLineNodeMap = aCurrOutLineNodeMap; + return; + } + } + else + { + if (lcl_IsContent(rParent, *m_xTreeView)) + { + SwWrtShell* pShell = GetWrtShell(); + // paranoid assert now that outline type is checked + assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent)))); + auto const nPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rParent))->GetOutlinePos(); + void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos )); + mOutLineNodeMap[key] = true; + } + } + } + + RequestingChildren(rParent); + if (pNodesToExpand) + pNodesToExpand->emplace_back(m_xTreeView->make_iterator(&rParent)); +} + +IMPL_LINK(SwContentTree, ExpandHdl, const weld::TreeIter&, rParent, bool) +{ + Expand(rParent, nullptr); + return true; +} + +IMPL_LINK(SwContentTree, CollapseHdl, const weld::TreeIter&, rParent, bool) +{ + if (!m_xTreeView->iter_has_child(rParent) || m_xTreeView->get_children_on_demand(rParent)) + return true; + + if (lcl_IsContentType(rParent, *m_xTreeView)) + { + if (m_bIsRoot) + { + // collapse to children of root node + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(&rParent)); + if (m_xTreeView->iter_children(*xEntry)) + { + do + { + m_xTreeView->collapse_row(*xEntry); + } + while (m_xTreeView->iter_next(*xEntry)); + } + return false; // return false to notify caller not to do collapse + } + SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent)); + const sal_Int32 nAnd = ~(1 << static_cast<int>(pCntType->GetType())); + if (State::HIDDEN != m_eState) + { + m_nActiveBlock &= nAnd; + m_pConfig->SetActiveBlock(m_nActiveBlock); + } + else + m_nHiddenBlock &= nAnd; + } + else if (lcl_IsContent(rParent, *m_xTreeView)) + { + SwWrtShell* pShell = GetWrtShell(); + assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent)))); + auto const nPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rParent))->GetOutlinePos(); + void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos )); + mOutLineNodeMap[key] = false; + } + + return true; +} + +// Also on double click will be initially opened only. +IMPL_LINK_NOARG(SwContentTree, ContentDoubleClickHdl, weld::TreeView&, bool) +{ + bool bConsumed = false; + + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + bool bEntry = m_xTreeView->get_cursor(xEntry.get()); + // Is it a content type? + OSL_ENSURE(bEntry, "no current entry!"); + if (bEntry) + { + if (lcl_IsContentType(*xEntry, *m_xTreeView) && !m_xTreeView->iter_has_child(*xEntry)) + { + RequestingChildren(*xEntry); + m_xTreeView->set_children_on_demand(*xEntry, false); + } + else if (!lcl_IsContentType(*xEntry, *m_xTreeView) && (State::HIDDEN != m_eState)) + { + assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)))); + SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry)); + assert(pCnt && "no UserData"); + if (pCnt && !pCnt->IsInvisible()) + { + if (State::CONSTANT == m_eState) + { + m_pActiveShell->GetView().GetViewFrame()->GetWindow().ToTop(); + } + //Jump to content type: + GotoContent(pCnt); + // fdo#36308 don't expand outlines on double-click + bConsumed = pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE; + } + } + } + + return bConsumed; // false/true == allow/disallow more to be done, i.e. expand/collapse children +} + +namespace +{ + OUString GetImageIdForContentTypeId(ContentTypeId eType) + { + OUString sResId; + + switch (eType) + { + case ContentTypeId::OUTLINE: + sResId = RID_BMP_NAVI_OUTLINE; + break; + case ContentTypeId::TABLE: + sResId = RID_BMP_NAVI_TABLE; + break; + case ContentTypeId::FRAME: + sResId = RID_BMP_NAVI_FRAME; + break; + case ContentTypeId::GRAPHIC: + sResId = RID_BMP_NAVI_GRAPHIC; + break; + case ContentTypeId::OLE: + sResId = RID_BMP_NAVI_OLE; + break; + case ContentTypeId::BOOKMARK: + sResId = RID_BMP_NAVI_BOOKMARK; + break; + case ContentTypeId::REGION: + sResId = RID_BMP_NAVI_REGION; + break; + case ContentTypeId::URLFIELD: + sResId = RID_BMP_NAVI_URLFIELD; + break; + case ContentTypeId::REFERENCE: + sResId = RID_BMP_NAVI_REFERENCE; + break; + case ContentTypeId::INDEX: + sResId = RID_BMP_NAVI_INDEX; + break; + case ContentTypeId::POSTIT: + sResId = RID_BMP_NAVI_POSTIT; + break; + case ContentTypeId::DRAWOBJECT: + sResId = RID_BMP_NAVI_DRAWOBJECT; + break; + case ContentTypeId::TEXTFIELD: + sResId = RID_BMP_NAVI_TEXTFIELD; + break; + case ContentTypeId::FOOTNOTE: + sResId = RID_BMP_NAVI_FOOTNOTE; + break; + case ContentTypeId::ENDNOTE: + sResId = RID_BMP_NAVI_ENDNOTE; + break; + case ContentTypeId::UNKNOWN: + SAL_WARN("sw.ui", "ContentTypeId::UNKNOWN has no bitmap preview"); + break; + } + + return sResId; + }; +} + +size_t SwContentTree::GetAbsPos(const weld::TreeIter& rIter) +{ + return weld::GetAbsPos(*m_xTreeView, rIter); +} + +size_t SwContentTree::GetEntryCount() const +{ + return m_nEntryCount; +} + +size_t SwContentTree::GetChildCount(const weld::TreeIter& rParent) const +{ + if (!m_xTreeView->iter_has_child(rParent)) + return 0; + + std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rParent)); + + size_t nCount = 0; + auto nRefDepth = m_xTreeView->get_iter_depth(*xParent); + auto nActDepth = nRefDepth; + do + { + if (!m_xTreeView->iter_next(*xParent)) + xParent.reset(); + else + nActDepth = m_xTreeView->get_iter_depth(*xParent); + nCount++; + } while(xParent && nRefDepth < nActDepth); + + nCount--; + return nCount; +} + +std::unique_ptr<weld::TreeIter> SwContentTree::GetEntryAtAbsPos(size_t nAbsPos) const +{ + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_iter_first(*xEntry)) + xEntry.reset(); + + while (nAbsPos && xEntry) + { + if (!m_xTreeView->iter_next(*xEntry)) + xEntry.reset(); + nAbsPos--; + } + return xEntry; +} + +void SwContentTree::Display( bool bActive ) +{ + // First read the selected entry to select it later again if necessary + // -> the user data here are no longer valid! + std::unique_ptr<weld::TreeIter> xOldSelEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_selected(xOldSelEntry.get())) + xOldSelEntry.reset(); + OUString sOldSelEntryId; + size_t nEntryRelPos = 0; // relative position to their parent + size_t nOldEntryCount = GetEntryCount(); + sal_Int32 nOldScrollPos = 0; + if (xOldSelEntry) + { + UpdateLastSelType(); + sOldSelEntryId = m_xTreeView->get_id(*xOldSelEntry); + nOldScrollPos = m_xTreeView->vadjustment_get_value(); + std::unique_ptr<weld::TreeIter> xParentEntry = m_xTreeView->make_iterator(xOldSelEntry.get()); + while (m_xTreeView->get_iter_depth(*xParentEntry)) + m_xTreeView->iter_parent(*xParentEntry); + if (m_xTreeView->get_iter_depth(*xOldSelEntry)) + nEntryRelPos = GetAbsPos(*xOldSelEntry) - GetAbsPos(*xParentEntry); + } + + clear(); + + if (!bActive) + m_eState = State::HIDDEN; + else if (State::HIDDEN == m_eState) + m_eState = State::ACTIVE; + SwWrtShell* pShell = GetWrtShell(); + const bool bReadOnly = !pShell || pShell->GetView().GetDocShell()->IsReadOnly(); + if(bReadOnly != m_bIsLastReadOnly) + { + m_bIsLastReadOnly = bReadOnly; + bool bDisable = pShell == nullptr || bReadOnly; + SwNavigationPI* pNavi = GetParentWindow(); + pNavi->m_xContent6ToolBox->set_item_sensitive("chapterup", !bDisable); + pNavi->m_xContent6ToolBox->set_item_sensitive("chapterdown", !bDisable); + pNavi->m_xContent6ToolBox->set_item_sensitive("promote", !bDisable); + pNavi->m_xContent6ToolBox->set_item_sensitive("demote", !bDisable); + pNavi->m_xContent5ToolBox->set_item_sensitive("reminder", !bDisable); + } + + if (pShell) + { + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator(); + std::unique_ptr<weld::TreeIter> xCntTypeEntry; + std::vector<std::unique_ptr<weld::TreeIter>> aNodesToExpand; + // all content navigation view + if(m_nRootType == ContentTypeId::UNKNOWN) + { + m_xTreeView->freeze(); + + for( ContentTypeId nCntType : o3tl::enumrange<ContentTypeId>() ) + { + std::unique_ptr<SwContentType>& rpContentT = bActive ? + m_aActiveContentArr[nCntType] : + m_aHiddenContentArr[nCntType]; + if(!rpContentT) + rpContentT.reset(new SwContentType(pShell, nCntType, m_nOutlineLevel )); + + OUString aImage(GetImageIdForContentTypeId(nCntType)); + bool bChOnDemand = 0 != rpContentT->GetMemberCount(); + OUString sId(weld::toId(rpContentT.get())); + insert(nullptr, rpContentT->GetName(), sId, bChOnDemand, xEntry.get()); + m_xTreeView->set_image(*xEntry, aImage); + + m_xTreeView->set_sensitive(*xEntry, bChOnDemand); + + if (nCntType == m_nLastSelType) + xCntTypeEntry = m_xTreeView->make_iterator(xEntry.get()); + + sal_Int32 nExpandOptions = (State::HIDDEN == m_eState) + ? m_nHiddenBlock + : m_nActiveBlock; + if (nExpandOptions & (1 << static_cast<int>(nCntType))) + { + // fill contents of to-be expanded entries while frozen + Expand(*xEntry, &aNodesToExpand); + m_xTreeView->set_children_on_demand(*xEntry, false); + } + } + + m_xTreeView->thaw(); + + // restore visual expanded tree state + for (const auto& rNode : aNodesToExpand) + m_xTreeView->expand_row(*rNode); + } + // root content navigation view + else + { + m_xTreeView->freeze(); + + std::unique_ptr<SwContentType>& rpRootContentT = bActive ? + m_aActiveContentArr[m_nRootType] : + m_aHiddenContentArr[m_nRootType]; + if(!rpRootContentT) + rpRootContentT.reset(new SwContentType(pShell, m_nRootType, m_nOutlineLevel )); + OUString aImage(GetImageIdForContentTypeId(m_nRootType)); + bool bChOnDemand = m_nRootType == ContentTypeId::OUTLINE; + OUString sId(weld::toId(rpRootContentT.get())); + insert(nullptr, rpRootContentT->GetName(), sId, bChOnDemand, xEntry.get()); + m_xTreeView->set_image(*xEntry, aImage); + + xCntTypeEntry = m_xTreeView->make_iterator(xEntry.get()); + + if (!bChOnDemand) + { + bool bRegion = rpRootContentT->GetType() == ContentTypeId::REGION; + + std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator(); + for (size_t i = 0; i < rpRootContentT->GetMemberCount(); ++i) + { + const SwContent* pCnt = rpRootContentT->GetMember(i); + if (pCnt) + { + OUString sEntry = pCnt->GetName(); + if(sEntry.isEmpty()) + sEntry = m_sSpace; + OUString sSubId(weld::toId(pCnt)); + insert(xEntry.get(), sEntry, sSubId, false, xChild.get()); + m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible()); + if (bRegion) + m_xTreeView->set_extra_row_indent(*xChild, static_cast<const SwRegionContent*>(pCnt)->GetRegionLevel()); + } + } + } + else + { + // fill contents of to-be expanded entries while frozen + Expand(*xEntry, &aNodesToExpand); + m_xTreeView->set_children_on_demand(*xEntry, false); + } + + m_xTreeView->set_sensitive(*xEntry, m_xTreeView->iter_has_child(*xEntry)); + + m_xTreeView->thaw(); + + if (bChOnDemand) + { + // restore visual expanded tree state + for (const auto& rNode : aNodesToExpand) + m_xTreeView->expand_row(*rNode); + } + else + m_xTreeView->expand_row(*xEntry); + } + + // Reselect the old selected entry. If it is not available, select the entry at the old + // selected entry position unless that entry position is now a content type or is past the + // end of the member list then select the entry at the previous entry position. + if (xOldSelEntry) + { + std::unique_ptr<weld::TreeIter> xSelEntry = m_xTreeView->make_iterator(xCntTypeEntry.get()); + if (nEntryRelPos) + { + std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator(xCntTypeEntry.get())); + std::unique_ptr<weld::TreeIter> xTemp(m_xTreeView->make_iterator(xIter.get())); + sal_uLong nPos = 1; + bool bNext; + while ((bNext = m_xTreeView->iter_next(*xIter) && lcl_IsContent(*xIter, *m_xTreeView))) + { + if (m_xTreeView->get_id(*xIter) == sOldSelEntryId || nPos == nEntryRelPos) + { + m_xTreeView->copy_iterator(*xIter, *xSelEntry); + break; + } + m_xTreeView->copy_iterator(*xIter, *xTemp); // note previous entry + nPos++; + } + if (!bNext) + xSelEntry = std::move(xTemp); + } + // set_cursor unselects all entries, makes passed entry visible, and selects it + m_xTreeView->set_cursor(*xSelEntry); + Select(); + } + } + + if (!m_bIgnoreDocChange && GetEntryCount() == nOldEntryCount) + { + m_xTreeView->vadjustment_set_value(nOldScrollPos); + } +} + +void SwContentTree::clear() +{ + m_xTreeView->freeze(); + m_xTreeView->clear(); + m_nEntryCount = 0; + m_xTreeView->thaw(); +} + +bool SwContentTree::FillTransferData( TransferDataContainer& rTransfer, + sal_Int8& rDragMode ) +{ + bool bRet = false; + SwWrtShell* pWrtShell = GetWrtShell(); + OSL_ENSURE(pWrtShell, "no Shell!"); + + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + bool bEntry = m_xTreeView->get_cursor(xEntry.get()); + if (!bEntry || lcl_IsContentType(*xEntry, *m_xTreeView) || !pWrtShell) + return false; + OUString sEntry; + assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)))); + SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry)); + + const ContentTypeId nActType = pCnt->GetParent()->GetType(); + OUString sUrl; + bool bOutline = false; + OUString sOutlineText; + switch( nActType ) + { + case ContentTypeId::OUTLINE: + { + const SwOutlineNodes::size_type nPos = static_cast<SwOutlineContent*>(pCnt)->GetOutlinePos(); + OSL_ENSURE(nPos < pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount(), + "outlinecnt changed"); + + // make sure outline may actually be copied + if( pWrtShell->IsOutlineCopyable( nPos ) ) + { + const SwNumRule* pOutlRule = pWrtShell->GetOutlineNumRule(); + const SwTextNode* pTextNd = + pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineNode(nPos); + if (pTextNd && pOutlRule && pTextNd->IsNumbered(pWrtShell->GetLayout())) + { + SwNumberTree::tNumberVector aNumVector = + pTextNd->GetNumberVector(pWrtShell->GetLayout()); + for( int nLevel = 0; + nLevel <= pTextNd->GetActualListLevel(); + nLevel++ ) + { + const SwNumberTree::tSwNumTreeNumber nVal = aNumVector[nLevel] + 1; + sEntry += OUString::number( nVal - pOutlRule->Get(nLevel).GetStart() ) + "."; + } + } + sEntry += pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText(nPos, pWrtShell->GetLayout(), false); + sOutlineText = pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText(nPos, pWrtShell->GetLayout()); + m_bIsOutlineMoveable = static_cast<SwOutlineContent*>(pCnt)->IsMoveable(); + bOutline = true; + } + } + break; + case ContentTypeId::POSTIT: + case ContentTypeId::INDEX: + case ContentTypeId::REFERENCE : + case ContentTypeId::TEXTFIELD: + // cannot be inserted, neither as URL nor as section + break; + case ContentTypeId::URLFIELD: + sUrl = static_cast<SwURLFieldContent*>(pCnt)->GetURL(); + [[fallthrough]]; + case ContentTypeId::OLE: + case ContentTypeId::GRAPHIC: + if(GetParentWindow()->GetRegionDropMode() != RegionMode::NONE) + break; + else + rDragMode &= ~( DND_ACTION_MOVE | DND_ACTION_LINK ); + [[fallthrough]]; + default: + sEntry = m_xTreeView->get_text(*xEntry); + } + + if(!sEntry.isEmpty()) + { + const SwDocShell* pDocShell = pWrtShell->GetView().GetDocShell(); + if(sUrl.isEmpty()) + { + if(pDocShell->HasName()) + { + SfxMedium* pMedium = pDocShell->GetMedium(); + sUrl = pMedium->GetURLObject().GetURLNoMark(); + // only if a primarily link shall be integrated. + bRet = true; + } + else if ( nActType == ContentTypeId::REGION || nActType == ContentTypeId::BOOKMARK ) + { + // For field and bookmarks a link is also allowed + // without a filename into its own document. + bRet = true; + } + else if (State::CONSTANT == m_eState && + ( !::GetActiveView() || + m_pActiveShell != ::GetActiveView()->GetWrtShellPtr())) + { + // Urls of inactive views cannot dragged without + // file names, also. + bRet = false; + } + else + { + bRet = GetParentWindow()->GetRegionDropMode() == RegionMode::NONE; + rDragMode = DND_ACTION_MOVE; + } + + const OUString& rToken = pCnt->GetParent()->GetTypeToken(); + sUrl += "#" + sEntry; + if(!rToken.isEmpty()) + { + sUrl += OUStringChar(cMarkSeparator) + rToken; + } + } + else + bRet = true; + + if( bRet ) + { + // In Outlines of heading text must match + // the real number into the description. + if(bOutline) + sEntry = sOutlineText; + + { + NaviContentBookmark aBmk( sUrl, sEntry, + GetParentWindow()->GetRegionDropMode(), + pDocShell); + aBmk.Copy( rTransfer ); + } + + // An INetBookmark must a be delivered to foreign DocShells + if( pDocShell->HasName() ) + { + INetBookmark aBkmk( sUrl, sEntry ); + rTransfer.CopyINetBookmark( aBkmk ); + } + } + } + return bRet; +} + +void SwContentTree::ToggleToRoot() +{ + if(!m_bIsRoot) + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + bool bEntry = m_xTreeView->get_cursor(xEntry.get()); + if (bEntry) + { + const SwContentType* pCntType; + if (lcl_IsContentType(*xEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)))); + pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry)); + } + else + { + assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)))); + pCntType = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->GetParent(); + } + m_nRootType = pCntType->GetType(); + m_bIsRoot = true; + if (m_nRootType == ContentTypeId::OUTLINE || m_nRootType == ContentTypeId::DRAWOBJECT) + { + m_xTreeView->set_selection_mode(SelectionMode::Multiple); + } + Display(State::HIDDEN != m_eState); + } + } + else + { + m_xTreeView->set_selection_mode(SelectionMode::Single); + m_nLastSelType = m_nRootType; + m_nRootType = ContentTypeId::UNKNOWN; + m_bIsRoot = false; + // Other content type member data could have changed while in root view. Fill the content + // member lists excluding the toggled from root content which should already have the most + // recent data. + if (State::HIDDEN != m_eState) + { + for (ContentTypeId i : o3tl::enumrange<ContentTypeId>()) + { + if (i != m_nLastSelType && m_aActiveContentArr[i]) + m_aActiveContentArr[i]->FillMemberList(); + } + } + Display(State::HIDDEN != m_eState); + } + m_pConfig->SetRootType( m_nRootType ); + weld::Toolbar* pBox = GetParentWindow()->m_xContent5ToolBox.get(); + pBox->set_item_active("root", m_bIsRoot); +} + +bool SwContentTree::HasContentChanged() +{ + bool bContentChanged = false; + +// - Run through the local array and the Treelistbox in parallel. +// - Are the records not expanded, they are discarded only in the array +// and the content type will be set as the new UserData. +// - Is the root mode is active only this will be updated. + +// Valid for the displayed content types is: +// the Memberlist will be erased and the membercount will be updated +// If content will be checked, the memberlists will be replenished +// at the same time. Once a difference occurs it will be only replenished +// no longer checked. Finally, the box is filled again. + + if (State::HIDDEN == m_eState) + { + for(ContentTypeId i : o3tl::enumrange<ContentTypeId>()) + { + if(m_aActiveContentArr[i]) + m_aActiveContentArr[i]->Invalidate(); + } + return false; + } + + // root content navigation view + if(m_bIsRoot) + { + std::unique_ptr<weld::TreeIter> xRootEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_iter_first(*xRootEntry)) + return true; + + assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xRootEntry)))); + const ContentTypeId nType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xRootEntry))->GetType(); + SwContentType* pArrType = m_aActiveContentArr[nType].get(); + assert(weld::toId(pArrType) == m_xTreeView->get_id(*xRootEntry)); + if (!pArrType) + return true; + + pArrType->FillMemberList(&bContentChanged); + if (bContentChanged) + return true; + + // FillMemberList tests if member count in old member array equals member count in new + // member array. Test here for member count difference between array and tree. + const size_t nChildCount = GetChildCount(*xRootEntry); + if (nChildCount != pArrType->GetMemberCount()) + return true; + + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(xRootEntry.get())); + for (size_t j = 0; j < nChildCount; ++j) + { + if (!m_xTreeView->iter_next(*xEntry)) + { + SAL_WARN("sw.ui", "unexpected missing entry"); + return true; + } + + // FillMemberList clears the content type member list and refills with new data. + // Treeview entry user data is set here to the string representation of the pointer to + // the member data in the array. The Display function will clear and recreate the + // treeview from the content type member arrays if content change is detected. + const SwContent* pCnt = pArrType->GetMember(j); + OUString sSubId(weld::toId(pCnt)); + m_xTreeView->set_id(*xEntry, sSubId); + + OUString sEntryText = m_xTreeView->get_text(*xEntry); + if (sEntryText != pCnt->GetName() && + !(sEntryText == m_sSpace && pCnt->GetName().isEmpty())) + { + return true; + } + } + } + // all content navigation view + else + { + // Fill member list for each content type and check for content change. If content change + // is detected only fill member lists for remaining content types. The Display function + // will clear and recreate the treeview from the content type member arrays if content has + // changed. + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + + // lambda function to find the next content type entry + auto lcl_nextContentTypeEntry = [this, &xEntry](){ + while (m_xTreeView->get_iter_depth(*xEntry)) + m_xTreeView->iter_parent(*xEntry); + return m_xTreeView->iter_next_sibling(*xEntry); + }; + + for (bool bEntry = m_xTreeView->get_iter_first(*xEntry); bEntry; + bEntry = lcl_nextContentTypeEntry()) + { + assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)))); + SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry)); + const size_t nCntCount = pCntType->GetMemberCount(); + const ContentTypeId nType = pCntType->GetType(); + SwContentType* pArrType = m_aActiveContentArr[nType].get(); + assert(weld::toId(pArrType) == m_xTreeView->get_id(*xEntry)); + + if (!pArrType) + { + bContentChanged = true; + continue; + } + + // all content type member lists must be filled! + if (bContentChanged) + { + // If content change has already been detected there is no need to detect + // other content change so no argument is supplied here to FillMemberList. + pArrType->FillMemberList(); + continue; + } + + pArrType->FillMemberList(&bContentChanged); + if (bContentChanged) + continue; + + // does entry have children? + if (m_xTreeView->get_row_expanded(*xEntry)) + { + const size_t nChildCount = GetChildCount(*xEntry); + if(nChildCount != pArrType->GetMemberCount()) + { + bContentChanged = true; + continue; + } + + for(size_t j = 0; j < nChildCount; ++j) + { + if (!m_xTreeView->iter_next(*xEntry)) + { + SAL_WARN("sw.ui", "unexpected missing entry"); + bContentChanged = true; + continue; + } + + const SwContent* pCnt = pArrType->GetMember(j); + OUString sSubId(weld::toId(pCnt)); + m_xTreeView->set_id(*xEntry, sSubId); + + OUString sEntryText = m_xTreeView->get_text(*xEntry); + if( sEntryText != pCnt->GetName() && + !(sEntryText == m_sSpace && pCnt->GetName().isEmpty())) + { + bContentChanged = true; + continue; + } + } + } + // not expanded and has children + else if (m_xTreeView->iter_has_child(*xEntry)) + { + bool bRemoveChildren = false; + const size_t nOldChildCount = GetChildCount(*xEntry); + const size_t nNewChildCount = pArrType->GetMemberCount(); + if (nOldChildCount != nNewChildCount) + { + bRemoveChildren = true; + } + else + { + std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(xEntry.get())); + (void)m_xTreeView->iter_children(*xChild); + for (size_t j = 0; j < nOldChildCount; ++j) + { + const SwContent* pCnt = pArrType->GetMember(j); + OUString sSubId(weld::toId(pCnt)); + m_xTreeView->set_id(*xChild, sSubId); + OUString sEntryText = m_xTreeView->get_text(*xChild); + if( sEntryText != pCnt->GetName() && + !(sEntryText == m_sSpace && pCnt->GetName().isEmpty())) + { + bRemoveChildren = true; + } + (void)m_xTreeView->iter_next(*xChild); + } + } + if (bRemoveChildren) + { + std::unique_ptr<weld::TreeIter> xRemove(m_xTreeView->make_iterator(xEntry.get())); + while (m_xTreeView->iter_children(*xRemove)) + { + remove(*xRemove); + m_xTreeView->copy_iterator(*xEntry, *xRemove); + } + m_xTreeView->set_children_on_demand(*xEntry, nNewChildCount != 0); + } + } + else if((nCntCount != 0) + != (pArrType->GetMemberCount()!=0)) + { + bContentChanged = true; + continue; + } + } + } + + return bContentChanged; +} + +void SwContentTree::UpdateLastSelType() +{ + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_selected(xEntry.get())) + { + while (m_xTreeView->get_iter_depth(*xEntry)) + m_xTreeView->iter_parent(*xEntry); + void* pId = weld::fromId<void*>(m_xTreeView->get_id(*xEntry)); + if (pId && lcl_IsContentType(*xEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwContentType*>(static_cast<SwTypeNumber*>(pId))); + m_nLastSelType = static_cast<SwContentType*>(pId)->GetType(); + } + } +} + +void SwContentTree::FindActiveTypeAndRemoveUserData() +{ + UpdateLastSelType(); + + // If clear is called by TimerUpdate: + // Only for root can the validity of the UserData be guaranteed. + m_xTreeView->all_foreach([this](weld::TreeIter& rEntry){ + m_xTreeView->set_id(rEntry, ""); + return false; + }); +} + +void SwContentTree::SetHiddenShell(SwWrtShell* pSh) +{ + m_pHiddenShell = pSh; + m_eState = State::HIDDEN; + FindActiveTypeAndRemoveUserData(); + for(ContentTypeId i : o3tl::enumrange<ContentTypeId>()) + { + m_aHiddenContentArr[i].reset(); + } + Display(false); + + GetParentWindow()->UpdateListBox(); +} + +void SwContentTree::SetActiveShell(SwWrtShell* pSh) +{ + bool bClear = m_pActiveShell != pSh; + if (State::ACTIVE == m_eState && bClear) + { + if (m_pActiveShell) + EndListening(*m_pActiveShell->GetView().GetDocShell()); + m_pActiveShell = pSh; + FindActiveTypeAndRemoveUserData(); + clear(); + } + else if (State::CONSTANT == m_eState) + { + if (m_pActiveShell) + EndListening(*m_pActiveShell->GetView().GetDocShell()); + m_pActiveShell = pSh; + m_eState = State::ACTIVE; + bClear = true; + } + + // tdf#148432 in LTR UI override the navigator treeview direction based on + // the first page directionality + if (m_pActiveShell && !AllSettings::GetLayoutRTL()) + { + const SwPageDesc& rDesc = m_pActiveShell->GetPageDesc(0); + const SvxFrameDirectionItem& rFrameDir = rDesc.GetMaster().GetFrameDir(); + m_xTreeView->set_direction(rFrameDir.GetValue() == SvxFrameDirection::Horizontal_RL_TB); + } + + // Only if it is the active view, the array will be deleted and + // the screen filled new. + if (State::ACTIVE == m_eState && bClear) + { + if (m_pActiveShell) + StartListening(*m_pActiveShell->GetView().GetDocShell()); + FindActiveTypeAndRemoveUserData(); + for(ContentTypeId i : o3tl::enumrange<ContentTypeId>()) + { + m_aActiveContentArr[i].reset(); + } + Display(true); + } +} + +void SwContentTree::SetConstantShell(SwWrtShell* pSh) +{ + if (m_pActiveShell) + EndListening(*m_pActiveShell->GetView().GetDocShell()); + m_pActiveShell = pSh; + m_eState = State::CONSTANT; + StartListening(*m_pActiveShell->GetView().GetDocShell()); + FindActiveTypeAndRemoveUserData(); + for(ContentTypeId i : o3tl::enumrange<ContentTypeId>()) + { + m_aActiveContentArr[i].reset(); + } + Display(true); +} + +void SwContentTree::Notify(SfxBroadcaster & rBC, SfxHint const& rHint) +{ + SfxViewEventHint const*const pVEHint(dynamic_cast<SfxViewEventHint const*>(&rHint)); + SwXTextView* pDyingShell = nullptr; + if (m_pActiveShell && pVEHint && pVEHint->GetEventName() == "OnViewClosed") + pDyingShell = dynamic_cast<SwXTextView*>(pVEHint->GetController().get()); + if (pDyingShell && pDyingShell->GetView() == &m_pActiveShell->GetView()) + { + SetActiveShell(nullptr); // our view is dying, clear our pointers to it + } + else + { + SfxListener::Notify(rBC, rHint); + } + switch (rHint.GetId()) + { + case SfxHintId::SwNavigatorUpdateTracking: + UpdateTracking(); + break; + case SfxHintId::SwNavigatorSelectOutlinesWithSelections: + { + if (m_nRootType == ContentTypeId::OUTLINE) + { + SelectOutlinesWithSelection(); + // make first selected entry visible + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (xEntry && m_xTreeView->get_selected(xEntry.get())) + m_xTreeView->scroll_to_row(*xEntry); + } + else if (m_nRootType == ContentTypeId::UNKNOWN) + m_xTreeView->unselect_all(); + break; + } + case SfxHintId::DocChanged: + if (!m_bIgnoreDocChange) + { + m_bDocHasChanged = true; + TimerUpdate(&m_aUpdTimer); + } + break; + case SfxHintId::ModeChanged: + if (SwWrtShell* pShell = GetWrtShell()) + { + const bool bReadOnly = pShell->GetView().GetDocShell()->IsReadOnly(); + if (bReadOnly != m_bIsLastReadOnly) + { + m_bIsLastReadOnly = bReadOnly; + + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_cursor(xEntry.get())) + { + m_xTreeView->select(*xEntry); + Select(); + } + else + m_xTreeView->unselect_all(); + } + } + break; + default: + break; + } +} + +void SwContentTree::ExecCommand(std::string_view rCmd, bool bOutlineWithChildren) +{ + MakeAllOutlineContentTemporarilyVisible a(GetWrtShell()->GetDoc()); + + const bool bUp = rCmd == "chapterup"; + const bool bUpDown = bUp || rCmd == "chapterdown"; + const bool bLeft = rCmd == "promote"; + const bool bLeftRight = bLeft || rCmd == "demote"; + if (!bUpDown && !bLeftRight) + return; + if (GetWrtShell()->GetView().GetDocShell()->IsReadOnly() || + (State::ACTIVE != m_eState && + (State::CONSTANT != m_eState || m_pActiveShell != GetParentWindow()->GetCreateView()->GetWrtShellPtr()))) + { + return; + } + + m_bIgnoreDocChange = true; + + SwWrtShell *const pShell = GetWrtShell(); + sal_Int8 nActOutlineLevel = m_nOutlineLevel; + SwOutlineNodes::size_type nActPos = pShell->GetOutlinePos(nActOutlineLevel); + + std::vector<SwTextNode*> selectedOutlineNodes; + std::vector<std::unique_ptr<weld::TreeIter>> selected; + + m_xTreeView->selected_foreach([this, pShell, &bLeftRight, &bOutlineWithChildren, &selected, &selectedOutlineNodes](weld::TreeIter& rEntry){ + // it's possible to select the root node too which is a really bad idea + bool bSkip = lcl_IsContentType(rEntry, *m_xTreeView); + // filter out children of selected parents so they don't get promoted + // or moved twice (except if there is Ctrl modifier, since in that + // case children are re-parented) + if ((bLeftRight || bOutlineWithChildren) && !selected.empty()) + { + std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rEntry)); + for (bool bParent = m_xTreeView->iter_parent(*xParent); bParent; bParent = m_xTreeView->iter_parent(*xParent)) + { + if (m_xTreeView->iter_compare(*selected.back(), *xParent) == 0) + { + bSkip = true; + break; + } + } + } + if (!bSkip) + { + selected.emplace_back(m_xTreeView->make_iterator(&rEntry)); + const SwNodes& rNodes = pShell->GetNodes(); + const size_t nPos = GetAbsPos(rEntry) - 1; + if (nPos < rNodes.GetOutLineNds().size()) + { + SwNode* pNode = rNodes.GetOutLineNds()[ nPos ]; + if (pNode) + { + selectedOutlineNodes.push_back(pNode->GetTextNode()); + } + } + } + return false; + }); + + if (bUpDown && !bUp) + { // to move down, start at the end! + std::reverse(selected.begin(), selected.end()); + } + + SwOutlineNodes::difference_type nDirLast = bUp ? -1 : 1; + bool bStartedAction = false; + for (auto const& pCurrentEntry : selected) + { + assert(pCurrentEntry && lcl_IsContent(*pCurrentEntry, *m_xTreeView)); + if (lcl_IsContent(*pCurrentEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*pCurrentEntry)))); + if ((m_bIsRoot && m_nRootType == ContentTypeId::OUTLINE) || + weld::fromId<SwContent*>(m_xTreeView->get_id(*pCurrentEntry))->GetParent()->GetType() + == ContentTypeId::OUTLINE) + { + nActPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*pCurrentEntry))->GetOutlinePos(); + } + } + if (nActPos == SwOutlineNodes::npos || (bUpDown && !pShell->IsOutlineMovable(nActPos))) + { + continue; + } + + if (!bStartedAction) + { + pShell->StartAllAction(); + pShell->StartUndo(bLeftRight ? SwUndoId::OUTLINE_LR : SwUndoId::OUTLINE_UD); + bStartedAction = true; + } + pShell->GotoOutline( nActPos); // If text selection != box selection + pShell->Push(); + pShell->MakeOutlineSel(nActPos, nActPos, bOutlineWithChildren); + if (bUpDown) + { + const size_t nEntryAbsPos(GetAbsPos(*pCurrentEntry)); + SwOutlineNodes::difference_type nDir = bUp ? -1 : 1; + if (!bOutlineWithChildren && ((nDir == -1 && nActPos > 0) || + (nDir == 1 && nEntryAbsPos < GetEntryCount() - 2))) + { + pShell->MoveOutlinePara( nDir ); + // Set cursor back to the current position + pShell->GotoOutline( nActPos + nDir); + } + else if (bOutlineWithChildren) + { + SwOutlineNodes::size_type nActEndPos = nActPos; + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(pCurrentEntry.get())); + assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*pCurrentEntry)))); + const auto nActLevel = weld::fromId<SwOutlineContent*>( + m_xTreeView->get_id(*pCurrentEntry))->GetOutlineLevel(); + bool bEntry = m_xTreeView->iter_next(*xEntry); + while (bEntry && lcl_IsContent(*xEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)))); + if (nActLevel >= weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xEntry))->GetOutlineLevel()) + break; + nActEndPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xEntry))->GetOutlinePos(); + bEntry = m_xTreeView->iter_next(*xEntry); + } + if (nDir == 1) // move down + { + std::unique_ptr<weld::TreeIter> xNextSibling(m_xTreeView->make_iterator(pCurrentEntry.get())); + if (m_xTreeView->iter_next_sibling(*xNextSibling) && m_xTreeView->is_selected(*xNextSibling)) + nDir = nDirLast; + else + { + // If the last entry is to be moved we're done + if (bEntry && lcl_IsContent(*xEntry, *m_xTreeView)) + { + // xEntry now points to the entry following the last + // selected entry. + SwOutlineNodes::size_type nDest = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xEntry))->GetOutlinePos(); + // here needs to found the next entry after next. + // The selection must be inserted in front of that. + while (bEntry) + { + bEntry = m_xTreeView->iter_next(*xEntry); + assert(!bEntry || !lcl_IsContent(*xEntry, *m_xTreeView)|| + dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)))); + // nDest++ may only executed if bEntry + if (bEntry) + { + if (!lcl_IsContent(*xEntry, *m_xTreeView)) + break; + else if (nActLevel >= weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xEntry))->GetOutlineLevel()) + { + // nDest needs adjusted if there are selected entries (including ancestral lineage) + // immediately before the current moved entry. + std::unique_ptr<weld::TreeIter> xTmp(m_xTreeView->make_iterator(xEntry.get())); + bool bTmp = m_xTreeView->iter_previous(*xTmp); + while (bTmp && lcl_IsContent(*xTmp, *m_xTreeView) && + nActLevel < weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xTmp))->GetOutlineLevel()) + { + while (bTmp && lcl_IsContent(*xTmp, *m_xTreeView) && !m_xTreeView->is_selected(*xTmp) && + nActLevel < weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xTmp))->GetOutlineLevel()) + { + bTmp = m_xTreeView->iter_parent(*xTmp); + } + if (!bTmp || !m_xTreeView->is_selected(*xTmp)) + break; + bTmp = m_xTreeView->iter_previous(*xTmp); + nDest = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xTmp))->GetOutlinePos(); + } + std::unique_ptr<weld::TreeIter> xPrevSibling(m_xTreeView->make_iterator(xEntry.get())); + if (!m_xTreeView->iter_previous_sibling(*xPrevSibling) || !m_xTreeView->is_selected(*xPrevSibling)) + break; + } + else + { + nDest = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xEntry))->GetOutlinePos(); + } + } + } + nDirLast = nDir = nDest - nActEndPos; + // If no entry was found that allows insertion before + // it, we just move it to the end. + } + else + nDirLast = nDir = 0; + } + } + else // move up + { + std::unique_ptr<weld::TreeIter> xPrevSibling(m_xTreeView->make_iterator(pCurrentEntry.get())); + if (m_xTreeView->iter_previous_sibling(*xPrevSibling) && m_xTreeView->is_selected(*xPrevSibling)) + nDir = nDirLast; + else + { + SwOutlineNodes::size_type nDest = nActPos; + bEntry = true; + m_xTreeView->copy_iterator(*pCurrentEntry, *xEntry); + while (bEntry && nDest) + { + bEntry = m_xTreeView->iter_previous(*xEntry); + assert(!bEntry || !lcl_IsContent(*xEntry, *m_xTreeView) || + dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)))); + if (bEntry && lcl_IsContent(*xEntry, *m_xTreeView)) + { + nDest = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xEntry))->GetOutlinePos(); + } + else + { + nDest = 0; // presumably? + } + if (bEntry) + { + if (!lcl_IsContent(*xEntry, *m_xTreeView)) + break; + else if (nActLevel >= weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xEntry))->GetOutlineLevel()) + { + // nDest needs adjusted if there are selected entries immediately + // after the level change. + std::unique_ptr<weld::TreeIter> xTmp(m_xTreeView->make_iterator(xEntry.get())); + bool bTmp = m_xTreeView->iter_next(*xTmp); + while (bTmp && lcl_IsContent(*xTmp, *m_xTreeView) && + nActLevel < weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xTmp))->GetOutlineLevel() && + m_xTreeView->is_selected(*xTmp)) + { + nDest = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xTmp))->GetOutlinePos(); + const auto nLevel = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xTmp))->GetOutlineLevel(); + // account for selected entries' descendent lineage + bTmp = m_xTreeView->iter_next(*xTmp); + while (bTmp && lcl_IsContent(*xTmp, *m_xTreeView) && + nLevel < weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xTmp))->GetOutlineLevel()) + { + nDest = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xTmp))->GetOutlinePos(); + bTmp = m_xTreeView->iter_next(*xTmp); + } + } + break; + } + } + } + nDirLast = nDir = nDest - nActPos; + } + } + if (nDir) + { + pShell->MoveOutlinePara( nDir ); + // Set cursor back to the current position + pShell->GotoOutline(nActPos + nDir); + } + } + } + else + { + if (!pShell->IsProtectedOutlinePara()) + pShell->OutlineUpDown(bLeft ? -1 : 1); + } + + pShell->ClearMark(); + pShell->Pop(SwCursorShell::PopMode::DeleteCurrent); // Cursor is now back at the current heading. + } + + if (bStartedAction) + { + pShell->EndUndo(); + pShell->EndAllAction(); + if (m_aActiveContentArr[ContentTypeId::OUTLINE]) + m_aActiveContentArr[ContentTypeId::OUTLINE]->Invalidate(); + + // tdf#143547 LO Writer: navigator should stand still on promoting and demoting + // In addition to m_bIgnoreDocChange being true, selections are cleared before the Display + // call. Either of these conditions disable restore of scroll position happening in the + // Display function so it needs to be done here. + auto nOldScrollPos = m_xTreeView->vadjustment_get_value(); + + // clear all selections to prevent the Display function from trying to reselect selected entries + m_xTreeView->unselect_all(); + Display(true); + m_xTreeView->vadjustment_set_value(nOldScrollPos); + + // reselect entries + const SwOutlineNodes::size_type nCurrPos = pShell->GetOutlinePos(MAXLEVEL); + std::unique_ptr<weld::TreeIter> xListEntry(m_xTreeView->make_iterator()); + bool bListEntry = m_xTreeView->get_iter_first(*xListEntry); + while ((bListEntry = m_xTreeView->iter_next(*xListEntry)) && lcl_IsContent(*xListEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xListEntry)))); + if (weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xListEntry))->GetOutlinePos() == nCurrPos) + { + std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(xListEntry.get())); + if (m_xTreeView->iter_parent(*xParent) && !m_xTreeView->get_row_expanded(*xParent)) + m_xTreeView->expand_row(*xParent); + m_xTreeView->set_cursor(*xListEntry); // unselect all entries, make entry visible, set focus, and select + Select(); + break; + } + } + + if (m_bIsRoot) + { + const SwOutlineNodes& rOutLineNds = pShell->GetNodes().GetOutLineNds(); + for (SwTextNode* pNode : selectedOutlineNodes) + { + SwOutlineNodes::const_iterator aFndIt = rOutLineNds.find(pNode); + if(aFndIt == rOutLineNds.end()) + continue; + const size_t nFndPos = aFndIt - rOutLineNds.begin(); + std::unique_ptr<weld::TreeIter> xEntry = GetEntryAtAbsPos(nFndPos + 1); + if (xEntry) + { + m_xTreeView->select(*xEntry); + std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(xEntry.get())); + if (m_xTreeView->iter_parent(*xParent) && !m_xTreeView->get_row_expanded(*xParent)) + m_xTreeView->expand_row(*xParent); + } + } + } + } + m_bIgnoreDocChange = false; +} + +void SwContentTree::ShowTree() +{ + m_xTreeView->show(); + m_aUpdTimer.Start(); +} + +void SwContentTree::HideTree() +{ + // folded together will not be idled + m_aUpdTimer.Stop(); + m_xTreeView->hide(); +} + +static void lcl_SelectByContentTypeAndAddress(SwContentTree* pThis, weld::TreeView& rContentTree, + ContentTypeId nType, const void* ptr) +{ + if (!ptr) + { + rContentTree.set_cursor(-1); + pThis->Select(); + return; + } + + // find content type entry + std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator()); + + bool bFoundEntry = rContentTree.get_iter_first(*xIter); + while (bFoundEntry) + { + void* pUserData = weld::fromId<void*>(rContentTree.get_id(*xIter)); + assert(dynamic_cast<SwContentType*>(static_cast<SwTypeNumber*>(pUserData))); + if (nType == static_cast<SwContentType*>(pUserData)->GetType()) + break; + bFoundEntry = rContentTree.iter_next_sibling(*xIter); + } + + if (!bFoundEntry) + { + rContentTree.set_cursor(-1); + pThis->Select(); + return; + } + + // assure content type entry is expanded + rContentTree.expand_row(*xIter); + + // find content type content entry and select it + const void* p = nullptr; + while (rContentTree.iter_next(*xIter) && lcl_IsContent(*xIter, rContentTree)) + { + void* pUserData = weld::fromId<void*>(rContentTree.get_id(*xIter)); + switch( nType ) + { + case ContentTypeId::FOOTNOTE: + case ContentTypeId::ENDNOTE: + { + assert(dynamic_cast<SwTextFootnoteContent*>(static_cast<SwTypeNumber*>(pUserData))); + SwTextFootnoteContent* pCnt = static_cast<SwTextFootnoteContent*>(pUserData); + p = pCnt->GetTextFootnote(); + break; + } + case ContentTypeId::URLFIELD: + { + assert(dynamic_cast<SwURLFieldContent*>(static_cast<SwTypeNumber*>(pUserData))); + SwURLFieldContent* pCnt = static_cast<SwURLFieldContent*>(pUserData); + p = static_cast<const SwTextAttr*>(pCnt->GetINetAttr()); + break; + } + case ContentTypeId::TEXTFIELD: + { + assert(dynamic_cast<SwTextFieldContent*>(static_cast<SwTypeNumber*>(pUserData))); + SwTextFieldContent* pCnt = static_cast<SwTextFieldContent*>(pUserData); + p = pCnt->GetFormatField()->GetField(); + break; + } + case ContentTypeId::POSTIT: + { + assert(dynamic_cast<SwPostItContent*>(static_cast<SwTypeNumber*>(pUserData))); + SwPostItContent* pCnt = static_cast<SwPostItContent*>(pUserData); + p = pCnt->GetPostIt()->GetField(); + break; + } + default: + break; + } + if (ptr == p) + { + // 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(); + } + return; + } + } + + rContentTree.set_cursor(-1); + pThis->Select(); + return; +} + +static void lcl_SelectByContentTypeAndName(SwContentTree* pThis, weld::TreeView& rContentTree, + std::u16string_view rContentTypeName, std::u16string_view rName) +{ + if (rName.empty()) + return; + + // 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) + return; + + 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; + } + } +} + +static void lcl_SelectDrawObjectByName(weld::TreeView& rContentTree, std::u16string_view rName) +{ + if (rName.empty()) + return; + + // find content type entry + std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator()); + bool bFoundEntry = rContentTree.get_iter_first(*xIter); + while (bFoundEntry && SwResId(STR_CONTENT_TYPE_DRAWOBJECT) != 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)) + { + if (!rContentTree.is_selected(*xIter)) + { + rContentTree.select(*xIter); + rContentTree.scroll_to_row(*xIter); + } + break; + } + } + } +} + +/** No idle with focus or while dragging */ +IMPL_LINK_NOARG(SwContentTree, TimerUpdate, Timer *, void) +{ + // No need to update if content tree is not visible + if (!m_xTreeView->is_visible()) + return; + + // No update while focus is not in document. + // No update while drag and drop. + // Query view because the Navigator is cleared too late. + SwView* pView = GetParentWindow()->GetCreateView(); + if(pView && pView->GetWrtShellPtr() && pView->GetWrtShellPtr()->GetWin() && + (pView->GetWrtShellPtr()->GetWin()->HasFocus() || m_bDocHasChanged || m_bViewHasChanged) && + !IsInDrag() && !pView->GetWrtShellPtr()->ActionPend()) + { + if (m_bDocHasChanged || m_bViewHasChanged) + { + SwWrtShell* pActShell = pView->GetWrtShellPtr(); + if (State::CONSTANT == m_eState && !lcl_FindShell(m_pActiveShell)) + { + SetActiveShell(pActShell); + GetParentWindow()->UpdateListBox(); + } + if (State::ACTIVE == m_eState && pActShell != GetWrtShell()) + { + SetActiveShell(pActShell); + } + else if ((State::ACTIVE == m_eState || (State::CONSTANT == m_eState && pActShell == GetWrtShell())) && + HasContentChanged()) + { + FindActiveTypeAndRemoveUserData(); + Display(true); + } + } + UpdateTracking(); + m_bIsIdleClear = false; + m_bDocHasChanged = false; + m_bViewHasChanged = false; + } + else if (!pView && State::ACTIVE == m_eState && !m_bIsIdleClear) // this block seems never to be entered + { + if(m_pActiveShell) + { + SetActiveShell(nullptr); + } + clear(); + m_bIsIdleClear = true; + } +} + +void SwContentTree::UpdateTracking() +{ + if (State::HIDDEN == m_eState || !m_pActiveShell) + return; + + // only when treeview or treeview context menu does not have focus + if (m_xTreeView->has_focus() || m_xTreeView->has_child_focus()) + return; + + // m_bIgnoreDocChange is set on delete and outline visibility toggle + if (m_bIgnoreDocChange) + { + m_bIgnoreDocChange = false; + return; + } + + // bTrack is used to disallow tracking after jumping to an outline until the outline position + // that was jumped to is no longer the current outline position. + bool bTrack = true; + if (m_nLastGotoContentWasOutlinePos != SwOutlineNodes::npos) + { + if (m_pActiveShell->GetOutlinePos() == m_nLastGotoContentWasOutlinePos) + bTrack = false; + else + m_nLastGotoContentWasOutlinePos = SwOutlineNodes::npos; + } + + if (bTrack) + { + // graphic, frame, and ole + if (m_pActiveShell->GetSelectionType() & + (SelectionType::Graphic | SelectionType::Frame | SelectionType::Ole)) + { + OUString aContentTypeName; + if (m_pActiveShell->GetSelectionType() == SelectionType::Graphic && + !(m_bIsRoot && m_nRootType != ContentTypeId::GRAPHIC)) + { + if (!mTrackContentType[ContentTypeId::GRAPHIC]) return; + aContentTypeName = SwResId(STR_CONTENT_TYPE_GRAPHIC); + } + else if (m_pActiveShell->GetSelectionType() == SelectionType::Frame && + !(m_bIsRoot && m_nRootType != ContentTypeId::FRAME)) + { + if (!mTrackContentType[ContentTypeId::FRAME]) return; + aContentTypeName = SwResId(STR_CONTENT_TYPE_FRAME); + } + else if (m_pActiveShell->GetSelectionType() == SelectionType::Ole && + !(m_bIsRoot && m_nRootType != ContentTypeId::OLE)) + { + if (!mTrackContentType[ContentTypeId::OLE]) return; + aContentTypeName = SwResId(STR_CONTENT_TYPE_OLE); + } + if (!aContentTypeName.isEmpty()) + { + OUString aName(m_pActiveShell->GetFlyName()); + lcl_SelectByContentTypeAndName(this, *m_xTreeView, aContentTypeName, aName); + return; + } + } + // drawing + if ((m_pActiveShell->GetSelectionType() & (SelectionType::DrawObject | + SelectionType::DrawObjectEditMode | + SelectionType::DbForm)) && + !(m_bIsRoot && m_nRootType != ContentTypeId::DRAWOBJECT)) + { + if (mTrackContentType[ContentTypeId::DRAWOBJECT]) + { + // Multiple selection is possible when in root content navigation view so unselect all + // selected entries before reselecting. This causes a bit of an annoyance when the treeview + // scroll bar is used and focus is in the document by causing the last selected entry to + // scroll back into view. + if (m_bIsRoot) + m_xTreeView->unselect_all(); + SdrView* pSdrView = m_pActiveShell->GetDrawView(); + if (pSdrView) + { + for (size_t nIdx(0); nIdx < pSdrView->GetMarkedObjectCount(); nIdx++) + { + SdrObject* pSelected = pSdrView->GetMarkedObjectByIndex(nIdx); + OUString aName(pSelected->GetName()); + if (!aName.isEmpty()) + lcl_SelectDrawObjectByName(*m_xTreeView, aName); + } + } + else + { + // clear treeview selections + m_xTreeView->unselect_all(); + } + Select(); + } + return; + } + // footnotes and endnotes + if (SwContentAtPos aContentAtPos(IsAttrAtPos::Ftn); + m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos) + && aContentAtPos.pFndTextAttr && + !(m_bIsRoot && (m_nRootType != ContentTypeId::FOOTNOTE && + m_nRootType != ContentTypeId::ENDNOTE))) + { + if (!aContentAtPos.pFndTextAttr->GetFootnote().IsEndNote()) + { + if (mTrackContentType[ContentTypeId::FOOTNOTE]) + lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, ContentTypeId::FOOTNOTE, + aContentAtPos.pFndTextAttr); + } + else if (mTrackContentType[ContentTypeId::ENDNOTE]) + lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, ContentTypeId::ENDNOTE, + aContentAtPos.pFndTextAttr); + return; + } + // bookmarks - track first bookmark at cursor + if (mTrackContentType[ContentTypeId::BOOKMARK] && + (m_pActiveShell->GetSelectionType() & SelectionType::Text)) + { + SwPaM* pCursor = m_pActiveShell->GetCursor(); + IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess(); + IDocumentMarkAccess::const_iterator_t ppBookmark = pMarkAccess->getBookmarksBegin(); + if (pCursor && ppBookmark != pMarkAccess->getBookmarksEnd() && + !(m_bIsRoot && m_nRootType != ContentTypeId::BOOKMARK)) + { + SwPosition* pCursorPoint = pCursor->GetPoint(); + while (ppBookmark != pMarkAccess->getBookmarksEnd()) + { + if (lcl_IsUiVisibleBookmark(*ppBookmark) && + *pCursorPoint >= (*ppBookmark)->GetMarkStart() && + *pCursorPoint <= (*ppBookmark)->GetMarkEnd()) + { + lcl_SelectByContentTypeAndName(this, *m_xTreeView, + SwResId(STR_CONTENT_TYPE_BOOKMARK), + (*ppBookmark)->GetName()); + return; + } + ++ppBookmark; + } + } + } + // references + if (SwContentAtPos aContentAtPos(IsAttrAtPos::RefMark); + m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos) && + aContentAtPos.pFndTextAttr && + !(m_bIsRoot && m_nRootType != ContentTypeId::REFERENCE)) + { + if (mTrackContentType[ContentTypeId::REFERENCE]) + { + const SwFormatRefMark& rRefMark = aContentAtPos.pFndTextAttr->GetRefMark(); + lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_REFERENCE), + rRefMark.GetRefName()); + } + return; + } + // hyperlinks + if (SwContentAtPos aContentAtPos(IsAttrAtPos::InetAttr); + m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos) && + !(m_bIsRoot && m_nRootType != ContentTypeId::URLFIELD)) + { + // There is no need to search for hyperlinks in ToxContent tdf#148312 + if (const SwTextINetFormat* pTextINetFormat = + static_txtattr_cast<const SwTextINetFormat*>(aContentAtPos.pFndTextAttr)) + { + if (const SwTextNode* pTextNode = pTextINetFormat->GetpTextNode()) + { + if (const SwSectionNode* pSectNd = pTextNode->FindSectionNode()) + { + SectionType eType = pSectNd->GetSection().GetType(); + if (SectionType::ToxContent == eType) + { + m_xTreeView->set_cursor(-1); + Select(); + return; + } + } + } + } + // Because hyperlink item names do not need to be unique, finding the corresponding item + // in the tree by name may result in incorrect selection. Find the item in the tree by + // comparing the SwTextINetFormat pointer at the document cursor position to that stored + // in the item SwURLFieldContent. + if (mTrackContentType[ContentTypeId::URLFIELD]) + lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, ContentTypeId::URLFIELD, + aContentAtPos.pFndTextAttr); + return; + } + // fields, comments + if (SwField* pField = m_pActiveShell->GetCurField(); pField && + !(m_bIsRoot && + m_nRootType != ContentTypeId::TEXTFIELD && + m_nRootType != ContentTypeId::POSTIT)) + { + ContentTypeId eCntTypeId = + pField->GetTypeId() == SwFieldTypesEnum::Postit ? ContentTypeId::POSTIT : + ContentTypeId::TEXTFIELD; + if (mTrackContentType[eCntTypeId]) + lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, eCntTypeId, pField); + return; + } + // table + if (m_pActiveShell->IsCursorInTable() && + !(m_bIsRoot && m_nRootType != ContentTypeId::TABLE)) + { + if (mTrackContentType[ContentTypeId::TABLE] && m_pActiveShell->GetTableFormat()) + { + OUString aName = m_pActiveShell->GetTableFormat()->GetName(); + lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_TABLE), + aName); + } + return; + } + // indexes + if (const SwTOXBase* pTOX = m_pActiveShell->GetCurTOX(); pTOX && + !(m_bIsRoot && m_nRootType != ContentTypeId::INDEX)) + { + if (mTrackContentType[ContentTypeId::INDEX]) + lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_INDEX), + pTOX->GetTOXName()); + return; + } + // section + if (const SwSection* pSection = m_pActiveShell->GetCurrSection(); pSection && + !(m_bIsRoot && m_nRootType != ContentTypeId::REGION)) + { + if (mTrackContentType[ContentTypeId::REGION]) + { + lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_REGION), + pSection->GetSectionName()); + return; + } + else + { + // prevent fall through to outline tracking when section tracking is off and the last + // GotoContent is the current section + if (m_nLastSelType == ContentTypeId::REGION && + m_xTreeView->get_selected_text() == pSection->GetSectionName()) + return; + } + // fall through to outline tracking when section tracking is off and the last GotoContent + // is not the current section + } + } + // outline + if (m_nOutlineTracking == 3) + return; + // find out where the cursor is + const SwOutlineNodes::size_type nActPos = GetWrtShell()->GetOutlinePos(MAXLEVEL); + if (!((m_bIsRoot && m_nRootType != ContentTypeId::OUTLINE) || 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()); + if (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) && weld::fromId<SwContent*>( + m_xTreeView->get_id(rEntry))->GetParent()->GetType() == + ContentTypeId::OUTLINE) + { + if (weld::fromId<SwOutlineContent*>( + m_xTreeView->get_id(rEntry))->GetOutlinePos() == nActPos) + { + 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) + { + if (m_nOutlineTracking == 2) // focused outline tracking + { + // 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)) + { + do + { + if (weld::fromId<SwContent*>( + m_xTreeView->get_id(*xChildEntry))-> + GetParent()->GetType() == ContentTypeId::OUTLINE) + m_xTreeView->collapse_row(*xChildEntry); + else + break; + } + while (m_xTreeView->iter_next(*xChildEntry)); + } + } + // unselect all entries, make pEntry visible, and select + m_xTreeView->set_cursor(rEntry); + Select(); + + // tdf#149279 show at least two outline entries before the set cursor entry + std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator(&rEntry)); + for (int i = 0; i < 2; i++) + { + if (m_xTreeView->get_iter_depth(*xIter) == 0) + break; + m_xTreeView->iter_previous(*xIter); + while (!weld::IsEntryVisible(*m_xTreeView, *xIter)) + m_xTreeView->iter_parent(*xIter); + } + // Assure the scroll to row is collapsed after scrolling if it was collapsed + // before. This is required here to make gtkinst scroll_to_row behave like + // salinst. + const bool bRowExpanded = m_xTreeView->get_row_expanded(*xIter); + m_xTreeView->scroll_to_row(*xIter); + if (!bRowExpanded) + m_xTreeView->collapse_row(*xIter); + } + bRet = true; + } + } + else + { + // use of this break assumes outline content type is first in tree + if (lcl_IsContentType(rEntry, *m_xTreeView) && + weld::fromId<SwContentType*>( + m_xTreeView->get_id(rEntry))->GetType() != + ContentTypeId::OUTLINE) + bRet = true; + } + return bRet; + }); + } + else + { + // clear treeview selections + if (m_xTreeView->count_selected_rows() > 0) + { + m_xTreeView->unselect_all(); + m_xTreeView->set_cursor(-1); + Select(); + } + } +} + +void SwContentTree::SelectOutlinesWithSelection() +{ + SwCursor* pFirstCursor = m_pActiveShell->GetCursor(); + SwCursor* pCursor = pFirstCursor; + std::vector<SwOutlineNodes::size_type> aOutlinePositions; + do + { + if (pCursor) + { + if (pCursor->HasMark()) + { + aOutlinePositions.push_back(m_pActiveShell->GetOutlinePos(UCHAR_MAX, pCursor)); + } + pCursor = pCursor->GetNext(); + } + } while (pCursor && pCursor != pFirstCursor); + + if (aOutlinePositions.empty()) + return; + + // remove duplicates before selecting + aOutlinePositions.erase(std::unique(aOutlinePositions.begin(), aOutlinePositions.end()), + aOutlinePositions.end()); + + m_xTreeView->unselect_all(); + + for (auto nOutlinePosition : aOutlinePositions) + { + m_xTreeView->all_foreach([this, nOutlinePosition](const weld::TreeIter& rEntry){ + if (lcl_IsContent(rEntry, *m_xTreeView) && + weld::fromId<SwContent*>( + m_xTreeView->get_id(rEntry))->GetParent()->GetType() == + ContentTypeId::OUTLINE) + { + if (weld::fromId<SwOutlineContent*>( + m_xTreeView->get_id(rEntry))->GetOutlinePos() == + nOutlinePosition) + { + std::unique_ptr<weld::TreeIter> xParent = + m_xTreeView->make_iterator(&rEntry); + if (m_xTreeView->iter_parent(*xParent) && + !m_xTreeView->get_row_expanded(*xParent)) + m_xTreeView->expand_row(*xParent); + m_xTreeView->select(rEntry); + return true; + } + } + return false; + }); + } + + Select(); +} + +void SwContentTree::MoveOutline(SwOutlineNodes::size_type nTargetPos) +{ + MakeAllOutlineContentTemporarilyVisible a(GetWrtShell()->GetDoc()); + + SwWrtShell *const pShell = GetWrtShell(); + pShell->StartAllAction(); + pShell->StartUndo(SwUndoId::OUTLINE_UD); + + SwOutlineNodes::size_type nPrevSourcePos = SwOutlineNodes::npos; + SwOutlineNodes::size_type nPrevTargetPosOrOffset = SwOutlineNodes::npos; + + bool bFirstMove = true; + + for (const auto& source : m_aDndOutlinesSelected) + { + SwOutlineNodes::size_type nSourcePos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*source))->GetOutlinePos(); + + // Done on the first selection move + if (bFirstMove) // only do once + { + if (nTargetPos == SwOutlineNodes::npos || nSourcePos > nTargetPos) + { + // Up moves + // The first up move sets the up move amount for the remaining selected outlines to be moved + if (nTargetPos != SwOutlineNodes::npos) + nPrevTargetPosOrOffset = nSourcePos - nTargetPos; + else + nPrevTargetPosOrOffset = nSourcePos + 1; + } + else if (nSourcePos < nTargetPos) + { + // Down moves + // The first down move sets the source and target positions for the remaining selected outlines to be moved + nPrevSourcePos = nSourcePos; + nPrevTargetPosOrOffset = nTargetPos; + } + bFirstMove = false; + } + else + { + if (nTargetPos == SwOutlineNodes::npos || nSourcePos > nTargetPos) + { + // Move up + nTargetPos = nSourcePos - nPrevTargetPosOrOffset; + } + else if (nSourcePos < nTargetPos) + { + // Move down + nSourcePos = nPrevSourcePos; + nTargetPos = nPrevTargetPosOrOffset; + } + } + GetParentWindow()->MoveOutline(nSourcePos, nTargetPos); + } + + pShell->EndUndo(); + pShell->EndAllAction(); + m_aActiveContentArr[ContentTypeId::OUTLINE]->Invalidate(); + Display(true); + m_aDndOutlinesSelected.clear(); +} + +// Update immediately +IMPL_LINK_NOARG(SwContentTree, FocusInHdl, weld::Widget&, void) +{ + SwView* pActView = GetParentWindow()->GetCreateView(); + if(pActView) + { + SwWrtShell* pActShell = pActView->GetWrtShellPtr(); + if (State::CONSTANT == m_eState && !lcl_FindShell(m_pActiveShell)) + { + SetActiveShell(pActShell); + } + + if (State::ACTIVE == m_eState && pActShell != GetWrtShell()) + SetActiveShell(pActShell); + // Only call HasContentChanged() if the document has changed since last called + else if ((State::ACTIVE == m_eState || (State::CONSTANT == m_eState && pActShell == GetWrtShell())) && + m_bDocHasChanged) + { + if (HasContentChanged()) + Display(true); + m_bDocHasChanged = false; + } + } + else if (State::ACTIVE == m_eState) + clear(); +} + +IMPL_LINK(SwContentTree, KeyInputHdl, const KeyEvent&, rEvent, bool) +{ + bool bConsumed = true; + + const vcl::KeyCode aCode = rEvent.GetKeyCode(); + if (aCode.GetCode() == KEY_MULTIPLY && aCode.IsMod1()) + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_selected(xEntry.get())) + ExpandOrCollapseAll(*m_xTreeView, *xEntry); + } + else if (aCode.GetCode() == KEY_RETURN) + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_selected(xEntry.get())) + { + switch(aCode.GetModifier()) + { + case KEY_MOD2: + // Switch boxes + GetParentWindow()->ToggleTree(); + break; + case KEY_MOD1: + // Switch RootMode + ToggleToRoot(); + break; + case 0: + if (lcl_IsContentType(*xEntry, *m_xTreeView)) + { + m_xTreeView->get_row_expanded(*xEntry) ? m_xTreeView->collapse_row(*xEntry) + : m_xTreeView->expand_row(*xEntry); + } + else + ContentDoubleClickHdl(*m_xTreeView); + break; + } + } + } + else if(aCode.GetCode() == KEY_DELETE && 0 == aCode.GetModifier()) + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_selected(xEntry.get()) && lcl_IsContent(*xEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)))); + if (weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->GetParent()->IsDeletable() && + !m_pActiveShell->GetView().GetDocShell()->IsReadOnly()) + { + EditEntry(*xEntry, EditEntryMode::DELETE); + } + } + } + //Make KEY_SPACE has same function as DoubleClick, and realize + //multi-selection. + else if (aCode.GetCode() == KEY_SPACE && 0 == aCode.GetModifier()) + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_cursor(xEntry.get())) + { + if (State::HIDDEN != m_eState) + { + if (State::CONSTANT == m_eState) + { + m_pActiveShell->GetView().GetViewFrame()->GetWindow().ToTop(); + } + + SwContent* pCnt = dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))); + + if (pCnt && pCnt->GetParent()->GetType() == ContentTypeId::DRAWOBJECT) + { + SdrView* pDrawView = m_pActiveShell->GetDrawView(); + if (pDrawView) + { + pDrawView->SdrEndTextEdit(); + + SwDrawModel* pDrawModel = m_pActiveShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel(); + SdrPage* pPage = pDrawModel->GetPage(0); + const size_t nCount = pPage->GetObjCount(); + bool hasObjectMarked = false; + + if (SdrObject* pObject = GetDrawingObjectsByContent(pCnt)) + { + SdrPageView* pPV = pDrawView->GetSdrPageView/*GetPageViewPvNum*/(/*0*/); + if( pPV ) + { + bool bUnMark = pDrawView->IsObjMarked(pObject); + pDrawView->MarkObj( pObject, pPV, bUnMark); + + } + } + for( size_t i=0; i<nCount; ++i ) + { + SdrObject* pTemp = pPage->GetObj(i); + bool bMark = pDrawView->IsObjMarked(pTemp); + switch( pTemp->GetObjIdentifier() ) + { + case SdrObjKind::Group: + case SdrObjKind::Text: + case SdrObjKind::Line: + case SdrObjKind::Rectangle: + case SdrObjKind::CircleOrEllipse: + case SdrObjKind::CircleSection: + case SdrObjKind::CircleArc: + case SdrObjKind::CircleCut: + case SdrObjKind::Polygon: + case SdrObjKind::PolyLine: + case SdrObjKind::PathLine: + case SdrObjKind::PathFill: + case SdrObjKind::FreehandLine: + case SdrObjKind::FreehandFill: + case SdrObjKind::PathPoly: + case SdrObjKind::PathPolyLine: + case SdrObjKind::Caption: + case SdrObjKind::CustomShape: + if( bMark ) + hasObjectMarked = true; + break; + default: + if ( bMark ) + { + SdrPageView* pPV = pDrawView->GetSdrPageView/*GetPageViewPvNum*/(/*0*/); + if (pPV) + { + pDrawView->MarkObj(pTemp, pPV, true); + } + } + } + //mod end + } + if ( !hasObjectMarked ) + { + SwEditWin& rEditWindow = m_pActiveShell->GetView().GetEditWin(); + vcl::KeyCode tempKeycode( KEY_ESCAPE ); + KeyEvent rKEvt( 0 , tempKeycode ); + static_cast<vcl::Window*>(&rEditWindow)->KeyInput( rKEvt ); + } + } + } + + m_bViewHasChanged = true; + } + } + } + else + { + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_cursor(xEntry.get())) + { + SwContent* pCnt = dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))); + if (pCnt && pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE) + { + if (m_bIsRoot && aCode.GetCode() == KEY_LEFT && aCode.GetModifier() == 0) + { + m_xTreeView->unselect_all(); + bConsumed = false; + } + else if (aCode.IsMod1()) + { + if (aCode.GetCode() == KEY_LEFT) + ExecCommand("promote", !aCode.IsShift()); + else if (aCode.GetCode() == KEY_RIGHT) + ExecCommand("demote", !aCode.IsShift()); + else if (aCode.GetCode() == KEY_UP) + ExecCommand("chapterup", !aCode.IsShift()); + else if (aCode.GetCode() == KEY_DOWN) + ExecCommand("chapterdown", !aCode.IsShift()); + else if (aCode.GetCode() == KEY_C) + CopyOutlineSelections(); + else + bConsumed = false; + } + else + bConsumed = false; + } + else + bConsumed = false; + } + else + bConsumed = false; + } + return bConsumed; +} + +IMPL_LINK(SwContentTree, QueryTooltipHdl, const weld::TreeIter&, rEntry, OUString) +{ + ContentTypeId nType; + bool bContent = false; + void* pUserData = weld::fromId<void*>(m_xTreeView->get_id(rEntry)); + if (lcl_IsContentType(rEntry, *m_xTreeView)) + { + assert(dynamic_cast<SwContentType*>(static_cast<SwTypeNumber*>(pUserData))); + nType = static_cast<SwContentType*>(pUserData)->GetType(); + } + else + { + assert(dynamic_cast<SwContent*>(static_cast<SwTypeNumber*>(pUserData))); + nType = static_cast<SwContent*>(pUserData)->GetParent()->GetType(); + bContent = true; + } + OUString sEntry; + if(bContent) + { + switch( nType ) + { + case ContentTypeId::URLFIELD: + assert(dynamic_cast<SwURLFieldContent*>(static_cast<SwTypeNumber*>(pUserData))); + sEntry = static_cast<SwURLFieldContent*>(pUserData)->GetURL(); + break; + + case ContentTypeId::POSTIT: + assert(dynamic_cast<SwPostItContent*>(static_cast<SwTypeNumber*>(pUserData))); + sEntry = static_cast<SwPostItContent*>(pUserData)->GetName(); + break; + case ContentTypeId::OUTLINE: + assert(dynamic_cast<SwOutlineContent*>(static_cast<SwTypeNumber*>(pUserData))); + sEntry = static_cast<SwOutlineContent*>(pUserData)->GetName(); + break; + case ContentTypeId::GRAPHIC: + assert(dynamic_cast<SwGraphicContent*>(static_cast<SwTypeNumber*>(pUserData))); + sEntry = static_cast<SwGraphicContent*>(pUserData)->GetLink(); + break; + case ContentTypeId::REGION: + { + assert(dynamic_cast<SwRegionContent*>(static_cast<SwTypeNumber*>(pUserData))); + sEntry = static_cast<SwRegionContent*>(pUserData)->GetName(); + const SwSectionFormats& rFormats = GetWrtShell()->GetDoc()->GetSections(); + for (SwSectionFormats::size_type n = rFormats.size(); n;) + { + const SwNodeIndex* pIdx = nullptr; + const SwSectionFormat* pFormat = rFormats[--n]; + const SwSection* pSect; + if (nullptr != (pSect = pFormat->GetSection()) && + pSect->GetSectionName() == sEntry && + nullptr != (pIdx = pFormat->GetContent().GetContentIdx()) && + pIdx->GetNode().GetNodes().IsDocNodes()) + { + SwDocStat aDocStat; + SwPaM aPaM(*pIdx, *pIdx->GetNode().EndOfSectionNode()); + SwDoc::CountWords(aPaM, aDocStat); + sEntry = SwResId(STR_REGION_DEFNAME) + ": " + sEntry + "\n" + + SwResId(FLD_STAT_WORD) + ": " + OUString::number(aDocStat.nWord) + "\n" + + SwResId(FLD_STAT_CHAR) + ": " + OUString::number(aDocStat.nChar); + break; + } + } + } + break; + case ContentTypeId::FOOTNOTE: + case ContentTypeId::ENDNOTE: + { + assert(dynamic_cast<SwTextFootnoteContent*>(static_cast<SwTypeNumber*>(pUserData))); + const SwTextFootnote* pFootnote = + static_cast<const SwTextFootnoteContent*>(pUserData)->GetTextFootnote(); + + sEntry = pFootnote->GetFootnote().IsEndNote() ? SwResId(STR_CONTENT_ENDNOTE) : + SwResId(STR_CONTENT_FOOTNOTE); + } + break; + default: break; + } + if(static_cast<SwContent*>(pUserData)->IsInvisible()) + { + if(!sEntry.isEmpty()) + sEntry += ", "; + sEntry += m_sInvisible; + } + } + else + { + size_t nMemberCount = static_cast<SwContentType*>(pUserData)->GetMemberCount(); + sEntry = OUString::number(nMemberCount) + " " + + (nMemberCount == 1 + ? static_cast<SwContentType*>(pUserData)->GetSingleName() + : static_cast<SwContentType*>(pUserData)->GetName()); + } + + return sEntry; +} + +void SwContentTree::ExecuteContextMenuAction(const OString& rSelectedPopupEntry) +{ + if (rSelectedPopupEntry == "copy") + { + CopyOutlineSelections(); + return; + } + if (rSelectedPopupEntry == "collapseallcategories") + { + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator(); + bool bEntry = m_xTreeView->get_iter_first(*xEntry); + while (bEntry) + { + m_xTreeView->collapse_row(*xEntry); + bEntry = m_xTreeView->iter_next_sibling(*xEntry); + } + return; + } + + { + std::map<OString, ContentTypeId> mPopupEntryToContentTypeId + { + {"tabletracking", ContentTypeId::TABLE}, + {"frametracking", ContentTypeId::FRAME}, + {"imagetracking", ContentTypeId::GRAPHIC}, + {"oleobjecttracking", ContentTypeId::OLE}, + {"bookmarktracking", ContentTypeId::BOOKMARK}, + {"sectiontracking", ContentTypeId::REGION}, + {"hyperlinktracking", ContentTypeId::URLFIELD}, + {"referencetracking", ContentTypeId::REFERENCE}, + {"indextracking", ContentTypeId::INDEX}, + {"commenttracking", ContentTypeId::POSTIT}, + {"drawingobjecttracking", ContentTypeId::DRAWOBJECT}, + {"fieldtracking", ContentTypeId::TEXTFIELD}, + {"footnotetracking", ContentTypeId::FOOTNOTE}, + {"endnotetracking", ContentTypeId::ENDNOTE} + }; + + if (mPopupEntryToContentTypeId.count(rSelectedPopupEntry)) + { + ContentTypeId eCntTypeId = mPopupEntryToContentTypeId[rSelectedPopupEntry]; + SetContentTypeTracking(eCntTypeId, !mTrackContentType[eCntTypeId]); + return; + } + } + + std::unique_ptr<weld::TreeIter> xFirst(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_selected(xFirst.get())) + return; // this shouldn't happen, but better to be safe than ... + + if (rSelectedPopupEntry == "sort") + { + SwContentType* pCntType; + const OUString& rId(m_xTreeView->get_id(*xFirst)); + if (lcl_IsContentType(*xFirst, *m_xTreeView)) + pCntType = weld::fromId<SwContentType*>(rId); + else + pCntType = const_cast<SwContentType*>(weld::fromId<SwContent*>(rId)->GetParent()); + pCntType->SetSortType(!pCntType->GetSortType()); + pCntType->FillMemberList(); + Display(true); + return; + } + + auto nSelectedPopupEntry = rSelectedPopupEntry.toUInt32(); + switch (nSelectedPopupEntry) + { + case TOGGLE_OUTLINE_CONTENT_VISIBILITY: + case HIDE_OUTLINE_CONTENT_VISIBILITY: + case SHOW_OUTLINE_CONTENT_VISIBILITY: + { + m_pActiveShell->EnterStdMode(); + m_bIgnoreDocChange = true; + SwOutlineContent* pCntFirst = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xFirst)); + + // toggle the outline node outline content visible attribute + if (nSelectedPopupEntry == TOGGLE_OUTLINE_CONTENT_VISIBILITY) + { + SwNode* pNode = m_pActiveShell->GetDoc()->GetNodes().GetOutLineNds()[pCntFirst->GetOutlinePos()]; + pNode->GetTextNode()->SetAttrOutlineContentVisible( + !m_pActiveShell->GetAttrOutlineContentVisible(pCntFirst->GetOutlinePos())); + } + else + { + // with subs + SwOutlineNodes::size_type nPos = pCntFirst->GetOutlinePos(); + if (lcl_IsContentType(*xFirst, *m_xTreeView)) // Headings root entry + nPos = SwOutlineNodes::npos; + SwOutlineNodes::size_type nOutlineNodesCount = m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount(); + int nLevel = -1; + if (nPos != SwOutlineNodes::npos) // not root + nLevel = m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos); + else + nPos = 0; + bool bShow(nSelectedPopupEntry == SHOW_OUTLINE_CONTENT_VISIBILITY); + do + { + if (m_pActiveShell->IsOutlineContentVisible(nPos) != bShow) + m_pActiveShell->GetDoc()->GetNodes().GetOutLineNds()[nPos]->GetTextNode()->SetAttrOutlineContentVisible(bShow); + } while (++nPos < nOutlineNodesCount + && (nLevel == -1 || m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos) > nLevel)); + } + m_pActiveShell->InvalidateOutlineContentVisibility(); + // show in the document what was toggled + if (lcl_IsContentType(*xFirst, *m_xTreeView)) // Headings root entry + m_pActiveShell->GotoPage(1, true); + else + m_pActiveShell->GotoOutline(pCntFirst->GetOutlinePos()); + grab_focus(); + m_bIgnoreDocChange = false; + m_pActiveShell->GetDoc()->GetDocShell()->Broadcast(SfxHint(SfxHintId::DocChanged)); + } + break; + case 11: + case 12: + case 13: + nSelectedPopupEntry -= 10; + if(m_nOutlineTracking != nSelectedPopupEntry) + SetOutlineTracking(static_cast<sal_uInt8>(nSelectedPopupEntry)); + break; + //Outlinelevel + case 101: + case 102: + case 103: + case 104: + case 105: + case 106: + case 107: + case 108: + case 109: + case 110: + nSelectedPopupEntry -= 100; + if(m_nOutlineLevel != nSelectedPopupEntry ) + SetOutlineLevel(static_cast<sal_Int8>(nSelectedPopupEntry)); + break; + case 201: + case 202: + case 203: + GetParentWindow()->SetRegionDropMode(static_cast<RegionMode>(nSelectedPopupEntry - 201)); + break; + case 401: + case 402: + EditEntry(*xFirst, nSelectedPopupEntry == 401 ? EditEntryMode::RMV_IDX : EditEntryMode::UPD_IDX); + break; + // Edit entry + case 403: + EditEntry(*xFirst, EditEntryMode::EDIT); + break; + case 404: + EditEntry(*xFirst, EditEntryMode::UNPROTECT_TABLE); + break; + case 405 : + { + const SwTOXBase* pBase = weld::fromId<SwTOXBaseContent*>(m_xTreeView->get_id(*xFirst)) + ->GetTOXBase(); + m_pActiveShell->SetTOXBaseReadonly(*pBase, !SwEditShell::IsTOXBaseReadonly(*pBase)); + } + break; + case 4: + break; + case 501: + EditEntry(*xFirst, EditEntryMode::DELETE); + break; + case 502 : + EditEntry(*xFirst, EditEntryMode::RENAME); + break; + case 600: + m_pActiveShell->GetView().GetPostItMgr()->Show(); + break; + case 601: + m_pActiveShell->GetView().GetPostItMgr()->Hide(); + break; + case 602: + { + m_pActiveShell->GetView().GetPostItMgr()->SetActiveSidebarWin(nullptr); + m_pActiveShell->GetView().GetPostItMgr()->Delete(); + break; + } + case 700: + { + m_pActiveShell->GetView().GetViewFrame()->GetDispatcher()->Execute(FN_OUTLINE_TO_CLIPBOARD); + break; + } + case 800: + ExpandOrCollapseAll(*m_xTreeView, *xFirst); + break; + case 801: + ExecCommand("chapterup", true); + break; + case 802: + ExecCommand("chapterdown", true); + break; + case 803: + ExecCommand("promote", true); + break; + case 804: + ExecCommand("demote", true); + break; + case 805: // select document content + { + m_pActiveShell->KillPams(); + m_pActiveShell->ClearMark(); + m_pActiveShell->EnterAddMode(); + SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xFirst)); + const ContentTypeId eTypeId = pCnt->GetParent()->GetType(); + if (eTypeId == ContentTypeId::OUTLINE) + { + SwOutlineNodes::size_type nActPos = weld::fromId<SwOutlineContent*>( + m_xTreeView->get_id(*xFirst))->GetOutlinePos(); + m_pActiveShell->GotoOutline(nActPos); + m_xTreeView->selected_foreach([this](weld::TreeIter& rEntry){ + SwOutlineNodes::size_type nPos = weld::fromId<SwOutlineContent*>( + m_xTreeView->get_id(rEntry))->GetOutlinePos(); + m_pActiveShell->SttSelect(); + // select children if not expanded and don't kill PaMs + m_pActiveShell->MakeOutlineSel(nPos, nPos, + !m_xTreeView->get_row_expanded(rEntry), false); + m_pActiveShell->EndSelect(); + return false; + }); + } + else if (eTypeId == ContentTypeId::TABLE) + { + m_pActiveShell->GotoTable(pCnt->GetName()); + m_pActiveShell->GetView().GetViewFrame()->GetDispatcher()->Execute(FN_TABLE_SELECT_ALL); + } + else if (eTypeId == ContentTypeId::REGION) + { + m_pActiveShell->EnterStdMode(); + m_pActiveShell->GotoRegion(pCnt->GetName()); + GotoCurrRegionAndSkip(m_pActiveShell->GetCurrentShellCursor(), fnRegionEnd, m_pActiveShell->IsReadOnlyAvailable()); + m_pActiveShell->SttSelect(); + GotoCurrRegionAndSkip(m_pActiveShell->GetCurrentShellCursor(), fnRegionStart, m_pActiveShell->IsReadOnlyAvailable()); + m_pActiveShell->EndSelect(); + m_pActiveShell->UpdateCursor(); + } + m_pActiveShell->LeaveAddMode(); + } + break; + case 806: + // Delete outline selections + EditEntry(*xFirst, EditEntryMode::DELETE); + break; + case 900: + { + SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xFirst)); + GotoContent(pCnt); + } + break; + //Display + default: + if(nSelectedPopupEntry > 300 && nSelectedPopupEntry < 400) + { + nSelectedPopupEntry -= 300; + SwView *pView = SwModule::GetFirstView(); + while (pView) + { + nSelectedPopupEntry --; + if(nSelectedPopupEntry == 0) + { + SetConstantShell(&pView->GetWrtShell()); + break; + } + pView = SwModule::GetNextView(pView); + } + if(nSelectedPopupEntry) + { + m_bViewHasChanged = nSelectedPopupEntry == 1; + m_eState = (nSelectedPopupEntry == 1) ? State::ACTIVE : State::HIDDEN; + Display(nSelectedPopupEntry == 1); + } + GetParentWindow()->UpdateListBox(); + } + } +} + +void SwContentTree::DeleteOutlineSelections() +{ + auto nChapters(0); + + m_pActiveShell->StartAction(); + + m_pActiveShell->EnterAddMode(); + m_xTreeView->selected_foreach([this, &nChapters](weld::TreeIter& rEntry){ + ++nChapters; + if (m_xTreeView->iter_has_child(rEntry) && + !m_xTreeView->get_row_expanded(rEntry)) // only count children if not expanded + { + nChapters += m_xTreeView->iter_n_children(rEntry); + } + SwOutlineNodes::size_type nActPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rEntry))->GetOutlinePos(); + m_pActiveShell->SttSelect(); + m_pActiveShell->MakeOutlineSel(nActPos, nActPos, !m_xTreeView->get_row_expanded(rEntry), false); // select children if not expanded + // The outline selection may already be to the start of the following outline paragraph + // as happens when a table is the last content of the to be deleted outline. In this case + // do not extend the to be deleted selection right or the first character of the following + // outline paragraph will be removed. Also check if no selection was made which indicates + // an empty paragraph and selection right is needed. + if (!m_pActiveShell->IsSttPara() || !m_pActiveShell->HasSelection()) + m_pActiveShell->Right(CRSR_SKIP_CHARS, true, 1, false); + m_pActiveShell->EndSelect(); + return false; + }); + m_pActiveShell->LeaveAddMode(); + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, SwResId(STR_CHAPTERS, nChapters)); + m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter); + m_pActiveShell->Delete(false); + m_pActiveShell->EndUndo(); + + m_pActiveShell->EndAction(); +} + +void SwContentTree::SetOutlineLevel(sal_uInt8 nSet) +{ + if (nSet == m_nOutlineLevel) + return; + m_nOutlineLevel = nSet; + m_pConfig->SetOutlineLevel( m_nOutlineLevel ); + std::unique_ptr<SwContentType>& rpContentT = (State::ACTIVE == m_eState) + ? m_aActiveContentArr[ContentTypeId::OUTLINE] + : m_aHiddenContentArr[ContentTypeId::OUTLINE]; + if(rpContentT) + { + rpContentT->SetOutlineLevel(m_nOutlineLevel); + rpContentT->FillMemberList(); + } + Display(State::ACTIVE == m_eState); +} + +void SwContentTree::SetOutlineTracking(sal_uInt8 nSet) +{ + m_nOutlineTracking = nSet; + m_pConfig->SetOutlineTracking(m_nOutlineTracking); +} + +void SwContentTree::SetContentTypeTracking(ContentTypeId eCntTypeId, bool bSet) +{ + mTrackContentType[eCntTypeId] = bSet; + m_pConfig->SetContentTypeTrack(eCntTypeId, bSet); +} + +// Mode Change: Show dropped Doc +void SwContentTree::ShowHiddenShell() +{ + if(m_pHiddenShell) + { + m_eState = State::HIDDEN; + Display(false); + } +} + +// Mode Change: Show active view +void SwContentTree::ShowActualView() +{ + m_eState = State::ACTIVE; + Display(true); + GetParentWindow()->UpdateListBox(); +} + +IMPL_LINK_NOARG(SwContentTree, SelectHdl, weld::TreeView&, void) +{ + if (m_pConfig->IsNavigateOnSelect()) + { + ContentDoubleClickHdl(*m_xTreeView); + grab_focus(); + } + Select(); + if (m_bIsRoot) + return; + // Select the content type in the Navigate By control + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_selected(xEntry.get())) + return; + while (m_xTreeView->get_iter_depth(*xEntry)) + m_xTreeView->iter_parent(*xEntry); + m_pDialog->SelectNavigateByContentType(m_xTreeView->get_text(*xEntry)); +} + +// Here the buttons for moving outlines are en-/disabled. +void SwContentTree::Select() +{ + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_selected(xEntry.get())) + return; + + bool bEnable = false; + std::unique_ptr<weld::TreeIter> xParentEntry(m_xTreeView->make_iterator(xEntry.get())); + bool bParentEntry = m_xTreeView->iter_parent(*xParentEntry); + while (bParentEntry && (!lcl_IsContentType(*xParentEntry, *m_xTreeView))) + bParentEntry = m_xTreeView->iter_parent(*xParentEntry); + if (!m_bIsLastReadOnly) + { + if (!m_xTreeView->get_visible()) + bEnable = true; + else if (bParentEntry) + { + if ((m_bIsRoot && m_nRootType == ContentTypeId::OUTLINE) || + (lcl_IsContent(*xEntry, *m_xTreeView) && + weld::fromId<SwContentType*>(m_xTreeView->get_id(*xParentEntry))->GetType() == ContentTypeId::OUTLINE)) + { + bEnable = true; + } + } + } + + SwNavigationPI* pNavi = GetParentWindow(); + pNavi->m_xContent6ToolBox->set_item_sensitive("chapterup", bEnable); + pNavi->m_xContent6ToolBox->set_item_sensitive("chapterdown", bEnable); + pNavi->m_xContent6ToolBox->set_item_sensitive("promote", bEnable); + pNavi->m_xContent6ToolBox->set_item_sensitive("demote", bEnable); +} + +void SwContentTree::SetRootType(ContentTypeId nType) +{ + m_nRootType = nType; + m_bIsRoot = true; + m_pConfig->SetRootType( m_nRootType ); +} + +OUString SwContentType::RemoveNewline(const OUString& rEntry) +{ + if (rEntry.isEmpty()) + return rEntry; + + OUStringBuffer aEntry(rEntry); + for (sal_Int32 i = 0; i < rEntry.getLength(); ++i) + if(aEntry[i] == 10 || aEntry[i] == 13) + aEntry[i] = 0x20; + + return aEntry.makeStringAndClear(); +} + +void SwContentTree::EditEntry(const weld::TreeIter& rEntry, EditEntryMode nMode) +{ + SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(rEntry)); + GotoContent(pCnt); + const ContentTypeId nType = pCnt->GetParent()->GetType(); + sal_uInt16 nSlot = 0; + + if(EditEntryMode::DELETE == nMode) + m_bIgnoreDocChange = true; + + uno::Reference< container::XNameAccess > xNameAccess, xSecond, xThird; + switch(nType) + { + case ContentTypeId::OUTLINE : + if(nMode == EditEntryMode::DELETE) + { + DeleteOutlineSelections(); + } + break; + + case ContentTypeId::TABLE : + if(nMode == EditEntryMode::UNPROTECT_TABLE) + { + m_pActiveShell->GetView().GetDocShell()-> + GetDoc()->UnProtectCells( pCnt->GetName()); + } + else if(nMode == EditEntryMode::DELETE) + { + m_pActiveShell->StartAction(); + OUString sTable = SwResId(STR_TABLE_NAME); + SwRewriter aRewriterTableName; + aRewriterTableName.AddRule(UndoArg1, SwResId(STR_START_QUOTE)); + aRewriterTableName.AddRule(UndoArg2, pCnt->GetName()); + aRewriterTableName.AddRule(UndoArg3, SwResId(STR_END_QUOTE)); + sTable = aRewriterTableName.Apply(sTable); + + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, sTable); + m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter); + m_pActiveShell->GetView().GetViewFrame()->GetDispatcher()->Execute(FN_TABLE_SELECT_ALL); + m_pActiveShell->DeleteRow(); + m_pActiveShell->EndUndo(); + m_pActiveShell->EndAction(); + } + else if(nMode == EditEntryMode::RENAME) + { + uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + uno::Reference< text::XTextTablesSupplier > xTables(xModel, uno::UNO_QUERY); + xNameAccess = xTables->getTextTables(); + } + else + nSlot = FN_FORMAT_TABLE_DLG; + break; + + case ContentTypeId::GRAPHIC : + if(nMode == EditEntryMode::DELETE) + { + m_pActiveShell->DelRight(); + } + else if(nMode == EditEntryMode::RENAME) + { + uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + uno::Reference< text::XTextGraphicObjectsSupplier > xGraphics(xModel, uno::UNO_QUERY); + xNameAccess = xGraphics->getGraphicObjects(); + uno::Reference< text::XTextFramesSupplier > xFrames(xModel, uno::UNO_QUERY); + xSecond = xFrames->getTextFrames(); + uno::Reference< text::XTextEmbeddedObjectsSupplier > xObjs(xModel, uno::UNO_QUERY); + xThird = xObjs->getEmbeddedObjects(); + } + else + nSlot = FN_FORMAT_GRAFIC_DLG; + break; + + case ContentTypeId::FRAME : + case ContentTypeId::OLE : + if(nMode == EditEntryMode::DELETE) + { + m_pActiveShell->DelRight(); + } + else if(nMode == EditEntryMode::RENAME) + { + uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + uno::Reference< text::XTextFramesSupplier > xFrames(xModel, uno::UNO_QUERY); + uno::Reference< text::XTextEmbeddedObjectsSupplier > xObjs(xModel, uno::UNO_QUERY); + if(ContentTypeId::FRAME == nType) + { + xNameAccess = xFrames->getTextFrames(); + xSecond = xObjs->getEmbeddedObjects(); + } + else + { + xNameAccess = xObjs->getEmbeddedObjects(); + xSecond = xFrames->getTextFrames(); + } + uno::Reference< text::XTextGraphicObjectsSupplier > xGraphics(xModel, uno::UNO_QUERY); + xThird = xGraphics->getGraphicObjects(); + } + else + nSlot = FN_FORMAT_FRAME_DLG; + break; + case ContentTypeId::BOOKMARK : + assert(!m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS)); + if(nMode == EditEntryMode::DELETE) + { + IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess(); + pMarkAccess->deleteMark(pMarkAccess->findMark(pCnt->GetName()), false); + } + else if(nMode == EditEntryMode::RENAME) + { + uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + uno::Reference< text::XBookmarksSupplier > xBkms(xModel, uno::UNO_QUERY); + xNameAccess = xBkms->getBookmarks(); + } + else + nSlot = FN_INSERT_BOOKMARK; + break; + + case ContentTypeId::REGION : + if(nMode == EditEntryMode::RENAME) + { + uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + uno::Reference< text::XTextSectionsSupplier > xSects(xModel, uno::UNO_QUERY); + xNameAccess = xSects->getTextSections(); + } + else + nSlot = FN_EDIT_REGION; + break; + + case ContentTypeId::URLFIELD: + if (nMode == EditEntryMode::DELETE) + nSlot = SID_REMOVE_HYPERLINK; + else + nSlot = SID_EDIT_HYPERLINK; + break; + case ContentTypeId::REFERENCE: + break; + case ContentTypeId::TEXTFIELD: + { + const SwTextFieldContent* pTextFieldCnt = static_cast<const SwTextFieldContent*>(pCnt); + if (nMode == EditEntryMode::DELETE) + { + const SwTextField* pTextField = pTextFieldCnt->GetFormatField()->GetTextField(); + SwTextField::DeleteTextField(*pTextField); + } + else + { + if (pTextFieldCnt->GetFormatField()->GetField()->GetTypeId() != SwFieldTypesEnum::Postit) + { + nSlot = FN_EDIT_FIELD; + break; + } + } + [[fallthrough]]; // execute FN_POSTIT assuring standard mode first + } + case ContentTypeId::POSTIT: + { + auto& rView = m_pActiveShell->GetView(); + auto pPostItMgr = rView.GetPostItMgr(); + pPostItMgr->AssureStdModeAtShell(); + pPostItMgr->SetActiveSidebarWin(nullptr); + rView.GetEditWin().GrabFocus(); + if(nMode == EditEntryMode::DELETE) + m_pActiveShell->DelRight(); + else + nSlot = FN_POSTIT; + } + break; + case ContentTypeId::INDEX: + { + const SwTOXBase* pBase = static_cast<SwTOXBaseContent*>(pCnt)->GetTOXBase(); + switch(nMode) + { + case EditEntryMode::EDIT: + if(pBase) + { + SwPtrItem aPtrItem( FN_INSERT_MULTI_TOX, const_cast<SwTOXBase *>(pBase)); + m_pActiveShell->GetView().GetViewFrame()-> + GetDispatcher()->ExecuteList(FN_INSERT_MULTI_TOX, + SfxCallMode::ASYNCHRON, { &aPtrItem }); + + } + break; + case EditEntryMode::RMV_IDX: + case EditEntryMode::DELETE: + { + if( pBase ) + m_pActiveShell->DeleteTOX(*pBase, EditEntryMode::DELETE == nMode); + } + break; + case EditEntryMode::UPD_IDX: + case EditEntryMode::RENAME: + { + Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + Reference< XDocumentIndexesSupplier > xIndexes(xModel, UNO_QUERY); + Reference< XIndexAccess> xIdxAcc(xIndexes->getDocumentIndexes()); + Reference< XNameAccess >xLocalNameAccess(xIdxAcc, UNO_QUERY); + if(EditEntryMode::RENAME == nMode) + xNameAccess = xLocalNameAccess; + else if(xLocalNameAccess.is() && xLocalNameAccess->hasByName(pBase->GetTOXName())) + { + Any aIdx = xLocalNameAccess->getByName(pBase->GetTOXName()); + Reference< XDocumentIndex> xIdx; + if(aIdx >>= xIdx) + xIdx->update(); + } + } + break; + default: break; + } + } + break; + case ContentTypeId::DRAWOBJECT : + if(EditEntryMode::DELETE == nMode) + nSlot = SID_DELETE; + else if(nMode == EditEntryMode::RENAME) + nSlot = FN_NAME_SHAPE; + break; + case ContentTypeId::FOOTNOTE: + case ContentTypeId::ENDNOTE: + if (EditEntryMode::EDIT == nMode) + nSlot = FN_FORMAT_FOOTNOTE_DLG; + break; + default: break; + } + if(nSlot) + m_pActiveShell->GetView().GetViewFrame()-> + GetDispatcher()->Execute(nSlot, SfxCallMode::SYNCHRON); + else if(xNameAccess.is()) + { + uno::Any aObj = xNameAccess->getByName(pCnt->GetName()); + uno::Reference< uno::XInterface > xTmp; + aObj >>= xTmp; + uno::Reference< container::XNamed > xNamed(xTmp, uno::UNO_QUERY); + SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSwRenameXNamedDlg> pDlg(pFact->CreateSwRenameXNamedDlg(m_xTreeView.get(), xNamed, xNameAccess)); + if(xSecond.is()) + pDlg->SetAlternativeAccess( xSecond, xThird); + + OUString sForbiddenChars; + if(ContentTypeId::BOOKMARK == nType) + { + sForbiddenChars = "/\\@:*?\";,.#"; + } + else if(ContentTypeId::TABLE == nType) + { + sForbiddenChars = " .<>"; + } + pDlg->SetForbiddenChars(sForbiddenChars); + pDlg->Execute(); + } + if(EditEntryMode::DELETE == nMode) + { + auto nPos = m_xTreeView->vadjustment_get_value(); + m_bViewHasChanged = true; + TimerUpdate(&m_aUpdTimer); + grab_focus(); + m_xTreeView->vadjustment_set_value(nPos); + } +} + +static void lcl_AssureStdModeAtShell(SwWrtShell* pWrtShell) +{ + // deselect any drawing or frame and leave editing mode + SdrView* pSdrView = pWrtShell->GetDrawView(); + if (pSdrView && pSdrView->IsTextEdit() ) + { + bool bLockView = pWrtShell->IsViewLocked(); + pWrtShell->LockView(true); + pWrtShell->EndTextEdit(); + pWrtShell->LockView(bLockView); + } + + if (pWrtShell->IsSelFrameMode() || pWrtShell->IsObjSelected()) + { + pWrtShell->UnSelectFrame(); + pWrtShell->LeaveSelFrameMode(); + pWrtShell->GetView().LeaveDrawCreate(); + pWrtShell->EnterStdMode(); + pWrtShell->DrawSelChanged(); + pWrtShell->GetView().StopShellTimer(); + } + else + pWrtShell->EnterStdMode(); +} + +void SwContentTree::CopyOutlineSelections() +{ + m_pActiveShell->LockView(true); + { + MakeAllOutlineContentTemporarilyVisible a(m_pActiveShell->GetDoc()); + lcl_AssureStdModeAtShell(m_pActiveShell); + m_pActiveShell->EnterAddMode(); + size_t nCount = m_xTreeView->get_selected_rows().size(); + m_xTreeView->selected_foreach([this, &nCount](weld::TreeIter& rEntry){ + SwOutlineNodes::size_type nOutlinePos = reinterpret_cast<SwOutlineContent*>( + m_xTreeView->get_id(rEntry).toInt64())->GetOutlinePos(); + m_pActiveShell->SttSelect(); + m_pActiveShell->MakeOutlineSel(nOutlinePos, nOutlinePos, + !m_xTreeView->get_row_expanded(rEntry), false); + // don't move if this is the last selected outline or the cursor is at start of para + if (--nCount && !m_pActiveShell->IsSttPara()) + m_pActiveShell->Right(CRSR_SKIP_CHARS, true, 1, false); + m_pActiveShell->EndSelect(); + return false; + }); + m_pActiveShell->LeaveAddMode(); + m_pActiveShell->GetView().GetViewFrame()->GetBindings().Execute(SID_COPY); + } + m_pActiveShell->LockView(false); +} + +void SwContentTree::GotoContent(const SwContent* pCnt) +{ + m_nLastGotoContentWasOutlinePos = SwOutlineNodes::npos; + lcl_AssureStdModeAtShell(m_pActiveShell); + switch(m_nLastSelType = pCnt->GetParent()->GetType()) + { + case ContentTypeId::TEXTFIELD: + { + m_pActiveShell->GotoFormatField( + *static_cast<const SwTextFieldContent*>(pCnt)->GetFormatField()); + } + break; + case ContentTypeId::OUTLINE : + { + const SwOutlineNodes::size_type nPos = + static_cast<const SwOutlineContent*>(pCnt)->GetOutlinePos(); + m_pActiveShell->GotoOutline(nPos); + m_nLastGotoContentWasOutlinePos = nPos; + } + break; + case ContentTypeId::TABLE : + { + m_pActiveShell->GotoTable(pCnt->GetName()); + } + break; + case ContentTypeId::FRAME : + case ContentTypeId::GRAPHIC : + case ContentTypeId::OLE : + { + m_pActiveShell->GotoFly(pCnt->GetName()); + } + break; + case ContentTypeId::BOOKMARK: + { + m_pActiveShell->GotoMark(pCnt->GetName()); + } + break; + case ContentTypeId::REGION : + { + m_pActiveShell->GotoRegion(pCnt->GetName()); + } + break; + case ContentTypeId::URLFIELD: + { + if(m_pActiveShell->GotoINetAttr( + *static_cast<const SwURLFieldContent*>(pCnt)->GetINetAttr() )) + { + m_pActiveShell->Right( CRSR_SKIP_CHARS, true, 1, false); + m_pActiveShell->SwCursorShell::SelectTextAttr( RES_TXTATR_INETFMT, true ); + } + } + break; + case ContentTypeId::REFERENCE: + { + m_pActiveShell->GotoRefMark(pCnt->GetName()); + } + break; + case ContentTypeId::INDEX: + { + const OUString& sName(pCnt->GetName()); + if (!m_pActiveShell->GotoNextTOXBase(&sName)) + m_pActiveShell->GotoPrevTOXBase(&sName); + } + break; + case ContentTypeId::POSTIT: + m_pActiveShell->GotoFormatField(*static_cast<const SwPostItContent*>(pCnt)->GetPostIt()); + break; + case ContentTypeId::DRAWOBJECT: + { + m_pActiveShell->GotoDrawingObject(pCnt->GetName()); + } + break; + case ContentTypeId::FOOTNOTE: + case ContentTypeId::ENDNOTE: + { + const SwTextFootnote* pFootnote = + static_cast<const SwTextFootnoteContent*>(pCnt)->GetTextFootnote(); + if (!pFootnote) + return; + m_pActiveShell->GotoFootnoteAnchor(*pFootnote); + } + break; + default: break; + } + + 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(); + + // Assure cursor is in visible view area. + // (tdf#147041) Always show the navigated outline at the top of the visible view area. + if (pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE || + (!m_pActiveShell->IsCursorVisible() && !m_pActiveShell->IsFrameSelected() && + !m_pActiveShell->IsObjSelected())) + { + Point rPoint = m_pActiveShell->GetCursorDocPos(); + rPoint.setX(0); + rView.SetVisArea(rPoint); + } +} + +// Now even the matching text::Bookmark +NaviContentBookmark::NaviContentBookmark() + : + m_nDocSh(0), + m_nDefaultDrag( RegionMode::NONE ) +{ +} + +NaviContentBookmark::NaviContentBookmark( const OUString &rUrl, + const OUString& rDesc, + RegionMode nDragType, + const SwDocShell* pDocSh ) : + m_aUrl( rUrl ), + m_aDescription(rDesc), + m_nDocSh(reinterpret_cast<sal_IntPtr>(pDocSh)), + m_nDefaultDrag( nDragType ) +{ +} + +void NaviContentBookmark::Copy( TransferDataContainer& rData ) const +{ + rtl_TextEncoding eSysCSet = osl_getThreadTextEncoding(); + + OString sStrBuf(OUStringToOString(m_aUrl, eSysCSet) + OStringChar(NAVI_BOOKMARK_DELIM) + + OUStringToOString(m_aDescription, eSysCSet) + OStringChar(NAVI_BOOKMARK_DELIM) + + OString::number(static_cast<int>(m_nDefaultDrag)) + OStringChar(NAVI_BOOKMARK_DELIM) + + OString::number(m_nDocSh)); + rData.CopyByteString(SotClipboardFormatId::SONLK, sStrBuf); +} + +bool NaviContentBookmark::Paste( const TransferableDataHelper& rData ) +{ + OUString sStr; + bool bRet = rData.GetString( SotClipboardFormatId::SONLK, sStr ); + if( bRet ) + { + sal_Int32 nPos = 0; + m_aUrl = sStr.getToken(0, NAVI_BOOKMARK_DELIM, nPos ); + m_aDescription = sStr.getToken(0, NAVI_BOOKMARK_DELIM, nPos ); + m_nDefaultDrag= static_cast<RegionMode>( o3tl::toInt32(o3tl::getToken(sStr, 0, NAVI_BOOKMARK_DELIM, nPos )) ); + m_nDocSh = o3tl::toInt32(o3tl::getToken(sStr, 0, NAVI_BOOKMARK_DELIM, nPos )); + } + return bRet; +} + +SwNavigationPI* SwContentTree::GetParentWindow() +{ + return m_pDialog; +} + +void SwContentTree::SelectContentType(std::u16string_view rContentTypeName) +{ + std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_iter_first(*xIter)) + return; + do + { + if (m_xTreeView->get_text(*xIter) == rContentTypeName) + { + m_xTreeView->set_cursor(*xIter); + Select(); + break; + } + } while (m_xTreeView->iter_next_sibling(*xIter)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |