Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / uibase / utlui / content.cxx
blobcd6528752fefed8d013198142f61780af5d68ebc
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <comphelper/string.hxx>
21 #include <editeng/frmdiritem.hxx>
22 #include <svl/urlbmk.hxx>
23 #include <osl/thread.h>
24 #include <sal/log.hxx>
25 #include <tools/urlobj.hxx>
26 #include <sfx2/docfile.hxx>
27 #include <sfx2/dispatch.hxx>
28 #include <sfx2/event.hxx>
29 #include <sfx2/viewfrm.hxx>
30 #include <o3tl/enumrange.hxx>
31 #include <o3tl/sorted_vector.hxx>
32 #include <utility>
33 #include <vcl/commandevent.hxx>
34 #include <vcl/weldutils.hxx>
35 #include <sot/formats.hxx>
36 #include <o3tl/string_view.hxx>
37 #include <uiitems.hxx>
38 #include <fmtanchr.hxx>
39 #include <fmtinfmt.hxx>
40 #include <txtinet.hxx>
41 #include <fmtfld.hxx>
42 #include <swmodule.hxx>
43 #include <wrtsh.hxx>
44 #include <view.hxx>
45 #include <docsh.hxx>
46 #include <drawdoc.hxx>
47 #include <content.hxx>
48 #include <frmatr.hxx>
49 #include <frmfmt.hxx>
50 #include <fldbas.hxx>
51 #include <IMark.hxx>
52 #include <section.hxx>
53 #include <tox.hxx>
54 #include <navipi.hxx>
55 #include <navicont.hxx>
56 #include <navicfg.hxx>
57 #include <edtwin.hxx>
58 #include <doc.hxx>
59 #include <IDocumentSettingAccess.hxx>
60 #include <IDocumentDrawModelAccess.hxx>
61 #include <IDocumentOutlineNodes.hxx>
62 #include <unotxvw.hxx>
63 #include <cmdid.h>
64 #include <helpids.h>
65 #include <strings.hrc>
66 #include <com/sun/star/text/XTextSectionsSupplier.hpp>
67 #include <com/sun/star/text/XTextGraphicObjectsSupplier.hpp>
68 #include <com/sun/star/text/XTextTablesSupplier.hpp>
69 #include <com/sun/star/text/XDocumentIndexesSupplier.hpp>
70 #include <com/sun/star/text/XDocumentIndex.hpp>
71 #include <com/sun/star/text/XBookmarksSupplier.hpp>
72 #include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp>
73 #include <com/sun/star/text/XTextFramesSupplier.hpp>
74 #include <com/sun/star/ui/XSidebarProvider.hpp>
75 #include <com/sun/star/ui/XDecks.hpp>
76 #include <com/sun/star/ui/XDeck.hpp>
77 #include <com/sun/star/ui/XPanels.hpp>
78 #include <com/sun/star/ui/XPanel.hpp>
79 #include <svx/svdpage.hxx>
80 #include <svx/svdview.hxx>
81 #include <SwRewriter.hxx>
82 #include <hints.hxx>
83 #include <numrule.hxx>
84 #include <swundo.hxx>
85 #include <ndtxt.hxx>
86 #include <PostItMgr.hxx>
87 #include <postithelper.hxx>
89 #include <swabstdlg.hxx>
90 #include <bitmaps.hlst>
92 #include <AnnotationWin.hxx>
93 #include <memory>
95 #include <fmtcntnt.hxx>
96 #include <docstat.hxx>
98 #include <viewopt.hxx>
100 #include <IDocumentFieldsAccess.hxx>
101 #include <txtfld.hxx>
102 #include <fldmgr.hxx>
104 #include <frameformats.hxx>
106 #include <ftnidx.hxx>
107 #include <txtftn.hxx>
108 #include <fmtftn.hxx>
110 #include <txtannotationfld.hxx>
111 #include <txtfrm.hxx>
112 #include <txtrfmrk.hxx>
113 #include <svx/sdr/overlay/overlayselection.hxx>
114 #include <svx/sdr/overlay/overlayobject.hxx>
115 #include <svx/sdr/overlay/overlaymanager.hxx>
116 #include <svx/sdrpaintwindow.hxx>
117 #include <node2lay.hxx>
119 #define CTYPE_CNT 0
120 #define CTYPE_CTT 1
122 using namespace ::com::sun::star;
123 using namespace ::com::sun::star::text;
124 using namespace ::com::sun::star::uno;
125 using namespace ::com::sun::star::container;
127 namespace {
130 Symbolic name representations of numeric values used for the Outline Content Visibility popup
131 menu item ids. The numbers are chosen arbitrarily to not over overlap other menu item ids.
132 see: SwContentTree::ExecuteContextMenuAction, navigatorcontextmenu.ui
134 1512 toggle outline content visibility of the selected outline entry
135 1513 make the outline content of the selected outline entry and children not visible
136 1514 make the outline content of the selected entry and children visible
138 const sal_uInt32 TOGGLE_OUTLINE_CONTENT_VISIBILITY = 1512;
139 const sal_uInt32 HIDE_OUTLINE_CONTENT_VISIBILITY = 1513;
140 const sal_uInt32 SHOW_OUTLINE_CONTENT_VISIBILITY = 1514;
142 constexpr char NAVI_BOOKMARK_DELIM = '\x01';
146 class SwContentArr
147 : public o3tl::sorted_vector<std::unique_ptr<SwContent>, o3tl::less_uniqueptr_to<SwContent>,
148 o3tl::find_partialorder_ptrequals>
152 namespace
154 std::map<OUString, std::map<void*, bool>> lcl_DocOutLineExpandStateMap;
156 bool lcl_IsContent(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView)
158 return weld::fromId<const SwTypeNumber*>(rTreeView.get_id(rEntry))->GetTypeId() == CTYPE_CNT;
161 bool lcl_IsContentType(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView)
163 return weld::fromId<const SwTypeNumber*>(rTreeView.get_id(rEntry))->GetTypeId() == CTYPE_CTT;
166 bool lcl_IsLowerOutlineContent(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView, sal_uInt8 nLevel)
168 return weld::fromId<const SwOutlineContent*>(rTreeView.get_id(rEntry))->GetOutlineLevel() < nLevel;
171 bool lcl_FindShell(SwWrtShell const * pShell)
173 bool bFound = false;
174 SwView *pView = SwModule::GetFirstView();
175 while (pView)
177 if(pShell == &pView->GetWrtShell())
179 bFound = true;
180 break;
182 pView = SwModule::GetNextView(pView);
184 return bFound;
187 bool lcl_IsUiVisibleBookmark(const ::sw::mark::IMark* pMark)
189 return IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::BOOKMARK;
192 OUString lcl_GetFootnoteText(const SwTextFootnote& rTextFootnote)
194 SwNodeIndex aIdx(*rTextFootnote.GetStartNode(), 1);
195 SwContentNode* pCNd = aIdx.GetNode().GetTextNode();
196 if(!pCNd)
197 pCNd = aIdx.GetNodes().GoNext(&aIdx);
198 return pCNd->IsTextNode() ? static_cast<SwTextNode*>(pCNd)->GetText() : OUString();
202 // Content, contains names and reference at the content type.
204 SwContent::SwContent(const SwContentType* pCnt, OUString aName, double nYPos) :
205 SwTypeNumber(CTYPE_CNT),
206 m_pParent(pCnt),
207 m_sContentName(std::move(aName)),
208 m_nYPosition(nYPos),
209 m_bInvisible(false)
214 SwTypeNumber::~SwTypeNumber()
218 bool SwContent::IsProtect() const
220 return false;
223 bool SwTextFieldContent::IsProtect() const
225 return m_pFormatField->IsProtect();
228 bool SwPostItContent::IsProtect() const
230 return m_pField->IsProtect();
233 bool SwURLFieldContent::IsProtect() const
235 return m_pINetAttr->IsProtect();
238 SwGraphicContent::~SwGraphicContent()
242 SwTOXBaseContent::~SwTOXBaseContent()
246 const TranslateId STR_CONTENT_TYPE_ARY[] =
248 STR_CONTENT_TYPE_OUTLINE,
249 STR_CONTENT_TYPE_TABLE,
250 STR_CONTENT_TYPE_FRAME,
251 STR_CONTENT_TYPE_GRAPHIC,
252 STR_CONTENT_TYPE_OLE,
253 STR_CONTENT_TYPE_BOOKMARK,
254 STR_CONTENT_TYPE_REGION,
255 STR_CONTENT_TYPE_URLFIELD,
256 STR_CONTENT_TYPE_REFERENCE,
257 STR_CONTENT_TYPE_INDEX,
258 STR_CONTENT_TYPE_POSTIT,
259 STR_CONTENT_TYPE_DRAWOBJECT,
260 STR_CONTENT_TYPE_TEXTFIELD,
261 STR_CONTENT_TYPE_FOOTNOTE,
262 STR_CONTENT_TYPE_ENDNOTE
265 const TranslateId STR_CONTENT_TYPE_SINGLE_ARY[] =
267 STR_CONTENT_TYPE_SINGLE_OUTLINE,
268 STR_CONTENT_TYPE_SINGLE_TABLE,
269 STR_CONTENT_TYPE_SINGLE_FRAME,
270 STR_CONTENT_TYPE_SINGLE_GRAPHIC,
271 STR_CONTENT_TYPE_SINGLE_OLE,
272 STR_CONTENT_TYPE_SINGLE_BOOKMARK,
273 STR_CONTENT_TYPE_SINGLE_REGION,
274 STR_CONTENT_TYPE_SINGLE_URLFIELD,
275 STR_CONTENT_TYPE_SINGLE_REFERENCE,
276 STR_CONTENT_TYPE_SINGLE_INDEX,
277 STR_CONTENT_TYPE_SINGLE_POSTIT,
278 STR_CONTENT_TYPE_SINGLE_DRAWOBJECT,
279 STR_CONTENT_TYPE_SINGLE_TEXTFIELD,
280 STR_CONTENT_TYPE_SINGLE_FOOTNOTE,
281 STR_CONTENT_TYPE_SINGLE_ENDNOTE
284 namespace
286 bool checkVisibilityChanged(
287 const SwContentArr& rSwContentArrA,
288 const SwContentArr& rSwContentArrB)
290 if(rSwContentArrA.size() != rSwContentArrB.size())
292 return true;
295 for(size_t a(0); a < rSwContentArrA.size(); a++)
297 if(rSwContentArrA[a]->IsInvisible() != rSwContentArrB[a]->IsInvisible())
299 return true;
303 return false;
305 // Gets "YPos" for content, i.e. a number used to sort content members in Navigator's list
306 sal_Int32 getYPos(const SwNode& rNode)
308 SwNodeOffset nIndex = rNode.GetIndex();
309 if (rNode.GetNodes().GetEndOfExtras().GetIndex() >= nIndex)
311 // Not a node of BodyText
312 // Are we in a fly?
313 if (const auto pFlyFormat = rNode.GetFlyFormat())
315 // Get node index of anchor
316 if (SwNode* pAnchorNode = pFlyFormat->GetAnchor().GetAnchorNode())
318 return getYPos(*pAnchorNode);
322 return sal_Int32(nIndex);
324 } // end of anonymous namespace
326 SwContentType::SwContentType(SwWrtShell* pShell, ContentTypeId nType, sal_uInt8 nLevel) :
327 SwTypeNumber(CTYPE_CTT),
328 m_pWrtShell(pShell),
329 m_sContentTypeName(SwResId(STR_CONTENT_TYPE_ARY[static_cast<int>(nType)])),
330 m_sSingleContentTypeName(SwResId(STR_CONTENT_TYPE_SINGLE_ARY[static_cast<int>(nType)])),
331 m_nMemberCount(0),
332 m_nContentType(nType),
333 m_nOutlineLevel(nLevel),
334 m_bDataValid(false),
335 m_bEdit(false),
336 m_bDelete(true)
338 switch(m_nContentType)
340 case ContentTypeId::OUTLINE:
341 m_sTypeToken = "outline";
342 break;
343 case ContentTypeId::TABLE:
344 m_sTypeToken = "table";
345 m_bEdit = true;
346 break;
347 case ContentTypeId::FRAME:
348 m_sTypeToken = "frame";
349 m_bEdit = true;
350 break;
351 case ContentTypeId::GRAPHIC:
352 m_sTypeToken = "graphic";
353 m_bEdit = true;
354 break;
355 case ContentTypeId::OLE:
356 m_sTypeToken = "ole";
357 m_bEdit = true;
358 break;
359 case ContentTypeId::TEXTFIELD:
360 m_bEdit = true;
361 m_bDelete = true;
362 break;
363 case ContentTypeId::FOOTNOTE:
364 case ContentTypeId::ENDNOTE:
365 m_bEdit = true;
366 m_bDelete = false;
367 break;
368 case ContentTypeId::BOOKMARK:
370 const bool bProtectedBM = m_pWrtShell->getIDocumentSettingAccess().get(
371 DocumentSettingId::PROTECT_BOOKMARKS);
372 m_bEdit = true;
373 m_bDelete = !bProtectedBM;
375 break;
376 case ContentTypeId::REGION:
377 m_sTypeToken = "region";
378 m_bEdit = true;
379 m_bDelete = false;
380 break;
381 case ContentTypeId::INDEX:
382 m_bEdit = true;
383 m_bDelete = true;
384 break;
385 case ContentTypeId::REFERENCE:
386 m_bEdit = false;
387 m_bDelete = true;
388 break;
389 case ContentTypeId::URLFIELD:
390 m_bEdit = true;
391 m_bDelete = true;
392 break;
393 case ContentTypeId::POSTIT:
394 m_bEdit = true;
395 break;
396 case ContentTypeId::DRAWOBJECT:
397 m_sTypeToken = "drawingobject";
398 m_bEdit = true;
399 break;
400 default: break;
402 FillMemberList();
405 SwContentType::~SwContentType()
409 const SwContent* SwContentType::GetMember(size_t nIndex)
411 if(!m_bDataValid || !m_pMember)
413 FillMemberList();
415 if(nIndex < m_pMember->size())
416 return (*m_pMember)[nIndex].get();
418 return nullptr;
421 void SwContentType::Invalidate()
423 m_bDataValid = false;
426 void SwContentType::FillMemberList(bool* pbContentChanged)
428 std::unique_ptr<SwContentArr> pOldMember;
429 size_t nOldMemberCount = 0;
430 if(m_pMember && pbContentChanged)
432 pOldMember = std::move(m_pMember);
433 nOldMemberCount = pOldMember->size();
434 m_pMember.reset( new SwContentArr );
435 *pbContentChanged = false;
437 else if(!m_pMember)
438 m_pMember.reset( new SwContentArr );
439 else
440 m_pMember->clear();
441 switch(m_nContentType)
443 case ContentTypeId::OUTLINE :
445 const SwNodeOffset nEndOfExtrasIndex = m_pWrtShell->GetNodes().GetEndOfExtras().GetIndex();
446 // provide for up to 99999 outline nodes in frames to be sorted in document layout order
447 double nOutlinesInFramesIndexAdjustment = 0.00001;
448 const SwOutlineNodes& rOutlineNodes(m_pWrtShell->GetNodes().GetOutLineNds());
449 const size_t nOutlineCount = rOutlineNodes.size();
451 for (size_t i = 0; i < nOutlineCount; ++i)
453 SwTextNode* pNode = rOutlineNodes[i]->GetTextNode();
454 const sal_uInt8 nLevel = pNode->GetAttrOutlineLevel() - 1;
455 if (nLevel >= m_nOutlineLevel || !pNode->getLayoutFrame(m_pWrtShell->GetLayout()))
456 continue;
457 double nYPos = m_bAlphabeticSort ? 0 : static_cast<double>(getYPos(*pNode));
458 if (nEndOfExtrasIndex >= pNode->GetIndex() && pNode->GetFlyFormat())
460 nYPos += nOutlinesInFramesIndexAdjustment;
461 nOutlinesInFramesIndexAdjustment += 0.00001;
463 OUString aEntry(comphelper::string::stripStart(
464 m_pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText(
465 i, m_pWrtShell->GetLayout(), true, false, false), ' '));
466 aEntry = SwNavigationPI::CleanEntry(aEntry);
467 auto pCnt(std::make_unique<SwOutlineContent>(this, aEntry, i, nLevel,
468 m_pWrtShell->IsOutlineMovable(i), nYPos));
469 m_pMember->insert(std::move(pCnt));
472 // need to check level and equal entry number after creation due to possible outline
473 // nodes in frames, headers, footers
474 if (pOldMember)
476 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
477 if (pOldMember->size() != m_pMember->size())
479 *pbContentChanged = true;
480 break;
482 for (size_t i = 0; i < pOldMember->size(); i++)
484 if (static_cast<SwOutlineContent*>((*pOldMember)[i].get())->GetOutlineLevel() !=
485 static_cast<SwOutlineContent*>((*m_pMember)[i].get())->GetOutlineLevel())
487 *pbContentChanged = true;
488 break;
493 break;
494 case ContentTypeId::TABLE :
496 const size_t nCount = m_pWrtShell->GetTableFrameFormatCount(true);
497 const sw::TableFrameFormats* pFrameFormats = m_pWrtShell->GetDoc()->GetTableFrameFormats();
498 SwAutoFormatGetDocNode aGetHt(&m_pWrtShell->GetNodes());
499 for(size_t n = 0, i = 0; i < nCount + n; ++i)
501 const SwTableFormat& rTableFormat = *(*pFrameFormats)[i];
502 if (rTableFormat.GetInfo(aGetHt)) // skip deleted tables
504 n++;
505 continue;
507 tools::Long nYPos = 0;
508 if (!m_bAlphabeticSort)
510 if (SwTable* pTable = SwTable::FindTable(&rTableFormat))
511 nYPos = getYPos(*pTable->GetTableNode());
513 auto pCnt = std::make_unique<SwContent>(this, rTableFormat.GetName(), nYPos);
514 if(!rTableFormat.IsVisible())
515 pCnt->SetInvisible();
516 m_pMember->insert(std::move(pCnt));
519 if (pOldMember)
521 // need to check visibility (and equal entry number) after
522 // creation due to a sorted list being used here (before,
523 // entries with same index were compared already at creation
524 // time what worked before a sorted list was used)
525 *pbContentChanged = checkVisibilityChanged(
526 *pOldMember,
527 *m_pMember);
530 break;
531 case ContentTypeId::OLE :
532 case ContentTypeId::FRAME :
533 case ContentTypeId::GRAPHIC :
535 FlyCntType eType = FLYCNTTYPE_FRM;
536 if(m_nContentType == ContentTypeId::OLE)
537 eType = FLYCNTTYPE_OLE;
538 else if(m_nContentType == ContentTypeId::GRAPHIC)
539 eType = FLYCNTTYPE_GRF;
540 Point aNullPt;
541 size_t nCount = m_pWrtShell->GetFlyCount(eType, /*bIgnoreTextBoxes=*/true);
542 std::vector<SwFrameFormat const*> formats(m_pWrtShell->GetFlyFrameFormats(eType, /*bIgnoreTextBoxes=*/true));
543 SAL_WARN_IF(nCount != formats.size(), "sw.ui", "Count differs");
544 nCount = formats.size();
545 for (size_t i = 0; i < nCount; ++i)
547 SwFrameFormat const*const pFrameFormat = formats[i];
548 const OUString sFrameName = pFrameFormat->GetName();
550 SwContent* pCnt;
551 tools::Long nYPos =
552 m_bAlphabeticSort ? 0 : pFrameFormat->FindLayoutRect(false, &aNullPt).Top();
553 if(ContentTypeId::GRAPHIC == m_nContentType)
555 OUString sLink;
556 m_pWrtShell->GetGrfNms( &sLink, nullptr, static_cast<const SwFlyFrameFormat*>( pFrameFormat));
557 pCnt = new SwGraphicContent(this, sFrameName, INetURLObject::decode(sLink,
558 INetURLObject::DecodeMechanism::Unambiguous), nYPos);
560 else
562 pCnt = new SwContent(this, sFrameName, nYPos);
564 if(!pFrameFormat->IsVisible())
565 pCnt->SetInvisible();
566 m_pMember->insert(std::unique_ptr<SwContent>(pCnt));
569 if (pOldMember)
571 // need to check visibility (and equal entry number) after
572 // creation due to a sorted list being used here (before,
573 // entries with same index were compared already at creation
574 // time what worked before a sorted list was used)
575 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
576 *pbContentChanged = checkVisibilityChanged(
577 *pOldMember,
578 *m_pMember);
581 break;
582 case ContentTypeId::BOOKMARK:
584 tools::Long nYPos = 0;
585 IDocumentMarkAccess* const pMarkAccess = m_pWrtShell->getIDocumentMarkAccess();
586 for(IDocumentMarkAccess::const_iterator_t ppBookmark = pMarkAccess->getBookmarksBegin();
587 ppBookmark != pMarkAccess->getBookmarksEnd();
588 ++ppBookmark)
590 if(lcl_IsUiVisibleBookmark(*ppBookmark))
592 const OUString& rBkmName = (*ppBookmark)->GetName();
593 //nYPos from 0 -> text::Bookmarks will be sorted alphabetically
594 auto pCnt(std::make_unique<SwContent>(this, rBkmName,
595 m_bAlphabeticSort ? 0 : nYPos++));
596 m_pMember->insert(std::move(pCnt));
600 break;
601 case ContentTypeId::TEXTFIELD:
603 std::vector<SwTextField*> aArr;
604 const SwFieldTypes& rFieldTypes =
605 *m_pWrtShell->GetDoc()->getIDocumentFieldsAccess().GetFieldTypes();
606 const size_t nSize = rFieldTypes.size();
607 for (size_t i = 0; i < nSize; ++i)
609 const SwFieldType* pFieldType = rFieldTypes[i].get();
610 if (pFieldType->Which() == SwFieldIds::Postit)
611 continue;
612 std::vector<SwFormatField*> vFields;
613 pFieldType->GatherFields(vFields);
614 for (SwFormatField* pFormatField: vFields)
616 if (SwTextField* pTextField = pFormatField->GetTextField())
618 // fields in header footer don't behave well, skip them
619 if (m_pWrtShell->GetDoc()->IsInHeaderFooter(pTextField->GetTextNode()))
620 continue;
621 aArr.emplace_back(pTextField);
625 if (!m_bAlphabeticSort)
627 const SwNodeOffset nEndOfExtrasIndex = m_pWrtShell->GetNodes().GetEndOfExtras().GetIndex();
628 bool bHasEntryInFly = false;
630 // use stable sort array to list fields in document model order
631 std::stable_sort(aArr.begin(), aArr.end(),
632 [](const SwTextField* a, const SwTextField* b){
633 SwPosition aPos(a->GetTextNode(), a->GetStart());
634 SwPosition bPos(b->GetTextNode(), b->GetStart());
635 return aPos < bPos;});
637 // determine if there is a text field in a fly frame
638 for (SwTextField* pTextField : aArr)
640 if (!bHasEntryInFly)
642 if (nEndOfExtrasIndex >= pTextField->GetTextNode().GetIndex())
644 // Not a node of BodyText
645 // Are we in a fly?
646 if (pTextField->GetTextNode().GetFlyFormat())
648 bHasEntryInFly = true;
649 break;
655 // When there are fields in fly frames do an additional sort using the fly frame
656 // anchor position to place field entries in order of document layout appearance.
657 if (bHasEntryInFly)
659 std::stable_sort(aArr.begin(), aArr.end(),
660 [nEndOfExtrasIndex](const SwTextField* a, const SwTextField* b){
661 SwTextNode& aTextNode = a->GetTextNode();
662 SwTextNode& bTextNode = b->GetTextNode();
663 SwPosition aPos(aTextNode, a->GetStart());
664 SwPosition bPos(bTextNode, b->GetStart());
665 // use anchor position for entries that are located in flys
666 if (nEndOfExtrasIndex >= aTextNode.GetIndex())
667 if (auto pFlyFormat = aTextNode.GetFlyFormat())
668 if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor())
669 aPos = *pPos;
670 if (nEndOfExtrasIndex >= bTextNode.GetIndex())
671 if (auto pFlyFormat = bTextNode.GetFlyFormat())
672 if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor())
673 bPos = *pPos;
674 return aPos < bPos;});
677 std::vector<OUString> aDocumentStatisticsSubTypesList;
678 tools::Long nYPos = 0;
679 for (SwTextField* pTextField : aArr)
681 const SwField* pField = pTextField->GetFormatField().GetField();
682 OUString sExpandField = pField->ExpandField(true, m_pWrtShell->GetLayout());
683 if (!sExpandField.isEmpty())
684 sExpandField = u" - " + sExpandField;
685 OUString sText;
686 if (pField->GetTypeId() == SwFieldTypesEnum::DocumentStatistics)
688 if (aDocumentStatisticsSubTypesList.empty())
689 SwFieldMgr(m_pWrtShell).GetSubTypes(SwFieldTypesEnum::DocumentStatistics,
690 aDocumentStatisticsSubTypesList);
691 OUString sSubType;
692 if (pField->GetSubType() < aDocumentStatisticsSubTypesList.size())
693 sSubType = u" - " + aDocumentStatisticsSubTypesList[pField->GetSubType()];
694 sText = pField->GetDescription() + u" - " + pField->GetFieldName() + sSubType +
695 sExpandField;
697 else if (pField->GetTypeId() == SwFieldTypesEnum::GetRef)
699 assert(dynamic_cast<const SwGetRefField*>(pField));
700 const SwGetRefField* pRefField(static_cast<const SwGetRefField*>(pField));
701 if (pRefField->IsRefToHeadingCrossRefBookmark() ||
702 pRefField->IsRefToNumItemCrossRefBookmark())
704 OUString sExpandedTextOfReferencedTextNode =
705 pRefField->GetExpandedTextOfReferencedTextNode(
706 *m_pWrtShell->GetLayout());
707 if (sExpandedTextOfReferencedTextNode.getLength() > 80)
709 sExpandedTextOfReferencedTextNode = OUString::Concat(
710 sExpandedTextOfReferencedTextNode.subView(0, 80)) + u"...";
712 sText = pField->GetDescription() + u" - "
713 + sExpandedTextOfReferencedTextNode + sExpandField;
715 else
717 OUString sFieldSubTypeOrName;
718 auto nSubType = pField->GetSubType();
719 if (nSubType == REF_FOOTNOTE)
720 sFieldSubTypeOrName = SwResId(STR_FLDREF_FOOTNOTE);
721 else if (nSubType == REF_ENDNOTE)
722 sFieldSubTypeOrName = SwResId(STR_FLDREF_ENDNOTE);
723 else
724 sFieldSubTypeOrName = pField->GetFieldName();
725 sText = pField->GetDescription() + u" - " + sFieldSubTypeOrName
726 + sExpandField;
729 else
730 sText = pField->GetDescription() + u" - " + pField->GetFieldName()
731 + sExpandField;
732 auto pCnt(std::make_unique<SwTextFieldContent>(this, sText,
733 &pTextField->GetFormatField(),
734 m_bAlphabeticSort ? 0 : nYPos++));
735 if (!pTextField->GetTextNode().getLayoutFrame(m_pWrtShell->GetLayout()))
736 pCnt->SetInvisible();
737 m_pMember->insert(std::move(pCnt));
740 break;
741 // We will separate footnotes and endnotes here.
742 case ContentTypeId::FOOTNOTE:
743 case ContentTypeId::ENDNOTE:
745 const SwFootnoteIdxs& rFootnoteIdxs = m_pWrtShell->GetDoc()->GetFootnoteIdxs();
746 if (rFootnoteIdxs.size() == 0)
747 break;
748 // insert footnotes and endnotes
749 tools::Long nPos = 0;
750 for (const SwTextFootnote* pTextFootnote : rFootnoteIdxs)
752 if ((!pTextFootnote->GetFootnote().IsEndNote()
753 && m_nContentType == ContentTypeId::FOOTNOTE)
754 || (pTextFootnote->GetFootnote().IsEndNote()
755 && m_nContentType == ContentTypeId::ENDNOTE))
757 const SwFormatFootnote& rFormatFootnote = pTextFootnote->GetFootnote();
758 const OUString& sText
759 = rFormatFootnote.GetViewNumStr(*m_pWrtShell->GetDoc(),
760 m_pWrtShell->GetLayout(), true)
761 + " " + lcl_GetFootnoteText(*pTextFootnote);
762 auto pCnt(std::make_unique<SwTextFootnoteContent>(
763 this, sText, pTextFootnote, ++nPos));
764 if (!pTextFootnote->GetTextNode().getLayoutFrame(m_pWrtShell->GetLayout()))
765 pCnt->SetInvisible();
766 m_pMember->insert(std::move(pCnt));
770 break;
771 case ContentTypeId::REGION :
773 size_t nCount = m_pWrtShell->GetSectionFormatCount();
774 for (size_t i = 0; i < nCount; ++i)
776 const SwSectionFormat* pFormat = &m_pWrtShell->GetSectionFormat(i);
777 if (!pFormat->IsInNodesArr())
778 continue;
779 const SwSection* pSection = pFormat->GetSection();
780 if (SectionType eTmpType = pSection->GetType();
781 eTmpType == SectionType::ToxContent || eTmpType == SectionType::ToxHeader)
782 continue;
783 const SwNodeIndex* pNodeIndex = pFormat->GetContent().GetContentIdx();
784 if (pNodeIndex)
786 const OUString& sSectionName = pSection->GetSectionName();
788 sal_uInt8 nLevel = 0;
789 SwSectionFormat* pParentFormat = pFormat->GetParent();
790 while(pParentFormat)
792 nLevel++;
793 pParentFormat = pParentFormat->GetParent();
796 std::unique_ptr<SwContent> pCnt(new SwRegionContent(this, sSectionName,
797 nLevel, m_bAlphabeticSort ? 0 : getYPos(pNodeIndex->GetNode())));
798 if(!pFormat->IsVisible())
799 pCnt->SetInvisible();
800 m_pMember->insert(std::move(pCnt));
803 if (pOldMember)
805 // need to check visibility (and equal entry number) after
806 // creation due to a sorted list being used here (before,
807 // entries with same index were compared already at creation
808 // time what worked before a sorted list was used)
809 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
810 *pbContentChanged = checkVisibilityChanged(
811 *pOldMember,
812 *m_pMember);
816 break;
817 case ContentTypeId::REFERENCE:
819 std::vector<OUString> aRefMarks;
820 m_pWrtShell->GetRefMarks( &aRefMarks );
822 tools::Long nYPos = 0;
823 for (const auto& rRefMark : aRefMarks)
825 m_pMember->insert(std::make_unique<SwContent>(this, rRefMark,
826 m_bAlphabeticSort ? 0 : nYPos++));
829 break;
830 case ContentTypeId::URLFIELD:
832 SwGetINetAttrs aArr;
833 m_pWrtShell->GetINetAttrs(aArr, false);
835 if (m_bAlphabeticSort)
837 for (auto& r : aArr)
839 auto pCnt(std::make_unique<SwURLFieldContent>(this, r.sText, INetURLObject::decode(
840 r.rINetAttr.GetINetFormat().GetValue(),
841 INetURLObject::DecodeMechanism::Unambiguous),
842 &r.rINetAttr, 0));
843 m_pMember->insert(std::move(pCnt));
845 break;
848 // use stable sort array to list hyperlinks in document order
849 const SwNodeOffset nEndOfExtrasIndex = m_pWrtShell->GetNodes().GetEndOfExtras().GetIndex();
850 bool bHasEntryInFly = false;
851 std::vector<SwGetINetAttr*> aStableSortINetAttrsArray;
853 for (SwGetINetAttr& r : aArr)
855 aStableSortINetAttrsArray.emplace_back(&r);
856 if (!bHasEntryInFly)
858 if (nEndOfExtrasIndex >= r.rINetAttr.GetTextNode().GetIndex())
860 // Not a node of BodyText
861 // Are we in a fly?
862 if (r.rINetAttr.GetTextNode().GetFlyFormat())
863 bHasEntryInFly = true;
868 std::stable_sort(aStableSortINetAttrsArray.begin(), aStableSortINetAttrsArray.end(),
869 [](const SwGetINetAttr* a, const SwGetINetAttr* b){
870 SwPosition aSwPos(a->rINetAttr.GetTextNode(),
871 a->rINetAttr.GetStart());
872 SwPosition bSwPos(b->rINetAttr.GetTextNode(),
873 b->rINetAttr.GetStart());
874 return aSwPos < bSwPos;});
876 // When there are hyperlinks in text frames do an additional sort using the text frame
877 // anchor position to place entries in the order of document layout appearance.
878 if (bHasEntryInFly)
880 std::stable_sort(aStableSortINetAttrsArray.begin(), aStableSortINetAttrsArray.end(),
881 [nEndOfExtrasIndex](const SwGetINetAttr* a, const SwGetINetAttr* b){
882 const SwTextNode& aTextNode = a->rINetAttr.GetTextNode();
883 const SwTextNode& bTextNode = b->rINetAttr.GetTextNode();
884 SwPosition aPos(aTextNode, a->rINetAttr.GetStart());
885 SwPosition bPos(bTextNode, b->rINetAttr.GetStart());
886 // use anchor position for entries that are located in flys
887 if (nEndOfExtrasIndex >= aTextNode.GetIndex())
888 if (auto pFlyFormat = aTextNode.GetFlyFormat())
889 if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor())
890 aPos = *pPos;
891 if (nEndOfExtrasIndex >= bTextNode.GetIndex())
892 if (auto pFlyFormat = bTextNode.GetFlyFormat())
893 if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor())
894 bPos = *pPos;
895 return aPos < bPos;});
898 SwGetINetAttrs::size_type n = 0;
899 for (auto p : aStableSortINetAttrsArray)
901 auto pCnt = std::make_unique<SwURLFieldContent>(this, p->sText,
902 INetURLObject::decode(p->rINetAttr.GetINetFormat().GetValue(),
903 INetURLObject::DecodeMechanism::Unambiguous),
904 &p->rINetAttr, ++n);
905 m_pMember->insert(std::move(pCnt));
908 break;
909 case ContentTypeId::INDEX:
911 const sal_uInt16 nCount = m_pWrtShell->GetTOXCount();
913 for ( sal_uInt16 nTox = 0; nTox < nCount; nTox++ )
915 const SwTOXBase* pBase = m_pWrtShell->GetTOX( nTox );
916 OUString sTOXNm( pBase->GetTOXName() );
918 SwContent* pCnt = new SwTOXBaseContent(
919 this, sTOXNm, m_bAlphabeticSort ? 0 : nTox, *pBase);
921 if(pBase && !pBase->IsVisible())
922 pCnt->SetInvisible();
924 m_pMember->insert( std::unique_ptr<SwContent>(pCnt) );
925 const size_t nPos = m_pMember->size() - 1;
926 if (pOldMember)
928 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
929 if (!*pbContentChanged && nOldMemberCount > nPos &&
930 (*pOldMember)[nPos]->IsInvisible() != pCnt->IsInvisible())
931 *pbContentChanged = true;
935 break;
936 case ContentTypeId::POSTIT:
938 SwPostItMgr* aMgr = m_pWrtShell->GetView().GetPostItMgr();
939 if (aMgr)
941 tools::Long nYPos = 0;
942 for(SwPostItMgr::const_iterator i = aMgr->begin(); i != aMgr->end(); ++i)
944 if (const SwFormatField* pFormatField = dynamic_cast<const SwFormatField *>((*i)->GetBroadcaster())) // SwPostit
946 if (pFormatField->GetTextField() && pFormatField->IsFieldInDoc())
948 OUString sEntry = pFormatField->GetField()->GetPar2();
949 sEntry = RemoveNewline(sEntry);
950 std::unique_ptr<SwPostItContent> pCnt(new SwPostItContent(
951 this,
952 sEntry,
953 pFormatField,
954 nYPos));
955 if (!pFormatField->GetTextField()->GetTextNode().getLayoutFrame(
956 m_pWrtShell->GetLayout()))
957 pCnt->SetInvisible();
958 if (pOldMember)
960 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
961 if (!*pbContentChanged &&
962 nOldMemberCount > o3tl::make_unsigned(nYPos) &&
963 (*pOldMember)[nYPos]->IsInvisible() != pCnt->IsInvisible())
964 *pbContentChanged = true;
966 m_pMember->insert(std::move(pCnt));
967 nYPos++;
973 break;
974 case ContentTypeId::DRAWOBJECT:
976 IDocumentDrawModelAccess& rIDDMA = m_pWrtShell->getIDocumentDrawModelAccess();
977 SwDrawModel* pModel = rIDDMA.GetDrawModel();
978 if(pModel)
980 SdrPage* pPage = pModel->GetPage(0);
981 const size_t nCount = pPage->GetObjCount();
982 for( size_t i=0; i<nCount; ++i )
984 SdrObject* pTemp = pPage->GetObj(i);
985 // #i51726# - all drawing objects can be named now
986 if (!pTemp->GetName().isEmpty())
988 tools::Long nYPos = LONG_MIN;
989 const bool bIsVisible = rIDDMA.IsVisibleLayerId(pTemp->GetLayer());
990 if (bIsVisible)
991 nYPos = m_bAlphabeticSort ? 0 : pTemp->GetLogicRect().Top();
992 auto pCnt(std::make_unique<SwContent>(this, pTemp->GetName(), nYPos));
993 if (!bIsVisible)
994 pCnt->SetInvisible();
995 m_pMember->insert(std::move(pCnt));
999 if (pOldMember)
1001 // need to check visibility (and equal entry number) after
1002 // creation due to a sorted list being used here (before,
1003 // entries with same index were compared already at creation
1004 // time what worked before a sorted list was used)
1005 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
1006 *pbContentChanged = checkVisibilityChanged(
1007 *pOldMember,
1008 *m_pMember);
1012 break;
1013 default: break;
1015 m_nMemberCount = m_pMember->size();
1016 if (pOldMember)
1018 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
1019 if (!*pbContentChanged && pOldMember->size() != m_nMemberCount)
1020 *pbContentChanged = true;
1023 m_bDataValid = true;
1026 namespace {
1028 enum STR_CONTEXT_IDX
1030 IDX_STR_OUTLINE_LEVEL = 0,
1031 IDX_STR_DRAGMODE = 1,
1032 IDX_STR_HYPERLINK = 2,
1033 IDX_STR_LINK_REGION = 3,
1034 IDX_STR_COPY_REGION = 4,
1035 IDX_STR_DISPLAY = 5,
1036 IDX_STR_ACTIVE_VIEW = 6,
1037 IDX_STR_HIDDEN = 7,
1038 IDX_STR_ACTIVE = 8,
1039 IDX_STR_INACTIVE = 9,
1040 IDX_STR_EDIT_ENTRY = 10,
1041 IDX_STR_DELETE_ENTRY = 11,
1042 IDX_STR_SEND_OUTLINE_TO_CLIPBOARD_ENTRY = 12,
1043 IDX_STR_OUTLINE_TRACKING = 13,
1044 IDX_STR_OUTLINE_TRACKING_DEFAULT = 14,
1045 IDX_STR_OUTLINE_TRACKING_FOCUS = 15,
1046 IDX_STR_OUTLINE_TRACKING_OFF = 16
1051 const TranslateId STR_CONTEXT_ARY[] =
1053 STR_OUTLINE_LEVEL,
1054 STR_DRAGMODE,
1055 STR_HYPERLINK,
1056 STR_LINK_REGION,
1057 STR_COPY_REGION,
1058 STR_DISPLAY,
1059 STR_ACTIVE_VIEW,
1060 STR_HIDDEN,
1061 STR_ACTIVE,
1062 STR_INACTIVE,
1063 STR_EDIT_ENTRY,
1064 STR_DELETE_ENTRY,
1065 STR_SEND_OUTLINE_TO_CLIPBOARD_ENTRY,
1066 STR_OUTLINE_TRACKING,
1067 STR_OUTLINE_TRACKING_DEFAULT,
1068 STR_OUTLINE_TRACKING_FOCUS,
1069 STR_OUTLINE_TRACKING_OFF
1072 SwContentTree::SwContentTree(std::unique_ptr<weld::TreeView> xTreeView, SwNavigationPI* pDialog)
1073 : m_xTreeView(std::move(xTreeView))
1074 , m_aDropTargetHelper(*this)
1075 , m_pDialog(pDialog)
1076 , m_sSpace(OUString(" "))
1077 , m_aUpdTimer("SwContentTree m_aUpdTimer")
1078 , m_aOverlayObjectDelayTimer("SwContentTree m_aOverlayObjectDelayTimer")
1079 , m_sInvisible(SwResId(STR_INVISIBLE))
1080 , m_pHiddenShell(nullptr)
1081 , m_pActiveShell(nullptr)
1082 , m_pConfig(SW_MOD()->GetNavigationConfig())
1083 , m_nActiveBlock(0)
1084 , m_nHiddenBlock(0)
1085 , m_nEntryCount(0)
1086 , m_nRootType(ContentTypeId::UNKNOWN)
1087 , m_nLastSelType(ContentTypeId::UNKNOWN)
1088 , m_nOutlineLevel(MAXLEVEL)
1089 , m_eState(State::ACTIVE)
1090 , m_bIsRoot(false)
1091 , m_bIsIdleClear(false)
1092 , m_bIsLastReadOnly(false)
1093 , m_bIsOutlineMoveable(true)
1094 , m_bViewHasChanged(false)
1096 m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 30,
1097 m_xTreeView->get_text_height() * 14);
1099 m_xTreeView->set_help_id(HID_NAVIGATOR_TREELIST);
1101 m_xTreeView->connect_expanding(LINK(this, SwContentTree, ExpandHdl));
1102 m_xTreeView->connect_collapsing(LINK(this, SwContentTree, CollapseHdl));
1103 m_xTreeView->connect_row_activated(LINK(this, SwContentTree, ContentDoubleClickHdl));
1104 m_xTreeView->connect_changed(LINK(this, SwContentTree, SelectHdl));
1105 m_xTreeView->connect_focus_in(LINK(this, SwContentTree, FocusInHdl));
1106 m_xTreeView->connect_key_press(LINK(this, SwContentTree, KeyInputHdl));
1107 m_xTreeView->connect_popup_menu(LINK(this, SwContentTree, CommandHdl));
1108 m_xTreeView->connect_query_tooltip(LINK(this, SwContentTree, QueryTooltipHdl));
1109 m_xTreeView->connect_drag_begin(LINK(this, SwContentTree, DragBeginHdl));
1110 m_xTreeView->connect_mouse_move(LINK(this, SwContentTree, MouseMoveHdl));
1111 m_xTreeView->connect_mouse_press(LINK(this, SwContentTree, MousePressHdl));
1113 for (ContentTypeId i : o3tl::enumrange<ContentTypeId>())
1115 if (i != ContentTypeId::OUTLINE)
1116 mTrackContentType[i] = true;
1117 m_aActiveContentArr[i] = nullptr;
1118 m_aHiddenContentArr[i] = nullptr;
1120 for (int i = 0; i < CONTEXT_COUNT; ++i)
1122 m_aContextStrings[i] = SwResId(STR_CONTEXT_ARY[i]);
1124 m_nActiveBlock = m_pConfig->GetActiveBlock();
1126 // Restore outline headings expand state (same session persistence only)
1127 if (SwView* pView = GetActiveView(); pView && pView->GetDocShell())
1129 OUString sDocTitle = pView->GetDocShell()->GetTitle();
1130 if (lcl_DocOutLineExpandStateMap.find(sDocTitle) != lcl_DocOutLineExpandStateMap.end())
1131 mOutLineNodeMap = lcl_DocOutLineExpandStateMap[sDocTitle];
1134 m_aUpdTimer.SetInvokeHandler(LINK(this, SwContentTree, TimerUpdate));
1135 m_aUpdTimer.SetTimeout(1000);
1136 m_aOverlayObjectDelayTimer.SetInvokeHandler(LINK(this, SwContentTree, OverlayObjectDelayTimerHdl));
1137 m_aOverlayObjectDelayTimer.SetTimeout(500);
1140 SwContentTree::~SwContentTree()
1142 if (SwView* pView = GetActiveView(); pView && pView->GetDocShell())
1144 OUString sDocTitle = pView->GetDocShell()->GetTitle();
1145 lcl_DocOutLineExpandStateMap[sDocTitle] = mOutLineNodeMap;
1147 clear(); // If applicable erase content types previously.
1148 m_aUpdTimer.Stop();
1149 SetActiveShell(nullptr);
1152 IMPL_LINK(SwContentTree, MousePressHdl, const MouseEvent&, rMEvt, bool)
1154 m_bSelectTo = rMEvt.IsShift() && (m_pConfig->IsNavigateOnSelect() || rMEvt.GetClicks() == 2);
1155 return false;
1158 IMPL_LINK(SwContentTree, MouseMoveHdl, const MouseEvent&, rMEvt, bool)
1160 if (m_eState == State::HIDDEN)
1161 return false;
1162 if (std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
1163 m_xTreeView->get_dest_row_at_pos(rMEvt.GetPosPixel(), xEntry.get(), false, false) &&
1164 !rMEvt.IsLeaveWindow())
1166 if (!m_xOverlayCompareEntry)
1167 m_xOverlayCompareEntry.reset(m_xTreeView->make_iterator().release());
1168 else if (m_xTreeView->iter_compare(*xEntry, *m_xOverlayCompareEntry) == 0)
1169 return false; // The entry under the mouse has not changed.
1170 m_xTreeView->copy_iterator(*xEntry, *m_xOverlayCompareEntry);
1171 BringEntryToAttention(*xEntry);
1173 else
1175 if (m_xOverlayCompareEntry)
1176 m_xOverlayCompareEntry.reset();
1177 m_aOverlayObjectDelayTimer.Stop();
1178 if (m_xOverlayObject && m_xOverlayObject->getOverlayManager())
1180 m_xOverlayObject->getOverlayManager()->remove(*m_xOverlayObject);
1181 m_xOverlayObject.reset();
1184 return false;
1187 // Drag&Drop methods
1188 IMPL_LINK(SwContentTree, DragBeginHdl, bool&, rUnsetDragIcon, bool)
1190 rUnsetDragIcon = true;
1192 bool bDisallow = true;
1194 // don't allow if tree root is selected
1195 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
1196 bool bEntry = m_xTreeView->get_selected(xEntry.get());
1197 if (!bEntry || lcl_IsContentType(*xEntry, *m_xTreeView))
1199 return true; // disallow
1202 rtl::Reference<TransferDataContainer> xContainer = new TransferDataContainer;
1203 sal_Int8 nDragMode = DND_ACTION_COPYMOVE | DND_ACTION_LINK;
1205 if (FillTransferData(*xContainer, nDragMode))
1206 bDisallow = false;
1208 if (m_bIsRoot && m_nRootType == ContentTypeId::OUTLINE)
1210 // Only move drag entry and continuous selected siblings:
1211 m_aDndOutlinesSelected.clear();
1213 std::unique_ptr<weld::TreeIter> xScratch(m_xTreeView->make_iterator());
1215 // Find first selected of continuous siblings
1216 while (true)
1218 m_xTreeView->copy_iterator(*xEntry, *xScratch);
1219 if (!m_xTreeView->iter_previous_sibling(*xScratch))
1220 break;
1221 if (!m_xTreeView->is_selected(*xScratch))
1222 break;
1223 m_xTreeView->copy_iterator(*xScratch, *xEntry);
1225 // Record continuous selected siblings
1228 m_aDndOutlinesSelected.push_back(m_xTreeView->make_iterator(xEntry.get()));
1230 while (m_xTreeView->iter_next_sibling(*xEntry) && m_xTreeView->is_selected(*xEntry));
1231 bDisallow = false;
1234 if (!bDisallow)
1235 m_xTreeView->enable_drag_source(xContainer, nDragMode);
1236 return bDisallow;
1239 SwContentTreeDropTarget::SwContentTreeDropTarget(SwContentTree& rTreeView)
1240 : DropTargetHelper(rTreeView.get_widget().get_drop_target())
1241 , m_rTreeView(rTreeView)
1245 sal_Int8 SwContentTreeDropTarget::AcceptDrop(const AcceptDropEvent& rEvt)
1247 sal_Int8 nAccept = m_rTreeView.AcceptDrop(rEvt);
1249 if (nAccept != DND_ACTION_NONE)
1251 // to enable the autoscroll when we're close to the edges
1252 weld::TreeView& rWidget = m_rTreeView.get_widget();
1253 rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true);
1256 return nAccept;
1259 bool SwContentTree::IsInDrag() const
1261 return m_xTreeView->get_drag_source() == m_xTreeView.get();
1264 // QueryDrop will be executed in the navigator
1265 sal_Int8 SwContentTree::AcceptDrop(const AcceptDropEvent& rEvt)
1267 sal_Int8 nRet = DND_ACTION_NONE;
1268 if( m_bIsRoot )
1270 if( m_bIsOutlineMoveable )
1271 nRet = rEvt.mnAction;
1273 else if (!IsInDrag())
1274 nRet = GetParentWindow()->AcceptDrop();
1275 return nRet;
1278 // Drop will be executed in the navigator
1279 static void* lcl_GetOutlineKey(SwContentTree& rTree, SwOutlineContent const * pContent)
1281 void* key = nullptr;
1282 if (pContent)
1284 SwWrtShell* pShell = rTree.GetWrtShell();
1285 auto const nPos = pContent->GetOutlinePos();
1287 key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos ));
1289 return key;
1292 sal_Int8 SwContentTreeDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt)
1294 return m_rTreeView.ExecuteDrop(rEvt);
1297 sal_Int8 SwContentTree::ExecuteDrop(const ExecuteDropEvent& rEvt)
1299 std::unique_ptr<weld::TreeIter> xDropEntry(m_xTreeView->make_iterator());
1300 if (!m_xTreeView->get_dest_row_at_pos(rEvt.maPosPixel, xDropEntry.get(), true))
1301 xDropEntry.reset();
1303 if (m_nRootType == ContentTypeId::OUTLINE)
1305 if (xDropEntry && lcl_IsContent(*xDropEntry, *m_xTreeView))
1307 assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xDropEntry))));
1308 SwOutlineContent* pOutlineContent = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xDropEntry));
1309 assert(pOutlineContent);
1311 void* key = lcl_GetOutlineKey(*this, pOutlineContent);
1312 assert(key);
1313 if (!mOutLineNodeMap[key])
1315 while (m_xTreeView->iter_has_child(*xDropEntry))
1317 std::unique_ptr<weld::TreeIter> xChildEntry(m_xTreeView->make_iterator(xDropEntry.get()));
1318 bool bChildEntry = m_xTreeView->iter_children(*xChildEntry);
1319 while (bChildEntry)
1321 m_xTreeView->copy_iterator(*xChildEntry, *xDropEntry);
1322 bChildEntry = m_xTreeView->iter_next_sibling(*xChildEntry);
1328 SwOutlineNodes::size_type nTargetPos = 0;
1329 if (!xDropEntry)
1331 // dropped in blank space -> move to bottom
1332 nTargetPos = GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineNodesCount() - 1;
1334 else if (!lcl_IsContent(*xDropEntry, *m_xTreeView))
1336 // dropped on "heading" parent -> move to start
1337 nTargetPos = SwOutlineNodes::npos;
1339 else
1341 assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xDropEntry))));
1342 nTargetPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xDropEntry))->GetOutlinePos();
1345 if( MAXLEVEL > m_nOutlineLevel && // Not all layers are displayed.
1346 nTargetPos != SwOutlineNodes::npos)
1348 std::unique_ptr<weld::TreeIter> xNext(m_xTreeView->make_iterator(xDropEntry.get()));
1349 bool bNext = m_xTreeView->iter_next(*xNext);
1350 if (bNext)
1352 assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xNext))));
1353 nTargetPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xNext))->GetOutlinePos() - 1;
1355 else
1356 nTargetPos = GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineNodesCount() - 1;
1359 // remove the drop highlight before we change the contents of the tree so we don't
1360 // try and dereference a removed entry in post-processing drop
1361 m_xTreeView->unset_drag_dest_row();
1362 MoveOutline(nTargetPos);
1365 return IsInDrag() ? DND_ACTION_NONE : GetParentWindow()->ExecuteDrop(rEvt);
1368 namespace
1370 bool IsAllExpanded(const weld::TreeView& rContentTree, const weld::TreeIter& rEntry)
1372 if (!rContentTree.get_row_expanded(rEntry))
1373 return false;
1375 if (!rContentTree.iter_has_child(rEntry))
1376 return false;
1378 std::unique_ptr<weld::TreeIter> xChild(rContentTree.make_iterator(&rEntry));
1379 (void)rContentTree.iter_children(*xChild);
1383 if (rContentTree.iter_has_child(*xChild) || rContentTree.get_children_on_demand(*xChild))
1385 if (!IsAllExpanded(rContentTree, *xChild))
1386 return false;
1389 while (rContentTree.iter_next_sibling(*xChild));
1390 return true;
1393 void ExpandOrCollapseAll(weld::TreeView& rContentTree, weld::TreeIter& rEntry)
1395 bool bExpand = !IsAllExpanded(rContentTree, rEntry);
1396 bExpand ? rContentTree.expand_row(rEntry) : rContentTree.collapse_row(rEntry);
1397 int nRefDepth = rContentTree.get_iter_depth(rEntry);
1398 while (rContentTree.iter_next(rEntry) && rContentTree.get_iter_depth(rEntry) > nRefDepth)
1400 if (rContentTree.iter_has_child(rEntry))
1401 bExpand ? rContentTree.expand_row(rEntry) : rContentTree.collapse_row(rEntry);
1406 // Handler for Dragging and ContextMenu
1407 static bool lcl_InsertExpandCollapseAllItem(const weld::TreeView& rContentTree, const weld::TreeIter& rEntry, weld::Menu& rPop)
1409 if (rContentTree.iter_has_child(rEntry) || rContentTree.get_children_on_demand(rEntry))
1411 rPop.set_label(OUString::number(800), IsAllExpanded(rContentTree, rEntry) ? SwResId(STR_COLLAPSEALL) : SwResId(STR_EXPANDALL));
1412 return false;
1414 return true;
1417 static void lcl_SetOutlineContentEntriesSensitivities(SwContentTree* pThis, const weld::TreeView& rContentTree, const weld::TreeIter& rEntry, weld::Menu& rPop)
1419 rPop.set_sensitive(OUString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY), false);
1420 rPop.set_sensitive(OUString::number(HIDE_OUTLINE_CONTENT_VISIBILITY), false);
1421 rPop.set_sensitive(OUString::number(SHOW_OUTLINE_CONTENT_VISIBILITY), false);
1423 // todo: multi selection
1424 if (rContentTree.count_selected_rows() > 1)
1425 return;
1427 bool bIsRoot = lcl_IsContentType(rEntry, rContentTree);
1429 if (const SwWrtShell* pSh = pThis->GetActiveWrtShell())
1431 if (pSh->GetViewOptions()->IsTreatSubOutlineLevelsAsContent())
1433 if (!bIsRoot)
1434 rPop.set_sensitive(OUString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY), true);
1435 return;
1439 const SwNodes& rNodes = pThis->GetWrtShell()->GetNodes();
1440 const SwOutlineNodes& rOutlineNodes = rNodes.GetOutLineNds();
1441 size_t nOutlinePos = weld::GetAbsPos(rContentTree, rEntry);
1443 if (!bIsRoot)
1444 --nOutlinePos;
1446 if (nOutlinePos >= rOutlineNodes.size())
1447 return;
1449 int nFirstLevel = pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineLevel(nOutlinePos);
1451 // determine if any concerned outline node has content
1452 bool bHasContent(false);
1453 size_t nPos = nOutlinePos;
1454 SwNode* pSttNd = rOutlineNodes[nPos];
1455 SwNode* pEndNd = &rNodes.GetEndOfContent();
1456 if (rOutlineNodes.size() > nPos + 1)
1457 pEndNd = rOutlineNodes[nPos + 1];
1459 // selected
1460 SwNodeIndex aIdx(*pSttNd);
1461 if (rNodes.GoNext(&aIdx) != pEndNd)
1462 bHasContent = true;
1464 // descendants
1465 if (!bHasContent && (rContentTree.iter_has_child(rEntry) || rContentTree.get_children_on_demand(rEntry)))
1467 while (++nPos < rOutlineNodes.size() &&
1468 (bIsRoot || pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos) > nFirstLevel))
1470 pSttNd = rOutlineNodes[nPos];
1471 pEndNd = &rNodes.GetEndOfContent();
1472 if (rOutlineNodes.size() > nPos + 1)
1473 pEndNd = rOutlineNodes[nPos + 1];
1475 // test for content in outline node
1476 aIdx.Assign(*pSttNd);
1477 if (rNodes.GoNext(&aIdx) != pEndNd)
1479 bHasContent = true;
1480 break;
1485 if (!bHasContent)
1486 return; // no content in any of the concerned outline nodes
1489 // determine for subs if all are folded or unfolded or if they are mixed
1490 if (rContentTree.iter_has_child(rEntry) || rContentTree.get_children_on_demand(rEntry))
1492 // skip no content nodes
1493 // we know there is content from results above so this is presumably safe
1494 size_t nPos = nOutlinePos;
1495 while (true)
1497 SwNode* pSttNd = rOutlineNodes[nPos];
1498 SwNode* pEndNd = rOutlineNodes.back();
1499 if (!bIsRoot && rOutlineNodes.size() > nPos + 1)
1500 pEndNd = rOutlineNodes[nPos + 1];
1502 SwNodeIndex aIdx(*pSttNd);
1503 if (rNodes.GoNext(&aIdx) != pEndNd)
1504 break;
1505 nPos++;
1508 bool bHasFolded(!pThis->GetWrtShell()->IsOutlineContentVisible(nPos));
1509 bool bHasUnfolded(!bHasFolded);
1511 while ((++nPos < pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineNodesCount()) &&
1512 (bIsRoot || pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos) > nFirstLevel))
1515 SwNode* pSttNd = rOutlineNodes[nPos];
1516 SwNode* pEndNd = &rNodes.GetEndOfContent();
1517 if (rOutlineNodes.size() > nPos + 1)
1518 pEndNd = rOutlineNodes[nPos + 1];
1520 SwNodeIndex aIdx(*pSttNd);
1521 if (rNodes.GoNext(&aIdx) == pEndNd)
1522 continue; // skip if no content
1524 if (!pThis->GetWrtShell()->IsOutlineContentVisible(nPos))
1525 bHasFolded = true;
1526 else
1527 bHasUnfolded = true;
1529 if (bHasFolded && bHasUnfolded)
1530 break; // mixed so no need to continue
1533 rPop.set_sensitive(OUString::number(HIDE_OUTLINE_CONTENT_VISIBILITY), bHasUnfolded);
1534 rPop.set_sensitive(OUString::number(SHOW_OUTLINE_CONTENT_VISIBILITY), bHasFolded);
1537 rPop.set_sensitive(OUString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY), !bIsRoot);
1540 IMPL_LINK(SwContentTree, CommandHdl, const CommandEvent&, rCEvt, bool)
1542 if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
1543 return false;
1545 grab_focus();
1547 // select clicked entry or limit selection to root entry if needed
1548 if (std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
1549 rCEvt.IsMouseEvent() && m_xTreeView->get_dest_row_at_pos(
1550 rCEvt.GetMousePosPixel(), xEntry.get(), false))
1552 // if clicked entry is not currently selected then clear selections and select it
1553 if (!m_xTreeView->is_selected(*xEntry))
1554 m_xTreeView->set_cursor(*xEntry);
1555 // if root entry is selected then clear selections and select it
1556 else if (m_xTreeView->is_selected(0))
1557 m_xTreeView->set_cursor(0);
1560 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), "modules/swriter/ui/navigatorcontextmenu.ui"));
1561 std::unique_ptr<weld::Menu> xPop = xBuilder->weld_menu("navmenu");
1563 bool bOutline(false);
1564 std::unique_ptr<weld::Menu> xSubPop1 = xBuilder->weld_menu("outlinelevel");
1565 std::unique_ptr<weld::Menu> xSubPop2 = xBuilder->weld_menu("dragmodemenu");
1566 std::unique_ptr<weld::Menu> xSubPop3 = xBuilder->weld_menu("displaymenu");
1567 std::unique_ptr<weld::Menu> xSubPopOutlineTracking = xBuilder->weld_menu("outlinetracking");
1569 std::unique_ptr<weld::Menu> xSubPopOutlineContent = xBuilder->weld_menu("outlinecontent");
1571 xSubPopOutlineContent->append(OUString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY),
1572 SwResId(STR_OUTLINE_CONTENT_VISIBILITY_TOGGLE));
1573 xSubPopOutlineContent->append(OUString::number(HIDE_OUTLINE_CONTENT_VISIBILITY),
1574 SwResId(STR_OUTLINE_CONTENT_VISIBILITY_HIDE_ALL));
1575 xSubPopOutlineContent->append(OUString::number(SHOW_OUTLINE_CONTENT_VISIBILITY),
1576 SwResId(STR_OUTLINE_CONTENT_VISIBILITY_SHOW_ALL));
1578 for(int i = 1; i <= 3; ++i)
1579 xSubPopOutlineTracking->append_radio(OUString::number(i + 10), m_aContextStrings[IDX_STR_OUTLINE_TRACKING + i]);
1580 xSubPopOutlineTracking->set_active(OUString::number(10 + m_nOutlineTracking), true);
1582 for (int i = 1; i <= MAXLEVEL; ++i)
1583 xSubPop1->append_radio(OUString::number(i + 100), OUString::number(i));
1584 xSubPop1->set_active(OUString::number(100 + m_nOutlineLevel), true);
1586 for (int i=0; i < 3; ++i)
1587 xSubPop2->append_radio(OUString::number(i + 201), m_aContextStrings[IDX_STR_HYPERLINK + i]);
1588 xSubPop2->set_active(OUString::number(201 + static_cast<int>(GetParentWindow()->GetRegionDropMode())), true);
1590 // Insert the list of the open files
1592 sal_uInt16 nId = 301;
1593 SwView *pView = SwModule::GetFirstView();
1594 while (pView)
1596 OUString sInsert = pView->GetDocShell()->GetTitle() + " (" +
1597 m_aContextStrings[pView == GetActiveView() ? IDX_STR_ACTIVE :
1598 IDX_STR_INACTIVE] + ")";
1599 xSubPop3->append_radio(OUString::number(nId), sInsert);
1600 if (State::CONSTANT == m_eState && m_pActiveShell == &pView->GetWrtShell())
1601 xSubPop3->set_active(OUString::number(nId), true);
1602 pView = SwModule::GetNextView(pView);
1603 nId++;
1605 xSubPop3->append_radio(OUString::number(nId++), m_aContextStrings[IDX_STR_ACTIVE_VIEW]);
1606 if (m_pHiddenShell) // can have only one hidden shell
1608 OUString sHiddenEntry = m_pHiddenShell->GetView().GetDocShell()->GetTitle() +
1609 " (" +
1610 m_aContextStrings[IDX_STR_HIDDEN] +
1611 ")";
1612 xSubPop3->append_radio(OUString::number(nId), sHiddenEntry);
1614 if (State::ACTIVE == m_eState)
1615 xSubPop3->set_active(OUString::number(--nId), true);
1616 else if (State::HIDDEN == m_eState)
1617 xSubPop3->set_active(OUString::number(nId), true);
1620 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
1621 if (!m_xTreeView->get_selected(xEntry.get()))
1622 xEntry.reset();
1624 bool bRemoveGotoEntry = false;
1625 if (State::HIDDEN == m_eState || !xEntry || !lcl_IsContent(*xEntry, *m_xTreeView) ||
1626 weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->IsInvisible())
1627 bRemoveGotoEntry = true;
1629 bool bRemovePostItEntries = true;
1630 bool bRemoveIndexEntries = true;
1631 bool bRemoveCopyEntry = true;
1632 bool bRemoveEditEntry = true;
1633 bool bRemoveUnprotectEntry = true;
1634 bool bRemoveDeleteChapterEntry = true,
1635 bRemoveDeleteTableEntry = true,
1636 bRemoveDeleteFrameEntry = true,
1637 bRemoveDeleteImageEntry = true,
1638 bRemoveDeleteOLEObjectEntry = true,
1639 bRemoveDeleteBookmarkEntry = true,
1640 bRemoveDeleteHyperlinkEntry = true,
1641 bRemoveDeleteReferenceEntry = true,
1642 bRemoveDeleteIndexEntry= true,
1643 bRemoveDeleteCommentEntry = true,
1644 bRemoveDeleteDrawingObjectEntry = true,
1645 bRemoveDeleteFieldEntry = true;
1646 bool bRemoveRenameEntry = true;
1647 bool bRemoveSelectEntry = true;
1648 bool bRemoveToggleExpandEntry = true;
1649 bool bRemoveChapterEntries = true;
1650 bool bRemoveSendOutlineEntry = true;
1652 bool bRemoveTableTracking = true;
1653 bool bRemoveSectionTracking = true;
1654 bool bRemoveFrameTracking = true;
1655 bool bRemoveImageTracking = true;
1656 bool bRemoveOLEobjectTracking = true;
1657 bool bRemoveBookmarkTracking = true;
1658 bool bRemoveHyperlinkTracking = true;
1659 bool bRemoveReferenceTracking = true;
1660 bool bRemoveIndexTracking = true;
1661 bool bRemoveCommentTracking = true;
1662 bool bRemoveDrawingObjectTracking = true;
1663 bool bRemoveFieldTracking = true;
1664 bool bRemoveFootnoteTracking = true;
1665 bool bRemoveEndnoteTracking = true;
1667 bool bRemoveSortEntry = true;
1669 if (xEntry)
1671 const SwContentType* pType;
1672 if (lcl_IsContentType(*xEntry, *m_xTreeView))
1673 pType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry));
1674 else
1675 pType = weld::fromId<SwContent*>(
1676 m_xTreeView->get_id(*xEntry))->GetParent();
1677 const ContentTypeId nContentType = pType->GetType();
1679 if (nContentType != ContentTypeId::FOOTNOTE && nContentType != ContentTypeId::ENDNOTE
1680 && nContentType != ContentTypeId::POSTIT)
1682 bRemoveSortEntry = false;
1683 xPop->set_active("sort", pType->GetSortType());
1686 OUString aIdent;
1687 switch (nContentType)
1689 case ContentTypeId::TABLE:
1690 aIdent = "tabletracking";
1691 bRemoveTableTracking = false;
1692 break;
1693 case ContentTypeId::REGION:
1694 aIdent = "sectiontracking";
1695 bRemoveSectionTracking = false;
1696 break;
1697 case ContentTypeId::FRAME:
1698 aIdent = "frametracking";
1699 bRemoveFrameTracking = false;
1700 break;
1701 case ContentTypeId::GRAPHIC:
1702 aIdent = "imagetracking";
1703 bRemoveImageTracking = false;
1704 break;
1705 case ContentTypeId::OLE:
1706 aIdent = "oleobjecttracking";
1707 bRemoveOLEobjectTracking = false;
1708 break;
1709 case ContentTypeId::BOOKMARK:
1710 aIdent = "bookmarktracking";
1711 bRemoveBookmarkTracking = false;
1712 break;
1713 case ContentTypeId::URLFIELD:
1714 aIdent = "hyperlinktracking";
1715 bRemoveHyperlinkTracking = false;
1716 break;
1717 case ContentTypeId::REFERENCE:
1718 aIdent = "referencetracking";
1719 bRemoveReferenceTracking = false;
1720 break;
1721 case ContentTypeId::INDEX:
1722 aIdent = "indextracking";
1723 bRemoveIndexTracking = false;
1724 break;
1725 case ContentTypeId::POSTIT:
1726 aIdent = "commenttracking";
1727 bRemoveCommentTracking = false;
1728 break;
1729 case ContentTypeId::DRAWOBJECT:
1730 aIdent = "drawingobjecttracking";
1731 bRemoveDrawingObjectTracking = false;
1732 break;
1733 case ContentTypeId::TEXTFIELD:
1734 aIdent = "fieldtracking";
1735 bRemoveFieldTracking = false;
1736 break;
1737 case ContentTypeId::FOOTNOTE:
1738 aIdent = "footnotetracking";
1739 bRemoveFootnoteTracking = false;
1740 break;
1741 case ContentTypeId::ENDNOTE:
1742 aIdent = "endnotetracking";
1743 bRemoveEndnoteTracking = false;
1744 break;
1745 default: break;
1747 if (!aIdent.isEmpty())
1748 xPop->set_active(aIdent, mTrackContentType[nContentType]);
1750 // Edit only if the shown content is coming from the current view.
1751 if (State::HIDDEN != m_eState &&
1752 (State::ACTIVE == m_eState || (GetActiveView() && m_pActiveShell == GetActiveView()->GetWrtShellPtr()))
1753 && lcl_IsContent(*xEntry, *m_xTreeView))
1755 const bool bReadonly = m_pActiveShell->GetView().GetDocShell()->IsReadOnly();
1756 const bool bVisible = !weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->IsInvisible();
1757 const bool bProtected = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->IsProtect();
1758 const bool bProtectBM = (ContentTypeId::BOOKMARK == nContentType)
1759 && m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS);
1760 const bool bEditable = pType->IsEditable() &&
1761 ((bVisible && !bProtected) || ContentTypeId::REGION == nContentType);
1762 const bool bDeletable = pType->IsDeletable() &&
1763 ((bVisible && !bProtected && !bProtectBM) || ContentTypeId::REGION == nContentType);
1764 const bool bRenamable = bEditable && !bReadonly &&
1765 (ContentTypeId::TABLE == nContentType ||
1766 ContentTypeId::FRAME == nContentType ||
1767 ContentTypeId::GRAPHIC == nContentType ||
1768 ContentTypeId::OLE == nContentType ||
1769 (ContentTypeId::BOOKMARK == nContentType && !bProtectBM) ||
1770 ContentTypeId::REGION == nContentType ||
1771 ContentTypeId::INDEX == nContentType ||
1772 ContentTypeId::DRAWOBJECT == nContentType);
1773 // Choose which Delete entry to show.
1774 if (bDeletable)
1776 switch (nContentType)
1778 case ContentTypeId::OUTLINE:
1779 bRemoveDeleteChapterEntry = false;
1780 break;
1781 case ContentTypeId::TABLE:
1782 bRemoveDeleteTableEntry = false;
1783 break;
1784 case ContentTypeId::FRAME:
1785 bRemoveDeleteFrameEntry = false;
1786 break;
1787 case ContentTypeId::GRAPHIC:
1788 bRemoveDeleteImageEntry = false;
1789 break;
1790 case ContentTypeId::OLE:
1791 bRemoveDeleteOLEObjectEntry = false;
1792 break;
1793 case ContentTypeId::BOOKMARK:
1794 bRemoveDeleteBookmarkEntry = false;
1795 break;
1796 case ContentTypeId::URLFIELD:
1797 bRemoveDeleteHyperlinkEntry = false;
1798 break;
1799 case ContentTypeId::REFERENCE:
1800 bRemoveDeleteReferenceEntry = false;
1801 break;
1802 case ContentTypeId::INDEX:
1803 bRemoveDeleteIndexEntry = false;
1804 break;
1805 case ContentTypeId::POSTIT:
1806 bRemoveDeleteCommentEntry = false;
1807 break;
1808 case ContentTypeId::DRAWOBJECT:
1809 bRemoveDeleteDrawingObjectEntry = false;
1810 break;
1811 case ContentTypeId::TEXTFIELD:
1812 bRemoveDeleteFieldEntry = false;
1813 break;
1814 default: break;
1817 if (ContentTypeId::FOOTNOTE == nContentType || ContentTypeId::ENDNOTE == nContentType)
1819 void* pUserData = weld::fromId<void*>(m_xTreeView->get_id(*xEntry));
1820 const SwTextFootnote* pFootnote =
1821 static_cast<const SwTextFootnoteContent*>(pUserData)->GetTextFootnote();
1822 if (!pFootnote)
1823 bRemoveGotoEntry = true;
1825 else if(ContentTypeId::OUTLINE == nContentType)
1827 bOutline = true;
1828 lcl_SetOutlineContentEntriesSensitivities(this, *m_xTreeView, *xEntry, *xSubPopOutlineContent);
1829 bRemoveToggleExpandEntry = lcl_InsertExpandCollapseAllItem(*m_xTreeView, *xEntry, *xPop);
1830 if (!bReadonly)
1832 bRemoveSelectEntry = false;
1833 bRemoveChapterEntries = false;
1835 bRemoveCopyEntry = false;
1837 else if (!bReadonly && bEditable)
1839 if(ContentTypeId::INDEX == nContentType)
1841 bRemoveIndexEntries = false;
1843 const SwTOXBase* pBase = weld::fromId<SwTOXBaseContent*>(m_xTreeView->get_id(*xEntry))->GetTOXBase();
1844 if (!pBase->IsTOXBaseInReadonly())
1845 bRemoveEditEntry = false;
1847 xPop->set_active(OUString::number(405), SwEditShell::IsTOXBaseReadonly(*pBase));
1849 else if(ContentTypeId::TABLE == nContentType)
1851 bRemoveSelectEntry = false;
1852 bRemoveEditEntry = false;
1853 bRemoveUnprotectEntry = false;
1854 bool bFull = false;
1855 OUString sTableName = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->GetName();
1856 bool bProt = m_pActiveShell->HasTableAnyProtection( &sTableName, &bFull );
1857 xPop->set_sensitive(OUString::number(403), !bFull);
1858 xPop->set_sensitive(OUString::number(404), bProt);
1860 else if(ContentTypeId::REGION == nContentType)
1862 bRemoveSelectEntry = false;
1863 bRemoveEditEntry = false;
1865 else if (bEditable)
1866 bRemoveEditEntry = false;
1867 //Rename object
1868 if (bRenamable)
1869 bRemoveRenameEntry = false;
1872 else
1874 if (lcl_IsContentType(*xEntry, *m_xTreeView))
1875 pType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry));
1876 else
1877 pType = weld::fromId<SwContent*>(
1878 m_xTreeView->get_id(*xEntry))->GetParent();
1879 if (pType)
1881 if (ContentTypeId::OUTLINE == nContentType)
1883 bOutline = true;
1884 if (State::HIDDEN != m_eState)
1886 lcl_SetOutlineContentEntriesSensitivities(this, *m_xTreeView, *xEntry,
1887 *xSubPopOutlineContent);
1888 bRemoveSendOutlineEntry = false;
1890 bRemoveToggleExpandEntry = lcl_InsertExpandCollapseAllItem(*m_xTreeView, *xEntry,
1891 *xPop);
1893 else if (State::HIDDEN != m_eState &&
1894 nContentType == ContentTypeId::POSTIT &&
1895 !m_pActiveShell->GetView().GetDocShell()->IsReadOnly() &&
1896 pType->GetMemberCount() > 0)
1897 bRemovePostItEntries = false;
1902 if (bRemoveToggleExpandEntry)
1903 xPop->remove(OUString::number(800));
1905 if (bRemoveGotoEntry)
1906 xPop->remove(OUString::number(900));
1908 if (bRemoveSelectEntry)
1909 xPop->remove(OUString::number(805));
1911 if (bRemoveChapterEntries)
1913 xPop->remove(OUString::number(801));
1914 xPop->remove(OUString::number(802));
1915 xPop->remove(OUString::number(803));
1916 xPop->remove(OUString::number(804));
1919 if (bRemoveSendOutlineEntry)
1920 xPop->remove(OUString::number(700));
1922 if (bRemovePostItEntries)
1924 xPop->remove(OUString::number(600));
1925 xPop->remove(OUString::number(601));
1926 xPop->remove(OUString::number(602));
1929 if (bRemoveDeleteChapterEntry)
1930 xPop->remove("deletechapter");
1931 if (bRemoveDeleteTableEntry)
1932 xPop->remove("deletetable");
1933 if (bRemoveDeleteFrameEntry)
1934 xPop->remove("deleteframe");
1935 if (bRemoveDeleteImageEntry)
1936 xPop->remove("deleteimage");
1937 if (bRemoveDeleteOLEObjectEntry)
1938 xPop->remove("deleteoleobject");
1939 if (bRemoveDeleteBookmarkEntry)
1940 xPop->remove("deletebookmark");
1941 if (bRemoveDeleteHyperlinkEntry)
1942 xPop->remove("deletehyperlink");
1943 if (bRemoveDeleteReferenceEntry)
1944 xPop->remove("deletereference");
1945 if (bRemoveDeleteIndexEntry)
1946 xPop->remove("deleteindex");
1947 if (bRemoveDeleteCommentEntry)
1948 xPop->remove("deletecomment");
1949 if (bRemoveDeleteDrawingObjectEntry)
1950 xPop->remove("deletedrawingobject");
1951 if (bRemoveDeleteFieldEntry)
1952 xPop->remove("deletefield");
1954 bool bRemoveDeleteEntry =
1955 bRemoveDeleteChapterEntry &&
1956 bRemoveDeleteTableEntry &&
1957 bRemoveDeleteFrameEntry &&
1958 bRemoveDeleteImageEntry &&
1959 bRemoveDeleteOLEObjectEntry &&
1960 bRemoveDeleteBookmarkEntry &&
1961 bRemoveDeleteHyperlinkEntry &&
1962 bRemoveDeleteReferenceEntry &&
1963 bRemoveDeleteIndexEntry &&
1964 bRemoveDeleteCommentEntry &&
1965 bRemoveDeleteDrawingObjectEntry &&
1966 bRemoveDeleteFieldEntry;
1968 if (bRemoveRenameEntry)
1969 xPop->remove(OUString::number(502));
1971 if (bRemoveIndexEntries)
1973 xPop->remove(OUString::number(401));
1974 xPop->remove(OUString::number(402));
1975 xPop->remove(OUString::number(405));
1978 if (bRemoveUnprotectEntry)
1979 xPop->remove(OUString::number(404));
1981 if (bRemoveEditEntry)
1982 xPop->remove(OUString::number(403));
1984 if (bRemoveToggleExpandEntry &&
1985 bRemoveSendOutlineEntry)
1986 xPop->remove("separator1");
1988 if (bRemoveCopyEntry)
1989 xPop->remove("copy");
1991 if (bRemoveGotoEntry &&
1992 bRemoveCopyEntry &&
1993 bRemoveSelectEntry &&
1994 bRemoveDeleteEntry &&
1995 bRemoveChapterEntries &&
1996 bRemovePostItEntries &&
1997 bRemoveRenameEntry &&
1998 bRemoveIndexEntries &&
1999 bRemoveUnprotectEntry &&
2000 bRemoveEditEntry)
2001 xPop->remove("separator2");
2003 if (!bOutline)
2005 xSubPop1.reset();
2006 xPop->remove(OUString::number(1)); // outline level menu
2008 if (!bOutline || State::HIDDEN == m_eState)
2010 xSubPopOutlineTracking.reset();
2011 xPop->remove(OUString::number(4)); // outline tracking menu
2013 if (!bOutline || State::HIDDEN == m_eState ||
2014 !m_pActiveShell->GetViewOptions()->IsShowOutlineContentVisibilityButton() ||
2015 m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount() == 0)
2017 xSubPopOutlineContent.reset();
2018 xPop->remove(OUString::number(5)); // outline folding menu
2019 xPop->remove("separator3");
2022 if (bRemoveTableTracking)
2023 xPop->remove("tabletracking");
2024 if (bRemoveSectionTracking)
2025 xPop->remove("sectiontracking");
2026 if (bRemoveFrameTracking)
2027 xPop->remove("frametracking");
2028 if (bRemoveImageTracking)
2029 xPop->remove("imagetracking");
2030 if (bRemoveOLEobjectTracking)
2031 xPop->remove("oleobjecttracking");
2032 if (bRemoveBookmarkTracking)
2033 xPop->remove("bookmarktracking");
2034 if (bRemoveHyperlinkTracking)
2035 xPop->remove("hyperlinktracking");
2036 if (bRemoveReferenceTracking)
2037 xPop->remove("referencetracking");
2038 if (bRemoveIndexTracking)
2039 xPop->remove("indextracking");
2040 if (bRemoveCommentTracking)
2041 xPop->remove("commenttracking");
2042 if (bRemoveDrawingObjectTracking)
2043 xPop->remove("drawingobjecttracking");
2044 if (bRemoveFieldTracking)
2045 xPop->remove("fieldtracking");
2046 if (bRemoveFootnoteTracking)
2047 xPop->remove("footnotetracking");
2048 if (bRemoveEndnoteTracking)
2049 xPop->remove("endnotetracking");
2050 if (bRemoveSortEntry)
2051 xPop->remove("sort");
2053 bool bSetSensitiveCollapseAllCategories = false;
2054 if (!m_bIsRoot && xEntry)
2056 bool bEntry = m_xTreeView->get_iter_first(*xEntry);
2057 while (bEntry)
2059 if (m_xTreeView->get_row_expanded(*xEntry))
2061 bSetSensitiveCollapseAllCategories = true;
2062 break;
2064 bEntry = m_xTreeView->iter_next_sibling(*xEntry);
2067 xPop->set_sensitive("collapseallcategories", bSetSensitiveCollapseAllCategories);
2069 OUString sCommand = xPop->popup_at_rect(m_xTreeView.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)));
2070 if (!sCommand.isEmpty())
2071 ExecuteContextMenuAction(sCommand);
2073 return true;
2076 void SwContentTree::InsertContent(const weld::TreeIter& rParent)
2078 assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent))));
2079 SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent));
2080 bool bGraphic = pCntType->GetType() == ContentTypeId::GRAPHIC;
2081 bool bRegion = pCntType->GetType() == ContentTypeId::REGION;
2082 std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator();
2083 const size_t nCount = pCntType->GetMemberCount();
2084 for(size_t i = 0; i < nCount; ++i)
2086 const SwContent* pCnt = pCntType->GetMember(i);
2087 if (pCnt)
2089 OUString sEntry = pCnt->GetName();
2090 if (sEntry.isEmpty())
2091 sEntry = m_sSpace;
2092 OUString sId(weld::toId(pCnt));
2093 insert(&rParent, sEntry, sId, false, xChild.get());
2094 m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible());
2095 if (bGraphic && !static_cast<const SwGraphicContent*>(pCnt)->GetLink().isEmpty())
2096 m_xTreeView->set_image(*xChild, RID_BMP_NAVI_GRAPHIC_LINK);
2097 else if (bRegion)
2098 m_xTreeView->set_extra_row_indent(*xChild,
2099 static_cast<const SwRegionContent*>(pCnt)->GetRegionLevel());
2104 void SwContentTree::insert(const weld::TreeIter* pParent, const OUString& rStr, const OUString& rId,
2105 bool bChildrenOnDemand, weld::TreeIter* pRet)
2107 m_xTreeView->insert(pParent, -1, &rStr, &rId, nullptr, nullptr, bChildrenOnDemand, pRet);
2108 ++m_nEntryCount;
2111 void SwContentTree::remove(const weld::TreeIter& rIter)
2113 if (m_xTreeView->iter_has_child(rIter))
2115 std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator(&rIter);
2116 (void)m_xTreeView->iter_children(*xChild);
2117 remove(*xChild);
2119 m_xTreeView->remove(rIter);
2120 --m_nEntryCount;
2123 // Content will be integrated into the Box only on demand.
2124 bool SwContentTree::RequestingChildren(const weld::TreeIter& rParent)
2126 // Does the parent already have children or is it not a 'children on demand' node?
2127 if (m_xTreeView->iter_has_child(rParent) || !m_xTreeView->get_children_on_demand(rParent))
2128 return false;
2130 // Is this a content type?
2131 if (lcl_IsContentType(rParent, *m_xTreeView))
2133 std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator();
2135 assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent))));
2136 SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent));
2138 const size_t nCount = pCntType->GetMemberCount();
2139 // Add for outline plus/minus
2140 if (pCntType->GetType() == ContentTypeId::OUTLINE)
2142 std::vector<std::unique_ptr<weld::TreeIter>> aParentCandidates;
2143 for(size_t i = 0; i < nCount; ++i)
2145 const SwContent* pCnt = pCntType->GetMember(i);
2146 if(pCnt)
2148 const auto nLevel = static_cast<const SwOutlineContent*>(pCnt)->GetOutlineLevel();
2149 OUString sEntry = pCnt->GetName();
2150 if(sEntry.isEmpty())
2151 sEntry = m_sSpace;
2152 OUString sId(weld::toId(pCnt));
2154 auto lambda = [nLevel, this](const std::unique_ptr<weld::TreeIter>& entry)
2156 return lcl_IsLowerOutlineContent(*entry, *m_xTreeView, nLevel);
2159 // if there is a preceding outline node candidate with a lower outline level use
2160 // that as a parent, otherwise use the root node
2161 auto aFind = std::find_if(aParentCandidates.rbegin(), aParentCandidates.rend(), lambda);
2162 if (aFind != aParentCandidates.rend())
2163 insert(aFind->get(), sEntry, sId, false, xChild.get());
2164 else
2165 insert(&rParent, sEntry, sId, false, xChild.get());
2166 m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible());
2167 m_xTreeView->set_extra_row_indent(*xChild, nLevel + 1 - m_xTreeView->get_iter_depth(*xChild));
2169 // remove any parent candidates equal to or higher than this node
2170 aParentCandidates.erase(std::remove_if(aParentCandidates.begin(), aParentCandidates.end(),
2171 std::not_fn(lambda)), aParentCandidates.end());
2173 // add this node as a parent candidate for any following nodes at a higher outline level
2174 aParentCandidates.emplace_back(m_xTreeView->make_iterator(xChild.get()));
2178 else
2179 InsertContent(rParent);
2181 return nCount != 0;
2184 return false;
2187 SdrObject* SwContentTree::GetDrawingObjectsByContent(const SwContent *pCnt)
2189 SdrObject *pRetObj = nullptr;
2190 switch(pCnt->GetParent()->GetType())
2192 case ContentTypeId::DRAWOBJECT:
2194 SdrView* pDrawView = m_pActiveShell->GetDrawView();
2195 if (pDrawView)
2197 SwDrawModel* pDrawModel = m_pActiveShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel();
2198 SdrPage* pPage = pDrawModel->GetPage(0);
2199 const size_t nCount = pPage->GetObjCount();
2201 for( size_t i=0; i<nCount; ++i )
2203 SdrObject* pTemp = pPage->GetObj(i);
2204 if( pTemp->GetName() == pCnt->GetName())
2206 pRetObj = pTemp;
2207 break;
2211 break;
2213 default:
2214 pRetObj = nullptr;
2216 return pRetObj;
2219 void SwContentTree::Expand(const weld::TreeIter& rParent, std::vector<std::unique_ptr<weld::TreeIter>>* pNodesToExpand)
2221 if (!(m_xTreeView->iter_has_child(rParent) || m_xTreeView->get_children_on_demand(rParent)))
2222 return;
2224 if (!m_bIsRoot
2225 || (lcl_IsContentType(rParent, *m_xTreeView) &&
2226 weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent))->GetType() == ContentTypeId::OUTLINE)
2227 || (m_nRootType == ContentTypeId::OUTLINE))
2229 if (lcl_IsContentType(rParent, *m_xTreeView))
2231 SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent));
2232 const sal_Int32 nOr = 1 << static_cast<int>(pCntType->GetType()); //linear -> Bitposition
2233 if (State::HIDDEN != m_eState)
2235 m_nActiveBlock |= nOr;
2236 m_pConfig->SetActiveBlock(m_nActiveBlock);
2238 else
2239 m_nHiddenBlock |= nOr;
2240 if (pCntType->GetType() == ContentTypeId::OUTLINE)
2242 std::map< void*, bool > aCurrOutLineNodeMap;
2244 SwWrtShell* pShell = GetWrtShell();
2245 bool bParentHasChild = RequestingChildren(rParent);
2246 if (pNodesToExpand)
2247 pNodesToExpand->emplace_back(m_xTreeView->make_iterator(&rParent));
2248 if (bParentHasChild)
2250 std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(&rParent));
2251 bool bChild = m_xTreeView->iter_next(*xChild);
2252 while (bChild && lcl_IsContent(*xChild, *m_xTreeView))
2254 if (m_xTreeView->iter_has_child(*xChild))
2256 assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xChild))));
2257 auto const nPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xChild))->GetOutlinePos();
2258 void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos ));
2259 aCurrOutLineNodeMap.emplace( key, false );
2260 std::map<void*, bool>::iterator iter = mOutLineNodeMap.find( key );
2261 if( iter != mOutLineNodeMap.end() && mOutLineNodeMap[key])
2263 aCurrOutLineNodeMap[key] = true;
2264 RequestingChildren(*xChild);
2265 if (pNodesToExpand)
2266 pNodesToExpand->emplace_back(m_xTreeView->make_iterator(xChild.get()));
2267 m_xTreeView->set_children_on_demand(*xChild, false);
2270 bChild = m_xTreeView->iter_next(*xChild);
2273 mOutLineNodeMap = aCurrOutLineNodeMap;
2274 return;
2277 else
2279 if (lcl_IsContent(rParent, *m_xTreeView))
2281 SwWrtShell* pShell = GetWrtShell();
2282 // paranoid assert now that outline type is checked
2283 assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent))));
2284 auto const nPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rParent))->GetOutlinePos();
2285 void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos ));
2286 mOutLineNodeMap[key] = true;
2291 RequestingChildren(rParent);
2292 if (pNodesToExpand)
2293 pNodesToExpand->emplace_back(m_xTreeView->make_iterator(&rParent));
2296 IMPL_LINK(SwContentTree, ExpandHdl, const weld::TreeIter&, rParent, bool)
2298 Expand(rParent, nullptr);
2299 return true;
2302 IMPL_LINK(SwContentTree, CollapseHdl, const weld::TreeIter&, rParent, bool)
2304 if (!m_xTreeView->iter_has_child(rParent) || m_xTreeView->get_children_on_demand(rParent))
2305 return true;
2307 if (lcl_IsContentType(rParent, *m_xTreeView))
2309 if (m_bIsRoot)
2311 // collapse to children of root node
2312 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(&rParent));
2313 if (m_xTreeView->iter_children(*xEntry))
2317 m_xTreeView->collapse_row(*xEntry);
2319 while (m_xTreeView->iter_next(*xEntry));
2321 return false; // return false to notify caller not to do collapse
2323 SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent));
2324 const sal_Int32 nAnd = ~(1 << static_cast<int>(pCntType->GetType()));
2325 if (State::HIDDEN != m_eState)
2327 m_nActiveBlock &= nAnd;
2328 m_pConfig->SetActiveBlock(m_nActiveBlock);
2330 else
2331 m_nHiddenBlock &= nAnd;
2333 else if (lcl_IsContent(rParent, *m_xTreeView))
2335 SwWrtShell* pShell = GetWrtShell();
2336 assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent))));
2337 auto const nPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rParent))->GetOutlinePos();
2338 void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos ));
2339 mOutLineNodeMap[key] = false;
2342 return true;
2345 // Also on double click will be initially opened only.
2346 IMPL_LINK_NOARG(SwContentTree, ContentDoubleClickHdl, weld::TreeView&, bool)
2348 if (m_nRowActivateEventId)
2349 Application::RemoveUserEvent(m_nRowActivateEventId);
2350 // post the event to process row activate after mouse press event to be able to set key
2351 // modifier for selection feature (tdf#154211)
2352 m_nRowActivateEventId
2353 = Application::PostUserEvent(LINK(this, SwContentTree, AsyncContentDoubleClickHdl));
2355 bool bConsumed = false;
2357 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
2358 if (m_xTreeView->get_cursor(xEntry.get()) && lcl_IsContent(*xEntry, *m_xTreeView) &&
2359 (State::HIDDEN != m_eState))
2361 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry));
2362 assert(pCnt && "no UserData");
2363 if (pCnt && !pCnt->IsInvisible())
2365 // fdo#36308 don't expand outlines on double-click
2366 bConsumed = pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE;
2370 return bConsumed; // false/true == allow/disallow more to be done, i.e. expand/collapse children
2373 IMPL_LINK_NOARG(SwContentTree, AsyncContentDoubleClickHdl, void*, void)
2375 m_nRowActivateEventId = nullptr;
2377 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
2378 bool bEntry = m_xTreeView->get_cursor(xEntry.get());
2379 // Is it a content type?
2380 OSL_ENSURE(bEntry, "no current entry!");
2381 if (bEntry)
2383 if (lcl_IsContentType(*xEntry, *m_xTreeView) && !m_xTreeView->iter_has_child(*xEntry))
2385 RequestingChildren(*xEntry);
2386 m_xTreeView->set_children_on_demand(*xEntry, false);
2388 else if (!lcl_IsContentType(*xEntry, *m_xTreeView) && (State::HIDDEN != m_eState))
2390 assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
2391 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry));
2392 assert(pCnt && "no UserData");
2393 if (pCnt && !pCnt->IsInvisible())
2395 if (State::CONSTANT == m_eState)
2397 m_pActiveShell->GetView().GetViewFrame().GetWindow().ToTop();
2399 //Jump to content type:
2400 GotoContent(pCnt);
2406 namespace
2408 OUString GetImageIdForContentTypeId(ContentTypeId eType)
2410 OUString sResId;
2412 switch (eType)
2414 case ContentTypeId::OUTLINE:
2415 sResId = RID_BMP_NAVI_OUTLINE;
2416 break;
2417 case ContentTypeId::TABLE:
2418 sResId = RID_BMP_NAVI_TABLE;
2419 break;
2420 case ContentTypeId::FRAME:
2421 sResId = RID_BMP_NAVI_FRAME;
2422 break;
2423 case ContentTypeId::GRAPHIC:
2424 sResId = RID_BMP_NAVI_GRAPHIC;
2425 break;
2426 case ContentTypeId::OLE:
2427 sResId = RID_BMP_NAVI_OLE;
2428 break;
2429 case ContentTypeId::BOOKMARK:
2430 sResId = RID_BMP_NAVI_BOOKMARK;
2431 break;
2432 case ContentTypeId::REGION:
2433 sResId = RID_BMP_NAVI_REGION;
2434 break;
2435 case ContentTypeId::URLFIELD:
2436 sResId = RID_BMP_NAVI_URLFIELD;
2437 break;
2438 case ContentTypeId::REFERENCE:
2439 sResId = RID_BMP_NAVI_REFERENCE;
2440 break;
2441 case ContentTypeId::INDEX:
2442 sResId = RID_BMP_NAVI_INDEX;
2443 break;
2444 case ContentTypeId::POSTIT:
2445 sResId = RID_BMP_NAVI_POSTIT;
2446 break;
2447 case ContentTypeId::DRAWOBJECT:
2448 sResId = RID_BMP_NAVI_DRAWOBJECT;
2449 break;
2450 case ContentTypeId::TEXTFIELD:
2451 sResId = RID_BMP_NAVI_TEXTFIELD;
2452 break;
2453 case ContentTypeId::FOOTNOTE:
2454 sResId = RID_BMP_NAVI_FOOTNOTE;
2455 break;
2456 case ContentTypeId::ENDNOTE:
2457 sResId = RID_BMP_NAVI_ENDNOTE;
2458 break;
2459 case ContentTypeId::UNKNOWN:
2460 SAL_WARN("sw.ui", "ContentTypeId::UNKNOWN has no bitmap preview");
2461 break;
2464 return sResId;
2468 size_t SwContentTree::GetAbsPos(const weld::TreeIter& rIter)
2470 return weld::GetAbsPos(*m_xTreeView, rIter);
2473 size_t SwContentTree::GetEntryCount() const
2475 return m_nEntryCount;
2478 size_t SwContentTree::GetChildCount(const weld::TreeIter& rParent) const
2480 if (!m_xTreeView->iter_has_child(rParent))
2481 return 0;
2483 std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rParent));
2485 size_t nCount = 0;
2486 auto nRefDepth = m_xTreeView->get_iter_depth(*xParent);
2487 auto nActDepth = nRefDepth;
2490 if (!m_xTreeView->iter_next(*xParent))
2491 xParent.reset();
2492 else
2493 nActDepth = m_xTreeView->get_iter_depth(*xParent);
2494 nCount++;
2495 } while(xParent && nRefDepth < nActDepth);
2497 nCount--;
2498 return nCount;
2501 std::unique_ptr<weld::TreeIter> SwContentTree::GetEntryAtAbsPos(size_t nAbsPos) const
2503 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
2504 if (!m_xTreeView->get_iter_first(*xEntry))
2505 xEntry.reset();
2507 while (nAbsPos && xEntry)
2509 if (!m_xTreeView->iter_next(*xEntry))
2510 xEntry.reset();
2511 nAbsPos--;
2513 return xEntry;
2516 void SwContentTree::Display( bool bActive )
2518 // First read the selected entry to select it later again if necessary
2519 // -> the user data here are no longer valid!
2520 std::unique_ptr<weld::TreeIter> xOldSelEntry(m_xTreeView->make_iterator());
2521 if (!m_xTreeView->get_selected(xOldSelEntry.get()))
2522 xOldSelEntry.reset();
2523 OUString sOldSelEntryId;
2524 size_t nEntryRelPos = 0; // relative position to their parent
2525 size_t nOldEntryCount = GetEntryCount();
2526 sal_Int32 nOldScrollPos = 0;
2527 if (xOldSelEntry)
2529 UpdateLastSelType();
2530 sOldSelEntryId = m_xTreeView->get_id(*xOldSelEntry);
2531 nOldScrollPos = m_xTreeView->vadjustment_get_value();
2532 std::unique_ptr<weld::TreeIter> xParentEntry = m_xTreeView->make_iterator(xOldSelEntry.get());
2533 while (m_xTreeView->get_iter_depth(*xParentEntry))
2534 m_xTreeView->iter_parent(*xParentEntry);
2535 if (m_xTreeView->get_iter_depth(*xOldSelEntry))
2536 nEntryRelPos = GetAbsPos(*xOldSelEntry) - GetAbsPos(*xParentEntry);
2539 clear();
2541 if (!bActive)
2543 m_aOverlayObjectDelayTimer.Stop();
2544 if (m_xOverlayObject && m_xOverlayObject->getOverlayManager())
2546 m_xOverlayObject->getOverlayManager()->remove(*m_xOverlayObject);
2547 m_xOverlayObject.reset();
2549 m_eState = State::HIDDEN;
2551 else if (State::HIDDEN == m_eState)
2552 m_eState = State::ACTIVE;
2553 SwWrtShell* pShell = GetWrtShell();
2554 const bool bReadOnly = !pShell || pShell->GetView().GetDocShell()->IsReadOnly();
2555 if(bReadOnly != m_bIsLastReadOnly)
2557 m_bIsLastReadOnly = bReadOnly;
2558 bool bDisable = pShell == nullptr || bReadOnly;
2559 SwNavigationPI* pNavi = GetParentWindow();
2560 pNavi->m_xContent6ToolBox->set_item_sensitive("chapterup", !bDisable);
2561 pNavi->m_xContent6ToolBox->set_item_sensitive("chapterdown", !bDisable);
2562 pNavi->m_xContent6ToolBox->set_item_sensitive("promote", !bDisable);
2563 pNavi->m_xContent6ToolBox->set_item_sensitive("demote", !bDisable);
2564 pNavi->m_xContent5ToolBox->set_item_sensitive("reminder", !bDisable);
2567 if (pShell)
2569 std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
2570 std::unique_ptr<weld::TreeIter> xCntTypeEntry;
2571 std::vector<std::unique_ptr<weld::TreeIter>> aNodesToExpand;
2572 // all content navigation view
2573 if(m_nRootType == ContentTypeId::UNKNOWN)
2575 m_xTreeView->freeze();
2577 for( ContentTypeId nCntType : o3tl::enumrange<ContentTypeId>() )
2579 std::unique_ptr<SwContentType>& rpContentT = bActive ?
2580 m_aActiveContentArr[nCntType] :
2581 m_aHiddenContentArr[nCntType];
2582 if(!rpContentT)
2583 rpContentT.reset(new SwContentType(pShell, nCntType, m_nOutlineLevel ));
2585 OUString aImage(GetImageIdForContentTypeId(nCntType));
2586 bool bChOnDemand = 0 != rpContentT->GetMemberCount();
2587 OUString sId(weld::toId(rpContentT.get()));
2588 insert(nullptr, rpContentT->GetName(), sId, bChOnDemand, xEntry.get());
2589 m_xTreeView->set_image(*xEntry, aImage);
2591 m_xTreeView->set_sensitive(*xEntry, bChOnDemand);
2593 if (nCntType == m_nLastSelType)
2594 xCntTypeEntry = m_xTreeView->make_iterator(xEntry.get());
2596 sal_Int32 nExpandOptions = (State::HIDDEN == m_eState)
2597 ? m_nHiddenBlock
2598 : m_nActiveBlock;
2599 if (nExpandOptions & (1 << static_cast<int>(nCntType)))
2601 // fill contents of to-be expanded entries while frozen
2602 Expand(*xEntry, &aNodesToExpand);
2603 m_xTreeView->set_children_on_demand(*xEntry, false);
2607 m_xTreeView->thaw();
2609 // restore visual expanded tree state
2610 for (const auto& rNode : aNodesToExpand)
2611 m_xTreeView->expand_row(*rNode);
2613 // root content navigation view
2614 else
2616 m_xTreeView->freeze();
2618 std::unique_ptr<SwContentType>& rpRootContentT = bActive ?
2619 m_aActiveContentArr[m_nRootType] :
2620 m_aHiddenContentArr[m_nRootType];
2621 if(!rpRootContentT)
2622 rpRootContentT.reset(new SwContentType(pShell, m_nRootType, m_nOutlineLevel ));
2623 OUString aImage(GetImageIdForContentTypeId(m_nRootType));
2624 bool bChOnDemand = m_nRootType == ContentTypeId::OUTLINE;
2625 OUString sId(weld::toId(rpRootContentT.get()));
2626 insert(nullptr, rpRootContentT->GetName(), sId, bChOnDemand, xEntry.get());
2627 m_xTreeView->set_image(*xEntry, aImage);
2629 xCntTypeEntry = m_xTreeView->make_iterator(xEntry.get());
2631 if (!bChOnDemand)
2632 InsertContent(*xEntry);
2633 else
2635 // fill contents of to-be expanded entries while frozen
2636 Expand(*xEntry, &aNodesToExpand);
2637 m_xTreeView->set_children_on_demand(*xEntry, false);
2640 m_xTreeView->set_sensitive(*xEntry, m_xTreeView->iter_has_child(*xEntry));
2642 m_xTreeView->thaw();
2644 if (bChOnDemand)
2646 // restore visual expanded tree state
2647 for (const auto& rNode : aNodesToExpand)
2648 m_xTreeView->expand_row(*rNode);
2650 else
2651 m_xTreeView->expand_row(*xEntry);
2654 // Reselect the old selected entry. If it is not available, select the entry at the old
2655 // selected entry position unless that entry position is now a content type or is past the
2656 // end of the member list then select the entry at the previous entry position.
2657 if (xOldSelEntry)
2659 std::unique_ptr<weld::TreeIter> xSelEntry = m_xTreeView->make_iterator(xCntTypeEntry.get());
2660 if (nEntryRelPos)
2662 std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator(xCntTypeEntry.get()));
2663 std::unique_ptr<weld::TreeIter> xTemp(m_xTreeView->make_iterator(xIter.get()));
2664 sal_uLong nPos = 1;
2665 bool bNext;
2666 while ((bNext = m_xTreeView->iter_next(*xIter) && lcl_IsContent(*xIter, *m_xTreeView)))
2668 if (m_xTreeView->get_id(*xIter) == sOldSelEntryId || nPos == nEntryRelPos)
2670 m_xTreeView->copy_iterator(*xIter, *xSelEntry);
2671 break;
2673 m_xTreeView->copy_iterator(*xIter, *xTemp); // note previous entry
2674 nPos++;
2676 if (!bNext)
2677 xSelEntry = std::move(xTemp);
2679 // set_cursor unselects all entries, makes passed entry visible, and selects it
2680 m_xTreeView->set_cursor(*xSelEntry);
2681 Select();
2685 if (!m_bIgnoreDocChange && GetEntryCount() == nOldEntryCount)
2687 m_xTreeView->vadjustment_set_value(nOldScrollPos);
2691 void SwContentTree::clear()
2693 m_xTreeView->freeze();
2694 m_xTreeView->clear();
2695 m_nEntryCount = 0;
2696 m_xTreeView->thaw();
2699 bool SwContentTree::FillTransferData( TransferDataContainer& rTransfer,
2700 sal_Int8& rDragMode )
2702 bool bRet = false;
2703 SwWrtShell* pWrtShell = GetWrtShell();
2704 OSL_ENSURE(pWrtShell, "no Shell!");
2706 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
2707 bool bEntry = m_xTreeView->get_cursor(xEntry.get());
2708 if (!bEntry || lcl_IsContentType(*xEntry, *m_xTreeView) || !pWrtShell)
2709 return false;
2710 OUString sEntry;
2711 assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
2712 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry));
2714 const ContentTypeId nActType = pCnt->GetParent()->GetType();
2715 OUString sUrl;
2716 bool bOutline = false;
2717 OUString sOutlineText;
2718 switch( nActType )
2720 case ContentTypeId::OUTLINE:
2722 const SwOutlineNodes::size_type nPos = static_cast<SwOutlineContent*>(pCnt)->GetOutlinePos();
2723 OSL_ENSURE(nPos < pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount(),
2724 "outlinecnt changed");
2726 // make sure outline may actually be copied
2727 if( pWrtShell->IsOutlineCopyable( nPos ) )
2729 const SwNumRule* pOutlRule = pWrtShell->GetOutlineNumRule();
2730 const SwTextNode* pTextNd =
2731 pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineNode(nPos);
2732 if (pTextNd && pOutlRule && pTextNd->IsNumbered(pWrtShell->GetLayout()))
2734 SwNumberTree::tNumberVector aNumVector =
2735 pTextNd->GetNumberVector(pWrtShell->GetLayout());
2736 for( int nLevel = 0;
2737 nLevel <= pTextNd->GetActualListLevel();
2738 nLevel++ )
2740 const SwNumberTree::tSwNumTreeNumber nVal = aNumVector[nLevel] + 1;
2741 sEntry += OUString::number( nVal - pOutlRule->Get(nLevel).GetStart() ) + ".";
2744 sEntry += pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText(nPos, pWrtShell->GetLayout(), false);
2745 sOutlineText = pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText(nPos, pWrtShell->GetLayout());
2746 m_bIsOutlineMoveable = static_cast<SwOutlineContent*>(pCnt)->IsMoveable();
2747 bOutline = true;
2750 break;
2751 case ContentTypeId::POSTIT:
2752 case ContentTypeId::INDEX:
2753 case ContentTypeId::REFERENCE :
2754 case ContentTypeId::TEXTFIELD:
2755 case ContentTypeId::FOOTNOTE:
2756 case ContentTypeId::ENDNOTE:
2757 // cannot be inserted, neither as URL nor as section
2758 break;
2759 case ContentTypeId::URLFIELD:
2760 sUrl = static_cast<SwURLFieldContent*>(pCnt)->GetURL();
2761 [[fallthrough]];
2762 case ContentTypeId::OLE:
2763 case ContentTypeId::GRAPHIC:
2764 if(GetParentWindow()->GetRegionDropMode() != RegionMode::NONE)
2765 break;
2766 else
2767 rDragMode &= ~( DND_ACTION_MOVE | DND_ACTION_LINK );
2768 [[fallthrough]];
2769 default:
2770 sEntry = m_xTreeView->get_text(*xEntry);
2773 if(!sEntry.isEmpty())
2775 const SwDocShell* pDocShell = pWrtShell->GetView().GetDocShell();
2776 if(sUrl.isEmpty())
2778 if(pDocShell->HasName())
2780 SfxMedium* pMedium = pDocShell->GetMedium();
2781 sUrl = pMedium->GetURLObject().GetURLNoMark();
2782 // only if a primarily link shall be integrated.
2783 bRet = true;
2785 else if ( nActType == ContentTypeId::REGION || nActType == ContentTypeId::BOOKMARK )
2787 // For field and bookmarks a link is also allowed
2788 // without a filename into its own document.
2789 bRet = true;
2791 else if (State::CONSTANT == m_eState &&
2792 ( !::GetActiveView() ||
2793 m_pActiveShell != ::GetActiveView()->GetWrtShellPtr()))
2795 // Urls of inactive views cannot dragged without
2796 // file names, also.
2797 bRet = false;
2799 else
2801 bRet = GetParentWindow()->GetRegionDropMode() == RegionMode::NONE;
2802 rDragMode = DND_ACTION_MOVE;
2805 const OUString& rToken = pCnt->GetParent()->GetTypeToken();
2806 sUrl += "#" + sEntry;
2807 if(!rToken.isEmpty())
2809 sUrl += OUStringChar(cMarkSeparator) + rToken;
2812 else
2813 bRet = true;
2815 if( bRet )
2817 // In Outlines of heading text must match
2818 // the real number into the description.
2819 if(bOutline)
2820 sEntry = sOutlineText;
2823 NaviContentBookmark aBmk( sUrl, sEntry,
2824 GetParentWindow()->GetRegionDropMode(),
2825 pDocShell);
2826 aBmk.Copy( rTransfer );
2829 // An INetBookmark must a be delivered to foreign DocShells
2830 if( pDocShell->HasName() )
2832 INetBookmark aBkmk( sUrl, sEntry );
2833 rTransfer.CopyINetBookmark( aBkmk );
2837 return bRet;
2840 void SwContentTree::ToggleToRoot()
2842 if(!m_bIsRoot)
2844 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
2845 bool bEntry = m_xTreeView->get_cursor(xEntry.get());
2846 if (bEntry)
2848 const SwContentType* pCntType;
2849 if (lcl_IsContentType(*xEntry, *m_xTreeView))
2851 assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
2852 pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry));
2854 else
2856 assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
2857 pCntType = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->GetParent();
2859 m_nRootType = pCntType->GetType();
2860 m_bIsRoot = true;
2861 if (m_nRootType == ContentTypeId::OUTLINE || m_nRootType == ContentTypeId::DRAWOBJECT)
2863 m_xTreeView->set_selection_mode(SelectionMode::Multiple);
2865 Display(State::HIDDEN != m_eState);
2868 else
2870 m_xTreeView->set_selection_mode(SelectionMode::Single);
2871 m_nLastSelType = m_nRootType;
2872 m_nRootType = ContentTypeId::UNKNOWN;
2873 m_bIsRoot = false;
2874 // Other content type member data could have changed while in root view. Fill the content
2875 // member lists excluding the toggled from root content which should already have the most
2876 // recent data.
2877 if (State::HIDDEN != m_eState)
2879 for (ContentTypeId i : o3tl::enumrange<ContentTypeId>())
2881 if (i != m_nLastSelType && m_aActiveContentArr[i])
2882 m_aActiveContentArr[i]->FillMemberList();
2885 Display(State::HIDDEN != m_eState);
2887 m_pConfig->SetRootType( m_nRootType );
2888 weld::Toolbar* pBox = GetParentWindow()->m_xContent5ToolBox.get();
2889 pBox->set_item_active("root", m_bIsRoot);
2892 bool SwContentTree::HasContentChanged()
2894 bool bContentChanged = false;
2896 // - Run through the local array and the Treelistbox in parallel.
2897 // - Are the records not expanded, they are discarded only in the array
2898 // and the content type will be set as the new UserData.
2899 // - Is the root mode is active only this will be updated.
2901 // Valid for the displayed content types is:
2902 // the Memberlist will be erased and the membercount will be updated
2903 // If content will be checked, the memberlists will be replenished
2904 // at the same time. Once a difference occurs it will be only replenished
2905 // no longer checked. Finally, the box is filled again.
2907 if (State::HIDDEN == m_eState)
2909 for(ContentTypeId i : o3tl::enumrange<ContentTypeId>())
2911 if(m_aActiveContentArr[i])
2912 m_aActiveContentArr[i]->Invalidate();
2914 return false;
2917 // root content navigation view
2918 if(m_bIsRoot)
2920 std::unique_ptr<weld::TreeIter> xRootEntry(m_xTreeView->make_iterator());
2921 if (!m_xTreeView->get_iter_first(*xRootEntry))
2922 return true;
2924 assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xRootEntry))));
2925 const ContentTypeId nType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xRootEntry))->GetType();
2926 SwContentType* pArrType = m_aActiveContentArr[nType].get();
2927 assert(weld::toId(pArrType) == m_xTreeView->get_id(*xRootEntry));
2928 if (!pArrType)
2929 return true;
2931 pArrType->FillMemberList(&bContentChanged);
2932 if (bContentChanged)
2933 return true;
2935 // FillMemberList tests if member count in old member array equals member count in new
2936 // member array. Test here for member count difference between array and tree.
2937 const size_t nChildCount = GetChildCount(*xRootEntry);
2938 if (nChildCount != pArrType->GetMemberCount())
2939 return true;
2941 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(xRootEntry.get()));
2942 for (size_t j = 0; j < nChildCount; ++j)
2944 if (!m_xTreeView->iter_next(*xEntry))
2946 SAL_WARN("sw.ui", "unexpected missing entry");
2947 return true;
2950 // FillMemberList clears the content type member list and refills with new data.
2951 // Treeview entry user data is set here to the string representation of the pointer to
2952 // the member data in the array. The Display function will clear and recreate the
2953 // treeview from the content type member arrays if content change is detected.
2954 const SwContent* pCnt = pArrType->GetMember(j);
2955 OUString sSubId(weld::toId(pCnt));
2956 m_xTreeView->set_id(*xEntry, sSubId);
2958 OUString sEntryText = m_xTreeView->get_text(*xEntry);
2959 if (sEntryText != pCnt->GetName() &&
2960 !(sEntryText == m_sSpace && pCnt->GetName().isEmpty()))
2962 return true;
2966 // all content navigation view
2967 else
2969 // Fill member list for each content type and check for content change. If content change
2970 // is detected only fill member lists for remaining content types. The Display function
2971 // will clear and recreate the treeview from the content type member arrays if content has
2972 // changed.
2973 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
2975 // lambda function to find the next content type entry
2976 auto lcl_nextContentTypeEntry = [this, &xEntry](){
2977 while (m_xTreeView->get_iter_depth(*xEntry))
2978 m_xTreeView->iter_parent(*xEntry);
2979 return m_xTreeView->iter_next_sibling(*xEntry);
2982 for (bool bEntry = m_xTreeView->get_iter_first(*xEntry); bEntry;
2983 bEntry = lcl_nextContentTypeEntry())
2985 assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
2986 SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry));
2987 const size_t nCntCount = pCntType->GetMemberCount();
2988 const ContentTypeId nType = pCntType->GetType();
2989 SwContentType* pArrType = m_aActiveContentArr[nType].get();
2990 assert(weld::toId(pArrType) == m_xTreeView->get_id(*xEntry));
2992 if (!pArrType)
2994 bContentChanged = true;
2995 continue;
2998 // all content type member lists must be filled!
2999 if (bContentChanged)
3001 // If content change has already been detected there is no need to detect
3002 // other content change so no argument is supplied here to FillMemberList.
3003 pArrType->FillMemberList();
3004 continue;
3007 pArrType->FillMemberList(&bContentChanged);
3008 if (bContentChanged)
3009 continue;
3011 // does entry have children?
3012 if (m_xTreeView->get_row_expanded(*xEntry))
3014 const size_t nChildCount = GetChildCount(*xEntry);
3015 if(nChildCount != pArrType->GetMemberCount())
3017 bContentChanged = true;
3018 continue;
3021 for(size_t j = 0; j < nChildCount; ++j)
3023 if (!m_xTreeView->iter_next(*xEntry))
3025 SAL_WARN("sw.ui", "unexpected missing entry");
3026 bContentChanged = true;
3027 continue;
3030 const SwContent* pCnt = pArrType->GetMember(j);
3031 OUString sSubId(weld::toId(pCnt));
3032 m_xTreeView->set_id(*xEntry, sSubId);
3034 OUString sEntryText = m_xTreeView->get_text(*xEntry);
3035 if( sEntryText != pCnt->GetName() &&
3036 !(sEntryText == m_sSpace && pCnt->GetName().isEmpty()))
3038 bContentChanged = true;
3039 continue;
3043 // not expanded and has children
3044 else if (m_xTreeView->iter_has_child(*xEntry))
3046 bool bRemoveChildren = false;
3047 const size_t nOldChildCount = GetChildCount(*xEntry);
3048 const size_t nNewChildCount = pArrType->GetMemberCount();
3049 if (nOldChildCount != nNewChildCount)
3051 bRemoveChildren = true;
3053 else
3055 std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(xEntry.get()));
3056 (void)m_xTreeView->iter_children(*xChild);
3057 for (size_t j = 0; j < nOldChildCount; ++j)
3059 const SwContent* pCnt = pArrType->GetMember(j);
3060 OUString sSubId(weld::toId(pCnt));
3061 m_xTreeView->set_id(*xChild, sSubId);
3062 OUString sEntryText = m_xTreeView->get_text(*xChild);
3063 if( sEntryText != pCnt->GetName() &&
3064 !(sEntryText == m_sSpace && pCnt->GetName().isEmpty()))
3066 bRemoveChildren = true;
3068 (void)m_xTreeView->iter_next(*xChild);
3071 if (bRemoveChildren)
3073 std::unique_ptr<weld::TreeIter> xRemove(m_xTreeView->make_iterator(xEntry.get()));
3074 while (m_xTreeView->iter_children(*xRemove))
3076 remove(*xRemove);
3077 m_xTreeView->copy_iterator(*xEntry, *xRemove);
3079 m_xTreeView->set_children_on_demand(*xEntry, nNewChildCount != 0);
3082 else if((nCntCount != 0)
3083 != (pArrType->GetMemberCount()!=0))
3085 bContentChanged = true;
3086 continue;
3091 return bContentChanged;
3094 void SwContentTree::UpdateLastSelType()
3096 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
3097 if (m_xTreeView->get_selected(xEntry.get()))
3099 while (m_xTreeView->get_iter_depth(*xEntry))
3100 m_xTreeView->iter_parent(*xEntry);
3101 void* pId = weld::fromId<void*>(m_xTreeView->get_id(*xEntry));
3102 if (pId && lcl_IsContentType(*xEntry, *m_xTreeView))
3104 assert(dynamic_cast<SwContentType*>(static_cast<SwTypeNumber*>(pId)));
3105 m_nLastSelType = static_cast<SwContentType*>(pId)->GetType();
3110 void SwContentTree::FindActiveTypeAndRemoveUserData()
3112 UpdateLastSelType();
3114 // If clear is called by TimerUpdate:
3115 // Only for root can the validity of the UserData be guaranteed.
3116 m_xTreeView->all_foreach([this](weld::TreeIter& rEntry){
3117 m_xTreeView->set_id(rEntry, "");
3118 return false;
3122 void SwContentTree::SetHiddenShell(SwWrtShell* pSh)
3124 m_pHiddenShell = pSh;
3125 m_eState = State::HIDDEN;
3126 FindActiveTypeAndRemoveUserData();
3127 for(ContentTypeId i : o3tl::enumrange<ContentTypeId>())
3129 m_aHiddenContentArr[i].reset();
3131 Display(false);
3133 GetParentWindow()->UpdateListBox();
3136 void SwContentTree::SetActiveShell(SwWrtShell* pSh)
3138 bool bClear = m_pActiveShell != pSh;
3139 if (State::ACTIVE == m_eState && bClear)
3141 EndListeningAll();
3142 m_pActiveShell = pSh;
3143 FindActiveTypeAndRemoveUserData();
3144 clear();
3146 else if (State::CONSTANT == m_eState)
3148 EndListeningAll();
3149 m_pActiveShell = pSh;
3150 m_eState = State::ACTIVE;
3151 bClear = true;
3154 // tdf#148432 in LTR UI override the navigator treeview direction based on
3155 // the first page directionality
3156 if (m_pActiveShell && !AllSettings::GetLayoutRTL())
3158 const SwPageDesc& rDesc = m_pActiveShell->GetPageDesc(0);
3159 const SvxFrameDirectionItem& rFrameDir = rDesc.GetMaster().GetFrameDir();
3160 m_xTreeView->set_direction(rFrameDir.GetValue() == SvxFrameDirection::Horizontal_RL_TB);
3163 // Only if it is the active view, the array will be deleted and
3164 // the screen filled new.
3165 if (State::ACTIVE == m_eState && bClear)
3167 if (m_pActiveShell)
3168 StartListening(*m_pActiveShell->GetView().GetDocShell());
3169 FindActiveTypeAndRemoveUserData();
3170 for(ContentTypeId i : o3tl::enumrange<ContentTypeId>())
3172 m_aActiveContentArr[i].reset();
3174 Display(true);
3178 void SwContentTree::SetConstantShell(SwWrtShell* pSh)
3180 EndListeningAll();
3181 m_pActiveShell = pSh;
3182 m_eState = State::CONSTANT;
3183 StartListening(*m_pActiveShell->GetView().GetDocShell());
3184 FindActiveTypeAndRemoveUserData();
3185 for(ContentTypeId i : o3tl::enumrange<ContentTypeId>())
3187 m_aActiveContentArr[i].reset();
3189 Display(true);
3192 void SwContentTree::Notify(SfxBroadcaster & rBC, SfxHint const& rHint)
3194 SfxViewEventHint const*const pVEHint(dynamic_cast<SfxViewEventHint const*>(&rHint));
3195 SwXTextView* pDyingShell = nullptr;
3196 if (m_pActiveShell && pVEHint && pVEHint->GetEventName() == "OnViewClosed")
3197 pDyingShell = dynamic_cast<SwXTextView*>(pVEHint->GetController().get());
3198 if (pDyingShell && pDyingShell->GetView() == &m_pActiveShell->GetView())
3200 SetActiveShell(nullptr); // our view is dying, clear our pointers to it
3202 else
3204 SfxListener::Notify(rBC, rHint);
3206 switch (rHint.GetId())
3208 case SfxHintId::SwNavigatorUpdateTracking:
3209 UpdateTracking();
3210 break;
3211 case SfxHintId::SwNavigatorSelectOutlinesWithSelections:
3213 if (m_nRootType == ContentTypeId::OUTLINE)
3215 SelectOutlinesWithSelection();
3216 // make first selected entry visible
3217 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
3218 if (xEntry && m_xTreeView->get_selected(xEntry.get()))
3219 m_xTreeView->scroll_to_row(*xEntry);
3221 else if (m_nRootType == ContentTypeId::UNKNOWN)
3222 m_xTreeView->unselect_all();
3223 break;
3225 case SfxHintId::DocChanged:
3226 if (!m_bIgnoreDocChange)
3228 m_bDocHasChanged = true;
3229 TimerUpdate(&m_aUpdTimer);
3231 break;
3232 case SfxHintId::ModeChanged:
3233 if (SwWrtShell* pShell = GetWrtShell())
3235 const bool bReadOnly = pShell->GetView().GetDocShell()->IsReadOnly();
3236 if (bReadOnly != m_bIsLastReadOnly)
3238 m_bIsLastReadOnly = bReadOnly;
3240 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
3241 if (m_xTreeView->get_cursor(xEntry.get()))
3243 m_xTreeView->select(*xEntry);
3244 Select();
3246 else
3247 m_xTreeView->unselect_all();
3250 break;
3251 default:
3252 break;
3256 // Handler for outline entry up/down left/right movement
3257 void SwContentTree::ExecCommand(std::u16string_view rCmd, bool bOutlineWithChildren)
3259 if (m_xTreeView->count_selected_rows() == 0)
3260 return;
3262 const bool bUp = rCmd == u"chapterup";
3263 const bool bUpDown = bUp || rCmd == u"chapterdown";
3264 const bool bLeft = rCmd == u"promote";
3265 const bool bLeftRight = bLeft || rCmd == u"demote";
3266 if (!bUpDown && !bLeftRight)
3267 return;
3268 if (GetWrtShell()->GetView().GetDocShell()->IsReadOnly() ||
3269 (State::ACTIVE != m_eState &&
3270 (State::CONSTANT != m_eState || m_pActiveShell != GetParentWindow()->GetCreateView()->GetWrtShellPtr())))
3272 return;
3275 SwWrtShell *const pShell = GetWrtShell();
3277 const SwNodes& rNodes = pShell->GetNodes();
3278 const SwOutlineNodes& rOutlineNodes = rNodes.GetOutLineNds();
3279 const SwOutlineNodes::size_type nOutlineNdsSize = rOutlineNodes.size();
3281 std::vector<SwTextNode*> selectedOutlineNodes;
3282 std::vector<std::unique_ptr<weld::TreeIter>> selected;
3284 m_xTreeView->selected_foreach([&](weld::TreeIter& rEntry){
3285 // it's possible to select the root node too which is a really bad idea
3286 if (lcl_IsContentType(rEntry, *m_xTreeView))
3287 return false;
3288 // filter out children of selected parents so they don't get promoted
3289 // or moved twice (except if there is Ctrl modifier, since in that
3290 // case children are re-parented)
3291 if ((bLeftRight || bOutlineWithChildren) && !selected.empty())
3293 std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rEntry));
3294 for (bool bParent = m_xTreeView->iter_parent(*xParent); bParent; bParent = m_xTreeView->iter_parent(*xParent))
3296 if (m_xTreeView->iter_compare(*selected.back(), *xParent) == 0)
3298 return false;
3302 selected.emplace_back(m_xTreeView->make_iterator(&rEntry));
3304 // Use the outline node position in the SwOutlineNodes array. Bad things
3305 // happen if the tree entry position is used and it doesn't match the node position
3306 // in SwOutlineNodes, which is usually the case for outline nodes in frames.
3307 const SwOutlineNodes::size_type nPos
3308 = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rEntry))->GetOutlinePos();
3309 if (nPos < nOutlineNdsSize)
3311 SwNode* pNode = rNodes.GetOutLineNds()[ nPos ];
3312 if (pNode)
3314 selectedOutlineNodes.push_back(pNode->GetTextNode());
3317 return false;
3320 if (!selected.size())
3321 return;
3323 if (bUpDown && !bUp)
3324 { // to move down, start at the end!
3325 std::reverse(selected.begin(), selected.end());
3328 m_bIgnoreDocChange = true;
3330 SwOutlineNodes::size_type nActPos;
3331 bool bStartedAction = false;
3333 MakeAllOutlineContentTemporarilyVisible a(GetWrtShell()->GetDoc());
3335 // get first regular document content node outline node position in outline nodes array
3336 SwOutlineNodes::size_type nFirstRegularDocContentOutlineNodePos = SwOutlineNodes::npos;
3337 SwNodeOffset nEndOfExtrasIndex = rNodes.GetEndOfExtras().GetIndex();
3338 for (SwOutlineNodes::size_type nPos = 0; nPos < nOutlineNdsSize; nPos++)
3340 if (rOutlineNodes[nPos]->GetIndex() > nEndOfExtrasIndex)
3342 nFirstRegularDocContentOutlineNodePos = nPos;
3343 break;
3347 for (auto const& pCurrentEntry : selected)
3349 nActPos = weld::fromId<SwOutlineContent*>(
3350 m_xTreeView->get_id(*pCurrentEntry))->GetOutlinePos();
3352 // outline nodes in frames and tables are not up/down moveable
3353 if (nActPos == SwOutlineNodes::npos ||
3354 (bUpDown && (!pShell->IsOutlineMovable(nActPos) ||
3355 nFirstRegularDocContentOutlineNodePos == SwOutlineNodes::npos)))
3357 continue;
3360 if (!bStartedAction)
3362 pShell->StartAllAction();
3363 pShell->StartUndo(bLeftRight ? SwUndoId::OUTLINE_LR : SwUndoId::OUTLINE_UD);
3364 bStartedAction = true;
3367 pShell->GotoOutline( nActPos); // If text selection != box selection
3368 pShell->Push();
3370 if (bUpDown)
3372 // move outline position up/down (outline position promote/demote)
3373 SwOutlineNodes::difference_type nDir = bUp ? -1 : 1;
3374 if ((nDir == -1 && nActPos > 0) || (nDir == 1 && nActPos < nOutlineNdsSize - 1))
3376 // make outline selection for use by MoveOutlinePara
3377 pShell->MakeOutlineSel(nActPos, nActPos, bOutlineWithChildren);
3379 int nActPosOutlineLevel =
3380 rOutlineNodes[nActPos]->GetTextNode()->GetAttrOutlineLevel();
3381 SwOutlineNodes::size_type nPos = nActPos;
3382 if (!bUp)
3384 // move down
3385 int nPosOutlineLevel = -1;
3386 while (++nPos < nOutlineNdsSize)
3388 nPosOutlineLevel =
3389 rOutlineNodes[nPos]->GetTextNode()->GetAttrOutlineLevel();
3390 // discontinue if moving out of parent or equal level is found
3391 if (nPosOutlineLevel <= nActPosOutlineLevel)
3392 break;
3393 // count the children of the node when they are not included in the move
3394 if (!bOutlineWithChildren)
3395 nDir++;
3397 if (nPosOutlineLevel >= nActPosOutlineLevel)
3399 // move past children
3400 while (++nPos < nOutlineNdsSize)
3402 nPosOutlineLevel =
3403 rOutlineNodes[nPos]->GetTextNode()->GetAttrOutlineLevel();
3404 // discontinue if moving out of parent or equal level is found
3405 if (nPosOutlineLevel <= nActPosOutlineLevel)
3406 break;
3407 nDir++;
3411 else
3413 // move up
3414 while (nPos && --nPos >= nFirstRegularDocContentOutlineNodePos)
3416 int nPosOutlineLevel =
3417 rOutlineNodes[nPos]->GetTextNode()->GetAttrOutlineLevel();
3418 // discontinue if equal level is found
3419 if (nPosOutlineLevel == nActPosOutlineLevel)
3420 break;
3421 // discontinue if moving out of parent
3422 if (nPosOutlineLevel < nActPosOutlineLevel)
3424 // Required for expected chapter placement when the chapter being moved
3425 // up has an outline level less than the outline level of chapters it
3426 // is being moved above and then encounters a chapter with an outline
3427 // level that is greater before reaching a chapter with the same
3428 // outline level as itself.
3429 if (nDir < -1)
3430 nDir++;
3431 break;
3433 nDir--;
3436 pShell->MoveOutlinePara(nDir);
3438 pShell->ClearMark();
3440 else
3442 // move outline left/right (outline level promote/demote)
3443 if (!pShell->IsProtectedOutlinePara())
3445 bool bAllow = true;
3446 const SwOutlineNodes& rOutlNds = pShell->GetDoc()->GetNodes().GetOutLineNds();
3447 const int nActLevel = rOutlNds[nActPos]->GetTextNode()->GetAttrOutlineLevel();
3448 if (!bLeft)
3450 // disallow if any outline node to demote will exceed MAXLEVEL
3451 SwOutlineNodes::size_type nPos = nActPos;
3454 int nLevel = rOutlNds[nPos]->GetTextNode()->GetAttrOutlineLevel();
3455 if (nLevel == MAXLEVEL)
3457 bAllow = false;
3458 break;
3460 } while (bOutlineWithChildren && ++nPos < rOutlNds.size() &&
3461 rOutlNds[nPos]->GetTextNode()->GetAttrOutlineLevel() > nActLevel);
3463 else
3465 // disallow if trying to promote outline of level 1
3466 if (nActLevel == 1)
3467 bAllow = false;
3469 if (bAllow)
3471 SwOutlineNodes::size_type nPos = nActPos;
3474 pShell->SwCursorShell::GotoOutline(nPos);
3475 pShell->OutlineUpDown(bLeft ? -1 : 1);
3476 } while (bOutlineWithChildren && ++nPos < rOutlNds.size() &&
3477 rOutlNds[nPos]->GetTextNode()->GetAttrOutlineLevel() > nActLevel);
3482 pShell->Pop(SwCursorShell::PopMode::DeleteCurrent); // Cursor is now back at the current heading.
3485 if (bStartedAction)
3487 pShell->EndUndo();
3488 pShell->EndAllAction();
3489 if (m_aActiveContentArr[ContentTypeId::OUTLINE])
3490 m_aActiveContentArr[ContentTypeId::OUTLINE]->Invalidate();
3492 // tdf#143547 LO Writer: navigator should stand still on promoting and demoting
3493 // In addition to m_bIgnoreDocChange being true, selections are cleared before the Display
3494 // call. Either of these conditions disable restore of scroll position happening in the
3495 // Display function so it needs to be done here.
3496 auto nOldScrollPos = m_xTreeView->vadjustment_get_value();
3498 // clear all selections to prevent the Display function from trying to reselect selected entries
3499 m_xTreeView->unselect_all();
3500 Display(true);
3501 m_xTreeView->vadjustment_set_value(nOldScrollPos);
3503 if (m_bIsRoot)
3505 // reselect entries, do this only when in outline content navigation mode
3506 const SwOutlineNodes& rOutlineNds = pShell->GetNodes().GetOutLineNds();
3507 for (SwTextNode* pNode : selectedOutlineNodes)
3509 m_xTreeView->all_foreach([this, &rOutlineNds, pNode](weld::TreeIter& rEntry){
3510 if (lcl_IsContentType(rEntry, *m_xTreeView))
3511 return false;
3512 SwOutlineNodes::size_type nPos = weld::fromId<SwOutlineContent*>(
3513 m_xTreeView->get_id(rEntry))->GetOutlinePos();
3514 if (pNode == rOutlineNds[nPos]->GetTextNode())
3516 std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rEntry));
3517 if (m_xTreeView->iter_parent(*xParent)
3518 && !m_xTreeView->get_row_expanded(*xParent))
3520 m_xTreeView->expand_row(*xParent);
3522 m_xTreeView->select(rEntry);
3523 return true;
3525 return false;
3529 else
3531 m_pActiveShell->GetView().GetEditWin().GrabFocus();
3532 m_bIgnoreDocChange = false;
3533 UpdateTracking();
3534 grab_focus();
3537 m_bIgnoreDocChange = false;
3540 void SwContentTree::ShowTree()
3542 m_xTreeView->show();
3543 m_aUpdTimer.Start();
3546 void SwContentTree::HideTree()
3548 // folded together will not be idled
3549 m_aUpdTimer.Stop();
3550 m_xTreeView->hide();
3553 static void lcl_SelectByContentTypeAndAddress(SwContentTree* pThis, weld::TreeView& rContentTree,
3554 ContentTypeId nType, const void* ptr)
3556 if (!ptr)
3558 rContentTree.set_cursor(-1);
3559 pThis->Select();
3560 return;
3563 // find content type entry
3564 std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator());
3566 bool bFoundEntry = rContentTree.get_iter_first(*xIter);
3567 while (bFoundEntry)
3569 void* pUserData = weld::fromId<void*>(rContentTree.get_id(*xIter));
3570 assert(dynamic_cast<SwContentType*>(static_cast<SwTypeNumber*>(pUserData)));
3571 if (nType == static_cast<SwContentType*>(pUserData)->GetType())
3572 break;
3573 bFoundEntry = rContentTree.iter_next_sibling(*xIter);
3576 if (!bFoundEntry)
3578 rContentTree.set_cursor(-1);
3579 pThis->Select();
3580 return;
3583 // assure content type entry is expanded
3584 rContentTree.expand_row(*xIter);
3586 // find content type content entry and select it
3587 const void* p = nullptr;
3588 while (rContentTree.iter_next(*xIter) && lcl_IsContent(*xIter, rContentTree))
3590 void* pUserData = weld::fromId<void*>(rContentTree.get_id(*xIter));
3591 switch( nType )
3593 case ContentTypeId::FOOTNOTE:
3594 case ContentTypeId::ENDNOTE:
3596 assert(dynamic_cast<SwTextFootnoteContent*>(static_cast<SwTypeNumber*>(pUserData)));
3597 SwTextFootnoteContent* pCnt = static_cast<SwTextFootnoteContent*>(pUserData);
3598 p = pCnt->GetTextFootnote();
3599 break;
3601 case ContentTypeId::URLFIELD:
3603 assert(dynamic_cast<SwURLFieldContent*>(static_cast<SwTypeNumber*>(pUserData)));
3604 SwURLFieldContent* pCnt = static_cast<SwURLFieldContent*>(pUserData);
3605 p = static_cast<const SwTextAttr*>(pCnt->GetINetAttr());
3606 break;
3608 case ContentTypeId::TEXTFIELD:
3610 assert(dynamic_cast<SwTextFieldContent*>(static_cast<SwTypeNumber*>(pUserData)));
3611 SwTextFieldContent* pCnt = static_cast<SwTextFieldContent*>(pUserData);
3612 p = pCnt->GetFormatField()->GetField();
3613 break;
3615 case ContentTypeId::POSTIT:
3617 assert(dynamic_cast<SwPostItContent*>(static_cast<SwTypeNumber*>(pUserData)));
3618 SwPostItContent* pCnt = static_cast<SwPostItContent*>(pUserData);
3619 p = pCnt->GetPostIt()->GetField();
3620 break;
3622 default:
3623 break;
3625 if (ptr == p)
3627 // get first selected for comparison
3628 std::unique_ptr<weld::TreeIter> xFirstSelected(rContentTree.make_iterator());
3629 if (!rContentTree.get_selected(xFirstSelected.get()))
3630 xFirstSelected.reset();
3631 if (rContentTree.count_selected_rows() != 1 || !xFirstSelected ||
3632 rContentTree.iter_compare(*xIter, *xFirstSelected) != 0)
3634 // unselect all entries and make passed entry visible and selected
3635 rContentTree.set_cursor(*xIter);
3636 pThis->Select();
3638 return;
3642 rContentTree.set_cursor(-1);
3643 pThis->Select();
3644 return;
3647 static void lcl_SelectByContentTypeAndName(SwContentTree* pThis, weld::TreeView& rContentTree,
3648 std::u16string_view rContentTypeName, std::u16string_view rName)
3650 if (rName.empty())
3651 return;
3653 // find content type entry
3654 std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator());
3655 bool bFoundEntry = rContentTree.get_iter_first(*xIter);
3656 while (bFoundEntry && rContentTypeName != rContentTree.get_text(*xIter))
3657 bFoundEntry = rContentTree.iter_next_sibling(*xIter);
3658 // find content type content entry and select it
3659 if (!bFoundEntry)
3660 return;
3662 rContentTree.expand_row(*xIter); // assure content type entry is expanded
3663 while (rContentTree.iter_next(*xIter) && lcl_IsContent(*xIter, rContentTree))
3665 if (rName == rContentTree.get_text(*xIter))
3667 // get first selected for comparison
3668 std::unique_ptr<weld::TreeIter> xFirstSelected(rContentTree.make_iterator());
3669 if (!rContentTree.get_selected(xFirstSelected.get()))
3670 xFirstSelected.reset();
3671 if (rContentTree.count_selected_rows() != 1 || !xFirstSelected ||
3672 rContentTree.iter_compare(*xIter, *xFirstSelected) != 0)
3674 // unselect all entries and make passed entry visible and selected
3675 rContentTree.set_cursor(*xIter);
3676 pThis->Select();
3678 break;
3683 static void lcl_SelectDrawObjectByName(weld::TreeView& rContentTree, std::u16string_view rName)
3685 if (rName.empty())
3686 return;
3688 // find content type entry
3689 std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator());
3690 bool bFoundEntry = rContentTree.get_iter_first(*xIter);
3691 while (bFoundEntry && SwResId(STR_CONTENT_TYPE_DRAWOBJECT) != rContentTree.get_text(*xIter))
3692 bFoundEntry = rContentTree.iter_next_sibling(*xIter);
3693 // find content type content entry and select it
3694 if (bFoundEntry)
3696 rContentTree.expand_row(*xIter); // assure content type entry is expanded
3697 while (rContentTree.iter_next(*xIter) && lcl_IsContent(*xIter, rContentTree))
3699 if (rName == rContentTree.get_text(*xIter))
3701 if (!rContentTree.is_selected(*xIter))
3703 rContentTree.select(*xIter);
3704 rContentTree.scroll_to_row(*xIter);
3706 break;
3712 /** No idle with focus or while dragging */
3713 IMPL_LINK_NOARG(SwContentTree, TimerUpdate, Timer *, void)
3715 // No need to update if content tree is not visible
3716 if (!m_xTreeView->is_visible())
3717 return;
3719 // No update while focus is not in document.
3720 // No update while drag and drop.
3721 // Query view because the Navigator is cleared too late.
3722 SwView* pView = GetParentWindow()->GetCreateView();
3724 SwWrtShell* pActShell = pView ? pView->GetWrtShellPtr() : nullptr;
3725 if(pActShell && pActShell->GetWin() &&
3726 (pActShell->GetWin()->HasFocus() || m_bDocHasChanged || m_bViewHasChanged) &&
3727 !IsInDrag() && !pActShell->ActionPend())
3729 if (m_bDocHasChanged || m_bViewHasChanged)
3731 if (State::CONSTANT == m_eState && !lcl_FindShell(m_pActiveShell))
3733 SetActiveShell(pActShell);
3734 GetParentWindow()->UpdateListBox();
3736 if (State::ACTIVE == m_eState && pActShell != GetWrtShell())
3738 SetActiveShell(pActShell);
3740 else if ((State::ACTIVE == m_eState || (State::CONSTANT == m_eState && pActShell == GetWrtShell())) &&
3741 HasContentChanged())
3743 FindActiveTypeAndRemoveUserData();
3744 Display(true);
3747 UpdateTracking();
3748 m_bIsIdleClear = false;
3749 m_bDocHasChanged = false;
3750 m_bViewHasChanged = false;
3752 else if (!pView && State::ACTIVE == m_eState && !m_bIsIdleClear) // this block seems never to be entered
3754 if(m_pActiveShell)
3756 SetActiveShell(nullptr);
3758 clear();
3759 m_bIsIdleClear = true;
3763 void SwContentTree::UpdateTracking()
3765 if (State::HIDDEN == m_eState || !m_pActiveShell)
3766 return;
3768 // only when treeview or treeview context menu does not have focus
3769 if (m_xTreeView->has_focus() || m_xTreeView->has_child_focus())
3770 return;
3772 // m_bIgnoreDocChange is set on delete and outline visibility toggle
3773 if (m_bIgnoreDocChange)
3775 m_bIgnoreDocChange = false;
3776 return;
3779 // bTrack is used to disallow tracking after jumping to an outline until the outline position
3780 // that was jumped to is no longer the current outline position.
3781 bool bTrack = true;
3782 if (m_nLastGotoContentWasOutlinePos != SwOutlineNodes::npos)
3784 if (m_pActiveShell->GetOutlinePos() == m_nLastGotoContentWasOutlinePos)
3785 bTrack = false;
3786 else
3787 m_nLastGotoContentWasOutlinePos = SwOutlineNodes::npos;
3790 if (bTrack)
3792 // graphic, frame, and ole
3793 if (m_pActiveShell->GetSelectionType() &
3794 (SelectionType::Graphic | SelectionType::Frame | SelectionType::Ole))
3796 OUString aContentTypeName;
3797 if (m_pActiveShell->GetSelectionType() == SelectionType::Graphic &&
3798 !(m_bIsRoot && m_nRootType != ContentTypeId::GRAPHIC))
3800 if (!mTrackContentType[ContentTypeId::GRAPHIC]) return;
3801 aContentTypeName = SwResId(STR_CONTENT_TYPE_GRAPHIC);
3803 else if (m_pActiveShell->GetSelectionType() == SelectionType::Frame &&
3804 !(m_bIsRoot && m_nRootType != ContentTypeId::FRAME))
3806 if (!mTrackContentType[ContentTypeId::FRAME]) return;
3807 aContentTypeName = SwResId(STR_CONTENT_TYPE_FRAME);
3809 else if (m_pActiveShell->GetSelectionType() == SelectionType::Ole &&
3810 !(m_bIsRoot && m_nRootType != ContentTypeId::OLE))
3812 if (!mTrackContentType[ContentTypeId::OLE]) return;
3813 aContentTypeName = SwResId(STR_CONTENT_TYPE_OLE);
3815 if (!aContentTypeName.isEmpty())
3817 OUString aName(m_pActiveShell->GetFlyName());
3818 lcl_SelectByContentTypeAndName(this, *m_xTreeView, aContentTypeName, aName);
3819 return;
3822 // drawing
3823 if ((m_pActiveShell->GetSelectionType() & (SelectionType::DrawObject |
3824 SelectionType::DrawObjectEditMode |
3825 SelectionType::DbForm)) &&
3826 !(m_bIsRoot && m_nRootType != ContentTypeId::DRAWOBJECT))
3828 if (mTrackContentType[ContentTypeId::DRAWOBJECT])
3830 // Multiple selection is possible when in root content navigation view so unselect all
3831 // selected entries before reselecting. This causes a bit of an annoyance when the treeview
3832 // scroll bar is used and focus is in the document by causing the last selected entry to
3833 // scroll back into view.
3834 if (m_bIsRoot)
3835 m_xTreeView->unselect_all();
3836 SdrView* pSdrView = m_pActiveShell->GetDrawView();
3837 if (pSdrView)
3839 for (size_t nIdx(0); nIdx < pSdrView->GetMarkedObjectCount(); nIdx++)
3841 SdrObject* pSelected = pSdrView->GetMarkedObjectByIndex(nIdx);
3842 OUString aName(pSelected->GetName());
3843 if (!aName.isEmpty())
3844 lcl_SelectDrawObjectByName(*m_xTreeView, aName);
3847 else
3849 // clear treeview selections
3850 m_xTreeView->unselect_all();
3852 Select();
3854 return;
3856 // footnotes and endnotes
3857 if (SwContentAtPos aContentAtPos(IsAttrAtPos::Ftn);
3858 m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos)
3859 && aContentAtPos.pFndTextAttr &&
3860 !(m_bIsRoot && (m_nRootType != ContentTypeId::FOOTNOTE &&
3861 m_nRootType != ContentTypeId::ENDNOTE)))
3863 if (!aContentAtPos.pFndTextAttr->GetFootnote().IsEndNote())
3865 if (mTrackContentType[ContentTypeId::FOOTNOTE])
3866 lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, ContentTypeId::FOOTNOTE,
3867 aContentAtPos.pFndTextAttr);
3869 else if (mTrackContentType[ContentTypeId::ENDNOTE])
3870 lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, ContentTypeId::ENDNOTE,
3871 aContentAtPos.pFndTextAttr);
3872 return;
3874 // bookmarks - track first bookmark at cursor
3875 if (mTrackContentType[ContentTypeId::BOOKMARK] &&
3876 (m_pActiveShell->GetSelectionType() & SelectionType::Text))
3878 SwPaM* pCursor = m_pActiveShell->GetCursor();
3879 IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess();
3880 IDocumentMarkAccess::const_iterator_t ppBookmark = pMarkAccess->getBookmarksBegin();
3881 if (pCursor && ppBookmark != pMarkAccess->getBookmarksEnd() &&
3882 !(m_bIsRoot && m_nRootType != ContentTypeId::BOOKMARK))
3884 OUString sBookmarkName;
3885 SwPosition* pCursorPoint = pCursor->GetPoint();
3886 while (ppBookmark != pMarkAccess->getBookmarksEnd())
3888 if (lcl_IsUiVisibleBookmark(*ppBookmark) &&
3889 *pCursorPoint >= (*ppBookmark)->GetMarkStart() &&
3890 *pCursorPoint <= (*ppBookmark)->GetMarkEnd())
3892 sBookmarkName = (*ppBookmark)->GetName();
3893 // keep previously selected bookmark instead
3894 // of selecting a different bookmark inside of it
3895 if (sBookmarkName == m_sSelectedItem)
3896 break;
3898 else if (!sBookmarkName.isEmpty() &&
3899 *pCursorPoint < (*ppBookmark)->GetMarkStart())
3901 // don't search a different bookmark inside the
3902 // previous one, if the starting position of the next bookmarks
3903 // is after the cursor position (assuming that the
3904 // bookmark iterator jumps inside the same text by positions)
3905 break;
3907 ++ppBookmark;
3910 if (!sBookmarkName.isEmpty())
3912 // select the bookmark
3913 lcl_SelectByContentTypeAndName(this, *m_xTreeView,
3914 SwResId(STR_CONTENT_TYPE_BOOKMARK),
3915 sBookmarkName);
3916 return;
3920 // references
3921 if (SwContentAtPos aContentAtPos(IsAttrAtPos::RefMark);
3922 m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos) &&
3923 aContentAtPos.pFndTextAttr &&
3924 !(m_bIsRoot && m_nRootType != ContentTypeId::REFERENCE))
3926 if (mTrackContentType[ContentTypeId::REFERENCE])
3928 const SwFormatRefMark& rRefMark = aContentAtPos.pFndTextAttr->GetRefMark();
3929 lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_REFERENCE),
3930 rRefMark.GetRefName());
3932 return;
3934 // hyperlinks
3935 // not in ToxContent tdf#148312
3936 if (const SwSection* pSection = m_pActiveShell->GetCurrSection(); !pSection
3937 || (pSection && pSection->GetType() != SectionType::ToxContent))
3939 if (SwContentAtPos aContentAtPos(IsAttrAtPos::InetAttr);
3940 m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos)
3941 && (!m_bIsRoot || m_nRootType == ContentTypeId::URLFIELD))
3943 // Because hyperlink item names do not need to be unique, finding the corresponding
3944 // item in the tree by name may result in incorrect selection. Find the item in the
3945 // tree by comparing the SwTextINetFormat pointer at the document cursor position to
3946 // that stored in the item SwURLFieldContent.
3947 if (mTrackContentType[ContentTypeId::URLFIELD])
3948 lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, ContentTypeId::URLFIELD,
3949 aContentAtPos.pFndTextAttr);
3950 return;
3953 // fields, comments
3954 if (SwField* pField = m_pActiveShell->GetCurField(); pField &&
3955 !(m_bIsRoot &&
3956 m_nRootType != ContentTypeId::TEXTFIELD &&
3957 m_nRootType != ContentTypeId::POSTIT))
3959 ContentTypeId eCntTypeId =
3960 pField->GetTypeId() == SwFieldTypesEnum::Postit ? ContentTypeId::POSTIT :
3961 ContentTypeId::TEXTFIELD;
3962 if (mTrackContentType[eCntTypeId])
3963 lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, eCntTypeId, pField);
3964 return;
3966 // table
3967 if (m_pActiveShell->IsCursorInTable() &&
3968 !(m_bIsRoot && m_nRootType != ContentTypeId::TABLE))
3970 if (mTrackContentType[ContentTypeId::TABLE] && m_pActiveShell->GetTableFormat())
3972 OUString aName = m_pActiveShell->GetTableFormat()->GetName();
3973 lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_TABLE),
3974 aName);
3976 return;
3978 // indexes
3979 if (const SwTOXBase* pTOX = m_pActiveShell->GetCurTOX(); pTOX &&
3980 !(m_bIsRoot && m_nRootType != ContentTypeId::INDEX))
3982 if (mTrackContentType[ContentTypeId::INDEX])
3983 lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_INDEX),
3984 pTOX->GetTOXName());
3985 return;
3987 // section
3988 if (const SwSection* pSection = m_pActiveShell->GetCurrSection(); pSection &&
3989 !(m_bIsRoot && m_nRootType != ContentTypeId::REGION))
3991 if (mTrackContentType[ContentTypeId::REGION])
3993 lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_REGION),
3994 pSection->GetSectionName());
3995 return;
3997 else
3999 // prevent fall through to outline tracking when section tracking is off and the last
4000 // GotoContent is the current section
4001 if (m_nLastSelType == ContentTypeId::REGION &&
4002 m_xTreeView->get_selected_text() == pSection->GetSectionName())
4003 return;
4005 // fall through to outline tracking when section tracking is off and the last GotoContent
4006 // is not the current section
4009 // outline
4010 if (m_nOutlineTracking == 3)
4011 return;
4012 // find out where the cursor is
4013 const SwOutlineNodes::size_type nActPos = GetWrtShell()->GetOutlinePos(MAXLEVEL);
4014 if (!((m_bIsRoot && m_nRootType != ContentTypeId::OUTLINE) || nActPos == SwOutlineNodes::npos))
4016 // assure outline content type is expanded
4017 // this assumes outline content type is first in treeview
4018 std::unique_ptr<weld::TreeIter> xFirstEntry(m_xTreeView->make_iterator());
4019 if (m_xTreeView->get_iter_first(*xFirstEntry))
4020 m_xTreeView->expand_row(*xFirstEntry);
4022 m_xTreeView->all_foreach([this, nActPos](weld::TreeIter& rEntry){
4023 bool bRet = false;
4024 if (lcl_IsContent(rEntry, *m_xTreeView) && weld::fromId<SwContent*>(
4025 m_xTreeView->get_id(rEntry))->GetParent()->GetType() ==
4026 ContentTypeId::OUTLINE)
4028 if (weld::fromId<SwOutlineContent*>(
4029 m_xTreeView->get_id(rEntry))->GetOutlinePos() == nActPos)
4031 std::unique_ptr<weld::TreeIter> xFirstSelected(
4032 m_xTreeView->make_iterator());
4033 if (!m_xTreeView->get_selected(xFirstSelected.get()))
4034 xFirstSelected.reset();
4035 // only select if not already selected or tree has multiple entries selected
4036 if (m_xTreeView->count_selected_rows() != 1 || !xFirstSelected ||
4037 m_xTreeView->iter_compare(rEntry, *xFirstSelected) != 0)
4039 if (m_nOutlineTracking == 2) // focused outline tracking
4041 // collapse to children of root node
4042 std::unique_ptr<weld::TreeIter> xChildEntry(
4043 m_xTreeView->make_iterator());
4044 if (m_xTreeView->get_iter_first(*xChildEntry) &&
4045 m_xTreeView->iter_children(*xChildEntry))
4049 if (weld::fromId<SwContent*>(
4050 m_xTreeView->get_id(*xChildEntry))->
4051 GetParent()->GetType() == ContentTypeId::OUTLINE)
4052 m_xTreeView->collapse_row(*xChildEntry);
4053 else
4054 break;
4056 while (m_xTreeView->iter_next(*xChildEntry));
4059 // unselect all entries, make pEntry visible, and select
4060 m_xTreeView->set_cursor(rEntry);
4061 Select();
4063 // tdf#149279 show at least two outline entries before the set cursor entry
4064 std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator(&rEntry));
4065 for (int i = 0; i < 2; i++)
4067 if (m_xTreeView->get_iter_depth(*xIter) == 0)
4068 break;
4069 if (!m_xTreeView->iter_previous(*xIter))
4070 break;
4071 while (!weld::IsEntryVisible(*m_xTreeView, *xIter))
4072 m_xTreeView->iter_parent(*xIter);
4074 // Assure the scroll to row is collapsed after scrolling if it was collapsed
4075 // before. This is required here to make gtkinst scroll_to_row behave like
4076 // salinst.
4077 const bool bRowExpanded = m_xTreeView->get_row_expanded(*xIter);
4078 m_xTreeView->scroll_to_row(*xIter);
4079 if (!bRowExpanded)
4080 m_xTreeView->collapse_row(*xIter);
4082 bRet = true;
4085 else
4087 // use of this break assumes outline content type is first in tree
4088 if (lcl_IsContentType(rEntry, *m_xTreeView) &&
4089 weld::fromId<SwContentType*>(
4090 m_xTreeView->get_id(rEntry))->GetType() !=
4091 ContentTypeId::OUTLINE)
4092 bRet = true;
4094 return bRet;
4097 else
4099 // clear treeview selections
4100 if (m_xTreeView->count_selected_rows() > 0)
4102 m_xTreeView->unselect_all();
4103 m_xTreeView->set_cursor(-1);
4104 Select();
4109 void SwContentTree::SelectOutlinesWithSelection()
4111 SwCursor* pFirstCursor = m_pActiveShell->GetCursor();
4112 SwCursor* pCursor = pFirstCursor;
4113 std::vector<SwOutlineNodes::size_type> aOutlinePositions;
4116 if (pCursor)
4118 if (pCursor->HasMark())
4120 aOutlinePositions.push_back(m_pActiveShell->GetOutlinePos(UCHAR_MAX, pCursor));
4122 pCursor = pCursor->GetNext();
4124 } while (pCursor && pCursor != pFirstCursor);
4126 if (aOutlinePositions.empty())
4127 return;
4129 // remove duplicates before selecting
4130 aOutlinePositions.erase(std::unique(aOutlinePositions.begin(), aOutlinePositions.end()),
4131 aOutlinePositions.end());
4133 m_xTreeView->unselect_all();
4135 for (auto nOutlinePosition : aOutlinePositions)
4137 m_xTreeView->all_foreach([this, nOutlinePosition](const weld::TreeIter& rEntry){
4138 if (lcl_IsContent(rEntry, *m_xTreeView) &&
4139 weld::fromId<SwContent*>(
4140 m_xTreeView->get_id(rEntry))->GetParent()->GetType() ==
4141 ContentTypeId::OUTLINE)
4143 if (weld::fromId<SwOutlineContent*>(
4144 m_xTreeView->get_id(rEntry))->GetOutlinePos() ==
4145 nOutlinePosition)
4147 std::unique_ptr<weld::TreeIter> xParent =
4148 m_xTreeView->make_iterator(&rEntry);
4149 if (m_xTreeView->iter_parent(*xParent) &&
4150 !m_xTreeView->get_row_expanded(*xParent))
4151 m_xTreeView->expand_row(*xParent);
4152 m_xTreeView->select(rEntry);
4153 return true;
4156 return false;
4160 Select();
4163 void SwContentTree::MoveOutline(SwOutlineNodes::size_type nTargetPos)
4165 MakeAllOutlineContentTemporarilyVisible a(GetWrtShell()->GetDoc());
4167 SwWrtShell *const pShell = GetWrtShell();
4168 pShell->StartAllAction();
4169 pShell->StartUndo(SwUndoId::OUTLINE_UD);
4171 SwOutlineNodes::size_type nPrevSourcePos = SwOutlineNodes::npos;
4172 SwOutlineNodes::size_type nPrevTargetPosOrOffset = SwOutlineNodes::npos;
4174 bool bFirstMove = true;
4176 for (const auto& source : m_aDndOutlinesSelected)
4178 SwOutlineNodes::size_type nSourcePos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*source))->GetOutlinePos();
4180 // Done on the first selection move
4181 if (bFirstMove) // only do once
4183 if (nTargetPos == SwOutlineNodes::npos || nSourcePos > nTargetPos)
4185 // Up moves
4186 // The first up move sets the up move amount for the remaining selected outlines to be moved
4187 if (nTargetPos != SwOutlineNodes::npos)
4188 nPrevTargetPosOrOffset = nSourcePos - nTargetPos;
4189 else
4190 nPrevTargetPosOrOffset = nSourcePos + 1;
4192 else if (nSourcePos < nTargetPos)
4194 // Down moves
4195 // The first down move sets the source and target positions for the remaining selected outlines to be moved
4196 nPrevSourcePos = nSourcePos;
4197 nPrevTargetPosOrOffset = nTargetPos;
4199 bFirstMove = false;
4201 else
4203 if (nTargetPos == SwOutlineNodes::npos || nSourcePos > nTargetPos)
4205 // Move up
4206 nTargetPos = nSourcePos - nPrevTargetPosOrOffset;
4208 else if (nSourcePos < nTargetPos)
4210 // Move down
4211 nSourcePos = nPrevSourcePos;
4212 nTargetPos = nPrevTargetPosOrOffset;
4215 GetParentWindow()->MoveOutline(nSourcePos, nTargetPos);
4218 pShell->EndUndo();
4219 pShell->EndAllAction();
4220 m_aActiveContentArr[ContentTypeId::OUTLINE]->Invalidate();
4221 Display(true);
4222 m_aDndOutlinesSelected.clear();
4225 // Update immediately
4226 IMPL_LINK_NOARG(SwContentTree, FocusInHdl, weld::Widget&, void)
4228 SwView* pActView = GetParentWindow()->GetCreateView();
4229 if(pActView)
4231 SwWrtShell* pActShell = pActView->GetWrtShellPtr();
4232 if (State::CONSTANT == m_eState && !lcl_FindShell(m_pActiveShell))
4234 SetActiveShell(pActShell);
4237 if (State::ACTIVE == m_eState && pActShell != GetWrtShell())
4238 SetActiveShell(pActShell);
4239 // Only call HasContentChanged() if the document has changed since last called
4240 else if ((State::ACTIVE == m_eState || (State::CONSTANT == m_eState && pActShell == GetWrtShell())) &&
4241 m_bDocHasChanged)
4243 if (HasContentChanged())
4244 Display(true);
4245 m_bDocHasChanged = false;
4248 else if (State::ACTIVE == m_eState)
4249 clear();
4252 IMPL_LINK(SwContentTree, KeyInputHdl, const KeyEvent&, rEvent, bool)
4254 bool bConsumed = true;
4256 const vcl::KeyCode aCode = rEvent.GetKeyCode();
4257 if (aCode.GetCode() == KEY_MULTIPLY && aCode.IsMod1())
4259 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
4260 if (m_xTreeView->get_selected(xEntry.get()))
4261 ExpandOrCollapseAll(*m_xTreeView, *xEntry);
4263 else if (aCode.GetCode() == KEY_RETURN)
4265 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
4266 if (m_xTreeView->get_selected(xEntry.get()))
4268 switch(aCode.GetModifier())
4270 case KEY_MOD2:
4271 // Switch boxes
4272 GetParentWindow()->ToggleTree();
4273 break;
4274 case KEY_MOD1:
4275 // Switch RootMode
4276 ToggleToRoot();
4277 break;
4278 case 0:
4279 if (lcl_IsContentType(*xEntry, *m_xTreeView))
4281 m_xTreeView->get_row_expanded(*xEntry) ? m_xTreeView->collapse_row(*xEntry)
4282 : m_xTreeView->expand_row(*xEntry);
4284 else
4285 ContentDoubleClickHdl(*m_xTreeView);
4286 break;
4287 case KEY_SHIFT:
4288 m_bSelectTo = true;
4289 ContentDoubleClickHdl(*m_xTreeView);
4290 break;
4294 else if(aCode.GetCode() == KEY_DELETE && 0 == aCode.GetModifier())
4296 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
4297 if (m_xTreeView->get_selected(xEntry.get()) && lcl_IsContent(*xEntry, *m_xTreeView))
4299 assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
4300 if (weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->GetParent()->IsDeletable() &&
4301 !m_pActiveShell->GetView().GetDocShell()->IsReadOnly())
4303 EditEntry(*xEntry, EditEntryMode::DELETE);
4307 //Make KEY_SPACE has same function as DoubleClick, and realize
4308 //multi-selection.
4309 else if (aCode.GetCode() == KEY_SPACE && 0 == aCode.GetModifier())
4311 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
4312 if (m_xTreeView->get_cursor(xEntry.get()))
4314 if (State::HIDDEN != m_eState)
4316 if (State::CONSTANT == m_eState)
4318 m_pActiveShell->GetView().GetViewFrame().GetWindow().ToTop();
4321 SwContent* pCnt = dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)));
4323 if (pCnt && pCnt->GetParent()->GetType() == ContentTypeId::DRAWOBJECT)
4325 SdrView* pDrawView = m_pActiveShell->GetDrawView();
4326 if (pDrawView)
4328 pDrawView->SdrEndTextEdit();
4330 SwDrawModel* pDrawModel = m_pActiveShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel();
4331 SdrPage* pPage = pDrawModel->GetPage(0);
4332 const size_t nCount = pPage->GetObjCount();
4333 bool hasObjectMarked = false;
4335 if (SdrObject* pObject = GetDrawingObjectsByContent(pCnt))
4337 SdrPageView* pPV = pDrawView->GetSdrPageView/*GetPageViewPvNum*/(/*0*/);
4338 if( pPV )
4340 bool bUnMark = pDrawView->IsObjMarked(pObject);
4341 pDrawView->MarkObj( pObject, pPV, bUnMark);
4345 for( size_t i=0; i<nCount; ++i )
4347 SdrObject* pTemp = pPage->GetObj(i);
4348 bool bMark = pDrawView->IsObjMarked(pTemp);
4349 switch( pTemp->GetObjIdentifier() )
4351 case SdrObjKind::Group:
4352 case SdrObjKind::Text:
4353 case SdrObjKind::Line:
4354 case SdrObjKind::Rectangle:
4355 case SdrObjKind::CircleOrEllipse:
4356 case SdrObjKind::CircleSection:
4357 case SdrObjKind::CircleArc:
4358 case SdrObjKind::CircleCut:
4359 case SdrObjKind::Polygon:
4360 case SdrObjKind::PolyLine:
4361 case SdrObjKind::PathLine:
4362 case SdrObjKind::PathFill:
4363 case SdrObjKind::FreehandLine:
4364 case SdrObjKind::FreehandFill:
4365 case SdrObjKind::PathPoly:
4366 case SdrObjKind::PathPolyLine:
4367 case SdrObjKind::Caption:
4368 case SdrObjKind::CustomShape:
4369 if( bMark )
4370 hasObjectMarked = true;
4371 break;
4372 default:
4373 if ( bMark )
4375 SdrPageView* pPV = pDrawView->GetSdrPageView/*GetPageViewPvNum*/(/*0*/);
4376 if (pPV)
4378 pDrawView->MarkObj(pTemp, pPV, true);
4382 //mod end
4384 if ( !hasObjectMarked )
4386 SwEditWin& rEditWindow = m_pActiveShell->GetView().GetEditWin();
4387 vcl::KeyCode tempKeycode( KEY_ESCAPE );
4388 KeyEvent rKEvt( 0 , tempKeycode );
4389 static_cast<vcl::Window*>(&rEditWindow)->KeyInput( rKEvt );
4394 m_bViewHasChanged = true;
4398 else
4400 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
4401 if (m_xTreeView->get_cursor(xEntry.get()))
4403 SwContent* pCnt = dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)));
4404 if (pCnt && pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE)
4406 if (m_bIsRoot && aCode.GetCode() == KEY_LEFT && aCode.GetModifier() == 0)
4408 m_xTreeView->unselect_all();
4409 bConsumed = false;
4411 else if (aCode.IsMod1())
4413 if (aCode.GetCode() == KEY_LEFT)
4414 ExecCommand(u"promote", !aCode.IsShift());
4415 else if (aCode.GetCode() == KEY_RIGHT)
4416 ExecCommand(u"demote", !aCode.IsShift());
4417 else if (aCode.GetCode() == KEY_UP)
4418 ExecCommand(u"chapterup", !aCode.IsShift());
4419 else if (aCode.GetCode() == KEY_DOWN)
4420 ExecCommand(u"chapterdown", !aCode.IsShift());
4421 else if (aCode.GetCode() == KEY_C)
4422 CopyOutlineSelections();
4423 else
4424 bConsumed = false;
4426 else
4427 bConsumed = false;
4429 else
4430 bConsumed = false;
4432 else
4433 bConsumed = false;
4435 return bConsumed;
4438 IMPL_LINK(SwContentTree, QueryTooltipHdl, const weld::TreeIter&, rEntry, OUString)
4440 ContentTypeId nType;
4441 bool bContent = false;
4442 void* pUserData = weld::fromId<void*>(m_xTreeView->get_id(rEntry));
4443 if (lcl_IsContentType(rEntry, *m_xTreeView))
4445 assert(dynamic_cast<SwContentType*>(static_cast<SwTypeNumber*>(pUserData)));
4446 nType = static_cast<SwContentType*>(pUserData)->GetType();
4448 else
4450 assert(dynamic_cast<SwContent*>(static_cast<SwTypeNumber*>(pUserData)));
4451 nType = static_cast<SwContent*>(pUserData)->GetParent()->GetType();
4452 bContent = true;
4454 OUString sEntry;
4455 if(bContent)
4457 switch( nType )
4459 case ContentTypeId::URLFIELD:
4460 assert(dynamic_cast<SwURLFieldContent*>(static_cast<SwTypeNumber*>(pUserData)));
4461 sEntry = static_cast<SwURLFieldContent*>(pUserData)->GetURL();
4462 break;
4464 case ContentTypeId::POSTIT:
4465 assert(dynamic_cast<SwPostItContent*>(static_cast<SwTypeNumber*>(pUserData)));
4466 sEntry = static_cast<SwPostItContent*>(pUserData)->GetName();
4467 break;
4468 case ContentTypeId::OUTLINE:
4469 assert(dynamic_cast<SwOutlineContent*>(static_cast<SwTypeNumber*>(pUserData)));
4470 sEntry = static_cast<SwOutlineContent*>(pUserData)->GetName();
4471 break;
4472 case ContentTypeId::GRAPHIC:
4473 assert(dynamic_cast<SwGraphicContent*>(static_cast<SwTypeNumber*>(pUserData)));
4474 sEntry = static_cast<SwGraphicContent*>(pUserData)->GetLink();
4475 break;
4476 case ContentTypeId::REGION:
4478 assert(dynamic_cast<SwRegionContent*>(static_cast<SwTypeNumber*>(pUserData)));
4479 sEntry = static_cast<SwRegionContent*>(pUserData)->GetName();
4480 const SwSectionFormats& rFormats = GetWrtShell()->GetDoc()->GetSections();
4481 for (SwSectionFormats::size_type n = rFormats.size(); n;)
4483 const SwNodeIndex* pIdx = nullptr;
4484 const SwSectionFormat* pFormat = rFormats[--n];
4485 const SwSection* pSect;
4486 if (nullptr != (pSect = pFormat->GetSection()) &&
4487 pSect->GetSectionName() == sEntry &&
4488 nullptr != (pIdx = pFormat->GetContent().GetContentIdx()) &&
4489 pIdx->GetNode().GetNodes().IsDocNodes())
4491 SwDocStat aDocStat;
4492 SwPaM aPaM(pIdx->GetNode(), *pIdx->GetNode().EndOfSectionNode());
4493 SwDoc::CountWords(aPaM, aDocStat);
4494 sEntry = SwResId(STR_REGION_DEFNAME) + ": " + sEntry + "\n" +
4495 SwResId(FLD_STAT_WORD) + ": " + OUString::number(aDocStat.nWord) + "\n" +
4496 SwResId(FLD_STAT_CHAR) + ": " + OUString::number(aDocStat.nChar);
4497 break;
4501 break;
4502 case ContentTypeId::FOOTNOTE:
4503 case ContentTypeId::ENDNOTE:
4505 assert(dynamic_cast<SwTextFootnoteContent*>(static_cast<SwTypeNumber*>(pUserData)));
4506 const SwTextFootnote* pFootnote =
4507 static_cast<const SwTextFootnoteContent*>(pUserData)->GetTextFootnote();
4509 sEntry = pFootnote->GetFootnote().IsEndNote() ? SwResId(STR_CONTENT_ENDNOTE) :
4510 SwResId(STR_CONTENT_FOOTNOTE);
4512 break;
4513 default: break;
4515 if(static_cast<SwContent*>(pUserData)->IsInvisible())
4517 if(!sEntry.isEmpty())
4518 sEntry += ", ";
4519 sEntry += m_sInvisible;
4522 else
4524 size_t nMemberCount = static_cast<SwContentType*>(pUserData)->GetMemberCount();
4525 sEntry = OUString::number(nMemberCount) + " " +
4526 (nMemberCount == 1
4527 ? static_cast<SwContentType*>(pUserData)->GetSingleName()
4528 : static_cast<SwContentType*>(pUserData)->GetName());
4531 return sEntry;
4534 void SwContentTree::ExecuteContextMenuAction(const OUString& rSelectedPopupEntry)
4536 if (rSelectedPopupEntry == "copy")
4538 CopyOutlineSelections();
4539 return;
4541 if (rSelectedPopupEntry == "collapseallcategories")
4543 std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
4544 bool bEntry = m_xTreeView->get_iter_first(*xEntry);
4545 while (bEntry)
4547 m_xTreeView->collapse_row(*xEntry);
4548 bEntry = m_xTreeView->iter_next_sibling(*xEntry);
4550 return;
4554 std::map<OUString, ContentTypeId> mPopupEntryToContentTypeId
4556 {"tabletracking", ContentTypeId::TABLE},
4557 {"frametracking", ContentTypeId::FRAME},
4558 {"imagetracking", ContentTypeId::GRAPHIC},
4559 {"oleobjecttracking", ContentTypeId::OLE},
4560 {"bookmarktracking", ContentTypeId::BOOKMARK},
4561 {"sectiontracking", ContentTypeId::REGION},
4562 {"hyperlinktracking", ContentTypeId::URLFIELD},
4563 {"referencetracking", ContentTypeId::REFERENCE},
4564 {"indextracking", ContentTypeId::INDEX},
4565 {"commenttracking", ContentTypeId::POSTIT},
4566 {"drawingobjecttracking", ContentTypeId::DRAWOBJECT},
4567 {"fieldtracking", ContentTypeId::TEXTFIELD},
4568 {"footnotetracking", ContentTypeId::FOOTNOTE},
4569 {"endnotetracking", ContentTypeId::ENDNOTE}
4572 if (mPopupEntryToContentTypeId.count(rSelectedPopupEntry))
4574 ContentTypeId eCntTypeId = mPopupEntryToContentTypeId[rSelectedPopupEntry];
4575 SetContentTypeTracking(eCntTypeId, !mTrackContentType[eCntTypeId]);
4576 return;
4580 std::unique_ptr<weld::TreeIter> xFirst(m_xTreeView->make_iterator());
4581 if (!m_xTreeView->get_selected(xFirst.get()))
4582 return; // this shouldn't happen, but better to be safe than ...
4584 if (rSelectedPopupEntry == "sort")
4586 SwContentType* pCntType;
4587 const OUString& rId(m_xTreeView->get_id(*xFirst));
4588 if (lcl_IsContentType(*xFirst, *m_xTreeView))
4589 pCntType = weld::fromId<SwContentType*>(rId);
4590 else
4591 pCntType = const_cast<SwContentType*>(weld::fromId<SwContent*>(rId)->GetParent());
4592 pCntType->SetSortType(!pCntType->GetSortType());
4593 pCntType->FillMemberList();
4594 Display(true);
4595 return;
4597 else if (rSelectedPopupEntry == "deletechapter" ||
4598 rSelectedPopupEntry == "deletetable" ||
4599 rSelectedPopupEntry == "deleteframe" ||
4600 rSelectedPopupEntry == "deleteimage" ||
4601 rSelectedPopupEntry == "deleteoleobject" ||
4602 rSelectedPopupEntry == "deletebookmark" ||
4603 rSelectedPopupEntry == "deletehyperlink" ||
4604 rSelectedPopupEntry == "deletereference" ||
4605 rSelectedPopupEntry == "deleteindex" ||
4606 rSelectedPopupEntry == "deletecomment" ||
4607 rSelectedPopupEntry == "deletedrawingobject" ||
4608 rSelectedPopupEntry == "deletefield")
4610 EditEntry(*xFirst, EditEntryMode::DELETE);
4611 return;
4614 auto nSelectedPopupEntry = rSelectedPopupEntry.toUInt32();
4615 switch (nSelectedPopupEntry)
4617 case TOGGLE_OUTLINE_CONTENT_VISIBILITY:
4618 case HIDE_OUTLINE_CONTENT_VISIBILITY:
4619 case SHOW_OUTLINE_CONTENT_VISIBILITY:
4621 m_pActiveShell->EnterStdMode();
4622 m_bIgnoreDocChange = true;
4623 SwOutlineContent* pCntFirst = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xFirst));
4625 // toggle the outline node outline content visible attribute
4626 if (nSelectedPopupEntry == TOGGLE_OUTLINE_CONTENT_VISIBILITY)
4628 SwNode* pNode = m_pActiveShell->GetDoc()->GetNodes().GetOutLineNds()[pCntFirst->GetOutlinePos()];
4629 pNode->GetTextNode()->SetAttrOutlineContentVisible(
4630 !m_pActiveShell->GetAttrOutlineContentVisible(pCntFirst->GetOutlinePos()));
4632 else
4634 // with subs
4635 SwOutlineNodes::size_type nPos = pCntFirst->GetOutlinePos();
4636 if (lcl_IsContentType(*xFirst, *m_xTreeView)) // Headings root entry
4637 nPos = SwOutlineNodes::npos;
4638 SwOutlineNodes::size_type nOutlineNodesCount = m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount();
4639 int nLevel = -1;
4640 if (nPos != SwOutlineNodes::npos) // not root
4641 nLevel = m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos);
4642 else
4643 nPos = 0;
4644 bool bShow(nSelectedPopupEntry == SHOW_OUTLINE_CONTENT_VISIBILITY);
4647 if (m_pActiveShell->IsOutlineContentVisible(nPos) != bShow)
4648 m_pActiveShell->GetDoc()->GetNodes().GetOutLineNds()[nPos]->GetTextNode()->SetAttrOutlineContentVisible(bShow);
4649 } while (++nPos < nOutlineNodesCount
4650 && (nLevel == -1 || m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos) > nLevel));
4652 m_pActiveShell->InvalidateOutlineContentVisibility();
4653 // show in the document what was toggled
4654 if (lcl_IsContentType(*xFirst, *m_xTreeView)) // Headings root entry
4655 m_pActiveShell->GotoPage(1, true);
4656 else
4657 m_pActiveShell->GotoOutline(pCntFirst->GetOutlinePos());
4658 grab_focus();
4659 m_bIgnoreDocChange = false;
4660 m_pActiveShell->SetModified();
4661 m_pActiveShell->GetDoc()->GetDocShell()->Broadcast(SfxHint(SfxHintId::DocChanged));
4663 break;
4664 case 11:
4665 case 12:
4666 case 13:
4667 nSelectedPopupEntry -= 10;
4668 if(m_nOutlineTracking != nSelectedPopupEntry)
4669 SetOutlineTracking(static_cast<sal_uInt8>(nSelectedPopupEntry));
4670 break;
4671 //Outlinelevel
4672 case 101:
4673 case 102:
4674 case 103:
4675 case 104:
4676 case 105:
4677 case 106:
4678 case 107:
4679 case 108:
4680 case 109:
4681 case 110:
4682 nSelectedPopupEntry -= 100;
4683 if(m_nOutlineLevel != nSelectedPopupEntry )
4684 SetOutlineLevel(static_cast<sal_Int8>(nSelectedPopupEntry));
4685 break;
4686 case 201:
4687 case 202:
4688 case 203:
4689 GetParentWindow()->SetRegionDropMode(static_cast<RegionMode>(nSelectedPopupEntry - 201));
4690 break;
4691 case 401:
4692 case 402:
4693 EditEntry(*xFirst, nSelectedPopupEntry == 401 ? EditEntryMode::RMV_IDX : EditEntryMode::UPD_IDX);
4694 break;
4695 // Edit entry
4696 case 403:
4697 EditEntry(*xFirst, EditEntryMode::EDIT);
4698 break;
4699 case 404:
4700 EditEntry(*xFirst, EditEntryMode::UNPROTECT_TABLE);
4701 break;
4702 case 405 :
4704 const SwTOXBase* pBase = weld::fromId<SwTOXBaseContent*>(m_xTreeView->get_id(*xFirst))
4705 ->GetTOXBase();
4706 m_pActiveShell->SetTOXBaseReadonly(*pBase, !SwEditShell::IsTOXBaseReadonly(*pBase));
4708 break;
4709 case 502 :
4710 EditEntry(*xFirst, EditEntryMode::RENAME);
4711 break;
4712 case 600:
4713 m_pActiveShell->GetView().GetPostItMgr()->Show();
4714 break;
4715 case 601:
4716 m_pActiveShell->GetView().GetPostItMgr()->Hide();
4717 break;
4718 case 602:
4720 m_pActiveShell->GetView().GetPostItMgr()->SetActiveSidebarWin(nullptr);
4721 m_pActiveShell->GetView().GetPostItMgr()->Delete();
4722 break;
4724 case 700:
4726 m_pActiveShell->GetView().GetViewFrame().GetDispatcher()->Execute(FN_OUTLINE_TO_CLIPBOARD);
4727 break;
4729 case 800:
4730 ExpandOrCollapseAll(*m_xTreeView, *xFirst);
4731 break;
4732 case 801:
4733 ExecCommand(u"chapterup", true);
4734 break;
4735 case 802:
4736 ExecCommand(u"chapterdown", true);
4737 break;
4738 case 803:
4739 ExecCommand(u"promote", true);
4740 break;
4741 case 804:
4742 ExecCommand(u"demote", true);
4743 break;
4744 case 805: // select document content
4746 m_pActiveShell->KillPams();
4747 m_pActiveShell->ClearMark();
4748 m_pActiveShell->EnterAddMode();
4749 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xFirst));
4750 const ContentTypeId eTypeId = pCnt->GetParent()->GetType();
4751 if (eTypeId == ContentTypeId::OUTLINE)
4753 SwOutlineNodes::size_type nActPos = weld::fromId<SwOutlineContent*>(
4754 m_xTreeView->get_id(*xFirst))->GetOutlinePos();
4755 m_pActiveShell->GotoOutline(nActPos);
4756 m_xTreeView->selected_foreach([this](weld::TreeIter& rEntry){
4757 SwOutlineNodes::size_type nPos = weld::fromId<SwOutlineContent*>(
4758 m_xTreeView->get_id(rEntry))->GetOutlinePos();
4759 m_pActiveShell->SttSelect();
4760 // select children if not expanded and don't kill PaMs
4761 m_pActiveShell->MakeOutlineSel(nPos, nPos,
4762 !m_xTreeView->get_row_expanded(rEntry), false);
4763 m_pActiveShell->EndSelect();
4764 return false;
4767 else if (eTypeId == ContentTypeId::TABLE)
4769 m_pActiveShell->GotoTable(pCnt->GetName());
4770 m_pActiveShell->GetView().GetViewFrame().GetDispatcher()->Execute(FN_TABLE_SELECT_ALL);
4772 else if (eTypeId == ContentTypeId::REGION)
4774 m_pActiveShell->EnterStdMode();
4775 m_pActiveShell->GotoRegion(pCnt->GetName());
4776 GotoCurrRegionAndSkip(m_pActiveShell->GetCurrentShellCursor(), fnRegionEnd, m_pActiveShell->IsReadOnlyAvailable());
4777 m_pActiveShell->SttSelect();
4778 GotoCurrRegionAndSkip(m_pActiveShell->GetCurrentShellCursor(), fnRegionStart, m_pActiveShell->IsReadOnlyAvailable());
4779 m_pActiveShell->EndSelect();
4780 m_pActiveShell->UpdateCursor();
4782 m_pActiveShell->LeaveAddMode();
4784 break;
4785 case 900:
4787 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xFirst));
4788 GotoContent(pCnt);
4790 break;
4791 //Display
4792 default:
4793 if(nSelectedPopupEntry > 300 && nSelectedPopupEntry < 400)
4795 nSelectedPopupEntry -= 300;
4796 SwView *pView = SwModule::GetFirstView();
4797 while (pView)
4799 nSelectedPopupEntry --;
4800 if(nSelectedPopupEntry == 0)
4802 SetConstantShell(&pView->GetWrtShell());
4803 break;
4805 pView = SwModule::GetNextView(pView);
4807 if(nSelectedPopupEntry)
4809 m_bViewHasChanged = nSelectedPopupEntry == 1;
4810 m_eState = (nSelectedPopupEntry == 1) ? State::ACTIVE : State::HIDDEN;
4811 Display(nSelectedPopupEntry == 1);
4813 GetParentWindow()->UpdateListBox();
4818 void SwContentTree::DeleteOutlineSelections()
4820 auto nChapters(0);
4822 m_pActiveShell->StartAction();
4824 m_pActiveShell->EnterAddMode();
4825 m_xTreeView->selected_foreach([this, &nChapters](weld::TreeIter& rEntry){
4826 ++nChapters;
4827 if (m_xTreeView->iter_has_child(rEntry) &&
4828 !m_xTreeView->get_row_expanded(rEntry)) // only count children if not expanded
4830 nChapters += m_xTreeView->iter_n_children(rEntry);
4832 SwOutlineNodes::size_type nActPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rEntry))->GetOutlinePos();
4833 m_pActiveShell->SttSelect();
4834 m_pActiveShell->MakeOutlineSel(nActPos, nActPos, !m_xTreeView->get_row_expanded(rEntry), false); // select children if not expanded
4835 // The outline selection may already be to the start of the following outline paragraph
4836 // as happens when a table is the last content of the to be deleted outline. In this case
4837 // do not extend the to be deleted selection right or the first character of the following
4838 // outline paragraph will be removed. Also check if no selection was made which indicates
4839 // an empty paragraph and selection right is needed.
4840 if (!m_pActiveShell->IsSttPara() || !m_pActiveShell->HasSelection())
4841 m_pActiveShell->Right(SwCursorSkipMode::Chars, true, 1, false);
4842 m_pActiveShell->EndSelect();
4843 return false;
4845 m_pActiveShell->LeaveAddMode();
4847 SwRewriter aRewriter;
4848 aRewriter.AddRule(UndoArg1, SwResId(STR_CHAPTERS, nChapters));
4849 m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter);
4850 m_pActiveShell->Delete(false);
4851 m_pActiveShell->EndUndo();
4853 m_pActiveShell->EndAction();
4856 void SwContentTree::SetOutlineLevel(sal_uInt8 nSet)
4858 if (nSet == m_nOutlineLevel)
4859 return;
4860 m_nOutlineLevel = nSet;
4861 m_pConfig->SetOutlineLevel( m_nOutlineLevel );
4862 std::unique_ptr<SwContentType>& rpContentT = (State::ACTIVE == m_eState)
4863 ? m_aActiveContentArr[ContentTypeId::OUTLINE]
4864 : m_aHiddenContentArr[ContentTypeId::OUTLINE];
4865 if(rpContentT)
4867 rpContentT->SetOutlineLevel(m_nOutlineLevel);
4868 rpContentT->FillMemberList();
4870 Display(State::ACTIVE == m_eState);
4873 void SwContentTree::SetOutlineTracking(sal_uInt8 nSet)
4875 m_nOutlineTracking = nSet;
4876 m_pConfig->SetOutlineTracking(m_nOutlineTracking);
4879 void SwContentTree::SetContentTypeTracking(ContentTypeId eCntTypeId, bool bSet)
4881 mTrackContentType[eCntTypeId] = bSet;
4882 m_pConfig->SetContentTypeTrack(eCntTypeId, bSet);
4885 // Mode Change: Show dropped Doc
4886 void SwContentTree::ShowHiddenShell()
4888 if(m_pHiddenShell)
4890 m_eState = State::HIDDEN;
4891 Display(false);
4895 // Mode Change: Show active view
4896 void SwContentTree::ShowActualView()
4898 m_eState = State::ACTIVE;
4899 Display(true);
4900 GetParentWindow()->UpdateListBox();
4903 IMPL_LINK_NOARG(SwContentTree, SelectHdl, weld::TreeView&, void)
4905 if (m_pConfig->IsNavigateOnSelect())
4907 ContentDoubleClickHdl(*m_xTreeView);
4908 grab_focus();
4910 Select();
4911 if (m_bIsRoot)
4912 return;
4913 // Select the content type in the Navigate By control
4914 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
4915 if (!m_xTreeView->get_selected(xEntry.get()))
4916 return;
4917 while (m_xTreeView->get_iter_depth(*xEntry))
4918 m_xTreeView->iter_parent(*xEntry);
4919 m_pDialog->SelectNavigateByContentType(m_xTreeView->get_text(*xEntry));
4922 // Here the buttons for moving outlines are en-/disabled.
4923 void SwContentTree::Select()
4925 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
4926 if (!m_xTreeView->get_selected(xEntry.get()))
4927 return;
4929 bool bEnable = false;
4930 std::unique_ptr<weld::TreeIter> xParentEntry(m_xTreeView->make_iterator(xEntry.get()));
4931 bool bParentEntry = m_xTreeView->iter_parent(*xParentEntry);
4932 while (bParentEntry && (!lcl_IsContentType(*xParentEntry, *m_xTreeView)))
4933 bParentEntry = m_xTreeView->iter_parent(*xParentEntry);
4934 if (!m_bIsLastReadOnly)
4936 if (!m_xTreeView->get_visible())
4937 bEnable = true;
4938 else if (bParentEntry)
4940 if ((m_bIsRoot && m_nRootType == ContentTypeId::OUTLINE) ||
4941 (lcl_IsContent(*xEntry, *m_xTreeView) &&
4942 weld::fromId<SwContentType*>(m_xTreeView->get_id(*xParentEntry))->GetType() == ContentTypeId::OUTLINE))
4944 bEnable = true;
4949 SwNavigationPI* pNavi = GetParentWindow();
4950 pNavi->m_xContent6ToolBox->set_item_sensitive("chapterup", bEnable);
4951 pNavi->m_xContent6ToolBox->set_item_sensitive("chapterdown", bEnable);
4952 pNavi->m_xContent6ToolBox->set_item_sensitive("promote", bEnable);
4953 pNavi->m_xContent6ToolBox->set_item_sensitive("demote", bEnable);
4956 void SwContentTree::SetRootType(ContentTypeId nType)
4958 m_nRootType = nType;
4959 m_bIsRoot = true;
4960 m_pConfig->SetRootType( m_nRootType );
4963 OUString SwContentType::RemoveNewline(const OUString& rEntry)
4965 if (rEntry.isEmpty())
4966 return rEntry;
4968 OUStringBuffer aEntry(rEntry);
4969 for (sal_Int32 i = 0; i < rEntry.getLength(); ++i)
4970 if(aEntry[i] == 10 || aEntry[i] == 13)
4971 aEntry[i] = 0x20;
4973 return aEntry.makeStringAndClear();
4976 void SwContentTree::EditEntry(const weld::TreeIter& rEntry, EditEntryMode nMode)
4978 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(rEntry));
4979 GotoContent(pCnt);
4980 const ContentTypeId nType = pCnt->GetParent()->GetType();
4981 sal_uInt16 nSlot = 0;
4983 if(EditEntryMode::DELETE == nMode)
4984 m_bIgnoreDocChange = true;
4986 uno::Reference< container::XNameAccess > xNameAccess, xSecond, xThird;
4987 switch(nType)
4989 case ContentTypeId::OUTLINE :
4990 if(nMode == EditEntryMode::DELETE)
4992 DeleteOutlineSelections();
4994 break;
4996 case ContentTypeId::TABLE :
4997 if(nMode == EditEntryMode::UNPROTECT_TABLE)
4999 m_pActiveShell->GetView().GetDocShell()->
5000 GetDoc()->UnProtectCells( pCnt->GetName());
5002 else if(nMode == EditEntryMode::DELETE)
5004 m_pActiveShell->StartAction();
5005 OUString sTable = SwResId(STR_TABLE_NAME);
5006 SwRewriter aRewriterTableName;
5007 aRewriterTableName.AddRule(UndoArg1, SwResId(STR_START_QUOTE));
5008 aRewriterTableName.AddRule(UndoArg2, pCnt->GetName());
5009 aRewriterTableName.AddRule(UndoArg3, SwResId(STR_END_QUOTE));
5010 sTable = aRewriterTableName.Apply(sTable);
5012 SwRewriter aRewriter;
5013 aRewriter.AddRule(UndoArg1, sTable);
5014 m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter);
5015 m_pActiveShell->GetView().GetViewFrame().GetDispatcher()->Execute(FN_TABLE_SELECT_ALL);
5016 m_pActiveShell->DeleteRow();
5017 m_pActiveShell->EndUndo();
5018 m_pActiveShell->EndAction();
5020 else if(nMode == EditEntryMode::RENAME)
5022 uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
5023 uno::Reference< text::XTextTablesSupplier > xTables(xModel, uno::UNO_QUERY);
5024 xNameAccess = xTables->getTextTables();
5026 else
5027 nSlot = FN_FORMAT_TABLE_DLG;
5028 break;
5030 case ContentTypeId::GRAPHIC :
5031 if(nMode == EditEntryMode::DELETE)
5033 m_pActiveShell->DelRight();
5035 else if(nMode == EditEntryMode::RENAME)
5037 uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
5038 uno::Reference< text::XTextGraphicObjectsSupplier > xGraphics(xModel, uno::UNO_QUERY);
5039 xNameAccess = xGraphics->getGraphicObjects();
5040 uno::Reference< text::XTextFramesSupplier > xFrames(xModel, uno::UNO_QUERY);
5041 xSecond = xFrames->getTextFrames();
5042 uno::Reference< text::XTextEmbeddedObjectsSupplier > xObjs(xModel, uno::UNO_QUERY);
5043 xThird = xObjs->getEmbeddedObjects();
5045 else
5046 nSlot = FN_FORMAT_GRAFIC_DLG;
5047 break;
5049 case ContentTypeId::FRAME :
5050 case ContentTypeId::OLE :
5051 if(nMode == EditEntryMode::DELETE)
5053 m_pActiveShell->DelRight();
5055 else if(nMode == EditEntryMode::RENAME)
5057 uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
5058 uno::Reference< text::XTextFramesSupplier > xFrames(xModel, uno::UNO_QUERY);
5059 uno::Reference< text::XTextEmbeddedObjectsSupplier > xObjs(xModel, uno::UNO_QUERY);
5060 if(ContentTypeId::FRAME == nType)
5062 xNameAccess = xFrames->getTextFrames();
5063 xSecond = xObjs->getEmbeddedObjects();
5065 else
5067 xNameAccess = xObjs->getEmbeddedObjects();
5068 xSecond = xFrames->getTextFrames();
5070 uno::Reference< text::XTextGraphicObjectsSupplier > xGraphics(xModel, uno::UNO_QUERY);
5071 xThird = xGraphics->getGraphicObjects();
5073 else
5074 nSlot = FN_FORMAT_FRAME_DLG;
5075 break;
5076 case ContentTypeId::BOOKMARK :
5077 if(nMode == EditEntryMode::DELETE)
5079 assert(!m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS));
5080 IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess();
5081 pMarkAccess->deleteMark(pMarkAccess->findMark(pCnt->GetName()), false);
5083 else if(nMode == EditEntryMode::RENAME)
5085 assert(!m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS));
5086 uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
5087 uno::Reference< text::XBookmarksSupplier > xBkms(xModel, uno::UNO_QUERY);
5088 xNameAccess = xBkms->getBookmarks();
5090 else
5092 // allowed despite PROTECT_BOOKMARKS: the dialog itself enforces it
5093 SfxStringItem const name(FN_EDIT_BOOKMARK, pCnt->GetName());
5094 SfxPoolItem const* args[2] = { &name, nullptr };
5095 m_pActiveShell->GetView().GetViewFrame().
5096 GetDispatcher()->Execute(FN_EDIT_BOOKMARK, SfxCallMode::SYNCHRON, args);
5098 break;
5100 case ContentTypeId::REGION :
5101 if(nMode == EditEntryMode::RENAME)
5103 uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
5104 uno::Reference< text::XTextSectionsSupplier > xSects(xModel, uno::UNO_QUERY);
5105 xNameAccess = xSects->getTextSections();
5107 else
5108 nSlot = FN_EDIT_REGION;
5109 break;
5111 case ContentTypeId::URLFIELD:
5112 if (nMode == EditEntryMode::DELETE)
5113 nSlot = SID_REMOVE_HYPERLINK;
5114 else
5115 nSlot = SID_EDIT_HYPERLINK;
5116 break;
5117 case ContentTypeId::REFERENCE:
5119 if(nMode == EditEntryMode::DELETE)
5121 const OUString& rName = pCnt->GetName();
5122 for (SfxPoolItem* pItem :
5123 m_pActiveShell->GetDoc()->GetAttrPool().GetItemSurrogates(RES_TXTATR_REFMARK))
5125 assert(dynamic_cast<const SwFormatRefMark*>(pItem));
5126 const auto pFormatRefMark = static_cast<const SwFormatRefMark*>(pItem);
5127 if (!pFormatRefMark)
5128 continue;
5129 const SwTextRefMark* pTextRef = pFormatRefMark->GetTextRefMark();
5130 if (pTextRef && &pTextRef->GetTextNode().GetNodes() ==
5131 &m_pActiveShell->GetNodes() && rName == pFormatRefMark->GetRefName())
5133 m_pActiveShell->GetDoc()->DeleteFormatRefMark(pFormatRefMark);
5134 m_pActiveShell->SwViewShell::UpdateFields();
5135 break;
5140 break;
5141 case ContentTypeId::TEXTFIELD:
5143 if (nMode == EditEntryMode::DELETE)
5145 const SwTextFieldContent* pTextFieldCnt =
5146 static_cast<const SwTextFieldContent*>(pCnt);
5147 const SwTextField* pTextField = pTextFieldCnt->GetFormatField()->GetTextField();
5148 SwTextField::DeleteTextField(*pTextField);
5150 else
5151 nSlot = FN_EDIT_FIELD;
5153 break;
5154 case ContentTypeId::POSTIT:
5156 auto& rView = m_pActiveShell->GetView();
5157 auto pPostItMgr = rView.GetPostItMgr();
5158 pPostItMgr->AssureStdModeAtShell();
5159 pPostItMgr->SetActiveSidebarWin(nullptr);
5160 rView.GetEditWin().GrabFocus();
5161 if(nMode == EditEntryMode::DELETE)
5162 m_pActiveShell->DelRight();
5163 else
5164 nSlot = FN_POSTIT;
5166 break;
5167 case ContentTypeId::INDEX:
5169 const SwTOXBase* pBase = static_cast<SwTOXBaseContent*>(pCnt)->GetTOXBase();
5170 switch(nMode)
5172 case EditEntryMode::EDIT:
5173 if(pBase)
5175 SwPtrItem aPtrItem( FN_INSERT_MULTI_TOX, const_cast<SwTOXBase *>(pBase));
5176 m_pActiveShell->GetView().GetViewFrame().
5177 GetDispatcher()->ExecuteList(FN_INSERT_MULTI_TOX,
5178 SfxCallMode::ASYNCHRON, { &aPtrItem });
5181 break;
5182 case EditEntryMode::RMV_IDX:
5183 case EditEntryMode::DELETE:
5185 if( pBase )
5186 m_pActiveShell->DeleteTOX(*pBase, EditEntryMode::DELETE == nMode);
5188 break;
5189 case EditEntryMode::UPD_IDX:
5190 case EditEntryMode::RENAME:
5192 Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
5193 Reference< XDocumentIndexesSupplier > xIndexes(xModel, UNO_QUERY);
5194 Reference< XIndexAccess> xIdxAcc(xIndexes->getDocumentIndexes());
5195 Reference< XNameAccess >xLocalNameAccess(xIdxAcc, UNO_QUERY);
5196 if(EditEntryMode::RENAME == nMode)
5197 xNameAccess = xLocalNameAccess;
5198 else if(xLocalNameAccess.is() && xLocalNameAccess->hasByName(pBase->GetTOXName()))
5200 Any aIdx = xLocalNameAccess->getByName(pBase->GetTOXName());
5201 Reference< XDocumentIndex> xIdx;
5202 if(aIdx >>= xIdx)
5203 xIdx->update();
5206 break;
5207 default: break;
5210 break;
5211 case ContentTypeId::DRAWOBJECT :
5212 if(EditEntryMode::DELETE == nMode)
5213 nSlot = SID_DELETE;
5214 else if(nMode == EditEntryMode::RENAME)
5215 nSlot = FN_NAME_SHAPE;
5216 else if (nMode == EditEntryMode::EDIT)
5218 vcl::KeyCode aKeyCode(KEY_RETURN, false, false, false, false);
5219 KeyEvent aKeyEvent(0, aKeyCode);
5220 m_pActiveShell->GetWin()->KeyInput(aKeyEvent);
5222 break;
5223 case ContentTypeId::FOOTNOTE:
5224 case ContentTypeId::ENDNOTE:
5225 if (EditEntryMode::EDIT == nMode)
5226 nSlot = FN_FORMAT_FOOTNOTE_DLG;
5227 break;
5228 default: break;
5230 if(nSlot)
5231 m_pActiveShell->GetView().GetViewFrame().
5232 GetDispatcher()->Execute(nSlot, SfxCallMode::SYNCHRON);
5233 else if(xNameAccess.is())
5235 uno::Any aObj = xNameAccess->getByName(pCnt->GetName());
5236 uno::Reference< uno::XInterface > xTmp;
5237 aObj >>= xTmp;
5238 uno::Reference< container::XNamed > xNamed(xTmp, uno::UNO_QUERY);
5239 SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();
5240 ScopedVclPtr<AbstractSwRenameXNamedDlg> pDlg(pFact->CreateSwRenameXNamedDlg(m_xTreeView.get(), xNamed, xNameAccess));
5241 if(xSecond.is())
5242 pDlg->SetAlternativeAccess( xSecond, xThird);
5244 OUString sForbiddenChars;
5245 if(ContentTypeId::BOOKMARK == nType)
5247 sForbiddenChars = "/\\@:*?\";,.#";
5249 else if(ContentTypeId::TABLE == nType)
5251 sForbiddenChars = " .<>";
5253 pDlg->SetForbiddenChars(sForbiddenChars);
5254 pDlg->Execute();
5256 if(EditEntryMode::DELETE == nMode)
5258 auto nPos = m_xTreeView->vadjustment_get_value();
5259 m_bViewHasChanged = true;
5260 TimerUpdate(&m_aUpdTimer);
5261 grab_focus();
5262 m_xTreeView->vadjustment_set_value(nPos);
5266 static void lcl_AssureStdModeAtShell(SwWrtShell* pWrtShell)
5268 // deselect any drawing or frame and leave editing mode
5269 if (SdrView* pSdrView = pWrtShell->GetDrawView())
5271 if (pSdrView->IsTextEdit())
5273 bool bLockView = pWrtShell->IsViewLocked();
5274 pWrtShell->LockView(true);
5275 pWrtShell->EndTextEdit();
5276 pWrtShell->LockView(bLockView);
5278 // go out of the frame
5279 Point aPt(LONG_MIN, LONG_MIN);
5280 pWrtShell->SelectObj(aPt, SW_LEAVE_FRAME);
5283 if (pWrtShell->IsSelFrameMode() || pWrtShell->IsObjSelected())
5285 pWrtShell->UnSelectFrame();
5286 pWrtShell->LeaveSelFrameMode();
5287 pWrtShell->GetView().LeaveDrawCreate();
5288 pWrtShell->EnterStdMode();
5289 pWrtShell->DrawSelChanged();
5290 pWrtShell->GetView().StopShellTimer();
5292 else
5293 pWrtShell->EnterStdMode();
5296 void SwContentTree::CopyOutlineSelections()
5298 m_pActiveShell->LockView(true);
5300 MakeAllOutlineContentTemporarilyVisible a(m_pActiveShell->GetDoc());
5301 lcl_AssureStdModeAtShell(m_pActiveShell);
5302 m_pActiveShell->EnterAddMode();
5303 size_t nCount = m_xTreeView->get_selected_rows().size();
5304 m_xTreeView->selected_foreach([this, &nCount](weld::TreeIter& rEntry){
5305 SwOutlineNodes::size_type nOutlinePos = reinterpret_cast<SwOutlineContent*>(
5306 m_xTreeView->get_id(rEntry).toInt64())->GetOutlinePos();
5307 m_pActiveShell->SttSelect();
5308 m_pActiveShell->MakeOutlineSel(nOutlinePos, nOutlinePos,
5309 !m_xTreeView->get_row_expanded(rEntry), false);
5310 // don't move if this is the last selected outline or the cursor is at start of para
5311 if (--nCount && !m_pActiveShell->IsSttPara())
5312 m_pActiveShell->Right(SwCursorSkipMode::Chars, true, 1, false);
5313 m_pActiveShell->EndSelect();
5314 return false;
5316 m_pActiveShell->LeaveAddMode();
5317 m_pActiveShell->GetView().GetViewFrame().GetBindings().Execute(SID_COPY);
5319 m_pActiveShell->LockView(false);
5322 void SwContentTree::GotoContent(const SwContent* pCnt)
5324 if (m_bSelectTo)
5326 if (m_pActiveShell->IsCursorInTable() ||
5327 (m_pActiveShell->GetCursor()->GetPoint()->nNode.GetIndex() <=
5328 m_pActiveShell->GetDoc()->GetNodes().GetEndOfExtras().GetIndex()))
5330 m_bSelectTo = false;
5331 m_pActiveShell->GetView().GetEditWin().GrabFocus();
5332 return;
5336 m_nLastGotoContentWasOutlinePos = SwOutlineNodes::npos;
5337 m_sSelectedItem = "";
5338 lcl_AssureStdModeAtShell(m_pActiveShell);
5340 std::optional<std::unique_ptr<SwPosition>> oPosition;
5341 if (m_bSelectTo)
5342 oPosition.emplace(new SwPosition(m_pActiveShell->GetCursor()->GetPoint()->nNode,
5343 m_pActiveShell->GetCursor()->GetPoint()->nContent));
5345 switch(m_nLastSelType = pCnt->GetParent()->GetType())
5347 case ContentTypeId::TEXTFIELD:
5349 m_pActiveShell->GotoFormatField(
5350 *static_cast<const SwTextFieldContent*>(pCnt)->GetFormatField());
5352 break;
5353 case ContentTypeId::OUTLINE :
5355 const SwOutlineNodes::size_type nPos =
5356 static_cast<const SwOutlineContent*>(pCnt)->GetOutlinePos();
5357 m_pActiveShell->GotoOutline(nPos);
5358 m_nLastGotoContentWasOutlinePos = nPos;
5360 break;
5361 case ContentTypeId::TABLE :
5363 m_pActiveShell->GotoTable(pCnt->GetName());
5365 break;
5366 case ContentTypeId::FRAME :
5367 case ContentTypeId::GRAPHIC :
5368 case ContentTypeId::OLE :
5370 m_pActiveShell->GotoFly(pCnt->GetName());
5372 break;
5373 case ContentTypeId::BOOKMARK:
5375 m_pActiveShell->StartAction();
5376 m_pActiveShell->GotoMark(pCnt->GetName());
5377 m_pActiveShell->EndAction();
5378 m_sSelectedItem = pCnt->GetName();
5380 // If the hidden title of SwNavigatorPanel was emptied via UNO XPanel interface,
5381 // store the name of the selected bookmark there. This allows to query the
5382 // selected bookmark using UNO e.g. in add-ons, i.e. to disambiguate when
5383 // multiple bookmarks are there on the selected text range.
5384 // Note: this is a workaround because getDialog() of XPanel is not implemented
5385 // for SwNavigatorPanel.
5386 uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
5388 Reference<frame::XController2> xController( xModel->getCurrentController(), uno::UNO_QUERY);
5389 if ( !xController.is() )
5390 break;
5392 Reference<ui::XSidebarProvider> xSidebarProvider = xController->getSidebar();
5393 if ( !xSidebarProvider.is() )
5394 break;
5396 Reference<ui::XDecks> xDecks = xSidebarProvider->getDecks();
5397 if ( !xDecks.is() )
5398 break;
5400 if (!xDecks->hasByName("NavigatorDeck"))
5401 break;
5403 Reference<ui::XDeck> xDeck ( xDecks->getByName("NavigatorDeck"), uno::UNO_QUERY);
5404 if ( !xDeck.is() )
5405 break;
5407 Reference<ui::XPanels> xPanels = xDeck->getPanels();
5408 if ( !xPanels.is() )
5409 break;
5411 if (xPanels->hasByName("SwNavigatorPanel"))
5413 Reference<ui::XPanel> xPanel ( xPanels->getByName("SwNavigatorPanel"), uno::UNO_QUERY);
5414 if ( !xPanel.is() || !xPanel->getTitle().isEmpty() )
5415 break;
5417 xPanel->setTitle( pCnt->GetName() );
5420 break;
5421 case ContentTypeId::REGION :
5423 m_pActiveShell->GotoRegion(pCnt->GetName());
5425 break;
5426 case ContentTypeId::URLFIELD:
5428 if(m_pActiveShell->GotoINetAttr(
5429 *static_cast<const SwURLFieldContent*>(pCnt)->GetINetAttr() ))
5431 m_pActiveShell->Right( SwCursorSkipMode::Chars, true, 1, false);
5432 m_pActiveShell->SwCursorShell::SelectTextAttr( RES_TXTATR_INETFMT, true );
5435 break;
5436 case ContentTypeId::REFERENCE:
5438 m_pActiveShell->GotoRefMark(pCnt->GetName());
5440 break;
5441 case ContentTypeId::INDEX:
5443 const OUString& sName(pCnt->GetName());
5444 if (!m_pActiveShell->GotoNextTOXBase(&sName))
5445 m_pActiveShell->GotoPrevTOXBase(&sName);
5447 break;
5448 case ContentTypeId::POSTIT:
5449 m_pActiveShell->GotoFormatField(*static_cast<const SwPostItContent*>(pCnt)->GetPostIt());
5450 break;
5451 case ContentTypeId::DRAWOBJECT:
5453 m_pActiveShell->GotoDrawingObject(pCnt->GetName());
5455 break;
5456 case ContentTypeId::FOOTNOTE:
5457 case ContentTypeId::ENDNOTE:
5459 const SwTextFootnote* pFootnote =
5460 static_cast<const SwTextFootnoteContent*>(pCnt)->GetTextFootnote();
5461 if (!pFootnote)
5462 return;
5463 m_pActiveShell->GotoFootnoteAnchor(*pFootnote);
5465 break;
5466 default: break;
5469 if (m_bSelectTo)
5471 m_pActiveShell->SttCursorMove();
5472 while (m_pActiveShell->IsCursorInTable())
5474 m_pActiveShell->MoveTable(GotoCurrTable, fnTableStart);
5475 if (!m_pActiveShell->Left(SwCursorSkipMode::Chars, false, 1, false))
5476 break; // Table is at the beginning of the document. It can't be selected this way.
5478 m_pActiveShell->EndCursorMove();
5480 lcl_AssureStdModeAtShell(m_pActiveShell);
5482 m_pActiveShell->SetMark();
5483 m_pActiveShell->GetCursor()->GetMark()->nNode = oPosition.value()->nNode;
5484 m_pActiveShell->GetCursor()->GetMark()->nContent = oPosition.value()->nContent;
5485 m_pActiveShell->UpdateCursor();
5487 m_pActiveShell->GetView().GetEditWin().GrabFocus();
5489 m_bSelectTo = false;
5491 else
5493 if (m_pActiveShell->IsFrameSelected() || m_pActiveShell->IsObjSelected())
5495 m_pActiveShell->HideCursor();
5496 m_pActiveShell->EnterSelFrameMode();
5499 SwView& rView = m_pActiveShell->GetView();
5500 rView.StopShellTimer();
5501 rView.GetPostItMgr()->SetActiveSidebarWin(nullptr);
5502 rView.GetEditWin().GrabFocus();
5504 // Assure cursor is in visible view area.
5505 // (tdf#147041) Always show the navigated outline at the top of the visible view area.
5506 if (pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE ||
5507 (!m_pActiveShell->IsCursorVisible() && !m_pActiveShell->IsFrameSelected() &&
5508 !m_pActiveShell->IsObjSelected()))
5510 Point aPoint(rView.GetVisArea().getX(), m_pActiveShell->GetCursorDocPos().getY());
5511 rView.SetVisArea(aPoint);
5516 // Now even the matching text::Bookmark
5517 NaviContentBookmark::NaviContentBookmark()
5519 m_nDocSh(0),
5520 m_nDefaultDrag( RegionMode::NONE )
5524 NaviContentBookmark::NaviContentBookmark( OUString aUrl,
5525 OUString aDesc,
5526 RegionMode nDragType,
5527 const SwDocShell* pDocSh ) :
5528 m_aUrl(std::move( aUrl )),
5529 m_aDescription(std::move(aDesc)),
5530 m_nDocSh(reinterpret_cast<sal_IntPtr>(pDocSh)),
5531 m_nDefaultDrag( nDragType )
5535 void NaviContentBookmark::Copy( TransferDataContainer& rData ) const
5537 rtl_TextEncoding eSysCSet = osl_getThreadTextEncoding();
5539 OString sStrBuf(OUStringToOString(m_aUrl, eSysCSet) + OStringChar(NAVI_BOOKMARK_DELIM) +
5540 OUStringToOString(m_aDescription, eSysCSet) + OStringChar(NAVI_BOOKMARK_DELIM) +
5541 OString::number(static_cast<int>(m_nDefaultDrag)) + OStringChar(NAVI_BOOKMARK_DELIM) +
5542 OString::number(m_nDocSh));
5543 rData.CopyByteString(SotClipboardFormatId::SONLK, sStrBuf);
5546 bool NaviContentBookmark::Paste( const TransferableDataHelper& rData )
5548 OUString sStr;
5549 bool bRet = rData.GetString( SotClipboardFormatId::SONLK, sStr );
5550 if( bRet )
5552 sal_Int32 nPos = 0;
5553 m_aUrl = sStr.getToken(0, NAVI_BOOKMARK_DELIM, nPos );
5554 m_aDescription = sStr.getToken(0, NAVI_BOOKMARK_DELIM, nPos );
5555 m_nDefaultDrag= static_cast<RegionMode>( o3tl::toInt32(o3tl::getToken(sStr, 0, NAVI_BOOKMARK_DELIM, nPos )) );
5556 m_nDocSh = o3tl::toInt32(o3tl::getToken(sStr, 0, NAVI_BOOKMARK_DELIM, nPos ));
5558 return bRet;
5561 SwNavigationPI* SwContentTree::GetParentWindow()
5563 return m_pDialog;
5566 void SwContentTree::SelectContentType(std::u16string_view rContentTypeName)
5568 std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator());
5569 if (!m_xTreeView->get_iter_first(*xIter))
5570 return;
5573 if (m_xTreeView->get_text(*xIter) == rContentTypeName)
5575 m_xTreeView->set_cursor(*xIter);
5576 Select();
5577 break;
5579 } while (m_xTreeView->iter_next_sibling(*xIter));
5582 IMPL_LINK_NOARG(SwContentTree, OverlayObjectDelayTimerHdl, Timer *, void)
5584 m_aOverlayObjectDelayTimer.Stop();
5585 if (m_xOverlayObject)
5587 if (SdrView* pView = m_pActiveShell->GetDrawView())
5589 if (SdrPaintWindow* pPaintWindow = pView->GetPaintWindow(0))
5591 const rtl::Reference<sdr::overlay::OverlayManager>& xOverlayManager =
5592 pPaintWindow->GetOverlayManager();
5593 xOverlayManager->add(*m_xOverlayObject);
5599 void SwContentTree::OverlayObject(std::vector<basegfx::B2DRange>&& aRanges)
5601 m_aOverlayObjectDelayTimer.Stop();
5602 if (m_xOverlayObject && m_xOverlayObject->getOverlayManager())
5603 m_xOverlayObject->getOverlayManager()->remove(*m_xOverlayObject);
5604 if (aRanges.empty())
5605 m_xOverlayObject.reset();
5606 else
5608 m_xOverlayObject.reset(new sdr::overlay::OverlaySelection(
5609 sdr::overlay::OverlayType::Invert,
5610 Color(), std::move(aRanges), true/*unused for Invert type*/));
5611 m_aOverlayObjectDelayTimer.Start();
5615 void SwContentTree::BringEntryToAttention(const weld::TreeIter& rEntry)
5617 if (lcl_IsContent(rEntry, *m_xTreeView)) // content entry
5619 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(rEntry));
5620 if (pCnt->IsInvisible())
5621 OverlayObject();
5622 else
5624 const ContentTypeId nType = pCnt->GetParent()->GetType();
5625 if (nType == ContentTypeId::OUTLINE)
5627 BringTypesWithFlowFramesToAttention({m_pActiveShell->GetNodes().
5628 GetOutLineNds()[static_cast<SwOutlineContent*>(pCnt)->GetOutlinePos()]});
5630 else if (nType == ContentTypeId::TABLE)
5632 if (const sw::TableFrameFormats* pFrameFormats = m_pActiveShell->GetDoc()->GetTableFrameFormats())
5633 if (const SwTableFormat* pFrameFormat = pFrameFormats->FindFrameFormatByName(pCnt->GetName()))
5635 SwTable* pTable = SwTable::FindTable(pFrameFormat);
5636 if (pTable)
5637 BringTypesWithFlowFramesToAttention({pTable->GetTableNode()});
5640 else if (nType == ContentTypeId::FRAME || nType == ContentTypeId::GRAPHIC ||
5641 nType == ContentTypeId::OLE)
5643 SwNodeType eNodeType = SwNodeType::Text;
5644 if(nType == ContentTypeId::GRAPHIC)
5645 eNodeType = SwNodeType::Grf;
5646 else if(nType == ContentTypeId::OLE)
5647 eNodeType = SwNodeType::Ole;
5648 if (const SwFrameFormat* pFrameFormat =
5649 m_pActiveShell->GetDoc()->FindFlyByName(pCnt->GetName(), eNodeType))
5650 BringFramesToAttention(std::vector<const SwFrameFormat*> {pFrameFormat});
5652 else if (nType == ContentTypeId::BOOKMARK)
5654 BringBookmarksToAttention(std::vector<OUString> {pCnt->GetName()});
5656 else if (nType == ContentTypeId::REGION|| nType == ContentTypeId::INDEX)
5658 const SwSectionFormats& rFormats = m_pActiveShell->GetDoc()->GetSections();
5659 const SwSectionFormat* pFormat = rFormats.FindFormatByName(pCnt->GetName());
5660 if (pFormat)
5661 BringTypesWithFlowFramesToAttention({pFormat->GetSectionNode()});
5663 else if (nType == ContentTypeId::URLFIELD)
5665 BringURLFieldsToAttention(SwGetINetAttrs {SwGetINetAttr(pCnt->GetName(),
5666 *static_cast<SwURLFieldContent*>(pCnt)->GetINetAttr())});
5668 else if (nType == ContentTypeId::REFERENCE)
5670 if (const SwTextAttr* pTextAttr =
5671 m_pActiveShell->GetDoc()->GetRefMark(pCnt->GetName())->GetTextRefMark())
5673 std::vector<const SwTextAttr*> aTextAttrArr {pTextAttr};
5674 BringReferencesToAttention(aTextAttrArr);
5677 else if (nType == ContentTypeId::POSTIT)
5679 if (const SwTextAttr* pTextAttr =
5680 static_cast<SwPostItContent*>(pCnt)->GetPostIt()->GetTextField())
5682 std::vector<const SwTextAttr*> aTextAttrArr {pTextAttr};
5683 BringPostItFieldsToAttention(aTextAttrArr);
5686 else if (nType == ContentTypeId::DRAWOBJECT)
5688 std::vector<const SdrObject*> aSdrObjectArr {GetDrawingObjectsByContent(pCnt)};
5689 BringDrawingObjectsToAttention(aSdrObjectArr);
5691 else if (nType == ContentTypeId::TEXTFIELD)
5693 if (const SwTextAttr* pTextAttr =
5694 static_cast<SwTextFieldContent*>(pCnt)->GetFormatField()->GetTextField())
5696 std::vector<const SwTextAttr*> aTextAttrArr {pTextAttr};
5697 BringTextFieldsToAttention(aTextAttrArr);
5700 else if (nType == ContentTypeId::FOOTNOTE || nType == ContentTypeId::ENDNOTE)
5702 if (const SwTextAttr* pTextAttr =
5703 static_cast<SwTextFootnoteContent*> (pCnt)->GetTextFootnote())
5705 std::vector<const SwTextAttr*> aTextAttrArr {pTextAttr};
5706 BringFootnotesToAttention(aTextAttrArr);
5711 else // content type entry
5713 SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(rEntry));
5714 if (pCntType->GetMemberCount() == 0)
5715 OverlayObject();
5716 else
5718 const ContentTypeId nType = pCntType->GetType();
5719 if (nType == ContentTypeId::OUTLINE)
5721 std::vector<const SwNode*> aNodesArr(
5722 m_pActiveShell->GetNodes().GetOutLineNds().begin(),
5723 m_pActiveShell->GetNodes().GetOutLineNds().end());
5724 BringTypesWithFlowFramesToAttention(aNodesArr);
5726 else if (nType == ContentTypeId::TABLE)
5728 std::vector<const SwNode*> aNodesArr;
5729 const size_t nCount = m_pActiveShell->GetTableFrameFormatCount(false);
5730 const sw::TableFrameFormats& rTableFormats = *m_pActiveShell->GetDoc()->GetTableFrameFormats();
5731 SwAutoFormatGetDocNode aGetHt(&m_pActiveShell->GetNodes());
5732 for(size_t i = 0; i < nCount; ++i)
5734 if (const SwTableFormat* pTableFormat = rTableFormats[i])
5735 if (!pTableFormat->GetInfo(aGetHt)) // skip deleted tables
5737 SwTable* pTable = SwTable::FindTable(pTableFormat);
5738 if (pTable)
5739 aNodesArr.push_back(pTable->GetTableNode());
5742 BringTypesWithFlowFramesToAttention(aNodesArr);
5744 else if (nType == ContentTypeId::FRAME || nType == ContentTypeId::GRAPHIC ||
5745 nType == ContentTypeId::OLE)
5747 FlyCntType eType = FLYCNTTYPE_FRM;
5748 if(nType == ContentTypeId::GRAPHIC)
5749 eType = FLYCNTTYPE_GRF;
5750 else if(nType == ContentTypeId::OLE)
5751 eType = FLYCNTTYPE_OLE;
5752 BringFramesToAttention(m_pActiveShell->GetFlyFrameFormats(eType, true));
5754 else if (nType == ContentTypeId::BOOKMARK)
5756 std::vector<OUString> aNames;
5757 const auto nCount = pCntType->GetMemberCount();
5758 for (size_t i = 0; i < nCount; i++)
5760 const SwContent* pMember = pCntType->GetMember(i);
5761 if (pMember && !pMember->IsInvisible())
5762 aNames.push_back(pMember->GetName());
5764 BringBookmarksToAttention(aNames);
5766 else if (nType == ContentTypeId::REGION || nType == ContentTypeId::INDEX)
5768 std::vector<const SwNode*> aNodesArr;
5769 const SwSectionFormats& rFormats = m_pActiveShell->GetDoc()->GetSections();
5770 const size_t nSize = rFormats.size();
5771 for (SwSectionFormats::size_type n = nSize; n;)
5773 const SwSectionFormat* pSectionFormat = rFormats[--n];
5774 if (pSectionFormat && pSectionFormat->IsInNodesArr())
5776 const SwSection* pSection = pSectionFormat->GetSection();
5777 if (pSection && !pSection->IsHiddenFlag())
5779 const SectionType eSectionType = pSection->GetType();
5780 if (nType == ContentTypeId::REGION &&
5781 (eSectionType == SectionType::ToxContent ||
5782 eSectionType == SectionType::ToxHeader))
5783 continue;
5784 if (nType == ContentTypeId::INDEX &&
5785 eSectionType != SectionType::ToxContent)
5786 continue;
5787 if (const SwNode* pNode = pSectionFormat->GetSectionNode())
5788 aNodesArr.push_back(pNode);
5792 BringTypesWithFlowFramesToAttention(aNodesArr);
5794 else if (nType == ContentTypeId::URLFIELD)
5796 SwGetINetAttrs aINetAttrsArr;
5797 m_pActiveShell->GetINetAttrs(aINetAttrsArr, false);
5798 BringURLFieldsToAttention(aINetAttrsArr);
5800 else if (nType == ContentTypeId::REFERENCE)
5802 std::vector<const SwTextAttr*> aTextAttrArr;
5803 for (const SfxPoolItem* pItem :
5804 m_pActiveShell->GetAttrPool().GetItemSurrogates(RES_TXTATR_REFMARK))
5806 if (const auto pRefMark = dynamic_cast<const SwFormatRefMark*>(pItem))
5808 const SwTextRefMark* pTextRef = pRefMark->GetTextRefMark();
5809 if (pTextRef && &pTextRef->GetTextNode().GetNodes() ==
5810 &m_pActiveShell->GetNodes())
5811 aTextAttrArr.push_back(pTextRef);
5814 BringReferencesToAttention(aTextAttrArr);
5816 else if (nType == ContentTypeId::POSTIT)
5818 std::vector<const SwTextAttr*> aTextAttrArr;
5819 const auto nCount = pCntType->GetMemberCount();
5820 for (size_t i = 0; i < nCount; i++)
5822 const SwPostItContent* pPostItContent = static_cast<const SwPostItContent*>(
5823 pCntType->GetMember(i));
5824 if (pPostItContent && !pPostItContent->IsInvisible())
5825 if (const SwFormatField* pFormatField = pPostItContent->GetPostIt())
5826 if (const SwTextAttr* pTextAttr = pFormatField->GetTextField())
5827 aTextAttrArr.push_back(pTextAttr);
5829 BringPostItFieldsToAttention(aTextAttrArr);
5831 else if (nType == ContentTypeId::DRAWOBJECT)
5833 IDocumentDrawModelAccess& rIDDMA = m_pActiveShell->getIDocumentDrawModelAccess();
5834 if (const SwDrawModel* pModel = rIDDMA.GetDrawModel())
5836 if (const SdrPage* pPage = pModel->GetPage(0))
5838 if (const size_t nCount = pPage->GetObjCount())
5840 std::vector<const SdrObject*> aSdrObjectArr;
5841 for (size_t i = 0; i < nCount; ++i)
5843 const SdrObject* pObject = pPage->GetObj(i);
5844 if (pObject && !pObject->GetName().isEmpty() &&
5845 rIDDMA.IsVisibleLayerId(pObject->GetLayer()))
5846 aSdrObjectArr.push_back(pObject);
5848 BringDrawingObjectsToAttention(aSdrObjectArr);
5853 else if (nType == ContentTypeId::TEXTFIELD)
5855 std::vector<const SwTextAttr*> aTextAttrArr;
5856 const auto nCount = pCntType->GetMemberCount();
5857 for (size_t i = 0; i < nCount; i++)
5859 const SwTextFieldContent* pTextFieldCnt =
5860 static_cast<const SwTextFieldContent*>(pCntType->GetMember(i));
5861 if (pTextFieldCnt && !pTextFieldCnt->IsInvisible())
5862 if (const SwFormatField* pFormatField = pTextFieldCnt->GetFormatField())
5863 if (const SwTextAttr* pTextAttr = pFormatField->GetTextField())
5864 aTextAttrArr.push_back(pTextAttr);
5866 BringTextFieldsToAttention(aTextAttrArr);
5868 else if (nType == ContentTypeId::FOOTNOTE || nType == ContentTypeId::ENDNOTE)
5870 std::vector<const SwTextAttr*> aTextAttrArr;
5871 const auto nCount = pCntType->GetMemberCount();
5872 for (size_t i = 0; i < nCount; i++)
5874 const SwTextFootnoteContent* pTextFootnoteCnt =
5875 static_cast<const SwTextFootnoteContent*>(pCntType->GetMember(i));
5876 if (pTextFootnoteCnt && !pTextFootnoteCnt->IsInvisible())
5877 if (const SwTextAttr* pTextAttr = pTextFootnoteCnt->GetTextFootnote())
5878 aTextAttrArr.push_back(pTextAttr);
5880 BringFootnotesToAttention(aTextAttrArr);
5886 static void lcl_CalcOverlayRanges(const SwTextFrame* pStartFrame, const SwTextFrame* pEndFrame,
5887 const SwPosition& aStartPos, const SwPosition& aEndPos,
5888 std::vector<basegfx::B2DRange>& aRanges)
5890 if (pStartFrame && pEndFrame)
5892 SwRect aStartCharRect;
5893 pStartFrame->GetCharRect(aStartCharRect, aStartPos);
5894 SwRect aEndCharRect;
5895 pEndFrame->GetCharRect(aEndCharRect, aEndPos);
5896 if (aStartCharRect.Top() == aEndCharRect.Top())
5898 // single line range
5899 aRanges.emplace_back(aStartCharRect.Left(), aStartCharRect.Top(),
5900 aEndCharRect.Right() + 1, aEndCharRect.Bottom() + 1);
5902 else
5904 // multi line range
5905 SwRect aFrameRect = pStartFrame->getFrameArea();
5906 aRanges.emplace_back(aStartCharRect.Left(), aStartCharRect.Top(),
5907 aFrameRect.Right(), aStartCharRect.Bottom() + 1);
5908 if (aStartCharRect.Bottom() + 1 != aEndCharRect.Top())
5909 aRanges.emplace_back(aFrameRect.Left(), aStartCharRect.Bottom() + 1,
5910 aFrameRect.Right(), aEndCharRect.Top() + 1);
5911 aRanges.emplace_back(aFrameRect.Left(), aEndCharRect.Top() + 1,
5912 aEndCharRect.Right() + 1, aEndCharRect.Bottom() + 1);
5917 void SwContentTree::BringFramesToAttention(const std::vector<const SwFrameFormat*>& rFrameFormats)
5919 std::vector<basegfx::B2DRange> aRanges;
5920 for (const SwFrameFormat* pFrameFormat : rFrameFormats)
5922 if (!pFrameFormat)
5923 continue;
5924 SwRect aFrameRect = pFrameFormat->FindLayoutRect();
5925 if (!aFrameRect.IsEmpty())
5926 aRanges.emplace_back(aFrameRect.Left(), aFrameRect.Top(), aFrameRect.Right(),
5927 aFrameRect.Bottom());
5929 OverlayObject(std::move(aRanges));
5932 void SwContentTree::BringBookmarksToAttention(const std::vector<OUString>& rNames)
5934 std::vector<basegfx::B2DRange> aRanges;
5935 IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess();
5936 for (const auto& rName : rNames)
5938 IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findBookmark(rName);
5939 if (ppBkmk == pMarkAccess->getBookmarksEnd())
5940 continue;
5941 SwPosition aMarkStart = (*ppBkmk)->GetMarkStart();
5942 const SwTextNode* pMarkStartTextNode = aMarkStart.GetNode().GetTextNode();
5943 if (!pMarkStartTextNode)
5944 continue;
5945 const SwTextFrame* pMarkStartFrame = static_cast<const SwTextFrame*>(
5946 pMarkStartTextNode->getLayoutFrame(m_pActiveShell->GetLayout()));
5947 if (!pMarkStartFrame)
5948 continue;
5949 SwPosition aMarkEnd = (*ppBkmk)->GetMarkEnd();
5950 const SwTextNode* pMarkEndTextNode = aMarkEnd.GetNode().GetTextNode();
5951 if (!pMarkEndTextNode)
5952 continue;
5953 const SwTextFrame* pMarkEndFrame = static_cast<const SwTextFrame*>(
5954 pMarkEndTextNode->getLayoutFrame(m_pActiveShell->GetLayout()));
5955 if (!pMarkEndFrame)
5956 continue;
5957 // adjust span when mark start equals mark end
5958 if (aMarkStart == aMarkEnd)
5960 if (aMarkEnd.GetContentIndex() < pMarkEndTextNode->Len() - 1)
5961 aMarkEnd.AdjustContent(+1);
5962 else if (aMarkStart.GetContentIndex() > 0)
5963 aMarkStart.AdjustContent(-1);
5965 lcl_CalcOverlayRanges(pMarkStartFrame, pMarkEndFrame, aMarkStart, aMarkEnd, aRanges);
5967 OverlayObject(std::move(aRanges));
5970 void SwContentTree::BringTypesWithFlowFramesToAttention(const std::vector<const SwNode*>& rNodes)
5972 std::vector<basegfx::B2DRange> aRanges;
5973 for (const auto* pNode : rNodes)
5975 if (!pNode)
5976 continue;
5977 SwNode2Layout aTmp(*pNode, pNode->GetIndex() - 1);
5978 SwFrame* pFrame = aTmp.NextFrame();
5979 while (pFrame)
5981 const SwRect& rFrameRect = pFrame->getFrameArea();
5982 if (!rFrameRect.IsEmpty())
5983 aRanges.emplace_back(rFrameRect.Left(), rFrameRect.Top(), rFrameRect.Right(),
5984 rFrameRect.Bottom());
5985 if (!pFrame->IsFlowFrame())
5986 break;
5987 SwFlowFrame *pFollow = SwFlowFrame::CastFlowFrame(pFrame)->GetFollow();
5988 if (!pFollow)
5989 break;
5990 pFrame = &pFollow->GetFrame();
5993 OverlayObject(std::move(aRanges));
5996 void SwContentTree::BringURLFieldsToAttention(const SwGetINetAttrs& rINetAttrsArr)
5998 std::vector<basegfx::B2DRange> aRanges;
5999 for (const auto& r : rINetAttrsArr)
6001 const SwTextNode& rTextNode = r.rINetAttr.GetTextNode();
6002 if (SwTextFrame* pFrame = static_cast<SwTextFrame*>(
6003 rTextNode.getLayoutFrame(m_pActiveShell->GetLayout())))
6005 auto nStart = r.rINetAttr.GetStart();
6006 auto nEnd = r.rINetAttr.GetAnyEnd();
6007 SwPosition aStartPos(rTextNode, nStart), aEndPos(rTextNode, nEnd);
6008 lcl_CalcOverlayRanges(pFrame, pFrame, aStartPos, aEndPos, aRanges);
6011 OverlayObject(std::move(aRanges));
6014 void SwContentTree::BringReferencesToAttention(std::vector<const SwTextAttr*>& rTextAttrsArr)
6016 std::vector<basegfx::B2DRange> aRanges;
6017 for (const SwTextAttr* p : rTextAttrsArr)
6019 if (!p)
6020 continue;
6021 const SwTextRefMark* pTextRefMark = p->GetRefMark().GetTextRefMark();
6022 if (!pTextRefMark)
6023 continue;
6024 const SwTextNode& rTextNode = pTextRefMark->GetTextNode();
6025 if (SwTextFrame* pFrame = static_cast<SwTextFrame*>(
6026 rTextNode.getLayoutFrame(m_pActiveShell->GetLayout())))
6028 auto nStart = p->GetStart();
6029 auto nEnd = p->GetAnyEnd();
6030 SwPosition aStartPos(rTextNode, nStart), aEndPos(rTextNode, nEnd);
6031 lcl_CalcOverlayRanges(pFrame, pFrame, aStartPos, aEndPos, aRanges);
6034 OverlayObject(std::move(aRanges));
6037 void SwContentTree::BringPostItFieldsToAttention(std::vector<const SwTextAttr*>& rTextAttrsArr)
6039 std::vector<basegfx::B2DRange> aRanges;
6040 for (const SwTextAttr* p : rTextAttrsArr)
6042 if (!p)
6043 continue;
6044 const SwTextField* pTextField = p->GetFormatField().GetTextField();
6045 if (!pTextField)
6046 continue;
6047 // use as a fallback when there is no mark
6048 SwTextNode& rTextNode = pTextField->GetTextNode();
6049 if (!rTextNode.getLayoutFrame(m_pActiveShell->GetLayout()))
6050 continue;
6051 assert(dynamic_cast<const SwTextAnnotationField*>(pTextField));
6052 const SwTextAnnotationField* pTextAnnotationField =
6053 static_cast<const SwTextAnnotationField*>(pTextField);
6054 const ::sw::mark::IMark* pAnnotationMark = pTextAnnotationField->GetAnnotationMark();
6055 const SwPosition aMarkStart = pAnnotationMark ? pAnnotationMark->GetMarkStart()
6056 : SwPosition(rTextNode, p->GetStart());
6057 const SwPosition aMarkEnd = pAnnotationMark ? pAnnotationMark->GetMarkEnd()
6058 : SwPosition(rTextNode, p->GetAnyEnd());
6059 const SwTextFrame* pMarkStartFrame = static_cast<SwTextFrame*>(
6060 aMarkStart.GetNode().GetTextNode()->getLayoutFrame(m_pActiveShell->GetLayout()));
6061 const SwTextFrame* pMarkEndFrame = static_cast<SwTextFrame*>(
6062 aMarkEnd.GetNode().GetTextNode()->getLayoutFrame(m_pActiveShell->GetLayout()));
6063 if (!pMarkStartFrame || !pMarkEndFrame)
6064 continue;
6065 lcl_CalcOverlayRanges(pMarkStartFrame, pMarkEndFrame, aMarkStart,
6066 aMarkEnd, aRanges);
6068 OverlayObject(std::move(aRanges));
6071 void SwContentTree::BringFootnotesToAttention(std::vector<const SwTextAttr*>& rTextAttrsArr)
6073 std::vector<basegfx::B2DRange> aRanges;
6074 for (const SwTextAttr* p : rTextAttrsArr)
6076 if (!p)
6077 continue;
6078 const SwTextFootnote* pTextFootnote = p->GetFootnote().GetTextFootnote();
6079 if (!pTextFootnote)
6080 continue;
6081 const SwTextNode& rTextNode = pTextFootnote->GetTextNode();
6082 if (SwTextFrame* pFrame = static_cast<SwTextFrame*>(
6083 rTextNode.getLayoutFrame(m_pActiveShell->GetLayout())))
6085 auto nStart = p->GetStart();
6086 auto nEnd = nStart + 1;
6087 SwPosition aStartPos(rTextNode, nStart), aEndPos(rTextNode, nEnd);
6088 lcl_CalcOverlayRanges(pFrame, pFrame, aStartPos, aEndPos, aRanges);
6091 OverlayObject(std::move(aRanges));
6094 void SwContentTree::BringDrawingObjectsToAttention(std::vector<const SdrObject*>& rDrawingObjectsArr)
6096 std::vector<basegfx::B2DRange> aRanges;
6097 for (const SdrObject* pObject : rDrawingObjectsArr)
6099 if (pObject)
6101 tools::Rectangle aRect(pObject->GetLogicRect());
6102 if (!aRect.IsEmpty())
6103 aRanges.emplace_back(aRect.Left(), aRect.Top(), aRect.Right(), aRect.Bottom());
6106 OverlayObject(std::move(aRanges));
6109 void SwContentTree::BringTextFieldsToAttention(std::vector<const SwTextAttr*>& rTextAttrsArr)
6111 std::vector<basegfx::B2DRange> aRanges;
6112 std::shared_ptr<SwPaM> pPamForTextField;
6113 for (const SwTextAttr* p : rTextAttrsArr)
6115 if (!p)
6116 continue;
6117 const SwTextField* pTextField = p->GetFormatField().GetTextField();
6118 if (!pTextField)
6119 continue;
6120 if (SwTextFrame* pFrame = static_cast<SwTextFrame*>(
6121 pTextField->GetTextNode().getLayoutFrame(m_pActiveShell->GetLayout())))
6123 SwTextField::GetPamForTextField(*pTextField, pPamForTextField);
6124 if (!pPamForTextField)
6125 continue;
6126 SwPosition aStartPos(*pPamForTextField->GetMark());
6127 SwPosition aEndPos(*pPamForTextField->GetPoint());
6128 lcl_CalcOverlayRanges(pFrame, pFrame, aStartPos, aEndPos, aRanges);
6131 OverlayObject(std::move(aRanges));
6134 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */