docthemes: Save themes def. to a file when added to ColorSets
[LibreOffice.git] / sw / source / uibase / utlui / content.cxx
blob42c2be038a7dfd0c2bc58e871e489ebaaf8148dc
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/lok.hxx>
21 #include <comphelper/string.hxx>
22 #include <editeng/frmdiritem.hxx>
23 #include <svl/urlbmk.hxx>
24 #include <osl/thread.h>
25 #include <sal/log.hxx>
26 #include <tools/urlobj.hxx>
27 #include <sfx2/docfile.hxx>
28 #include <sfx2/dispatch.hxx>
29 #include <sfx2/event.hxx>
30 #include <sfx2/viewfrm.hxx>
31 #include <o3tl/enumrange.hxx>
32 #include <o3tl/sorted_vector.hxx>
33 #include <utility>
34 #include <vcl/commandevent.hxx>
35 #include <vcl/weldutils.hxx>
36 #include <sot/formats.hxx>
37 #include <o3tl/string_view.hxx>
38 #include <uiitems.hxx>
39 #include <fmtanchr.hxx>
40 #include <fmtinfmt.hxx>
41 #include <txtinet.hxx>
42 #include <fmtfld.hxx>
43 #include <swmodule.hxx>
44 #include <wrtsh.hxx>
45 #include <view.hxx>
46 #include <docsh.hxx>
47 #include <drawdoc.hxx>
48 #include <content.hxx>
49 #include <frmatr.hxx>
50 #include <frmfmt.hxx>
51 #include <fldbas.hxx>
52 #include <IMark.hxx>
53 #include <section.hxx>
54 #include <tox.hxx>
55 #include <navipi.hxx>
56 #include <navicont.hxx>
57 #include <navicfg.hxx>
58 #include <edtwin.hxx>
59 #include <doc.hxx>
60 #include <IDocumentSettingAccess.hxx>
61 #include <IDocumentDrawModelAccess.hxx>
62 #include <IDocumentOutlineNodes.hxx>
63 #include <unotxvw.hxx>
64 #include <cmdid.h>
65 #include <helpids.h>
66 #include <strings.hrc>
67 #include <com/sun/star/text/XTextSectionsSupplier.hpp>
68 #include <com/sun/star/text/XTextGraphicObjectsSupplier.hpp>
69 #include <com/sun/star/text/XTextTablesSupplier.hpp>
70 #include <com/sun/star/text/XDocumentIndexesSupplier.hpp>
71 #include <com/sun/star/text/XDocumentIndex.hpp>
72 #include <com/sun/star/text/XBookmarksSupplier.hpp>
73 #include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp>
74 #include <com/sun/star/text/XTextFramesSupplier.hpp>
75 #include <com/sun/star/ui/XSidebarProvider.hpp>
76 #include <com/sun/star/ui/XDecks.hpp>
77 #include <com/sun/star/ui/XDeck.hpp>
78 #include <com/sun/star/ui/XPanels.hpp>
79 #include <com/sun/star/ui/XPanel.hpp>
80 #include <svx/svdpage.hxx>
81 #include <svx/svdview.hxx>
82 #include <SwRewriter.hxx>
83 #include <hints.hxx>
84 #include <numrule.hxx>
85 #include <swundo.hxx>
86 #include <ndtxt.hxx>
87 #include <PostItMgr.hxx>
88 #include <postithelper.hxx>
90 #include <swabstdlg.hxx>
91 #include <bitmaps.hlst>
93 #include <AnnotationWin.hxx>
94 #include <memory>
96 #include <fmtcntnt.hxx>
97 #include <docstat.hxx>
99 #include <viewopt.hxx>
101 #include <IDocumentFieldsAccess.hxx>
102 #include <txtfld.hxx>
103 #include <fldmgr.hxx>
105 #include <frameformats.hxx>
107 #include <ftnidx.hxx>
108 #include <txtftn.hxx>
109 #include <fmtftn.hxx>
111 #include <txtannotationfld.hxx>
112 #include <txtfrm.hxx>
113 #include <txtrfmrk.hxx>
114 #include <svx/sdr/overlay/overlayselection.hxx>
115 #include <svx/sdr/overlay/overlayobject.hxx>
116 #include <svx/sdr/overlay/overlaymanager.hxx>
117 #include <svx/sdrpaintwindow.hxx>
118 #include <node2lay.hxx>
120 #include <sectfrm.hxx>
122 #include <docufld.hxx>
124 #include <svl/fstathelper.hxx>
126 #include <expfld.hxx>
127 #include <unotxdoc.hxx>
128 #include <unoidxcoll.hxx>
130 #define CTYPE_CNT 0
131 #define CTYPE_CTT 1
133 using namespace ::com::sun::star;
134 using namespace ::com::sun::star::text;
135 using namespace ::com::sun::star::uno;
136 using namespace ::com::sun::star::container;
138 namespace {
141 Symbolic name representations of numeric values used for the Outline Content Visibility popup
142 menu item ids. The numbers are chosen arbitrarily to not over overlap other menu item ids.
143 see: SwContentTree::ExecuteContextMenuAction, navigatorcontextmenu.ui
145 1512 toggle outline content visibility of the selected outline entry
146 1513 make the outline content of the selected outline entry and children not visible
147 1514 make the outline content of the selected entry and children visible
149 const sal_uInt32 TOGGLE_OUTLINE_CONTENT_VISIBILITY = 1512;
150 const sal_uInt32 HIDE_OUTLINE_CONTENT_VISIBILITY = 1513;
151 const sal_uInt32 SHOW_OUTLINE_CONTENT_VISIBILITY = 1514;
153 constexpr char NAVI_BOOKMARK_DELIM = '\x01';
157 class SwContentArr
158 : public o3tl::sorted_vector<std::unique_ptr<SwContent>, o3tl::less_ptr_to,
159 o3tl::find_partialorder_ptrequals>
163 namespace
165 std::map<OUString, std::map<void*, bool>> lcl_DocOutLineExpandStateMap;
167 bool lcl_IsContent(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView)
169 return weld::fromId<const SwTypeNumber*>(rTreeView.get_id(rEntry))->GetTypeId() == CTYPE_CNT;
172 bool lcl_IsContentType(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView)
174 return weld::fromId<const SwTypeNumber*>(rTreeView.get_id(rEntry))->GetTypeId() == CTYPE_CTT;
177 bool lcl_IsLowerOutlineContent(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView, sal_uInt8 nLevel)
179 return weld::fromId<const SwOutlineContent*>(rTreeView.get_id(rEntry))->GetOutlineLevel() < nLevel;
182 bool lcl_FindShell(SwWrtShell const * pShell)
184 bool bFound = false;
185 SwView *pView = SwModule::GetFirstView();
186 while (pView)
188 if(pShell == &pView->GetWrtShell())
190 bFound = true;
191 break;
193 pView = SwModule::GetNextView(pView);
195 return bFound;
198 bool lcl_IsUiVisibleBookmark(const ::sw::mark::MarkBase* pMark)
200 return IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::BOOKMARK;
203 OUString lcl_GetFootnoteText(const SwTextFootnote& rTextFootnote)
205 SwNodeIndex aIdx(*rTextFootnote.GetStartNode(), 1);
206 SwContentNode* pCNd = aIdx.GetNode().GetTextNode();
207 if(!pCNd)
208 pCNd = SwNodes::GoNext(&aIdx);
209 return pCNd->IsTextNode() ? static_cast<SwTextNode*>(pCNd)->GetText() : OUString();
212 void getAnchorPos(SwPosition& rPos)
214 // get the top most anchor position of the position
215 if (SwFrameFormat* pFlyFormat = rPos.GetNode().GetFlyFormat())
217 SwNode* pAnchorNode;
218 SwFrameFormat* pTmp = pFlyFormat;
219 while (pTmp && (pAnchorNode = pTmp->GetAnchor().GetAnchorNode()) &&
220 (pTmp = pAnchorNode->GetFlyFormat()))
222 pFlyFormat = pTmp;
224 if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor())
225 rPos = *pPos;
229 bool lcl_IsLowerRegionContent(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView, sal_uInt8 nLevel)
231 return weld::fromId<const SwRegionContent*>(rTreeView.get_id(rEntry))->GetRegionLevel() < nLevel;
234 void lcl_SelectAllFootnotesOrEndnotes(SwWrtShell& rWrtShell, SwContentType* pContentType)
236 const auto nCount = pContentType->GetMemberCount();
237 if (nCount == 0)
238 return;
240 rWrtShell.AssureStdMode();
241 SwCursor* pCursor = rWrtShell.getShellCursor(true);
243 rWrtShell.StartAction();
244 rWrtShell.EnterAddMode();
245 for (size_t i = 0; i < nCount; i++)
247 const SwTextFootnoteContent* pTextFootnoteCnt
248 = static_cast<const SwTextFootnoteContent*>(pContentType->GetMember(i));
249 if (pTextFootnoteCnt && !pTextFootnoteCnt->IsInvisible())
251 if (const SwTextAttr* pTextAttr = pTextFootnoteCnt->GetTextFootnote())
253 const SwTextFootnote* pTextFootnote
254 = pTextAttr->GetFootnote().GetTextFootnote();
255 if (!pTextFootnote)
256 continue;
257 const SwTextNode& rTextNode = pTextFootnote->GetTextNode();
258 auto nStart = pTextAttr->GetStart();
259 pCursor->GetPoint()->Assign(rTextNode, nStart + 1);
260 rWrtShell.SetMark();
261 rWrtShell.SttSelect();
262 pCursor->GetPoint()->Assign(rTextNode, nStart);
263 rWrtShell.EndSelect();
267 rWrtShell.LeaveAddMode();
268 rWrtShell.EndAction();
272 // Content, contains names and reference at the content type.
274 SwContent::SwContent(const SwContentType* pCnt, OUString aName, double nYPos) :
275 SwTypeNumber(CTYPE_CNT),
276 m_pParent(pCnt),
277 m_sContentName(std::move(aName)),
278 m_nYPosition(nYPos),
279 m_bInvisible(false)
284 SwTypeNumber::~SwTypeNumber()
288 bool SwContent::IsProtect() const
290 return false;
293 bool SwTextFieldContent::IsProtect() const
295 return m_pFormatField->IsProtect();
298 bool SwPostItContent::IsProtect() const
300 return m_pField->IsProtect();
303 bool SwURLFieldContent::IsProtect() const
305 return m_pINetAttr->IsProtect();
308 bool SwRegionContent::IsProtect() const
310 return m_pSectionFormat->GetSection()->IsProtect();
313 SwGraphicContent::~SwGraphicContent()
317 SwTOXBaseContent::~SwTOXBaseContent()
321 const TranslateId STR_CONTENT_TYPE_ARY[] =
323 STR_CONTENT_TYPE_OUTLINE,
324 STR_CONTENT_TYPE_TABLE,
325 STR_CONTENT_TYPE_FRAME,
326 STR_CONTENT_TYPE_GRAPHIC,
327 STR_CONTENT_TYPE_OLE,
328 STR_CONTENT_TYPE_BOOKMARK,
329 STR_CONTENT_TYPE_REGION,
330 STR_CONTENT_TYPE_URLFIELD,
331 STR_CONTENT_TYPE_REFERENCE,
332 STR_CONTENT_TYPE_INDEX,
333 STR_CONTENT_TYPE_POSTIT,
334 STR_CONTENT_TYPE_DRAWOBJECT,
335 STR_CONTENT_TYPE_TEXTFIELD,
336 STR_CONTENT_TYPE_FOOTNOTE,
337 STR_CONTENT_TYPE_ENDNOTE
340 const TranslateId STR_CONTENT_TYPE_SINGLE_ARY[] =
342 STR_CONTENT_TYPE_SINGLE_OUTLINE,
343 STR_CONTENT_TYPE_SINGLE_TABLE,
344 STR_CONTENT_TYPE_SINGLE_FRAME,
345 STR_CONTENT_TYPE_SINGLE_GRAPHIC,
346 STR_CONTENT_TYPE_SINGLE_OLE,
347 STR_CONTENT_TYPE_SINGLE_BOOKMARK,
348 STR_CONTENT_TYPE_SINGLE_REGION,
349 STR_CONTENT_TYPE_SINGLE_URLFIELD,
350 STR_CONTENT_TYPE_SINGLE_REFERENCE,
351 STR_CONTENT_TYPE_SINGLE_INDEX,
352 STR_CONTENT_TYPE_SINGLE_POSTIT,
353 STR_CONTENT_TYPE_SINGLE_DRAWOBJECT,
354 STR_CONTENT_TYPE_SINGLE_TEXTFIELD,
355 STR_CONTENT_TYPE_SINGLE_FOOTNOTE,
356 STR_CONTENT_TYPE_SINGLE_ENDNOTE
359 namespace
361 bool checkVisibilityChanged(
362 const SwContentArr& rSwContentArrA,
363 const SwContentArr& rSwContentArrB)
365 if(rSwContentArrA.size() != rSwContentArrB.size())
367 return true;
370 for(size_t a(0); a < rSwContentArrA.size(); a++)
372 if(rSwContentArrA[a]->IsInvisible() != rSwContentArrB[a]->IsInvisible())
374 return true;
378 return false;
380 // Gets "YPos" for content, i.e. a number used to sort content members in Navigator's list
381 sal_Int32 getYPos(const SwNode& rNode)
383 SwNodeOffset nIndex = rNode.GetIndex();
384 if (rNode.GetNodes().GetEndOfExtras().GetIndex() >= nIndex)
386 // Not a node of BodyText
387 // Are we in a fly?
388 if (const auto pFlyFormat = rNode.GetFlyFormat())
390 // Get node index of anchor
391 if (SwNode* pAnchorNode = pFlyFormat->GetAnchor().GetAnchorNode())
393 return getYPos(*pAnchorNode);
397 return sal_Int32(nIndex);
399 } // end of anonymous namespace
401 SwContentType::SwContentType(SwWrtShell* pShell, ContentTypeId nType, sal_uInt8 nLevel) :
402 SwTypeNumber(CTYPE_CTT),
403 m_pWrtShell(pShell),
404 m_sContentTypeName(SwResId(STR_CONTENT_TYPE_ARY[static_cast<int>(nType)])),
405 m_sSingleContentTypeName(SwResId(STR_CONTENT_TYPE_SINGLE_ARY[static_cast<int>(nType)])),
406 m_nMemberCount(0),
407 m_nContentType(nType),
408 m_nOutlineLevel(nLevel),
409 m_bDataValid(false),
410 m_bEdit(false),
411 m_bDelete(true)
413 switch(m_nContentType)
415 case ContentTypeId::OUTLINE:
416 m_sTypeToken = "outline";
417 break;
418 case ContentTypeId::TABLE:
419 m_sTypeToken = "table";
420 m_bEdit = true;
421 m_bRenamable = true;
422 break;
423 case ContentTypeId::FRAME:
424 m_sTypeToken = "frame";
425 m_bEdit = true;
426 m_bRenamable = true;
427 break;
428 case ContentTypeId::GRAPHIC:
429 m_sTypeToken = "graphic";
430 m_bEdit = true;
431 m_bRenamable = true;
432 break;
433 case ContentTypeId::OLE:
434 m_sTypeToken = "ole";
435 m_bEdit = true;
436 m_bRenamable = true;
437 break;
438 case ContentTypeId::TEXTFIELD:
439 m_bEdit = true;
440 break;
441 case ContentTypeId::FOOTNOTE:
442 case ContentTypeId::ENDNOTE:
443 m_bEdit = true;
444 break;
445 case ContentTypeId::BOOKMARK:
447 const bool bProtectedBM = m_pWrtShell->getIDocumentSettingAccess().get(
448 DocumentSettingId::PROTECT_BOOKMARKS);
449 m_bEdit = true;
450 m_bDelete = !bProtectedBM;
451 m_bRenamable = !bProtectedBM;
453 break;
454 case ContentTypeId::REGION:
455 m_sTypeToken = "region";
456 m_bEdit = true;
457 m_bRenamable = true;
458 break;
459 case ContentTypeId::INDEX:
460 m_bEdit = true;
461 m_bRenamable = true;
462 break;
463 case ContentTypeId::REFERENCE:
464 m_bEdit = false;
465 break;
466 case ContentTypeId::URLFIELD:
467 m_bEdit = true;
468 break;
469 case ContentTypeId::POSTIT:
470 m_bEdit = true;
471 break;
472 case ContentTypeId::DRAWOBJECT:
473 m_sTypeToken = "drawingobject";
474 m_bEdit = true;
475 m_bRenamable = true;
476 break;
477 default: break;
480 const int nShift = static_cast<int>(m_nContentType);
481 assert(nShift > -1);
482 const sal_Int32 nMask = 1 << nShift;
483 const sal_Int32 nBlock = SwModule::get()->GetNavigationConfig()->GetSortAlphabeticallyBlock();
484 m_bAlphabeticSort = nBlock & nMask;
486 FillMemberList();
489 SwContentType::~SwContentType()
493 const SwContent* SwContentType::GetMember(size_t nIndex)
495 if(!m_bDataValid || !m_pMember)
497 FillMemberList();
499 if(nIndex < m_pMember->size())
500 return (*m_pMember)[nIndex].get();
502 return nullptr;
505 void SwContentType::Invalidate()
507 m_bDataValid = false;
510 void SwContentType::FillMemberList(bool* pbContentChanged)
512 std::unique_ptr<SwContentArr> pOldMember;
513 size_t nOldMemberCount = 0;
514 if(m_pMember && pbContentChanged)
516 pOldMember = std::move(m_pMember);
517 nOldMemberCount = pOldMember->size();
518 m_pMember.reset( new SwContentArr );
519 *pbContentChanged = false;
521 else if(!m_pMember)
522 m_pMember.reset( new SwContentArr );
523 else
524 m_pMember->clear();
525 switch(m_nContentType)
527 case ContentTypeId::OUTLINE :
529 const SwNodeOffset nEndOfExtrasIndex = m_pWrtShell->GetNodes().GetEndOfExtras().GetIndex();
530 // provide for up to 99999 outline nodes in frames to be sorted in document layout order
531 double nOutlinesInFramesIndexAdjustment = 0.00001;
532 const SwOutlineNodes& rOutlineNodes(m_pWrtShell->GetNodes().GetOutLineNds());
533 const size_t nOutlineCount = rOutlineNodes.size();
535 for (size_t i = 0; i < nOutlineCount; ++i)
537 SwTextNode* pNode = rOutlineNodes[i]->GetTextNode();
538 const sal_uInt8 nLevel = pNode->GetAttrOutlineLevel() - 1;
539 if (nLevel >= m_nOutlineLevel)
540 continue;
541 double nYPos = m_bAlphabeticSort ? 0 : static_cast<double>(getYPos(*pNode));
542 if (nEndOfExtrasIndex >= pNode->GetIndex() && pNode->GetFlyFormat())
544 nYPos += nOutlinesInFramesIndexAdjustment;
545 nOutlinesInFramesIndexAdjustment += 0.00001;
547 OUString aEntry(comphelper::string::stripStart(
548 m_pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText(
549 i, m_pWrtShell->GetLayout(), true, false, false), ' '));
550 aEntry = SwNavigationPI::CleanEntry(aEntry);
551 auto pCnt(std::make_unique<SwOutlineContent>(this, aEntry, i, nLevel,
552 m_pWrtShell->IsOutlineMovable(i), nYPos));
553 if (!pNode->getLayoutFrame(m_pWrtShell->GetLayout()))
554 pCnt->SetInvisible();
555 m_pMember->insert(std::move(pCnt));
558 // need to check level and equal entry number after creation due to possible outline
559 // nodes in frames, headers, footers
560 if (pOldMember)
562 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
563 if (pOldMember->size() != m_pMember->size())
565 *pbContentChanged = true;
566 break;
568 for (size_t i = 0; i < pOldMember->size(); i++)
570 if (static_cast<SwOutlineContent*>((*pOldMember)[i].get())->GetOutlineLevel() !=
571 static_cast<SwOutlineContent*>((*m_pMember)[i].get())->GetOutlineLevel())
573 *pbContentChanged = true;
574 break;
579 break;
580 case ContentTypeId::TABLE :
582 const size_t nCount = m_pWrtShell->GetTableFrameFormatCount(true);
583 const sw::TableFrameFormats* pFrameFormats = m_pWrtShell->GetDoc()->GetTableFrameFormats();
584 for(size_t n = 0, i = 0; i < nCount + n; ++i)
586 const SwTableFormat& rTableFormat = *(*pFrameFormats)[i];
587 if(!rTableFormat.IsUsed()) // skip deleted tables
589 n++;
590 continue;
592 tools::Long nYPos = 0;
593 if (!m_bAlphabeticSort)
595 if (SwTable* pTable = SwTable::FindTable(&rTableFormat))
596 nYPos = getYPos(*pTable->GetTableNode());
598 auto pCnt = std::make_unique<SwContent>(this, rTableFormat.GetName(), nYPos);
599 if(!rTableFormat.IsVisible())
600 pCnt->SetInvisible();
601 m_pMember->insert(std::move(pCnt));
604 if (pOldMember)
606 // need to check visibility (and equal entry number) after
607 // creation due to a sorted list being used here (before,
608 // entries with same index were compared already at creation
609 // time what worked before a sorted list was used)
610 *pbContentChanged = checkVisibilityChanged(
611 *pOldMember,
612 *m_pMember);
615 break;
616 case ContentTypeId::OLE :
617 case ContentTypeId::FRAME :
618 case ContentTypeId::GRAPHIC :
620 FlyCntType eType = FLYCNTTYPE_FRM;
621 if(m_nContentType == ContentTypeId::OLE)
622 eType = FLYCNTTYPE_OLE;
623 else if(m_nContentType == ContentTypeId::GRAPHIC)
624 eType = FLYCNTTYPE_GRF;
625 Point aNullPt;
626 size_t nCount = m_pWrtShell->GetFlyCount(eType, /*bIgnoreTextBoxes=*/true);
627 std::vector<SwFrameFormat const*> formats(m_pWrtShell->GetFlyFrameFormats(eType, /*bIgnoreTextBoxes=*/true));
628 SAL_WARN_IF(nCount != formats.size(), "sw.ui", "Count differs");
629 nCount = formats.size();
630 for (size_t i = 0; i < nCount; ++i)
632 SwFrameFormat const*const pFrameFormat = formats[i];
633 const OUString sFrameName = pFrameFormat->GetName();
635 SwContent* pCnt;
636 tools::Long nYPos =
637 m_bAlphabeticSort ? 0 : pFrameFormat->FindLayoutRect(false, &aNullPt).Top();
638 if(ContentTypeId::GRAPHIC == m_nContentType)
640 OUString sLink;
641 SwDoc::GetGrfNms(*static_cast<const SwFlyFrameFormat*>(pFrameFormat), &sLink,
642 nullptr);
643 pCnt = new SwGraphicContent(this, sFrameName, INetURLObject::decode(sLink,
644 INetURLObject::DecodeMechanism::Unambiguous), nYPos);
646 else
648 pCnt = new SwContent(this, sFrameName, nYPos);
650 if(!pFrameFormat->IsVisible())
651 pCnt->SetInvisible();
652 m_pMember->insert(std::unique_ptr<SwContent>(pCnt));
655 if (pOldMember)
657 // need to check visibility (and equal entry number) after
658 // creation due to a sorted list being used here (before,
659 // entries with same index were compared already at creation
660 // time what worked before a sorted list was used)
661 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
662 *pbContentChanged = checkVisibilityChanged(
663 *pOldMember,
664 *m_pMember);
667 break;
668 case ContentTypeId::BOOKMARK:
670 tools::Long nYPos = 0;
671 IDocumentMarkAccess* const pMarkAccess = m_pWrtShell->getIDocumentMarkAccess();
672 for(auto ppBookmark = pMarkAccess->getBookmarksBegin();
673 ppBookmark != pMarkAccess->getBookmarksEnd();
674 ++ppBookmark)
676 if(lcl_IsUiVisibleBookmark(*ppBookmark))
678 const OUString& rBkmName = (*ppBookmark)->GetName();
679 //nYPos from 0 -> text::Bookmarks will be sorted alphabetically
680 auto pCnt(std::make_unique<SwContent>(this, rBkmName,
681 m_bAlphabeticSort ? 0 : nYPos++));
682 m_pMember->insert(std::move(pCnt));
686 break;
687 case ContentTypeId::TEXTFIELD:
689 std::vector<SwTextField*> aArr;
690 const SwFieldTypes& rFieldTypes =
691 *m_pWrtShell->GetDoc()->getIDocumentFieldsAccess().GetFieldTypes();
692 const size_t nSize = rFieldTypes.size();
693 for (size_t i = 0; i < nSize; ++i)
695 const SwFieldType* pFieldType = rFieldTypes[i].get();
696 if (pFieldType->Which() == SwFieldIds::Postit)
697 continue;
698 std::vector<SwFormatField*> vFields;
699 pFieldType->GatherFields(vFields);
700 for (SwFormatField* pFormatField: vFields)
702 if (SwTextField* pTextField = pFormatField->GetTextField())
704 // fields in header footer don't behave well, skip them
705 if (m_pWrtShell->GetDoc()->IsInHeaderFooter(pTextField->GetTextNode()))
706 continue;
707 aArr.emplace_back(pTextField);
711 if (!m_bAlphabeticSort)
713 const SwNodeOffset nEndOfExtrasIndex =
714 m_pWrtShell->GetNodes().GetEndOfExtras().GetIndex();
715 // use stable sort array to list fields in document model order
716 std::stable_sort(aArr.begin(), aArr.end(),
717 [&nEndOfExtrasIndex, this](
718 const SwTextField* a, const SwTextField* b){
719 SwPosition aPos(a->GetTextNode(), a->GetStart());
720 SwPosition bPos(b->GetTextNode(), b->GetStart());
721 // use anchor position for entries that are located in flys
722 if (nEndOfExtrasIndex >= aPos.GetNodeIndex())
723 getAnchorPos(aPos);
724 if (nEndOfExtrasIndex >= bPos.GetNodeIndex())
725 getAnchorPos(bPos);
726 if (aPos == bPos)
728 // probably in same or nested fly frame
729 // sort using layout position
730 SwRect aCharRect, bCharRect;
731 std::shared_ptr<SwPaM> pPamForTextField;
732 if (SwTextFrame* pFrame = static_cast<SwTextFrame*>(
733 a->GetTextNode().getLayoutFrame(m_pWrtShell->GetLayout())))
735 SwTextField::GetPamForTextField(*a, pPamForTextField);
736 if (pPamForTextField)
737 pFrame->GetCharRect(aCharRect, *pPamForTextField->GetPoint());
739 if (SwTextFrame* pFrame = static_cast<SwTextFrame*>(
740 b->GetTextNode().getLayoutFrame(m_pWrtShell->GetLayout())))
742 SwTextField::GetPamForTextField(*b, pPamForTextField);
743 if (pPamForTextField)
744 pFrame->GetCharRect(bCharRect, *pPamForTextField->GetPoint());
746 return aCharRect.Top() < bCharRect.Top();
748 return aPos < bPos;});
750 std::vector<OUString> aDocumentStatisticsSubTypesList;
751 tools::Long nYPos = 0;
752 for (SwTextField* pTextField : aArr)
754 const SwField* pField = pTextField->GetFormatField().GetField();
755 OUString sExpandField = pField->ExpandField(true, m_pWrtShell->GetLayout());
756 if (!sExpandField.isEmpty())
757 sExpandField = u" - " + sExpandField;
758 OUString sText;
759 if (pField->GetTypeId() == SwFieldTypesEnum::DocumentStatistics)
761 if (aDocumentStatisticsSubTypesList.empty())
762 SwFieldMgr(m_pWrtShell).GetSubTypes(SwFieldTypesEnum::DocumentStatistics,
763 aDocumentStatisticsSubTypesList);
764 OUString sSubType;
765 if (pField->GetSubType() < aDocumentStatisticsSubTypesList.size())
766 sSubType = u" - " + aDocumentStatisticsSubTypesList[pField->GetSubType()];
767 sText = pField->GetDescription() + u" - " + pField->GetFieldName() + sSubType +
768 sExpandField;
770 else if (pField->GetTypeId() == SwFieldTypesEnum::GetRef)
772 assert(dynamic_cast<const SwGetRefField*>(pField));
773 const SwGetRefField* pRefField(static_cast<const SwGetRefField*>(pField));
774 if (pRefField->IsRefToHeadingCrossRefBookmark() ||
775 pRefField->IsRefToNumItemCrossRefBookmark())
777 OUString sExpandedTextOfReferencedTextNode =
778 pRefField->GetExpandedTextOfReferencedTextNode(
779 *m_pWrtShell->GetLayout());
780 if (sExpandedTextOfReferencedTextNode.getLength() > 80)
782 sExpandedTextOfReferencedTextNode = OUString::Concat(
783 sExpandedTextOfReferencedTextNode.subView(0, 80)) + u"...";
785 sText = pField->GetDescription() + u" - "
786 + sExpandedTextOfReferencedTextNode + sExpandField;
788 else
790 OUString sFieldSubTypeOrName;
791 auto nSubType = pField->GetSubType();
792 if (nSubType == REF_FOOTNOTE)
793 sFieldSubTypeOrName = SwResId(STR_FLDREF_FOOTNOTE);
794 else if (nSubType == REF_ENDNOTE)
795 sFieldSubTypeOrName = SwResId(STR_FLDREF_ENDNOTE);
796 else
797 sFieldSubTypeOrName = pField->GetFieldName();
798 sText = pField->GetDescription() + u" - " + sFieldSubTypeOrName
799 + sExpandField;
802 else
803 sText = pField->GetDescription() + u" - " + pField->GetFieldName()
804 + sExpandField;
805 auto pCnt(std::make_unique<SwTextFieldContent>(this, sText,
806 &pTextField->GetFormatField(),
807 m_bAlphabeticSort ? 0 : nYPos++));
808 if (!pTextField->GetTextNode().getLayoutFrame(m_pWrtShell->GetLayout()))
809 pCnt->SetInvisible();
810 m_pMember->insert(std::move(pCnt));
813 break;
814 // We will separate footnotes and endnotes here.
815 case ContentTypeId::FOOTNOTE:
816 case ContentTypeId::ENDNOTE:
818 const SwFootnoteIdxs& rFootnoteIdxs = m_pWrtShell->GetDoc()->GetFootnoteIdxs();
819 if (rFootnoteIdxs.empty())
820 break;
821 // insert footnotes and endnotes
822 tools::Long nPos = 0;
823 for (const SwTextFootnote* pTextFootnote : rFootnoteIdxs)
825 if ((!pTextFootnote->GetFootnote().IsEndNote()
826 && m_nContentType == ContentTypeId::FOOTNOTE)
827 || (pTextFootnote->GetFootnote().IsEndNote()
828 && m_nContentType == ContentTypeId::ENDNOTE))
830 const SwFormatFootnote& rFormatFootnote = pTextFootnote->GetFootnote();
831 const OUString sText
832 = rFormatFootnote.GetViewNumStr(*m_pWrtShell->GetDoc(),
833 m_pWrtShell->GetLayout(), true)
834 + " " + lcl_GetFootnoteText(*pTextFootnote);
835 auto pCnt(std::make_unique<SwTextFootnoteContent>(
836 this, sText, pTextFootnote, ++nPos));
837 if (!pTextFootnote->GetTextNode().getLayoutFrame(m_pWrtShell->GetLayout()))
838 pCnt->SetInvisible();
839 m_pMember->insert(std::move(pCnt));
843 break;
844 case ContentTypeId::REGION :
846 size_t nCount = m_pWrtShell->GetSectionFormatCount();
847 for (size_t i = 0; i < nCount; ++i)
849 const SwSectionFormat* pFormat = &m_pWrtShell->GetSectionFormat(i);
850 if (!pFormat->IsInNodesArr())
851 continue;
852 const SwSection* pSection = pFormat->GetSection();
853 if (SectionType eTmpType = pSection->GetType();
854 eTmpType == SectionType::ToxContent || eTmpType == SectionType::ToxHeader)
855 continue;
856 const SwNodeIndex* pNodeIndex = pFormat->GetContent().GetContentIdx();
857 if (pNodeIndex)
859 const OUString& sSectionName = pSection->GetSectionName();
861 sal_uInt8 nLevel = 0;
862 SwSectionFormat* pParentFormat = pFormat->GetParent();
863 while(pParentFormat)
865 nLevel++;
866 pParentFormat = pParentFormat->GetParent();
869 auto pCnt(std::make_unique<SwRegionContent>(this, sSectionName, nLevel,
870 m_bAlphabeticSort ? 0 : getYPos(pNodeIndex->GetNode()),
871 pFormat));
873 if (!pFormat->IsVisible() || pSection->IsHidden())
874 pCnt->SetInvisible();
875 m_pMember->insert(std::move(pCnt));
878 if (pOldMember)
880 // need to check visibility (and equal entry number) after
881 // creation due to a sorted list being used here (before,
882 // entries with same index were compared already at creation
883 // time what worked before a sorted list was used)
884 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
885 *pbContentChanged = checkVisibilityChanged(
886 *pOldMember,
887 *m_pMember);
891 break;
892 case ContentTypeId::REFERENCE:
894 std::vector<OUString> aRefMarks;
895 m_pWrtShell->GetRefMarks( &aRefMarks );
897 tools::Long nYPos = 0;
898 for (const auto& rRefMark : aRefMarks)
900 m_pMember->insert(std::make_unique<SwContent>(this, rRefMark,
901 m_bAlphabeticSort ? 0 : nYPos++));
904 break;
905 case ContentTypeId::URLFIELD:
907 SwGetINetAttrs aArr;
908 m_pWrtShell->GetINetAttrs(aArr, false);
910 if (m_bAlphabeticSort)
912 for (auto& r : aArr)
914 auto pCnt(std::make_unique<SwURLFieldContent>(this, r.sText, INetURLObject::decode(
915 r.rINetAttr.GetINetFormat().GetValue(),
916 INetURLObject::DecodeMechanism::Unambiguous),
917 &r.rINetAttr, 0));
918 m_pMember->insert(std::move(pCnt));
920 break;
923 // use stable sort array to list hyperlinks in document order
924 const SwNodeOffset nEndOfExtrasIndex = m_pWrtShell->GetNodes().GetEndOfExtras().GetIndex();
925 bool bHasEntryInFly = false;
926 std::vector<SwGetINetAttr*> aStableSortINetAttrsArray;
928 for (SwGetINetAttr& r : aArr)
930 aStableSortINetAttrsArray.emplace_back(&r);
931 if (!bHasEntryInFly)
933 if (nEndOfExtrasIndex >= r.rINetAttr.GetTextNode().GetIndex())
935 // Not a node of BodyText
936 // Are we in a fly?
937 if (r.rINetAttr.GetTextNode().GetFlyFormat())
938 bHasEntryInFly = true;
943 std::stable_sort(aStableSortINetAttrsArray.begin(), aStableSortINetAttrsArray.end(),
944 [](const SwGetINetAttr* a, const SwGetINetAttr* b){
945 SwPosition aSwPos(a->rINetAttr.GetTextNode(),
946 a->rINetAttr.GetStart());
947 SwPosition bSwPos(b->rINetAttr.GetTextNode(),
948 b->rINetAttr.GetStart());
949 return aSwPos < bSwPos;});
951 // When there are hyperlinks in text frames do an additional sort using the text frame
952 // anchor position to place entries in the order of document layout appearance.
953 if (bHasEntryInFly)
955 std::stable_sort(aStableSortINetAttrsArray.begin(), aStableSortINetAttrsArray.end(),
956 [nEndOfExtrasIndex](const SwGetINetAttr* a, const SwGetINetAttr* b){
957 const SwTextNode& aTextNode = a->rINetAttr.GetTextNode();
958 const SwTextNode& bTextNode = b->rINetAttr.GetTextNode();
959 SwPosition aPos(aTextNode, a->rINetAttr.GetStart());
960 SwPosition bPos(bTextNode, b->rINetAttr.GetStart());
961 // use anchor position for entries that are located in flys
962 if (nEndOfExtrasIndex >= aTextNode.GetIndex())
963 if (auto pFlyFormat = aTextNode.GetFlyFormat())
964 if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor())
965 aPos = *pPos;
966 if (nEndOfExtrasIndex >= bTextNode.GetIndex())
967 if (auto pFlyFormat = bTextNode.GetFlyFormat())
968 if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor())
969 bPos = *pPos;
970 return aPos < bPos;});
973 SwGetINetAttrs::size_type n = 0;
974 for (auto p : aStableSortINetAttrsArray)
976 auto pCnt = std::make_unique<SwURLFieldContent>(this, p->sText,
977 INetURLObject::decode(p->rINetAttr.GetINetFormat().GetValue(),
978 INetURLObject::DecodeMechanism::Unambiguous),
979 &p->rINetAttr, ++n);
980 m_pMember->insert(std::move(pCnt));
983 break;
984 case ContentTypeId::INDEX:
986 const sal_uInt16 nCount = m_pWrtShell->GetTOXCount();
988 for ( sal_uInt16 nTox = 0; nTox < nCount; nTox++ )
990 const SwTOXBase* pBase = m_pWrtShell->GetTOX( nTox );
991 OUString sTOXNm( pBase->GetTOXName() );
993 SwContent* pCnt = new SwTOXBaseContent(
994 this, sTOXNm, m_bAlphabeticSort ? 0 : nTox, *pBase);
996 if(pBase && !pBase->IsVisible())
997 pCnt->SetInvisible();
999 m_pMember->insert( std::unique_ptr<SwContent>(pCnt) );
1000 const size_t nPos = m_pMember->size() - 1;
1001 if (pOldMember)
1003 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
1004 if (!*pbContentChanged && nOldMemberCount > nPos &&
1005 (*pOldMember)[nPos]->IsInvisible() != pCnt->IsInvisible())
1006 *pbContentChanged = true;
1010 break;
1011 case ContentTypeId::POSTIT:
1013 SwPostItMgr* aMgr = m_pWrtShell->GetView().GetPostItMgr();
1014 if (aMgr)
1016 tools::Long nYPos = 0;
1017 for(SwPostItMgr::const_iterator i = aMgr->begin(); i != aMgr->end(); ++i)
1019 if (const SwFormatField* pFormatField = dynamic_cast<const SwFormatField *>((*i)->GetBroadcaster())) // SwPostit
1021 if (pFormatField->GetTextField() && pFormatField->IsFieldInDoc())
1023 OUString sEntry = pFormatField->GetField()->GetPar2();
1024 sEntry = RemoveNewline(sEntry);
1025 std::unique_ptr<SwPostItContent> pCnt(new SwPostItContent(
1026 this,
1027 sEntry,
1028 pFormatField,
1029 nYPos));
1030 if (!pFormatField->GetTextField()->GetTextNode().getLayoutFrame(
1031 m_pWrtShell->GetLayout()))
1032 pCnt->SetInvisible();
1033 if (pOldMember)
1035 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
1036 if (!*pbContentChanged &&
1037 nOldMemberCount > o3tl::make_unsigned(nYPos) &&
1038 (*pOldMember)[nYPos]->IsInvisible() != pCnt->IsInvisible())
1039 *pbContentChanged = true;
1041 m_pMember->insert(std::move(pCnt));
1042 nYPos++;
1048 break;
1049 case ContentTypeId::DRAWOBJECT:
1051 IDocumentDrawModelAccess& rIDDMA = m_pWrtShell->getIDocumentDrawModelAccess();
1052 SwDrawModel* pModel = rIDDMA.GetDrawModel();
1053 if(pModel)
1055 SdrPage* pPage = pModel->GetPage(0);
1056 for (const rtl::Reference<SdrObject>& pTemp : *pPage)
1058 // #i51726# - all drawing objects can be named now
1059 if (!pTemp->IsVirtualObj() && !pTemp->GetName().isEmpty())
1061 tools::Long nYPos = LONG_MIN;
1062 const bool bIsVisible = rIDDMA.IsVisibleLayerId(pTemp->GetLayer());
1063 if (bIsVisible)
1064 nYPos = m_bAlphabeticSort ? 0 : pTemp->GetLogicRect().Top();
1065 auto pCnt(std::make_unique<SwContent>(this, pTemp->GetName(), nYPos));
1066 if (!bIsVisible)
1067 pCnt->SetInvisible();
1068 m_pMember->insert(std::move(pCnt));
1072 if (pOldMember)
1074 // need to check visibility (and equal entry number) after
1075 // creation due to a sorted list being used here (before,
1076 // entries with same index were compared already at creation
1077 // time what worked before a sorted list was used)
1078 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
1079 *pbContentChanged = checkVisibilityChanged(
1080 *pOldMember,
1081 *m_pMember);
1085 break;
1086 default: break;
1088 m_nMemberCount = m_pMember->size();
1089 if (pOldMember)
1091 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
1092 if (!*pbContentChanged && pOldMember->size() != m_nMemberCount)
1093 *pbContentChanged = true;
1096 m_bDataValid = true;
1099 namespace {
1101 enum STR_CONTEXT_IDX
1103 IDX_STR_OUTLINE_LEVEL = 0,
1104 IDX_STR_DISPLAY = 1,
1105 IDX_STR_ACTIVE_VIEW = 2,
1106 IDX_STR_HIDDEN = 3,
1107 IDX_STR_ACTIVE = 4,
1108 IDX_STR_INACTIVE = 5,
1109 IDX_STR_EDIT_ENTRY = 6,
1110 IDX_STR_DELETE_ENTRY = 7,
1111 IDX_STR_SEND_OUTLINE_TO_CLIPBOARD_ENTRY = 8,
1112 IDX_STR_OUTLINE_TRACKING = 9,
1113 IDX_STR_OUTLINE_TRACKING_DEFAULT = 10,
1114 IDX_STR_OUTLINE_TRACKING_FOCUS = 11,
1115 IDX_STR_OUTLINE_TRACKING_OFF = 12
1120 const TranslateId STR_CONTEXT_ARY[] =
1122 STR_OUTLINE_LEVEL,
1123 STR_DISPLAY,
1124 STR_ACTIVE_VIEW,
1125 STR_HIDDEN,
1126 STR_ACTIVE,
1127 STR_INACTIVE,
1128 STR_EDIT_ENTRY,
1129 STR_DELETE_ENTRY,
1130 STR_SEND_OUTLINE_TO_CLIPBOARD_ENTRY,
1131 STR_OUTLINE_TRACKING,
1132 STR_OUTLINE_TRACKING_DEFAULT,
1133 STR_OUTLINE_TRACKING_FOCUS,
1134 STR_OUTLINE_TRACKING_OFF
1137 SwContentTree::SwContentTree(std::unique_ptr<weld::TreeView> xTreeView, SwNavigationPI* pDialog)
1138 : m_xTreeView(std::move(xTreeView))
1139 , m_aDropTargetHelper(*this)
1140 , m_pDialog(pDialog)
1141 , m_sSpace(u" "_ustr)
1142 , m_aUpdTimer("SwContentTree m_aUpdTimer")
1143 , m_aOverlayObjectDelayTimer("SwContentTree m_aOverlayObjectDelayTimer")
1144 , m_sInvisible(SwResId(STR_INVISIBLE))
1145 , m_pHiddenShell(nullptr)
1146 , m_pActiveShell(nullptr)
1147 , m_pConfig(SwModule::get()->GetNavigationConfig())
1148 , m_nActiveBlock(0)
1149 , m_nHiddenBlock(0)
1150 , m_nEntryCount(0)
1151 , m_nRootType(ContentTypeId::UNKNOWN)
1152 , m_nLastSelType(ContentTypeId::UNKNOWN)
1153 , m_nOutlineLevel(MAXLEVEL)
1154 , m_eState(State::ACTIVE)
1155 , m_bIsRoot(false)
1156 , m_bIsIdleClear(false)
1157 , m_bIsLastReadOnly(false)
1158 , m_bIsOutlineMoveable(true)
1159 , m_bViewHasChanged(false)
1161 m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 30,
1162 m_xTreeView->get_text_height() * 14);
1164 m_xTreeView->set_help_id(HID_NAVIGATOR_TREELIST);
1166 m_xTreeView->connect_expanding(LINK(this, SwContentTree, ExpandHdl));
1167 m_xTreeView->connect_collapsing(LINK(this, SwContentTree, CollapseHdl));
1168 m_xTreeView->connect_row_activated(LINK(this, SwContentTree, ContentDoubleClickHdl));
1169 m_xTreeView->connect_selection_changed(LINK(this, SwContentTree, SelectHdl));
1170 m_xTreeView->connect_focus_in(LINK(this, SwContentTree, FocusInHdl));
1171 m_xTreeView->connect_key_press(LINK(this, SwContentTree, KeyInputHdl));
1172 m_xTreeView->connect_popup_menu(LINK(this, SwContentTree, CommandHdl));
1173 m_xTreeView->connect_query_tooltip(LINK(this, SwContentTree, QueryTooltipHdl));
1174 m_xTreeView->connect_drag_begin(LINK(this, SwContentTree, DragBeginHdl));
1175 m_xTreeView->connect_mouse_move(LINK(this, SwContentTree, MouseMoveHdl));
1176 m_xTreeView->connect_mouse_press(LINK(this, SwContentTree, MousePressHdl));
1178 for (ContentTypeId i : o3tl::enumrange<ContentTypeId>())
1180 if (i != ContentTypeId::OUTLINE)
1181 mTrackContentType[i] = true;
1182 m_aActiveContentArr[i] = nullptr;
1183 m_aHiddenContentArr[i] = nullptr;
1185 for (int i = 0; i < CONTEXT_COUNT; ++i)
1187 m_aContextStrings[i] = SwResId(STR_CONTEXT_ARY[i]);
1189 m_nActiveBlock = m_pConfig->GetActiveBlock();
1191 // Restore outline headings expand state (same session persistence only)
1192 if (SwView* pView = GetActiveView(); pView && pView->GetDocShell())
1194 OUString sDocTitle = pView->GetDocShell()->GetTitle();
1195 auto it = lcl_DocOutLineExpandStateMap.find(sDocTitle);
1196 if (it != lcl_DocOutLineExpandStateMap.end())
1197 mOutLineNodeMap = it->second;
1198 if (comphelper::LibreOfficeKit::isActive()) {
1199 if (pView->m_nNaviExpandedStatus < 0)
1200 m_nActiveBlock = 1;
1201 else
1202 m_nActiveBlock = pView->m_nNaviExpandedStatus;
1206 m_aUpdTimer.SetInvokeHandler(LINK(this, SwContentTree, TimerUpdate));
1207 m_aUpdTimer.SetTimeout(1000);
1208 m_aOverlayObjectDelayTimer.SetInvokeHandler(LINK(this, SwContentTree, OverlayObjectDelayTimerHdl));
1209 m_aOverlayObjectDelayTimer.SetTimeout(500);
1212 SwContentTree::~SwContentTree()
1214 if (SwView* pView = GetActiveView(); pView && pView->GetDocShell())
1216 OUString sDocTitle = pView->GetDocShell()->GetTitle();
1217 lcl_DocOutLineExpandStateMap[sDocTitle] = mOutLineNodeMap;
1218 if (comphelper::LibreOfficeKit::isActive())
1219 pView->m_nNaviExpandedStatus = m_nActiveBlock;
1221 clear(); // If applicable erase content types previously.
1222 m_aUpdTimer.Stop();
1223 SetActiveShell(nullptr);
1226 IMPL_LINK(SwContentTree, MousePressHdl, const MouseEvent&, rMEvt, bool)
1228 m_bSelectTo = rMEvt.IsShift() && (m_pConfig->IsNavigateOnSelect() || rMEvt.GetClicks() == 2);
1229 return false;
1232 IMPL_LINK(SwContentTree, MouseMoveHdl, const MouseEvent&, rMEvt, bool)
1234 // Prevent trying to bring entry to attention when handling document change. The mouse over
1235 // entry might not be valid, for example, when the mouse pointer is on an entry that is deleted
1236 // in the document by an undo/redo.
1237 if (m_bDocHasChanged)
1238 return false;
1239 if (m_eState == State::HIDDEN)
1240 return false;
1241 if (std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
1242 m_xTreeView->get_dest_row_at_pos(rMEvt.GetPosPixel(), xEntry.get(), false, false) &&
1243 !rMEvt.IsLeaveWindow())
1245 if (!m_xOverlayCompareEntry)
1246 m_xOverlayCompareEntry.reset(m_xTreeView->make_iterator().release());
1247 else if (m_xTreeView->iter_compare(*xEntry, *m_xOverlayCompareEntry) == 0)
1248 return false; // The entry under the mouse has not changed.
1249 m_xTreeView->copy_iterator(*xEntry, *m_xOverlayCompareEntry);
1250 BringEntryToAttention(*xEntry);
1252 else
1254 if (m_xOverlayCompareEntry)
1255 m_xOverlayCompareEntry.reset();
1256 m_aOverlayObjectDelayTimer.Stop();
1257 if (m_xOverlayObject && m_xOverlayObject->getOverlayManager())
1259 m_xOverlayObject->getOverlayManager()->remove(*m_xOverlayObject);
1260 m_xOverlayObject.reset();
1263 return false;
1266 // Drag&Drop methods
1267 IMPL_LINK(SwContentTree, DragBeginHdl, bool&, rUnsetDragIcon, bool)
1269 rUnsetDragIcon = true;
1271 bool bDisallow = true;
1273 // don't allow if tree root is selected
1274 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
1275 bool bEntry = m_xTreeView->get_selected(xEntry.get());
1276 if (!bEntry || lcl_IsContentType(*xEntry, *m_xTreeView))
1278 return true; // disallow
1281 if (m_bIsRoot && m_nRootType == ContentTypeId::OUTLINE)
1283 // Only move drag entry and continuous selected siblings:
1284 m_aDndOutlinesSelected.clear();
1286 std::unique_ptr<weld::TreeIter> xScratch(m_xTreeView->make_iterator(xEntry.get()));
1288 // Find first selected of continuous siblings
1289 while (m_xTreeView->iter_previous_sibling(*xScratch) && m_xTreeView->is_selected(*xScratch))
1291 m_xTreeView->copy_iterator(*xScratch, *xEntry);
1293 // Record continuous selected siblings
1296 SwOutlineContent* pOutlineContent
1297 = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xEntry));
1298 if (!pOutlineContent) // shouldn't happen
1299 continue;
1300 m_aDndOutlinesSelected.push_back(pOutlineContent->GetOutlinePos());
1301 } while (m_xTreeView->iter_next_sibling(*xEntry) && m_xTreeView->is_selected(*xEntry));
1303 bDisallow = false;
1306 rtl::Reference<TransferDataContainer> xContainer = new TransferDataContainer;
1308 if (FillTransferData(*xContainer))
1310 bDisallow = false;
1311 m_xTreeView->enable_drag_source(xContainer, DND_ACTION_COPY);
1314 return bDisallow;
1317 SwContentTreeDropTarget::SwContentTreeDropTarget(SwContentTree& rTreeView)
1318 : DropTargetHelper(rTreeView.get_widget().get_drop_target())
1319 , m_rTreeView(rTreeView)
1323 sal_Int8 SwContentTreeDropTarget::AcceptDrop(const AcceptDropEvent& rEvt)
1325 sal_Int8 nAccept = m_rTreeView.AcceptDrop(rEvt);
1327 if (nAccept != DND_ACTION_NONE)
1329 // to enable the autoscroll when we're close to the edges
1330 weld::TreeView& rWidget = m_rTreeView.get_widget();
1331 rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true);
1334 return nAccept;
1337 bool SwContentTree::IsInDrag() const
1339 return m_xTreeView->get_drag_source() == m_xTreeView.get();
1342 bool SwContentTree::HasHeadings() const
1344 const std::unique_ptr<SwContentType>& rpContentT = m_aActiveContentArr[ContentTypeId::OUTLINE];
1345 if (rpContentT && rpContentT->GetMemberCount() > 0)
1346 return true;
1347 return false;
1350 // QueryDrop will be executed in the navigator
1351 sal_Int8 SwContentTree::AcceptDrop(const AcceptDropEvent& rEvt)
1353 sal_Int8 nRet = DND_ACTION_NONE;
1354 if( m_bIsRoot )
1356 if( m_bIsOutlineMoveable )
1357 nRet = rEvt.mnAction;
1359 else if (!IsInDrag())
1360 nRet = GetParentWindow()->AcceptDrop();
1361 return nRet;
1364 // Drop will be executed in the navigator
1365 static void* lcl_GetOutlineKey(SwContentTree& rTree, SwOutlineContent const * pContent)
1367 void* key = nullptr;
1368 if (pContent)
1370 SwWrtShell* pShell = rTree.GetWrtShell();
1371 auto const nPos = pContent->GetOutlinePos();
1373 key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos ));
1375 return key;
1378 sal_Int8 SwContentTreeDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt)
1380 return m_rTreeView.ExecuteDrop(rEvt);
1383 sal_Int8 SwContentTree::ExecuteDrop(const ExecuteDropEvent& rEvt)
1385 std::unique_ptr<weld::TreeIter> xDropEntry(m_xTreeView->make_iterator());
1386 if (!m_xTreeView->get_dest_row_at_pos(rEvt.maPosPixel, xDropEntry.get(), true))
1387 xDropEntry.reset();
1389 if (m_nRootType == ContentTypeId::OUTLINE)
1391 if (xDropEntry && lcl_IsContent(*xDropEntry, *m_xTreeView))
1393 assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xDropEntry))));
1394 SwOutlineContent* pOutlineContent = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xDropEntry));
1395 assert(pOutlineContent);
1397 void* key = lcl_GetOutlineKey(*this, pOutlineContent);
1398 assert(key);
1399 if (!mOutLineNodeMap[key])
1401 while (m_xTreeView->iter_has_child(*xDropEntry))
1403 std::unique_ptr<weld::TreeIter> xChildEntry(m_xTreeView->make_iterator(xDropEntry.get()));
1404 bool bChildEntry = m_xTreeView->iter_children(*xChildEntry);
1405 while (bChildEntry)
1407 m_xTreeView->copy_iterator(*xChildEntry, *xDropEntry);
1408 bChildEntry = m_xTreeView->iter_next_sibling(*xChildEntry);
1414 SwOutlineNodes::size_type nTargetPos = 0;
1415 if (!xDropEntry)
1417 // dropped in blank space -> move to bottom
1418 nTargetPos = GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineNodesCount() - 1;
1420 else if (!lcl_IsContent(*xDropEntry, *m_xTreeView))
1422 // dropped on "heading" parent -> move to start
1423 nTargetPos = SwOutlineNodes::npos;
1425 else
1427 assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xDropEntry))));
1428 nTargetPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xDropEntry))->GetOutlinePos();
1431 if( MAXLEVEL > m_nOutlineLevel && // Not all layers are displayed.
1432 nTargetPos != SwOutlineNodes::npos)
1434 std::unique_ptr<weld::TreeIter> xNext(m_xTreeView->make_iterator(xDropEntry.get()));
1435 bool bNext = m_xTreeView->iter_next(*xNext);
1436 if (bNext)
1438 assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xNext))));
1439 nTargetPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xNext))->GetOutlinePos() - 1;
1441 else
1442 nTargetPos = GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineNodesCount() - 1;
1445 // remove the drop highlight before we change the contents of the tree so we don't
1446 // try and dereference a removed entry in post-processing drop
1447 m_xTreeView->unset_drag_dest_row();
1448 MoveOutline(nTargetPos);
1451 return IsInDrag() ? DND_ACTION_NONE : GetParentWindow()->ExecuteDrop(rEvt);
1454 namespace
1456 bool IsAllExpanded(const weld::TreeView& rContentTree, const weld::TreeIter& rEntry)
1458 if (!rContentTree.get_row_expanded(rEntry))
1459 return false;
1461 if (!rContentTree.iter_has_child(rEntry))
1462 return false;
1464 std::unique_ptr<weld::TreeIter> xChild(rContentTree.make_iterator(&rEntry));
1465 (void)rContentTree.iter_children(*xChild);
1469 if (rContentTree.iter_has_child(*xChild) || rContentTree.get_children_on_demand(*xChild))
1471 if (!IsAllExpanded(rContentTree, *xChild))
1472 return false;
1475 while (rContentTree.iter_next_sibling(*xChild));
1476 return true;
1479 void ExpandOrCollapseAll(weld::TreeView& rContentTree, weld::TreeIter& rEntry)
1481 bool bExpand = !IsAllExpanded(rContentTree, rEntry);
1482 bExpand ? rContentTree.expand_row(rEntry) : rContentTree.collapse_row(rEntry);
1483 int nRefDepth = rContentTree.get_iter_depth(rEntry);
1484 while (rContentTree.iter_next(rEntry) && rContentTree.get_iter_depth(rEntry) > nRefDepth)
1486 if (rContentTree.iter_has_child(rEntry))
1487 bExpand ? rContentTree.expand_row(rEntry) : rContentTree.collapse_row(rEntry);
1492 // Handler for Dragging and ContextMenu
1493 static bool lcl_InsertExpandCollapseAllItem(const weld::TreeView& rContentTree, const weld::TreeIter& rEntry, weld::Menu& rPop)
1495 if (rContentTree.iter_has_child(rEntry) || rContentTree.get_children_on_demand(rEntry))
1497 rPop.set_label(OUString::number(800), IsAllExpanded(rContentTree, rEntry) ? SwResId(STR_COLLAPSEALL) : SwResId(STR_EXPANDALL));
1498 return false;
1500 return true;
1503 static void lcl_SetOutlineContentEntriesSensitivities(SwContentTree* pThis, const weld::TreeView& rContentTree, const weld::TreeIter& rEntry, weld::Menu& rPop)
1505 rPop.set_sensitive(OUString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY), false);
1506 rPop.set_sensitive(OUString::number(HIDE_OUTLINE_CONTENT_VISIBILITY), false);
1507 rPop.set_sensitive(OUString::number(SHOW_OUTLINE_CONTENT_VISIBILITY), false);
1509 // todo: multi selection
1510 if (rContentTree.count_selected_rows() > 1)
1511 return;
1513 bool bIsRoot = lcl_IsContentType(rEntry, rContentTree);
1515 const SwNodes& rNodes = pThis->GetWrtShell()->GetNodes();
1516 const SwOutlineNodes& rOutlineNodes = rNodes.GetOutLineNds();
1517 size_t nOutlinePos = weld::GetAbsPos(rContentTree, rEntry);
1519 if (!bIsRoot)
1520 --nOutlinePos;
1522 if (nOutlinePos >= rOutlineNodes.size())
1523 return;
1525 int nFirstLevel = pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineLevel(nOutlinePos);
1527 // determine if any concerned outline node has content
1528 bool bHasContent(false);
1529 size_t nPos = nOutlinePos;
1530 SwNode* pSttNd = rOutlineNodes[nPos];
1531 SwNode* pEndNd = &rNodes.GetEndOfContent();
1532 if (rOutlineNodes.size() > nPos + 1)
1533 pEndNd = rOutlineNodes[nPos + 1];
1535 // selected
1536 SwNodeIndex aIdx(*pSttNd);
1537 if (SwNodes::GoNext(&aIdx) != pEndNd)
1538 bHasContent = true;
1540 // descendants
1541 if (!bHasContent && (rContentTree.iter_has_child(rEntry) || rContentTree.get_children_on_demand(rEntry)))
1543 while (++nPos < rOutlineNodes.size() &&
1544 (bIsRoot || pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos) > nFirstLevel))
1546 pSttNd = rOutlineNodes[nPos];
1547 pEndNd = &rNodes.GetEndOfContent();
1548 if (rOutlineNodes.size() > nPos + 1)
1549 pEndNd = rOutlineNodes[nPos + 1];
1551 // test for content in outline node
1552 aIdx.Assign(*pSttNd);
1553 if (SwNodes::GoNext(&aIdx) != pEndNd)
1555 bHasContent = true;
1556 break;
1561 if (!bHasContent)
1562 return; // no content in any of the concerned outline nodes
1565 // determine for subs if all are folded or unfolded or if they are mixed
1566 if (rContentTree.iter_has_child(rEntry) || rContentTree.get_children_on_demand(rEntry))
1568 // skip no content nodes
1569 // we know there is content from results above so this is presumably safe
1570 size_t nPos = nOutlinePos;
1571 while (true)
1573 SwNode* pSttNd = rOutlineNodes[nPos];
1574 SwNode* pEndNd = rOutlineNodes.back();
1575 if (!bIsRoot && rOutlineNodes.size() > nPos + 1)
1576 pEndNd = rOutlineNodes[nPos + 1];
1578 SwNodeIndex aIdx(*pSttNd);
1579 if (SwNodes::GoNext(&aIdx) != pEndNd)
1580 break;
1581 nPos++;
1584 bool bHasFolded(!pThis->GetWrtShell()->IsOutlineContentVisible(nPos));
1585 bool bHasUnfolded(!bHasFolded);
1587 while ((++nPos < pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineNodesCount()) &&
1588 (bIsRoot || pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos) > nFirstLevel))
1591 SwNode* pSttNd = rOutlineNodes[nPos];
1592 SwNode* pEndNd = &rNodes.GetEndOfContent();
1593 if (rOutlineNodes.size() > nPos + 1)
1594 pEndNd = rOutlineNodes[nPos + 1];
1596 SwNodeIndex aIdx(*pSttNd);
1597 if (SwNodes::GoNext(&aIdx) == pEndNd)
1598 continue; // skip if no content
1600 if (!pThis->GetWrtShell()->IsOutlineContentVisible(nPos))
1601 bHasFolded = true;
1602 else
1603 bHasUnfolded = true;
1605 if (bHasFolded && bHasUnfolded)
1606 break; // mixed so no need to continue
1609 rPop.set_sensitive(OUString::number(HIDE_OUTLINE_CONTENT_VISIBILITY), bHasUnfolded);
1610 rPop.set_sensitive(OUString::number(SHOW_OUTLINE_CONTENT_VISIBILITY), bHasFolded);
1613 rPop.set_sensitive(OUString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY), !bIsRoot);
1616 IMPL_LINK(SwContentTree, CommandHdl, const CommandEvent&, rCEvt, bool)
1618 if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
1619 return false;
1621 grab_focus();
1623 // select clicked entry or limit selection to root entry if needed
1624 if (std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
1625 rCEvt.IsMouseEvent() && m_xTreeView->get_dest_row_at_pos(
1626 rCEvt.GetMousePosPixel(), xEntry.get(), false))
1628 // if clicked entry is not currently selected then clear selections and select it
1629 if (!m_xTreeView->is_selected(*xEntry))
1630 m_xTreeView->set_cursor(*xEntry);
1631 // if root entry is selected then clear selections and select it
1632 else if (m_xTreeView->is_selected(0))
1633 m_xTreeView->set_cursor(0);
1636 UpdateContentFunctionsToolbar();
1638 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), u"modules/swriter/ui/navigatorcontextmenu.ui"_ustr));
1639 std::unique_ptr<weld::Menu> xPop = xBuilder->weld_menu(u"navmenu"_ustr);
1641 bool bOutline(false);
1642 std::unique_ptr<weld::Menu> xSubPop1 = xBuilder->weld_menu(u"outlinelevel"_ustr);
1643 std::unique_ptr<weld::Menu> xSubPop2 = xBuilder->weld_menu(u"dragmodemenu"_ustr);
1644 std::unique_ptr<weld::Menu> xSubPop3 = xBuilder->weld_menu(u"displaymenu"_ustr);
1645 std::unique_ptr<weld::Menu> xSubPopOutlineTracking = xBuilder->weld_menu(u"outlinetracking"_ustr);
1647 std::unique_ptr<weld::Menu> xSubPopOutlineContent = xBuilder->weld_menu(u"outlinecontent"_ustr);
1649 xSubPopOutlineContent->append(OUString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY),
1650 SwResId(STR_OUTLINE_CONTENT_VISIBILITY_TOGGLE));
1651 xSubPopOutlineContent->append(OUString::number(HIDE_OUTLINE_CONTENT_VISIBILITY),
1652 SwResId(STR_OUTLINE_CONTENT_VISIBILITY_HIDE_ALL));
1653 xSubPopOutlineContent->append(OUString::number(SHOW_OUTLINE_CONTENT_VISIBILITY),
1654 SwResId(STR_OUTLINE_CONTENT_VISIBILITY_SHOW_ALL));
1656 xSubPopOutlineContent->set_item_help_id(OUString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY),
1657 HID_NAVIGATOR_TREELIST);
1658 xSubPopOutlineContent->set_item_help_id(OUString::number(HIDE_OUTLINE_CONTENT_VISIBILITY),
1659 HID_NAVIGATOR_TREELIST);
1660 xSubPopOutlineContent->set_item_help_id(OUString::number(SHOW_OUTLINE_CONTENT_VISIBILITY),
1661 HID_NAVIGATOR_TREELIST);
1663 // Add entries to the Outline Tracking submenu
1664 OUString sId;
1665 for(int i = 1; i <= 3; ++i)
1667 sId = OUString::number(i + 10);
1668 xSubPopOutlineTracking->append_radio(sId, m_aContextStrings[IDX_STR_OUTLINE_TRACKING + i]);
1669 xSubPopOutlineTracking->set_item_help_id(sId, HID_NAV_OUTLINE_TRACKING);
1671 xSubPopOutlineTracking->set_active(OUString::number(10 + m_nOutlineTracking), true);
1673 // Add entries to the Outline Level submenu
1674 for (int i = 1; i <= MAXLEVEL; ++i)
1676 sId = OUString::number(i + 100);
1677 xSubPop1->append_radio(sId, OUString::number(i));
1678 xSubPop1->set_item_help_id(sId, HID_NAV_OUTLINE_LEVEL);
1680 xSubPop1->set_active(OUString::number(100 + m_nOutlineLevel), true);
1682 // Insert the list of the open files in the Display submenu
1684 sal_uInt16 nId = 301;
1685 SwView *pView = SwModule::GetFirstView();
1686 while (pView)
1688 OUString sInsert = pView->GetDocShell()->GetTitle() + " (" +
1689 m_aContextStrings[pView == GetActiveView() ? IDX_STR_ACTIVE :
1690 IDX_STR_INACTIVE] + ")";
1691 sId = OUString::number(nId);
1692 xSubPop3->append_radio(sId, sInsert);
1693 xSubPop3->set_item_help_id(sId, HID_NAV_DISPLAY);
1694 if (State::CONSTANT == m_eState && m_pActiveShell == &pView->GetWrtShell())
1695 xSubPop3->set_active(sId, true);
1696 pView = SwModule::GetNextView(pView);
1697 nId++;
1699 // Active Window
1700 sId = OUString::number(nId++);
1701 xSubPop3->append_radio(sId, m_aContextStrings[IDX_STR_ACTIVE_VIEW]);
1702 xSubPop3->set_item_help_id(sId, HID_NAV_DISPLAY);
1703 // There can be only one hidden shell
1704 if (m_pHiddenShell)
1706 OUString sHiddenEntry = m_pHiddenShell->GetView().GetDocShell()->GetTitle() +
1707 " (" +
1708 m_aContextStrings[IDX_STR_HIDDEN] +
1709 ")";
1710 sId = OUString::number(nId);
1711 xSubPop3->append_radio(sId, sHiddenEntry);
1712 xSubPop3->set_item_help_id(sId, HID_NAV_DISPLAY);
1714 if (State::ACTIVE == m_eState)
1715 xSubPop3->set_active(OUString::number(--nId), true);
1716 else if (State::HIDDEN == m_eState)
1717 xSubPop3->set_active(OUString::number(nId), true);
1720 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
1721 if (!m_xTreeView->get_selected(xEntry.get()))
1722 xEntry.reset();
1724 bool bRemoveGotoEntry = false;
1725 if (State::HIDDEN == m_eState || !xEntry || !lcl_IsContent(*xEntry, *m_xTreeView) ||
1726 weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->IsInvisible())
1727 bRemoveGotoEntry = true;
1729 bool bRemovePostItEntries = true;
1730 bool bRemoveUpdateIndexEntry = true;
1731 bool bRemoveReadonlyIndexEntry = true;
1732 bool bRemoveCopyEntry = true;
1733 bool bRemoveEditEntry = true;
1734 bool bRemoveUnprotectEntry = true;
1735 bool bRemoveDeleteChapterEntry = true,
1736 bRemoveDeleteAllTablesEntry = true,
1737 bRemoveDeleteTableEntry = true,
1738 bRemoveDeleteAllFramesEntry = true,
1739 bRemoveDeleteFrameEntry = true,
1740 bRemoveDeleteAllImagesEntry = true,
1741 bRemoveDeleteImageEntry = true,
1742 bRemoveDeleteAllOLEObjectsEntry = true,
1743 bRemoveDeleteOLEObjectEntry = true,
1744 bRemoveDeleteAllBookmarksEntry = true,
1745 bRemoveDeleteBookmarkEntry = true,
1746 bRemoveDeleteAllRegionsEntry = true,
1747 bRemoveDeleteRegionEntry = true,
1748 bRemoveDeleteAllHyperlinksEntry = true,
1749 bRemoveDeleteHyperlinkEntry = true,
1750 bRemoveDeleteAllReferencesEntry = true,
1751 bRemoveDeleteReferenceEntry = true,
1752 bRemoveDeleteAllIndexesEntry = true,
1753 bRemoveDeleteIndexEntry= true,
1754 bRemoveDeleteAllCommentsEntry = true,
1755 bRemoveDeleteCommentEntry = true,
1756 bRemoveDeleteAllDrawingObjectsEntry = true,
1757 bRemoveDeleteDrawingObjectEntry = true,
1758 bRemoveDeleteAllFieldsEntry = true,
1759 bRemoveDeleteFieldEntry = true,
1760 bRemoveDeleteAllFootnotesEntry = true,
1761 bRemoveDeleteFootnoteEntry = true,
1762 bRemoveDeleteAllEndnotesEntry = true,
1763 bRemoveDeleteEndnoteEntry = true;
1764 bool bRemoveMakeAllFootnotesEndnotesEntry = true,
1765 bRemoveMakeAllEndnotesFootnotesEntry = true;
1766 bool bRemoveRenameEntry = true;
1767 bool bRemoveSelectEntry = true;
1768 bool bRemoveToggleExpandEntry = true;
1769 bool bRemoveChapterEntries = true;
1770 bool bRemoveSendOutlineEntry = true;
1772 bool bRemoveTableTracking = true;
1773 bool bRemoveSectionTracking = true;
1774 bool bRemoveFrameTracking = true;
1775 bool bRemoveImageTracking = true;
1776 bool bRemoveOLEobjectTracking = true;
1777 bool bRemoveBookmarkTracking = true;
1778 bool bRemoveHyperlinkTracking = true;
1779 bool bRemoveReferenceTracking = true;
1780 bool bRemoveIndexTracking = true;
1781 bool bRemoveCommentTracking = true;
1782 bool bRemoveDrawingObjectTracking = true;
1783 bool bRemoveFieldTracking = true;
1784 bool bRemoveFootnoteTracking = true;
1785 bool bRemoveEndnoteTracking = true;
1787 bool bRemoveSortEntry = true;
1789 bool bRemoveProtectSection = true;
1790 bool bRemoveHideSection = true;
1792 if (xEntry)
1794 const SwContentType* pType;
1795 if (lcl_IsContentType(*xEntry, *m_xTreeView))
1796 pType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry));
1797 else
1798 pType = weld::fromId<SwContent*>(
1799 m_xTreeView->get_id(*xEntry))->GetParent();
1800 const ContentTypeId nContentType = pType->GetType();
1802 if (nContentType != ContentTypeId::FOOTNOTE && nContentType != ContentTypeId::ENDNOTE
1803 && nContentType != ContentTypeId::POSTIT && nContentType != ContentTypeId::UNKNOWN)
1805 bRemoveSortEntry = false;
1806 xPop->set_active(u"sort"_ustr, pType->IsAlphabeticSort());
1809 OUString aIdent;
1810 switch (nContentType)
1812 case ContentTypeId::TABLE:
1813 aIdent = "tabletracking";
1814 bRemoveTableTracking = false;
1815 break;
1816 case ContentTypeId::REGION:
1817 aIdent = "sectiontracking";
1818 bRemoveSectionTracking = false;
1819 break;
1820 case ContentTypeId::FRAME:
1821 aIdent = "frametracking";
1822 bRemoveFrameTracking = false;
1823 break;
1824 case ContentTypeId::GRAPHIC:
1825 aIdent = "imagetracking";
1826 bRemoveImageTracking = false;
1827 break;
1828 case ContentTypeId::OLE:
1829 aIdent = "oleobjecttracking";
1830 bRemoveOLEobjectTracking = false;
1831 break;
1832 case ContentTypeId::BOOKMARK:
1833 aIdent = "bookmarktracking";
1834 bRemoveBookmarkTracking = false;
1835 break;
1836 case ContentTypeId::URLFIELD:
1837 aIdent = "hyperlinktracking";
1838 bRemoveHyperlinkTracking = false;
1839 break;
1840 case ContentTypeId::REFERENCE:
1841 aIdent = "referencetracking";
1842 bRemoveReferenceTracking = false;
1843 break;
1844 case ContentTypeId::INDEX:
1845 aIdent = "indextracking";
1846 bRemoveIndexTracking = false;
1847 break;
1848 case ContentTypeId::POSTIT:
1849 aIdent = "commenttracking";
1850 bRemoveCommentTracking = false;
1851 break;
1852 case ContentTypeId::DRAWOBJECT:
1853 aIdent = "drawingobjecttracking";
1854 bRemoveDrawingObjectTracking = false;
1855 break;
1856 case ContentTypeId::TEXTFIELD:
1857 aIdent = "fieldtracking";
1858 bRemoveFieldTracking = false;
1859 break;
1860 case ContentTypeId::FOOTNOTE:
1861 aIdent = "footnotetracking";
1862 bRemoveFootnoteTracking = false;
1863 break;
1864 case ContentTypeId::ENDNOTE:
1865 aIdent = "endnotetracking";
1866 bRemoveEndnoteTracking = false;
1867 break;
1868 default: break;
1870 if (!aIdent.isEmpty())
1871 xPop->set_active(aIdent, mTrackContentType[nContentType]);
1873 // Edit only if the shown content is coming from the current view.
1874 if (State::HIDDEN != m_eState &&
1875 (State::ACTIVE == m_eState || (GetActiveView() && m_pActiveShell == GetActiveView()->GetWrtShellPtr()))
1876 && lcl_IsContent(*xEntry, *m_xTreeView))
1878 bool bReadonly = m_pActiveShell->GetView().GetDocShell()->IsReadOnly();
1879 const bool bVisible = !weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->IsInvisible();
1880 const bool bProtected = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->IsProtect();
1881 const bool bProtectBM = (ContentTypeId::BOOKMARK == nContentType)
1882 && m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS);
1883 const bool bEditable
1884 = !bReadonly && pType->IsEditable()
1885 && ((bVisible && !bProtected) || ContentTypeId::REGION == nContentType);
1886 const bool bDeletable = pType->IsDeletable() && IsDeletable(*xEntry);
1887 const bool bRenamable
1888 = !bReadonly
1889 && (pType->IsRenamable()
1890 || (ContentTypeId::BOOKMARK == nContentType && !bProtectBM));
1892 // Choose which Delete entry to show.
1893 if (bDeletable)
1895 switch (nContentType)
1897 case ContentTypeId::OUTLINE:
1898 bRemoveDeleteChapterEntry = false;
1899 break;
1900 case ContentTypeId::FRAME:
1901 bRemoveDeleteFrameEntry = false;
1902 break;
1903 case ContentTypeId::GRAPHIC:
1904 bRemoveDeleteImageEntry = false;
1905 break;
1906 case ContentTypeId::OLE:
1907 bRemoveDeleteOLEObjectEntry = false;
1908 break;
1909 case ContentTypeId::BOOKMARK:
1910 bRemoveDeleteBookmarkEntry = false;
1911 break;
1912 case ContentTypeId::REGION:
1913 bRemoveDeleteRegionEntry = false;
1914 break;
1915 case ContentTypeId::URLFIELD:
1916 bRemoveDeleteHyperlinkEntry = false;
1917 break;
1918 case ContentTypeId::REFERENCE:
1919 bRemoveDeleteReferenceEntry = false;
1920 break;
1921 case ContentTypeId::POSTIT:
1922 bRemoveDeleteCommentEntry = false;
1923 break;
1924 case ContentTypeId::DRAWOBJECT:
1925 bRemoveDeleteDrawingObjectEntry = false;
1926 break;
1927 case ContentTypeId::TEXTFIELD:
1928 bRemoveDeleteFieldEntry = false;
1929 break;
1930 case ContentTypeId::FOOTNOTE:
1931 bRemoveDeleteFootnoteEntry = false;
1932 break;
1933 case ContentTypeId::ENDNOTE:
1934 bRemoveDeleteEndnoteEntry = false;
1935 break;
1936 default: break;
1939 if (ContentTypeId::FOOTNOTE == nContentType || ContentTypeId::ENDNOTE == nContentType)
1941 void* pUserData = weld::fromId<void*>(m_xTreeView->get_id(*xEntry));
1942 const SwTextFootnote* pFootnote =
1943 static_cast<const SwTextFootnoteContent*>(pUserData)->GetTextFootnote();
1944 if (!pFootnote)
1945 bRemoveGotoEntry = true;
1947 else if(ContentTypeId::OUTLINE == nContentType)
1949 bOutline = true;
1950 lcl_SetOutlineContentEntriesSensitivities(this, *m_xTreeView, *xEntry, *xSubPopOutlineContent);
1951 bRemoveToggleExpandEntry = lcl_InsertExpandCollapseAllItem(*m_xTreeView, *xEntry, *xPop);
1952 if (!bReadonly)
1954 bRemoveSelectEntry = false;
1955 if (!pType->IsAlphabeticSort())
1956 bRemoveChapterEntries = false;
1958 bRemoveCopyEntry = false;
1960 else if (bEditable)
1962 if(ContentTypeId::INDEX == nContentType)
1964 bRemoveReadonlyIndexEntry = false;
1965 bRemoveEditEntry = false;
1966 const SwTOXBase* pBase = weld::fromId<SwTOXBaseContent*>(m_xTreeView->get_id(*xEntry))->GetTOXBase();
1967 if (!pBase->IsTOXBaseInReadonly() && !SwEditShell::IsTOXBaseReadonly(*pBase))
1969 bRemoveUpdateIndexEntry = false;
1970 bRemoveDeleteIndexEntry = false;
1972 else
1973 bReadonly = true;
1974 xPop->set_active(OUString::number(405), SwEditShell::IsTOXBaseReadonly(*pBase));
1976 else if(ContentTypeId::TABLE == nContentType)
1978 bRemoveSelectEntry = false;
1979 bRemoveEditEntry = false;
1980 bRemoveUnprotectEntry = false;
1981 bRemoveDeleteTableEntry = false;
1982 bool bFull = false;
1983 OUString sTableName = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->GetName();
1984 bool bProt = m_pActiveShell->HasTableAnyProtection( &sTableName, &bFull );
1985 xPop->set_sensitive(OUString::number(403), !bFull);
1986 xPop->set_sensitive(OUString::number(404), bProt);
1987 xPop->set_sensitive(u"deletetable"_ustr, !bFull);
1989 else if(ContentTypeId::REGION == nContentType)
1991 bRemoveEditEntry = false;
1992 bRemoveProtectSection = false;
1993 bRemoveHideSection = false;
1994 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry));
1995 assert(dynamic_cast<SwRegionContent*>(static_cast<SwTypeNumber*>(pCnt)));
1996 const SwSectionFormat* pSectionFormat
1997 = static_cast<SwRegionContent*>(pCnt)->GetSectionFormat();
1998 bool bHidden = pSectionFormat->GetSection()->IsHidden();
1999 bRemoveSelectEntry = bHidden || !bVisible;
2000 xPop->set_active(u"protectsection"_ustr, bProtected);
2001 xPop->set_active(u"hidesection"_ustr, bHidden);
2003 else
2004 bRemoveEditEntry = false;
2006 if (bRenamable && !bReadonly)
2007 bRemoveRenameEntry = false;
2009 else
2011 if (ContentTypeId::OUTLINE == nContentType)
2013 bOutline = true;
2014 if (State::HIDDEN != m_eState)
2016 lcl_SetOutlineContentEntriesSensitivities(this, *m_xTreeView, *xEntry,
2017 *xSubPopOutlineContent);
2018 bRemoveSendOutlineEntry = false;
2020 bRemoveToggleExpandEntry
2021 = lcl_InsertExpandCollapseAllItem(*m_xTreeView, *xEntry, *xPop);
2023 else if (State::HIDDEN != m_eState
2024 && !m_pActiveShell->GetView().GetDocShell()->IsReadOnly()
2025 && pType->GetMemberCount() > 0)
2027 // Choose which Delete All entry to show.
2028 if (pType->IsDeletable() && IsDeletable(*xEntry))
2030 switch (nContentType)
2032 case ContentTypeId::TABLE:
2033 bRemoveDeleteAllTablesEntry = false;
2034 break;
2035 case ContentTypeId::FRAME:
2036 bRemoveDeleteAllFramesEntry = false;
2037 break;
2038 case ContentTypeId::GRAPHIC:
2039 bRemoveDeleteAllImagesEntry = false;
2040 break;
2041 case ContentTypeId::OLE:
2042 bRemoveDeleteAllOLEObjectsEntry = false;
2043 break;
2044 case ContentTypeId::BOOKMARK:
2045 bRemoveDeleteAllBookmarksEntry = false;
2046 break;
2047 case ContentTypeId::REGION:
2048 bRemoveDeleteAllRegionsEntry = false;
2049 break;
2050 case ContentTypeId::URLFIELD:
2051 bRemoveDeleteAllHyperlinksEntry = false;
2052 break;
2053 case ContentTypeId::REFERENCE:
2054 bRemoveDeleteAllReferencesEntry = false;
2055 break;
2056 case ContentTypeId::INDEX:
2057 bRemoveDeleteAllIndexesEntry = false;
2058 break;
2059 case ContentTypeId::POSTIT:
2060 bRemoveDeleteAllCommentsEntry = false;
2061 break;
2062 case ContentTypeId::DRAWOBJECT:
2063 bRemoveDeleteAllDrawingObjectsEntry = false;
2064 break;
2065 case ContentTypeId::TEXTFIELD:
2066 bRemoveDeleteAllFieldsEntry = false;
2067 break;
2068 case ContentTypeId::FOOTNOTE:
2069 bRemoveDeleteAllFootnotesEntry = false;
2070 bRemoveMakeAllFootnotesEndnotesEntry = false;
2071 break;
2072 case ContentTypeId::ENDNOTE:
2073 bRemoveDeleteAllEndnotesEntry = false;
2074 bRemoveMakeAllEndnotesFootnotesEntry = false;
2075 break;
2076 default: break;
2079 if (nContentType == ContentTypeId::POSTIT)
2081 const SwViewOption* m_pViewOpt = m_pActiveShell->GetViewOptions();
2082 xPop->set_active(u"showcomments"_ustr, m_pViewOpt->IsPostIts());
2083 xPop->set_active(u"showresolvedcomments"_ustr, m_pViewOpt->IsResolvedPostIts());
2084 bRemovePostItEntries = false;
2090 if (bRemoveToggleExpandEntry)
2091 xPop->remove(OUString::number(800));
2093 if (bRemoveGotoEntry)
2094 xPop->remove(OUString::number(900));
2096 if (bRemoveSelectEntry)
2097 xPop->remove(OUString::number(805));
2099 if (bRemoveChapterEntries)
2101 xPop->remove(OUString::number(801));
2102 xPop->remove(OUString::number(802));
2103 xPop->remove(OUString::number(803));
2104 xPop->remove(OUString::number(804));
2107 if (bRemoveSendOutlineEntry)
2108 xPop->remove(OUString::number(700));
2110 if (bRemovePostItEntries)
2112 xPop->remove(u"showcomments"_ustr);
2113 xPop->remove(u"showresolvedcomments"_ustr);
2116 if (bRemoveDeleteChapterEntry)
2117 xPop->remove(u"deletechapter"_ustr);
2118 if (bRemoveDeleteAllTablesEntry)
2119 xPop->remove(u"deletealltables"_ustr);
2120 if (bRemoveDeleteTableEntry)
2121 xPop->remove(u"deletetable"_ustr);
2122 if (bRemoveDeleteAllFramesEntry)
2123 xPop->remove(u"deleteallframes"_ustr);
2124 if (bRemoveDeleteFrameEntry)
2125 xPop->remove(u"deleteframe"_ustr);
2126 if (bRemoveDeleteAllImagesEntry)
2127 xPop->remove(u"deleteallimages"_ustr);
2128 if (bRemoveDeleteImageEntry)
2129 xPop->remove(u"deleteimage"_ustr);
2130 if (bRemoveDeleteAllOLEObjectsEntry)
2131 xPop->remove(u"deletealloleobjects"_ustr);
2132 if (bRemoveDeleteOLEObjectEntry)
2133 xPop->remove(u"deleteoleobject"_ustr);
2134 if (bRemoveDeleteAllBookmarksEntry)
2135 xPop->remove(u"deleteallbookmarks"_ustr);
2136 if (bRemoveDeleteBookmarkEntry)
2137 xPop->remove(u"deletebookmark"_ustr);
2138 if (bRemoveDeleteAllRegionsEntry)
2139 xPop->remove(u"deleteallregions"_ustr);
2140 if (bRemoveDeleteRegionEntry)
2141 xPop->remove(u"deleteregion"_ustr);
2142 if (bRemoveDeleteAllHyperlinksEntry)
2143 xPop->remove(u"deleteallhyperlinks"_ustr);
2144 if (bRemoveDeleteHyperlinkEntry)
2145 xPop->remove(u"deletehyperlink"_ustr);
2146 if (bRemoveDeleteAllReferencesEntry)
2147 xPop->remove(u"deleteallreferences"_ustr);
2148 if (bRemoveDeleteReferenceEntry)
2149 xPop->remove(u"deletereference"_ustr);
2150 if (bRemoveDeleteAllIndexesEntry)
2151 xPop->remove(u"deleteallindexes"_ustr);
2152 if (bRemoveDeleteIndexEntry)
2153 xPop->remove(u"deleteindex"_ustr);
2154 if (bRemoveDeleteAllCommentsEntry)
2155 xPop->remove(u"deleteallcomments"_ustr);
2156 if (bRemoveDeleteCommentEntry)
2157 xPop->remove(u"deletecomment"_ustr);
2158 if (bRemoveDeleteAllDrawingObjectsEntry)
2159 xPop->remove(u"deletealldrawingobjects"_ustr);
2160 if (bRemoveDeleteDrawingObjectEntry)
2161 xPop->remove(u"deletedrawingobject"_ustr);
2162 if (bRemoveDeleteAllFieldsEntry)
2163 xPop->remove(u"deleteallfields"_ustr);
2164 if (bRemoveDeleteFieldEntry)
2165 xPop->remove(u"deletefield"_ustr);
2166 if (bRemoveDeleteAllFootnotesEntry)
2167 xPop->remove(u"deleteallfootnotes"_ustr);
2168 if (bRemoveDeleteFootnoteEntry)
2169 xPop->remove(u"deletefootnote"_ustr);
2170 if (bRemoveDeleteAllEndnotesEntry)
2171 xPop->remove(u"deleteallendnotes"_ustr);
2172 if (bRemoveDeleteEndnoteEntry)
2173 xPop->remove(u"deleteendnote"_ustr);
2175 if (bRemoveMakeAllFootnotesEndnotesEntry)
2176 xPop->remove(u"makeallfootnotesendnotes"_ustr);
2177 if (bRemoveMakeAllEndnotesFootnotesEntry)
2178 xPop->remove(u"makeallendnotesfootnotes"_ustr);
2180 bool bRemoveDeleteEntry =
2181 bRemoveDeleteChapterEntry &&
2182 bRemoveDeleteTableEntry &&
2183 bRemoveDeleteFrameEntry &&
2184 bRemoveDeleteImageEntry &&
2185 bRemoveDeleteOLEObjectEntry &&
2186 bRemoveDeleteBookmarkEntry &&
2187 bRemoveDeleteRegionEntry &&
2188 bRemoveDeleteHyperlinkEntry &&
2189 bRemoveDeleteReferenceEntry &&
2190 bRemoveDeleteIndexEntry &&
2191 bRemoveDeleteCommentEntry &&
2192 bRemoveDeleteDrawingObjectEntry &&
2193 bRemoveDeleteFieldEntry &&
2194 bRemoveDeleteFootnoteEntry &&
2195 bRemoveDeleteEndnoteEntry &&
2196 bRemoveDeleteAllTablesEntry &&
2197 bRemoveDeleteAllFramesEntry &&
2198 bRemoveDeleteAllImagesEntry &&
2199 bRemoveDeleteAllOLEObjectsEntry &&
2200 bRemoveDeleteAllBookmarksEntry &&
2201 bRemoveDeleteAllRegionsEntry &&
2202 bRemoveDeleteAllHyperlinksEntry &&
2203 bRemoveDeleteAllReferencesEntry &&
2204 bRemoveDeleteAllIndexesEntry &&
2205 bRemoveDeleteCommentEntry &&
2206 bRemoveDeleteAllDrawingObjectsEntry &&
2207 bRemoveDeleteAllFieldsEntry &&
2208 bRemoveDeleteAllFootnotesEntry &&
2209 bRemoveDeleteAllEndnotesEntry;
2211 bool bRemoveMakeFootnotesEndnotesViceVersaEntry =
2212 bRemoveMakeAllFootnotesEndnotesEntry &&
2213 bRemoveMakeAllEndnotesFootnotesEntry;
2215 if (bRemoveRenameEntry)
2216 xPop->remove(OUString::number(502));
2218 if (bRemoveUpdateIndexEntry)
2219 xPop->remove(OUString::number(402));
2221 if (bRemoveReadonlyIndexEntry)
2222 xPop->remove(OUString::number(405));
2224 if (bRemoveUnprotectEntry)
2225 xPop->remove(OUString::number(404));
2227 if (bRemoveEditEntry)
2228 xPop->remove(OUString::number(403));
2230 if (bRemoveToggleExpandEntry &&
2231 bRemoveSendOutlineEntry)
2232 xPop->remove(u"separator1"_ustr);
2234 if (bRemoveCopyEntry)
2235 xPop->remove(u"copy"_ustr);
2237 if (bRemoveGotoEntry &&
2238 bRemoveCopyEntry &&
2239 bRemoveSelectEntry &&
2240 bRemoveDeleteEntry &&
2241 bRemoveMakeFootnotesEndnotesViceVersaEntry &&
2242 bRemoveChapterEntries &&
2243 bRemovePostItEntries &&
2244 bRemoveRenameEntry &&
2245 bRemoveReadonlyIndexEntry &&
2246 bRemoveUnprotectEntry &&
2247 bRemoveEditEntry)
2248 xPop->remove(u"separator2"_ustr);
2250 if (!bOutline)
2252 xSubPop1.reset();
2253 xPop->remove(OUString::number(1)); // outline level menu
2255 if (!bOutline || State::HIDDEN == m_eState)
2257 xSubPopOutlineTracking.reset();
2258 xPop->remove(OUString::number(4)); // outline tracking menu
2260 if (!bOutline || State::HIDDEN == m_eState ||
2261 !m_pActiveShell->GetViewOptions()->IsShowOutlineContentVisibilityButton() ||
2262 m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount() == 0)
2264 xSubPopOutlineContent.reset();
2265 xPop->remove(OUString::number(5)); // outline folding menu
2266 xPop->remove(u"separator3"_ustr);
2269 if (bRemoveTableTracking)
2270 xPop->remove(u"tabletracking"_ustr);
2271 if (bRemoveSectionTracking)
2272 xPop->remove(u"sectiontracking"_ustr);
2273 if (bRemoveFrameTracking)
2274 xPop->remove(u"frametracking"_ustr);
2275 if (bRemoveImageTracking)
2276 xPop->remove(u"imagetracking"_ustr);
2277 if (bRemoveOLEobjectTracking)
2278 xPop->remove(u"oleobjecttracking"_ustr);
2279 if (bRemoveBookmarkTracking)
2280 xPop->remove(u"bookmarktracking"_ustr);
2281 if (bRemoveHyperlinkTracking)
2282 xPop->remove(u"hyperlinktracking"_ustr);
2283 if (bRemoveReferenceTracking)
2284 xPop->remove(u"referencetracking"_ustr);
2285 if (bRemoveIndexTracking)
2286 xPop->remove(u"indextracking"_ustr);
2287 if (bRemoveCommentTracking)
2288 xPop->remove(u"commenttracking"_ustr);
2289 if (bRemoveDrawingObjectTracking)
2290 xPop->remove(u"drawingobjecttracking"_ustr);
2291 if (bRemoveFieldTracking)
2292 xPop->remove(u"fieldtracking"_ustr);
2293 if (bRemoveFootnoteTracking)
2294 xPop->remove(u"footnotetracking"_ustr);
2295 if (bRemoveEndnoteTracking)
2296 xPop->remove(u"endnotetracking"_ustr);
2297 if (bRemoveSortEntry)
2298 xPop->remove(u"sort"_ustr);
2299 if (bRemoveProtectSection)
2300 xPop->remove(u"protectsection"_ustr);
2301 if (bRemoveHideSection)
2302 xPop->remove(u"hidesection"_ustr);
2304 bool bSetSensitiveCollapseAllCategories = false;
2305 if (!m_bIsRoot && xEntry)
2307 bool bEntry = m_xTreeView->get_iter_first(*xEntry);
2308 while (bEntry)
2310 if (m_xTreeView->get_row_expanded(*xEntry))
2312 bSetSensitiveCollapseAllCategories = true;
2313 break;
2315 bEntry = m_xTreeView->iter_next_sibling(*xEntry);
2318 xPop->set_sensitive(u"collapseallcategories"_ustr, bSetSensitiveCollapseAllCategories);
2320 OUString sCommand = xPop->popup_at_rect(m_xTreeView.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)));
2321 if (!sCommand.isEmpty())
2322 ExecuteContextMenuAction(sCommand);
2324 return true;
2327 void SwContentTree::InsertContent(const weld::TreeIter& rParent)
2329 assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent))));
2330 SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent));
2331 bool bGraphic = pCntType->GetType() == ContentTypeId::GRAPHIC;
2332 std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator();
2333 const size_t nCount = pCntType->GetMemberCount();
2334 for(size_t i = 0; i < nCount; ++i)
2336 const SwContent* pCnt = pCntType->GetMember(i);
2337 OUString sEntry = pCnt->GetName();
2338 if (sEntry.isEmpty())
2339 sEntry = m_sSpace;
2340 OUString sId(weld::toId(pCnt));
2341 insert(&rParent, sEntry, sId, false, xChild.get());
2342 m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible());
2343 if (bGraphic)
2345 const OUString& rsURL = static_cast<const SwGraphicContent*>(pCnt)->GetLink();
2346 if (!rsURL.isEmpty())
2348 if (FStatHelper::IsDocument(rsURL))
2350 m_xTreeView->set_image(*xChild, RID_BMP_NAVI_GRAPHIC_LINK);
2352 else
2354 m_xTreeView->set_image(*xChild, RID_BMP_NAVI_GRAPHIC_BROKENLINK);
2361 void SwContentTree::insert(const weld::TreeIter* pParent, const OUString& rStr, const OUString& rId,
2362 bool bChildrenOnDemand, weld::TreeIter* pRet)
2364 m_xTreeView->insert(pParent, -1, &rStr, &rId, nullptr, nullptr, bChildrenOnDemand, pRet);
2365 ++m_nEntryCount;
2368 void SwContentTree::remove(const weld::TreeIter& rIter)
2370 if (m_xTreeView->iter_has_child(rIter))
2372 std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator(&rIter);
2373 (void)m_xTreeView->iter_children(*xChild);
2374 remove(*xChild);
2376 m_xTreeView->remove(rIter);
2377 --m_nEntryCount;
2380 // Content will be integrated into the Box only on demand.
2381 bool SwContentTree::RequestingChildren(const weld::TreeIter& rParent)
2383 // Does the parent already have children or is it not a 'children on demand' node?
2384 if (m_xTreeView->iter_has_child(rParent) || !m_xTreeView->get_children_on_demand(rParent))
2385 return false;
2387 // Is this a content type?
2388 if (lcl_IsContentType(rParent, *m_xTreeView))
2390 std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator();
2392 assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent))));
2393 SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent));
2395 const size_t nCount = pCntType->GetMemberCount();
2396 // Add for outline plus/minus
2397 if (pCntType->GetType() == ContentTypeId::OUTLINE)
2399 if (pCntType->IsAlphabeticSort())
2401 for (size_t i = 0; i < nCount; ++i)
2403 const SwContent* pCnt = pCntType->GetMember(i);
2404 if (pCnt)
2406 OUString sEntry = pCnt->GetName();
2407 if (sEntry.isEmpty())
2408 sEntry = m_sSpace;
2409 OUString sId(weld::toId(pCnt));
2411 insert(&rParent, sEntry, sId, false, xChild.get());
2412 m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible());
2416 else
2418 std::vector<std::unique_ptr<weld::TreeIter>> aParentCandidates;
2419 for (size_t i = 0; i < nCount; ++i)
2421 const SwContent* pCnt = pCntType->GetMember(i);
2422 if (pCnt)
2424 const auto nLevel
2425 = static_cast<const SwOutlineContent*>(pCnt)->GetOutlineLevel();
2426 OUString sEntry = pCnt->GetName();
2427 if (sEntry.isEmpty())
2428 sEntry = m_sSpace;
2429 OUString sId(weld::toId(pCnt));
2431 auto lambda = [nLevel, this](const std::unique_ptr<weld::TreeIter>& entry) {
2432 return lcl_IsLowerOutlineContent(*entry, *m_xTreeView, nLevel);
2435 // if there is a preceding outline node candidate with a lower outline level
2436 // use that as a parent, otherwise use the root node
2437 auto aFind = std::find_if(aParentCandidates.rbegin(),
2438 aParentCandidates.rend(), lambda);
2439 if (aFind != aParentCandidates.rend())
2440 insert(aFind->get(), sEntry, sId, false, xChild.get());
2441 else
2442 insert(&rParent, sEntry, sId, false, xChild.get());
2443 m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible());
2444 m_xTreeView->set_extra_row_indent(
2445 *xChild, nLevel + 1 - m_xTreeView->get_iter_depth(*xChild));
2447 // remove any parent candidates equal to or higher than this node
2448 std::erase_if(aParentCandidates, std::not_fn(lambda));
2450 // add this node as a parent candidate for any following nodes at a higher
2451 // outline level
2452 aParentCandidates.emplace_back(m_xTreeView->make_iterator(xChild.get()));
2457 else if (pCntType->GetType() == ContentTypeId::REGION)
2459 if (pCntType->IsAlphabeticSort())
2461 for(size_t i = 0; i < nCount; ++i)
2463 const SwRegionContent* pCnt =
2464 static_cast<const SwRegionContent*>(pCntType->GetMember(i));
2466 OUString sEntry = pCnt->GetName();
2467 OUString sId(weld::toId(pCnt));
2469 const auto nLevel = pCnt->GetRegionLevel();
2470 insert(&rParent, sEntry, sId, false, xChild.get());
2472 m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible());
2473 m_xTreeView->set_extra_row_indent(*xChild, nLevel);
2475 bool bHidden = pCnt->GetSectionFormat()->GetSection()->IsHidden();
2476 if (pCnt->IsProtect())
2477 m_xTreeView->set_image(*xChild, bHidden ? RID_BMP_PROT_HIDE : RID_BMP_PROT_NO_HIDE);
2478 else
2479 m_xTreeView->set_image(*xChild, bHidden ? RID_BMP_HIDE : RID_BMP_NO_HIDE);
2482 else
2484 std::vector<std::unique_ptr<weld::TreeIter>> aParentCandidates;
2485 for(size_t i = 0; i < nCount; ++i)
2487 const SwRegionContent* pCnt =
2488 static_cast<const SwRegionContent*>(pCntType->GetMember(i));
2490 OUString sEntry = pCnt->GetName();
2491 OUString sId(weld::toId(pCnt));
2493 const auto nLevel = pCnt->GetRegionLevel();
2494 auto lambda = [nLevel, this](const std::unique_ptr<weld::TreeIter>& xEntry)
2496 return lcl_IsLowerRegionContent(*xEntry, *m_xTreeView, nLevel);
2499 // if there is a preceding region node candidate with a lower region level use
2500 // that as a parent, otherwise use the root node
2501 auto aFind = std::find_if(aParentCandidates.rbegin(), aParentCandidates.rend(), lambda);
2502 if (aFind != aParentCandidates.rend())
2503 insert(aFind->get(), sEntry, sId, false, xChild.get());
2504 else
2505 insert(&rParent, sEntry, sId, false, xChild.get());
2506 m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible());
2508 bool bHidden = pCnt->GetSectionFormat()->GetSection()->IsHidden();
2509 if (pCnt->IsProtect())
2510 m_xTreeView->set_image(*xChild, bHidden ? RID_BMP_PROT_HIDE : RID_BMP_PROT_NO_HIDE);
2511 else
2512 m_xTreeView->set_image(*xChild, bHidden ? RID_BMP_HIDE : RID_BMP_NO_HIDE);
2514 // remove any parent candidates equal to or higher than this node
2515 std::erase_if(aParentCandidates, std::not_fn(lambda));
2517 // add this node as a parent candidate for any following nodes at a higher region level
2518 aParentCandidates.emplace_back(m_xTreeView->make_iterator(xChild.get()));
2522 else if (pCntType->GetType() == ContentTypeId::POSTIT)
2524 std::vector<std::unique_ptr<weld::TreeIter>> aParentCandidates;
2525 for(size_t i = 0; i < nCount; ++i)
2527 const SwPostItContent* pCnt =
2528 static_cast<const SwPostItContent*>(pCntType->GetMember(i));
2530 OUString sEntry = pCnt->GetName();
2531 OUString sId(weld::toId(pCnt));
2533 const SwPostItField* pPostItField =
2534 static_cast<const SwPostItField*>(pCnt->GetPostIt()->GetField());
2535 auto lambda = [&pPostItField, this](const std::unique_ptr<weld::TreeIter>& xEntry)
2537 SwPostItContent* pParentCandidateCnt =
2538 weld::fromId<SwPostItContent*>(m_xTreeView->get_id(*xEntry));
2539 return pPostItField->GetParentPostItId() ==
2540 static_cast<const SwPostItField*>(pParentCandidateCnt->GetPostIt()
2541 ->GetField())->GetPostItId();
2544 // if a parent candidate is not found use the passed root node
2545 auto aFind = std::find_if(aParentCandidates.rbegin(), aParentCandidates.rend(), lambda);
2546 if (aFind != aParentCandidates.rend())
2547 insert(aFind->get(), sEntry, sId, false, xChild.get());
2548 else
2549 insert(&rParent, sEntry, sId, false, xChild.get());
2551 m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible());
2553 // clear parent candidates when encountering a postit that doesn't have a parent
2554 // following postits can't have a parent that is in these candidates
2555 if (pPostItField->GetParentPostItId() == 0)
2556 aParentCandidates.clear();
2558 aParentCandidates.emplace_back(m_xTreeView->make_iterator(xChild.get()));
2561 else
2562 InsertContent(rParent);
2564 return nCount != 0;
2567 return false;
2570 void SwContentTree::ExpandAllHeadings()
2572 if (HasHeadings())
2574 std::unique_ptr<weld::TreeIter> xEntry = GetEntryAtAbsPos(0);
2575 if (xEntry)
2577 if (!IsAllExpanded(*m_xTreeView, *xEntry))
2578 ExpandOrCollapseAll(*m_xTreeView, *xEntry);
2583 SdrObject* SwContentTree::GetDrawingObjectsByContent(const SwContent *pCnt)
2585 SdrObject *pRetObj = nullptr;
2586 switch(pCnt->GetParent()->GetType())
2588 case ContentTypeId::DRAWOBJECT:
2590 SdrView* pDrawView = m_pActiveShell->GetDrawView();
2591 if (pDrawView)
2593 SwDrawModel* pDrawModel = m_pActiveShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel();
2594 SdrPage* pPage = pDrawModel->GetPage(0);
2596 for (const rtl::Reference<SdrObject>& pTemp : *pPage)
2598 if( pTemp->GetName() == pCnt->GetName())
2600 pRetObj = pTemp.get();
2601 break;
2605 break;
2607 default:
2608 pRetObj = nullptr;
2610 return pRetObj;
2613 void SwContentTree::Expand(const weld::TreeIter& rParent,
2614 std::vector<std::unique_ptr<weld::TreeIter>>* pNodesToExpand)
2616 if (!m_xTreeView->iter_has_child(rParent) && !m_xTreeView->get_children_on_demand(rParent))
2617 return;
2619 // pNodesToExpand is used by the Display function to restore the trees expand structure for
2620 // hierarchical content types, e.g., OUTLINE and REGION.
2621 if (pNodesToExpand)
2622 pNodesToExpand->emplace_back(m_xTreeView->make_iterator(&rParent));
2624 // rParentId is a string representation of a pointer to SwContentType or SwContent
2625 const OUString aParentId = m_xTreeView->get_id(rParent);
2626 // bParentIsContentType tells if the passed rParent tree entry is a content type or content
2627 const bool bParentIsContentType = lcl_IsContentType(rParent, *m_xTreeView);
2628 // eParentContentTypeId is the content type of the passed rParent tree entry
2629 const ContentTypeId eParentContentTypeId =
2630 bParentIsContentType ? weld::fromId<SwContentType*>(aParentId)->GetType() :
2631 weld::fromId<SwContent*>(aParentId)->GetParent()->GetType();
2633 if (m_nRootType == ContentTypeId::UNKNOWN && bParentIsContentType)
2635 // m_nActiveBlock and m_nHiddenBlock are used to persist the content type expand state for
2636 // the all content view mode
2637 const int nShift = static_cast<int>(eParentContentTypeId);
2638 SAL_WARN_IF(nShift < 0, "sw.ui", "ContentTypeId::UNKNOWN negative shift");
2639 if (nShift >= 0)
2641 const sal_Int32 nOr = 1 << nShift; //linear -> Bitposition
2642 if (State::HIDDEN != m_eState)
2644 m_nActiveBlock |= nOr;
2645 m_pConfig->SetActiveBlock(m_nActiveBlock);
2647 else
2648 m_nHiddenBlock |= nOr;
2652 if (m_nRootType == ContentTypeId::OUTLINE || (m_nRootType == ContentTypeId::UNKNOWN &&
2653 eParentContentTypeId == ContentTypeId::OUTLINE))
2655 if (bParentIsContentType)
2657 std::map< void*, bool > aCurrOutLineNodeMap;
2659 SwWrtShell* pShell = GetWrtShell();
2660 bool bParentHasChild = RequestingChildren(rParent);
2661 if (bParentHasChild)
2663 std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(&rParent));
2664 bool bChild = m_xTreeView->iter_next(*xChild);
2665 while (bChild && lcl_IsContent(*xChild, *m_xTreeView))
2667 if (m_xTreeView->iter_has_child(*xChild))
2669 assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xChild))));
2670 auto const nPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xChild))->GetOutlinePos();
2671 void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos ));
2672 aCurrOutLineNodeMap.emplace( key, false );
2673 std::map<void*, bool>::iterator iter = mOutLineNodeMap.find( key );
2674 if( iter != mOutLineNodeMap.end() && mOutLineNodeMap[key])
2676 aCurrOutLineNodeMap[key] = true;
2677 RequestingChildren(*xChild);
2678 if (pNodesToExpand)
2679 pNodesToExpand->emplace_back(m_xTreeView->make_iterator(xChild.get()));
2680 m_xTreeView->set_children_on_demand(*xChild, false);
2683 bChild = m_xTreeView->iter_next(*xChild);
2686 mOutLineNodeMap = std::move(aCurrOutLineNodeMap);
2687 return;
2689 else // content entry
2691 SwWrtShell* pShell = GetWrtShell();
2692 assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(aParentId)));
2693 auto const nPos = weld::fromId<SwOutlineContent*>(aParentId)->GetOutlinePos();
2694 void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos ));
2695 mOutLineNodeMap[key] = true;
2698 else if (m_nRootType == ContentTypeId::REGION || (m_nRootType == ContentTypeId::UNKNOWN &&
2699 eParentContentTypeId == ContentTypeId::REGION))
2701 if (bParentIsContentType)
2703 std::map<const void*, bool> aCurrentRegionNodeExpandMap;
2704 if (RequestingChildren(rParent))
2706 std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(&rParent));
2707 while (m_xTreeView->iter_next(*xChild) && lcl_IsContent(*xChild, *m_xTreeView))
2709 if (m_xTreeView->iter_has_child(*xChild))
2711 assert(dynamic_cast<SwRegionContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xChild))));
2712 const void* key =
2713 static_cast<const void*>(weld::fromId<SwRegionContent*>(
2714 m_xTreeView->get_id(*xChild))->GetSectionFormat());
2715 bool bExpandNode =
2716 m_aRegionNodeExpandMap.contains(key) && m_aRegionNodeExpandMap[key];
2717 aCurrentRegionNodeExpandMap.emplace(key, bExpandNode);
2718 if (bExpandNode)
2720 if (pNodesToExpand)
2721 pNodesToExpand->emplace_back(m_xTreeView->make_iterator(xChild.get()));
2722 RequestingChildren(*xChild);
2723 m_xTreeView->set_children_on_demand(*xChild, false);
2728 m_aRegionNodeExpandMap = std::move(aCurrentRegionNodeExpandMap);
2729 return;
2731 else // content entry
2733 assert(dynamic_cast<SwRegionContent*>(weld::fromId<SwTypeNumber*>(aParentId)));
2734 const void* key = static_cast<const void*>(
2735 weld::fromId<SwRegionContent*>(aParentId)->GetSectionFormat());
2736 m_aRegionNodeExpandMap[key] = true;
2739 else if (m_nRootType == ContentTypeId::POSTIT || (m_nRootType == ContentTypeId::UNKNOWN &&
2740 eParentContentTypeId == ContentTypeId::POSTIT))
2742 if (bParentIsContentType)
2744 std::map<const void*, bool> aCurrentPostItNodeExpandMap;
2745 if (RequestingChildren(rParent))
2747 std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(&rParent));
2748 while (m_xTreeView->iter_next(*xChild) && lcl_IsContent(*xChild, *m_xTreeView))
2750 if (m_xTreeView->iter_has_child(*xChild))
2752 assert(dynamic_cast<SwPostItContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xChild))));
2753 const void* key =
2754 static_cast<const void*>(weld::fromId<SwPostItContent*>(
2755 m_xTreeView->get_id(*xChild))->GetPostIt());
2756 bool bExpandNode =
2757 m_aPostItNodeExpandMap.contains(key) && m_aPostItNodeExpandMap[key];
2758 aCurrentPostItNodeExpandMap.emplace(key, bExpandNode);
2759 if (bExpandNode)
2761 if (pNodesToExpand)
2762 pNodesToExpand->emplace_back(m_xTreeView->make_iterator(xChild.get()));
2763 RequestingChildren(*xChild);
2764 m_xTreeView->set_children_on_demand(*xChild, false);
2769 m_aPostItNodeExpandMap = std::move(aCurrentPostItNodeExpandMap);
2770 return;
2772 else // content entry
2774 assert(dynamic_cast<SwPostItContent*>(weld::fromId<SwTypeNumber*>(aParentId)));
2775 const void* key = static_cast<const void*>(
2776 weld::fromId<SwPostItContent*>(aParentId)->GetPostIt());
2777 m_aPostItNodeExpandMap[key] = true;
2781 RequestingChildren(rParent);
2784 IMPL_LINK(SwContentTree, ExpandHdl, const weld::TreeIter&, rParent, bool)
2786 Expand(rParent, nullptr);
2787 return true;
2790 IMPL_LINK(SwContentTree, CollapseHdl, const weld::TreeIter&, rParent, bool)
2792 if (!m_xTreeView->iter_has_child(rParent) || m_xTreeView->get_children_on_demand(rParent))
2793 return true;
2795 if (lcl_IsContentType(rParent, *m_xTreeView))
2797 if (m_bIsRoot)
2799 // collapse to children of root node
2800 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(&rParent));
2801 if (m_xTreeView->iter_children(*xEntry))
2805 m_xTreeView->collapse_row(*xEntry);
2807 while (m_xTreeView->iter_next(*xEntry));
2809 return false; // return false to notify caller not to do collapse
2811 ContentTypeId eContentTypeId =
2812 weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent))->GetType();
2813 const int nShift = static_cast<int>(eContentTypeId);
2814 SAL_WARN_IF(nShift < 0, "sw.ui", "ContentTypeId::UNKNOWN negative shift");
2815 if (nShift >= 0)
2817 const sal_Int32 nAnd = ~(1 << nShift);
2818 if (State::HIDDEN != m_eState)
2820 m_nActiveBlock &= nAnd;
2821 m_pConfig->SetActiveBlock(m_nActiveBlock);
2823 else
2824 m_nHiddenBlock &= nAnd;
2827 else // content entry
2829 SwWrtShell* pShell = GetWrtShell();
2830 ContentTypeId eContentTypeId =
2831 weld::fromId<SwContent*>(m_xTreeView->get_id(rParent))->GetParent()->GetType();
2832 if (eContentTypeId == ContentTypeId::OUTLINE)
2834 assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent))));
2835 auto const nPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rParent))->GetOutlinePos();
2836 void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos ));
2837 mOutLineNodeMap[key] = false;
2839 else if(eContentTypeId == ContentTypeId::REGION)
2841 assert(dynamic_cast<SwRegionContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent))));
2842 const void* key = static_cast<const void*>(weld::fromId<SwRegionContent*>(m_xTreeView->get_id(rParent))->GetSectionFormat());
2843 m_aRegionNodeExpandMap[key] = false;
2845 else if(eContentTypeId == ContentTypeId::POSTIT)
2847 assert(dynamic_cast<SwPostItContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent))));
2848 const void* key = static_cast<const void*>(weld::fromId<SwPostItContent*>(m_xTreeView->get_id(rParent))->GetPostIt());
2849 m_aPostItNodeExpandMap[key] = false;
2853 return true;
2856 // Also on double click will be initially opened only.
2857 IMPL_LINK_NOARG(SwContentTree, ContentDoubleClickHdl, weld::TreeView&, bool)
2859 if (m_nRowActivateEventId)
2860 Application::RemoveUserEvent(m_nRowActivateEventId);
2861 // post the event to process row activate after mouse press event to be able to set key
2862 // modifier for selection feature (tdf#154211)
2863 m_nRowActivateEventId
2864 = Application::PostUserEvent(LINK(this, SwContentTree, AsyncContentDoubleClickHdl));
2866 bool bConsumed = false;
2868 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
2869 if (m_xTreeView->get_cursor(xEntry.get()) && lcl_IsContent(*xEntry, *m_xTreeView) &&
2870 (State::HIDDEN != m_eState))
2872 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry));
2873 assert(pCnt && "no UserData");
2874 if (pCnt && !pCnt->IsInvisible())
2876 // fdo#36308 don't expand outlines on double-click
2877 bConsumed = pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE;
2881 return bConsumed; // false/true == allow/disallow more to be done, i.e. expand/collapse children
2884 IMPL_LINK_NOARG(SwContentTree, AsyncContentDoubleClickHdl, void*, void)
2886 m_nRowActivateEventId = nullptr;
2888 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
2889 bool bEntry = m_xTreeView->get_cursor(xEntry.get());
2890 // Is it a content type?
2891 OSL_ENSURE(bEntry, "no current entry!");
2892 if (bEntry)
2894 if (lcl_IsContentType(*xEntry, *m_xTreeView) && !m_xTreeView->iter_has_child(*xEntry))
2896 RequestingChildren(*xEntry);
2897 m_xTreeView->set_children_on_demand(*xEntry, false);
2899 else if (!lcl_IsContentType(*xEntry, *m_xTreeView) && (State::HIDDEN != m_eState))
2901 assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
2902 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry));
2903 assert(pCnt && "no UserData");
2904 if (pCnt && !pCnt->IsInvisible())
2906 if (State::CONSTANT == m_eState)
2908 m_pActiveShell->GetView().GetViewFrame().GetWindow().ToTop();
2910 //Jump to content type:
2911 GotoContent(pCnt);
2917 namespace
2919 OUString GetImageIdForContentTypeId(ContentTypeId eType)
2921 OUString sResId;
2923 switch (eType)
2925 case ContentTypeId::OUTLINE:
2926 sResId = RID_BMP_NAVI_OUTLINE;
2927 break;
2928 case ContentTypeId::TABLE:
2929 sResId = RID_BMP_NAVI_TABLE;
2930 break;
2931 case ContentTypeId::FRAME:
2932 sResId = RID_BMP_NAVI_FRAME;
2933 break;
2934 case ContentTypeId::GRAPHIC:
2935 sResId = RID_BMP_NAVI_GRAPHIC;
2936 break;
2937 case ContentTypeId::OLE:
2938 sResId = RID_BMP_NAVI_OLE;
2939 break;
2940 case ContentTypeId::BOOKMARK:
2941 sResId = RID_BMP_NAVI_BOOKMARK;
2942 break;
2943 case ContentTypeId::REGION:
2944 sResId = RID_BMP_NAVI_REGION;
2945 break;
2946 case ContentTypeId::URLFIELD:
2947 sResId = RID_BMP_NAVI_URLFIELD;
2948 break;
2949 case ContentTypeId::REFERENCE:
2950 sResId = RID_BMP_NAVI_REFERENCE;
2951 break;
2952 case ContentTypeId::INDEX:
2953 sResId = RID_BMP_NAVI_INDEX;
2954 break;
2955 case ContentTypeId::POSTIT:
2956 sResId = RID_BMP_NAVI_POSTIT;
2957 break;
2958 case ContentTypeId::DRAWOBJECT:
2959 sResId = RID_BMP_NAVI_DRAWOBJECT;
2960 break;
2961 case ContentTypeId::TEXTFIELD:
2962 sResId = RID_BMP_NAVI_TEXTFIELD;
2963 break;
2964 case ContentTypeId::FOOTNOTE:
2965 sResId = RID_BMP_NAVI_FOOTNOTE;
2966 break;
2967 case ContentTypeId::ENDNOTE:
2968 sResId = RID_BMP_NAVI_ENDNOTE;
2969 break;
2970 case ContentTypeId::UNKNOWN:
2971 SAL_WARN("sw.ui", "ContentTypeId::UNKNOWN has no bitmap preview");
2972 break;
2975 return sResId;
2979 size_t SwContentTree::GetAbsPos(const weld::TreeIter& rIter)
2981 return weld::GetAbsPos(*m_xTreeView, rIter);
2984 size_t SwContentTree::GetEntryCount() const
2986 return m_nEntryCount;
2989 size_t SwContentTree::GetChildCount(const weld::TreeIter& rParent) const
2991 if (!m_xTreeView->iter_has_child(rParent))
2992 return 0;
2994 std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rParent));
2996 size_t nCount = 0;
2997 auto nRefDepth = m_xTreeView->get_iter_depth(*xParent);
2998 auto nActDepth = nRefDepth;
3001 if (!m_xTreeView->iter_next(*xParent))
3002 xParent.reset();
3003 else
3004 nActDepth = m_xTreeView->get_iter_depth(*xParent);
3005 nCount++;
3006 } while(xParent && nRefDepth < nActDepth);
3008 nCount--;
3009 return nCount;
3012 std::unique_ptr<weld::TreeIter> SwContentTree::GetEntryAtAbsPos(size_t nAbsPos) const
3014 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
3015 if (!m_xTreeView->get_iter_first(*xEntry))
3016 xEntry.reset();
3018 while (nAbsPos && xEntry)
3020 if (!m_xTreeView->iter_next(*xEntry))
3021 xEntry.reset();
3022 nAbsPos--;
3024 return xEntry;
3027 void SwContentTree::Display( bool bActive )
3029 // First read the selected entry to select it later again if necessary
3030 // -> the user data here are no longer valid!
3031 std::unique_ptr<weld::TreeIter> xOldSelEntry(m_xTreeView->make_iterator());
3032 if (!m_xTreeView->get_selected(xOldSelEntry.get()))
3033 xOldSelEntry.reset();
3034 size_t nEntryRelPos = 0; // relative position to their parent
3035 size_t nOldEntryCount = GetEntryCount();
3036 sal_Int32 nOldScrollPos = 0;
3037 if (xOldSelEntry)
3039 UpdateLastSelType();
3040 nOldScrollPos = m_xTreeView->vadjustment_get_value();
3041 std::unique_ptr<weld::TreeIter> xParentEntry = m_xTreeView->make_iterator(xOldSelEntry.get());
3042 while (m_xTreeView->get_iter_depth(*xParentEntry))
3043 m_xTreeView->iter_parent(*xParentEntry);
3044 if (m_xTreeView->get_iter_depth(*xOldSelEntry))
3045 nEntryRelPos = GetAbsPos(*xOldSelEntry) - GetAbsPos(*xParentEntry);
3048 clear();
3050 if (!bActive)
3052 m_aOverlayObjectDelayTimer.Stop();
3053 if (m_xOverlayObject && m_xOverlayObject->getOverlayManager())
3055 m_xOverlayObject->getOverlayManager()->remove(*m_xOverlayObject);
3056 m_xOverlayObject.reset();
3058 m_eState = State::HIDDEN;
3060 else if (State::HIDDEN == m_eState)
3061 m_eState = State::ACTIVE;
3063 SwWrtShell* pShell = GetWrtShell();
3064 if (pShell)
3066 std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
3067 std::unique_ptr<weld::TreeIter> xCntTypeEntry;
3068 std::vector<std::unique_ptr<weld::TreeIter>> aNodesToExpand;
3069 // all content navigation view
3070 if(m_nRootType == ContentTypeId::UNKNOWN)
3072 m_xTreeView->freeze();
3074 for( ContentTypeId nCntType : o3tl::enumrange<ContentTypeId>() )
3076 std::unique_ptr<SwContentType>& rpContentT = bActive ?
3077 m_aActiveContentArr[nCntType] :
3078 m_aHiddenContentArr[nCntType];
3079 if(!rpContentT)
3080 rpContentT.reset(new SwContentType(pShell, nCntType, m_nOutlineLevel ));
3082 OUString aImage(GetImageIdForContentTypeId(nCntType));
3083 bool bChOnDemand = 0 != rpContentT->GetMemberCount();
3085 // In case of LOK, empty content types must be hidden in the contenttree
3086 if (comphelper::LibreOfficeKit::isActive() && !bChOnDemand)
3088 continue;
3091 OUString sId(weld::toId(rpContentT.get()));
3092 insert(nullptr, rpContentT->GetName(), sId, bChOnDemand, xEntry.get());
3093 m_xTreeView->set_image(*xEntry, aImage);
3095 m_xTreeView->set_sensitive(*xEntry, bChOnDemand);
3097 if (nCntType == m_nLastSelType)
3098 xCntTypeEntry = m_xTreeView->make_iterator(xEntry.get());
3100 sal_Int32 nExpandOptions = (State::HIDDEN == m_eState)
3101 ? m_nHiddenBlock
3102 : m_nActiveBlock;
3103 if (nExpandOptions & (1 << static_cast<int>(nCntType)))
3105 // fill contents of to-be expanded entries while frozen
3106 Expand(*xEntry, &aNodesToExpand);
3107 m_xTreeView->set_children_on_demand(*xEntry, false);
3111 m_xTreeView->thaw();
3113 // restore visual expanded tree state
3114 for (const auto& rNode : aNodesToExpand)
3115 m_xTreeView->expand_row(*rNode);
3117 // root content navigation view
3118 else
3120 m_xTreeView->freeze();
3122 std::unique_ptr<SwContentType>& rpRootContentT = bActive ?
3123 m_aActiveContentArr[m_nRootType] :
3124 m_aHiddenContentArr[m_nRootType];
3125 if(!rpRootContentT)
3126 rpRootContentT.reset(new SwContentType(pShell, m_nRootType, m_nOutlineLevel ));
3127 OUString aImage(GetImageIdForContentTypeId(m_nRootType));
3128 bool bChOnDemand(m_nRootType == ContentTypeId::OUTLINE ||
3129 m_nRootType == ContentTypeId::REGION ||
3130 m_nRootType == ContentTypeId::POSTIT);
3131 OUString sId(weld::toId(rpRootContentT.get()));
3132 insert(nullptr, rpRootContentT->GetName(), sId, bChOnDemand, xEntry.get());
3133 m_xTreeView->set_image(*xEntry, aImage);
3135 xCntTypeEntry = m_xTreeView->make_iterator(xEntry.get());
3137 if (!bChOnDemand)
3138 InsertContent(*xEntry);
3139 else
3141 // fill contents of to-be expanded entries while frozen
3142 Expand(*xEntry, &aNodesToExpand);
3143 m_xTreeView->set_children_on_demand(*xEntry, false);
3146 m_xTreeView->set_sensitive(*xEntry, m_xTreeView->iter_has_child(*xEntry));
3148 m_xTreeView->thaw();
3150 if (bChOnDemand)
3152 // restore visual expanded tree state
3153 for (const auto& rNode : aNodesToExpand)
3154 m_xTreeView->expand_row(*rNode);
3156 else
3157 m_xTreeView->expand_row(*xEntry);
3160 // Reselect the old selected entry. If it is not available, select the entry at the old
3161 // selected entry position unless that entry position is now a content type or is past the
3162 // end of the member list then select the entry at the previous entry position.
3163 if (xOldSelEntry)
3165 std::unique_ptr<weld::TreeIter> xSelEntry = m_xTreeView->make_iterator(xCntTypeEntry.get());
3166 if (nEntryRelPos)
3168 std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator(xCntTypeEntry.get()));
3169 std::unique_ptr<weld::TreeIter> xTemp(m_xTreeView->make_iterator(xIter.get()));
3170 sal_uLong nPos = 1;
3171 bool bNext;
3172 while ((bNext = m_xTreeView->iter_next(*xIter) && lcl_IsContent(*xIter, *m_xTreeView)))
3174 if (nPos == nEntryRelPos)
3176 m_xTreeView->copy_iterator(*xIter, *xSelEntry);
3177 break;
3179 m_xTreeView->copy_iterator(*xIter, *xTemp); // note previous entry
3180 nPos++;
3182 if (!bNext)
3183 xSelEntry = std::move(xTemp);
3185 // set_cursor unselects all entries, makes passed entry visible, and selects it
3186 m_xTreeView->set_cursor(*xSelEntry);
3189 UpdateContentFunctionsToolbar();
3192 if (!m_bIgnoreDocChange && GetEntryCount() == nOldEntryCount)
3194 m_xTreeView->vadjustment_set_value(nOldScrollPos);
3198 void SwContentTree::clear()
3200 m_xTreeView->freeze();
3201 m_xTreeView->clear();
3202 m_nEntryCount = 0;
3203 m_xTreeView->thaw();
3206 bool SwContentTree::FillTransferData(TransferDataContainer& rTransfer)
3208 SwWrtShell* pWrtShell = GetWrtShell();
3209 OSL_ENSURE(pWrtShell, "no Shell!");
3211 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
3212 bool bEntry = m_xTreeView->get_cursor(xEntry.get());
3213 if (!bEntry || lcl_IsContentType(*xEntry, *m_xTreeView) || !pWrtShell)
3214 return false;
3215 assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
3216 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry));
3218 OUString sEntry;
3219 OUString sUrl;
3220 OUString sCrossRef;
3221 bool bUrl = true;
3222 bool bCrossRef = true;
3223 OUString sOutlineText;
3225 const ContentTypeId eActType = pCnt->GetParent()->GetType();
3226 switch (eActType)
3228 case ContentTypeId::OUTLINE:
3230 const SwOutlineNodes::size_type nPos = static_cast<SwOutlineContent*>(pCnt)->GetOutlinePos();
3231 OSL_ENSURE(nPos < pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount(),
3232 "outlinecnt changed");
3234 // make sure outline may actually be copied
3235 if( pWrtShell->IsOutlineCopyable( nPos ) )
3237 const SwNumRule* pOutlRule = pWrtShell->GetOutlineNumRule();
3238 const SwTextNode* pTextNd =
3239 pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineNode(nPos);
3240 if (pTextNd && pOutlRule && pTextNd->IsNumbered(pWrtShell->GetLayout()))
3242 SwNumberTree::tNumberVector aNumVector =
3243 pTextNd->GetNumberVector(pWrtShell->GetLayout());
3244 for( int nLevel = 0;
3245 nLevel <= pTextNd->GetActualListLevel();
3246 nLevel++ )
3248 const SwNumberTree::tSwNumTreeNumber nVal = aNumVector[nLevel] + 1;
3249 sEntry += OUString::number( nVal - pOutlRule->Get(nLevel).GetStart() ) + ".";
3252 sEntry += pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText(nPos, pWrtShell->GetLayout(), false);
3253 sOutlineText = pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText(nPos, pWrtShell->GetLayout());
3254 m_bIsOutlineMoveable = static_cast<SwOutlineContent*>(pCnt)->IsMoveable();
3257 break;
3258 case ContentTypeId::BOOKMARK:
3259 sEntry = m_xTreeView->get_text(*xEntry);
3260 break;
3261 case ContentTypeId::TABLE:
3262 case ContentTypeId::FRAME:
3263 case ContentTypeId::REGION:
3264 bCrossRef = false;
3265 sEntry = m_xTreeView->get_text(*xEntry);
3266 break;
3267 // content types that cannot be inserted, as URL, section, or reference
3268 case ContentTypeId::POSTIT:
3269 case ContentTypeId::INDEX:
3270 return false;
3271 // content types than can only be inserted as a cross-reference
3272 case ContentTypeId::REFERENCE:
3273 case ContentTypeId::TEXTFIELD:
3274 case ContentTypeId::FOOTNOTE:
3275 case ContentTypeId::ENDNOTE:
3276 bUrl = false;
3277 sEntry = m_xTreeView->get_text(*xEntry);
3278 break;
3279 // content types that can only be inserted as a hyperlink
3280 case ContentTypeId::URLFIELD:
3281 sUrl = static_cast<SwURLFieldContent*>(pCnt)->GetURL();
3282 [[fallthrough]];
3283 case ContentTypeId::OLE:
3284 case ContentTypeId::GRAPHIC:
3285 case ContentTypeId::DRAWOBJECT:
3286 bCrossRef = false;
3287 sEntry = m_xTreeView->get_text(*xEntry);
3288 break;
3289 default:
3290 return false;
3293 if(!sEntry.isEmpty())
3295 const SwDocShell* pDocShell = pWrtShell->GetView().GetDocShell();
3296 if (bUrl && sUrl.isEmpty())
3298 if(pDocShell->HasName())
3300 SfxMedium* pMedium = pDocShell->GetMedium();
3301 sUrl = pMedium->GetURLObject().GetURLNoMark();
3303 else if (SwView* pView = GetActiveView(); State::CONSTANT == m_eState
3304 && (!pView || m_pActiveShell != pView->GetWrtShellPtr()))
3306 // Urls of inactive views cannot dragged without
3307 // file names, also.
3308 return false;
3310 else if (eActType != ContentTypeId::REGION && eActType != ContentTypeId::BOOKMARK)
3312 // For sections and bookmarks a link is also allowed
3313 // without a filename into its own document.
3314 return false;
3317 const OUString& rToken = pCnt->GetParent()->GetTypeToken();
3318 sUrl += "#" + sEntry;
3319 if(!rToken.isEmpty())
3321 sUrl += OUStringChar(cMarkSeparator) + rToken;
3325 if (bCrossRef)
3327 if (eActType == ContentTypeId::TEXTFIELD)
3329 SwTextFieldContent* pTextFieldContent = static_cast<SwTextFieldContent*>(pCnt);
3330 const SwFormatField* pFormatField = pTextFieldContent->GetFormatField();
3331 const SwField* pField = pFormatField->GetField();
3333 if (SwFieldTypesEnum::Sequence != pField->GetTypeId())
3334 return false;
3336 OUString sVal = pField->ExpandField(true, m_pActiveShell->GetLayout());
3337 sal_uInt32 nSeqNo = sVal.toUInt32();
3338 if (nSeqNo > 0)
3340 --nSeqNo;
3341 sVal = OUString::number(nSeqNo);
3343 else
3344 return false;
3346 const OUString sFieldTypeName = pField->GetTyp()->GetName();
3347 sCrossRef = OUString::number(static_cast<int>(REFERENCESUBTYPE::REF_SEQUENCEFLD))
3348 + u"|" + sFieldTypeName + u"|" + sVal;
3350 else if (eActType == ContentTypeId::REFERENCE)
3352 sCrossRef = OUString::number(static_cast<int>(REFERENCESUBTYPE::REF_SETREFATTR))
3353 + u"|" + sEntry;
3355 else if (eActType == ContentTypeId::BOOKMARK)
3357 sCrossRef = OUString::number(static_cast<int>(REFERENCESUBTYPE::REF_BOOKMARK))
3358 + u"|" + sEntry;
3360 else if (eActType == ContentTypeId::FOOTNOTE || eActType == ContentTypeId::ENDNOTE)
3362 SeqFieldLstElem aElem(sEntry, 0);
3363 SwSeqFieldList aArr;
3364 size_t nIdx = 0;
3365 OUString sVal;
3367 if (m_pActiveShell->GetSeqFootnoteList(aArr, eActType == ContentTypeId::ENDNOTE)
3368 && aArr.SeekEntry(aElem, &nIdx))
3369 sVal = OUString::number(aArr[nIdx].nSeqNo);
3370 else
3371 return false;
3373 REFERENCESUBTYPE eReferenceSubType =
3374 eActType == ContentTypeId::FOOTNOTE ? REFERENCESUBTYPE::REF_FOOTNOTE :
3375 REFERENCESUBTYPE::REF_ENDNOTE;
3377 sCrossRef = OUString::number(static_cast<int>(eReferenceSubType)) + u"|"
3378 + sEntry + u"|" + sVal;
3380 else if (eActType == ContentTypeId::OUTLINE)
3382 sEntry = sOutlineText;
3383 const SwOutlineNodes::size_type nPos =
3384 static_cast<SwOutlineContent*>(pCnt)->GetOutlinePos();
3385 const SwTextNode* pTextNode =
3386 pWrtShell->GetNodes().GetOutLineNds()[nPos]->GetTextNode();
3387 sw::mark::MarkBase const * const pMark =
3388 pWrtShell->getIDocumentMarkAccess()->getMarkForTextNode(
3389 *pTextNode, IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK);
3390 // REFERENCESUBTYPE_OUTLINE is changed to REFERENCESUBTYPE::BOOKMARK in
3391 // SwWrtShell::NavigatorPaste. It is used to differentiate between a
3392 // headings reference and a regular bookmark reference to show different
3393 // options in the reference mark type popup menu.
3394 sCrossRef = OUString::number(static_cast<int>(REFERENCESUBTYPE::REF_OUTLINE))
3395 + u"|" + pMark->GetName();
3399 NaviContentBookmark aBmk(sUrl, sCrossRef, sEntry, pDocShell);
3400 aBmk.Copy(rTransfer);
3402 // An INetBookmark must a be delivered to foreign DocShells
3403 if (bUrl && pDocShell->HasName())
3405 INetBookmark aBkmk( sUrl, sEntry );
3406 rTransfer.CopyINetBookmark( aBkmk );
3410 return true;
3413 void SwContentTree::ToggleToRoot()
3415 if(!m_bIsRoot)
3417 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
3418 bool bEntry = m_xTreeView->get_cursor(xEntry.get());
3419 if (bEntry)
3421 const SwContentType* pCntType;
3422 if (lcl_IsContentType(*xEntry, *m_xTreeView))
3424 assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
3425 pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry));
3427 else
3429 assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
3430 pCntType = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->GetParent();
3432 m_nRootType = pCntType->GetType();
3433 m_bIsRoot = true;
3434 if (m_nRootType == ContentTypeId::OUTLINE || m_nRootType == ContentTypeId::DRAWOBJECT)
3436 m_xTreeView->set_selection_mode(SelectionMode::Multiple);
3438 Display(State::HIDDEN != m_eState);
3441 else
3443 m_xTreeView->set_selection_mode(SelectionMode::Single);
3444 m_nLastSelType = m_nRootType;
3445 m_nRootType = ContentTypeId::UNKNOWN;
3446 m_bIsRoot = false;
3447 // Other content type member data could have changed while in root view. Fill the content
3448 // member lists excluding the toggled from root content which should already have the most
3449 // recent data.
3450 if (State::HIDDEN != m_eState)
3452 for (ContentTypeId i : o3tl::enumrange<ContentTypeId>())
3454 if (i != m_nLastSelType && m_aActiveContentArr[i])
3455 m_aActiveContentArr[i]->FillMemberList();
3458 Display(State::HIDDEN != m_eState);
3460 m_pConfig->SetRootType( m_nRootType );
3461 weld::Toolbar* pBox = GetParentWindow()->m_xContent5ToolBox.get();
3462 pBox->set_item_active(u"root"_ustr, m_bIsRoot);
3465 bool SwContentTree::HasContentChanged()
3467 bool bContentChanged = false;
3469 // - Run through the local array and the Treelistbox in parallel.
3470 // - Are the records not expanded, they are discarded only in the array
3471 // and the content type will be set as the new UserData.
3472 // - Is the root mode is active only this will be updated.
3474 // Valid for the displayed content types is:
3475 // the Memberlist will be erased and the membercount will be updated
3476 // If content will be checked, the memberlists will be replenished
3477 // at the same time. Once a difference occurs it will be only replenished
3478 // no longer checked. Finally, the box is filled again.
3480 if (State::HIDDEN == m_eState)
3482 for(ContentTypeId i : o3tl::enumrange<ContentTypeId>())
3484 if(m_aActiveContentArr[i])
3485 m_aActiveContentArr[i]->Invalidate();
3487 return false;
3490 // single content type navigation view
3491 if(m_bIsRoot)
3493 std::unique_ptr<weld::TreeIter> xRootEntry(m_xTreeView->make_iterator());
3494 if (!m_xTreeView->get_iter_first(*xRootEntry))
3495 return true;
3497 assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xRootEntry))));
3498 const ContentTypeId nType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xRootEntry))->GetType();
3499 SwContentType* pArrType = m_aActiveContentArr[nType].get();
3500 assert(weld::toId(pArrType) == m_xTreeView->get_id(*xRootEntry));
3501 if (!pArrType)
3502 return true;
3504 pArrType->FillMemberList(&bContentChanged);
3505 if (bContentChanged)
3506 return true;
3508 // FillMemberList tests if member count in old member array equals member count in new
3509 // member array. Test here for member count difference between array and tree.
3510 const size_t nChildCount = GetChildCount(*xRootEntry);
3511 if (nChildCount != pArrType->GetMemberCount())
3512 return true;
3514 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(xRootEntry.get()));
3515 for (size_t j = 0; j < nChildCount; ++j)
3517 if (!m_xTreeView->iter_next(*xEntry))
3519 SAL_WARN("sw.ui", "unexpected missing entry");
3520 return true;
3523 // FillMemberList clears the content type member list and refills with new data.
3524 // Treeview entry user data is set here to the string representation of the pointer to
3525 // the member data in the array. The Display function will clear and recreate the
3526 // treeview from the content type member arrays if content change is detected.
3527 const SwContent* pCnt = pArrType->GetMember(j);
3529 if (pCnt->IsInvisible() != m_xTreeView->get_sensitive(*xEntry, 0))
3530 return true;
3532 OUString sEntryText = m_xTreeView->get_text(*xEntry);
3533 if (sEntryText != pCnt->GetName() &&
3534 !(sEntryText == m_sSpace && pCnt->GetName().isEmpty()))
3536 return true;
3539 // Set_id needs to be done here because FillMemberList clears the content type member
3540 // list and refills with new data making the previously set id invalid. If there is no
3541 // content change detected the Display function will not be called and the tree entry
3542 // user data will not be set to the new content member pointer address.
3543 OUString sSubId(weld::toId(pCnt));
3544 m_xTreeView->set_id(*xEntry, sSubId);
3547 // all content types navigation view
3548 else
3550 // Fill member list for each content type and check for content change. If content change
3551 // is detected only fill member lists for remaining content types. The Display function
3552 // will clear and recreate the treeview from the content type member arrays if content has
3553 // changed.
3555 if (comphelper::LibreOfficeKit::isActive())
3557 // In case of LOK, empty contentTypes are hidden, even in all content view
3558 // so it is not enough to check only the m_xTreeView.
3559 bool bCountChanged = false;
3560 bool bHasContentChanged = false;
3561 for (ContentTypeId i : o3tl::enumrange<ContentTypeId>())
3563 if (m_aActiveContentArr[i])
3565 auto nLastTMCount = m_aActiveContentArr[i]->GetMemberCount();
3566 if (i == ContentTypeId::OUTLINE) // this is required for checking if header level is changed
3567 m_aActiveContentArr[i]->FillMemberList(&bHasContentChanged);
3568 else
3569 m_aActiveContentArr[i]->FillMemberList();
3570 // If the member count of a type is changed, then the content is surely changed
3571 if (m_aActiveContentArr[i]->GetMemberCount() != nLastTMCount)
3572 bCountChanged = true;
3573 if (bHasContentChanged)
3574 bContentChanged = true;
3577 if (bCountChanged || bContentChanged)
3578 return true;
3581 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
3583 // lambda function to find the next content type entry
3584 auto lcl_nextContentTypeEntry = [this, &xEntry](){
3585 while (m_xTreeView->get_iter_depth(*xEntry))
3586 m_xTreeView->iter_parent(*xEntry);
3587 return m_xTreeView->iter_next_sibling(*xEntry);
3590 for (bool bEntry = m_xTreeView->get_iter_first(*xEntry); bEntry;
3591 bEntry = lcl_nextContentTypeEntry())
3593 assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
3594 SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry));
3595 const size_t nCntCount = pCntType->GetMemberCount();
3596 const ContentTypeId nType = pCntType->GetType();
3597 SwContentType* pArrType = m_aActiveContentArr[nType].get();
3598 assert(weld::toId(pArrType) == m_xTreeView->get_id(*xEntry));
3600 if (!pArrType)
3602 bContentChanged = true;
3603 continue;
3606 // all content type member lists must be filled!
3607 if (bContentChanged)
3609 // If content change has already been detected there is no need to detect
3610 // other content change so no argument is supplied here to FillMemberList.
3611 pArrType->FillMemberList();
3612 continue;
3615 pArrType->FillMemberList(&bContentChanged);
3616 if (bContentChanged)
3617 continue;
3619 // does entry have children?
3620 if (m_xTreeView->get_row_expanded(*xEntry))
3622 const size_t nChildCount = GetChildCount(*xEntry);
3623 if(nChildCount != pArrType->GetMemberCount())
3625 bContentChanged = true;
3626 continue;
3629 for(size_t j = 0; j < nChildCount; ++j)
3631 if (!m_xTreeView->iter_next(*xEntry))
3633 SAL_WARN("sw.ui", "unexpected missing entry");
3634 bContentChanged = true;
3635 break;
3638 const SwContent* pCnt = pArrType->GetMember(j);
3640 if (pCnt->IsInvisible() != m_xTreeView->get_sensitive(*xEntry, 0))
3642 bContentChanged = true;
3643 break;
3646 OUString sEntryText = m_xTreeView->get_text(*xEntry);
3647 if( sEntryText != pCnt->GetName() &&
3648 !(sEntryText == m_sSpace && pCnt->GetName().isEmpty()))
3650 bContentChanged = true;
3651 break;
3654 // See comment above in single content type navigation view block for why the
3655 // following is done here.
3656 OUString sSubId(weld::toId(pCnt));
3657 m_xTreeView->set_id(*xEntry, sSubId);
3660 // not expanded and has children
3661 else if (m_xTreeView->iter_has_child(*xEntry))
3663 bool bRemoveChildren = false;
3664 const size_t nOldChildCount = GetChildCount(*xEntry);
3665 const size_t nNewChildCount = pArrType->GetMemberCount();
3666 if (nOldChildCount != nNewChildCount)
3668 bRemoveChildren = true;
3670 else
3672 std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(xEntry.get()));
3673 (void)m_xTreeView->iter_children(*xChild);
3674 for (size_t j = 0; j < nOldChildCount; ++j)
3676 const SwContent* pCnt = pArrType->GetMember(j);
3677 OUString sSubId(weld::toId(pCnt));
3678 m_xTreeView->set_id(*xChild, sSubId);
3679 OUString sEntryText = m_xTreeView->get_text(*xChild);
3680 if( sEntryText != pCnt->GetName() &&
3681 !(sEntryText == m_sSpace && pCnt->GetName().isEmpty()))
3683 bRemoveChildren = true;
3685 (void)m_xTreeView->iter_next(*xChild);
3688 if (bRemoveChildren)
3690 std::unique_ptr<weld::TreeIter> xRemove(m_xTreeView->make_iterator(xEntry.get()));
3691 while (m_xTreeView->iter_children(*xRemove))
3693 remove(*xRemove);
3694 m_xTreeView->copy_iterator(*xEntry, *xRemove);
3696 m_xTreeView->set_children_on_demand(*xEntry, nNewChildCount != 0);
3699 else if((nCntCount != 0)
3700 != (pArrType->GetMemberCount()!=0))
3702 bContentChanged = true;
3703 continue;
3708 return bContentChanged;
3711 void SwContentTree::UpdateLastSelType()
3713 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
3714 if (m_xTreeView->get_selected(xEntry.get()))
3716 while (m_xTreeView->get_iter_depth(*xEntry))
3717 m_xTreeView->iter_parent(*xEntry);
3718 void* pId = weld::fromId<void*>(m_xTreeView->get_id(*xEntry));
3719 if (pId && lcl_IsContentType(*xEntry, *m_xTreeView))
3721 assert(dynamic_cast<SwContentType*>(static_cast<SwTypeNumber*>(pId)));
3722 m_nLastSelType = static_cast<SwContentType*>(pId)->GetType();
3727 void SwContentTree::FindActiveTypeAndRemoveUserData()
3729 UpdateLastSelType();
3731 // If clear is called by TimerUpdate:
3732 // Only for root can the validity of the UserData be guaranteed.
3733 m_xTreeView->all_foreach([this](weld::TreeIter& rEntry){
3734 m_xTreeView->set_id(rEntry, u""_ustr);
3735 return false;
3739 void SwContentTree::SetHiddenShell(SwWrtShell* pSh)
3741 m_pHiddenShell = pSh;
3742 m_eState = State::HIDDEN;
3743 FindActiveTypeAndRemoveUserData();
3744 for(ContentTypeId i : o3tl::enumrange<ContentTypeId>())
3746 m_aHiddenContentArr[i].reset();
3748 Display(false);
3750 GetParentWindow()->UpdateListBox();
3753 void SwContentTree::SetActiveShell(SwWrtShell* pSh)
3755 bool bClear = m_pActiveShell != pSh;
3756 if (State::ACTIVE == m_eState && bClear)
3758 EndListeningAll();
3759 m_pActiveShell = pSh;
3760 FindActiveTypeAndRemoveUserData();
3761 clear();
3763 else if (State::CONSTANT == m_eState)
3765 EndListeningAll();
3766 m_pActiveShell = pSh;
3767 m_eState = State::ACTIVE;
3768 bClear = true;
3771 // tdf#148432 in LTR UI override the navigator treeview direction based on
3772 // the first page directionality
3773 if (m_pActiveShell && !AllSettings::GetLayoutRTL())
3775 const SwPageDesc& rDesc = m_pActiveShell->GetPageDesc(0);
3776 const SvxFrameDirectionItem& rFrameDir = rDesc.GetMaster().GetFrameDir();
3777 m_xTreeView->set_direction(rFrameDir.GetValue() == SvxFrameDirection::Horizontal_RL_TB);
3780 // Only if it is the active view, the array will be deleted and
3781 // the screen filled new.
3782 if (State::ACTIVE == m_eState && bClear)
3784 if (m_pActiveShell)
3785 StartListening(*m_pActiveShell->GetView().GetDocShell());
3786 FindActiveTypeAndRemoveUserData();
3787 for(ContentTypeId i : o3tl::enumrange<ContentTypeId>())
3789 m_aActiveContentArr[i].reset();
3791 Display(true);
3795 void SwContentTree::SetConstantShell(SwWrtShell* pSh)
3797 EndListeningAll();
3798 m_pActiveShell = pSh;
3799 m_eState = State::CONSTANT;
3800 StartListening(*m_pActiveShell->GetView().GetDocShell());
3801 FindActiveTypeAndRemoveUserData();
3802 for(ContentTypeId i : o3tl::enumrange<ContentTypeId>())
3804 m_aActiveContentArr[i].reset();
3806 Display(true);
3809 void SwContentTree::Notify(SfxBroadcaster & rBC, SfxHint const& rHint)
3811 if (rHint.GetId() == SfxHintId::ThisIsAnSfxEventHint)
3813 const SfxEventHint* pEventHint = static_cast<const SfxEventHint*>(&rHint);
3814 if (pEventHint->GetEventId() == SfxEventHintId::CloseView)
3816 SfxViewEventHint const*const pVEHint(static_cast<SfxViewEventHint const*>(&rHint));
3817 if (m_pActiveShell)
3819 SwXTextView* pDyingShell = dynamic_cast<SwXTextView*>(pVEHint->GetController().get());
3820 if (pDyingShell && pDyingShell->GetView() == &m_pActiveShell->GetView())
3822 SetActiveShell(nullptr); // our view is dying, clear our pointers to it
3825 return;
3828 SfxListener::Notify(rBC, rHint);
3829 switch (rHint.GetId())
3831 case SfxHintId::SwNavigatorUpdateTracking:
3832 UpdateTracking();
3833 break;
3834 case SfxHintId::SwNavigatorSelectOutlinesWithSelections:
3836 if (m_nRootType == ContentTypeId::OUTLINE)
3838 SelectOutlinesWithSelection();
3839 // make first selected entry visible
3840 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
3841 if (xEntry && m_xTreeView->get_selected(xEntry.get()))
3842 m_xTreeView->scroll_to_row(*xEntry);
3844 else if (m_nRootType == ContentTypeId::UNKNOWN)
3845 m_xTreeView->unselect_all();
3846 break;
3848 case SfxHintId::DocChanged:
3849 OverlayObject();
3850 if (!m_bIgnoreDocChange)
3852 m_bDocHasChanged = true;
3853 TimerUpdate(&m_aUpdTimer);
3855 break;
3856 case SfxHintId::ModeChanged:
3857 if (SwWrtShell* pShell = GetWrtShell())
3859 const bool bReadOnly = pShell->GetView().GetDocShell()->IsReadOnly();
3860 if (bReadOnly != m_bIsLastReadOnly)
3862 m_bIsLastReadOnly = bReadOnly;
3864 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
3865 if (m_xTreeView->get_cursor(xEntry.get()))
3867 m_xTreeView->select(*xEntry);
3868 UpdateContentFunctionsToolbar();
3870 else
3871 m_xTreeView->unselect_all();
3874 break;
3875 default:
3876 break;
3880 // Handler for outline entry up/down left/right movement
3881 void SwContentTree::ExecCommand(std::u16string_view rCmd, bool bOutlineWithChildren)
3883 if (m_xTreeView->count_selected_rows() == 0)
3884 return;
3886 const bool bUp = rCmd == u"chapterup";
3887 const bool bUpDown = bUp || rCmd == u"chapterdown";
3888 const bool bLeft = rCmd == u"promote";
3889 const bool bLeftRight = bLeft || rCmd == u"demote";
3890 if (!bUpDown && !bLeftRight)
3891 return;
3892 if (GetWrtShell()->GetView().GetDocShell()->IsReadOnly() ||
3893 (State::ACTIVE != m_eState &&
3894 (State::CONSTANT != m_eState || m_pActiveShell != GetParentWindow()->GetCreateView()->GetWrtShellPtr())))
3896 return;
3899 SwWrtShell *const pShell = GetWrtShell();
3901 const SwNodes& rNodes = pShell->GetNodes();
3902 const SwOutlineNodes& rOutlineNodes = rNodes.GetOutLineNds();
3903 const SwOutlineNodes::size_type nOutlineNdsSize = rOutlineNodes.size();
3905 std::vector<SwTextNode*> selectedOutlineNodes;
3906 std::vector<std::unique_ptr<weld::TreeIter>> selected;
3908 m_xTreeView->selected_foreach([&](weld::TreeIter& rEntry){
3909 // it's possible to select the root node too which is a really bad idea
3910 if (lcl_IsContentType(rEntry, *m_xTreeView))
3911 return false;
3912 // filter out children of selected parents so they don't get promoted
3913 // or moved twice (except if there is Ctrl modifier, since in that
3914 // case children are re-parented)
3915 if ((bLeftRight || bOutlineWithChildren) && !selected.empty())
3917 std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rEntry));
3918 for (bool bParent = m_xTreeView->iter_parent(*xParent); bParent; bParent = m_xTreeView->iter_parent(*xParent))
3920 if (m_xTreeView->iter_compare(*selected.back(), *xParent) == 0)
3922 return false;
3926 selected.emplace_back(m_xTreeView->make_iterator(&rEntry));
3928 // Use the outline node position in the SwOutlineNodes array. Bad things
3929 // happen if the tree entry position is used and it doesn't match the node position
3930 // in SwOutlineNodes, which is usually the case for outline nodes in frames.
3931 const SwOutlineNodes::size_type nPos
3932 = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rEntry))->GetOutlinePos();
3933 if (nPos < nOutlineNdsSize)
3935 SwNode* pNode = rNodes.GetOutLineNds()[ nPos ];
3936 if (pNode)
3938 selectedOutlineNodes.push_back(pNode->GetTextNode());
3941 return false;
3944 if (!selected.size())
3945 return;
3947 if (bUpDown && !bUp)
3948 { // to move down, start at the end!
3949 std::reverse(selected.begin(), selected.end());
3952 m_bIgnoreDocChange = true;
3954 SwOutlineNodes::size_type nActPos;
3955 bool bStartedAction = false;
3957 MakeAllOutlineContentTemporarilyVisible a(GetWrtShell()->GetDoc());
3959 // get first regular document content node outline node position in outline nodes array
3960 SwOutlineNodes::size_type nFirstRegularDocContentOutlineNodePos = SwOutlineNodes::npos;
3961 SwNodeOffset nEndOfExtrasIndex = rNodes.GetEndOfExtras().GetIndex();
3962 sal_Int32 nHasInlineHeading = 0;
3963 for (SwOutlineNodes::size_type nPos = 0; nPos < nOutlineNdsSize; nPos++)
3965 if (rOutlineNodes[nPos]->GetIndex() > nEndOfExtrasIndex)
3967 nFirstRegularDocContentOutlineNodePos = nPos;
3968 break;
3970 if ( bUpDown && rOutlineNodes[nPos] != SwOutlineNodes::GetRootNode(rOutlineNodes[nPos]) )
3971 ++nHasInlineHeading;
3974 for (auto const& pCurrentEntry : selected)
3976 nActPos = weld::fromId<SwOutlineContent*>(
3977 m_xTreeView->get_id(*pCurrentEntry))->GetOutlinePos();
3979 // outline nodes in frames and tables are not up/down moveable
3980 if (nActPos == SwOutlineNodes::npos ||
3981 (bUpDown && (!pShell->IsOutlineMovable(nActPos) ||
3982 nFirstRegularDocContentOutlineNodePos == SwOutlineNodes::npos)))
3984 // except inline headings, i.e. Inline Heading frames with
3985 // single outlines, and anchored as characters, which headings
3986 // are movable with their anchor node, if they are
3987 // 1) not in other frames or 2) not in tables and 3) not protected
3988 const SwNode* pRootNode = nHasInlineHeading > 0
3989 ? SwOutlineNodes::GetRootNode(rOutlineNodes[nActPos])
3990 : nullptr;
3991 if ( !pRootNode || pRootNode == rOutlineNodes[nActPos] ||
3992 pRootNode != SwOutlineNodes::GetRootNode(pRootNode) || // frame in frame
3993 pRootNode->FindTableNode() || // frame in table
3994 pRootNode->IsProtect() ) // write protection
3996 continue;
4000 if (!bStartedAction)
4002 pShell->StartAllAction();
4003 pShell->StartUndo(bLeftRight ? SwUndoId::OUTLINE_LR : SwUndoId::OUTLINE_UD);
4004 bStartedAction = true;
4007 pShell->GotoOutline( nActPos); // If text selection != box selection
4008 pShell->Push();
4010 if (nHasInlineHeading && bUpDown)
4012 SwOutlineNodesInline aOutlineNodesInline;
4013 // sort inline headings correctly
4014 for (SwNode* pNode : rOutlineNodes)
4015 aOutlineNodesInline.insert(pNode);
4016 const SwOutlineNodes::size_type nOutlineNdsSizeInline = aOutlineNodesInline.size();
4018 // move outline position up/down (outline position promote/demote)
4019 SwOutlineNodes::difference_type nDir = bUp ? -1 : 1;
4020 SwOutlineNodesInline::size_type nActPosInline;
4021 aOutlineNodesInline.Seek_Entry(rOutlineNodes[nActPos], &nActPosInline);
4022 if ( (nDir == -1 && nActPosInline > 0) ||
4023 (nDir == 1 && nActPosInline < nOutlineNdsSizeInline - 1) )
4025 // make outline selection for use by MoveOutlinePara
4026 pShell->MakeOutlineSel(nActPos, nActPos, bOutlineWithChildren, true, &aOutlineNodesInline);
4028 int nActPosOutlineLevel =
4029 rOutlineNodes[nActPos]->GetTextNode()->GetAttrOutlineLevel();
4030 // search for sorted position
4031 SwOutlineNodesInline::size_type nPos;
4032 aOutlineNodesInline.Seek_Entry_By_Anchor(SwOutlineNodes::GetRootNode(rOutlineNodes[nActPos]), &nPos);
4033 if (!bUp)
4035 // move down
4036 int nPosOutlineLevel = -1;
4037 while (++nPos < nOutlineNdsSizeInline)
4039 nPosOutlineLevel = aOutlineNodesInline[nPos]->GetTextNode()->GetAttrOutlineLevel();
4041 // discontinue if moving out of parent or equal level is found
4042 if (nPosOutlineLevel <= nActPosOutlineLevel)
4044 break;
4046 // count the children of the node when they are not included in the move
4047 if (!bOutlineWithChildren)
4048 nDir++;
4050 if (nPosOutlineLevel >= nActPosOutlineLevel)
4052 // move past children
4053 while (++nPos < nOutlineNdsSizeInline)
4055 nPosOutlineLevel = aOutlineNodesInline[nPos]->GetTextNode()->GetAttrOutlineLevel();
4056 // discontinue if moving out of parent or equal level is found
4057 if (nPosOutlineLevel <= nActPosOutlineLevel)
4058 break;
4059 nDir++;
4063 else
4065 // move up
4066 while (nPos && --nPos >= nFirstRegularDocContentOutlineNodePos - nHasInlineHeading)
4068 int nPosOutlineLevel =
4069 aOutlineNodesInline[nPos]->GetTextNode()->GetAttrOutlineLevel();
4070 // discontinue if equal level is found
4071 if (nPosOutlineLevel == nActPosOutlineLevel)
4073 break;
4075 // discontinue if moving out of parent
4076 if (nPosOutlineLevel < nActPosOutlineLevel)
4078 // Required for expected chapter placement when the chapter being moved
4079 // up has an outline level less than the outline level of chapters it
4080 // is being moved above and then encounters a chapter with an outline
4081 // level that is greater before reaching a chapter with the same
4082 // outline level as itself.
4083 if (nDir < -1)
4084 nDir++;
4085 break;
4087 nDir--;
4090 pShell->MoveOutlinePara(nDir, &aOutlineNodesInline);
4092 pShell->ClearMark();
4094 else if (bUpDown)
4096 // move outline position up/down (outline position promote/demote)
4097 SwOutlineNodes::difference_type nDir = bUp ? -1 : 1;
4098 if ((nDir == -1 && nActPos > 0) || (nDir == 1 && nActPos < nOutlineNdsSize - 1))
4100 // make outline selection for use by MoveOutlinePara
4101 pShell->MakeOutlineSel(nActPos, nActPos, bOutlineWithChildren);
4103 int nActPosOutlineLevel =
4104 rOutlineNodes[nActPos]->GetTextNode()->GetAttrOutlineLevel();
4105 SwOutlineNodes::size_type nPos = nActPos;
4106 if (!bUp)
4108 // move down
4109 int nPosOutlineLevel = -1;
4110 while (++nPos < nOutlineNdsSize)
4112 nPosOutlineLevel =
4113 rOutlineNodes[nPos]->GetTextNode()->GetAttrOutlineLevel();
4114 // discontinue if moving out of parent or equal level is found
4115 if (nPosOutlineLevel <= nActPosOutlineLevel)
4116 break;
4117 // count the children of the node when they are not included in the move
4118 if (!bOutlineWithChildren)
4119 nDir++;
4121 if (nPosOutlineLevel >= nActPosOutlineLevel)
4123 // move past children
4124 while (++nPos < nOutlineNdsSize)
4126 nPosOutlineLevel =
4127 rOutlineNodes[nPos]->GetTextNode()->GetAttrOutlineLevel();
4128 // discontinue if moving out of parent or equal level is found
4129 if (nPosOutlineLevel <= nActPosOutlineLevel)
4130 break;
4131 nDir++;
4135 else
4137 // move up
4138 while (nPos && --nPos >= nFirstRegularDocContentOutlineNodePos)
4140 int nPosOutlineLevel =
4141 rOutlineNodes[nPos]->GetTextNode()->GetAttrOutlineLevel();
4142 // discontinue if equal level is found
4143 if (nPosOutlineLevel == nActPosOutlineLevel)
4144 break;
4145 // discontinue if moving out of parent
4146 if (nPosOutlineLevel < nActPosOutlineLevel)
4148 // Required for expected chapter placement when the chapter being moved
4149 // up has an outline level less than the outline level of chapters it
4150 // is being moved above and then encounters a chapter with an outline
4151 // level that is greater before reaching a chapter with the same
4152 // outline level as itself.
4153 if (nDir < -1)
4154 nDir++;
4155 break;
4157 nDir--;
4160 pShell->MoveOutlinePara(nDir);
4162 pShell->ClearMark();
4164 else
4166 // move outline left/right (outline level promote/demote)
4167 if (!pShell->IsProtectedOutlinePara())
4169 bool bAllow = true;
4170 const SwOutlineNodes& rOutlNds = pShell->GetDoc()->GetNodes().GetOutLineNds();
4171 const int nActLevel = rOutlNds[nActPos]->GetTextNode()->GetAttrOutlineLevel();
4172 if (!bLeft)
4174 // disallow if any outline node to demote will exceed MAXLEVEL
4175 SwOutlineNodes::size_type nPos = nActPos;
4178 int nLevel = rOutlNds[nPos]->GetTextNode()->GetAttrOutlineLevel();
4179 if (nLevel == MAXLEVEL)
4181 bAllow = false;
4182 break;
4184 } while (bOutlineWithChildren && ++nPos < rOutlNds.size() &&
4185 rOutlNds[nPos]->GetTextNode()->GetAttrOutlineLevel() > nActLevel);
4187 else
4189 // disallow if trying to promote outline of level 1
4190 if (nActLevel == 1)
4191 bAllow = false;
4193 if (bAllow)
4195 SwOutlineNodes::size_type nPos = nActPos;
4198 pShell->SwCursorShell::GotoOutline(nPos);
4199 pShell->OutlineUpDown(bLeft ? -1 : 1);
4200 } while (bOutlineWithChildren && ++nPos < rOutlNds.size() &&
4201 rOutlNds[nPos]->GetTextNode()->GetAttrOutlineLevel() > nActLevel);
4206 pShell->Pop(SwCursorShell::PopMode::DeleteCurrent); // Cursor is now back at the current heading.
4209 if (bStartedAction)
4211 pShell->EndUndo();
4212 pShell->EndAllAction();
4213 if (m_aActiveContentArr[ContentTypeId::OUTLINE])
4214 m_aActiveContentArr[ContentTypeId::OUTLINE]->Invalidate();
4216 // tdf#143547 LO Writer: navigator should stand still on promoting and demoting
4217 // In addition to m_bIgnoreDocChange being true, selections are cleared before the Display
4218 // call. Either of these conditions disable restore of scroll position happening in the
4219 // Display function so it needs to be done here.
4220 auto nOldScrollPos = m_xTreeView->vadjustment_get_value();
4222 // clear all selections to prevent the Display function from trying to reselect selected entries
4223 m_xTreeView->unselect_all();
4224 Display(true);
4225 m_xTreeView->vadjustment_set_value(nOldScrollPos);
4227 if (m_bIsRoot)
4229 // reselect entries, do this only when in outline content navigation mode
4230 const SwOutlineNodes& rOutlineNds = pShell->GetNodes().GetOutLineNds();
4231 for (SwTextNode* pNode : selectedOutlineNodes)
4233 m_xTreeView->all_foreach([this, &rOutlineNds, pNode](weld::TreeIter& rEntry){
4234 if (lcl_IsContentType(rEntry, *m_xTreeView))
4235 return false;
4236 SwOutlineNodes::size_type nPos = weld::fromId<SwOutlineContent*>(
4237 m_xTreeView->get_id(rEntry))->GetOutlinePos();
4238 if (pNode == rOutlineNds[nPos]->GetTextNode())
4240 std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rEntry));
4241 if (m_xTreeView->iter_parent(*xParent)
4242 && !m_xTreeView->get_row_expanded(*xParent))
4244 m_xTreeView->expand_row(*xParent);
4246 m_xTreeView->select(rEntry);
4247 return true;
4249 return false;
4252 UpdateContentFunctionsToolbar();
4254 else
4256 m_pActiveShell->GetView().GetEditWin().GrabFocus();
4257 m_bIgnoreDocChange = false;
4258 UpdateTracking();
4259 grab_focus();
4262 m_bIgnoreDocChange = false;
4265 void SwContentTree::ShowTree()
4267 m_xTreeView->show();
4268 m_aUpdTimer.Start();
4271 void SwContentTree::HideTree()
4273 // folded together will not be idled
4274 m_aUpdTimer.Stop();
4275 m_xTreeView->hide();
4278 static void lcl_SelectByContentTypeAndAddress(SwContentTree* pThis, weld::TreeView& rContentTree,
4279 ContentTypeId nType, const void* ptr)
4281 if (!ptr)
4283 rContentTree.set_cursor(-1);
4284 pThis->UpdateContentFunctionsToolbar();
4285 return;
4288 // find content type entry
4289 std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator());
4291 bool bFoundEntry = rContentTree.get_iter_first(*xIter);
4292 while (bFoundEntry)
4294 void* pUserData = weld::fromId<void*>(rContentTree.get_id(*xIter));
4295 assert(dynamic_cast<SwContentType*>(static_cast<SwTypeNumber*>(pUserData)));
4296 if (nType == static_cast<SwContentType*>(pUserData)->GetType())
4297 break;
4298 bFoundEntry = rContentTree.iter_next_sibling(*xIter);
4301 if (!bFoundEntry)
4303 rContentTree.set_cursor(-1);
4304 pThis->UpdateContentFunctionsToolbar();
4305 return;
4308 // assure content type entry is expanded
4309 rContentTree.expand_row(*xIter);
4311 // find content type content entry and select it
4312 const void* p = nullptr;
4313 while (rContentTree.iter_next(*xIter) && lcl_IsContent(*xIter, rContentTree))
4315 void* pUserData = weld::fromId<void*>(rContentTree.get_id(*xIter));
4316 switch( nType )
4318 case ContentTypeId::FOOTNOTE:
4319 case ContentTypeId::ENDNOTE:
4321 assert(dynamic_cast<SwTextFootnoteContent*>(static_cast<SwTypeNumber*>(pUserData)));
4322 SwTextFootnoteContent* pCnt = static_cast<SwTextFootnoteContent*>(pUserData);
4323 p = pCnt->GetTextFootnote();
4324 break;
4326 case ContentTypeId::URLFIELD:
4328 assert(dynamic_cast<SwURLFieldContent*>(static_cast<SwTypeNumber*>(pUserData)));
4329 SwURLFieldContent* pCnt = static_cast<SwURLFieldContent*>(pUserData);
4330 p = static_cast<const SwTextAttr*>(pCnt->GetINetAttr());
4331 break;
4333 case ContentTypeId::TEXTFIELD:
4335 assert(dynamic_cast<SwTextFieldContent*>(static_cast<SwTypeNumber*>(pUserData)));
4336 SwTextFieldContent* pCnt = static_cast<SwTextFieldContent*>(pUserData);
4337 p = pCnt->GetFormatField()->GetField();
4338 break;
4340 case ContentTypeId::POSTIT:
4342 assert(dynamic_cast<SwPostItContent*>(static_cast<SwTypeNumber*>(pUserData)));
4343 SwPostItContent* pCnt = static_cast<SwPostItContent*>(pUserData);
4344 p = pCnt->GetPostIt()->GetField();
4345 break;
4347 default:
4348 break;
4350 if (ptr == p)
4352 // get first selected for comparison
4353 std::unique_ptr<weld::TreeIter> xFirstSelected(rContentTree.make_iterator());
4354 if (!rContentTree.get_selected(xFirstSelected.get()))
4355 xFirstSelected.reset();
4356 if (rContentTree.count_selected_rows() != 1 || !xFirstSelected ||
4357 rContentTree.iter_compare(*xIter, *xFirstSelected) != 0)
4359 // unselect all entries and make passed entry visible and selected
4360 rContentTree.set_cursor(*xIter);
4361 pThis->UpdateContentFunctionsToolbar();
4363 return;
4367 rContentTree.set_cursor(-1);
4368 pThis->UpdateContentFunctionsToolbar();
4369 return;
4372 static void lcl_SelectByContentTypeAndName(SwContentTree* pThis, weld::TreeView& rContentTree,
4373 std::u16string_view rContentTypeName, std::u16string_view rName)
4375 if (rName.empty())
4376 return;
4378 // find content type entry
4379 std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator());
4380 bool bFoundEntry = rContentTree.get_iter_first(*xIter);
4381 while (bFoundEntry && rContentTypeName != rContentTree.get_text(*xIter))
4382 bFoundEntry = rContentTree.iter_next_sibling(*xIter);
4383 // find content type content entry and select it
4384 if (!bFoundEntry)
4385 return;
4387 rContentTree.expand_row(*xIter); // assure content type entry is expanded
4388 while (rContentTree.iter_next(*xIter) && lcl_IsContent(*xIter, rContentTree))
4390 if (rName == rContentTree.get_text(*xIter))
4392 // get first selected for comparison
4393 std::unique_ptr<weld::TreeIter> xFirstSelected(rContentTree.make_iterator());
4394 if (!rContentTree.get_selected(xFirstSelected.get()))
4395 xFirstSelected.reset();
4396 if (rContentTree.count_selected_rows() != 1 || !xFirstSelected ||
4397 rContentTree.iter_compare(*xIter, *xFirstSelected) != 0)
4399 // unselect all entries and make passed entry visible and selected
4400 rContentTree.set_cursor(*xIter);
4401 pThis->UpdateContentFunctionsToolbar();
4403 break;
4408 static void lcl_SelectDrawObjectByName(SwContentTree* pThis, weld::TreeView& rContentTree,
4409 std::u16string_view rName)
4411 if (rName.empty())
4412 return;
4414 // find content type entry
4415 std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator());
4416 bool bFoundEntry = rContentTree.get_iter_first(*xIter);
4417 while (bFoundEntry && SwResId(STR_CONTENT_TYPE_DRAWOBJECT) != rContentTree.get_text(*xIter))
4418 bFoundEntry = rContentTree.iter_next_sibling(*xIter);
4419 // find content type content entry and select it
4420 if (bFoundEntry)
4422 rContentTree.expand_row(*xIter); // assure content type entry is expanded
4423 while (rContentTree.iter_next(*xIter) && lcl_IsContent(*xIter, rContentTree))
4425 if (rName == rContentTree.get_text(*xIter))
4427 if (!rContentTree.is_selected(*xIter))
4429 rContentTree.select(*xIter);
4430 rContentTree.scroll_to_row(*xIter);
4431 pThis->UpdateContentFunctionsToolbar();
4433 break;
4439 /** No idle with focus or while dragging */
4440 IMPL_LINK_NOARG(SwContentTree, TimerUpdate, Timer *, void)
4442 // No need to update if content tree is not visible
4443 if (!m_xTreeView->is_visible())
4444 return;
4446 // No update while focus is not in document.
4447 // No update while drag and drop.
4448 // Query view because the Navigator is cleared too late.
4449 SwView* pView = GetParentWindow()->GetCreateView();
4451 SwWrtShell* pActShell = pView ? pView->GetWrtShellPtr() : nullptr;
4452 if(pActShell && pActShell->GetWin() &&
4453 (pActShell->GetWin()->HasFocus() || m_bDocHasChanged || m_bViewHasChanged) &&
4454 !IsInDrag() && !pActShell->ActionPend())
4456 if (m_bDocHasChanged || m_bViewHasChanged)
4458 if (State::CONSTANT == m_eState && !lcl_FindShell(m_pActiveShell))
4460 SetActiveShell(pActShell);
4461 GetParentWindow()->UpdateListBox();
4463 if (State::ACTIVE == m_eState && pActShell != GetWrtShell())
4465 SetActiveShell(pActShell);
4467 else if (SolarMutexGuard aGuard;
4468 (State::ACTIVE == m_eState || (State::CONSTANT == m_eState && pActShell == GetWrtShell())) &&
4469 HasContentChanged())
4471 FindActiveTypeAndRemoveUserData();
4472 Display(true);
4475 UpdateTracking();
4476 m_bIsIdleClear = false;
4477 m_bDocHasChanged = false;
4478 m_bViewHasChanged = false;
4480 else if (!pView && State::ACTIVE == m_eState && !m_bIsIdleClear) // this block seems never to be entered
4482 if(m_pActiveShell)
4484 SetActiveShell(nullptr);
4486 clear();
4487 m_bIsIdleClear = true;
4491 void SwContentTree::UpdateTracking()
4493 if (State::HIDDEN == m_eState || !m_pActiveShell)
4494 return;
4496 // only when treeview or treeview context menu does not have focus
4497 if (m_xTreeView->has_focus() || m_xTreeView->has_child_focus())
4498 return;
4500 // m_bIgnoreDocChange is set on delete and outline visibility toggle
4501 if (m_bIgnoreDocChange)
4503 m_bIgnoreDocChange = false;
4504 return;
4507 // bTrack is used to disallow tracking after jumping to an outline until the outline position
4508 // that was jumped to is no longer the current outline position.
4509 bool bTrack = true;
4510 if (m_nLastGotoContentWasOutlinePos != SwOutlineNodes::npos)
4512 if (m_pActiveShell->GetOutlinePos() == m_nLastGotoContentWasOutlinePos)
4513 bTrack = false;
4514 else
4515 m_nLastGotoContentWasOutlinePos = SwOutlineNodes::npos;
4518 if (bTrack)
4520 // graphic, frame, and ole
4521 if (m_pActiveShell->GetSelectionType() &
4522 (SelectionType::Graphic | SelectionType::Frame | SelectionType::Ole))
4524 OUString aContentTypeName;
4525 if (m_pActiveShell->GetSelectionType() == SelectionType::Graphic &&
4526 !(m_bIsRoot && m_nRootType != ContentTypeId::GRAPHIC))
4528 if (!mTrackContentType[ContentTypeId::GRAPHIC]) return;
4529 aContentTypeName = SwResId(STR_CONTENT_TYPE_GRAPHIC);
4531 else if (m_pActiveShell->GetSelectionType() == SelectionType::Frame &&
4532 !(m_bIsRoot && m_nRootType != ContentTypeId::FRAME))
4534 if (!mTrackContentType[ContentTypeId::FRAME]) return;
4535 aContentTypeName = SwResId(STR_CONTENT_TYPE_FRAME);
4537 else if (m_pActiveShell->GetSelectionType() == SelectionType::Ole &&
4538 !(m_bIsRoot && m_nRootType != ContentTypeId::OLE))
4540 if (!mTrackContentType[ContentTypeId::OLE]) return;
4541 aContentTypeName = SwResId(STR_CONTENT_TYPE_OLE);
4543 if (!aContentTypeName.isEmpty())
4545 OUString aName(m_pActiveShell->GetFlyName());
4546 lcl_SelectByContentTypeAndName(this, *m_xTreeView, aContentTypeName, aName);
4547 return;
4550 // drawing
4551 if ((m_pActiveShell->GetSelectionType() & (SelectionType::DrawObject |
4552 SelectionType::DrawObjectEditMode |
4553 SelectionType::DbForm)) &&
4554 !(m_bIsRoot && m_nRootType != ContentTypeId::DRAWOBJECT))
4556 if (mTrackContentType[ContentTypeId::DRAWOBJECT])
4558 // Multiple selection is possible when in root content navigation view so unselect all
4559 // selected entries before reselecting. This causes a bit of an annoyance when the treeview
4560 // scroll bar is used and focus is in the document by causing the last selected entry to
4561 // scroll back into view.
4562 if (m_bIsRoot)
4563 m_xTreeView->unselect_all();
4564 SdrView* pSdrView = m_pActiveShell->GetDrawView();
4565 if (pSdrView)
4567 for (size_t nIdx(0); nIdx < pSdrView->GetMarkedObjectList().GetMarkCount(); nIdx++)
4569 SdrObject* pSelected = pSdrView->GetMarkedObjectList().GetMark(nIdx)->GetMarkedSdrObj();
4570 OUString aName(pSelected->GetName());
4571 if (!aName.isEmpty())
4572 lcl_SelectDrawObjectByName(this, *m_xTreeView, aName);
4575 else
4577 // clear treeview selections
4578 m_xTreeView->unselect_all();
4579 UpdateContentFunctionsToolbar();
4582 return;
4584 // footnotes and endnotes
4585 if (SwContentAtPos aContentAtPos(IsAttrAtPos::Ftn);
4586 m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos)
4587 && aContentAtPos.pFndTextAttr &&
4588 !(m_bIsRoot && (m_nRootType != ContentTypeId::FOOTNOTE &&
4589 m_nRootType != ContentTypeId::ENDNOTE)))
4591 if (!aContentAtPos.pFndTextAttr->GetFootnote().IsEndNote())
4593 if (mTrackContentType[ContentTypeId::FOOTNOTE])
4594 lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, ContentTypeId::FOOTNOTE,
4595 aContentAtPos.pFndTextAttr);
4597 else if (mTrackContentType[ContentTypeId::ENDNOTE])
4598 lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, ContentTypeId::ENDNOTE,
4599 aContentAtPos.pFndTextAttr);
4600 return;
4602 // references
4603 if (SwContentAtPos aContentAtPos(IsAttrAtPos::RefMark);
4604 m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos) &&
4605 aContentAtPos.pFndTextAttr &&
4606 !(m_bIsRoot && m_nRootType != ContentTypeId::REFERENCE))
4608 if (mTrackContentType[ContentTypeId::REFERENCE])
4610 const SwFormatRefMark& rRefMark = aContentAtPos.pFndTextAttr->GetRefMark();
4611 lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_REFERENCE),
4612 rRefMark.GetRefName());
4614 return;
4616 // hyperlinks
4617 // not in ToxContent tdf#148312
4618 if (const SwSection* pSection = m_pActiveShell->GetCurrSection(); !pSection
4619 || (pSection && pSection->GetType() != SectionType::ToxContent))
4621 if (SwContentAtPos aContentAtPos(IsAttrAtPos::InetAttr);
4622 m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos)
4623 && (!m_bIsRoot || m_nRootType == ContentTypeId::URLFIELD))
4625 // Because hyperlink item names do not need to be unique, finding the corresponding
4626 // item in the tree by name may result in incorrect selection. Find the item in the
4627 // tree by comparing the SwTextINetFormat pointer at the document cursor position to
4628 // that stored in the item SwURLFieldContent.
4629 if (mTrackContentType[ContentTypeId::URLFIELD])
4630 lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, ContentTypeId::URLFIELD,
4631 aContentAtPos.pFndTextAttr);
4632 return;
4635 // fields, comments
4636 if (SwField* pField = m_pActiveShell->GetCurField(); pField &&
4637 !(m_bIsRoot &&
4638 m_nRootType != ContentTypeId::TEXTFIELD &&
4639 m_nRootType != ContentTypeId::POSTIT))
4641 ContentTypeId eCntTypeId =
4642 pField->GetTypeId() == SwFieldTypesEnum::Postit ? ContentTypeId::POSTIT :
4643 ContentTypeId::TEXTFIELD;
4644 if (mTrackContentType[eCntTypeId])
4645 lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, eCntTypeId, pField);
4646 return;
4648 if (SwPostItMgr* pPostItMgr = m_pActiveShell->GetPostItMgr();
4649 pPostItMgr && pPostItMgr->HasActiveAnnotationWin()
4650 && !(m_bIsRoot && m_nRootType != ContentTypeId::POSTIT))
4652 if (mTrackContentType[ContentTypeId::POSTIT])
4654 if (const SwField* pField = pPostItMgr->GetActiveSidebarWin()->GetPostItField())
4655 lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, ContentTypeId::POSTIT,
4656 pField);
4658 return;
4660 // table
4661 if (m_pActiveShell->IsCursorInTable() &&
4662 !(m_bIsRoot && m_nRootType != ContentTypeId::TABLE))
4664 if (mTrackContentType[ContentTypeId::TABLE] && m_pActiveShell->GetTableFormat())
4666 OUString aName = m_pActiveShell->GetTableFormat()->GetName();
4667 lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_TABLE),
4668 aName);
4669 return;
4672 // indexes
4673 if (const SwTOXBase* pTOX = m_pActiveShell->GetCurTOX(); pTOX &&
4674 !(m_bIsRoot && m_nRootType != ContentTypeId::INDEX))
4676 if (mTrackContentType[ContentTypeId::INDEX])
4677 lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_INDEX),
4678 pTOX->GetTOXName());
4679 return;
4681 // section
4682 if (const SwSection* pSection = m_pActiveShell->GetCurrSection(); pSection &&
4683 !(m_bIsRoot && m_nRootType != ContentTypeId::REGION))
4685 if (mTrackContentType[ContentTypeId::REGION])
4687 lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_REGION),
4688 pSection->GetSectionName());
4689 return;
4691 else
4693 // prevent fall through to outline tracking when section tracking is off and the last
4694 // GotoContent is the current section
4695 if (m_nLastSelType == ContentTypeId::REGION &&
4696 m_xTreeView->get_selected_text() == pSection->GetSectionName())
4697 return;
4699 // fall through to outline tracking when section tracking is off and the last GotoContent
4700 // is not the current section
4703 // find out where the cursor is
4704 const SwOutlineNodes::size_type nActPos = GetWrtShell()->GetOutlinePos(MAXLEVEL);
4705 if (m_nOutlineTracking != 3
4706 && !((m_bIsRoot && m_nRootType != ContentTypeId::OUTLINE)
4707 || nActPos == SwOutlineNodes::npos))
4709 // assure outline content type is expanded
4710 // this assumes outline content type is first in treeview
4711 std::unique_ptr<weld::TreeIter> xFirstEntry(m_xTreeView->make_iterator());
4712 if (m_xTreeView->get_iter_first(*xFirstEntry))
4713 m_xTreeView->expand_row(*xFirstEntry);
4715 m_xTreeView->all_foreach([this, nActPos](weld::TreeIter& rEntry){
4716 bool bRet = false;
4717 if (lcl_IsContent(rEntry, *m_xTreeView) && weld::fromId<SwContent*>(
4718 m_xTreeView->get_id(rEntry))->GetParent()->GetType() ==
4719 ContentTypeId::OUTLINE)
4721 if (weld::fromId<SwOutlineContent*>(
4722 m_xTreeView->get_id(rEntry))->GetOutlinePos() == nActPos)
4724 std::unique_ptr<weld::TreeIter> xFirstSelected(
4725 m_xTreeView->make_iterator());
4726 if (!m_xTreeView->get_selected(xFirstSelected.get()))
4727 xFirstSelected.reset();
4728 // only select if not already selected or tree has multiple entries selected
4729 if (m_xTreeView->count_selected_rows() != 1 || !xFirstSelected ||
4730 m_xTreeView->iter_compare(rEntry, *xFirstSelected) != 0)
4732 if (m_nOutlineTracking == 2) // focused outline tracking
4734 // collapse to children of root node
4735 std::unique_ptr<weld::TreeIter> xChildEntry(
4736 m_xTreeView->make_iterator());
4737 if (m_xTreeView->get_iter_first(*xChildEntry) &&
4738 m_xTreeView->iter_children(*xChildEntry))
4742 if (weld::fromId<SwContent*>(
4743 m_xTreeView->get_id(*xChildEntry))->
4744 GetParent()->GetType() == ContentTypeId::OUTLINE)
4745 m_xTreeView->collapse_row(*xChildEntry);
4746 else
4747 break;
4749 while (m_xTreeView->iter_next(*xChildEntry));
4752 // unselect all entries, make pEntry visible, and select
4753 m_xTreeView->set_cursor(rEntry);
4754 UpdateContentFunctionsToolbar();
4756 // tdf#149279 show at least two outline entries before the set cursor entry
4757 std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator(&rEntry));
4758 for (int i = 0; i < 2; i++)
4760 if (m_xTreeView->get_iter_depth(*xIter) == 0)
4761 break;
4762 if (!m_xTreeView->iter_previous(*xIter))
4763 break;
4764 while (!weld::IsEntryVisible(*m_xTreeView, *xIter))
4765 m_xTreeView->iter_parent(*xIter);
4767 // Assure the scroll to row is collapsed after scrolling if it was collapsed
4768 // before. This is required here to make gtkinst scroll_to_row behave like
4769 // salinst.
4770 const bool bRowExpanded = m_xTreeView->get_row_expanded(*xIter);
4771 m_xTreeView->scroll_to_row(*xIter);
4772 if (!bRowExpanded)
4773 m_xTreeView->collapse_row(*xIter);
4775 bRet = true;
4778 else
4780 // use of this break assumes outline content type is first in tree
4781 if (lcl_IsContentType(rEntry, *m_xTreeView) &&
4782 weld::fromId<SwContentType*>(
4783 m_xTreeView->get_id(rEntry))->GetType() !=
4784 ContentTypeId::OUTLINE)
4785 bRet = true;
4787 return bRet;
4789 return;
4792 // bookmarks - track first bookmark at cursor
4793 // tdf#159428 Only when no outline found. Showing the outline is more important than
4794 // showing a bookmark at the cursor position.
4795 if (mTrackContentType[ContentTypeId::BOOKMARK] &&
4796 (m_pActiveShell->GetSelectionType() & SelectionType::Text))
4798 SwPaM* pCursor = m_pActiveShell->GetCursor();
4799 IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess();
4800 auto ppBookmark = pMarkAccess->getBookmarksBegin();
4801 if (pCursor && ppBookmark != pMarkAccess->getBookmarksEnd()
4802 && !(m_bIsRoot && m_nRootType != ContentTypeId::BOOKMARK))
4804 OUString sBookmarkName;
4805 SwPosition* pCursorPoint = pCursor->GetPoint();
4806 while (ppBookmark != pMarkAccess->getBookmarksEnd())
4808 if (lcl_IsUiVisibleBookmark(*ppBookmark)
4809 && *pCursorPoint >= (*ppBookmark)->GetMarkStart()
4810 && *pCursorPoint <= (*ppBookmark)->GetMarkEnd())
4812 sBookmarkName = (*ppBookmark)->GetName();
4813 // keep previously selected bookmark instead
4814 // of selecting a different bookmark inside of it
4815 if (sBookmarkName == m_sSelectedItem)
4816 break;
4818 else if (!sBookmarkName.isEmpty() && *pCursorPoint < (*ppBookmark)->GetMarkStart())
4820 // don't search a different bookmark inside the
4821 // previous one, if the starting position of the next bookmarks
4822 // is after the cursor position (assuming that the
4823 // bookmark iterator jumps inside the same text by positions)
4824 break;
4826 ++ppBookmark;
4829 if (!sBookmarkName.isEmpty())
4831 // select the bookmark
4832 lcl_SelectByContentTypeAndName(this, *m_xTreeView,
4833 SwResId(STR_CONTENT_TYPE_BOOKMARK), sBookmarkName);
4834 return;
4839 // clear treeview selections
4840 if (m_xTreeView->count_selected_rows() > 0)
4842 m_xTreeView->unselect_all();
4843 m_xTreeView->set_cursor(-1);
4844 UpdateContentFunctionsToolbar();
4848 static bool lcl_IsSelectedCompareByContentTypeAndAddress(const weld::TreeIter& rEntry,
4849 weld::TreeView& rContentTree,
4850 ContentTypeId eContentType,
4851 const void* ptr)
4853 if (!ptr)
4854 return false;
4856 std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator());
4858 // find content type entry
4859 bool bFoundEntry = rContentTree.get_iter_first(*xIter);
4860 while (bFoundEntry)
4862 assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(rContentTree.get_id(*xIter))));
4863 SwContentType* pContentType = weld::fromId<SwContentType*>(rContentTree.get_id(*xIter));
4864 if (eContentType == pContentType->GetType())
4865 break;
4866 bFoundEntry = rContentTree.iter_next_sibling(*xIter);
4869 if (!bFoundEntry)
4870 return false;
4872 // find content type content entry and compare it to the passed entry
4873 const void* p = nullptr;
4874 while (rContentTree.iter_next(*xIter) && lcl_IsContent(*xIter, rContentTree))
4876 assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(rContentTree.get_id(*xIter))));
4877 SwContent* pContent = weld::fromId<SwContent*>(rContentTree.get_id(*xIter));
4878 switch (eContentType)
4880 case ContentTypeId::FOOTNOTE:
4881 case ContentTypeId::ENDNOTE:
4883 assert(dynamic_cast<SwTextFootnoteContent*>(static_cast<SwTypeNumber*>(pContent)));
4884 SwTextFootnoteContent* pCnt = static_cast<SwTextFootnoteContent*>(pContent);
4885 p = pCnt->GetTextFootnote();
4886 break;
4888 case ContentTypeId::URLFIELD:
4890 assert(dynamic_cast<SwURLFieldContent*>(static_cast<SwTypeNumber*>(pContent)));
4891 SwURLFieldContent* pCnt = static_cast<SwURLFieldContent*>(pContent);
4892 p = static_cast<const SwTextAttr*>(pCnt->GetINetAttr());
4893 break;
4895 case ContentTypeId::TEXTFIELD:
4897 assert(dynamic_cast<SwTextFieldContent*>(static_cast<SwTypeNumber*>(pContent)));
4898 SwTextFieldContent* pCnt = static_cast<SwTextFieldContent*>(pContent);
4899 p = pCnt->GetFormatField()->GetField();
4900 break;
4902 case ContentTypeId::POSTIT:
4904 assert(dynamic_cast<SwPostItContent*>(static_cast<SwTypeNumber*>(pContent)));
4905 SwPostItContent* pCnt = static_cast<SwPostItContent*>(pContent);
4906 p = pCnt->GetPostIt()->GetField();
4907 break;
4909 case ContentTypeId::INDEX:
4911 assert(dynamic_cast<SwTOXBaseContent*>(static_cast<SwTypeNumber*>(pContent)));
4912 SwTOXBaseContent* pCnt = static_cast<SwTOXBaseContent*>(pContent);
4913 p = pCnt->GetTOXBase();
4914 break;
4916 default:
4917 break;
4919 if (ptr == p)
4920 return rContentTree.iter_compare(*xIter, rEntry) == 0;
4922 return false;
4925 static bool lcl_IsSelectedCompareByContentTypeAndName(const weld::TreeIter& rEntry,
4926 weld::TreeView& rContentTree,
4927 ContentTypeId eContentType,
4928 std::u16string_view rName)
4930 std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator());
4932 // find content type entry
4933 bool bFoundEntry = rContentTree.get_iter_first(*xIter);
4934 while (bFoundEntry)
4936 assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(rContentTree.get_id(*xIter))));
4937 SwContentType* pContentType = weld::fromId<SwContentType*>(rContentTree.get_id(*xIter));
4938 if (eContentType == pContentType->GetType())
4939 break;
4940 bFoundEntry = rContentTree.iter_next_sibling(*xIter);
4943 if (!bFoundEntry)
4944 return false;
4946 // find content type content entry and compare it to the passed entry
4947 while (rContentTree.iter_next(*xIter))
4949 if (rName == rContentTree.get_text(*xIter))
4951 if (rContentTree.iter_compare(*xIter, rEntry) == 0)
4952 return true;
4955 return false;
4958 bool SwContentTree::IsSelectedEntryCurrentDocCursorPosition(const weld::TreeIter& rEntry)
4960 if (State::HIDDEN == m_eState || !m_pActiveShell)
4961 return false;
4963 // table
4964 if (m_pActiveShell->IsCursorInTable())
4966 return lcl_IsSelectedCompareByContentTypeAndName(
4967 rEntry, *m_xTreeView, ContentTypeId::TABLE,
4968 m_pActiveShell->GetTableFormat()->GetName());
4970 // graphic, frame, and ole
4971 if (m_pActiveShell->GetSelectionType()
4972 & (SelectionType::Graphic | SelectionType::Frame | SelectionType::Ole))
4974 ContentTypeId eContentTypeId;
4975 if (m_pActiveShell->GetSelectionType() == SelectionType::Graphic)
4976 eContentTypeId = ContentTypeId::GRAPHIC;
4977 else if (m_pActiveShell->GetSelectionType() == SelectionType::Frame)
4978 eContentTypeId = ContentTypeId::FRAME;
4979 else if (m_pActiveShell->GetSelectionType() == SelectionType::Ole)
4980 eContentTypeId = ContentTypeId::OLE;
4981 else // to quiet compiler warning/error
4982 return false;
4983 return lcl_IsSelectedCompareByContentTypeAndName(rEntry, *m_xTreeView, eContentTypeId,
4984 m_pActiveShell->GetFlyName());
4986 // hyperlinks
4987 // not in ToxContent tdf#148312 <- does this apply here?
4988 if (const SwSection* pSection = m_pActiveShell->GetCurrSection();
4989 !pSection || (pSection && pSection->GetType() != SectionType::ToxContent))
4991 if (SwContentAtPos aContentAtPos(IsAttrAtPos::InetAttr);
4992 m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos))
4994 // Because hyperlink item names do not need to be unique, finding the corresponding
4995 // item in the tree by name may result in incorrect selection. Find the item in the
4996 // tree by comparing the SwTextINetFormat pointer at the document cursor position to
4997 // that stored in the item SwURLFieldContent.
4998 return lcl_IsSelectedCompareByContentTypeAndAddress(
4999 rEntry, *m_xTreeView, ContentTypeId::URLFIELD, aContentAtPos.pFndTextAttr);
5002 // references
5003 if (SwContentAtPos aContentAtPos(IsAttrAtPos::RefMark);
5004 m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos)
5005 && aContentAtPos.pFndTextAttr)
5007 const SwFormatRefMark& rRefMark = aContentAtPos.pFndTextAttr->GetRefMark();
5008 return lcl_IsSelectedCompareByContentTypeAndName(
5009 rEntry, *m_xTreeView, ContentTypeId::REFERENCE, rRefMark.GetRefName());
5011 // indexes
5012 if (const SwTOXBase* pTOXBase = m_pActiveShell->GetCurTOX())
5014 return lcl_IsSelectedCompareByContentTypeAndAddress(rEntry, *m_xTreeView,
5015 ContentTypeId::INDEX, pTOXBase);
5016 // alternatively:
5017 // return lcl_IsSelectedCompareByContentTypeAndName(rEntry, *m_xTreeView,
5018 // ContentTypeId::INDEX,
5019 // pTOX->GetTOXName());
5021 // fields, comments
5022 if (SwField* pField = m_pActiveShell->GetCurField())
5024 ContentTypeId eContentTypeId = pField->GetTypeId() == SwFieldTypesEnum::Postit
5025 ? ContentTypeId::POSTIT
5026 : ContentTypeId::TEXTFIELD;
5027 return lcl_IsSelectedCompareByContentTypeAndAddress(rEntry, *m_xTreeView, eContentTypeId,
5028 pField);
5030 // drawing
5031 if (m_pActiveShell->GetSelectionType()
5032 & (SelectionType::DrawObject | SelectionType::DrawObjectEditMode | SelectionType::DbForm))
5034 SdrView* pSdrView = m_pActiveShell->GetDrawView();
5035 if (!pSdrView)
5036 return false;
5037 // ONLY CHECKS FIRST MARKED OBJECT
5038 for (size_t nIdx = 0; nIdx < pSdrView->GetMarkedObjectList().GetMarkCount(); nIdx++)
5040 SdrObject* pObject = pSdrView->GetMarkedObjectList().GetMark(nIdx)->GetMarkedSdrObj();
5041 if (lcl_IsSelectedCompareByContentTypeAndName(
5042 rEntry, *m_xTreeView, ContentTypeId::DRAWOBJECT, pObject->GetName()))
5043 return true;
5045 return false;
5047 // footnotes and endnotes
5048 if (SwContentAtPos aContentAtPos(IsAttrAtPos::Ftn);
5049 m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos)
5050 && aContentAtPos.pFndTextAttr)
5052 ContentTypeId eContentTypeId = aContentAtPos.pFndTextAttr->GetFootnote().IsEndNote()
5053 ? ContentTypeId::ENDNOTE
5054 : ContentTypeId::FOOTNOTE;
5055 return lcl_IsSelectedCompareByContentTypeAndAddress(rEntry, *m_xTreeView, eContentTypeId,
5056 aContentAtPos.pFndTextAttr);
5058 // section
5059 if (const SwSection* pSection = m_pActiveShell->GetCurrSection())
5061 return lcl_IsSelectedCompareByContentTypeAndName(
5062 rEntry, *m_xTreeView, ContentTypeId::REGION, pSection->GetSectionName());
5064 // bookmark (unsure about this)
5065 if (m_pActiveShell->GetSelectionType() & SelectionType::Text)
5067 SwPaM* pCursor = m_pActiveShell->GetCursor();
5068 IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess();
5069 auto ppBookmark = pMarkAccess->getBookmarksBegin();
5070 if (pCursor && ppBookmark != pMarkAccess->getBookmarksEnd())
5072 OUString sBookmarkName;
5073 SwPosition* pCursorPoint = pCursor->GetPoint();
5074 while (ppBookmark != pMarkAccess->getBookmarksEnd())
5076 if (lcl_IsUiVisibleBookmark(*ppBookmark)
5077 && *pCursorPoint >= (*ppBookmark)->GetMarkStart()
5078 && *pCursorPoint <= (*ppBookmark)->GetMarkEnd())
5080 sBookmarkName = (*ppBookmark)->GetName();
5081 // keep previously selected bookmark instead
5082 // of selecting a different bookmark inside of it
5083 if (sBookmarkName == m_sSelectedItem)
5084 return lcl_IsSelectedCompareByContentTypeAndName(
5085 rEntry, *m_xTreeView, ContentTypeId::BOOKMARK, sBookmarkName);
5087 else if (!sBookmarkName.isEmpty() && *pCursorPoint < (*ppBookmark)->GetMarkStart())
5089 // don't search a different bookmark inside the
5090 // previous one, if the starting position of the next bookmarks
5091 // is after the cursor position (assuming that the
5092 // bookmark iterator jumps inside the same text by positions)
5093 return lcl_IsSelectedCompareByContentTypeAndName(
5094 rEntry, *m_xTreeView, ContentTypeId::BOOKMARK, sBookmarkName);
5096 ++ppBookmark;
5100 return false;
5103 void SwContentTree::SelectOutlinesWithSelection()
5105 SwCursor* pFirstCursor = m_pActiveShell->GetCursor();
5106 SwCursor* pCursor = pFirstCursor;
5107 std::vector<SwOutlineNodes::size_type> aOutlinePositions;
5110 if (pCursor)
5112 if (pCursor->HasMark())
5114 aOutlinePositions.push_back(m_pActiveShell->GetOutlinePos(UCHAR_MAX, pCursor));
5116 pCursor = pCursor->GetNext();
5118 } while (pCursor && pCursor != pFirstCursor);
5120 if (aOutlinePositions.empty())
5121 return;
5123 // remove duplicates before selecting
5124 aOutlinePositions.erase(std::unique(aOutlinePositions.begin(), aOutlinePositions.end()),
5125 aOutlinePositions.end());
5127 m_xTreeView->unselect_all();
5129 for (auto nOutlinePosition : aOutlinePositions)
5131 m_xTreeView->all_foreach([this, nOutlinePosition](const weld::TreeIter& rEntry){
5132 if (lcl_IsContent(rEntry, *m_xTreeView) &&
5133 weld::fromId<SwContent*>(
5134 m_xTreeView->get_id(rEntry))->GetParent()->GetType() ==
5135 ContentTypeId::OUTLINE)
5137 if (weld::fromId<SwOutlineContent*>(
5138 m_xTreeView->get_id(rEntry))->GetOutlinePos() ==
5139 nOutlinePosition)
5141 std::unique_ptr<weld::TreeIter> xParent =
5142 m_xTreeView->make_iterator(&rEntry);
5143 if (m_xTreeView->iter_parent(*xParent) &&
5144 !m_xTreeView->get_row_expanded(*xParent))
5145 m_xTreeView->expand_row(*xParent);
5146 m_xTreeView->select(rEntry);
5147 return true;
5150 return false;
5154 UpdateContentFunctionsToolbar();
5157 void SwContentTree::MoveOutline(SwOutlineNodes::size_type nTargetPos)
5159 MakeAllOutlineContentTemporarilyVisible a(GetWrtShell()->GetDoc());
5161 SwWrtShell *const pShell = GetWrtShell();
5162 pShell->StartAllAction();
5163 pShell->StartUndo(SwUndoId::OUTLINE_UD);
5165 SwOutlineNodes::size_type nPrevSourcePos = SwOutlineNodes::npos;
5166 SwOutlineNodes::size_type nPrevTargetPosOrOffset = SwOutlineNodes::npos;
5168 bool bFirstMove = true;
5170 for (const SwOutlineNodes::size_type& nPos : m_aDndOutlinesSelected)
5172 SwOutlineNodes::size_type nSourcePos = nPos;
5174 // Done on the first selection move
5175 if (bFirstMove) // only do once
5177 if (nTargetPos == SwOutlineNodes::npos || nSourcePos > nTargetPos)
5179 // Up moves
5180 // The first up move sets the up move amount for the remaining selected outlines to be moved
5181 if (nTargetPos != SwOutlineNodes::npos)
5182 nPrevTargetPosOrOffset = nSourcePos - nTargetPos;
5183 else
5184 nPrevTargetPosOrOffset = nSourcePos + 1;
5186 else if (nSourcePos < nTargetPos)
5188 // Down moves
5189 // The first down move sets the source and target positions for the remaining selected outlines to be moved
5190 nPrevSourcePos = nSourcePos;
5191 nPrevTargetPosOrOffset = nTargetPos;
5193 bFirstMove = false;
5195 else
5197 if (nTargetPos == SwOutlineNodes::npos || nSourcePos > nTargetPos)
5199 // Move up
5200 nTargetPos = nSourcePos - nPrevTargetPosOrOffset;
5202 else if (nSourcePos < nTargetPos)
5204 // Move down
5205 nSourcePos = nPrevSourcePos;
5206 nTargetPos = nPrevTargetPosOrOffset;
5209 GetParentWindow()->MoveOutline(nSourcePos, nTargetPos);
5212 pShell->EndUndo();
5213 pShell->EndAllAction();
5214 m_aActiveContentArr[ContentTypeId::OUTLINE]->Invalidate();
5215 Display(true);
5216 m_aDndOutlinesSelected.clear();
5219 // Update immediately
5220 IMPL_LINK_NOARG(SwContentTree, FocusInHdl, weld::Widget&, void)
5222 SwView* pActView = GetParentWindow()->GetCreateView();
5223 if(pActView)
5225 SwWrtShell* pActShell = pActView->GetWrtShellPtr();
5226 if (State::CONSTANT == m_eState && !lcl_FindShell(m_pActiveShell))
5228 SetActiveShell(pActShell);
5231 if (State::ACTIVE == m_eState && pActShell != GetWrtShell())
5232 SetActiveShell(pActShell);
5233 // Only call HasContentChanged() if the document has changed since last called
5234 else if ((State::ACTIVE == m_eState || (State::CONSTANT == m_eState && pActShell == GetWrtShell())) &&
5235 m_bDocHasChanged)
5237 if (HasContentChanged())
5238 Display(true);
5239 m_bDocHasChanged = false;
5242 else if (State::ACTIVE == m_eState)
5243 clear();
5246 IMPL_LINK(SwContentTree, KeyInputHdl, const KeyEvent&, rEvent, bool)
5248 bool bConsumed = true;
5250 const vcl::KeyCode aCode = rEvent.GetKeyCode();
5251 if (aCode.GetCode() == KEY_MULTIPLY && aCode.IsMod1())
5253 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
5254 if (m_xTreeView->get_selected(xEntry.get()))
5255 ExpandOrCollapseAll(*m_xTreeView, *xEntry);
5257 else if (aCode.GetCode() == KEY_RETURN)
5259 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
5260 if (m_xTreeView->get_selected(xEntry.get()))
5262 switch(aCode.GetModifier())
5264 case KEY_MOD2:
5265 // Switch boxes
5266 GetParentWindow()->ToggleTree();
5267 break;
5268 case KEY_MOD1:
5269 // Switch RootMode
5270 ToggleToRoot();
5271 break;
5272 case 0:
5273 if (lcl_IsContentType(*xEntry, *m_xTreeView))
5275 m_xTreeView->get_row_expanded(*xEntry) ? m_xTreeView->collapse_row(*xEntry)
5276 : m_xTreeView->expand_row(*xEntry);
5278 else
5279 ContentDoubleClickHdl(*m_xTreeView);
5280 break;
5281 case KEY_SHIFT:
5282 m_bSelectTo = true;
5283 ContentDoubleClickHdl(*m_xTreeView);
5284 break;
5288 else if(aCode.GetCode() == KEY_DELETE && 0 == aCode.GetModifier())
5290 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
5291 if (!m_pActiveShell->GetView().GetDocShell()->IsReadOnly()
5292 && m_xTreeView->get_selected(xEntry.get()))
5294 if (lcl_IsContent(*xEntry, *m_xTreeView))
5296 assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
5297 if (weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->GetParent()->IsDeletable())
5299 EditEntry(*xEntry, EditEntryMode::DELETE);
5302 else
5304 SwContentType* pContentType
5305 = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry));
5306 if (pContentType->GetMemberCount()
5307 && (pContentType->GetType() == ContentTypeId::FOOTNOTE
5308 || pContentType->GetType() == ContentTypeId::ENDNOTE))
5310 DeleteAllContentOfEntryContentType(*xEntry);
5315 //Make KEY_SPACE has same function as DoubleClick, and realize
5316 //multi-selection.
5317 else if (aCode.GetCode() == KEY_SPACE && 0 == aCode.GetModifier())
5319 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
5320 if (m_xTreeView->get_cursor(xEntry.get()))
5322 if (State::HIDDEN != m_eState)
5324 if (State::CONSTANT == m_eState)
5326 m_pActiveShell->GetView().GetViewFrame().GetWindow().ToTop();
5329 SwContent* pCnt = dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)));
5331 if (pCnt && pCnt->GetParent()->GetType() == ContentTypeId::DRAWOBJECT)
5333 SdrView* pDrawView = m_pActiveShell->GetDrawView();
5334 if (pDrawView)
5336 pDrawView->SdrEndTextEdit();
5338 SwDrawModel* pDrawModel = m_pActiveShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel();
5339 SdrPage* pPage = pDrawModel->GetPage(0);
5340 bool hasObjectMarked = false;
5342 if (SdrObject* pObject = GetDrawingObjectsByContent(pCnt))
5344 SdrPageView* pPV = pDrawView->GetSdrPageView/*GetPageViewPvNum*/(/*0*/);
5345 if( pPV )
5347 bool bUnMark = pDrawView->IsObjMarked(pObject);
5348 pDrawView->MarkObj( pObject, pPV, bUnMark);
5352 for (const rtl::Reference<SdrObject>& pTemp : *pPage)
5354 bool bMark = pDrawView->IsObjMarked(pTemp.get());
5355 switch( pTemp->GetObjIdentifier() )
5357 case SdrObjKind::Group:
5358 case SdrObjKind::Text:
5359 case SdrObjKind::Line:
5360 case SdrObjKind::Rectangle:
5361 case SdrObjKind::CircleOrEllipse:
5362 case SdrObjKind::CircleSection:
5363 case SdrObjKind::CircleArc:
5364 case SdrObjKind::CircleCut:
5365 case SdrObjKind::Polygon:
5366 case SdrObjKind::PolyLine:
5367 case SdrObjKind::PathLine:
5368 case SdrObjKind::PathFill:
5369 case SdrObjKind::FreehandLine:
5370 case SdrObjKind::FreehandFill:
5371 case SdrObjKind::PathPoly:
5372 case SdrObjKind::PathPolyLine:
5373 case SdrObjKind::Caption:
5374 case SdrObjKind::CustomShape:
5375 if( bMark )
5376 hasObjectMarked = true;
5377 break;
5378 default:
5379 if ( bMark )
5381 SdrPageView* pPV = pDrawView->GetSdrPageView/*GetPageViewPvNum*/(/*0*/);
5382 if (pPV)
5384 pDrawView->MarkObj(pTemp.get(), pPV, true);
5388 //mod end
5390 if ( !hasObjectMarked )
5392 SwEditWin& rEditWindow = m_pActiveShell->GetView().GetEditWin();
5393 vcl::KeyCode tempKeycode( KEY_ESCAPE );
5394 KeyEvent rKEvt( 0 , tempKeycode );
5395 static_cast<vcl::Window*>(&rEditWindow)->KeyInput( rKEvt );
5400 m_bViewHasChanged = true;
5404 else
5406 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
5407 if (m_xTreeView->get_cursor(xEntry.get()))
5409 SwContent* pCnt = dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)));
5410 if (pCnt && pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE)
5412 if (m_bIsRoot && aCode.GetCode() == KEY_LEFT && aCode.GetModifier() == 0)
5414 m_xTreeView->unselect_all();
5415 bConsumed = false;
5417 else if (aCode.IsMod1())
5419 if (aCode.GetCode() == KEY_LEFT)
5420 ExecCommand(u"promote", !aCode.IsShift());
5421 else if (aCode.GetCode() == KEY_RIGHT)
5422 ExecCommand(u"demote", !aCode.IsShift());
5423 else if (aCode.GetCode() == KEY_UP)
5424 ExecCommand(u"chapterup", !aCode.IsShift());
5425 else if (aCode.GetCode() == KEY_DOWN)
5426 ExecCommand(u"chapterdown", !aCode.IsShift());
5427 else if (aCode.GetCode() == KEY_C)
5428 CopyOutlineSelections();
5429 else
5430 bConsumed = false;
5432 else
5433 bConsumed = false;
5435 else
5436 bConsumed = false;
5438 else
5439 bConsumed = false;
5441 return bConsumed;
5444 IMPL_LINK(SwContentTree, QueryTooltipHdl, const weld::TreeIter&, rEntry, OUString)
5446 // Prevent tool tip handling when handling document change. The entry that was present when
5447 // the tooltip signal was fired might no longer be valid by the time it gets here. For example,
5448 // when the mouse pointer is on an entry in the tree that is deleted in the document by an
5449 // undo/redo. Please see similar note in MouseMoveHdl.
5450 if (m_bDocHasChanged)
5451 return OUString();
5453 ContentTypeId nType;
5454 bool bContent = false;
5455 void* pUserData = weld::fromId<void*>(m_xTreeView->get_id(rEntry));
5456 if (lcl_IsContentType(rEntry, *m_xTreeView))
5458 assert(dynamic_cast<SwContentType*>(static_cast<SwTypeNumber*>(pUserData)));
5459 nType = static_cast<SwContentType*>(pUserData)->GetType();
5461 else
5463 assert(dynamic_cast<SwContent*>(static_cast<SwTypeNumber*>(pUserData)));
5464 nType = static_cast<SwContent*>(pUserData)->GetParent()->GetType();
5465 bContent = true;
5467 OUString sEntry;
5468 if(bContent)
5470 switch( nType )
5472 case ContentTypeId::URLFIELD:
5473 assert(dynamic_cast<SwURLFieldContent*>(static_cast<SwTypeNumber*>(pUserData)));
5474 sEntry = static_cast<SwURLFieldContent*>(pUserData)->GetURL();
5475 break;
5477 case ContentTypeId::POSTIT:
5478 assert(dynamic_cast<SwPostItContent*>(static_cast<SwTypeNumber*>(pUserData)));
5479 sEntry = static_cast<SwPostItContent*>(pUserData)->GetName();
5480 break;
5481 case ContentTypeId::OUTLINE:
5483 assert(dynamic_cast<SwOutlineContent*>(static_cast<SwTypeNumber*>(pUserData)));
5484 SwOutlineContent* pOutlineContent = static_cast<SwOutlineContent*>(pUserData);
5485 SwOutlineNodes::size_type nOutlinePos = pOutlineContent->GetOutlinePos();
5486 const SwNodes& rNodes = m_pActiveShell->GetDoc()->GetNodes();
5487 const SwOutlineNodes& rOutlineNodes = rNodes.GetOutLineNds();
5488 SwNode* pStartNode = rOutlineNodes[nOutlinePos];
5489 SwNode* pEndNode = &rNodes.GetEndOfContent();
5490 if (nOutlinePos + 1 < rOutlineNodes.size())
5491 pEndNode = rOutlineNodes[nOutlinePos + 1];
5492 SwPaM aPaM(*pStartNode, *pEndNode);
5493 SwDocStat aDocStat;
5494 SwDoc::CountWords(aPaM, aDocStat);
5495 sEntry = pOutlineContent->GetName() + "\n" + SwResId(FLD_STAT_WORD) + ": "
5496 + OUString::number(aDocStat.nWord) + "\n" + SwResId(FLD_STAT_CHAR) + ": "
5497 + OUString::number(aDocStat.nChar);
5499 break;
5500 case ContentTypeId::GRAPHIC:
5501 assert(dynamic_cast<SwGraphicContent*>(static_cast<SwTypeNumber*>(pUserData)));
5502 sEntry = static_cast<SwGraphicContent*>(pUserData)->GetLink();
5503 break;
5504 case ContentTypeId::REGION:
5506 assert(dynamic_cast<SwRegionContent*>(static_cast<SwTypeNumber*>(pUserData)));
5507 sEntry = static_cast<SwRegionContent*>(pUserData)->GetName();
5508 const SwSectionFormats& rFormats = GetWrtShell()->GetDoc()->GetSections();
5509 for (SwSectionFormats::size_type n = rFormats.size(); n;)
5511 const SwNodeIndex* pIdx = nullptr;
5512 const SwSectionFormat* pFormat = rFormats[--n];
5513 const SwSection* pSect;
5514 if (nullptr != (pSect = pFormat->GetSection()) &&
5515 pSect->GetSectionName() == sEntry &&
5516 nullptr != (pIdx = pFormat->GetContent().GetContentIdx()) &&
5517 pIdx->GetNode().GetNodes().IsDocNodes())
5519 SwDocStat aDocStat;
5520 SwPaM aPaM(pIdx->GetNode(), *pIdx->GetNode().EndOfSectionNode());
5521 SwDoc::CountWords(aPaM, aDocStat);
5522 sEntry = SwResId(STR_REGION_DEFNAME) + ": " + sEntry + "\n" +
5523 SwResId(FLD_STAT_WORD) + ": " + OUString::number(aDocStat.nWord) + "\n" +
5524 SwResId(FLD_STAT_CHAR) + ": " + OUString::number(aDocStat.nChar);
5525 break;
5529 break;
5530 case ContentTypeId::FOOTNOTE:
5531 case ContentTypeId::ENDNOTE:
5533 assert(dynamic_cast<SwTextFootnoteContent*>(static_cast<SwTypeNumber*>(pUserData)));
5534 const SwTextFootnote* pFootnote =
5535 static_cast<const SwTextFootnoteContent*>(pUserData)->GetTextFootnote();
5537 sEntry = pFootnote->GetFootnote().IsEndNote() ? SwResId(STR_CONTENT_ENDNOTE) :
5538 SwResId(STR_CONTENT_FOOTNOTE);
5540 break;
5541 default: break;
5543 if(static_cast<SwContent*>(pUserData)->IsInvisible())
5545 if(!sEntry.isEmpty())
5546 sEntry += ", ";
5547 sEntry += m_sInvisible;
5550 else
5552 size_t nMemberCount = static_cast<SwContentType*>(pUserData)->GetMemberCount();
5553 sEntry = OUString::number(nMemberCount) + " " +
5554 (nMemberCount == 1
5555 ? static_cast<SwContentType*>(pUserData)->GetSingleName()
5556 : static_cast<SwContentType*>(pUserData)->GetName());
5559 return sEntry;
5562 void SwContentTree::ExecuteContextMenuAction(const OUString& rSelectedPopupEntry)
5564 if (rSelectedPopupEntry == "makeallfootnotesendnotes"
5565 || rSelectedPopupEntry == "makeallendnotesfootnotes")
5567 std::unique_ptr<weld::TreeIter> xEntryIter(m_xTreeView->make_iterator());
5568 if (!m_xTreeView->get_selected(xEntryIter.get()))
5569 return; // this shouldn't happen
5570 SwContentType* pContentType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntryIter));
5571 m_pActiveShell->StartUndo(rSelectedPopupEntry == "makeallfootnotesendnotes"
5572 ? SwUndoId::MAKE_FOOTNOTES_ENDNOTES
5573 : SwUndoId::MAKE_ENDNOTES_FOOTNOTES);
5574 lcl_SelectAllFootnotesOrEndnotes(*m_pActiveShell, pContentType);
5575 SwFormatFootnote aNote(rSelectedPopupEntry == "makeallfootnotesendnotes");
5576 m_pActiveShell->SetCurFootnote(aNote);
5577 m_pActiveShell->EndUndo();
5578 return;
5581 if (rSelectedPopupEntry == "copy")
5583 CopyOutlineSelections();
5584 return;
5586 if (rSelectedPopupEntry == "collapseallcategories")
5588 std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
5589 bool bEntry = m_xTreeView->get_iter_first(*xEntry);
5590 while (bEntry)
5592 m_xTreeView->collapse_row(*xEntry);
5593 bEntry = m_xTreeView->iter_next_sibling(*xEntry);
5595 return;
5599 std::map<OUString, ContentTypeId> mPopupEntryToContentTypeId
5601 {"tabletracking", ContentTypeId::TABLE},
5602 {"frametracking", ContentTypeId::FRAME},
5603 {"imagetracking", ContentTypeId::GRAPHIC},
5604 {"oleobjecttracking", ContentTypeId::OLE},
5605 {"bookmarktracking", ContentTypeId::BOOKMARK},
5606 {"sectiontracking", ContentTypeId::REGION},
5607 {"hyperlinktracking", ContentTypeId::URLFIELD},
5608 {"referencetracking", ContentTypeId::REFERENCE},
5609 {"indextracking", ContentTypeId::INDEX},
5610 {"commenttracking", ContentTypeId::POSTIT},
5611 {"drawingobjecttracking", ContentTypeId::DRAWOBJECT},
5612 {"fieldtracking", ContentTypeId::TEXTFIELD},
5613 {"footnotetracking", ContentTypeId::FOOTNOTE},
5614 {"endnotetracking", ContentTypeId::ENDNOTE}
5617 if (mPopupEntryToContentTypeId.count(rSelectedPopupEntry))
5619 ContentTypeId eCntTypeId = mPopupEntryToContentTypeId[rSelectedPopupEntry];
5620 SetContentTypeTracking(eCntTypeId, !mTrackContentType[eCntTypeId]);
5621 return;
5625 std::unique_ptr<weld::TreeIter> xFirst(m_xTreeView->make_iterator());
5626 if (!m_xTreeView->get_selected(xFirst.get()))
5627 return; // this shouldn't happen, but better to be safe than ...
5629 if (rSelectedPopupEntry == "deletealltables" || rSelectedPopupEntry == "deleteallframes"
5630 || rSelectedPopupEntry == "deleteallimages" || rSelectedPopupEntry == "deletealloleobjects"
5631 || rSelectedPopupEntry == "deleteallbookmarks" || rSelectedPopupEntry == "deleteallregions"
5632 || rSelectedPopupEntry == "deleteallhyperlinks"
5633 || rSelectedPopupEntry == "deleteallreferences" || rSelectedPopupEntry == "deleteallindexes"
5634 || rSelectedPopupEntry == "deleteallcomments"
5635 || rSelectedPopupEntry == "deletealldrawingobjects"
5636 || rSelectedPopupEntry == "deleteallfields" || rSelectedPopupEntry == "deleteallfootnotes"
5637 || rSelectedPopupEntry == "deleteallendnotes")
5639 DeleteAllContentOfEntryContentType(*xFirst);
5640 return;
5643 if (rSelectedPopupEntry == "protectsection" || rSelectedPopupEntry == "hidesection")
5645 SwRegionContent* pCnt = weld::fromId<SwRegionContent*>(m_xTreeView->get_id(*xFirst));
5646 assert(dynamic_cast<SwRegionContent*>(static_cast<SwTypeNumber*>(pCnt)));
5647 const SwSectionFormat* pSectionFormat = pCnt->GetSectionFormat();
5648 SwSection* pSection = pSectionFormat->GetSection();
5649 SwSectionData aSectionData(*pSection);
5650 if (rSelectedPopupEntry == "protectsection")
5651 aSectionData.SetProtectFlag(!pSection->IsProtect());
5652 else
5653 aSectionData.SetHidden(!pSection->IsHidden());
5654 m_pActiveShell->UpdateSection(m_pActiveShell->GetSectionFormatPos(*pSectionFormat),
5655 aSectionData);
5657 else if (rSelectedPopupEntry == "sort")
5659 SwContentType* pCntType;
5660 const OUString aId(m_xTreeView->get_id(*xFirst));
5661 if (lcl_IsContentType(*xFirst, *m_xTreeView))
5662 pCntType = weld::fromId<SwContentType*>(aId);
5663 else
5664 pCntType = const_cast<SwContentType*>(weld::fromId<SwContent*>(aId)->GetParent());
5666 // toggle and persist alphabetical sort setting
5668 // 1. Get the position of the bit in the block where the value of the alphabetical sort
5669 // setting is persistently stored for the content type.
5670 const int nShift = static_cast<int>(pCntType->GetType());
5671 assert(nShift > -1);
5673 // 2. Create a bit mask to use to filter the sort value from the persistent block.
5674 const sal_Int32 nMask = 1 << nShift;
5676 // 3. Toggle the persistent sort value only when it is different than the instance sort
5677 // value. These may already be the same if both the floating and sidebar version of the
5678 // Navigator are open.
5679 const sal_Int32 nBlock = m_pConfig->GetSortAlphabeticallyBlock();
5680 bool bConfigSortValue = ~nBlock & nMask;
5681 bool bInstanceSortValue = pCntType->IsAlphabeticSort();
5682 if (bConfigSortValue != bInstanceSortValue)
5683 m_pConfig->SetSortAlphabeticallyBlock(nBlock ^ nMask);
5685 // 4. Always toggle the instance value.
5686 pCntType->SetAlphabeticSort(!bInstanceSortValue);
5688 pCntType->FillMemberList();
5689 Display(true);
5690 return;
5692 else if (rSelectedPopupEntry == "deletechapter" ||
5693 rSelectedPopupEntry == "deletetable" ||
5694 rSelectedPopupEntry == "deleteframe" ||
5695 rSelectedPopupEntry == "deleteimage" ||
5696 rSelectedPopupEntry == "deleteoleobject" ||
5697 rSelectedPopupEntry == "deletebookmark" ||
5698 rSelectedPopupEntry == "deleteregion" ||
5699 rSelectedPopupEntry == "deletehyperlink" ||
5700 rSelectedPopupEntry == "deletereference" ||
5701 rSelectedPopupEntry == "deleteindex" ||
5702 rSelectedPopupEntry == "deletecomment" ||
5703 rSelectedPopupEntry == "deletedrawingobject" ||
5704 rSelectedPopupEntry == "deletefield" ||
5705 rSelectedPopupEntry == "deletefootnote" ||
5706 rSelectedPopupEntry == "deleteendnote")
5708 EditEntry(*xFirst, EditEntryMode::DELETE);
5709 return;
5712 auto nSelectedPopupEntry = rSelectedPopupEntry.toUInt32();
5713 switch (nSelectedPopupEntry)
5715 case TOGGLE_OUTLINE_CONTENT_VISIBILITY:
5716 case HIDE_OUTLINE_CONTENT_VISIBILITY:
5717 case SHOW_OUTLINE_CONTENT_VISIBILITY:
5719 m_pActiveShell->EnterStdMode();
5720 m_bIgnoreDocChange = true;
5721 SwOutlineContent* pCntFirst = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xFirst));
5723 // toggle the outline node outline content visible attribute
5724 if (nSelectedPopupEntry == TOGGLE_OUTLINE_CONTENT_VISIBILITY)
5726 SwNode* pNode = m_pActiveShell->GetDoc()->GetNodes().GetOutLineNds()[pCntFirst->GetOutlinePos()];
5727 pNode->GetTextNode()->SetAttrOutlineContentVisible(
5728 !m_pActiveShell->GetAttrOutlineContentVisible(pCntFirst->GetOutlinePos()));
5730 else
5732 // with subs
5733 SwOutlineNodes::size_type nPos = pCntFirst->GetOutlinePos();
5734 if (lcl_IsContentType(*xFirst, *m_xTreeView)) // Headings root entry
5735 nPos = SwOutlineNodes::npos;
5736 SwOutlineNodes::size_type nOutlineNodesCount = m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount();
5737 int nLevel = -1;
5738 if (nPos != SwOutlineNodes::npos) // not root
5739 nLevel = m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos);
5740 else
5741 nPos = 0;
5742 bool bShow(nSelectedPopupEntry == SHOW_OUTLINE_CONTENT_VISIBILITY);
5745 if (m_pActiveShell->IsOutlineContentVisible(nPos) != bShow)
5746 m_pActiveShell->GetDoc()->GetNodes().GetOutLineNds()[nPos]->GetTextNode()->SetAttrOutlineContentVisible(bShow);
5747 } while (++nPos < nOutlineNodesCount
5748 && (nLevel == -1 || m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos) > nLevel));
5750 m_pActiveShell->InvalidateOutlineContentVisibility();
5751 // show in the document what was toggled
5752 if (lcl_IsContentType(*xFirst, *m_xTreeView)) // Headings root entry
5753 m_pActiveShell->GotoPage(1, true);
5754 else
5755 m_pActiveShell->GotoOutline(pCntFirst->GetOutlinePos());
5756 grab_focus();
5757 m_bIgnoreDocChange = false;
5758 m_pActiveShell->SetModified();
5759 m_pActiveShell->GetDoc()->GetDocShell()->Broadcast(SfxHint(SfxHintId::DocChanged));
5761 break;
5762 case 11:
5763 case 12:
5764 case 13:
5765 nSelectedPopupEntry -= 10;
5766 if(m_nOutlineTracking != nSelectedPopupEntry)
5767 SetOutlineTracking(static_cast<sal_uInt8>(nSelectedPopupEntry));
5768 break;
5769 //Outlinelevel
5770 case 101:
5771 case 102:
5772 case 103:
5773 case 104:
5774 case 105:
5775 case 106:
5776 case 107:
5777 case 108:
5778 case 109:
5779 case 110:
5780 nSelectedPopupEntry -= 100;
5781 if(m_nOutlineLevel != nSelectedPopupEntry )
5782 SetOutlineLevel(static_cast<sal_Int8>(nSelectedPopupEntry));
5783 break;
5784 case 402:
5785 EditEntry(*xFirst, EditEntryMode::UPD_IDX);
5786 break;
5787 // Edit entry
5788 case 403:
5789 EditEntry(*xFirst, EditEntryMode::EDIT);
5790 break;
5791 case 404:
5792 EditEntry(*xFirst, EditEntryMode::UNPROTECT_TABLE);
5793 break;
5794 case 405 :
5796 const SwTOXBase* pBase = weld::fromId<SwTOXBaseContent*>(m_xTreeView->get_id(*xFirst))
5797 ->GetTOXBase();
5798 m_pActiveShell->SetTOXBaseReadonly(*pBase, !SwEditShell::IsTOXBaseReadonly(*pBase));
5800 break;
5801 case 502 :
5802 EditEntry(*xFirst, EditEntryMode::RENAME);
5803 break;
5804 case 700:
5806 m_pActiveShell->GetView().GetViewFrame().GetDispatcher()->Execute(FN_OUTLINE_TO_CLIPBOARD);
5807 break;
5809 case 800:
5810 ExpandOrCollapseAll(*m_xTreeView, *xFirst);
5811 break;
5812 case 801:
5813 ExecCommand(u"chapterup", true);
5814 break;
5815 case 802:
5816 ExecCommand(u"chapterdown", true);
5817 break;
5818 case 803:
5819 ExecCommand(u"promote", true);
5820 break;
5821 case 804:
5822 ExecCommand(u"demote", true);
5823 break;
5824 case 805: // select document content
5826 m_pActiveShell->KillPams();
5827 m_pActiveShell->ClearMark();
5828 m_pActiveShell->EnterAddMode();
5829 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xFirst));
5830 const ContentTypeId eTypeId = pCnt->GetParent()->GetType();
5831 if (eTypeId == ContentTypeId::OUTLINE)
5833 SwOutlineNodes::size_type nActPos = weld::fromId<SwOutlineContent*>(
5834 m_xTreeView->get_id(*xFirst))->GetOutlinePos();
5835 m_pActiveShell->GotoOutline(nActPos);
5836 m_xTreeView->selected_foreach([this](weld::TreeIter& rEntry){
5837 SwOutlineNodes::size_type nPos = weld::fromId<SwOutlineContent*>(
5838 m_xTreeView->get_id(rEntry))->GetOutlinePos();
5839 m_pActiveShell->SttSelect();
5840 // select children if not expanded and don't kill PaMs
5841 m_pActiveShell->MakeOutlineSel(nPos, nPos,
5842 !m_xTreeView->get_row_expanded(rEntry), false);
5843 m_pActiveShell->EndSelect();
5844 return false;
5847 else if (eTypeId == ContentTypeId::TABLE)
5849 m_pActiveShell->GotoTable(pCnt->GetName());
5850 m_pActiveShell->GetView().GetViewFrame().GetDispatcher()->Execute(FN_TABLE_SELECT_ALL);
5852 else if (eTypeId == ContentTypeId::REGION)
5854 m_pActiveShell->EnterStdMode();
5855 m_pActiveShell->GotoRegion(pCnt->GetName());
5856 GotoCurrRegionAndSkip(m_pActiveShell->GetCurrentShellCursor(), fnRegionEnd, m_pActiveShell->IsReadOnlyAvailable());
5857 m_pActiveShell->SttSelect();
5858 GotoCurrRegionAndSkip(m_pActiveShell->GetCurrentShellCursor(), fnRegionStart, m_pActiveShell->IsReadOnlyAvailable());
5859 m_pActiveShell->EndSelect();
5860 m_pActiveShell->UpdateCursor();
5862 m_pActiveShell->LeaveAddMode();
5864 break;
5865 case 900:
5867 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xFirst));
5868 GotoContent(pCnt);
5870 break;
5871 //Display
5872 default:
5873 if(nSelectedPopupEntry > 300 && nSelectedPopupEntry < 400)
5875 nSelectedPopupEntry -= 300;
5876 SwView *pView = SwModule::GetFirstView();
5877 while (pView)
5879 nSelectedPopupEntry --;
5880 if(nSelectedPopupEntry == 0)
5882 SetConstantShell(&pView->GetWrtShell());
5883 break;
5885 pView = SwModule::GetNextView(pView);
5887 if(nSelectedPopupEntry)
5889 m_bViewHasChanged = nSelectedPopupEntry == 1;
5890 m_eState = (nSelectedPopupEntry == 1) ? State::ACTIVE : State::HIDDEN;
5891 Display(nSelectedPopupEntry == 1);
5893 GetParentWindow()->UpdateListBox();
5898 void SwContentTree::DeleteOutlineSelections()
5900 const SwOutlineNodes& rOutlineNodes = m_pActiveShell->GetNodes().GetOutLineNds();
5901 auto nChapters(0);
5903 m_pActiveShell->StartAction();
5905 m_pActiveShell->EnterAddMode();
5906 m_xTreeView->selected_foreach([this, &rOutlineNodes, &nChapters](weld::TreeIter& rEntry){
5907 ++nChapters;
5908 if (m_xTreeView->iter_has_child(rEntry) &&
5909 !m_xTreeView->get_row_expanded(rEntry)) // only count children if not expanded
5911 nChapters += m_xTreeView->iter_n_children(rEntry);
5913 SwOutlineNodes::size_type nActPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rEntry))->GetOutlinePos();
5914 if (m_pActiveShell->GetViewOptions()->IsShowOutlineContentVisibilityButton())
5916 // make folded content visible so it can be selected
5917 if (!m_pActiveShell->IsOutlineContentVisible(nActPos))
5918 m_pActiveShell->MakeOutlineContentVisible(nActPos);
5919 if (!m_xTreeView->get_row_expanded(rEntry))
5921 // include children
5922 SwNode* pNode = rOutlineNodes[nActPos];
5923 const int nLevel = pNode->GetTextNode()->GetAttrOutlineLevel() - 1;
5924 for (auto nPos = nActPos + 1; nPos < rOutlineNodes.size(); ++nPos)
5926 pNode = rOutlineNodes[nPos];
5927 const int nNextLevel = pNode->GetTextNode()->GetAttrOutlineLevel() - 1;
5928 if (nNextLevel <= nLevel)
5929 break;
5930 if (!m_pActiveShell->IsOutlineContentVisible(nNextLevel))
5931 m_pActiveShell->MakeOutlineContentVisible(nNextLevel);
5935 m_pActiveShell->SttSelect();
5936 m_pActiveShell->MakeOutlineSel(nActPos, nActPos, !m_xTreeView->get_row_expanded(rEntry), false); // select children if not expanded
5937 // The outline selection may already be to the start of the following outline paragraph
5938 // as happens when a table is the last content of the to be deleted outline. In this case
5939 // do not extend the to be deleted selection right or the first character of the following
5940 // outline paragraph will be removed. Also check if no selection was made which indicates
5941 // an empty paragraph and selection right is needed.
5942 if (!m_pActiveShell->IsSttPara() || !m_pActiveShell->HasSelection())
5943 m_pActiveShell->Right(SwCursorSkipMode::Chars, true, 1, false);
5944 m_pActiveShell->EndSelect();
5945 return false;
5947 m_pActiveShell->LeaveAddMode();
5949 SwRewriter aRewriter;
5950 aRewriter.AddRule(UndoArg1, SwResId(STR_CHAPTERS, nChapters));
5951 m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter);
5952 m_pActiveShell->Delete(false);
5953 m_pActiveShell->EndUndo();
5955 m_pActiveShell->EndAction();
5958 void SwContentTree::SetOutlineLevel(sal_uInt8 nSet)
5960 if (nSet == m_nOutlineLevel)
5961 return;
5962 m_nOutlineLevel = nSet;
5963 m_pConfig->SetOutlineLevel( m_nOutlineLevel );
5964 std::unique_ptr<SwContentType>& rpContentT = (State::ACTIVE == m_eState)
5965 ? m_aActiveContentArr[ContentTypeId::OUTLINE]
5966 : m_aHiddenContentArr[ContentTypeId::OUTLINE];
5967 if(rpContentT)
5969 rpContentT->SetOutlineLevel(m_nOutlineLevel);
5970 rpContentT->FillMemberList();
5972 Display(State::ACTIVE == m_eState);
5975 void SwContentTree::SetOutlineTracking(sal_uInt8 nSet)
5977 m_nOutlineTracking = nSet;
5978 m_pConfig->SetOutlineTracking(m_nOutlineTracking);
5981 void SwContentTree::SetContentTypeTracking(ContentTypeId eCntTypeId, bool bSet)
5983 mTrackContentType[eCntTypeId] = bSet;
5984 m_pConfig->SetContentTypeTrack(eCntTypeId, bSet);
5987 // Mode Change: Show dropped Doc
5988 void SwContentTree::ShowHiddenShell()
5990 if(m_pHiddenShell)
5992 m_eState = State::HIDDEN;
5993 Display(false);
5997 // Mode Change: Show active view
5998 // only called from IMPL_LINK(SwNavigationPI, DocListBoxSelectHdl, weld::ComboBox&, rBox, void)
5999 void SwContentTree::ShowActualView()
6001 if (SwView* pView = m_pDialog->GetCreateView())
6003 SetConstantShell(pView->GetWrtShellPtr());
6004 m_pDialog->UpdateListBox();
6008 IMPL_LINK_NOARG(SwContentTree, SelectHdl, weld::TreeView&, void)
6010 if (m_pConfig->IsNavigateOnSelect())
6012 ContentDoubleClickHdl(*m_xTreeView);
6013 grab_focus();
6015 UpdateContentFunctionsToolbar();
6016 if (m_bIsRoot)
6017 return;
6018 // Select the content type in the Navigate By control
6019 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
6020 if (!m_xTreeView->get_selected(xEntry.get()))
6021 return;
6022 while (m_xTreeView->get_iter_depth(*xEntry))
6023 m_xTreeView->iter_parent(*xEntry);
6024 m_pDialog->SelectNavigateByContentType(m_xTreeView->get_text(*xEntry));
6027 void SwContentTree::UpdateContentFunctionsToolbar()
6029 SwNavigationPI* pNavi = GetParentWindow();
6030 if (pNavi)
6031 pNavi->UpdateContentFunctionsToolbar();
6034 void SwContentTree::SetRootType(ContentTypeId nType)
6036 m_nRootType = nType;
6037 m_bIsRoot = true;
6038 m_pConfig->SetRootType( m_nRootType );
6041 OUString SwContentType::RemoveNewline(const OUString& rEntry)
6043 if (rEntry.isEmpty())
6044 return rEntry;
6046 OUStringBuffer aEntry(rEntry);
6047 for (sal_Int32 i = 0; i < rEntry.getLength(); ++i)
6048 if(aEntry[i] == 10 || aEntry[i] == 13)
6049 aEntry[i] = 0x20;
6051 return aEntry.makeStringAndClear();
6054 void SwContentTree::EditEntry(const weld::TreeIter& rEntry, EditEntryMode nMode)
6056 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(rEntry));
6057 GotoContent(pCnt);
6058 const ContentTypeId nType = pCnt->GetParent()->GetType();
6059 sal_uInt16 nSlot = 0;
6061 if(EditEntryMode::DELETE == nMode)
6062 m_bIgnoreDocChange = true;
6064 uno::Reference< container::XNameAccess > xNameAccess, xSecond, xThird;
6065 switch(nType)
6067 case ContentTypeId::OUTLINE :
6068 if(nMode == EditEntryMode::DELETE)
6070 DeleteOutlineSelections();
6072 break;
6074 case ContentTypeId::TABLE :
6075 if(nMode == EditEntryMode::UNPROTECT_TABLE)
6077 m_pActiveShell->GetView().GetDocShell()->
6078 GetDoc()->UnProtectCells( pCnt->GetName());
6080 else if(nMode == EditEntryMode::DELETE)
6082 nSlot = FN_TABLE_DELETE_TABLE;
6084 else if(nMode == EditEntryMode::RENAME)
6086 rtl::Reference< SwXTextDocument > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
6087 xNameAccess = xModel->getTextTables();
6089 else
6090 nSlot = FN_FORMAT_TABLE_DLG;
6091 break;
6093 case ContentTypeId::GRAPHIC :
6094 if(nMode == EditEntryMode::DELETE)
6096 m_pActiveShell->DelRight();
6098 else if(nMode == EditEntryMode::RENAME)
6100 rtl::Reference< SwXTextDocument > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
6101 xNameAccess = xModel->getGraphicObjects();
6102 xSecond = xModel->getTextFrames();
6103 xThird = xModel->getEmbeddedObjects();
6105 else
6106 nSlot = FN_FORMAT_GRAFIC_DLG;
6107 break;
6109 case ContentTypeId::FRAME :
6110 case ContentTypeId::OLE :
6111 if(nMode == EditEntryMode::DELETE)
6113 m_pActiveShell->DelRight();
6115 else if(nMode == EditEntryMode::RENAME)
6117 rtl::Reference< SwXTextDocument > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
6118 if(ContentTypeId::FRAME == nType)
6120 xNameAccess = xModel->getTextFrames();
6121 xSecond = xModel->getEmbeddedObjects();
6123 else
6125 xNameAccess = xModel->getEmbeddedObjects();
6126 xSecond = xModel->getTextFrames();
6128 xThird = xModel->getGraphicObjects();
6130 else
6131 nSlot = FN_FORMAT_FRAME_DLG;
6132 break;
6133 case ContentTypeId::BOOKMARK :
6134 if(nMode == EditEntryMode::DELETE)
6136 assert(!m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS));
6137 IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess();
6138 pMarkAccess->deleteMark(pMarkAccess->findMark(pCnt->GetName()), false);
6140 else if(nMode == EditEntryMode::RENAME)
6142 assert(!m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS));
6143 rtl::Reference< SwXTextDocument > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
6144 xNameAccess = xModel->getBookmarks();
6146 else
6148 // allowed despite PROTECT_BOOKMARKS: the dialog itself enforces it
6149 SfxStringItem const name(FN_EDIT_BOOKMARK, pCnt->GetName());
6150 SfxPoolItem const* args[2] = { &name, nullptr };
6151 m_pActiveShell->GetView().GetViewFrame().
6152 GetDispatcher()->Execute(FN_EDIT_BOOKMARK, SfxCallMode::SYNCHRON, args);
6154 break;
6156 case ContentTypeId::REGION :
6157 if (nMode == EditEntryMode::DELETE)
6159 assert(dynamic_cast<SwRegionContent*>(static_cast<SwTypeNumber*>(pCnt)));
6160 const SwSectionFormat* pSectionFormat
6161 = static_cast<SwRegionContent*>(pCnt)->GetSectionFormat();
6162 m_pActiveShell->GetDoc()->DelSectionFormat(
6163 const_cast<SwSectionFormat*>(pSectionFormat), false);
6165 else if (nMode == EditEntryMode::RENAME)
6167 rtl::Reference< SwXTextDocument > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
6168 xNameAccess = xModel->getTextSections();
6170 else
6171 nSlot = FN_EDIT_REGION;
6172 break;
6174 case ContentTypeId::URLFIELD:
6175 if (nMode == EditEntryMode::DELETE)
6176 nSlot = SID_REMOVE_HYPERLINK;
6177 else
6178 nSlot = SID_EDIT_HYPERLINK;
6179 break;
6180 case ContentTypeId::REFERENCE:
6182 if(nMode == EditEntryMode::DELETE)
6184 const OUString& rName = pCnt->GetName();
6185 m_pActiveShell->GetDoc()->ForEachRefMark(
6186 [&rName, this] (const SwFormatRefMark& rFormatRefMark) -> bool
6188 const SwTextRefMark* pTextRef = rFormatRefMark.GetTextRefMark();
6189 if (pTextRef && rName == rFormatRefMark.GetRefName())
6191 m_pActiveShell->GetDoc()->DeleteFormatRefMark(&rFormatRefMark);
6192 m_pActiveShell->SwViewShell::UpdateFields();
6193 return false;
6195 return true;
6199 break;
6200 case ContentTypeId::TEXTFIELD:
6202 if (nMode == EditEntryMode::DELETE)
6204 const SwTextFieldContent* pTextFieldCnt =
6205 static_cast<const SwTextFieldContent*>(pCnt);
6206 const SwTextField* pTextField = pTextFieldCnt->GetFormatField()->GetTextField();
6207 SwTextField::DeleteTextField(*pTextField);
6209 else
6210 nSlot = FN_EDIT_FIELD;
6212 break;
6213 case ContentTypeId::POSTIT:
6215 auto& rView = m_pActiveShell->GetView();
6216 auto pPostItMgr = rView.GetPostItMgr();
6217 pPostItMgr->AssureStdModeAtShell();
6218 pPostItMgr->SetActiveSidebarWin(nullptr);
6219 rView.GetEditWin().GrabFocus();
6220 if(nMode == EditEntryMode::DELETE)
6221 m_pActiveShell->DelRight();
6222 else
6223 nSlot = FN_POSTIT;
6225 break;
6226 case ContentTypeId::INDEX:
6228 const SwTOXBase* pBase = static_cast<SwTOXBaseContent*>(pCnt)->GetTOXBase();
6229 switch(nMode)
6231 case EditEntryMode::EDIT:
6232 if(pBase)
6234 SwPtrItem aPtrItem( FN_INSERT_MULTI_TOX, const_cast<SwTOXBase *>(pBase));
6235 m_pActiveShell->GetView().GetViewFrame().
6236 GetDispatcher()->ExecuteList(FN_INSERT_MULTI_TOX,
6237 SfxCallMode::ASYNCHRON, { &aPtrItem });
6240 break;
6241 case EditEntryMode::DELETE:
6243 if( pBase )
6244 m_pActiveShell->DeleteTOX(*pBase, EditEntryMode::DELETE == nMode);
6246 break;
6247 case EditEntryMode::UPD_IDX:
6248 case EditEntryMode::RENAME:
6250 rtl::Reference< SwXTextDocument > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
6251 rtl::Reference< SwXDocumentIndexes> xIdxAcc = xModel->getSwDocumentIndexes();
6252 if(EditEntryMode::RENAME == nMode)
6253 xNameAccess = xIdxAcc;
6254 else if(xIdxAcc.is() && xIdxAcc->hasByName(pBase->GetTOXName()))
6256 Any aIdx = xIdxAcc->getByName(pBase->GetTOXName());
6257 Reference< XDocumentIndex> xIdx;
6258 if(aIdx >>= xIdx)
6259 xIdx->update();
6262 break;
6263 default: break;
6266 break;
6267 case ContentTypeId::DRAWOBJECT :
6268 if(EditEntryMode::DELETE == nMode)
6269 nSlot = SID_DELETE;
6270 else if(nMode == EditEntryMode::RENAME)
6271 nSlot = FN_NAME_SHAPE;
6272 else if (nMode == EditEntryMode::EDIT)
6274 vcl::KeyCode aKeyCode(KEY_RETURN, false, false, false, false);
6275 KeyEvent aKeyEvent(0, aKeyCode);
6276 m_pActiveShell->GetWin()->KeyInput(aKeyEvent);
6278 break;
6279 case ContentTypeId::FOOTNOTE:
6280 case ContentTypeId::ENDNOTE:
6282 if (nMode == EditEntryMode::DELETE)
6283 m_pActiveShell->DelRight();
6284 else if (EditEntryMode::EDIT == nMode)
6285 nSlot = FN_FORMAT_FOOTNOTE_DLG;
6287 break;
6288 default: break;
6290 if(nSlot)
6291 m_pActiveShell->GetView().GetViewFrame().
6292 GetDispatcher()->Execute(nSlot, SfxCallMode::SYNCHRON);
6293 else if(xNameAccess.is())
6295 uno::Any aObj = xNameAccess->getByName(pCnt->GetName());
6296 uno::Reference< uno::XInterface > xTmp;
6297 aObj >>= xTmp;
6298 uno::Reference< container::XNamed > xNamed(xTmp, uno::UNO_QUERY);
6299 SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();
6300 ScopedVclPtr<AbstractSwRenameXNamedDlg> pDlg(pFact->CreateSwRenameXNamedDlg(m_xTreeView.get(), xNamed, xNameAccess));
6301 if(xSecond.is())
6302 pDlg->SetAlternativeAccess( xSecond, xThird);
6304 OUString sForbiddenChars;
6305 if(ContentTypeId::BOOKMARK == nType)
6307 sForbiddenChars = "/\\@:*?\";,.#";
6309 else if(ContentTypeId::TABLE == nType)
6311 sForbiddenChars = " .<>";
6313 pDlg->SetForbiddenChars(sForbiddenChars);
6314 pDlg->Execute();
6316 if(EditEntryMode::DELETE == nMode)
6318 auto nPos = m_xTreeView->vadjustment_get_value();
6319 m_bViewHasChanged = true;
6320 TimerUpdate(&m_aUpdTimer);
6321 grab_focus();
6322 m_xTreeView->vadjustment_set_value(nPos);
6326 bool SwContentTree::IsDeletable(const weld::TreeIter& rEntry)
6328 if (lcl_IsContentType(rEntry, *m_xTreeView))
6330 if (State::HIDDEN == m_eState || !m_pActiveShell)
6331 return false;
6332 if (m_pActiveShell->GetView().GetDocShell()->IsReadOnly())
6333 return false;
6334 SwContentType* pContentType
6335 = weld::fromId<SwContentType*>(m_xTreeView->get_id(rEntry));
6336 auto nCount = pContentType->GetMemberCount();
6337 if (nCount == 0)
6338 return false;
6339 for (size_t i = 0; i < nCount; i++)
6341 const SwContent* pContent = pContentType->GetMember(i);
6342 if (IsDeletable(pContent))
6343 return true;
6345 return false;
6347 return IsDeletable(weld::fromId<SwContent*>(m_xTreeView->get_id(rEntry)));
6350 bool SwContentTree::IsDeletable(const SwContent* pContent)
6352 if (State::HIDDEN == m_eState || !m_pActiveShell)
6353 return false;
6354 if (m_pActiveShell->GetView().GetDocShell()->IsReadOnly())
6355 return false;
6356 if (pContent->IsInvisible() || pContent->IsProtect())
6357 return false;
6359 ContentTypeId eContentTypeId = pContent->GetParent()->GetType();
6361 // table
6362 if (eContentTypeId == ContentTypeId::TABLE)
6364 bool bFull = false;
6365 m_pActiveShell->HasTableAnyProtection(&pContent->GetName(), &bFull);
6366 return !bFull;
6368 // bookmark
6369 if (eContentTypeId == ContentTypeId::BOOKMARK)
6370 return !m_pActiveShell->getIDocumentSettingAccess().get(
6371 DocumentSettingId::PROTECT_BOOKMARKS);
6372 // index
6373 if (eContentTypeId == ContentTypeId::INDEX)
6375 const SwTOXBase* pBase = static_cast<const SwTOXBaseContent*>(pContent)->GetTOXBase();
6376 return !SwEditShell::IsTOXBaseReadonly(*pBase);
6379 return true;
6382 void SwContentTree::DeleteAllContentOfEntryContentType(const weld::TreeIter& rEntry)
6384 weld::WaitObject aWait(m_xTreeView.get());
6386 SwContentType* pContentType;
6387 if (lcl_IsContentType(rEntry, *m_xTreeView))
6389 assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rEntry))));
6390 pContentType = weld::fromId<SwContentType*>(m_xTreeView->get_id(rEntry));
6392 else
6394 assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rEntry))));
6395 pContentType = const_cast<SwContentType*>(
6396 weld::fromId<SwContent*>(m_xTreeView->get_id(rEntry))->GetParent());
6399 const ContentTypeId eContentTypeId = pContentType->GetType();
6400 if (eContentTypeId == ContentTypeId::TABLE)
6402 m_pActiveShell->AssureStdMode();
6404 const auto nCount = pContentType->GetMemberCount();
6406 m_pActiveShell->StartAction();
6407 SwRewriter aRewriter;
6408 aRewriter.AddRule(UndoArg1, pContentType->GetName());
6409 m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter);
6410 for (size_t i = 0; i < nCount; i++)
6412 const SwContent* pContent = pContentType->GetMember(i);
6413 m_pActiveShell->GotoTable(pContent->GetName());
6414 m_pActiveShell->SelTable();
6415 m_pActiveShell->DeleteTable();
6417 m_pActiveShell->EndUndo();
6418 m_pActiveShell->EndAction();
6420 else if (eContentTypeId == ContentTypeId::FRAME
6421 || eContentTypeId == ContentTypeId::GRAPHIC
6422 || eContentTypeId == ContentTypeId::OLE)
6424 m_pActiveShell->AssureStdMode();
6426 const auto nCount = pContentType->GetMemberCount();
6428 m_pActiveShell->LockView(true);
6429 SwRewriter aRewriter;
6430 aRewriter.AddRule(UndoArg1, pContentType->GetName());
6431 m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter);
6432 for (size_t i = 0; i < nCount; i++)
6434 const OUString& rName(pContentType->GetMember(i)->GetName());
6435 m_pActiveShell->GotoFly(rName);
6436 m_pActiveShell->DelRight();
6438 m_pActiveShell->EndUndo();
6439 m_pActiveShell->LockView(false);
6441 else if (eContentTypeId == ContentTypeId::BOOKMARK)
6443 m_pActiveShell->AssureStdMode();
6445 const auto nCount = pContentType->GetMemberCount();
6447 IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess();
6449 m_pActiveShell->StartAction();
6450 SwRewriter aRewriter;
6451 aRewriter.AddRule(UndoArg1, pContentType->GetName());
6452 m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter);
6453 for (size_t i = 0; i < nCount; i++)
6455 const OUString& rName(pContentType->GetMember(i)->GetName());
6456 pMarkAccess->deleteMark(pMarkAccess->findMark(rName), false);
6458 m_pActiveShell->EndUndo();
6459 m_pActiveShell->EndAction();
6461 else if (eContentTypeId == ContentTypeId::REGION)
6463 const auto nCount = pContentType->GetMemberCount();
6465 m_pActiveShell->StartAction();
6466 SwRewriter aRewriter;
6467 aRewriter.AddRule(UndoArg1, pContentType->GetName());
6468 m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter);
6469 for (size_t i = 0; i < nCount; i++)
6471 const SwRegionContent* pRegionContent
6472 = static_cast<const SwRegionContent*>(pContentType->GetMember(i));
6473 SwSectionFormat* pSectionFormat
6474 = const_cast<SwSectionFormat*>(pRegionContent->GetSectionFormat());
6475 m_pActiveShell->GetDoc()->DelSectionFormat(pSectionFormat, false);
6477 m_pActiveShell->EndUndo();
6478 m_pActiveShell->EndAction();
6480 else if (eContentTypeId == ContentTypeId::URLFIELD) // hyperlinks
6482 m_pActiveShell->AssureStdMode();
6484 const auto nCount = pContentType->GetMemberCount();
6486 m_pActiveShell->LockView(true);
6487 SwRewriter aRewriter;
6488 aRewriter.AddRule(UndoArg1, pContentType->GetName());
6489 m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter);
6490 for (size_t i = 0; i < nCount; i++)
6492 if (m_pActiveShell->GotoINetAttr(*static_cast<const SwURLFieldContent*>(
6493 pContentType->GetMember(i))->GetINetAttr()))
6495 m_pActiveShell->Right(SwCursorSkipMode::Chars, false, 1, false);
6496 m_pActiveShell->SwCursorShell::SelectTextAttr(RES_TXTATR_INETFMT, true);
6497 m_pActiveShell->DelRight();
6500 m_pActiveShell->EndUndo();
6501 m_pActiveShell->LockView(false);
6503 else if (eContentTypeId == ContentTypeId::REFERENCE )
6505 m_pActiveShell->AssureStdMode();
6507 const auto nCount = pContentType->GetMemberCount();
6509 m_pActiveShell->StartAction();
6510 SwRewriter aRewriter;
6511 aRewriter.AddRule(UndoArg1, pContentType->GetName());
6512 m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter);
6513 for (size_t i = 0; i < nCount; i++)
6515 const OUString& rName = pContentType->GetMember(i)->GetName();
6516 m_pActiveShell->GetDoc()->ForEachRefMark(
6517 [&rName, this] (const SwFormatRefMark& rFormatRefMark) -> bool
6519 const SwTextRefMark* pTextRef = rFormatRefMark.GetTextRefMark();
6520 if (pTextRef && rName == rFormatRefMark.GetRefName())
6522 m_pActiveShell->GetDoc()->DeleteFormatRefMark(&rFormatRefMark);
6523 return false;
6525 return true;
6528 m_pActiveShell->SwViewShell::UpdateFields();
6529 m_pActiveShell->EndUndo();
6530 m_pActiveShell->EndAction();
6532 else if (eContentTypeId == ContentTypeId::INDEX)
6534 m_pActiveShell->AssureStdMode();
6536 const auto nCount = pContentType->GetMemberCount();
6538 m_pActiveShell->StartAction();
6539 SwRewriter aRewriter;
6540 aRewriter.AddRule(UndoArg1, pContentType->GetName());
6541 m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter);
6542 for (size_t i = 0; i < nCount; i++)
6544 SwContent* pContent = const_cast<SwContent*>(pContentType->GetMember(i));
6545 const SwTOXBase* pBase
6546 = static_cast<SwTOXBaseContent*>(pContent)->GetTOXBase();
6547 if (pBase)
6548 m_pActiveShell->DeleteTOX(*pBase, true);
6550 m_pActiveShell->EndUndo();
6551 m_pActiveShell->EndAction();
6553 else if (eContentTypeId == ContentTypeId::POSTIT)
6555 m_pActiveShell->AssureStdMode();
6557 const auto nCount = pContentType->GetMemberCount();
6559 m_pActiveShell->StartAction();
6560 SwRewriter aRewriter;
6561 aRewriter.AddRule(UndoArg1, pContentType->GetName());
6562 m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter);
6563 for (size_t i = 0; i < nCount; i++)
6565 const SwPostItContent* pPostItContent
6566 = static_cast<const SwPostItContent*>(pContentType->GetMember(i));
6567 m_pActiveShell->GotoFormatField(*pPostItContent->GetPostIt());
6568 m_pActiveShell->DelRight();
6570 m_pActiveShell->EndUndo();
6571 m_pActiveShell->EndAction();
6573 else if (eContentTypeId == ContentTypeId::DRAWOBJECT)
6575 m_pActiveShell->AssureStdMode();
6577 const auto nCount = pContentType->GetMemberCount();
6579 m_pActiveShell->StartAction();
6580 SwRewriter aRewriter;
6581 aRewriter.AddRule(UndoArg1, pContentType->GetName());
6582 m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter);
6583 for (size_t i = 0; i < nCount; i++)
6585 const OUString& rName(pContentType->GetMember(i)->GetName());
6586 m_pActiveShell->GotoDrawingObject(rName);
6587 m_pActiveShell->DelRight();
6588 //m_pActiveShell->DelSelectedObj();
6590 m_pActiveShell->EndUndo();
6591 m_pActiveShell->EndAction();
6593 else if (eContentTypeId == ContentTypeId::TEXTFIELD)
6595 m_pActiveShell->AssureStdMode();
6597 const auto nCount = pContentType->GetMemberCount();
6599 m_pActiveShell->StartAction();
6600 SwRewriter aRewriter;
6601 aRewriter.AddRule(UndoArg1, pContentType->GetName());
6602 m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter);
6603 for (size_t i = 0; i < nCount; i++)
6605 const SwTextFieldContent* pTextFieldContent =
6606 static_cast<const SwTextFieldContent*>(pContentType->GetMember(i));
6607 const SwTextField* pTextField = pTextFieldContent->GetFormatField()->GetTextField();
6608 SwTextField::DeleteTextField(*pTextField);
6610 m_pActiveShell->EndUndo();
6611 m_pActiveShell->EndAction();
6613 else if (eContentTypeId == ContentTypeId::FOOTNOTE || eContentTypeId == ContentTypeId::ENDNOTE)
6615 //MakeAllOutlineContentTemporarilyVisible a(m_pActiveShell->GetDoc());
6616 lcl_SelectAllFootnotesOrEndnotes(*m_pActiveShell, pContentType);
6617 SwRewriter aRewriter;
6618 aRewriter.AddRule(UndoArg1, pContentType->GetName());
6619 m_pActiveShell->StartUndo(SwUndoId::DELETE, &aRewriter);
6620 m_pActiveShell->DelRight();
6621 m_pActiveShell->EndUndo();
6625 void SwContentTree::CopyOutlineSelections()
6627 m_pActiveShell->LockView(true);
6629 MakeAllOutlineContentTemporarilyVisible a(m_pActiveShell->GetDoc());
6630 m_pActiveShell->AssureStdMode();
6631 m_pActiveShell->EnterAddMode();
6632 size_t nCount = m_xTreeView->get_selected_rows().size();
6633 m_xTreeView->selected_foreach([this, &nCount](weld::TreeIter& rEntry){
6634 SwOutlineNodes::size_type nOutlinePos = reinterpret_cast<SwOutlineContent*>(
6635 m_xTreeView->get_id(rEntry).toInt64())->GetOutlinePos();
6636 m_pActiveShell->SttSelect();
6637 m_pActiveShell->MakeOutlineSel(nOutlinePos, nOutlinePos,
6638 !m_xTreeView->get_row_expanded(rEntry), false);
6639 // don't move if this is the last selected outline or the cursor is at start of para
6640 if (--nCount && !m_pActiveShell->IsSttPara())
6641 m_pActiveShell->Right(SwCursorSkipMode::Chars, true, 1, false);
6642 m_pActiveShell->EndSelect();
6643 return false;
6645 m_pActiveShell->LeaveAddMode();
6646 m_pActiveShell->GetView().GetViewFrame().GetBindings().Execute(SID_COPY);
6648 m_pActiveShell->LockView(false);
6651 void SwContentTree::GotoContent(const SwContent* pCnt)
6653 if (pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE)
6655 // Maybe the outline node doesn't have a layout frame to go to.
6656 const SwOutlineNodes::size_type nPos =
6657 static_cast<const SwOutlineContent*>(pCnt)->GetOutlinePos();
6658 const SwNodes& rNds = m_pActiveShell->GetDoc()->GetNodes();
6659 SwTextNode* pTextNd = rNds.GetOutLineNds()[nPos]->GetTextNode();
6660 if (!pTextNd->getLayoutFrame(m_pActiveShell->GetLayout()))
6661 return;
6664 if (m_bSelectTo)
6666 if (m_pActiveShell->IsCursorInTable() ||
6667 (m_pActiveShell->GetCursor()->GetPoint()->nNode.GetIndex() <=
6668 m_pActiveShell->GetDoc()->GetNodes().GetEndOfExtras().GetIndex()))
6670 m_bSelectTo = false;
6671 m_pActiveShell->GetView().GetEditWin().GrabFocus();
6672 return;
6676 m_nLastGotoContentWasOutlinePos = SwOutlineNodes::npos;
6677 m_sSelectedItem = "";
6679 m_pActiveShell->AssureStdMode();
6681 std::optional<SwPosition> oPosition;
6682 if (m_bSelectTo)
6683 oPosition.emplace(m_pActiveShell->GetCursor()->GetPoint()->nNode,
6684 m_pActiveShell->GetCursor()->GetPoint()->nContent);
6686 switch(m_nLastSelType = pCnt->GetParent()->GetType())
6688 case ContentTypeId::TEXTFIELD:
6690 m_pActiveShell->GotoFormatField(
6691 *static_cast<const SwTextFieldContent*>(pCnt)->GetFormatField());
6693 break;
6694 case ContentTypeId::OUTLINE :
6696 const SwOutlineNodes::size_type nPos =
6697 static_cast<const SwOutlineContent*>(pCnt)->GetOutlinePos();
6698 m_pActiveShell->GotoOutline(nPos);
6699 m_nLastGotoContentWasOutlinePos = nPos;
6701 break;
6702 case ContentTypeId::TABLE :
6704 m_pActiveShell->GotoTable(pCnt->GetName());
6706 break;
6707 case ContentTypeId::FRAME :
6708 case ContentTypeId::GRAPHIC :
6709 case ContentTypeId::OLE :
6711 m_pActiveShell->GotoFly(pCnt->GetName());
6713 break;
6714 case ContentTypeId::BOOKMARK:
6716 m_pActiveShell->StartAction();
6717 m_pActiveShell->GotoMark(pCnt->GetName());
6718 m_pActiveShell->EndAction();
6719 m_sSelectedItem = pCnt->GetName();
6721 // If the hidden title of SwNavigatorPanel was emptied via UNO XPanel interface,
6722 // store the name of the selected bookmark there. This allows to query the
6723 // selected bookmark using UNO e.g. in add-ons, i.e. to disambiguate when
6724 // multiple bookmarks are there on the selected text range.
6725 // Note: this is a workaround because getDialog() of XPanel is not implemented
6726 // for SwNavigatorPanel.
6727 rtl::Reference< SwXTextDocument > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
6729 Reference<frame::XController2> xController( xModel->getCurrentController(), uno::UNO_QUERY);
6730 if ( !xController.is() )
6731 break;
6733 Reference<ui::XSidebarProvider> xSidebarProvider = xController->getSidebar();
6734 if ( !xSidebarProvider.is() )
6735 break;
6737 Reference<ui::XDecks> xDecks = xSidebarProvider->getDecks();
6738 if ( !xDecks.is() )
6739 break;
6741 if (!xDecks->hasByName(u"NavigatorDeck"_ustr))
6742 break;
6744 Reference<ui::XDeck> xDeck ( xDecks->getByName(u"NavigatorDeck"_ustr), uno::UNO_QUERY);
6745 if ( !xDeck.is() )
6746 break;
6748 Reference<ui::XPanels> xPanels = xDeck->getPanels();
6749 if ( !xPanels.is() )
6750 break;
6752 if (xPanels->hasByName(u"SwNavigatorPanel"_ustr))
6754 Reference<ui::XPanel> xPanel ( xPanels->getByName(u"SwNavigatorPanel"_ustr), uno::UNO_QUERY);
6755 if ( !xPanel.is() || !xPanel->getTitle().isEmpty() )
6756 break;
6758 xPanel->setTitle( pCnt->GetName() );
6761 break;
6762 case ContentTypeId::REGION :
6764 m_pActiveShell->GotoRegion(pCnt->GetName());
6766 break;
6767 case ContentTypeId::URLFIELD:
6769 if(m_pActiveShell->GotoINetAttr(
6770 *static_cast<const SwURLFieldContent*>(pCnt)->GetINetAttr() ))
6772 m_pActiveShell->Right(SwCursorSkipMode::Chars, false, 1, false);
6775 break;
6776 case ContentTypeId::REFERENCE:
6778 m_pActiveShell->GotoRefMark(pCnt->GetName());
6780 break;
6781 case ContentTypeId::INDEX:
6783 const OUString& sName(pCnt->GetName());
6784 if (!m_pActiveShell->GotoNextTOXBase(&sName))
6785 m_pActiveShell->GotoPrevTOXBase(&sName);
6787 break;
6788 case ContentTypeId::POSTIT:
6789 m_pActiveShell->GotoFormatField(*static_cast<const SwPostItContent*>(pCnt)->GetPostIt());
6790 break;
6791 case ContentTypeId::DRAWOBJECT:
6793 m_pActiveShell->GotoDrawingObject(pCnt->GetName());
6795 break;
6796 case ContentTypeId::FOOTNOTE:
6797 case ContentTypeId::ENDNOTE:
6799 const SwTextFootnote* pFootnote =
6800 static_cast<const SwTextFootnoteContent*>(pCnt)->GetTextFootnote();
6801 if (!pFootnote)
6802 return;
6803 m_pActiveShell->GotoFootnoteAnchor(*pFootnote);
6805 break;
6806 default: break;
6809 if (m_bSelectTo)
6811 m_pActiveShell->SttCursorMove();
6812 while (m_pActiveShell->IsCursorInTable())
6814 m_pActiveShell->MoveTable(GotoCurrTable, fnTableStart);
6815 if (!m_pActiveShell->Left(SwCursorSkipMode::Chars, false, 1, false))
6816 break; // Table is at the beginning of the document. It can't be selected this way.
6818 m_pActiveShell->EndCursorMove();
6820 m_pActiveShell->AssureStdMode();
6822 m_pActiveShell->SetMark();
6823 m_pActiveShell->GetCursor()->GetMark()->nNode = oPosition->nNode;
6824 m_pActiveShell->GetCursor()->GetMark()->nContent = oPosition->nContent;
6825 m_pActiveShell->UpdateCursor();
6827 m_pActiveShell->GetView().GetEditWin().GrabFocus();
6829 m_bSelectTo = false;
6831 else
6833 if (m_pActiveShell->IsFrameSelected() || m_pActiveShell->GetSelectedObjCount())
6835 m_pActiveShell->HideCursor();
6836 m_pActiveShell->EnterSelFrameMode();
6839 SwView& rView = m_pActiveShell->GetView();
6840 rView.StopShellTimer();
6841 rView.GetPostItMgr()->SetActiveSidebarWin(nullptr);
6842 rView.GetEditWin().GrabFocus();
6844 // Assure cursor is in visible view area.
6845 // (tdf#147041) Always show the navigated outline at the top of the visible view area.
6846 if (pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE ||
6847 (!m_pActiveShell->IsCursorVisible() && !m_pActiveShell->IsFrameSelected() &&
6848 !m_pActiveShell->GetSelectedObjCount()))
6850 Point aPoint(rView.GetVisArea().getX(), m_pActiveShell->GetCursorDocPos().getY());
6851 rView.SetVisArea(aPoint);
6855 UpdateContentFunctionsToolbar();
6858 NaviContentBookmark::NaviContentBookmark() :
6859 m_nDocSh(0)
6863 NaviContentBookmark::NaviContentBookmark(OUString sURL, OUString sCrossRef, OUString aDesc,
6864 const SwDocShell* pDocSh) :
6865 m_sURL(std::move(sURL)),
6866 m_sCrossRef(std::move(sCrossRef)),
6867 m_aDescription(std::move(aDesc)),
6868 m_nDocSh(reinterpret_cast<sal_IntPtr>(pDocSh))
6872 void NaviContentBookmark::Copy( TransferDataContainer& rData ) const
6874 rtl_TextEncoding eSysCSet = osl_getThreadTextEncoding();
6875 OString sStrBuf(OUStringToOString(m_sURL, eSysCSet) + OStringChar(NAVI_BOOKMARK_DELIM) +
6876 OUStringToOString(m_sCrossRef, eSysCSet) + OStringChar(NAVI_BOOKMARK_DELIM) +
6877 OUStringToOString(m_aDescription, eSysCSet) + OStringChar(NAVI_BOOKMARK_DELIM) +
6878 OString::number(m_nDocSh));
6879 rData.CopyByteString(SotClipboardFormatId::SONLK, sStrBuf);
6882 bool NaviContentBookmark::Paste( const TransferableDataHelper& rData, const OUString& rsDesc )
6884 OUString sStr;
6885 bool bRet = rData.GetString( SotClipboardFormatId::SONLK, sStr );
6886 if( bRet )
6888 sal_Int32 nPos = 0;
6889 m_sURL = sStr.getToken(0, NAVI_BOOKMARK_DELIM, nPos );
6890 m_sCrossRef = sStr.getToken(0, NAVI_BOOKMARK_DELIM, nPos);
6891 m_aDescription = sStr.getToken(0, NAVI_BOOKMARK_DELIM, nPos );
6892 m_nDocSh = o3tl::toInt32(o3tl::getToken(sStr, 0, NAVI_BOOKMARK_DELIM, nPos ));
6893 if (!rsDesc.isEmpty())
6894 m_aDescription = rsDesc;
6896 return bRet;
6899 SwNavigationPI* SwContentTree::GetParentWindow()
6901 return m_pDialog;
6904 void SwContentTree::SelectContentType(std::u16string_view rContentTypeName)
6906 std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator());
6907 if (!m_xTreeView->get_iter_first(*xIter))
6908 return;
6911 if (m_xTreeView->get_text(*xIter) == rContentTypeName)
6913 m_xTreeView->set_cursor(*xIter);
6914 UpdateContentFunctionsToolbar();
6915 break;
6917 } while (m_xTreeView->iter_next_sibling(*xIter));
6920 IMPL_LINK_NOARG(SwContentTree, OverlayObjectDelayTimerHdl, Timer *, void)
6922 m_aOverlayObjectDelayTimer.Stop();
6923 if (m_xOverlayObject)
6925 if (SdrView* pView = m_pActiveShell->GetDrawView())
6927 if (SdrPaintWindow* pPaintWindow = pView->GetPaintWindow(0))
6929 const rtl::Reference<sdr::overlay::OverlayManager>& xOverlayManager =
6930 pPaintWindow->GetOverlayManager();
6931 xOverlayManager->add(*m_xOverlayObject);
6937 void SwContentTree::OverlayObject(std::vector<basegfx::B2DRange>&& aRanges)
6939 m_aOverlayObjectDelayTimer.Stop();
6940 if (m_xOverlayObject && m_xOverlayObject->getOverlayManager())
6941 m_xOverlayObject->getOverlayManager()->remove(*m_xOverlayObject);
6942 if (aRanges.empty())
6943 m_xOverlayObject.reset();
6944 else
6946 m_xOverlayObject.reset(new sdr::overlay::OverlaySelection(
6947 sdr::overlay::OverlayType::Invert,
6948 Color(), std::move(aRanges), true/*unused for Invert type*/));
6949 m_aOverlayObjectDelayTimer.Start();
6953 void SwContentTree::BringEntryToAttention(const weld::TreeIter& rEntry)
6955 if (lcl_IsContent(rEntry, *m_xTreeView)) // content entry
6957 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(rEntry));
6958 if (pCnt->IsInvisible())
6959 OverlayObject();
6960 else
6962 const ContentTypeId nType = pCnt->GetParent()->GetType();
6963 if (nType == ContentTypeId::OUTLINE)
6965 BringTypesWithFlowFramesToAttention({m_pActiveShell->GetNodes().
6966 GetOutLineNds()[static_cast<SwOutlineContent*>(pCnt)->GetOutlinePos()]},
6967 /*bIncludeTopMargin*/ false);
6969 else if (nType == ContentTypeId::TABLE)
6971 if (const sw::TableFrameFormats* pFrameFormats = m_pActiveShell->GetDoc()->GetTableFrameFormats())
6972 if (const SwTableFormat* pFrameFormat = pFrameFormats->FindFrameFormatByName(pCnt->GetName()))
6974 SwTable* pTable = SwTable::FindTable(pFrameFormat);
6975 if (pTable)
6976 BringTypesWithFlowFramesToAttention({pTable->GetTableNode()}, false);
6979 else if (nType == ContentTypeId::FRAME || nType == ContentTypeId::GRAPHIC ||
6980 nType == ContentTypeId::OLE)
6982 SwNodeType eNodeType = SwNodeType::Text;
6983 if(nType == ContentTypeId::GRAPHIC)
6984 eNodeType = SwNodeType::Grf;
6985 else if(nType == ContentTypeId::OLE)
6986 eNodeType = SwNodeType::Ole;
6987 if (const SwFrameFormat* pFrameFormat =
6988 m_pActiveShell->GetDoc()->FindFlyByName(pCnt->GetName(), eNodeType))
6989 BringFramesToAttention(std::vector<const SwFrameFormat*> {pFrameFormat});
6991 else if (nType == ContentTypeId::BOOKMARK)
6993 BringBookmarksToAttention(std::vector<OUString> {pCnt->GetName()});
6995 else if (nType == ContentTypeId::REGION || nType == ContentTypeId::INDEX)
6997 size_t nSectionFormatCount = m_pActiveShell->GetSectionFormatCount();
6998 for (size_t i = 0; i < nSectionFormatCount; ++i)
7000 const SwSectionFormat& rSectionFormat = m_pActiveShell->GetSectionFormat(i);
7001 if (!rSectionFormat.IsInNodesArr())
7002 continue;
7003 const SwSection* pSection = rSectionFormat.GetSection();
7004 if (!pSection)
7005 continue;
7006 if (pCnt->GetName() == pSection->GetSectionName())
7008 BringTypesWithFlowFramesToAttention({rSectionFormat.GetSectionNode()});
7009 break;
7013 else if (nType == ContentTypeId::URLFIELD)
7015 // tdf#159147 - Assure the SwURLFieldContent::SwTextINetFormat pointer is valid
7016 // before bringing to attention.
7017 const SwTextINetFormat* pTextINetFormat
7018 = static_cast<SwURLFieldContent*>(pCnt)->GetINetAttr();
7019 const SwCharFormats* pFormats = m_pActiveShell->GetDoc()->GetCharFormats();
7020 for (auto n = pFormats->size(); 1 < n;)
7022 SwIterator<SwTextINetFormat, SwCharFormat> aIter(*(*pFormats)[--n]);
7023 for (SwTextINetFormat* pFnd = aIter.First(); pFnd; pFnd = aIter.Next())
7025 if (pTextINetFormat == pFnd)
7027 BringURLFieldsToAttention(SwGetINetAttrs {SwGetINetAttr(pCnt->GetName(),
7028 *pTextINetFormat)});
7029 return;
7034 else if (nType == ContentTypeId::REFERENCE)
7036 if (const SwTextAttr* pTextAttr =
7037 m_pActiveShell->GetDoc()->GetRefMark(pCnt->GetName())->GetTextRefMark())
7039 std::vector<const SwTextAttr*> aTextAttrArr {pTextAttr};
7040 BringReferencesToAttention(aTextAttrArr);
7043 else if (nType == ContentTypeId::POSTIT)
7045 if (const SwTextAttr* pTextAttr =
7046 static_cast<SwPostItContent*>(pCnt)->GetPostIt()->GetTextField())
7048 std::vector<const SwTextAttr*> aTextAttrArr {pTextAttr};
7049 BringPostItFieldsToAttention(aTextAttrArr);
7052 else if (nType == ContentTypeId::DRAWOBJECT)
7054 std::vector<const SdrObject*> aSdrObjectArr {GetDrawingObjectsByContent(pCnt)};
7055 BringDrawingObjectsToAttention(aSdrObjectArr);
7057 else if (nType == ContentTypeId::TEXTFIELD)
7059 if (const SwTextAttr* pTextAttr =
7060 static_cast<SwTextFieldContent*>(pCnt)->GetFormatField()->GetTextField())
7062 std::vector<const SwTextAttr*> aTextAttrArr {pTextAttr};
7063 BringTextFieldsToAttention(aTextAttrArr);
7066 else if (nType == ContentTypeId::FOOTNOTE || nType == ContentTypeId::ENDNOTE)
7068 if (const SwTextAttr* pTextAttr =
7069 static_cast<SwTextFootnoteContent*> (pCnt)->GetTextFootnote())
7071 std::vector<const SwTextAttr*> aTextAttrArr {pTextAttr};
7072 BringFootnotesToAttention(aTextAttrArr);
7077 else // content type entry
7079 SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(rEntry));
7080 if (pCntType->GetMemberCount() == 0)
7081 OverlayObject();
7082 else
7084 const ContentTypeId nType = pCntType->GetType();
7085 if (nType == ContentTypeId::OUTLINE)
7087 std::vector<const SwNode*> aNodesArr(
7088 m_pActiveShell->GetNodes().GetOutLineNds().begin(),
7089 m_pActiveShell->GetNodes().GetOutLineNds().end());
7090 BringTypesWithFlowFramesToAttention(aNodesArr, /*bIncludeTopMargin*/ false);
7092 else if (nType == ContentTypeId::TABLE)
7094 std::vector<const SwNode*> aNodesArr;
7095 const size_t nCount = m_pActiveShell->GetTableFrameFormatCount(false);
7096 const sw::TableFrameFormats& rTableFormats = *m_pActiveShell->GetDoc()->GetTableFrameFormats();
7097 for(size_t i = 0; i < nCount; ++i)
7099 if (const SwTableFormat* pTableFormat = rTableFormats[i])
7100 if(pTableFormat->IsUsed()) // skip deleted tables
7102 SwTable* pTable = SwTable::FindTable(pTableFormat);
7103 if (pTable)
7104 aNodesArr.push_back(pTable->GetTableNode());
7107 BringTypesWithFlowFramesToAttention(aNodesArr, false);
7109 else if (nType == ContentTypeId::FRAME || nType == ContentTypeId::GRAPHIC ||
7110 nType == ContentTypeId::OLE)
7112 FlyCntType eType = FLYCNTTYPE_FRM;
7113 if(nType == ContentTypeId::GRAPHIC)
7114 eType = FLYCNTTYPE_GRF;
7115 else if(nType == ContentTypeId::OLE)
7116 eType = FLYCNTTYPE_OLE;
7117 BringFramesToAttention(m_pActiveShell->GetFlyFrameFormats(eType, true));
7119 else if (nType == ContentTypeId::BOOKMARK)
7121 std::vector<OUString> aNames;
7122 const auto nCount = pCntType->GetMemberCount();
7123 for (size_t i = 0; i < nCount; i++)
7125 const SwContent* pMember = pCntType->GetMember(i);
7126 if (pMember && !pMember->IsInvisible())
7127 aNames.push_back(pMember->GetName());
7129 BringBookmarksToAttention(aNames);
7131 else if (nType == ContentTypeId::REGION || nType == ContentTypeId::INDEX)
7133 std::vector<const SwNode*> aNodesArr;
7134 const SwSectionFormats& rFormats = m_pActiveShell->GetDoc()->GetSections();
7135 const size_t nSize = rFormats.size();
7136 for (SwSectionFormats::size_type n = nSize; n;)
7138 const SwSectionFormat* pSectionFormat = rFormats[--n];
7139 if (pSectionFormat && pSectionFormat->IsInNodesArr())
7141 const SwSection* pSection = pSectionFormat->GetSection();
7142 if (pSection && !pSection->IsHiddenFlag())
7144 const SectionType eSectionType = pSection->GetType();
7145 if (nType == ContentTypeId::REGION &&
7146 (eSectionType == SectionType::ToxContent ||
7147 eSectionType == SectionType::ToxHeader))
7148 continue;
7149 if (nType == ContentTypeId::INDEX &&
7150 eSectionType != SectionType::ToxContent)
7151 continue;
7152 if (const SwNode* pNode = pSectionFormat->GetSectionNode())
7153 aNodesArr.push_back(pNode);
7157 BringTypesWithFlowFramesToAttention(aNodesArr);
7159 else if (nType == ContentTypeId::URLFIELD)
7161 SwGetINetAttrs aINetAttrsArr;
7162 m_pActiveShell->GetINetAttrs(aINetAttrsArr, false);
7163 BringURLFieldsToAttention(aINetAttrsArr);
7165 else if (nType == ContentTypeId::REFERENCE)
7167 std::vector<const SwTextAttr*> aTextAttrArr;
7168 m_pActiveShell->GetDoc()->ForEachRefMark(
7169 [&aTextAttrArr] (const SwFormatRefMark& rRefMark) -> bool
7171 const SwTextRefMark* pTextRef = rRefMark.GetTextRefMark();
7172 if (pTextRef)
7173 aTextAttrArr.push_back(pTextRef);
7174 return true;
7176 BringReferencesToAttention(aTextAttrArr);
7178 else if (nType == ContentTypeId::POSTIT)
7180 std::vector<const SwTextAttr*> aTextAttrArr;
7181 const auto nCount = pCntType->GetMemberCount();
7182 for (size_t i = 0; i < nCount; i++)
7184 const SwPostItContent* pPostItContent = static_cast<const SwPostItContent*>(
7185 pCntType->GetMember(i));
7186 if (pPostItContent && !pPostItContent->IsInvisible())
7187 if (const SwFormatField* pFormatField = pPostItContent->GetPostIt())
7188 if (const SwTextAttr* pTextAttr = pFormatField->GetTextField())
7189 aTextAttrArr.push_back(pTextAttr);
7191 BringPostItFieldsToAttention(aTextAttrArr);
7193 else if (nType == ContentTypeId::DRAWOBJECT)
7195 IDocumentDrawModelAccess& rIDDMA = m_pActiveShell->getIDocumentDrawModelAccess();
7196 if (const SwDrawModel* pModel = rIDDMA.GetDrawModel())
7198 if (const SdrPage* pPage = pModel->GetPage(0))
7200 if (pPage->GetObjCount())
7202 std::vector<const SdrObject*> aSdrObjectArr;
7203 for (const rtl::Reference<SdrObject>& pObject : *pPage)
7205 if (pObject && !pObject->GetName().isEmpty() &&
7206 rIDDMA.IsVisibleLayerId(pObject->GetLayer()))
7207 aSdrObjectArr.push_back(pObject.get());
7209 BringDrawingObjectsToAttention(aSdrObjectArr);
7214 else if (nType == ContentTypeId::TEXTFIELD)
7216 std::vector<const SwTextAttr*> aTextAttrArr;
7217 const auto nCount = pCntType->GetMemberCount();
7218 for (size_t i = 0; i < nCount; i++)
7220 const SwTextFieldContent* pTextFieldCnt =
7221 static_cast<const SwTextFieldContent*>(pCntType->GetMember(i));
7222 if (pTextFieldCnt && !pTextFieldCnt->IsInvisible())
7223 if (const SwFormatField* pFormatField = pTextFieldCnt->GetFormatField())
7224 if (const SwTextAttr* pTextAttr = pFormatField->GetTextField())
7225 aTextAttrArr.push_back(pTextAttr);
7227 BringTextFieldsToAttention(aTextAttrArr);
7229 else if (nType == ContentTypeId::FOOTNOTE || nType == ContentTypeId::ENDNOTE)
7231 std::vector<const SwTextAttr*> aTextAttrArr;
7232 const auto nCount = pCntType->GetMemberCount();
7233 for (size_t i = 0; i < nCount; i++)
7235 const SwTextFootnoteContent* pTextFootnoteCnt =
7236 static_cast<const SwTextFootnoteContent*>(pCntType->GetMember(i));
7237 if (pTextFootnoteCnt && !pTextFootnoteCnt->IsInvisible())
7238 if (const SwTextAttr* pTextAttr = pTextFootnoteCnt->GetTextFootnote())
7239 aTextAttrArr.push_back(pTextAttr);
7241 BringFootnotesToAttention(aTextAttrArr);
7247 static void lcl_CalcOverlayRanges(const SwTextFrame* pStartFrame, const SwTextFrame* pEndFrame,
7248 const SwPosition& aStartPos, const SwPosition& aEndPos,
7249 std::vector<basegfx::B2DRange>& aRanges)
7251 if (pStartFrame && pEndFrame)
7253 SwRect aStartCharRect;
7254 pStartFrame->GetCharRect(aStartCharRect, aStartPos);
7255 SwRect aEndCharRect;
7256 pEndFrame->GetCharRect(aEndCharRect, aEndPos);
7257 if (aStartCharRect.Top() == aEndCharRect.Top())
7259 // single line range
7260 aRanges.emplace_back(aStartCharRect.Left(), aStartCharRect.Top(),
7261 aEndCharRect.Right() + 1, aEndCharRect.Bottom() + 1);
7263 else
7265 // multi line range
7266 SwRect aFrameRect = pStartFrame->getFrameArea();
7267 aRanges.emplace_back(aStartCharRect.Left(), aStartCharRect.Top(),
7268 aFrameRect.Right(), aStartCharRect.Bottom() + 1);
7269 if (aStartCharRect.Bottom() + 1 != aEndCharRect.Top())
7270 aRanges.emplace_back(aFrameRect.Left(), aStartCharRect.Bottom() + 1,
7271 aFrameRect.Right(), aEndCharRect.Top() + 1);
7272 aRanges.emplace_back(aFrameRect.Left(), aEndCharRect.Top() + 1,
7273 aEndCharRect.Right() + 1, aEndCharRect.Bottom() + 1);
7278 void SwContentTree::BringFramesToAttention(const std::vector<const SwFrameFormat*>& rFrameFormats)
7280 std::vector<basegfx::B2DRange> aRanges;
7281 for (const SwFrameFormat* pFrameFormat : rFrameFormats)
7283 if (!pFrameFormat)
7284 continue;
7285 SwRect aFrameRect = pFrameFormat->FindLayoutRect();
7286 if (!aFrameRect.IsEmpty())
7287 aRanges.emplace_back(aFrameRect.Left(), aFrameRect.Top(), aFrameRect.Right(),
7288 aFrameRect.Bottom());
7290 OverlayObject(std::move(aRanges));
7293 void SwContentTree::BringBookmarksToAttention(const std::vector<OUString>& rNames)
7295 std::vector<basegfx::B2DRange> aRanges;
7296 IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess();
7297 for (const auto& rName : rNames)
7299 auto ppBkmk = pMarkAccess->findBookmark(rName);
7300 if (ppBkmk == pMarkAccess->getBookmarksEnd())
7301 continue;
7302 SwPosition aMarkStart = (*ppBkmk)->GetMarkStart();
7303 const SwTextNode* pMarkStartTextNode = aMarkStart.GetNode().GetTextNode();
7304 if (!pMarkStartTextNode)
7305 continue;
7306 const SwTextFrame* pMarkStartFrame = static_cast<const SwTextFrame*>(
7307 pMarkStartTextNode->getLayoutFrame(m_pActiveShell->GetLayout()));
7308 if (!pMarkStartFrame)
7309 continue;
7310 SwPosition aMarkEnd = (*ppBkmk)->GetMarkEnd();
7311 const SwTextNode* pMarkEndTextNode = aMarkEnd.GetNode().GetTextNode();
7312 if (!pMarkEndTextNode)
7313 continue;
7314 const SwTextFrame* pMarkEndFrame = static_cast<const SwTextFrame*>(
7315 pMarkEndTextNode->getLayoutFrame(m_pActiveShell->GetLayout()));
7316 if (!pMarkEndFrame)
7317 continue;
7318 // adjust span when mark start equals mark end
7319 if (aMarkStart == aMarkEnd)
7321 if (aMarkEnd.GetContentIndex() < pMarkEndTextNode->Len() - 1)
7322 aMarkEnd.AdjustContent(+1);
7323 else if (aMarkStart.GetContentIndex() > 0)
7324 aMarkStart.AdjustContent(-1);
7326 lcl_CalcOverlayRanges(pMarkStartFrame, pMarkEndFrame, aMarkStart, aMarkEnd, aRanges);
7328 OverlayObject(std::move(aRanges));
7331 void SwContentTree::BringTypesWithFlowFramesToAttention(const std::vector<const SwNode*>& rNodes,
7332 const bool bIncludeTopMargin)
7334 std::vector<basegfx::B2DRange> aRanges;
7335 for (const auto* pNode : rNodes)
7337 if (!pNode)
7338 continue;
7339 const SwFrame* pFrame;
7340 if (pNode->IsContentNode() || pNode->IsTableNode())
7342 if (pNode->IsContentNode())
7343 pFrame = pNode->GetContentNode()->getLayoutFrame(m_pActiveShell->GetLayout());
7344 else // table node
7346 SwNode2Layout aTmp(*pNode, pNode->GetIndex() - 1);
7347 pFrame = aTmp.NextFrame();
7349 while (pFrame)
7351 const SwRect& rFrameRect = pFrame->getFrameArea();
7352 if (!rFrameRect.IsEmpty())
7353 aRanges.emplace_back(rFrameRect.Left(), bIncludeTopMargin ? rFrameRect.Top() :
7354 rFrameRect.Top() + pFrame->GetTopMargin(),
7355 rFrameRect.Right(), rFrameRect.Bottom());
7356 if (!pFrame->IsFlowFrame())
7357 break;
7358 const SwFlowFrame *pFollow = SwFlowFrame::CastFlowFrame(pFrame)->GetFollow();
7359 if (!pFollow)
7360 break;
7361 pFrame = &pFollow->GetFrame();
7364 else if (pNode->IsSectionNode())
7366 const SwNode* pEndOfSectionNode = pNode->EndOfSectionNode();
7367 SwNodeIndex aIdx(*pNode);
7368 while (&aIdx.GetNode() != pEndOfSectionNode)
7370 if (aIdx.GetNode().IsContentNode())
7372 if ((pFrame = aIdx.GetNode().GetContentNode()->
7373 getLayoutFrame(m_pActiveShell->GetLayout())))
7375 if (pFrame->IsInSct())
7376 pFrame = pFrame->FindSctFrame();
7377 if (pFrame)
7379 const SwRect& rFrameRect = pFrame->getFrameArea();
7380 if (!rFrameRect.IsEmpty())
7381 aRanges.emplace_back(rFrameRect.Left(), rFrameRect.Top(),
7382 rFrameRect.Right(), rFrameRect.Bottom());
7385 ++aIdx;
7386 while (!aIdx.GetNode().IsEndNode() && !aIdx.GetNode().IsSectionNode())
7387 ++aIdx;
7388 continue;
7390 if (!aIdx.GetNode().IsSectionNode())
7392 ++aIdx;
7393 continue;
7395 SwNode2Layout aTmp(aIdx.GetNode(), aIdx.GetNode().GetIndex() - 1);
7396 pFrame = aTmp.NextFrame();
7397 if (pFrame)
7399 if (!pFrame->getFrameArea().IsEmpty())
7401 const SwRect& rFrameRect = pFrame->getFrameArea();
7402 aRanges.emplace_back(rFrameRect.Left(), rFrameRect.Top(),
7403 rFrameRect.Right(), rFrameRect.Bottom());
7405 if (pFrame->IsSctFrame())
7407 const SwSectionFrame* pSectionFrame
7408 = static_cast<const SwSectionFrame*>(pFrame);
7409 if (pSectionFrame->HasFollow())
7411 const SwFlowFrame *pFollow
7412 = SwFlowFrame::CastFlowFrame(pSectionFrame)->GetFollow();
7413 while (pFollow)
7415 pFrame = &pFollow->GetFrame();
7416 if (!pFrame->getFrameArea().IsEmpty())
7418 const SwRect& rFrameRect = pFrame->getFrameArea();
7419 aRanges.emplace_back(rFrameRect.Left(), rFrameRect.Top(),
7420 rFrameRect.Right(), rFrameRect.Bottom());
7422 pFollow = SwFlowFrame::CastFlowFrame(pFrame)->GetFollow();
7427 ++aIdx;
7428 while (!aIdx.GetNode().IsEndNode() && !aIdx.GetNode().IsSectionNode())
7429 ++aIdx;
7431 // Remove nested sections. This wouldn't be needed if the overlay wasn't invert type.
7432 auto end = aRanges.end();
7433 for (auto it = aRanges.begin(); it != end; ++it)
7434 end = std::remove_if(it + 1, end, [&it](auto itt){ return it->isInside(itt); });
7435 aRanges.erase(end, aRanges.end());
7438 OverlayObject(std::move(aRanges));
7441 void SwContentTree::BringURLFieldsToAttention(const SwGetINetAttrs& rINetAttrsArr)
7443 std::vector<basegfx::B2DRange> aRanges;
7444 for (const auto& r : rINetAttrsArr)
7446 const SwTextNode& rTextNode = r.rINetAttr.GetTextNode();
7447 if (SwTextFrame* pFrame = static_cast<SwTextFrame*>(
7448 rTextNode.getLayoutFrame(m_pActiveShell->GetLayout())))
7450 auto nStart = r.rINetAttr.GetStart();
7451 auto nEnd = r.rINetAttr.GetAnyEnd();
7452 SwPosition aStartPos(rTextNode, nStart), aEndPos(rTextNode, nEnd);
7453 lcl_CalcOverlayRanges(pFrame, pFrame, aStartPos, aEndPos, aRanges);
7456 OverlayObject(std::move(aRanges));
7459 void SwContentTree::BringReferencesToAttention(std::vector<const SwTextAttr*>& rTextAttrsArr)
7461 std::vector<basegfx::B2DRange> aRanges;
7462 for (const SwTextAttr* p : rTextAttrsArr)
7464 if (!p)
7465 continue;
7466 const SwTextRefMark* pTextRefMark = p->GetRefMark().GetTextRefMark();
7467 if (!pTextRefMark)
7468 continue;
7469 const SwTextNode& rTextNode = pTextRefMark->GetTextNode();
7470 if (SwTextFrame* pFrame = static_cast<SwTextFrame*>(
7471 rTextNode.getLayoutFrame(m_pActiveShell->GetLayout())))
7473 auto nStart = p->GetStart();
7474 auto nEnd = p->GetAnyEnd();
7475 SwPosition aStartPos(rTextNode, nStart), aEndPos(rTextNode, nEnd);
7476 lcl_CalcOverlayRanges(pFrame, pFrame, aStartPos, aEndPos, aRanges);
7479 OverlayObject(std::move(aRanges));
7482 void SwContentTree::BringPostItFieldsToAttention(std::vector<const SwTextAttr*>& rTextAttrsArr)
7484 std::vector<basegfx::B2DRange> aRanges;
7485 for (const SwTextAttr* p : rTextAttrsArr)
7487 if (!p)
7488 continue;
7489 const SwTextField* pTextField = p->GetFormatField().GetTextField();
7490 if (!pTextField)
7491 continue;
7492 // use as a fallback when there is no mark
7493 SwTextNode& rTextNode = pTextField->GetTextNode();
7494 if (!rTextNode.getLayoutFrame(m_pActiveShell->GetLayout()))
7495 continue;
7496 assert(dynamic_cast<const SwTextAnnotationField*>(pTextField));
7497 const SwTextAnnotationField* pTextAnnotationField =
7498 static_cast<const SwTextAnnotationField*>(pTextField);
7499 const ::sw::mark::MarkBase* pAnnotationMark = pTextAnnotationField->GetAnnotationMark();
7500 const SwPosition aMarkStart = pAnnotationMark ? pAnnotationMark->GetMarkStart()
7501 : SwPosition(rTextNode, p->GetStart());
7502 const SwPosition aMarkEnd = pAnnotationMark ? pAnnotationMark->GetMarkEnd()
7503 : SwPosition(rTextNode, p->GetAnyEnd());
7504 const SwTextFrame* pMarkStartFrame = static_cast<SwTextFrame*>(
7505 aMarkStart.GetNode().GetTextNode()->getLayoutFrame(m_pActiveShell->GetLayout()));
7506 const SwTextFrame* pMarkEndFrame = static_cast<SwTextFrame*>(
7507 aMarkEnd.GetNode().GetTextNode()->getLayoutFrame(m_pActiveShell->GetLayout()));
7508 if (!pMarkStartFrame || !pMarkEndFrame)
7509 continue;
7510 lcl_CalcOverlayRanges(pMarkStartFrame, pMarkEndFrame, aMarkStart,
7511 aMarkEnd, aRanges);
7513 OverlayObject(std::move(aRanges));
7516 void SwContentTree::BringFootnotesToAttention(std::vector<const SwTextAttr*>& rTextAttrsArr)
7518 std::vector<basegfx::B2DRange> aRanges;
7519 for (const SwTextAttr* p : rTextAttrsArr)
7521 if (!p)
7522 continue;
7523 const SwTextFootnote* pTextFootnote = p->GetFootnote().GetTextFootnote();
7524 if (!pTextFootnote)
7525 continue;
7526 const SwTextNode& rTextNode = pTextFootnote->GetTextNode();
7527 if (SwTextFrame* pFrame = static_cast<SwTextFrame*>(
7528 rTextNode.getLayoutFrame(m_pActiveShell->GetLayout())))
7530 auto nStart = p->GetStart();
7531 auto nEnd = nStart + 1;
7532 SwPosition aStartPos(rTextNode, nStart), aEndPos(rTextNode, nEnd);
7533 lcl_CalcOverlayRanges(pFrame, pFrame, aStartPos, aEndPos, aRanges);
7536 OverlayObject(std::move(aRanges));
7539 void SwContentTree::BringDrawingObjectsToAttention(std::vector<const SdrObject*>& rDrawingObjectsArr)
7541 std::vector<basegfx::B2DRange> aRanges;
7542 for (const SdrObject* pObject : rDrawingObjectsArr)
7544 if (pObject)
7546 tools::Rectangle aRect(pObject->GetLogicRect());
7547 if (!aRect.IsEmpty())
7548 aRanges.emplace_back(aRect.Left(), aRect.Top(), aRect.Right(), aRect.Bottom());
7551 OverlayObject(std::move(aRanges));
7554 void SwContentTree::BringTextFieldsToAttention(std::vector<const SwTextAttr*>& rTextAttrsArr)
7556 std::vector<basegfx::B2DRange> aRanges;
7557 std::shared_ptr<SwPaM> pPamForTextField;
7558 for (const SwTextAttr* p : rTextAttrsArr)
7560 if (!p)
7561 continue;
7562 const SwTextField* pTextField = p->GetFormatField().GetTextField();
7563 if (!pTextField)
7564 continue;
7565 if (SwTextFrame* pFrame = static_cast<SwTextFrame*>(
7566 pTextField->GetTextNode().getLayoutFrame(m_pActiveShell->GetLayout())))
7568 SwTextField::GetPamForTextField(*pTextField, pPamForTextField);
7569 if (!pPamForTextField)
7570 continue;
7571 SwPosition aStartPos(*pPamForTextField->GetMark());
7572 SwPosition aEndPos(*pPamForTextField->GetPoint());
7573 lcl_CalcOverlayRanges(pFrame, pFrame, aStartPos, aEndPos, aRanges);
7576 OverlayObject(std::move(aRanges));
7579 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */