/* -*- 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 <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/help.hxx> #include <vcl/settings.hxx> #include <vcl/weldutils.hxx> #include <sot/formats.hxx> #include <uiitems.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 <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 <dcontact.hxx> #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 <navmgr.hxx> #include <AnnotationWin.hxx> #include <memory> #include <fmtcntnt.hxx> #include <docstat.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 { 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 reinterpret_cast<const SwTypeNumber*>(rTreeView.get_id(rEntry).toInt64())->GetTypeId() == CTYPE_CNT; } bool lcl_IsContentType(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView) { return reinterpret_cast<const SwTypeNumber*>(rTreeView.get_id(rEntry).toInt64())->GetTypeId() == CTYPE_CTT; } 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; } size_t lcl_InsertURLFieldContent( SwContentArr *pMember, SwWrtShell* pWrtShell, const SwContentType *pCntType) { SwGetINetAttrs aArr; pWrtShell->GetINetAttrs( aArr ); const SwGetINetAttrs::size_type nCount {aArr.size()}; for( SwGetINetAttrs::size_type n = 0; n < nCount; ++n ) { SwGetINetAttr* p = &aArr[ n ]; std::unique_ptr<SwURLFieldContent> pCnt(new SwURLFieldContent( pCntType, p->sText, INetURLObject::decode( p->rINetAttr.GetINetFormat().GetValue(), INetURLObject::DecodeMechanism::Unambiguous ), &p->rINetAttr, n )); pMember->insert( std::move(pCnt) ); } return nCount; } } // Content, contains names and reference at the content type. SwContent::SwContent(const SwContentType* pCnt, const OUString& rName, long nYPos) : SwTypeNumber(CTYPE_CNT), pParent(pCnt), sContentName(rName), nYPosition(nYPos), bInvisible(false) { } SwTypeNumber::~SwTypeNumber() { } bool SwContent::IsProtect() const { return false; } bool SwPostItContent::IsProtect() const { return pField->IsProtect(); } bool SwURLFieldContent::IsProtect() const { return pINetAttr->IsProtect(); } SwGraphicContent::~SwGraphicContent() { } SwTOXBaseContent::~SwTOXBaseContent() { } static const char* 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 }; static const char* 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 }; 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; } } // 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) { Init(); } void SwContentType::Init(bool* pbInvalidateWindow) { // if the MemberCount is changing ... size_t nOldMemberCount = m_nMemberCount; m_nMemberCount = 0; switch(m_nContentType) { case ContentTypeId::OUTLINE : { m_sTypeToken = "outline"; m_nMemberCount = m_pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount(); if (m_nMemberCount < MAXLEVEL) { const size_t nOutlineCount = m_nMemberCount; for(size_t j = 0; j < nOutlineCount; ++j) { if (m_pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineLevel(j) > m_nOutlineLevel || !m_pWrtShell->getIDocumentOutlineNodesAccess()->isOutlineInLayout(j, *m_pWrtShell->GetLayout())) { m_nMemberCount --; } } } } break; case ContentTypeId::TABLE : m_sTypeToken = "table"; m_nMemberCount = m_pWrtShell->GetTableFrameFormatCount(true); m_bEdit = true; break; case ContentTypeId::FRAME : case ContentTypeId::GRAPHIC : case ContentTypeId::OLE : { FlyCntType eType = FLYCNTTYPE_FRM; m_sTypeToken = "frame"; if(m_nContentType == ContentTypeId::OLE) { eType = FLYCNTTYPE_OLE; m_sTypeToken = "ole"; } else if(m_nContentType == ContentTypeId::GRAPHIC) { eType = FLYCNTTYPE_GRF; m_sTypeToken = "graphic"; } m_nMemberCount = m_pWrtShell->GetFlyCount(eType, /*bIgnoreTextBoxes=*/true); m_bEdit = true; } break; case ContentTypeId::BOOKMARK: { IDocumentMarkAccess* const pMarkAccess = m_pWrtShell->getIDocumentMarkAccess(); m_nMemberCount = count_if( pMarkAccess->getBookmarksBegin(), pMarkAccess->getBookmarksEnd(), &lcl_IsUiVisibleBookmark); m_sTypeToken.clear(); const bool bProtectedBM = m_pWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS); m_bEdit = !bProtectedBM; m_bDelete = !bProtectedBM; } break; case ContentTypeId::REGION : { std::unique_ptr<SwContentArr> pOldMember; if(!m_pMember) m_pMember.reset( new SwContentArr ); else if(!m_pMember->empty()) { pOldMember = std::move(m_pMember); m_pMember.reset( new SwContentArr ); } const Point aNullPt; m_nMemberCount = m_pWrtShell->GetSectionFormatCount(); for(size_t i = 0; i < m_nMemberCount; ++i) { const SwSectionFormat* pFormat; SectionType eTmpType; if( (pFormat = &m_pWrtShell->GetSectionFormat(i))->IsInNodesArr() && (eTmpType = pFormat->GetSection()->GetType()) != SectionType::ToxContent && SectionType::ToxHeader != eTmpType ) { const OUString& rSectionName = pFormat->GetSection()->GetSectionName(); sal_uInt8 nLevel = 0; SwSectionFormat* pParentFormat = pFormat->GetParent(); while(pParentFormat) { nLevel++; pParentFormat = pParentFormat->GetParent(); } std::unique_ptr<SwContent> pCnt(new SwRegionContent(this, rSectionName, nLevel, pFormat->FindLayoutRect( false, &aNullPt ).Top())); SwPtrMsgPoolItem aAskItem( RES_CONTENT_VISIBLE, nullptr ); if( !pFormat->GetInfo( aAskItem ) && !aAskItem.pObject ) // not visible pCnt->SetInvisible(); m_pMember->insert(std::move(pCnt)); } } m_nMemberCount = m_pMember->size(); m_sTypeToken = "region"; m_bEdit = true; m_bDelete = false; if(pOldMember) { if(nullptr != pbInvalidateWindow) { // 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) *pbInvalidateWindow = checkVisibilityChanged( *pOldMember, *m_pMember); } } } break; case ContentTypeId::INDEX: { m_nMemberCount = m_pWrtShell->GetTOXCount(); m_bEdit = true; m_bDelete = false; } break; case ContentTypeId::REFERENCE: { m_nMemberCount = m_pWrtShell->GetRefMarks(); m_bDelete = false; } break; case ContentTypeId::URLFIELD: { m_nMemberCount = 0; if(!m_pMember) m_pMember.reset( new SwContentArr ); else m_pMember->clear(); m_nMemberCount = lcl_InsertURLFieldContent(m_pMember.get(), m_pWrtShell, this); m_bEdit = true; nOldMemberCount = m_nMemberCount; m_bDelete = true; } break; case ContentTypeId::POSTIT: { m_nMemberCount = 0; if(!m_pMember) m_pMember.reset( new SwContentArr ); else m_pMember->clear(); SwPostItMgr* aMgr = m_pWrtShell->GetView().GetPostItMgr(); if (aMgr) { 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() && (*i)->mLayoutStatus!=SwPostItHelper::INVISIBLE ) { OUString sEntry = pFormatField->GetField()->GetPar2(); sEntry = RemoveNewline(sEntry); std::unique_ptr<SwPostItContent> pCnt(new SwPostItContent( this, sEntry, pFormatField, m_nMemberCount)); m_pMember->insert(std::move(pCnt)); m_nMemberCount++; } } } } m_sTypeToken.clear(); m_bEdit = true; nOldMemberCount = m_nMemberCount; } break; case ContentTypeId::DRAWOBJECT: { m_sTypeToken.clear(); m_bEdit = true; m_nMemberCount = 0; SwDrawModel* pModel = m_pWrtShell->getIDocumentDrawModelAccess().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()) m_nMemberCount++; } } } break; default: break; } // ... then, the data can also no longer be valid, // apart from those which have already been corrected, // then nOldMemberCount is nevertheless not so old. if( nOldMemberCount != m_nMemberCount ) m_bDataValid = false; } 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* pbLevelOrVisibilityChanged) { std::unique_ptr<SwContentArr> pOldMember; size_t nOldMemberCount = 0; SwPtrMsgPoolItem aAskItem( RES_CONTENT_VISIBLE, nullptr ); if(m_pMember && pbLevelOrVisibilityChanged) { pOldMember = std::move(m_pMember); nOldMemberCount = pOldMember->size(); m_pMember.reset( new SwContentArr ); *pbLevelOrVisibilityChanged = false; } else if(!m_pMember) m_pMember.reset( new SwContentArr ); else m_pMember->clear(); switch(m_nContentType) { case ContentTypeId::OUTLINE : { const size_t nOutlineCount = m_nMemberCount = m_pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount(); size_t nPos = 0; for (size_t i = 0; i < nOutlineCount; ++i) { const sal_uInt8 nLevel = m_pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineLevel(i); if(nLevel >= m_nOutlineLevel ) m_nMemberCount--; else { if (!m_pWrtShell->getIDocumentOutlineNodesAccess()->isOutlineInLayout(i, *m_pWrtShell->GetLayout())) { --m_nMemberCount; continue; // don't hide it, just skip it } OUString aEntry(comphelper::string::stripStart( m_pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText(i, m_pWrtShell->GetLayout()), ' ')); aEntry = SwNavigationPI::CleanEntry(aEntry); std::unique_ptr<SwOutlineContent> pCnt(new SwOutlineContent(this, aEntry, i, nLevel, m_pWrtShell->IsOutlineMovable( i ), nPos )); m_pMember->insert(std::move(pCnt)); // with the same number and existing "pOldMember" the // old one is compared with the new OutlinePos. if (nOldMemberCount > nPos && static_cast<SwOutlineContent*>((*pOldMember)[nPos].get())->GetOutlineLevel() != nLevel) *pbLevelOrVisibilityChanged = true; nPos++; } } } break; case ContentTypeId::TABLE : { const size_t nCount = m_pWrtShell->GetTableFrameFormatCount(true); OSL_ENSURE(m_nMemberCount == nCount, "MemberCount differs"); Point aNullPt; m_nMemberCount = nCount; for(size_t i = 0; i < m_nMemberCount; ++i) { const SwFrameFormat& rTableFormat = m_pWrtShell->GetTableFrameFormat(i, true); const OUString& sTableName( rTableFormat.GetName() ); SwContent* pCnt = new SwContent(this, sTableName, rTableFormat.FindLayoutRect(false, &aNullPt).Top() ); if( !rTableFormat.GetInfo( aAskItem ) && !aAskItem.pObject ) // not visible pCnt->SetInvisible(); m_pMember->insert(std::unique_ptr<SwContent>(pCnt)); if(nOldMemberCount > i && (*pOldMember)[i]->IsInvisible() != pCnt->IsInvisible()) *pbLevelOrVisibilityChanged = true; } } 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; m_nMemberCount = m_pWrtShell->GetFlyCount(eType, /*bIgnoreTextBoxes=*/true); std::vector<SwFrameFormat const*> formats(m_pWrtShell->GetFlyFrameFormats(eType, /*bIgnoreTextBoxes=*/true)); SAL_WARN_IF(m_nMemberCount != formats.size(), "sw.ui", "MemberCount differs"); m_nMemberCount = formats.size(); for (size_t i = 0; i < m_nMemberCount; ++i) { SwFrameFormat const*const pFrameFormat = formats[i]; const OUString sFrameName = pFrameFormat->GetName(); SwContent* pCnt; 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 ), pFrameFormat->FindLayoutRect(false, &aNullPt).Top()); } else { pCnt = new SwContent(this, sFrameName, pFrameFormat->FindLayoutRect(false, &aNullPt).Top() ); } if( !pFrameFormat->GetInfo( aAskItem ) && !aAskItem.pObject ) // not visible pCnt->SetInvisible(); m_pMember->insert(std::unique_ptr<SwContent>(pCnt)); if (nOldMemberCount > i && (*pOldMember)[i]->IsInvisible() != pCnt->IsInvisible()) *pbLevelOrVisibilityChanged = true; } } break; case ContentTypeId::BOOKMARK: { 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 std::unique_ptr<SwContent> pCnt(new SwContent(this, rBkmName, 0)); m_pMember->insert(std::move(pCnt)); } } } break; case ContentTypeId::REGION : { const Point aNullPt; m_nMemberCount = m_pWrtShell->GetSectionFormatCount(); for(size_t i = 0; i < m_nMemberCount; ++i) { const SwSectionFormat* pFormat; SectionType eTmpType; if( (pFormat = &m_pWrtShell->GetSectionFormat(i))->IsInNodesArr() && (eTmpType = pFormat->GetSection()->GetType()) != SectionType::ToxContent && SectionType::ToxHeader != eTmpType ) { OUString sSectionName = pFormat->GetSection()->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, pFormat->FindLayoutRect( false, &aNullPt ).Top())); if( !pFormat->GetInfo( aAskItem ) && !aAskItem.pObject ) // not visible pCnt->SetInvisible(); m_pMember->insert(std::move(pCnt)); } if(nullptr != pbLevelOrVisibilityChanged) { assert(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) *pbLevelOrVisibilityChanged = checkVisibilityChanged( *pOldMember, *m_pMember); } } m_nMemberCount = m_pMember->size(); } break; case ContentTypeId::REFERENCE: { std::vector<OUString> aRefMarks; m_nMemberCount = m_pWrtShell->GetRefMarks( &aRefMarks ); for (const auto& rRefMark : aRefMarks) { // References sorted alphabetically m_pMember->insert(std::make_unique<SwContent>(this, rRefMark, 0)); } } break; case ContentTypeId::URLFIELD: m_nMemberCount = lcl_InsertURLFieldContent(m_pMember.get(), m_pWrtShell, this); break; case ContentTypeId::INDEX: { const sal_uInt16 nCount = m_pWrtShell->GetTOXCount(); m_nMemberCount = nCount; 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, nTox, *pBase); if( !pBase->GetInfo( aAskItem ) && !aAskItem.pObject ) // not visible pCnt->SetInvisible(); m_pMember->insert( std::unique_ptr<SwContent>(pCnt) ); const size_t nPos = m_pMember->size() - 1; if(nOldMemberCount > nPos && (*pOldMember)[nPos]->IsInvisible() != pCnt->IsInvisible()) *pbLevelOrVisibilityChanged = true; } } break; case ContentTypeId::POSTIT: { m_nMemberCount = 0; m_pMember->clear(); SwPostItMgr* aMgr = m_pWrtShell->GetView().GetPostItMgr(); if (aMgr) { 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() && (*i)->mLayoutStatus!=SwPostItHelper::INVISIBLE ) { OUString sEntry = pFormatField->GetField()->GetPar2(); sEntry = RemoveNewline(sEntry); std::unique_ptr<SwPostItContent> pCnt(new SwPostItContent( this, sEntry, pFormatField, m_nMemberCount)); m_pMember->insert(std::move(pCnt)); m_nMemberCount++; } } } } } break; case ContentTypeId::DRAWOBJECT: { m_nMemberCount = 0; m_pMember->clear(); 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()) { SwContact* pContact = static_cast<SwContact*>(pTemp->GetUserCall()); long nYPos = 0; const Point aNullPt; if(pContact && pContact->GetFormat()) nYPos = pContact->GetFormat()->FindLayoutRect(false, &aNullPt).Top(); SwContent* pCnt = new SwContent( this, pTemp->GetName(), nYPos); if(!rIDDMA.IsVisibleLayerId(pTemp->GetLayer())) pCnt->SetInvisible(); m_pMember->insert(std::unique_ptr<SwContent>(pCnt)); m_nMemberCount++; if (nOldMemberCount > i && (*pOldMember)[i]->IsInvisible() != pCnt->IsInvisible() ) *pbLevelOrVisibilityChanged = true; } } } } break; default: break; } 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 }; } static const char* 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_aDropTargetHelper(*this) , m_xDialog(pDialog) , m_sSpace(OUString(" ")) , 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) { Size aSize(m_xDialog->LogicToPixel(Size(110, 112), MapMode(MapUnit::MapAppFont))); m_xTreeView->set_size_request(aSize.Width(), aSize.Height()); 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, FocusHdl)); 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>()) { 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); m_xDialog.clear(); } // 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); } 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())) xDropEntry.reset(); if (m_nRootType == ContentTypeId::OUTLINE) { if (xDropEntry && lcl_IsContent(*xDropEntry, *m_xTreeView)) { assert(dynamic_cast<SwContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xDropEntry).toInt64()))); SwOutlineContent* pOutlineContent = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xDropEntry).toInt64()); 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*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xDropEntry).toInt64()))); nTargetPos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xDropEntry).toInt64())->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*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xNext).toInt64()))); nTargetPos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xNext).toInt64())->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(weld::TreeView& rContentTree, 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; } IMPL_LINK(SwContentTree, CommandHdl, const CommandEvent&, rCEvt, bool) { if (rCEvt.GetCommand() != CommandEventId::ContextMenu) return false; 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"); 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; const SwView* pActiveView = ::GetActiveView(); SwView *pView = SwModule::GetFirstView(); while (pView) { OUString sInsert = pView->GetDocShell()->GetTitle(); if (pView == pActiveView) { sInsert += "(" + m_aContextStrings[IDX_STR_ACTIVE] + ")"; } 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) { 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(); if (!xEntry || !lcl_IsContent(*xEntry, *m_xTreeView)) xPop->remove(OString::number(900)); // go to bool bRemovePostItEntries = true; bool bRemoveIndexEntries = 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; // Edit only if the shown content is coming from the current view. if ((State::ACTIVE == m_eState || m_pActiveShell == pActiveView->GetWrtShellPtr()) && xEntry && lcl_IsContent(*xEntry, *m_xTreeView)) { assert(dynamic_cast<SwContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); const SwContentType* pContType = reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xEntry).toInt64())->GetParent(); const ContentTypeId nContentType = pContType->GetType(); const bool bReadonly = m_pActiveShell->GetView().GetDocShell()->IsReadOnly(); const bool bVisible = !reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xEntry).toInt64())->IsInvisible(); const bool bProtected = reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xEntry).toInt64())->IsProtect(); const bool bProtectBM = (ContentTypeId::BOOKMARK == nContentType) && m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS); const bool bEditable = pContType->IsEditable() && ((bVisible && !bProtected && !bProtectBM) || ContentTypeId::REGION == nContentType); const bool bDeletable = pContType->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 (!bReadonly && (bEditable || bDeletable)) { if(ContentTypeId::INDEX == nContentType) { bRemoveIndexEntries = false; const SwTOXBase* pBase = reinterpret_cast<SwTOXBaseContent*>(m_xTreeView->get_id(*xEntry).toInt64())->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 = reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xEntry).toInt64())->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::OUTLINE == nContentType) { bOutline = true; bRemoveToggleExpandEntry = lcl_InsertExpandCollapseAllItem(*m_xTreeView, *xEntry, *xPop); bRemoveSelectEntry = false; bRemoveChapterEntries = 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 (xEntry) { assert(dynamic_cast<SwContentType*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); SwContentType* pType = reinterpret_cast<SwContentType*>(m_xTreeView->get_id(*xEntry).toInt64()); if (ContentTypeId::OUTLINE == pType->GetType()) { bOutline = true; bRemoveToggleExpandEntry = lcl_InsertExpandCollapseAllItem(*m_xTreeView, *xEntry, *xPop); bRemoveSendOutlineEntry = false; } if ( (pType->GetType() == ContentTypeId::POSTIT) && (!m_pActiveShell->GetView().GetDocShell()->IsReadOnly()) && ( pType->GetMemberCount() > 0) ) bRemovePostItEntries = false; } if (bRemoveToggleExpandEntry) { xPop->remove("separator3"); xPop->remove(OString::number(800)); } if (bRemoveSelectEntry) xPop->remove(OString::number(805)); if (bRemoveChapterEntries) { xPop->remove("separator2"); 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 && bRemoveSelectEntry && bRemoveChapterEntries && bRemoveSendOutlineEntry && bRemovePostItEntries && bRemoveDeleteEntry && bRemoveRenameEntry && bRemoveIndexEntries && bRemoveUnprotectEntry && bRemoveEditEntry) { xPop->remove("separator1"); } if (!bOutline) { xSubPop1.reset(); xPop->remove(OString::number(1)); // outline level menu xSubPopOutlineTracking.reset(); xPop->remove(OString::number(4)); // outline tracking menu } 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, const OUString* pExpanderName, bool bChildrenOnDemand, weld::TreeIter* pRet) { m_xTreeView->insert(pParent, -1, &rStr, &rId, nullptr, nullptr, pExpanderName, 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*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(rParent).toInt64()))); SwContentType* pCntType = reinterpret_cast<SwContentType*>(m_xTreeView->get_id(rParent).toInt64()); const size_t nCount = pCntType->GetMemberCount(); // Add for outline plus/minus if (pCntType->GetType() == ContentTypeId::OUTLINE) { 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(OUString::number(reinterpret_cast<sal_Int64>(pCnt))); if (!bChild || (nLevel == 0)) { insert(&rParent, sEntry, sId, nullptr, 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)); bChild = true; } else { //back search parent. if(static_cast<const SwOutlineContent*>(pCntType->GetMember(i-1))->GetOutlineLevel() < nLevel) { insert(xChild.get(), sEntry, sId, nullptr, 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)); bChild = true; } else { bChild = m_xTreeView->iter_previous(*xChild); assert(!bChild || lcl_IsContentType(*xChild, *m_xTreeView) || dynamic_cast<SwOutlineContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xChild).toInt64()))); while (bChild && lcl_IsContent(*xChild, *m_xTreeView) && (reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xChild).toInt64())->GetOutlineLevel() >= nLevel) ) { bChild = m_xTreeView->iter_previous(*xChild); } if (bChild) { insert(xChild.get(), sEntry, sId, nullptr, 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)); } } } } } } 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(OUString::number(reinterpret_cast<sal_Int64>(pCnt))); insert(&rParent, sEntry, sId, nullptr, 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) && reinterpret_cast<SwContentType*>(m_xTreeView->get_id(rParent).toInt64())->GetType() == ContentTypeId::OUTLINE) || (m_nRootType == ContentTypeId::OUTLINE)) { if (lcl_IsContentType(rParent, *m_xTreeView)) { SwContentType* pCntType = reinterpret_cast<SwContentType*>(m_xTreeView->get_id(rParent).toInt64()); 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*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xChild).toInt64()))); auto const nPos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xChild).toInt64())->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*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(rParent).toInt64()))); auto const nPos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(rParent).toInt64())->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 = reinterpret_cast<SwContentType*>(m_xTreeView->get_id(rParent).toInt64()); 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*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(rParent).toInt64()))); auto const nPos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(rParent).toInt64())->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)) { if (State::CONSTANT == m_eState) { m_pActiveShell->GetView().GetViewFrame()->GetWindow().ToTop(); } //Jump to content type: assert(dynamic_cast<SwContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); SwContent* pCnt = reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xEntry).toInt64()); assert(pCnt && "no UserData"); GotoContent(pCnt); const ContentTypeId nActType = pCnt->GetParent()->GetType(); if (nActType == ContentTypeId::FRAME) m_pActiveShell->EnterStdMode(); // fdo#36308 don't expand outlines on double-click bConsumed = nActType == ContentTypeId::OUTLINE; } } 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::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 sEntryName; // Name of the entry size_t nEntryRelPos = 0; // relative position to their parent size_t nOldEntryCount = GetEntryCount(); sal_Int32 nOldScrollPos = 0; if (xOldSelEntry) { UpdateLastSelType(); nOldScrollPos = m_xTreeView->vadjustment_get_value(); sEntryName = m_xTreeView->get_text(*xOldSelEntry); 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_xContent3ToolBox->set_item_sensitive("chapterup", !bDisable); pNavi->m_xContent3ToolBox->set_item_sensitive("chapterdown", !bDisable); pNavi->m_xContent3ToolBox->set_item_sensitive("promote", !bDisable); pNavi->m_xContent3ToolBox->set_item_sensitive("demote", !bDisable); pNavi->m_xContent2ToolBox->set_item_sensitive("reminder", !bDisable); } if (pShell) { std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator(); std::unique_ptr<weld::TreeIter> xSelEntry; // all content navigation view if(m_nRootType == ContentTypeId::UNKNOWN) { m_xTreeView->freeze(); std::vector<std::unique_ptr<weld::TreeIter>> aNodesToExpand; 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 sEntry = rpContentT->GetName(); OUString aImage(GetImageIdForContentTypeId(nCntType)); bool bChOnDemand = 0 != rpContentT->GetMemberCount(); OUString sId(OUString::number(reinterpret_cast<sal_Int64>(rpContentT.get()))); insert(nullptr, sEntry, sId, &aImage, bChOnDemand, xEntry.get()); m_xTreeView->set_sensitive(*xEntry, bChOnDemand); if (nCntType == m_nLastSelType) xSelEntry = 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); (void)m_xTreeView->get_iter_first(*xEntry); for (ContentTypeId nCntType : o3tl::enumrange<ContentTypeId>()) { sal_Int32 nExpandOptions = (State::HIDDEN == m_eState) ? m_nHiddenBlock : m_nActiveBlock; if (nExpandOptions & (1 << static_cast<int>(nCntType))) { if (nEntryRelPos && nCntType == m_nLastSelType) { // reselect the entry std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(xEntry.get())); std::unique_ptr<weld::TreeIter> xTemp; sal_uLong nPos = 1; while (m_xTreeView->iter_next(*xChild)) { // The old text will be slightly favored if (sEntryName == m_xTreeView->get_text(*xChild) || nPos == nEntryRelPos) { m_xTreeView->copy_iterator(*xChild, *xSelEntry); break; } xTemp = m_xTreeView->make_iterator(xChild.get()); nPos++; } if (!xSelEntry || lcl_IsContentType(*xSelEntry, *m_xTreeView)) xSelEntry = std::move(xTemp); } } (void)m_xTreeView->iter_next_sibling(*xEntry); } if (!xSelEntry) { nOldScrollPos = 0; xSelEntry = m_xTreeView->make_iterator(); if (!m_xTreeView->get_iter_first(*xSelEntry)) xSelEntry.reset(); } if (xSelEntry) { m_xTreeView->set_cursor(*xSelEntry); Select(); } } // 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(OUString::number(reinterpret_cast<sal_Int64>(rpRootContentT.get()))); insert(nullptr, rpRootContentT->GetName(), sId, &aImage, bChOnDemand, 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(OUString::number(reinterpret_cast<sal_Int64>(pCnt))); insert(xEntry.get(), sEntry, sSubId, nullptr, 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 { RequestingChildren(*xEntry); m_xTreeView->set_children_on_demand(*xEntry, false); } m_xTreeView->set_sensitive(*xEntry, m_xTreeView->iter_has_child(*xEntry)); m_xTreeView->thaw(); m_xTreeView->expand_row(*xEntry); // reselect the entry if (nEntryRelPos) { std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(xEntry.get())); sal_uLong nPos = 1; while (m_xTreeView->iter_next(*xChild)) { // The old text will be slightly favored if (sEntryName == m_xTreeView->get_text(*xChild) || nPos == nEntryRelPos) { xSelEntry = std::move(xChild); break; } nPos++; } if (xSelEntry) { m_xTreeView->set_cursor(*xSelEntry); // unselect all entries, make pSelEntry visible, and select Select(); } } else { m_xTreeView->set_cursor(*xEntry); Select(); } } } if (!m_bIsInPromoteDemote && 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*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); SwContent* pCnt = reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xEntry).toInt64()); 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 : // 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()); const SwContentType* pCntType; if (bEntry) { if (lcl_IsContentType(*xEntry, *m_xTreeView)) { assert(dynamic_cast<SwContentType*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); pCntType = reinterpret_cast<SwContentType*>(m_xTreeView->get_id(*xEntry).toInt64()); } else { assert(dynamic_cast<SwContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); pCntType = reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xEntry).toInt64())->GetParent(); } m_nRootType = pCntType->GetType(); m_bIsRoot = true; Display(State::HIDDEN != m_eState); if (m_nRootType == ContentTypeId::OUTLINE) { m_xTreeView->set_selection_mode(SelectionMode::Multiple); } } } else { m_xTreeView->set_selection_mode(SelectionMode::Single); m_nRootType = ContentTypeId::UNKNOWN; m_bIsRoot = false; FindActiveTypeAndRemoveUserData(); Display(State::HIDDEN != m_eState); } m_pConfig->SetRootType( m_nRootType ); weld::Toolbar* pBox = GetParentWindow()->m_xContent2ToolBox.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. // bVisibilityChanged gets set to true if some element, like a section, // changed visibility and should have its name rerendered with a new // grayed-out state bool bVisibilityChanged = false; if (State::HIDDEN == m_eState) { for(ContentTypeId i : o3tl::enumrange<ContentTypeId>()) { if(m_aActiveContentArr[i]) m_aActiveContentArr[i]->Invalidate(); } } // root content navigation view else if(m_bIsRoot) { std::unique_ptr<weld::TreeIter> xRootEntry(m_xTreeView->make_iterator()); if (!m_xTreeView->get_iter_first(*xRootEntry)) bContentChanged = true; else { assert(dynamic_cast<SwContentType*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xRootEntry).toInt64()))); const ContentTypeId nType = reinterpret_cast<SwContentType*>(m_xTreeView->get_id(*xRootEntry).toInt64())->GetType(); SwContentType* pArrType = m_aActiveContentArr[nType].get(); if (!pArrType) bContentChanged = true; else { // start check if first selected outline level has changed bool bCheckChanged = m_nRootType == ContentTypeId::OUTLINE && !m_xTreeView->has_focus(); if (bCheckChanged) { std::unique_ptr<weld::TreeIter> xFirstSel(m_xTreeView->make_iterator()); bool bFirstSel = m_xTreeView->get_selected(xFirstSel.get()); if (bFirstSel && lcl_IsContent(*xFirstSel, *m_xTreeView)) { assert(dynamic_cast<SwOutlineContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xFirstSel).toInt64()))); const auto nSelLevel = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xFirstSel).toInt64())->GetOutlineLevel(); SwWrtShell* pSh = GetWrtShell(); const SwOutlineNodes::size_type nOutlinePos = pSh->GetOutlinePos(MAXLEVEL); if (nOutlinePos != SwOutlineNodes::npos && pSh->getIDocumentOutlineNodesAccess()->getOutlineLevel(nOutlinePos) != nSelLevel) bContentChanged = true; } } // end check if first selected outline level has changed pArrType->Init(&bVisibilityChanged); pArrType->FillMemberList(); OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pArrType))); m_xTreeView->set_id(*xRootEntry, sId); if (!bContentChanged) { const size_t nChildCount = GetChildCount(*xRootEntry); if (nChildCount != pArrType->GetMemberCount()) bContentChanged = true; else { std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(xRootEntry.get())); for (size_t j = 0; j < nChildCount; ++j) { m_xTreeView->iter_next(*xEntry); const SwContent* pCnt = pArrType->GetMember(j); OUString sSubId(OUString::number(reinterpret_cast<sal_Int64>(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; } } } } } } // all content navigation view else { std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); bool bEntry = m_xTreeView->get_iter_first(*xEntry); while (bEntry) { bool bNext = true; // at least a next must be assert(dynamic_cast<SwContentType*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); SwContentType* pCntType = reinterpret_cast<SwContentType*>(m_xTreeView->get_id(*xEntry).toInt64()); const size_t nCntCount = pCntType->GetMemberCount(); const ContentTypeId nType = pCntType->GetType(); SwContentType* pArrType = m_aActiveContentArr[nType].get(); if (!pArrType) bContentChanged = true; else { pArrType->Init(&bVisibilityChanged); OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pArrType))); m_xTreeView->set_id(*xEntry, sId); if (m_xTreeView->get_row_expanded(*xEntry)) { bool bLevelOrVisibilityChanged = false; // bLevelOrVisibilityChanged is set if outlines have changed their level // or if the visibility of objects (frames, sections, tables) has changed // i.e. in header/footer pArrType->FillMemberList(&bLevelOrVisibilityChanged); const size_t nChildCount = GetChildCount(*xEntry); if (bLevelOrVisibilityChanged) { if (nType == ContentTypeId::OUTLINE) bContentChanged = true; else bVisibilityChanged = true; } if(nChildCount != pArrType->GetMemberCount()) bContentChanged = true; else { for(size_t j = 0; j < nChildCount; ++j) { bEntry = m_xTreeView->iter_next(*xEntry); bNext = false; const SwContent* pCnt = pArrType->GetMember(j); OUString sSubId(OUString::number(reinterpret_cast<sal_Int64>(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; } } } // not expanded and has children else if (m_xTreeView->iter_has_child(*xEntry)) { // was the entry once opened, then must also the // invisible records be examined. // At least the user data must be updated. bool bLevelOrVisibilityChanged = false; // bLevelOrVisibilityChanged is set if outlines have changed their level // or if the visibility of objects (frames, sections, tables) has changed // i.e. in header/footer pArrType->FillMemberList(&bLevelOrVisibilityChanged); 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(OUString::number(reinterpret_cast<sal_Int64>(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; } } // The Root-Entry has to be found now while (bEntry && (bNext || m_xTreeView->get_iter_depth(*xEntry))) { bEntry = m_xTreeView->iter_next(*xEntry); bNext = false; } } } if (!bContentChanged && bVisibilityChanged) m_aUpdTimer.Start(); return bContentChanged || bVisibilityChanged; } 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); sal_Int64 nId = m_xTreeView->get_id(*xEntry).toInt64(); if (nId && lcl_IsContentType(*xEntry, *m_xTreeView)) { assert(dynamic_cast<SwContentType*>(reinterpret_cast<SwTypeNumber*>(nId))); m_nLastSelType = reinterpret_cast<SwContentType*>(nId)->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; } // 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::DocChanged: if (!m_bIsInPromoteDemote) { m_bViewHasChanged = 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(const OString& rCmd, bool bOutlineWithChildren) { 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_bIsInPromoteDemote = 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*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*pCurrentEntry).toInt64()))); if ((m_bIsRoot && m_nRootType == ContentTypeId::OUTLINE) || reinterpret_cast<SwContent*>(m_xTreeView->get_id(*pCurrentEntry).toInt64())->GetParent()->GetType() == ContentTypeId::OUTLINE) { nActPos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*pCurrentEntry).toInt64())->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*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*pCurrentEntry).toInt64()))); const auto nActLevel = reinterpret_cast<SwOutlineContent*>( m_xTreeView->get_id(*pCurrentEntry).toInt64())->GetOutlineLevel(); bool bEntry = m_xTreeView->iter_next(*xEntry); while (bEntry && lcl_IsContent(*xEntry, *m_xTreeView)) { assert(dynamic_cast<SwOutlineContent*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); if (nActLevel >= reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xEntry).toInt64())->GetOutlineLevel()) break; nActEndPos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xEntry).toInt64())->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 = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xEntry).toInt64())->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*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); // nDest++ may only executed if bEntry if (bEntry) { if (!lcl_IsContent(*xEntry, *m_xTreeView)) break; else if (nActLevel >= reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xEntry).toInt64())->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 < reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xTmp).toInt64())->GetOutlineLevel()) { while (bTmp && lcl_IsContent(*xTmp, *m_xTreeView) && !m_xTreeView->is_selected(*xTmp) && nActLevel < reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xTmp).toInt64())->GetOutlineLevel()) { bTmp = m_xTreeView->iter_parent(*xTmp); } if (!bTmp || !m_xTreeView->is_selected(*xTmp)) break; bTmp = m_xTreeView->iter_previous(*xTmp); nDest = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xTmp).toInt64())->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 = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xEntry).toInt64())->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*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); if (bEntry && lcl_IsContent(*xEntry, *m_xTreeView)) { nDest = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xEntry).toInt64())->GetOutlinePos(); } else { nDest = 0; // presumably? } if (bEntry) { if (!lcl_IsContent(*xEntry, *m_xTreeView)) break; else if (nActLevel >= reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xEntry).toInt64())->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 < reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xTmp).toInt64())->GetOutlineLevel() && m_xTreeView->is_selected(*xTmp)) { nDest = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xTmp).toInt64())->GetOutlinePos(); const auto nLevel = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xTmp).toInt64())->GetOutlineLevel(); // account for selected entries' descendent lineage bTmp = m_xTreeView->iter_next(*xTmp); while (bTmp && lcl_IsContent(*xTmp, *m_xTreeView) && nLevel < reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xTmp).toInt64())->GetOutlineLevel()) { nDest = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xTmp).toInt64())->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(); // clear all selections to prevent the Display function from trying to reselect selected entries m_xTreeView->unselect_all(); Display(true); // 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*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xListEntry).toInt64()))); if (reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*xListEntry).toInt64())->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_bIsInPromoteDemote = 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(); } /** No idle with focus or while dragging */ IMPL_LINK_NOARG(SwContentTree, TimerUpdate, Timer *, void) { // 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_bViewHasChanged) && !IsInDrag() && !pView->GetWrtShellPtr()->ActionPend()) { m_bViewHasChanged = false; m_bIsIdleClear = false; 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); } // track document outline position at cursor if (m_nOutlineTracking == 3) // no outline tracking return; const SwOutlineNodes::size_type nActPos = GetWrtShell()->GetOutlinePos(MAXLEVEL); // find out where the cursor is if (nActPos == SwOutlineNodes::npos) return; // only track if selection is already an outline std::unique_ptr<weld::TreeIter> xFirstSelected(m_xTreeView->make_iterator()); if (!m_xTreeView->get_selected(xFirstSelected.get())) xFirstSelected.reset(); if (xFirstSelected && lcl_IsContent(*xFirstSelected, *m_xTreeView) && reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xFirstSelected).toInt64())->GetParent()->GetType() != ContentTypeId::OUTLINE) return; if (xFirstSelected && lcl_IsContentType(*xFirstSelected, *m_xTreeView) && reinterpret_cast<SwContentType*>(m_xTreeView->get_id(*xFirstSelected).toInt64())->GetType() != ContentTypeId::OUTLINE) return; int nSelectedRows = m_xTreeView->count_selected_rows(); // find the outline in the tree and select it m_xTreeView->all_foreach([this, nSelectedRows, nActPos, &xFirstSelected](weld::TreeIter& rEntry){ bool bRet = false; if (lcl_IsContent(rEntry, *m_xTreeView) && reinterpret_cast<SwContent*>(m_xTreeView->get_id(rEntry).toInt64())->GetParent()->GetType() == ContentTypeId::OUTLINE) { // might have been scrolled out of view by the user so leave it that way if (reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(rEntry).toInt64())->GetOutlinePos() == nActPos) { // only select if not already selected or tree has multiple entries selected if (nSelectedRows != 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 (reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xChildEntry).toInt64())->GetParent()->GetType() == ContentTypeId::OUTLINE) m_xTreeView->collapse_row(*xChildEntry); else break; } while (m_xTreeView->iter_next(*xChildEntry)); } } m_xTreeView->set_cursor(rEntry); // unselect all entries, make pEntry visible, and select Select(); } bRet = true; } } else { // use of this break assumes outline content type is first in tree if (lcl_IsContentType(rEntry, *m_xTreeView) && reinterpret_cast<SwContentType*>(m_xTreeView->get_id(rEntry).toInt64())->GetType() != ContentTypeId::OUTLINE) bRet = true; } return bRet; }); } else if (!pView && State::ACTIVE == m_eState && !m_bIsIdleClear) { if(m_pActiveShell) { SetActiveShell(nullptr); } clear(); m_bIsIdleClear = true; } } void SwContentTree::MoveOutline(SwOutlineNodes::size_type nTargetPos) { 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 = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(*source).toInt64())->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, FocusHdl, 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); else if ((State::ACTIVE == m_eState || (State::CONSTANT == m_eState && pActShell == GetWrtShell())) && HasContentChanged()) { Display(true); } } 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*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64()))); if (reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xEntry).toInt64())->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*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64())); 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 OBJ_GRUP: case OBJ_TEXT: case OBJ_LINE: case OBJ_RECT: case OBJ_CIRC: case OBJ_SECT: case OBJ_CARC: case OBJ_CCUT: case OBJ_POLY: case OBJ_PLIN: case OBJ_PATHLINE: case OBJ_PATHFILL: case OBJ_FREELINE: case OBJ_FREEFILL: case OBJ_PATHPOLY: case OBJ_PATHPLIN: case OBJ_CAPTION: case OBJ_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*>(reinterpret_cast<SwTypeNumber*>(m_xTreeView->get_id(*xEntry).toInt64())); 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 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 = reinterpret_cast<void*>(m_xTreeView->get_id(rEntry).toInt64()); 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; default: break; } if(static_cast<SwContent*>(pUserData)->IsInvisible()) { if(!sEntry.isEmpty()) sEntry += ", "; sEntry += m_sInvisible; } } else { const 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) { std::unique_ptr<weld::TreeIter> xFirst(m_xTreeView->make_iterator()); if (!m_xTreeView->get_selected(xFirst.get())) xFirst.reset(); auto nSelectedPopupEntry = rSelectedPopupEntry.toUInt32(); switch (nSelectedPopupEntry) { case 11: case 12: case 13: nSelectedPopupEntry -= 10; if(m_nOutlineTracking != nSelectedPopupEntry) m_nOutlineTracking = 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 = reinterpret_cast<SwTOXBaseContent*>(m_xTreeView->get_id(*xFirst).toInt64()) ->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: { m_pActiveShell->KillPams(); m_pActiveShell->ClearMark(); m_pActiveShell->EnterAddMode(); SwContent* pCnt = reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xFirst).toInt64()); const ContentTypeId eTypeId = pCnt->GetParent()->GetType(); if (eTypeId == ContentTypeId::OUTLINE) { m_xTreeView->selected_foreach([this](weld::TreeIter& rEntry){ m_pActiveShell->SttSelect(); SwOutlineNodes::size_type nActPos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(rEntry).toInt64())->GetOutlinePos(); m_pActiveShell->MakeOutlineSel(nActPos, nActPos, !m_xTreeView->get_row_expanded(rEntry), false); // select children if not expanded m_pActiveShell->EndSelect(); return false; }); } else if (eTypeId == ContentTypeId::TABLE) { m_pActiveShell->GotoTable(pCnt->GetName()); m_pActiveShell->SelAll(); } 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 = reinterpret_cast<SwContent*>(m_xTreeView->get_id(*xFirst).toInt64()); 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() { m_pActiveShell->StartAction(); m_pActiveShell->EnterAddMode(); auto nChapters(0); 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); } m_pActiveShell->SttSelect(); SwOutlineNodes::size_type nActPos = reinterpret_cast<SwOutlineContent*>(m_xTreeView->get_id(rEntry).toInt64())->GetOutlinePos(); m_pActiveShell->MakeOutlineSel(nActPos, nActPos, !m_xTreeView->get_row_expanded(rEntry), false); // select children if not expanded 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->SetTextFormatColl(nullptr); m_pActiveShell->Delete(); m_pActiveShell->ClearMark(); m_pActiveShell->EndUndo(); m_pActiveShell->EndAction(); } void SwContentTree::SetOutlineLevel(sal_uInt8 nSet) { 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->Init(); } Display(State::ACTIVE == m_eState); } // 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) { Select(); } // 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) && reinterpret_cast<SwContentType*>(m_xTreeView->get_id(*xParentEntry).toInt64())->GetType() == ContentTypeId::OUTLINE)) { bEnable = true; } } } SwNavigationPI* pNavi = GetParentWindow(); pNavi->m_xContent3ToolBox->set_item_sensitive("chapterup", bEnable); pNavi->m_xContent3ToolBox->set_item_sensitive("chapterdown", bEnable); pNavi->m_xContent3ToolBox->set_item_sensitive("promote", bEnable); pNavi->m_xContent3ToolBox->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 = reinterpret_cast<SwContent*>(m_xTreeView->get_id(rEntry).toInt64()); GotoContent(pCnt); const ContentTypeId nType = pCnt->GetParent()->GetType(); sal_uInt16 nSlot = 0; 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()) ); } 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: nSlot = FN_EDIT_FIELD; break; case ContentTypeId::POSTIT: m_pActiveShell->GetView().GetPostItMgr()->AssureStdModeAtShell(); if(nMode == EditEntryMode::DELETE) { m_pActiveShell->GetView().GetPostItMgr()->SetActiveSidebarWin(nullptr); 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; default: break; } if(nSlot) m_pActiveShell->GetView().GetViewFrame()-> GetDispatcher()->Execute(nSlot, SfxCallMode::ASYNCHRON); 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(GetParentWindow()->GetFrameWeld(), 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) { m_bViewHasChanged = true; GetParentWindow()->UpdateListBox(); TimerUpdate(&m_aUpdTimer); grab_focus(); } } void SwContentTree::GotoContent(const SwContent* pCnt) { m_pActiveShell->EnterStdMode(); bool bSel = false; switch(pCnt->GetParent()->GetType()) { case ContentTypeId::OUTLINE : { m_pActiveShell->GotoOutline(static_cast<const SwOutlineContent*>(pCnt)->GetOutlinePos()); } break; case ContentTypeId::TABLE : { m_pActiveShell->GotoTable(pCnt->GetName()); } break; case ContentTypeId::FRAME : case ContentTypeId::GRAPHIC : case ContentTypeId::OLE : { if(m_pActiveShell->GotoFly(pCnt->GetName())) bSel = true; } 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->GetView().GetPostItMgr()->AssureStdModeAtShell(); m_pActiveShell->GotoFormatField(*static_cast<const SwPostItContent*>(pCnt)->GetPostIt()); break; case ContentTypeId::DRAWOBJECT: { SwPosition aPos = *m_pActiveShell->GetCursor()->GetPoint(); SdrView* pDrawView = m_pActiveShell->GetDrawView(); if (pDrawView) { pDrawView->SdrEndTextEdit(); pDrawView->UnmarkAll(); SwDrawModel* _pModel = m_pActiveShell->getIDocumentDrawModelAccess().GetDrawModel(); SdrPage* pPage = _pModel->GetPage(0); const size_t nCount = pPage->GetObjCount(); for( size_t i=0; i<nCount; ++i ) { SdrObject* pTemp = pPage->GetObj(i); if (pTemp->GetName() == pCnt->GetName()) { SdrPageView* pPV = pDrawView->GetSdrPageView(); if( pPV ) { pDrawView->MarkObj( pTemp, pPV ); } } } m_pActiveShell->GetNavigationMgr().addEntry(aPos); m_pActiveShell->EnterStdMode(); bSel = true; } } break; default: break; } if(bSel) { m_pActiveShell->HideCursor(); m_pActiveShell->EnterSelFrameMode(); } SwView& rView = m_pActiveShell->GetView(); rView.StopShellTimer(); rView.GetPostItMgr()->SetActiveSidebarWin(nullptr); rView.GetEditWin().GrabFocus(); // force scroll to cursor position when navigating to inactive document if(!bSel) { Point rPoint = m_pActiveShell->GetCursorDocPos(); rPoint.setX(0); rView.SetVisArea(rPoint); } } // Now even the matching text::Bookmark NaviContentBookmark::NaviContentBookmark() : nDocSh(0), nDefDrag( RegionMode::NONE ) { } NaviContentBookmark::NaviContentBookmark( const OUString &rUrl, const OUString& rDesc, RegionMode nDragType, const SwDocShell* pDocSh ) : aUrl( rUrl ), aDescr(rDesc), nDocSh(reinterpret_cast<sal_IntPtr>(pDocSh)), nDefDrag( nDragType ) { } void NaviContentBookmark::Copy( TransferDataContainer& rData ) const { rtl_TextEncoding eSysCSet = osl_getThreadTextEncoding(); OString sStrBuf(OUStringToOString(aUrl, eSysCSet) + OStringChar(NAVI_BOOKMARK_DELIM) + OUStringToOString(aDescr, eSysCSet) + OStringChar(NAVI_BOOKMARK_DELIM) + OString::number(static_cast<int>(nDefDrag)) + OStringChar(NAVI_BOOKMARK_DELIM) + OString::number(nDocSh)); rData.CopyByteString(SotClipboardFormatId::SONLK, sStrBuf); } bool NaviContentBookmark::Paste( TransferableDataHelper& rData ) { OUString sStr; bool bRet = rData.GetString( SotClipboardFormatId::SONLK, sStr ); if( bRet ) { sal_Int32 nPos = 0; aUrl = sStr.getToken(0, NAVI_BOOKMARK_DELIM, nPos ); aDescr = sStr.getToken(0, NAVI_BOOKMARK_DELIM, nPos ); nDefDrag= static_cast<RegionMode>( sStr.getToken(0, NAVI_BOOKMARK_DELIM, nPos ).toInt32() ); nDocSh = sStr.getToken(0, NAVI_BOOKMARK_DELIM, nPos ).toInt32(); } return bRet; } SwNavigationPI* SwContentTree::GetParentWindow() { return m_xDialog; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */