void getAnchorPos(SwPosition& rPos)
{ // get the top most anchor position of the position if (SwFrameFormat* pFlyFormat = rPos.GetNode().GetFlyFormat())
{
SwNode* pAnchorNode;
SwFrameFormat* pTmp = pFlyFormat; while (pTmp && (pAnchorNode = pTmp->GetAnchor().GetAnchorNode()) &&
(pTmp = pAnchorNode->GetFlyFormat()))
{
pFlyFormat = pTmp;
} if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor())
rPos = *pPos;
}
}
returnfalse;
} // Gets "YPos" for content, i.e. a number used to sort content members in Navigator's list
sal_Int32 getYPos(const SwNode& rNode)
{
SwNodeOffset nIndex = rNode.GetIndex(); if (rNode.GetNodes().GetEndOfExtras().GetIndex() >= nIndex)
{ // Not a node of BodyText // Are we in a fly? if (constauto pFlyFormat = rNode.GetFlyFormat())
{ // Get node index of anchor if (SwNode* pAnchorNode = pFlyFormat->GetAnchor().GetAnchorNode())
{ return getYPos(*pAnchorNode);
}
}
} return sal_Int32(nIndex);
}
} // end of anonymous namespace
// tdf#157250 Show deleted headings tracked changes in the Navigator only when show // tracked changes is on if (m_pWrtShell->GetLayout()->IsHideRedlines()
&& pNode->GetRedlineMergeFlag() != SwNode::Merge::None && aEntry.isEmpty()) continue;
auto pCnt(std::make_unique<SwOutlineContent>(this, aEntry, i, nLevel,
m_pWrtShell->IsOutlineMovable(i), nYPos)); if (!pNode->getLayoutFrame(m_pWrtShell->GetLayout()))
pCnt->SetInvisible();
m_pMember->insert(std::move(pCnt));
}
// need to check level and equal entry number after creation due to possible outline // nodes in frames, headers, footers if (pOldMember)
{
assert(pbContentChanged && "pbContentChanged is always set if pOldMember is"); if (pOldMember->size() != m_pMember->size())
{
*pbContentChanged = true; break;
} for (size_t i = 0; i < pOldMember->size(); i++)
{ if (static_cast<SwOutlineContent*>((*pOldMember)[i].get())->GetOutlineLevel() != static_cast<SwOutlineContent*>((*m_pMember)[i].get())->GetOutlineLevel())
{
*pbContentChanged = true; break;
}
}
}
} break; case ContentTypeId::TABLE :
{ const size_t nCount = m_pWrtShell->GetTableFrameFormatCount(true); const sw::TableFrameFormats* pFrameFormats = m_pWrtShell->GetDoc()->GetTableFrameFormats(); for(size_t n = 0, i = 0; i < nCount + n; ++i)
{ const SwTableFormat& rTableFormat = *(*pFrameFormats)[i]; if(!rTableFormat.IsUsed()) // skip deleted tables
{
n++; continue;
}
tools::Long nYPos = 0; if (!m_bAlphabeticSort)
{ if (SwTable* pTable = SwTable::FindTable(&rTableFormat))
nYPos = getYPos(*pTable->GetTableNode());
} auto pCnt = std::make_unique<SwContent>(this, rTableFormat.GetName().toString(), nYPos); if(!rTableFormat.IsVisible())
pCnt->SetInvisible();
m_pMember->insert(std::move(pCnt));
}
if (pOldMember)
{ // need to check visibility (and equal entry number) after // creation due to a sorted list being used here (before, // entries with same index were compared already at creation // time what worked before a sorted list was used)
*pbContentChanged = checkVisibilityChanged(
*pOldMember,
*m_pMember);
}
} break; case ContentTypeId::OLE : case ContentTypeId::FRAME : case ContentTypeId::GRAPHIC :
{
FlyCntType eType = FLYCNTTYPE_FRM; if(m_nContentType == ContentTypeId::OLE)
eType = FLYCNTTYPE_OLE; elseif(m_nContentType == ContentTypeId::GRAPHIC)
eType = FLYCNTTYPE_GRF;
Point aNullPt;
size_t nCount = m_pWrtShell->GetFlyCount(eType, /*bIgnoreTextBoxes=*/true);
std::vector<SwFrameFormat const*> formats(m_pWrtShell->GetFlyFrameFormats(eType, /*bIgnoreTextBoxes=*/true));
SAL_WARN_IF(nCount != formats.size(), "sw.ui", "Count differs");
nCount = formats.size(); for (size_t i = 0; i < nCount; ++i)
{
SwFrameFormat const*const pFrameFormat = formats[i]; const UIName sFrameName = pFrameFormat->GetName();
auto pCnt(std::make_unique<SwRegionContent>(this, sSectionName.toString(), nLevel,
m_bAlphabeticSort ? 0 : getYPos(pNodeIndex->GetNode()),
pFormat));
if (!pFormat->IsVisible() || pSection->IsHidden())
pCnt->SetInvisible();
m_pMember->insert(std::move(pCnt));
}
if (pOldMember)
{ // need to check visibility (and equal entry number) after // creation due to a sorted list being used here (before, // entries with same index were compared already at creation // time what worked before a sorted list was used)
assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
*pbContentChanged = checkVisibilityChanged(
*pOldMember,
*m_pMember);
}
}
} break; case ContentTypeId::REFERENCE:
{
std::vector<OUString> aRefMarks;
m_pWrtShell->GetRefMarks( &aRefMarks );
if (m_bAlphabeticSort)
{ for (auto& r : aArr)
{ auto pCnt(std::make_unique<SwURLFieldContent>(this, r.sText, INetURLObject::decode(
r.rINetAttr.GetINetFormat().GetValue(),
INetURLObject::DecodeMechanism::Unambiguous),
&r.rINetAttr, 0));
m_pMember->insert(std::move(pCnt));
} break;
}
// use stable sort array to list hyperlinks in document order const SwNodeOffset nEndOfExtrasIndex = m_pWrtShell->GetNodes().GetEndOfExtras().GetIndex(); bool bHasEntryInFly = false;
std::vector<SwGetINetAttr*> aStableSortINetAttrsArray;
for (SwGetINetAttr& r : aArr)
{
aStableSortINetAttrsArray.emplace_back(&r); if (!bHasEntryInFly)
{ if (nEndOfExtrasIndex >= r.rINetAttr.GetTextNode().GetIndex())
{ // Not a node of BodyText // Are we in a fly? if (r.rINetAttr.GetTextNode().GetFlyFormat())
bHasEntryInFly = true;
}
}
}
// When there are hyperlinks in text frames do an additional sort using the text frame // anchor position to place entries in the order of document layout appearance. if (bHasEntryInFly)
{
std::stable_sort(aStableSortINetAttrsArray.begin(), aStableSortINetAttrsArray.end(),
[nEndOfExtrasIndex](const SwGetINetAttr* a, const SwGetINetAttr* b){ const SwTextNode& aTextNode = a->rINetAttr.GetTextNode(); const SwTextNode& bTextNode = b->rINetAttr.GetTextNode();
SwPosition aPos(aTextNode, a->rINetAttr.GetStart());
SwPosition bPos(bTextNode, b->rINetAttr.GetStart()); // use anchor position for entries that are located in flys if (nEndOfExtrasIndex >= aTextNode.GetIndex()) if (auto pFlyFormat = aTextNode.GetFlyFormat()) if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor())
aPos = *pPos; if (nEndOfExtrasIndex >= bTextNode.GetIndex()) if (auto pFlyFormat = bTextNode.GetFlyFormat()) if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor())
bPos = *pPos; return aPos < bPos;});
}
SwGetINetAttrs::size_type n = 0; for (auto p : aStableSortINetAttrsArray)
{ auto pCnt = std::make_unique<SwURLFieldContent>(this, p->sText,
INetURLObject::decode(p->rINetAttr.GetINetFormat().GetValue(),
INetURLObject::DecodeMechanism::Unambiguous),
&p->rINetAttr, ++n);
m_pMember->insert(std::move(pCnt));
}
} break; case ContentTypeId::INDEX:
{ const sal_uInt16 nCount = m_pWrtShell->GetTOXCount();
m_pMember->insert( std::unique_ptr<SwContent>(pCnt) ); const size_t nPos = m_pMember->size() - 1; if (pOldMember)
{
assert(pbContentChanged && "pbContentChanged is always set if pOldMember is"); if (!*pbContentChanged && nOldMemberCount > nPos &&
(*pOldMember)[nPos]->IsInvisible() != pCnt->IsInvisible())
*pbContentChanged = true;
}
}
} break; case ContentTypeId::POSTIT:
{
SwPostItMgr* aMgr = m_pWrtShell->GetView().GetPostItMgr(); if (aMgr)
{
tools::Long nYPos = 0; for(SwPostItMgr::const_iterator i = aMgr->begin(); i != aMgr->end(); ++i)
{ if (const SwFormatField* pFormatField = dynamic_cast<const SwFormatField *>((*i)->GetBroadcaster())) // SwPostit
{ if (pFormatField->GetTextField() && pFormatField->IsFieldInDoc())
{
OUString sEntry = pFormatField->GetField()->GetPar2();
sEntry = RemoveNewline(sEntry);
std::unique_ptr<SwPostItContent> pCnt(new SwPostItContent( this,
sEntry,
pFormatField,
nYPos)); if (!pFormatField->GetTextField()->GetTextNode().getLayoutFrame(
m_pWrtShell->GetLayout()))
pCnt->SetInvisible(); if (pOldMember)
{
assert(pbContentChanged && "pbContentChanged is always set if pOldMember is"); if (!*pbContentChanged &&
nOldMemberCount > o3tl::make_unsigned(nYPos) &&
(*pOldMember)[nYPos]->IsInvisible() != pCnt->IsInvisible())
*pbContentChanged = true;
}
m_pMember->insert(std::move(pCnt));
nYPos++;
}
}
}
}
} break; case ContentTypeId::DRAWOBJECT:
{
IDocumentDrawModelAccess& rIDDMA = m_pWrtShell->getIDocumentDrawModelAccess();
SwDrawModel* pModel = rIDDMA.GetDrawModel(); if(pModel)
{
SdrPage* pPage = pModel->GetPage(0); for (const rtl::Reference<SdrObject>& pTemp : *pPage)
{ // #i51726# - all drawing objects can be named now if (!pTemp->IsVirtualObj() && !pTemp->GetName().isEmpty())
{
tools::Long nYPos = LONG_MIN; constbool bIsVisible = rIDDMA.IsVisibleLayerId(pTemp->GetLayer()); if (bIsVisible)
nYPos = m_bAlphabeticSort ? 0 : pTemp->GetLogicRect().Top(); auto pCnt(std::make_unique<SwContent>(this, pTemp->GetName(), nYPos)); if (!bIsVisible)
pCnt->SetInvisible();
m_pMember->insert(std::move(pCnt));
}
}
if (pOldMember)
{ // need to check visibility (and equal entry number) after // creation due to a sorted list being used here (before, // entries with same index were compared already at creation // time what worked before a sorted list was used)
assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
*pbContentChanged = checkVisibilityChanged(
*pOldMember,
*m_pMember);
}
}
} break; default: break;
}
m_nMemberCount = m_pMember->size(); if (pOldMember)
{
assert(pbContentChanged && "pbContentChanged is always set if pOldMember is"); if (!*pbContentChanged && pOldMember->size() != m_nMemberCount)
*pbContentChanged = true;
}
IMPL_LINK(SwContentTree, MouseMoveHdl, const MouseEvent&, rMEvt, bool)
{ // Prevent trying to bring entry to attention when handling document change. The mouse over // entry might not be valid, for example, when the mouse pointer is on an entry that is deleted // in the document by an undo/redo. if (m_bDocHasChanged) returnfalse; if (m_eState == State::HIDDEN) returnfalse; if (std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
m_xTreeView->get_dest_row_at_pos(rMEvt.GetPosPixel(), xEntry.get(), false, false) &&
!rMEvt.IsLeaveWindow())
{ if (!m_xOverlayCompareEntry)
m_xOverlayCompareEntry.reset(m_xTreeView->make_iterator().release()); elseif (m_xTreeView->iter_compare(*xEntry, *m_xOverlayCompareEntry) == 0) returnfalse; // The entry under the mouse has not changed.
m_xTreeView->copy_iterator(*xEntry, *m_xOverlayCompareEntry);
BringEntryToAttention(*xEntry);
} else
{ if (m_xOverlayCompareEntry)
m_xOverlayCompareEntry.reset();
m_aOverlayObjectDelayTimer.Stop(); if (m_xOverlayObject && m_xOverlayObject->getOverlayManager())
{
m_xOverlayObject->getOverlayManager()->remove(*m_xOverlayObject);
m_xOverlayObject.reset();
}
} returnfalse;
}
if (nAccept != DND_ACTION_NONE)
{ // to enable the autoscroll when we're close to the edges
weld::TreeView& rWidget = m_rTreeView.get_widget();
rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true);
}
if (m_nRootType == ContentTypeId::OUTLINE)
{ if (xDropEntry && lcl_IsContent(*xDropEntry, *m_xTreeView))
{
assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xDropEntry))));
SwOutlineContent* pOutlineContent = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xDropEntry));
assert(pOutlineContent);
void* key = lcl_GetOutlineKey(*this, pOutlineContent);
assert(key); if (!mOutLineNodeMap[key])
{ while (m_xTreeView->iter_has_child(*xDropEntry))
{
std::unique_ptr<weld::TreeIter> xChildEntry(m_xTreeView->make_iterator(xDropEntry.get())); bool bChildEntry = m_xTreeView->iter_children(*xChildEntry); while (bChildEntry)
{
m_xTreeView->copy_iterator(*xChildEntry, *xDropEntry);
bChildEntry = m_xTreeView->iter_next_sibling(*xChildEntry);
}
}
}
}
SwOutlineNodes::size_type nTargetPos = 0; if (!xDropEntry)
{ // dropped in blank space -> move to bottom
nTargetPos = GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineNodesCount() - 1;
} elseif (!lcl_IsContent(*xDropEntry, *m_xTreeView))
{ // dropped on "heading" parent -> move to start
nTargetPos = SwOutlineNodes::npos;
} else
{
assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xDropEntry))));
nTargetPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xDropEntry))->GetOutlinePos();
}
if( MAXLEVEL > m_nOutlineLevel && // Not all layers are displayed.
nTargetPos != SwOutlineNodes::npos)
{
std::unique_ptr<weld::TreeIter> xNext(m_xTreeView->make_iterator(xDropEntry.get())); bool bNext = m_xTreeView->iter_next(*xNext); if (bNext)
{
assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xNext))));
nTargetPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xNext))->GetOutlinePos() - 1;
} else
nTargetPos = GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineNodesCount() - 1;
}
// remove the drop highlight before we change the contents of the tree so we don't // try and dereference a removed entry in post-processing drop
m_xTreeView->unset_drag_dest_row();
MoveOutline(nTargetPos);
do
{ if (rContentTree.iter_has_child(*xChild) || rContentTree.get_children_on_demand(*xChild))
{ if (!IsAllExpanded(rContentTree, *xChild)) returnfalse;
}
} while (rContentTree.iter_next_sibling(*xChild)); returntrue;
}
// test for content in outline node
aIdx.Assign(*pSttNd); if (SwNodes::GoNext(&aIdx) != pEndNd)
{
bHasContent = true; break;
}
}
}
if (!bHasContent) return; // no content in any of the concerned outline nodes
}
// determine for subs if all are folded or unfolded or if they are mixed if (rContentTree.iter_has_child(rEntry) || rContentTree.get_children_on_demand(rEntry))
{ // skip no content nodes // we know there is content from results above so this is presumably safe
size_t nPos = nOutlinePos; while (true)
{
SwNode* pSttNd = rOutlineNodes[nPos];
SwNode* pEndNd = rOutlineNodes.back(); if (!bIsRoot && rOutlineNodes.size() > nPos + 1)
pEndNd = rOutlineNodes[nPos + 1];
SwNodeIndex aIdx(*pSttNd); if (SwNodes::GoNext(&aIdx) != pEndNd) break;
nPos++;
}
// select clicked entry or limit selection to root entry if needed if (std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
rCEvt.IsMouseEvent() && m_xTreeView->get_dest_row_at_pos(
rCEvt.GetMousePosPixel(), xEntry.get(), false))
{ // if clicked entry is not currently selected then clear selections and select it if (!m_xTreeView->is_selected(*xEntry))
m_xTreeView->set_cursor(*xEntry); // if root entry is selected then clear selections and select it elseif (m_xTreeView->is_selected(0))
m_xTreeView->set_cursor(0);
}
if (bRemoveToggleExpandEntry)
xPop->remove(OUString::number(800));
if (bRemoveGotoEntry)
xPop->remove(OUString::number(900));
if (bRemoveSelectEntry)
xPop->remove(OUString::number(805));
if (bRemoveChapterEntries)
{
xPop->remove(OUString::number(801));
xPop->remove(OUString::number(802));
xPop->remove(OUString::number(803));
xPop->remove(OUString::number(804));
}
if (bRemoveSendOutlineEntry)
xPop->remove(OUString::number(700));
if (bRemovePostItEntries)
{
xPop->remove(u"showcomments"_ustr);
xPop->remove(u"showresolvedcomments"_ustr);
}
if (bRemoveDeleteChapterEntry)
xPop->remove(u"deletechapter"_ustr); if (bRemoveDeleteAllTablesEntry)
xPop->remove(u"deletealltables"_ustr); if (bRemoveDeleteTableEntry)
xPop->remove(u"deletetable"_ustr); if (bRemoveDeleteAllFramesEntry)
xPop->remove(u"deleteallframes"_ustr); if (bRemoveDeleteFrameEntry)
xPop->remove(u"deleteframe"_ustr); if (bRemoveDeleteAllImagesEntry)
xPop->remove(u"deleteallimages"_ustr); if (bRemoveDeleteImageEntry)
xPop->remove(u"deleteimage"_ustr); if (bRemoveDeleteAllOLEObjectsEntry)
xPop->remove(u"deletealloleobjects"_ustr); if (bRemoveDeleteOLEObjectEntry)
xPop->remove(u"deleteoleobject"_ustr); if (bRemoveDeleteAllBookmarksEntry)
xPop->remove(u"deleteallbookmarks"_ustr); if (bRemoveDeleteBookmarkEntry)
xPop->remove(u"deletebookmark"_ustr); if (bRemoveDeleteAllRegionsEntry)
xPop->remove(u"deleteallregions"_ustr); if (bRemoveDeleteRegionEntry)
xPop->remove(u"deleteregion"_ustr); if (bRemoveDeleteAllHyperlinksEntry)
xPop->remove(u"deleteallhyperlinks"_ustr); if (bRemoveDeleteHyperlinkEntry)
xPop->remove(u"deletehyperlink"_ustr); if (bRemoveDeleteAllReferencesEntry)
xPop->remove(u"deleteallreferences"_ustr); if (bRemoveDeleteReferenceEntry)
xPop->remove(u"deletereference"_ustr); if (bRemoveDeleteAllIndexesEntry)
xPop->remove(u"deleteallindexes"_ustr); if (bRemoveDeleteIndexEntry)
xPop->remove(u"deleteindex"_ustr); if (bRemoveDeleteAllCommentsEntry)
xPop->remove(u"deleteallcomments"_ustr); if (bRemoveDeleteCommentEntry)
xPop->remove(u"deletecomment"_ustr); if (bRemoveDeleteAllDrawingObjectsEntry)
xPop->remove(u"deletealldrawingobjects"_ustr); if (bRemoveDeleteDrawingObjectEntry)
xPop->remove(u"deletedrawingobject"_ustr); if (bRemoveDeleteAllFieldsEntry)
xPop->remove(u"deleteallfields"_ustr); if (bRemoveDeleteFieldEntry)
xPop->remove(u"deletefield"_ustr); if (bRemoveDeleteAllFootnotesEntry)
xPop->remove(u"deleteallfootnotes"_ustr); if (bRemoveDeleteFootnoteEntry)
xPop->remove(u"deletefootnote"_ustr); if (bRemoveDeleteAllEndnotesEntry)
xPop->remove(u"deleteallendnotes"_ustr); if (bRemoveDeleteEndnoteEntry)
xPop->remove(u"deleteendnote"_ustr);
if (bRemoveMakeAllFootnotesEndnotesEntry)
xPop->remove(u"makeallfootnotesendnotes"_ustr); if (bRemoveMakeAllEndnotesFootnotesEntry)
xPop->remove(u"makeallendnotesfootnotes"_ustr);
if (!bOutline)
{
xSubPop1.reset();
xPop->remove(OUString::number(1)); // outline level menu
} if (!bOutline || State::HIDDEN == m_eState)
{
xSubPopOutlineTracking.reset();
xPop->remove(OUString::number(4)); // outline tracking menu
} if (!bOutline || State::HIDDEN == m_eState ||
!m_pActiveShell->GetViewOptions()->IsShowOutlineContentVisibilityButton() ||
m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount() == 0)
{
xSubPopOutlineContent.reset();
xPop->remove(OUString::number(5)); // outline folding menu
xPop->remove(u"separator3"_ustr);
}
if (bRemoveTableTracking)
xPop->remove(u"tabletracking"_ustr); if (bRemoveSectionTracking)
xPop->remove(u"sectiontracking"_ustr); if (bRemoveFrameTracking)
xPop->remove(u"frametracking"_ustr); if (bRemoveImageTracking)
xPop->remove(u"imagetracking"_ustr); if (bRemoveOLEobjectTracking)
xPop->remove(u"oleobjecttracking"_ustr); if (bRemoveBookmarkTracking)
xPop->remove(u"bookmarktracking"_ustr); if (bRemoveHyperlinkTracking)
xPop->remove(u"hyperlinktracking"_ustr); if (bRemoveReferenceTracking)
xPop->remove(u"referencetracking"_ustr); if (bRemoveIndexTracking)
xPop->remove(u"indextracking"_ustr); if (bRemoveCommentTracking)
xPop->remove(u"commenttracking"_ustr); if (bRemoveDrawingObjectTracking)
xPop->remove(u"drawingobjecttracking"_ustr); if (bRemoveFieldTracking)
xPop->remove(u"fieldtracking"_ustr); if (bRemoveFootnoteTracking)
xPop->remove(u"footnotetracking"_ustr); if (bRemoveEndnoteTracking)
xPop->remove(u"endnotetracking"_ustr); if (bRemoveSortEntry)
xPop->remove(u"sort"_ustr); if (bRemoveProtectSection)
xPop->remove(u"protectsection"_ustr); if (bRemoveHideSection)
xPop->remove(u"hidesection"_ustr);
// Content will be integrated into the Box only on demand. bool SwContentTree::RequestingChildren(const weld::TreeIter& rParent)
{ // Does the parent already have children or is it not a 'children on demand' node? if (m_xTreeView->iter_has_child(rParent) || !m_xTreeView->get_children_on_demand(rParent)) returnfalse;
// Is this a content type? if (lcl_IsContentType(rParent, *m_xTreeView))
{
std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator();
// if there is a preceding outline node candidate with a lower outline level // use that as a parent, otherwise use the root node auto aFind = std::find_if(aParentCandidates.rbegin(),
aParentCandidates.rend(), lambda); if (aFind != aParentCandidates.rend())
insert(aFind->get(), sEntry, sId, false, xChild.get()); else
insert(&rParent, sEntry, sId, false, xChild.get());
m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible());
m_xTreeView->set_extra_row_indent(
*xChild, nLevel + 1 - m_xTreeView->get_iter_depth(*xChild));
// remove any parent candidates equal to or higher than this node
std::erase_if(aParentCandidates, std::not_fn(lambda));
// add this node as a parent candidate for any following nodes at a higher // outline level
aParentCandidates.emplace_back(m_xTreeView->make_iterator(xChild.get()));
}
}
}
} elseif (pCntType->GetType() == ContentTypeId::REGION)
{ if (pCntType->IsAlphabeticSort())
{ for(size_t i = 0; i < nCount; ++i)
{ const SwRegionContent* pCnt = static_cast<const SwRegionContent*>(pCntType->GetMember(i));
// if there is a preceding region node candidate with a lower region level use // that as a parent, otherwise use the root node auto aFind = std::find_if(aParentCandidates.rbegin(), aParentCandidates.rend(), lambda); if (aFind != aParentCandidates.rend())
insert(aFind->get(), sEntry, sId, false, xChild.get()); else
insert(&rParent, sEntry, sId, false, xChild.get());
m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible());
// remove any parent candidates equal to or higher than this node
std::erase_if(aParentCandidates, std::not_fn(lambda));
// add this node as a parent candidate for any following nodes at a higher region level
aParentCandidates.emplace_back(m_xTreeView->make_iterator(xChild.get()));
}
}
} elseif (pCntType->GetType() == ContentTypeId::POSTIT)
{
std::vector<std::unique_ptr<weld::TreeIter>> aParentCandidates; for(size_t i = 0; i < nCount; ++i)
{ const SwPostItContent* pCnt = static_cast<const SwPostItContent*>(pCntType->GetMember(i));
// if a parent candidate is not found use the passed root node auto aFind = std::find_if(aParentCandidates.rbegin(), aParentCandidates.rend(), lambda); if (aFind != aParentCandidates.rend())
insert(aFind->get(), sEntry, sId, false, xChild.get()); else
insert(&rParent, sEntry, sId, false, xChild.get());
// clear parent candidates when encountering a postit that doesn't have a parent // following postits can't have a parent that is in these candidates if (pPostItField->GetParentPostItId() == 0)
aParentCandidates.clear();
// pNodesToExpand is used by the Display function to restore the trees expand structure for // hierarchical content types, e.g., OUTLINE and REGION. if (pNodesToExpand)
pNodesToExpand->emplace_back(m_xTreeView->make_iterator(&rParent));
// rParentId is a string representation of a pointer to SwContentType or SwContent const OUString aParentId = m_xTreeView->get_id(rParent); // bParentIsContentType tells if the passed rParent tree entry is a content type or content constbool bParentIsContentType = lcl_IsContentType(rParent, *m_xTreeView); // eParentContentTypeId is the content type of the passed rParent tree entry const ContentTypeId eParentContentTypeId =
bParentIsContentType ? weld::fromId<SwContentType*>(aParentId)->GetType() :
weld::fromId<SwContent*>(aParentId)->GetParent()->GetType();
if (m_nRootType == ContentTypeId::UNKNOWN && bParentIsContentType)
{ // m_nActiveBlock and m_nHiddenBlock are used to persist the content type expand state for // the all content view mode constint nShift = static_cast<int>(eParentContentTypeId);
SAL_WARN_IF(nShift < 0, "sw.ui", "ContentTypeId::UNKNOWN negative shift"); if (nShift >= 0)
{ const sal_Int32 nOr = 1 << nShift; //linear -> Bitposition if (State::HIDDEN != m_eState)
{
m_nActiveBlock |= nOr;
m_pConfig->SetActiveBlock(m_nActiveBlock);
} else
m_nHiddenBlock |= nOr;
}
}
if (!sForbiddenChars.isEmpty())
{ for (int i = 0; i < sForbiddenChars.getLength(); i++)
{ if (sNewName.indexOf(sForbiddenChars[i]) != -1)
{
m_bEditing = false; returnfalse;
}
}
}
if (xNameAccess.is())
{ if (xNameAccess->hasByName(sNewName) || (xSecond.is() && xSecond->hasByName(sNewName))
|| (xThird.is() && xThird->hasByName(sNewName)))
{
m_bEditing = false; returnfalse;
}
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));
} returnfalse; // return false to notify caller not to do collapse
}
ContentTypeId eContentTypeId =
weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent))->GetType(); constint nShift = static_cast<int>(eContentTypeId);
SAL_WARN_IF(nShift < 0, "sw.ui", "ContentTypeId::UNKNOWN negative shift"); if (nShift >= 0)
{ const sal_Int32 nAnd = ~(1 << nShift); if (State::HIDDEN != m_eState)
{
m_nActiveBlock &= nAnd;
m_pConfig->SetActiveBlock(m_nActiveBlock);
} else
m_nHiddenBlock &= nAnd;
}
} else// content entry
{
SwWrtShell* pShell = GetWrtShell();
ContentTypeId eContentTypeId =
weld::fromId<SwContent*>(m_xTreeView->get_id(rParent))->GetParent()->GetType(); if (eContentTypeId == ContentTypeId::OUTLINE)
{
assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent)))); autoconst nPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rParent))->GetOutlinePos(); void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos ));
mOutLineNodeMap[key] = false;
} elseif(eContentTypeId == ContentTypeId::REGION)
{
assert(dynamic_cast<SwRegionContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent)))); constvoid* key = static_cast<constvoid*>(weld::fromId<SwRegionContent*>(m_xTreeView->get_id(rParent))->GetSectionFormat());
m_aRegionNodeExpandMap[key] = false;
} elseif(eContentTypeId == ContentTypeId::POSTIT)
{
assert(dynamic_cast<SwPostItContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent)))); constvoid* key = static_cast<constvoid*>(weld::fromId<SwPostItContent*>(m_xTreeView->get_id(rParent))->GetPostIt());
m_aPostItNodeExpandMap[key] = false;
}
}
returntrue;
}
// Also on double click will be initially opened only.
IMPL_LINK_NOARG(SwContentTree, ContentDoubleClickHdl, weld::TreeView&, bool)
{ if (m_nRowActivateEventId)
Application::RemoveUserEvent(m_nRowActivateEventId); // post the event to process row activate after mouse press event to be able to set key // modifier for selection feature (tdf#154211)
m_nRowActivateEventId
= Application::PostUserEvent(LINK(this, SwContentTree, AsyncContentDoubleClickHdl));
bool bConsumed = false;
std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); if (m_xTreeView->get_cursor(xEntry.get()) && lcl_IsContent(*xEntry, *m_xTreeView) &&
(State::HIDDEN != m_eState))
{
SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry));
assert(pCnt && "no UserData"); if (pCnt && !pCnt->IsInvisible())
{ // fdo#36308 don't expand outlines on double-click
bConsumed = pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE;
}
}
return bConsumed; // false/true == allow/disallow more to be done, i.e. expand/collapse children
}
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();
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();
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);
}
if (!bChOnDemand)
InsertContent(*xEntry); else
{ // fill contents of to-be expanded entries while frozen
Expand(*xEntry, &aNodesToExpand);
m_xTreeView->set_children_on_demand(*xEntry, false);
}
if (bChOnDemand)
{ // restore visual expanded tree state for (constauto& rNode : aNodesToExpand)
m_xTreeView->expand_row(*rNode);
} else
m_xTreeView->expand_row(*xEntry);
}
// Reselect the old selected entry. If it is not available, select the entry at the old // selected entry position unless that entry position is now a content type or is past the // end of the member list then select the entry at the previous entry position. if (xOldSelEntry)
{
std::unique_ptr<weld::TreeIter> xSelEntry = m_xTreeView->make_iterator(xCntTypeEntry.get()); if (nEntryRelPos)
{
std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator(xCntTypeEntry.get()));
std::unique_ptr<weld::TreeIter> xTemp(m_xTreeView->make_iterator(xIter.get()));
sal_uLong nPos = 1; bool bNext; while ((bNext = m_xTreeView->iter_next(*xIter) && lcl_IsContent(*xIter, *m_xTreeView)))
{ if (nPos == nEntryRelPos)
{
m_xTreeView->copy_iterator(*xIter, *xSelEntry); break;
}
m_xTreeView->copy_iterator(*xIter, *xTemp); // note previous entry
nPos++;
} if (!bNext)
xSelEntry = std::move(xTemp);
} // set_cursor unselects all entries, makes passed entry visible, and selects it
m_xTreeView->set_cursor(*xSelEntry);
}
UpdateContentFunctionsToolbar();
}
if (!m_bIgnoreDocChange && GetEntryCount() == nOldEntryCount)
{
m_xTreeView->vadjustment_set_value(nOldScrollPos);
}
}
// 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();
}
} break; case ContentTypeId::BOOKMARK:
sEntry = m_xTreeView->get_text(*xEntry); break; case ContentTypeId::TABLE: case ContentTypeId::FRAME: case ContentTypeId::REGION:
bCrossRef = false;
sEntry = m_xTreeView->get_text(*xEntry); break; // content types that cannot be inserted, as URL, section, or reference case ContentTypeId::POSTIT: case ContentTypeId::INDEX: returnfalse; // content types than can only be inserted as a cross-reference case ContentTypeId::REFERENCE: case ContentTypeId::TEXTFIELD: case ContentTypeId::FOOTNOTE: case ContentTypeId::ENDNOTE:
bUrl = false;
sEntry = m_xTreeView->get_text(*xEntry); break; // content types that can only be inserted as a hyperlink case ContentTypeId::URLFIELD:
sUrl = static_cast<SwURLFieldContent*>(pCnt)->GetURL();
[[fallthrough]]; case ContentTypeId::OLE: case ContentTypeId::GRAPHIC: case ContentTypeId::DRAWOBJECT:
bCrossRef = false;
sEntry = m_xTreeView->get_text(*xEntry); break; default: returnfalse;
}
if(!sEntry.isEmpty())
{ const SwDocShell* pDocShell = pWrtShell->GetView().GetDocShell(); if (bUrl && sUrl.isEmpty())
{ if(pDocShell->HasName())
{
SfxMedium* pMedium = pDocShell->GetMedium();
sUrl = pMedium->GetURLObject().GetURLNoMark();
} elseif (SwView* pView = GetActiveView(); State::CONSTANT == m_eState
&& (!pView || m_pActiveShell != pView->GetWrtShellPtr()))
{ // Urls of inactive views cannot dragged without // file names, also. returnfalse;
} elseif (eActType != ContentTypeId::REGION && eActType != ContentTypeId::BOOKMARK)
{ // For sections and bookmarks a link is also allowed // without a filename into its own document. returnfalse;
}
sCrossRef = OUString::number(static_cast<int>(eReferenceSubType)) + u"|"
+ sEntry + u"|" + sVal;
} elseif (eActType == ContentTypeId::OUTLINE)
{
sEntry = sOutlineText; const SwOutlineNodes::size_type nPos = static_cast<SwOutlineContent*>(pCnt)->GetOutlinePos(); const SwTextNode* pTextNode =
pWrtShell->GetNodes().GetOutLineNds()[nPos]->GetTextNode();
sw::mark::MarkBase const * const pMark =
pWrtShell->getIDocumentMarkAccess()->getMarkForTextNode(
*pTextNode, IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK); // REFERENCESUBTYPE_OUTLINE is changed to REFERENCESUBTYPE::BOOKMARK in // SwWrtShell::NavigatorPaste. It is used to differentiate between a // headings reference and a regular bookmark reference to show different // options in the reference mark type popup menu.
sCrossRef = OUString::number(static_cast<int>(REFERENCESUBTYPE::REF_OUTLINE))
+ u"|" + pMark->GetName().toString();
}
}
// An INetBookmark must a be delivered to foreign DocShells if (bUrl && pDocShell->HasName())
{
INetBookmark aBkmk( sUrl, sEntry );
rTransfer.CopyINetBookmark( aBkmk );
}
}
returntrue;
}
void SwContentTree::ToggleToRoot()
{ if(!m_bIsRoot)
{
std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); bool bEntry = m_xTreeView->get_cursor(xEntry.get()); if (bEntry)
{ const SwContentType* pCntType; if (lcl_IsContentType(*xEntry, *m_xTreeView))
{
assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry));
} else
{
assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
pCntType = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->GetParent();
}
m_nRootType = pCntType->GetType();
m_bIsRoot = true; if (m_nRootType == ContentTypeId::OUTLINE || m_nRootType == ContentTypeId::DRAWOBJECT)
{
m_xTreeView->set_selection_mode(SelectionMode::Multiple);
}
Display(State::HIDDEN != m_eState);
}
} else
{
m_xTreeView->set_selection_mode(SelectionMode::Single);
m_nLastSelType = m_nRootType;
m_nRootType = ContentTypeId::UNKNOWN;
m_bIsRoot = false; // Other content type member data could have changed while in root view. Fill the content // member lists excluding the toggled from root content which should already have the most // recent data. if (State::HIDDEN != m_eState)
{ for (ContentTypeId i : o3tl::enumrange<ContentTypeId>())
{ if (i != m_nLastSelType && m_aActiveContentArr[i])
m_aActiveContentArr[i]->FillMemberList();
}
}
Display(State::HIDDEN != m_eState);
}
m_pConfig->SetRootType( m_nRootType );
weld::Toolbar* pBox = GetParentWindow()->m_xContent5ToolBox.get();
pBox->set_item_active(u"root"_ustr, m_bIsRoot);
}
bool SwContentTree::HasContentChanged()
{ if (m_bEditing)
{ returnfalse;
}
bool bContentChanged = false;
// - Run through the local array and the Treelistbox in parallel. // - Are the records not expanded, they are discarded only in the array // and the content type will be set as the new UserData. // - Is the root mode is active only this will be updated.
// Valid for the displayed content types is: // the Memberlist will be erased and the membercount will be updated // If content will be checked, the memberlists will be replenished // at the same time. Once a difference occurs it will be only replenished // no longer checked. Finally, the box is filled again.
if (State::HIDDEN == m_eState)
{ for(ContentTypeId i : o3tl::enumrange<ContentTypeId>())
{ if(m_aActiveContentArr[i])
m_aActiveContentArr[i]->Invalidate();
} returnfalse;
}
// single content type navigation view if(m_bIsRoot)
{
std::unique_ptr<weld::TreeIter> xRootEntry(m_xTreeView->make_iterator()); if (!m_xTreeView->get_iter_first(*xRootEntry)) returntrue;
pArrType->FillMemberList(&bContentChanged); if (bContentChanged) returntrue;
// FillMemberList tests if member count in old member array equals member count in new // member array. Test here for member count difference between array and tree. const size_t nChildCount = GetChildCount(*xRootEntry); if (nChildCount != pArrType->GetMemberCount()) returntrue;
// FillMemberList clears the content type member list and refills with new data. // Treeview entry user data is set here to the string representation of the pointer to // the member data in the array. The Display function will clear and recreate the // treeview from the content type member arrays if content change is detected. const SwContent* pCnt = pArrType->GetMember(j);
if (pCnt->IsInvisible() != m_xTreeView->get_sensitive(*xEntry, 0)) returntrue;
// Set_id needs to be done here because FillMemberList clears the content type member // list and refills with new data making the previously set id invalid. If there is no // content change detected the Display function will not be called and the tree entry // user data will not be set to the new content member pointer address.
OUString sSubId(weld::toId(pCnt));
m_xTreeView->set_id(*xEntry, sSubId);
}
} // all content types navigation view else
{ // Fill member list for each content type and check for content change. If content change // is detected only fill member lists for remaining content types. The Display function // will clear and recreate the treeview from the content type member arrays if content has // changed.
if (comphelper::LibreOfficeKit::isActive())
{ // In case of LOK, empty contentTypes are hidden, even in all content view // so it is not enough to check only the m_xTreeView. bool bCountChanged = false; bool bHasContentChanged = false; for (ContentTypeId i : o3tl::enumrange<ContentTypeId>())
{ if (m_aActiveContentArr[i])
{ auto nLastTMCount = m_aActiveContentArr[i]->GetMemberCount(); if (i == ContentTypeId::OUTLINE) // this is required for checking if header level is changed
m_aActiveContentArr[i]->FillMemberList(&bHasContentChanged); else
m_aActiveContentArr[i]->FillMemberList(); // If the member count of a type is changed, then the content is surely changed if (m_aActiveContentArr[i]->GetMemberCount() != nLastTMCount)
bCountChanged = true; if (bHasContentChanged)
bContentChanged = true;
}
} if (bCountChanged || bContentChanged) returntrue;
}
// lambda function to find the next content type entry auto lcl_nextContentTypeEntry = [this, &xEntry](){ while (m_xTreeView->get_iter_depth(*xEntry))
m_xTreeView->iter_parent(*xEntry); return m_xTreeView->iter_next_sibling(*xEntry);
};
if (!pArrType)
{
bContentChanged = true; continue;
}
// all content type member lists must be filled! if (bContentChanged)
{ // If content change has already been detected there is no need to detect // other content change so no argument is supplied here to FillMemberList.
pArrType->FillMemberList(); continue;
}
pArrType->FillMemberList(&bContentChanged); if (bContentChanged) continue;
// does entry have children? if (m_xTreeView->get_row_expanded(*xEntry))
{ const size_t nChildCount = GetChildCount(*xEntry); if(nChildCount != pArrType->GetMemberCount())
{
bContentChanged = true; continue;
}
// 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, u""_ustr); returnfalse;
});
}
// tdf#148432 in LTR UI override the navigator treeview direction based on // the first page directionality if (m_pActiveShell && !AllSettings::GetLayoutRTL())
{ const SwPageDesc& rDesc = m_pActiveShell->GetPageDesc(0); const SvxFrameDirectionItem& rFrameDir = rDesc.GetMaster().GetFrameDir();
m_xTreeView->set_direction(rFrameDir.GetValue() == SvxFrameDirection::Horizontal_RL_TB);
}
// Only if it is the active view, the array will be deleted and // the screen filled new. if (State::ACTIVE == m_eState && bClear)
{ if (m_pActiveShell)
StartListening(*m_pActiveShell->GetView().GetDocShell());
FindActiveTypeAndRemoveUserData(); for(ContentTypeId i : o3tl::enumrange<ContentTypeId>())
{
m_aActiveContentArr[i].reset();
}
Display(true);
}
}
m_xTreeView->selected_foreach([&](weld::TreeIter& rEntry){ // it's possible to select the root node too which is a really bad idea if (lcl_IsContentType(rEntry, *m_xTreeView)) returnfalse; // 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)
{ returnfalse;
}
}
}
selected.emplace_back(m_xTreeView->make_iterator(&rEntry));
// Use the outline node position in the SwOutlineNodes array. Bad things // happen if the tree entry position is used and it doesn't match the node position // in SwOutlineNodes, which is usually the case for outline nodes in frames. const SwOutlineNodes::size_type nPos
= weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rEntry))->GetOutlinePos(); if (nPos < nOutlineNdsSize)
{
SwNode* pNode = rNodes.GetOutLineNds()[ nPos ]; if (pNode)
{
selectedOutlineNodes.push_back(pNode->GetTextNode());
}
} returnfalse;
});
if (!selected.size()) return;
if (bUpDown && !bUp)
{ // to move down, start at the end!
std::reverse(selected.begin(), selected.end());
}
// get first regular document content node outline node position in outline nodes array
SwOutlineNodes::size_type nFirstRegularDocContentOutlineNodePos = SwOutlineNodes::npos;
SwNodeOffset nEndOfExtrasIndex = rNodes.GetEndOfExtras().GetIndex();
sal_Int32 nHasInlineHeading = 0; for (SwOutlineNodes::size_type nPos = 0; nPos < nOutlineNdsSize; nPos++)
{ if (rOutlineNodes[nPos]->GetIndex() > nEndOfExtrasIndex)
{
nFirstRegularDocContentOutlineNodePos = nPos; break;
} if ( bUpDown && rOutlineNodes[nPos] != SwOutlineNodes::GetRootNode(rOutlineNodes[nPos]) )
++nHasInlineHeading;
}
for (autoconst& pCurrentEntry : selected)
{
nActPos = weld::fromId<SwOutlineContent*>(
m_xTreeView->get_id(*pCurrentEntry))->GetOutlinePos();
// outline nodes in frames and tables are not up/down moveable if (nActPos == SwOutlineNodes::npos ||
(bUpDown && (!pShell->IsOutlineMovable(nActPos) ||
nFirstRegularDocContentOutlineNodePos == SwOutlineNodes::npos)))
{ // except inline headings, i.e. Inline Heading frames with // single outlines, and anchored as characters, which headings // are movable with their anchor node, if they are // 1) not in other frames or 2) not in tables and 3) not protected const SwNode* pRootNode = nHasInlineHeading > 0
? SwOutlineNodes::GetRootNode(rOutlineNodes[nActPos])
: nullptr; if ( !pRootNode || pRootNode == rOutlineNodes[nActPos] ||
pRootNode != SwOutlineNodes::GetRootNode(pRootNode) || // frame in frame
pRootNode->FindTableNode() || // frame in table
pRootNode->IsProtect() ) // write protection
{ continue;
}
}
// move outline position up/down (outline position promote/demote)
SwOutlineNodes::difference_type nDir = bUp ? -1 : 1;
SwOutlineNodesInline::size_type nActPosInline;
aOutlineNodesInline.Seek_Entry(rOutlineNodes[nActPos], &nActPosInline); if ( (nDir == -1 && nActPosInline > 0) ||
(nDir == 1 && nActPosInline < nOutlineNdsSizeInline - 1) )
{ // make outline selection for use by MoveOutlinePara
pShell->MakeOutlineSel(nActPos, nActPos, bOutlineWithChildren, true, &aOutlineNodesInline);
int nActPosOutlineLevel =
rOutlineNodes[nActPos]->GetTextNode()->GetAttrOutlineLevel(); // search for sorted position
SwOutlineNodesInline::size_type nPos;
aOutlineNodesInline.Seek_Entry_By_Anchor(SwOutlineNodes::GetRootNode(rOutlineNodes[nActPos]), &nPos); if (!bUp)
{ // move down int nPosOutlineLevel = -1; while (++nPos < nOutlineNdsSizeInline)
{
nPosOutlineLevel = aOutlineNodesInline[nPos]->GetTextNode()->GetAttrOutlineLevel();
// discontinue if moving out of parent or equal level is found if (nPosOutlineLevel <= nActPosOutlineLevel)
{ break;
} // count the children of the node when they are not included in the move if (!bOutlineWithChildren)
nDir++;
} if (nPosOutlineLevel >= nActPosOutlineLevel)
{ // move past children while (++nPos < nOutlineNdsSizeInline)
{
nPosOutlineLevel = aOutlineNodesInline[nPos]->GetTextNode()->GetAttrOutlineLevel(); // discontinue if moving out of parent or equal level is found if (nPosOutlineLevel <= nActPosOutlineLevel) break;
nDir++;
}
}
} else
{ // move up while (nPos && --nPos >= nFirstRegularDocContentOutlineNodePos - nHasInlineHeading)
{ int nPosOutlineLevel =
aOutlineNodesInline[nPos]->GetTextNode()->GetAttrOutlineLevel(); // discontinue if equal level is found if (nPosOutlineLevel == nActPosOutlineLevel)
{ break;
} // discontinue if moving out of parent if (nPosOutlineLevel < nActPosOutlineLevel)
{ // Required for expected chapter placement when the chapter being moved // up has an outline level less than the outline level of chapters it // is being moved above and then encounters a chapter with an outline // level that is greater before reaching a chapter with the same // outline level as itself. if (nDir < -1)
nDir++; break;
}
nDir--;
}
}
pShell->MoveOutlinePara(nDir, &aOutlineNodesInline);
}
pShell->ClearMark();
} elseif (bUpDown)
{ // move outline position up/down (outline position promote/demote)
SwOutlineNodes::difference_type nDir = bUp ? -1 : 1; if ((nDir == -1 && nActPos > 0) || (nDir == 1 && nActPos < nOutlineNdsSize - 1))
{ // make outline selection for use by MoveOutlinePara
pShell->MakeOutlineSel(nActPos, nActPos, bOutlineWithChildren);
int nActPosOutlineLevel =
rOutlineNodes[nActPos]->GetTextNode()->GetAttrOutlineLevel();
SwOutlineNodes::size_type nPos = nActPos; if (!bUp)
{ // move down int nPosOutlineLevel = -1; while (++nPos < nOutlineNdsSize)
{
nPosOutlineLevel =
rOutlineNodes[nPos]->GetTextNode()->GetAttrOutlineLevel(); // discontinue if moving out of parent or equal level is found if (nPosOutlineLevel <= nActPosOutlineLevel) break; // count the children of the node when they are not included in the move if (!bOutlineWithChildren)
nDir++;
} if (nPosOutlineLevel >= nActPosOutlineLevel)
{ // move past children while (++nPos < nOutlineNdsSize)
{
nPosOutlineLevel =
rOutlineNodes[nPos]->GetTextNode()->GetAttrOutlineLevel(); // discontinue if moving out of parent or equal level is found if (nPosOutlineLevel <= nActPosOutlineLevel) break;
nDir++;
}
}
} else
{ // move up while (nPos && --nPos >= nFirstRegularDocContentOutlineNodePos)
{ int nPosOutlineLevel =
rOutlineNodes[nPos]->GetTextNode()->GetAttrOutlineLevel(); // discontinue if equal level is found if (nPosOutlineLevel == nActPosOutlineLevel) break; // discontinue if moving out of parent if (nPosOutlineLevel < nActPosOutlineLevel)
{ // Required for expected chapter placement when the chapter being moved // up has an outline level less than the outline level of chapters it // is being moved above and then encounters a chapter with an outline // level that is greater before reaching a chapter with the same // outline level as itself. if (nDir < -1)
nDir++; break;
}
nDir--;
}
}
pShell->MoveOutlinePara(nDir);
}
pShell->ClearMark();
} else
{ // move outline left/right (outline level promote/demote) if (!pShell->IsProtectedOutlinePara())
{ bool bAllow = true; const SwOutlineNodes& rOutlNds = pShell->GetDoc()->GetNodes().GetOutLineNds(); constint nActLevel = rOutlNds[nActPos]->GetTextNode()->GetAttrOutlineLevel(); if (!bLeft)
{ // disallow if any outline node to demote will exceed MAXLEVEL
SwOutlineNodes::size_type nPos = nActPos; do
{ int nLevel = rOutlNds[nPos]->GetTextNode()->GetAttrOutlineLevel(); if (nLevel == MAXLEVEL)
{
bAllow = false; break;
}
} while (bOutlineWithChildren && ++nPos < rOutlNds.size() &&
rOutlNds[nPos]->GetTextNode()->GetAttrOutlineLevel() > nActLevel);
} else
{ // disallow if trying to promote outline of level 1 if (nActLevel == 1)
bAllow = false;
} if (bAllow)
{
SwOutlineNodes::size_type nPos = nActPos; do
{
pShell->SwCursorShell::GotoOutline(nPos);
pShell->OutlineUpDown(bLeft ? -1 : 1);
} while (bOutlineWithChildren && ++nPos < rOutlNds.size() &&
rOutlNds[nPos]->GetTextNode()->GetAttrOutlineLevel() > nActLevel);
}
}
}
pShell->Pop(SwCursorShell::PopMode::DeleteCurrent); // Cursor is now back at the current heading.
}
if (bStartedAction)
{
pShell->EndUndo();
pShell->EndAllAction(); if (m_aActiveContentArr[ContentTypeId::OUTLINE])
m_aActiveContentArr[ContentTypeId::OUTLINE]->Invalidate();
// tdf#143547 LO Writer: navigator should stand still on promoting and demoting // In addition to m_bIgnoreDocChange being true, selections are cleared before the Display // call. Either of these conditions disable restore of scroll position happening in the // Display function so it needs to be done here. auto nOldScrollPos = m_xTreeView->vadjustment_get_value();
// clear all selections to prevent the Display function from trying to reselect selected entries
m_xTreeView->unselect_all();
Display(true);
m_xTreeView->vadjustment_set_value(nOldScrollPos);
if (m_bIsRoot)
{ // reselect entries, do this only when in outline content navigation mode const SwOutlineNodes& rOutlineNds = pShell->GetNodes().GetOutLineNds(); for (SwTextNode* pNode : selectedOutlineNodes)
{
m_xTreeView->all_foreach([this, &rOutlineNds, pNode](weld::TreeIter& rEntry){ if (lcl_IsContentType(rEntry, *m_xTreeView)) returnfalse;
SwOutlineNodes::size_type nPos = weld::fromId<SwOutlineContent*>(
m_xTreeView->get_id(rEntry))->GetOutlinePos(); if (pNode == rOutlineNds[nPos]->GetTextNode())
{
std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rEntry)); if (m_xTreeView->iter_parent(*xParent)
&& !m_xTreeView->get_row_expanded(*xParent))
{
m_xTreeView->expand_row(*xParent);
}
m_xTreeView->select(rEntry); returntrue;
} returnfalse;
});
}
UpdateContentFunctionsToolbar();
} else
{
m_pActiveShell->GetView().GetEditWin().GrabFocus();
m_bIgnoreDocChange = false;
UpdateTracking();
grab_focus();
}
}
m_bIgnoreDocChange = false;
}
// find content type entry
std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator()); bool bFoundEntry = rContentTree.get_iter_first(*xIter); while (bFoundEntry && rContentTypeName != rContentTree.get_text(*xIter))
bFoundEntry = rContentTree.iter_next_sibling(*xIter); // find content type content entry and select it if (!bFoundEntry) return;
rContentTree.expand_row(*xIter); // assure content type entry is expanded while (rContentTree.iter_next(*xIter) && lcl_IsContent(*xIter, rContentTree))
{ if (rName == rContentTree.get_text(*xIter))
{ // get first selected for comparison
std::unique_ptr<weld::TreeIter> xFirstSelected(rContentTree.make_iterator()); if (!rContentTree.get_selected(xFirstSelected.get()))
xFirstSelected.reset(); if (rContentTree.count_selected_rows() != 1 || !xFirstSelected ||
rContentTree.iter_compare(*xIter, *xFirstSelected) != 0)
{ // unselect all entries and make passed entry visible and selected
rContentTree.set_cursor(*xIter);
pThis->UpdateContentFunctionsToolbar();
} break;
}
}
}
// find content type entry
std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator()); bool bFoundEntry = rContentTree.get_iter_first(*xIter); while (bFoundEntry && SwResId(STR_CONTENT_TYPE_DRAWOBJECT) != rContentTree.get_text(*xIter))
bFoundEntry = rContentTree.iter_next_sibling(*xIter); // find content type content entry and select it if (bFoundEntry)
{
rContentTree.expand_row(*xIter); // assure content type entry is expanded while (rContentTree.iter_next(*xIter) && lcl_IsContent(*xIter, rContentTree))
{ if (rName == rContentTree.get_text(*xIter))
{ if (!rContentTree.is_selected(*xIter))
{
rContentTree.select(*xIter);
rContentTree.scroll_to_row(*xIter);
pThis->UpdateContentFunctionsToolbar();
} break;
}
}
}
}
/** No idle with focus or while dragging */
IMPL_LINK_NOARG(SwContentTree, TimerUpdate, Timer *, void)
{ // No need to update if content tree is not visible if (!m_xTreeView->is_visible()) return;
// No update while focus is not in document. // No update while drag and drop. // Query view because the Navigator is cleared too late.
SwView* pView = GetParentWindow()->GetCreateView();
void SwContentTree::UpdateTracking()
{ if (State::HIDDEN == m_eState || !m_pActiveShell) return;
// only when treeview or treeview context menu does not have focus if (m_xTreeView->has_focus() || m_xTreeView->has_child_focus()) return;
// m_bIgnoreDocChange is set on delete and outline visibility toggle if (m_bIgnoreDocChange)
{
m_bIgnoreDocChange = false; return;
}
// bTrack is used to disallow tracking after jumping to an outline until the outline position // that was jumped to is no longer the current outline position. bool bTrack = true; if (m_nLastGotoContentWasOutlinePos != SwOutlineNodes::npos)
{ if (m_pActiveShell->GetOutlinePos() == m_nLastGotoContentWasOutlinePos)
bTrack = false; else
m_nLastGotoContentWasOutlinePos = SwOutlineNodes::npos;
}
if (bTrack)
{ // graphic, frame, and ole if (m_pActiveShell->GetSelectionType() &
(SelectionType::Graphic | SelectionType::Frame | SelectionType::Ole))
{
OUString aContentTypeName; if (m_pActiveShell->GetSelectionType() == SelectionType::Graphic &&
!(m_bIsRoot && m_nRootType != ContentTypeId::GRAPHIC))
{ if (!mTrackContentType[ContentTypeId::GRAPHIC]) return;
aContentTypeName = SwResId(STR_CONTENT_TYPE_GRAPHIC);
} elseif (m_pActiveShell->GetSelectionType() == SelectionType::Frame &&
!(m_bIsRoot && m_nRootType != ContentTypeId::FRAME))
{ if (!mTrackContentType[ContentTypeId::FRAME]) return;
aContentTypeName = SwResId(STR_CONTENT_TYPE_FRAME);
} elseif (m_pActiveShell->GetSelectionType() == SelectionType::Ole &&
!(m_bIsRoot && m_nRootType != ContentTypeId::OLE))
{ if (!mTrackContentType[ContentTypeId::OLE]) return;
aContentTypeName = SwResId(STR_CONTENT_TYPE_OLE);
} if (!aContentTypeName.isEmpty())
{
UIName aName(m_pActiveShell->GetFlyName());
lcl_SelectByContentTypeAndName(this, *m_xTreeView, aContentTypeName, aName.toString()); return;
}
} // drawing if ((m_pActiveShell->GetSelectionType() & (SelectionType::DrawObject |
SelectionType::DrawObjectEditMode |
SelectionType::DbForm)) &&
!(m_bIsRoot && m_nRootType != ContentTypeId::DRAWOBJECT))
{ if (mTrackContentType[ContentTypeId::DRAWOBJECT])
{ // Multiple selection is possible when in root content navigation view so unselect all // selected entries before reselecting. This causes a bit of an annoyance when the treeview // scroll bar is used and focus is in the document by causing the last selected entry to // scroll back into view. if (m_bIsRoot)
m_xTreeView->unselect_all();
SdrView* pSdrView = m_pActiveShell->GetDrawView(); if (pSdrView)
{ for (size_t nIdx(0); nIdx < pSdrView->GetMarkedObjectList().GetMarkCount(); nIdx++)
{
SdrObject* pSelected = pSdrView->GetMarkedObjectList().GetMark(nIdx)->GetMarkedSdrObj();
OUString aName(pSelected->GetName()); if (!aName.isEmpty())
lcl_SelectDrawObjectByName(this, *m_xTreeView, aName);
}
} else
{ // clear treeview selections
m_xTreeView->unselect_all();
UpdateContentFunctionsToolbar();
}
} return;
} // footnotes and endnotes if (SwContentAtPos aContentAtPos(IsAttrAtPos::Footnote);
m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos)
&& aContentAtPos.pFndTextAttr &&
!(m_bIsRoot && (m_nRootType != ContentTypeId::FOOTNOTE &&
m_nRootType != ContentTypeId::ENDNOTE)))
{ if (!aContentAtPos.pFndTextAttr->GetFootnote().IsEndNote())
{ if (mTrackContentType[ContentTypeId::FOOTNOTE])
lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, ContentTypeId::FOOTNOTE,
aContentAtPos.pFndTextAttr);
} elseif (mTrackContentType[ContentTypeId::ENDNOTE])
lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, ContentTypeId::ENDNOTE,
aContentAtPos.pFndTextAttr); return;
} // references if (SwContentAtPos aContentAtPos(IsAttrAtPos::RefMark);
m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos) &&
aContentAtPos.pFndTextAttr &&
!(m_bIsRoot && m_nRootType != ContentTypeId::REFERENCE))
{ if (mTrackContentType[ContentTypeId::REFERENCE])
{ const SwFormatRefMark& rRefMark = aContentAtPos.pFndTextAttr->GetRefMark();
lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_REFERENCE),
rRefMark.GetRefName().toString());
} return;
} // hyperlinks // not in ToxContent tdf#148312 if (const SwSection* pSection = m_pActiveShell->GetCurrSection(); !pSection
|| (pSection && pSection->GetType() != SectionType::ToxContent))
{ if (SwContentAtPos aContentAtPos(IsAttrAtPos::InetAttr);
m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos)
&& (!m_bIsRoot || m_nRootType == ContentTypeId::URLFIELD))
{ // Because hyperlink item names do not need to be unique, finding the corresponding // item in the tree by name may result in incorrect selection. Find the item in the // tree by comparing the SwTextINetFormat pointer at the document cursor position to // that stored in the item SwURLFieldContent. if (mTrackContentType[ContentTypeId::URLFIELD])
lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, ContentTypeId::URLFIELD,
aContentAtPos.pFndTextAttr); return;
}
} // fields, comments if (SwField* pField = m_pActiveShell->GetCurField(); pField &&
!(m_bIsRoot &&
m_nRootType != ContentTypeId::TEXTFIELD &&
m_nRootType != ContentTypeId::POSTIT))
{
ContentTypeId eCntTypeId =
pField->GetTypeId() == SwFieldTypesEnum::Postit ? ContentTypeId::POSTIT :
ContentTypeId::TEXTFIELD; if (mTrackContentType[eCntTypeId])
lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, eCntTypeId, pField); return;
} if (SwPostItMgr* pPostItMgr = m_pActiveShell->GetPostItMgr();
pPostItMgr && pPostItMgr->HasActiveAnnotationWin()
&& !(m_bIsRoot && m_nRootType != ContentTypeId::POSTIT))
{ if (mTrackContentType[ContentTypeId::POSTIT])
{ if (const SwField* pField = pPostItMgr->GetActiveSidebarWin()->GetPostItField())
lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, ContentTypeId::POSTIT,
pField);
} return;
} // table if (m_pActiveShell->IsCursorInTable() &&
!(m_bIsRoot && m_nRootType != ContentTypeId::TABLE))
{ if (mTrackContentType[ContentTypeId::TABLE] && m_pActiveShell->GetTableFormat())
{
UIName aName = m_pActiveShell->GetTableFormat()->GetName();
lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_TABLE),
aName.toString()); return;
}
} // indexes if (const SwTOXBase* pTOX = m_pActiveShell->GetCurTOX(); pTOX &&
!(m_bIsRoot && m_nRootType != ContentTypeId::INDEX))
{ if (mTrackContentType[ContentTypeId::INDEX])
lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_INDEX),
pTOX->GetTOXName().toString()); return;
} // section if (const SwSection* pSection = m_pActiveShell->GetCurrSection(); pSection &&
!(m_bIsRoot && m_nRootType != ContentTypeId::REGION))
{ if (mTrackContentType[ContentTypeId::REGION])
{
lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_REGION),
pSection->GetSectionName().toString()); return;
} else
{ // prevent fall through to outline tracking when section tracking is off and the last // GotoContent is the current section if (m_nLastSelType == ContentTypeId::REGION &&
m_xTreeView->get_selected_text() == pSection->GetSectionName()) return;
} // fall through to outline tracking when section tracking is off and the last GotoContent // is not the current section
}
} // find out where the cursor is const SwOutlineNodes::size_type nActPos = GetWrtShell()->GetOutlinePos(MAXLEVEL); if (m_nOutlineTracking != 3
&& !((m_bIsRoot && m_nRootType != ContentTypeId::OUTLINE)
|| nActPos == SwOutlineNodes::npos))
{ // assure outline content type is expanded // this assumes outline content type is first in treeview
std::unique_ptr<weld::TreeIter> xFirstEntry(m_xTreeView->make_iterator()); if (m_xTreeView->get_iter_first(*xFirstEntry))
m_xTreeView->expand_row(*xFirstEntry);
m_xTreeView->all_foreach([this, nActPos](weld::TreeIter& rEntry){ bool bRet = false; if (lcl_IsContent(rEntry, *m_xTreeView) && weld::fromId<SwContent*>(
m_xTreeView->get_id(rEntry))->GetParent()->GetType() ==
ContentTypeId::OUTLINE)
{ if (weld::fromId<SwOutlineContent*>(
m_xTreeView->get_id(rEntry))->GetOutlinePos() == nActPos)
{
std::unique_ptr<weld::TreeIter> xFirstSelected(
m_xTreeView->make_iterator()); if (!m_xTreeView->get_selected(xFirstSelected.get()))
xFirstSelected.reset(); // only select if not already selected or tree has multiple entries selected if (m_xTreeView->count_selected_rows() != 1 || !xFirstSelected ||
m_xTreeView->iter_compare(rEntry, *xFirstSelected) != 0)
{ if (m_nOutlineTracking == 2) // focused outline tracking
{ // collapse to children of root node
std::unique_ptr<weld::TreeIter> xChildEntry(
m_xTreeView->make_iterator()); if (m_xTreeView->get_iter_first(*xChildEntry) &&
m_xTreeView->iter_children(*xChildEntry))
{ do
{ if (weld::fromId<SwContent*>(
m_xTreeView->get_id(*xChildEntry))->
GetParent()->GetType() == ContentTypeId::OUTLINE)
m_xTreeView->collapse_row(*xChildEntry); else break;
} while (m_xTreeView->iter_next(*xChildEntry));
}
} // unselect all entries, make pEntry visible, and select
m_xTreeView->set_cursor(rEntry);
UpdateContentFunctionsToolbar();
// tdf#149279 show at least two outline entries before the set cursor entry
std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator(&rEntry)); for (int i = 0; i < 2; i++)
{ if (m_xTreeView->get_iter_depth(*xIter) == 0) break; if (!m_xTreeView->iter_previous(*xIter)) break; while (!weld::IsEntryVisible(*m_xTreeView, *xIter))
m_xTreeView->iter_parent(*xIter);
} // Assure the scroll to row is collapsed after scrolling if it was collapsed // before. This is required here to make gtkinst scroll_to_row behave like // salinst. constbool bRowExpanded = m_xTreeView->get_row_expanded(*xIter);
m_xTreeView->scroll_to_row(*xIter); if (!bRowExpanded)
m_xTreeView->collapse_row(*xIter);
}
bRet = true;
}
} else
{ // use of this break assumes outline content type is first in tree if (lcl_IsContentType(rEntry, *m_xTreeView) &&
weld::fromId<SwContentType*>(
m_xTreeView->get_id(rEntry))->GetType() !=
ContentTypeId::OUTLINE)
bRet = true;
} return bRet;
}); return;
}
// bookmarks - track first bookmark at cursor // tdf#159428 Only when no outline found. Showing the outline is more important than // showing a bookmark at the cursor position. if (mTrackContentType[ContentTypeId::BOOKMARK] &&
(m_pActiveShell->GetSelectionType() & SelectionType::Text))
{
SwPaM* pCursor = m_pActiveShell->GetCursor();
IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess(); auto ppBookmark = pMarkAccess->getBookmarksBegin(); if (pCursor && ppBookmark != pMarkAccess->getBookmarksEnd()
&& !(m_bIsRoot && m_nRootType != ContentTypeId::BOOKMARK))
{
SwMarkName sBookmarkName;
SwPosition* pCursorPoint = pCursor->GetPoint(); while (ppBookmark != pMarkAccess->getBookmarksEnd())
{ if (lcl_IsUiVisibleBookmark(*ppBookmark)
&& *pCursorPoint >= (*ppBookmark)->GetMarkStart()
&& *pCursorPoint <= (*ppBookmark)->GetMarkEnd())
{
sBookmarkName = (*ppBookmark)->GetName(); // keep previously selected bookmark instead // of selecting a different bookmark inside of it if (sBookmarkName == m_sSelectedItem) break;
} elseif (!sBookmarkName.isEmpty() && *pCursorPoint < (*ppBookmark)->GetMarkStart())
{ // don't search a different bookmark inside the // previous one, if the starting position of the next bookmarks // is after the cursor position (assuming that the // bookmark iterator jumps inside the same text by positions) break;
}
++ppBookmark;
}
if (!sBookmarkName.isEmpty())
{ // select the bookmark
lcl_SelectByContentTypeAndName(this, *m_xTreeView,
SwResId(STR_CONTENT_TYPE_BOOKMARK), sBookmarkName.toString()); return;
}
}
}
// find content type entry bool bFoundEntry = rContentTree.get_iter_first(*xIter); while (bFoundEntry)
{
assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(rContentTree.get_id(*xIter))));
SwContentType* pContentType = weld::fromId<SwContentType*>(rContentTree.get_id(*xIter)); if (eContentType == pContentType->GetType()) break;
bFoundEntry = rContentTree.iter_next_sibling(*xIter);
}
if (!bFoundEntry) returnfalse;
// find content type content entry and compare it to the passed entry while (rContentTree.iter_next(*xIter))
{ if (rName == rContentTree.get_text(*xIter))
{ if (rContentTree.iter_compare(*xIter, rEntry) == 0) returntrue;
}
} returnfalse;
}
// 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;
} elseif (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;
} elseif (nSourcePos < nTargetPos)
{ // Move down
nSourcePos = nPrevSourcePos;
nTargetPos = nPrevTargetPosOrOffset;
}
}
GetParentWindow()->MoveOutline(nSourcePos, nTargetPos);
}
}
} for (const rtl::Reference<SdrObject>& pTemp : *pPage)
{ bool bMark = pDrawView->IsObjMarked(pTemp.get()); switch( pTemp->GetObjIdentifier() )
{ case SdrObjKind::Group: case SdrObjKind::Text: case SdrObjKind::Line: case SdrObjKind::Rectangle: case SdrObjKind::CircleOrEllipse: case SdrObjKind::CircleSection: case SdrObjKind::CircleArc: case SdrObjKind::CircleCut: case SdrObjKind::Polygon: case SdrObjKind::PolyLine: case SdrObjKind::PathLine: case SdrObjKind::PathFill: case SdrObjKind::FreehandLine: case SdrObjKind::FreehandFill: case SdrObjKind::PathPoly: case SdrObjKind::PathPolyLine: case SdrObjKind::Caption: case SdrObjKind::CustomShape: if( bMark )
hasObjectMarked = true; break; default: if ( bMark )
{
SdrPageView* pPV = pDrawView->GetSdrPageView/*GetPageViewPvNum*/(/*0*/); if (pPV)
{
pDrawView->MarkObj(pTemp.get(), 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 );
}
}
}
IMPL_LINK(SwContentTree, QueryTooltipHdl, const weld::TreeIter&, rEntry, OUString)
{ // Prevent tool tip handling when handling document change. The entry that was present when // the tooltip signal was fired might no longer be valid by the time it gets here. For example, // when the mouse pointer is on an entry in the tree that is deleted in the document by an // undo/redo. Please see similar note in MouseMoveHdl. if (m_bDocHasChanged) return OUString();
// tdf#163646 - Show in the tooltip for heading entries in Writer Navigator the // outline word and character count of the heading including the outline word and // character count of all sub headings int nEntryDepth = m_xTreeView->get_iter_depth(rEntry);
std::unique_ptr<weld::TreeIter> xIter = m_xTreeView->make_iterator(&rEntry); int nIterDepth; while (m_xTreeView->iter_next(*xIter)
&& (nIterDepth = m_xTreeView->get_iter_depth(*xIter)))
{ if (nIterDepth <= nEntryDepth)
{
pOutlineContent
= weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xIter));
pEndNode = rOutlineNodes[pOutlineContent->GetOutlinePos()]; break;
}
}
std::unique_ptr<weld::TreeIter> xFirst(m_xTreeView->make_iterator()); if (!m_xTreeView->get_selected(xFirst.get())) return; // this shouldn't happen, but better to be safe than ...
// 1. Get the position of the bit in the block where the value of the alphabetical sort // setting is persistently stored for the content type. constint nShift = static_cast<int>(pCntType->GetType());
assert(nShift > -1);
// 2. Create a bit mask to use to filter the sort value from the persistent block. const sal_Int32 nMask = 1 << nShift;
// 3. Toggle the persistent sort value only when it is different than the instance sort // value. These may already be the same if both the floating and sidebar version of the // Navigator are open. const sal_Int32 nBlock = m_pConfig->GetSortAlphabeticallyBlock(); bool bConfigSortValue = ~nBlock & nMask; bool bInstanceSortValue = pCntType->IsAlphabeticSort(); if (bConfigSortValue != bInstanceSortValue)
m_pConfig->SetSortAlphabeticallyBlock(nBlock ^ nMask);
// 4. Always toggle the instance value.
pCntType->SetAlphabeticSort(!bInstanceSortValue);
void SwContentTree::DeleteOutlineSelections()
{ const SwOutlineNodes& rOutlineNodes = m_pActiveShell->GetNodes().GetOutLineNds(); auto nChapters(0);
m_pActiveShell->StartAction();
m_pActiveShell->EnterAddMode();
m_xTreeView->selected_foreach([this, &rOutlineNodes, &nChapters](weld::TreeIter& rEntry){
++nChapters; if (m_xTreeView->iter_has_child(rEntry) &&
!m_xTreeView->get_row_expanded(rEntry)) // only count children if not expanded
{
nChapters += m_xTreeView->iter_n_children(rEntry);
}
SwOutlineNodes::size_type nActPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rEntry))->GetOutlinePos(); if (m_pActiveShell->GetViewOptions()->IsShowOutlineContentVisibilityButton())
{ // make folded content visible so it can be selected if (!m_pActiveShell->IsOutlineContentVisible(nActPos))
m_pActiveShell->MakeOutlineContentVisible(nActPos); if (!m_xTreeView->get_row_expanded(rEntry))
{ // include children
SwNode* pNode = rOutlineNodes[nActPos]; constint nLevel = pNode->GetTextNode()->GetAttrOutlineLevel() - 1; for (auto nPos = nActPos + 1; nPos < rOutlineNodes.size(); ++nPos)
{
pNode = rOutlineNodes[nPos]; constint nNextLevel = pNode->GetTextNode()->GetAttrOutlineLevel() - 1; if (nNextLevel <= nLevel) break; if (!m_pActiveShell->IsOutlineContentVisible(nNextLevel))
m_pActiveShell->MakeOutlineContentVisible(nNextLevel);
}
}
}
m_pActiveShell->SttSelect();
m_pActiveShell->MakeOutlineSel(nActPos, nActPos, !m_xTreeView->get_row_expanded(rEntry), false); // select children if not expanded // The outline selection may already be to the start of the following outline paragraph // as happens when a table is the last content of the to be deleted outline. In this case // do not extend the to be deleted selection right or the first character of the following // outline paragraph will be removed. Also check if no selection was made which indicates // an empty paragraph and selection right is needed. if (!m_pActiveShell->IsSttPara() || !m_pActiveShell->HasSelection())
m_pActiveShell->Right(SwCursorSkipMode::Chars, true, 1, false);
m_pActiveShell->EndSelect(); returnfalse;
});
m_pActiveShell->LeaveAddMode();
// Mode Change: Show active view // only called from IMPL_LINK(SwNavigationPI, DocListBoxSelectHdl, weld::ComboBox&, rBox, void) void SwContentTree::ShowActualView()
{ if (SwView* pView = m_pDialog->GetCreateView())
{
SetConstantShell(pView->GetWrtShellPtr());
m_pDialog->UpdateListBox();
}
}
IMPL_LINK_NOARG(SwContentTree, SelectHdl, weld::TreeView&, void)
{ if (m_pConfig->IsNavigateOnSelect())
{
ContentDoubleClickHdl(*m_xTreeView);
grab_focus();
}
UpdateContentFunctionsToolbar(); if (m_bIsRoot) return; // Select the content type in the Navigate By control
std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); if (!m_xTreeView->get_selected(xEntry.get())) return; while (m_xTreeView->get_iter_depth(*xEntry))
m_xTreeView->iter_parent(*xEntry);
m_pDialog->SelectNavigateByContentType(m_xTreeView->get_text(*xEntry));
}
void SwContentTree::CopyOutlineSelections()
{
m_pActiveShell->LockView(true);
{
MakeAllOutlineContentTemporarilyVisible a(m_pActiveShell->GetDoc());
m_pActiveShell->AssureStdMode();
m_pActiveShell->EnterAddMode();
size_t nCount = m_xTreeView->get_selected_rows().size();
m_xTreeView->selected_foreach([this, &nCount](weld::TreeIter& rEntry){
SwOutlineNodes::size_type nOutlinePos = reinterpret_cast<SwOutlineContent*>(
m_xTreeView->get_id(rEntry).toInt64())->GetOutlinePos();
m_pActiveShell->SttSelect();
m_pActiveShell->MakeOutlineSel(nOutlinePos, nOutlinePos,
!m_xTreeView->get_row_expanded(rEntry), false); // don't move if this is the last selected outline or the cursor is at start of para if (--nCount && !m_pActiveShell->IsSttPara())
m_pActiveShell->Right(SwCursorSkipMode::Chars, true, 1, false);
m_pActiveShell->EndSelect(); returnfalse;
});
m_pActiveShell->LeaveAddMode();
m_pActiveShell->GetView().GetViewFrame().GetBindings().Execute(SID_COPY);
}
m_pActiveShell->LockView(false);
}
void SwContentTree::GotoContent(const SwContent* pCnt)
{ if (pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE)
{ // Maybe the outline node doesn't have a layout frame to go to. const SwOutlineNodes::size_type nPos = static_cast<const SwOutlineContent*>(pCnt)->GetOutlinePos(); const SwNodes& rNds = m_pActiveShell->GetDoc()->GetNodes();
SwTextNode* pTextNd = rNds.GetOutLineNds()[nPos]->GetTextNode(); if (!pTextNd->getLayoutFrame(m_pActiveShell->GetLayout())) return;
}
if (m_bSelectTo)
{ if (m_pActiveShell->IsCursorInTable() ||
(m_pActiveShell->GetCursor()->GetPoint()->nNode.GetIndex() <=
m_pActiveShell->GetDoc()->GetNodes().GetEndOfExtras().GetIndex()))
{
m_bSelectTo = false;
m_pActiveShell->GetView().GetEditWin().GrabFocus(); return;
}
}
std::optional<SwPosition> oPosition; if (m_bSelectTo)
oPosition.emplace(m_pActiveShell->GetCursor()->GetPoint()->nNode,
m_pActiveShell->GetCursor()->GetPoint()->nContent);
switch(m_nLastSelType = pCnt->GetParent()->GetType())
{ case ContentTypeId::TEXTFIELD:
{
m_pActiveShell->GotoFormatField(
*static_cast<const SwTextFieldContent*>(pCnt)->GetFormatField());
} break; case ContentTypeId::OUTLINE :
{ const SwOutlineNodes::size_type nPos = static_cast<const SwOutlineContent*>(pCnt)->GetOutlinePos();
m_pActiveShell->GotoOutline(nPos);
m_nLastGotoContentWasOutlinePos = nPos;
} break; case ContentTypeId::TABLE :
{
m_pActiveShell->GotoTable(UIName(pCnt->GetName()));
} break; case ContentTypeId::FRAME : case ContentTypeId::GRAPHIC : case ContentTypeId::OLE :
{
m_pActiveShell->GotoFly(UIName(pCnt->GetName()));
} break; case ContentTypeId::BOOKMARK:
{
m_pActiveShell->StartAction();
m_pActiveShell->GotoMark(SwMarkName(pCnt->GetName()));
m_pActiveShell->EndAction();
m_sSelectedItem = pCnt->GetName();
// If the hidden title of SwNavigatorPanel was emptied via UNO XPanel interface, // store the name of the selected bookmark there. This allows to query the // selected bookmark using UNO e.g. in add-ons, i.e. to disambiguate when // multiple bookmarks are there on the selected text range. // Note: this is a workaround because getDialog() of XPanel is not implemented // for SwNavigatorPanel.
rtl::Reference< SwXTextDocument > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
Reference<frame::XController2> xController( xModel->getCurrentController(), uno::UNO_QUERY); if ( !xController.is() ) break;
Reference<ui::XSidebarProvider> xSidebarProvider = xController->getSidebar(); if ( !xSidebarProvider.is() ) break;
Reference<ui::XDecks> xDecks = xSidebarProvider->getDecks(); if ( !xDecks.is() ) break;
if (!xDecks->hasByName(u"NavigatorDeck"_ustr)) break;
Reference<ui::XDeck> xDeck ( xDecks->getByName(u"NavigatorDeck"_ustr), uno::UNO_QUERY); if ( !xDeck.is() ) break;
Reference<ui::XPanels> xPanels = xDeck->getPanels(); if ( !xPanels.is() ) break;
if (xPanels->hasByName(u"SwNavigatorPanel"_ustr))
{
Reference<ui::XPanel> xPanel ( xPanels->getByName(u"SwNavigatorPanel"_ustr), uno::UNO_QUERY); if ( !xPanel.is() || !xPanel->getTitle().isEmpty() ) break;
xPanel->setTitle( 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(SwCursorSkipMode::Chars, false, 1, false);
}
} break; case ContentTypeId::REFERENCE:
{
m_pActiveShell->GotoRefMark(SwMarkName(pCnt->GetName()));
} break; case ContentTypeId::INDEX:
{ const UIName sName(pCnt->GetName()); if (!m_pActiveShell->GotoNextTOXBase(&sName))
m_pActiveShell->GotoPrevTOXBase(&sName);
} break; case ContentTypeId::POSTIT: if (SwFormatField const*const pField{static_cast<const SwPostItContent*>(pCnt)->GetPostIt()})
{
m_pActiveShell->GotoFormatField(*pField);
} break; case ContentTypeId::DRAWOBJECT:
{
m_pActiveShell->GotoDrawingObject(pCnt->GetName());
} break; case ContentTypeId::FOOTNOTE: case ContentTypeId::ENDNOTE:
{ const SwTextFootnote* pFootnote = static_cast<const SwTextFootnoteContent*>(pCnt)->GetTextFootnote(); if (!pFootnote) return;
m_pActiveShell->GotoFootnoteAnchor(*pFootnote);
} break; default: break;
}
if (m_bSelectTo)
{
m_pActiveShell->SttCursorMove(); while (m_pActiveShell->IsCursorInTable())
{
m_pActiveShell->MoveTable(GotoCurrTable, fnTableStart); if (!m_pActiveShell->Left(SwCursorSkipMode::Chars, false, 1, false)) break; // Table is at the beginning of the document. It can't be selected this way.
}
m_pActiveShell->EndCursorMove();
// Assure cursor is in visible view area. // (tdf#147041) Always show the navigated outline at the top of the visible view area. if (pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE ||
(!m_pActiveShell->IsCursorVisible() && !m_pActiveShell->IsFrameSelected() &&
!m_pActiveShell->GetSelectedObjCount()))
{
Point aPoint(rView.GetVisArea().getX(), m_pActiveShell->GetCursorDocPos().getY());
rView.SetVisArea(aPoint);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.