LibreOffice Module sw (master) 1
content.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <comphelper/string.hxx>
22#include <svl/urlbmk.hxx>
23#include <osl/thread.h>
24#include <sal/log.hxx>
25#include <tools/urlobj.hxx>
26#include <sfx2/docfile.hxx>
27#include <sfx2/dispatch.hxx>
28#include <sfx2/event.hxx>
29#include <sfx2/viewfrm.hxx>
30#include <o3tl/enumrange.hxx>
32#include <utility>
33#include <vcl/commandevent.hxx>
34#include <vcl/weldutils.hxx>
35#include <sot/formats.hxx>
36#include <o3tl/string_view.hxx>
37#include <uiitems.hxx>
38#include <fmtanchr.hxx>
39#include <fmtinfmt.hxx>
40#include <txtinet.hxx>
41#include <fmtfld.hxx>
42#include <swmodule.hxx>
43#include <wrtsh.hxx>
44#include <view.hxx>
45#include <docsh.hxx>
46#include <drawdoc.hxx>
47#include <content.hxx>
48#include <frmatr.hxx>
49#include <frmfmt.hxx>
50#include <fldbas.hxx>
51#include <IMark.hxx>
52#include <section.hxx>
53#include <tox.hxx>
54#include <navipi.hxx>
55#include <navicont.hxx>
56#include <navicfg.hxx>
57#include <edtwin.hxx>
58#include <doc.hxx>
62#include <unotxvw.hxx>
63#include <cmdid.h>
64#include <helpids.h>
65#include <strings.hrc>
66#include <com/sun/star/text/XTextSectionsSupplier.hpp>
67#include <com/sun/star/text/XTextGraphicObjectsSupplier.hpp>
68#include <com/sun/star/text/XTextTablesSupplier.hpp>
69#include <com/sun/star/text/XDocumentIndexesSupplier.hpp>
70#include <com/sun/star/text/XDocumentIndex.hpp>
71#include <com/sun/star/text/XBookmarksSupplier.hpp>
72#include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp>
73#include <com/sun/star/text/XTextFramesSupplier.hpp>
74#include <svx/svdpage.hxx>
75#include <svx/svdview.hxx>
76#include <SwRewriter.hxx>
77#include <hints.hxx>
78#include <numrule.hxx>
79#include <swundo.hxx>
80#include <ndtxt.hxx>
81#include <PostItMgr.hxx>
82#include <postithelper.hxx>
83
84#include <swabstdlg.hxx>
85#include <bitmaps.hlst>
86
87#include <AnnotationWin.hxx>
88#include <memory>
89
90#include <fmtcntnt.hxx>
91#include <docstat.hxx>
92
93#include <viewopt.hxx>
94
96#include <txtfld.hxx>
97#include <fldmgr.hxx>
98
99#include <frameformats.hxx>
100
101#include <ftnidx.hxx>
102#include <txtftn.hxx>
103#include <fmtftn.hxx>
104
105#define CTYPE_CNT 0
106#define CTYPE_CTT 1
107
108using namespace ::std;
109using namespace ::com::sun::star;
110using namespace ::com::sun::star::text;
111using namespace ::com::sun::star::uno;
112using namespace ::com::sun::star::container;
113
114namespace {
115
116/*
117 Symbolic name representations of numeric values used for the Outline Content Visibility popup
118 menu item ids. The numbers are chosen arbitrarily to not over overlap other menu item ids.
119 see: SwContentTree::ExecuteContextMenuAction, navigatorcontextmenu.ui
120
121 1512 toggle outline content visibility of the selected outline entry
122 1513 make the outline content of the selected outline entry and children not visible
123 1514 make the outline content of the selected entry and children visible
124*/
125const sal_uInt32 TOGGLE_OUTLINE_CONTENT_VISIBILITY = 1512;
126const sal_uInt32 HIDE_OUTLINE_CONTENT_VISIBILITY = 1513;
127const sal_uInt32 SHOW_OUTLINE_CONTENT_VISIBILITY = 1514;
128
129constexpr char NAVI_BOOKMARK_DELIM = '\x01';
130
131}
132
134 : public o3tl::sorted_vector<std::unique_ptr<SwContent>, o3tl::less_uniqueptr_to<SwContent>,
135 o3tl::find_partialorder_ptrequals>
136{
137};
138
139namespace
140{
141 std::map<OUString, std::map<void*, bool>> lcl_DocOutLineExpandStateMap;
142
143 bool lcl_IsContent(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView)
144 {
145 return weld::fromId<const SwTypeNumber*>(rTreeView.get_id(rEntry))->GetTypeId() == CTYPE_CNT;
146 }
147
148 bool lcl_IsContentType(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView)
149 {
150 return weld::fromId<const SwTypeNumber*>(rTreeView.get_id(rEntry))->GetTypeId() == CTYPE_CTT;
151 }
152
153 bool lcl_IsLowerOutlineContent(const weld::TreeIter& rEntry, const weld::TreeView& rTreeView, sal_uInt8 nLevel)
154 {
155 return weld::fromId<const SwOutlineContent*>(rTreeView.get_id(rEntry))->GetOutlineLevel() < nLevel;
156 }
157
158 bool lcl_FindShell(SwWrtShell const * pShell)
159 {
160 bool bFound = false;
162 while (pView)
163 {
164 if(pShell == &pView->GetWrtShell())
165 {
166 bFound = true;
167 break;
168 }
169 pView = SwModule::GetNextView(pView);
170 }
171 return bFound;
172 }
173
174 bool lcl_IsUiVisibleBookmark(const ::sw::mark::IMark* pMark)
175 {
177 }
178
179 OUString lcl_GetFootnoteText(const SwTextFootnote& rTextFootnote)
180 {
181 SwNodeIndex aIdx(*rTextFootnote.GetStartNode(), 1);
182 SwContentNode* pCNd = aIdx.GetNode().GetTextNode();
183 if(!pCNd)
184 pCNd = aIdx.GetNodes().GoNext(&aIdx);
185 return pCNd->IsTextNode() ? static_cast<SwTextNode*>(pCNd)->GetText() : OUString();
186 }
187}
188
189// Content, contains names and reference at the content type.
190
191SwContent::SwContent(const SwContentType* pCnt, OUString aName, double nYPos) :
193 m_pParent(pCnt),
194 m_sContentName(std::move(aName)),
195 m_nYPosition(nYPos),
196 m_bInvisible(false)
197{
198}
199
200
202{
203}
204
206{
207 return false;
208}
209
211{
212 return m_pFormatField->IsProtect();
213}
214
216{
217 return m_pField->IsProtect();
218}
219
221{
222 return m_pINetAttr->IsProtect();
223}
224
226{
227}
228
230{
231}
232
234{
235 STR_CONTENT_TYPE_OUTLINE,
236 STR_CONTENT_TYPE_TABLE,
237 STR_CONTENT_TYPE_FRAME,
238 STR_CONTENT_TYPE_GRAPHIC,
239 STR_CONTENT_TYPE_OLE,
240 STR_CONTENT_TYPE_BOOKMARK,
241 STR_CONTENT_TYPE_REGION,
242 STR_CONTENT_TYPE_URLFIELD,
243 STR_CONTENT_TYPE_REFERENCE,
244 STR_CONTENT_TYPE_INDEX,
245 STR_CONTENT_TYPE_POSTIT,
246 STR_CONTENT_TYPE_DRAWOBJECT,
247 STR_CONTENT_TYPE_TEXTFIELD,
248 STR_CONTENT_TYPE_FOOTNOTE,
249 STR_CONTENT_TYPE_ENDNOTE
250};
251
253{
254 STR_CONTENT_TYPE_SINGLE_OUTLINE,
255 STR_CONTENT_TYPE_SINGLE_TABLE,
256 STR_CONTENT_TYPE_SINGLE_FRAME,
257 STR_CONTENT_TYPE_SINGLE_GRAPHIC,
258 STR_CONTENT_TYPE_SINGLE_OLE,
259 STR_CONTENT_TYPE_SINGLE_BOOKMARK,
260 STR_CONTENT_TYPE_SINGLE_REGION,
261 STR_CONTENT_TYPE_SINGLE_URLFIELD,
262 STR_CONTENT_TYPE_SINGLE_REFERENCE,
263 STR_CONTENT_TYPE_SINGLE_INDEX,
264 STR_CONTENT_TYPE_SINGLE_POSTIT,
265 STR_CONTENT_TYPE_SINGLE_DRAWOBJECT,
266 STR_CONTENT_TYPE_SINGLE_TEXTFIELD,
267 STR_CONTENT_TYPE_SINGLE_FOOTNOTE,
268 STR_CONTENT_TYPE_SINGLE_ENDNOTE
269};
270
271namespace
272{
273 bool checkVisibilityChanged(
274 const SwContentArr& rSwContentArrA,
275 const SwContentArr& rSwContentArrB)
276 {
277 if(rSwContentArrA.size() != rSwContentArrB.size())
278 {
279 return true;
280 }
281
282 for(size_t a(0); a < rSwContentArrA.size(); a++)
283 {
284 if(rSwContentArrA[a]->IsInvisible() != rSwContentArrB[a]->IsInvisible())
285 {
286 return true;
287 }
288 }
289
290 return false;
291 }
292// Gets "YPos" for content, i.e. a number used to sort content members in Navigator's list
293sal_Int32 getYPos(const SwNode& rNode)
294{
295 SwNodeOffset nIndex = rNode.GetIndex();
296 if (rNode.GetNodes().GetEndOfExtras().GetIndex() >= nIndex)
297 {
298 // Not a node of BodyText
299 // Are we in a fly?
300 if (const auto pFlyFormat = rNode.GetFlyFormat())
301 {
302 // Get node index of anchor
303 if (SwNode* pAnchorNode = pFlyFormat->GetAnchor().GetAnchorNode())
304 {
305 return getYPos(*pAnchorNode);
306 }
307 }
308 }
309 return sal_Int32(nIndex);
310}
311} // end of anonymous namespace
312
315 m_pWrtShell(pShell),
316 m_sContentTypeName(SwResId(STR_CONTENT_TYPE_ARY[static_cast<int>(nType)])),
317 m_sSingleContentTypeName(SwResId(STR_CONTENT_TYPE_SINGLE_ARY[static_cast<int>(nType)])),
318 m_nMemberCount(0),
319 m_nContentType(nType),
320 m_nOutlineLevel(nLevel),
321 m_bDataValid(false),
322 m_bEdit(false),
323 m_bDelete(true)
324{
325 switch(m_nContentType)
326 {
328 m_sTypeToken = "outline";
329 break;
331 m_sTypeToken = "table";
332 m_bEdit = true;
333 break;
335 m_sTypeToken = "frame";
336 m_bEdit = true;
337 break;
339 m_sTypeToken = "graphic";
340 m_bEdit = true;
341 break;
343 m_sTypeToken = "ole";
344 m_bEdit = true;
345 break;
347 m_bEdit = true;
348 m_bDelete = true;
349 break;
352 m_bEdit = true;
353 m_bDelete = false;
354 break;
356 {
357 const bool bProtectedBM = m_pWrtShell->getIDocumentSettingAccess().get(
359 m_bEdit = true;
360 m_bDelete = !bProtectedBM;
361 }
362 break;
364 m_sTypeToken = "region";
365 m_bEdit = true;
366 m_bDelete = false;
367 break;
369 m_bEdit = true;
370 m_bDelete = true;
371 break;
373 m_bEdit = false;
374 m_bDelete = false;
375 break;
377 m_bEdit = true;
378 m_bDelete = true;
379 break;
381 m_bEdit = true;
382 break;
384 m_bEdit = true;
385 break;
386 default: break;
387 }
389}
390
392{
393}
394
396{
397 if(!m_bDataValid || !m_pMember)
398 {
400 }
401 if(nIndex < m_pMember->size())
402 return (*m_pMember)[nIndex].get();
403
404 return nullptr;
405}
406
408{
409 m_bDataValid = false;
410}
411
412void SwContentType::FillMemberList(bool* pbContentChanged)
413{
414 std::unique_ptr<SwContentArr> pOldMember;
415 size_t nOldMemberCount = 0;
416 SwPtrMsgPoolItem aAskItem( RES_CONTENT_VISIBLE, nullptr );
417 if(m_pMember && pbContentChanged)
418 {
419 pOldMember = std::move(m_pMember);
420 nOldMemberCount = pOldMember->size();
421 m_pMember.reset( new SwContentArr );
422 *pbContentChanged = false;
423 }
424 else if(!m_pMember)
425 m_pMember.reset( new SwContentArr );
426 else
427 m_pMember->clear();
428 switch(m_nContentType)
429 {
431 {
432 const SwNodeOffset nEndOfExtrasIndex = m_pWrtShell->GetNodes().GetEndOfExtras().GetIndex();
433 // provide for up to 99999 outline nodes in frames to be sorted in document layout order
434 double nOutlinesInFramesIndexAdjustment = 0.00001;
435 const SwOutlineNodes& rOutlineNodes(m_pWrtShell->GetNodes().GetOutLineNds());
436 const size_t nOutlineCount = rOutlineNodes.size();
437
438 for (size_t i = 0; i < nOutlineCount; ++i)
439 {
440 SwTextNode* pNode = rOutlineNodes[i]->GetTextNode();
441 const sal_uInt8 nLevel = pNode->GetAttrOutlineLevel() - 1;
442 if (nLevel >= m_nOutlineLevel || !pNode->getLayoutFrame(m_pWrtShell->GetLayout()))
443 continue;
444 double nYPos = m_bAlphabeticSort ? 0 : static_cast<double>(getYPos(*pNode));
445 if (nEndOfExtrasIndex >= pNode->GetIndex() && pNode->GetFlyFormat())
446 {
447 nYPos += nOutlinesInFramesIndexAdjustment;
448 nOutlinesInFramesIndexAdjustment += 0.00001;
449 }
450 OUString aEntry(comphelper::string::stripStart(
452 i, m_pWrtShell->GetLayout(), true, false, false), ' '));
453 aEntry = SwNavigationPI::CleanEntry(aEntry);
454 auto pCnt(make_unique<SwOutlineContent>(this, aEntry, i, nLevel,
455 m_pWrtShell->IsOutlineMovable(i), nYPos));
456 m_pMember->insert(std::move(pCnt));
457 }
458
459 // need to check level and equal entry number after creation due to possible outline
460 // nodes in frames, headers, footers
461 if (pOldMember)
462 {
463 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
464 if (pOldMember->size() != m_pMember->size())
465 {
466 *pbContentChanged = true;
467 break;
468 }
469 for (size_t i = 0; i < pOldMember->size(); i++)
470 {
471 if (static_cast<SwOutlineContent*>((*pOldMember)[i].get())->GetOutlineLevel() !=
472 static_cast<SwOutlineContent*>((*m_pMember)[i].get())->GetOutlineLevel())
473 {
474 *pbContentChanged = true;
475 break;
476 }
477 }
478 }
479 }
480 break;
482 {
483 const size_t nCount = m_pWrtShell->GetTableFrameFormatCount(true);
484 const SwFrameFormats* pFrameFormats = m_pWrtShell->GetDoc()->GetTableFrameFormats();
486 for(size_t n = 0, i = 0; i < nCount + n; ++i)
487 {
488 const SwTableFormat& rTableFormat =
489 *static_cast<SwTableFormat*>(pFrameFormats->GetFormat(i));
490 if (rTableFormat.GetInfo(aGetHt)) // skip deleted tables
491 {
492 n++;
493 continue;
494 }
495 tools::Long nYPos = 0;
497 {
498 if (SwTable* pTable = SwTable::FindTable(&rTableFormat))
499 nYPos = getYPos(*pTable->GetTableNode());
500 }
501 auto pCnt = make_unique<SwContent>(this, rTableFormat.GetName(), nYPos);
502 if( !rTableFormat.GetInfo( aAskItem ) &&
503 !aAskItem.pObject ) // not visible
504 pCnt->SetInvisible();
505 m_pMember->insert(std::move(pCnt));
506 }
507
508 if (pOldMember)
509 {
510 // need to check visibility (and equal entry number) after
511 // creation due to a sorted list being used here (before,
512 // entries with same index were compared already at creation
513 // time what worked before a sorted list was used)
514 *pbContentChanged = checkVisibilityChanged(
515 *pOldMember,
516 *m_pMember);
517 }
518 }
519 break;
520 case ContentTypeId::OLE :
523 {
529 Point aNullPt;
530 size_t nCount = m_pWrtShell->GetFlyCount(eType, /*bIgnoreTextBoxes=*/true);
531 std::vector<SwFrameFormat const*> formats(m_pWrtShell->GetFlyFrameFormats(eType, /*bIgnoreTextBoxes=*/true));
532 SAL_WARN_IF(nCount != formats.size(), "sw.ui", "Count differs");
533 nCount = formats.size();
534 for (size_t i = 0; i < nCount; ++i)
535 {
536 SwFrameFormat const*const pFrameFormat = formats[i];
537 const OUString sFrameName = pFrameFormat->GetName();
538
539 SwContent* pCnt;
540 tools::Long nYPos =
541 m_bAlphabeticSort ? 0 : pFrameFormat->FindLayoutRect(false, &aNullPt).Top();
543 {
544 OUString sLink;
545 m_pWrtShell->GetGrfNms( &sLink, nullptr, static_cast<const SwFlyFrameFormat*>( pFrameFormat));
546 pCnt = new SwGraphicContent(this, sFrameName, INetURLObject::decode(sLink,
548 }
549 else
550 {
551 pCnt = new SwContent(this, sFrameName, nYPos);
552 }
553 if( !pFrameFormat->GetInfo( aAskItem ) &&
554 !aAskItem.pObject ) // not visible
555 pCnt->SetInvisible();
556 m_pMember->insert(std::unique_ptr<SwContent>(pCnt));
557 }
558
559 if (pOldMember)
560 {
561 // need to check visibility (and equal entry number) after
562 // creation due to a sorted list being used here (before,
563 // entries with same index were compared already at creation
564 // time what worked before a sorted list was used)
565 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
566 *pbContentChanged = checkVisibilityChanged(
567 *pOldMember,
568 *m_pMember);
569 }
570 }
571 break;
573 {
574 tools::Long nYPos = 0;
576 for(IDocumentMarkAccess::const_iterator_t ppBookmark = pMarkAccess->getBookmarksBegin();
577 ppBookmark != pMarkAccess->getBookmarksEnd();
578 ++ppBookmark)
579 {
580 if(lcl_IsUiVisibleBookmark(*ppBookmark))
581 {
582 const OUString& rBkmName = (*ppBookmark)->GetName();
583 //nYPos from 0 -> text::Bookmarks will be sorted alphabetically
584 auto pCnt(std::make_unique<SwContent>(this, rBkmName,
585 m_bAlphabeticSort ? 0 : nYPos++));
586 m_pMember->insert(std::move(pCnt));
587 }
588 }
589 }
590 break;
592 {
593 std::vector<SwTextField*> aArr;
594 const SwFieldTypes& rFieldTypes =
596 const size_t nSize = rFieldTypes.size();
597 for (size_t i = 0; i < nSize; ++i)
598 {
599 const SwFieldType* pFieldType = rFieldTypes[i].get();
600 if (pFieldType->Which() == SwFieldIds::Postit)
601 continue;
602 std::vector<SwFormatField*> vFields;
603 pFieldType->GatherFields(vFields);
604 for (SwFormatField* pFormatField: vFields)
605 {
606 if (SwTextField* pTextField = pFormatField->GetTextField())
607 {
608 // fields in header footer don't behave well, skip them
609 if (m_pWrtShell->GetDoc()->IsInHeaderFooter(pTextField->GetTextNode()))
610 continue;
611 aArr.emplace_back(pTextField);
612 }
613 }
614 }
616 {
617 const SwNodeOffset nEndOfExtrasIndex = m_pWrtShell->GetNodes().GetEndOfExtras().GetIndex();
618 bool bHasEntryInFly = false;
619
620 // use stable sort array to list fields in document model order
621 std::stable_sort(aArr.begin(), aArr.end(),
622 [](const SwTextField* a, const SwTextField* b){
623 SwPosition aPos(a->GetTextNode(), a->GetStart());
624 SwPosition bPos(b->GetTextNode(), b->GetStart());
625 return aPos < bPos;});
626
627 // determine if there is a text field in a fly frame
628 for (SwTextField* pTextField : aArr)
629 {
630 if (!bHasEntryInFly)
631 {
632 if (nEndOfExtrasIndex >= pTextField->GetTextNode().GetIndex())
633 {
634 // Not a node of BodyText
635 // Are we in a fly?
636 if (pTextField->GetTextNode().GetFlyFormat())
637 {
638 bHasEntryInFly = true;
639 break;
640 }
641 }
642 }
643 }
644
645 // When there are fields in fly frames do an additional sort using the fly frame
646 // anchor position to place field entries in order of document layout appearance.
647 if (bHasEntryInFly)
648 {
649 std::stable_sort(aArr.begin(), aArr.end(),
650 [nEndOfExtrasIndex](const SwTextField* a, const SwTextField* b){
651 SwTextNode& aTextNode = a->GetTextNode();
652 SwTextNode& bTextNode = b->GetTextNode();
653 SwPosition aPos(aTextNode, a->GetStart());
654 SwPosition bPos(bTextNode, b->GetStart());
655 // use anchor position for entries that are located in flys
656 if (nEndOfExtrasIndex >= aTextNode.GetIndex())
657 if (auto pFlyFormat = aTextNode.GetFlyFormat())
658 if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor())
659 aPos = *pPos;
660 if (nEndOfExtrasIndex >= bTextNode.GetIndex())
661 if (auto pFlyFormat = bTextNode.GetFlyFormat())
662 if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor())
663 bPos = *pPos;
664 return aPos < bPos;});
665 }
666 }
667 std::vector<OUString> aDocumentStatisticsSubTypesList;
668 tools::Long nYPos = 0;
669 for (SwTextField* pTextField : aArr)
670 {
671 const SwField* pField = pTextField->GetFormatField().GetField();
672 OUString sExpandField = pField->ExpandField(true, m_pWrtShell->GetLayout());
673 if (!sExpandField.isEmpty())
674 sExpandField = u" - " + sExpandField;
675 OUString sText;
677 {
678 if (aDocumentStatisticsSubTypesList.empty())
680 aDocumentStatisticsSubTypesList);
681 OUString sSubType;
682 if (pField->GetSubType() < aDocumentStatisticsSubTypesList.size())
683 sSubType = u" - " + aDocumentStatisticsSubTypesList[pField->GetSubType()];
684 sText = pField->GetDescription() + u" - " + pField->GetFieldName() + sSubType +
685 sExpandField;
686 }
687 else if (pField->GetTypeId() == SwFieldTypesEnum::GetRef)
688 {
689 assert(dynamic_cast<const SwGetRefField*>(pField));
690 const SwGetRefField* pRefField(static_cast<const SwGetRefField*>(pField));
691 if (pRefField->IsRefToHeadingCrossRefBookmark() ||
693 {
694 OUString sExpandedTextOfReferencedTextNode =
697 if (sExpandedTextOfReferencedTextNode.getLength() > 80)
698 {
699 sExpandedTextOfReferencedTextNode = OUString::Concat(
700 sExpandedTextOfReferencedTextNode.subView(0, 80)) + u"...";
701 }
702 sText = pField->GetDescription() + u" - "
703 + sExpandedTextOfReferencedTextNode + sExpandField;
704 }
705 else
706 {
707 OUString sFieldSubTypeOrName;
708 auto nSubType = pField->GetSubType();
709 if (nSubType == REF_FOOTNOTE)
710 sFieldSubTypeOrName = SwResId(STR_FLDREF_FOOTNOTE);
711 else if (nSubType == REF_ENDNOTE)
712 sFieldSubTypeOrName = SwResId(STR_FLDREF_ENDNOTE);
713 else
714 sFieldSubTypeOrName = pField->GetFieldName();
715 sText = pField->GetDescription() + u" - " + sFieldSubTypeOrName
716 + sExpandField;
717 }
718 }
719 else
720 sText = pField->GetDescription() + u" - " + pField->GetFieldName()
721 + sExpandField;
722 auto pCnt(std::make_unique<SwTextFieldContent>(this, sText,
723 &pTextField->GetFormatField(),
724 m_bAlphabeticSort ? 0 : nYPos++));
725 if (!pTextField->GetTextNode().getLayoutFrame(m_pWrtShell->GetLayout()))
726 pCnt->SetInvisible();
727 m_pMember->insert(std::move(pCnt));
728 }
729 }
730 break;
731 // We will separate footnotes and endnotes here.
734 {
735 const SwFootnoteIdxs& rFootnoteIdxs = m_pWrtShell->GetDoc()->GetFootnoteIdxs();
736 if (rFootnoteIdxs.size() == 0)
737 break;
738 // insert footnotes and endnotes
739 tools::Long nPos = 0;
740 for (const SwTextFootnote* pTextFootnote : rFootnoteIdxs)
741 {
742 if ((!pTextFootnote->GetFootnote().IsEndNote()
744 || (pTextFootnote->GetFootnote().IsEndNote()
746 {
747 const SwFormatFootnote& rFormatFootnote = pTextFootnote->GetFootnote();
748 const OUString& sText
749 = rFormatFootnote.GetViewNumStr(*m_pWrtShell->GetDoc(),
750 m_pWrtShell->GetLayout(), true)
751 + " " + lcl_GetFootnoteText(*pTextFootnote);
752 auto pCnt(make_unique<SwTextFootnoteContent>(
753 this, sText, pTextFootnote, ++nPos));
754 if (!pTextFootnote->GetTextNode().getLayoutFrame(m_pWrtShell->GetLayout()))
755 pCnt->SetInvisible();
756 m_pMember->insert(std::move(pCnt));
757 }
758 }
759 }
760 break;
762 {
764 for (size_t i = 0; i < nCount; ++i)
765 {
766 const SwSectionFormat* pFormat = &m_pWrtShell->GetSectionFormat(i);
767 if (!pFormat->IsInNodesArr())
768 continue;
769 const SwSection* pSection = pFormat->GetSection();
770 if (SectionType eTmpType = pSection->GetType();
771 eTmpType == SectionType::ToxContent || eTmpType == SectionType::ToxHeader)
772 continue;
773 const SwNodeIndex* pNodeIndex = pFormat->GetContent().GetContentIdx();
774 if (pNodeIndex)
775 {
776 const OUString& sSectionName = pSection->GetSectionName();
777
778 sal_uInt8 nLevel = 0;
779 SwSectionFormat* pParentFormat = pFormat->GetParent();
780 while(pParentFormat)
781 {
782 nLevel++;
783 pParentFormat = pParentFormat->GetParent();
784 }
785
786 std::unique_ptr<SwContent> pCnt(new SwRegionContent(this, sSectionName,
787 nLevel, m_bAlphabeticSort ? 0 : getYPos(pNodeIndex->GetNode())));
788 if( !pFormat->GetInfo( aAskItem ) &&
789 !aAskItem.pObject ) // not visible
790 pCnt->SetInvisible();
791 m_pMember->insert(std::move(pCnt));
792 }
793
794 if (pOldMember)
795 {
796 // need to check visibility (and equal entry number) after
797 // creation due to a sorted list being used here (before,
798 // entries with same index were compared already at creation
799 // time what worked before a sorted list was used)
800 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
801 *pbContentChanged = checkVisibilityChanged(
802 *pOldMember,
803 *m_pMember);
804 }
805 }
806 }
807 break;
809 {
810 std::vector<OUString> aRefMarks;
811 m_pWrtShell->GetRefMarks( &aRefMarks );
812
813 tools::Long nYPos = 0;
814 for (const auto& rRefMark : aRefMarks)
815 {
816 m_pMember->insert(std::make_unique<SwContent>(this, rRefMark,
817 m_bAlphabeticSort ? 0 : nYPos++));
818 }
819 }
820 break;
822 {
825
827 {
828 for (auto& r : aArr)
829 {
830 auto pCnt(make_unique<SwURLFieldContent>(this, r.sText, INetURLObject::decode(
831 r.rINetAttr.GetINetFormat().GetValue(),
833 &r.rINetAttr, 0));
834 m_pMember->insert(std::move(pCnt));
835 }
836 break;
837 }
838
839 // use stable sort array to list hyperlinks in document order
840 const SwNodeOffset nEndOfExtrasIndex = m_pWrtShell->GetNodes().GetEndOfExtras().GetIndex();
841 bool bHasEntryInFly = false;
842 std::vector<SwGetINetAttr*> aStableSortINetAttrsArray;
843
844 for (SwGetINetAttr& r : aArr)
845 {
846 aStableSortINetAttrsArray.emplace_back(&r);
847 if (!bHasEntryInFly)
848 {
849 if (nEndOfExtrasIndex >= r.rINetAttr.GetTextNode().GetIndex())
850 {
851 // Not a node of BodyText
852 // Are we in a fly?
853 if (r.rINetAttr.GetTextNode().GetFlyFormat())
854 bHasEntryInFly = true;
855 }
856 }
857 }
858
859 std::stable_sort(aStableSortINetAttrsArray.begin(), aStableSortINetAttrsArray.end(),
860 [](const SwGetINetAttr* a, const SwGetINetAttr* b){
861 SwPosition aSwPos(a->rINetAttr.GetTextNode(),
862 a->rINetAttr.GetStart());
863 SwPosition bSwPos(b->rINetAttr.GetTextNode(),
864 b->rINetAttr.GetStart());
865 return aSwPos < bSwPos;});
866
867 // When there are hyperlinks in text frames do an additional sort using the text frame
868 // anchor position to place entries in the order of document layout appearance.
869 if (bHasEntryInFly)
870 {
871 std::stable_sort(aStableSortINetAttrsArray.begin(), aStableSortINetAttrsArray.end(),
872 [nEndOfExtrasIndex](const SwGetINetAttr* a, const SwGetINetAttr* b){
873 const SwTextNode& aTextNode = a->rINetAttr.GetTextNode();
874 const SwTextNode& bTextNode = b->rINetAttr.GetTextNode();
875 SwPosition aPos(aTextNode, a->rINetAttr.GetStart());
876 SwPosition bPos(bTextNode, b->rINetAttr.GetStart());
877 // use anchor position for entries that are located in flys
878 if (nEndOfExtrasIndex >= aTextNode.GetIndex())
879 if (auto pFlyFormat = aTextNode.GetFlyFormat())
880 if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor())
881 aPos = *pPos;
882 if (nEndOfExtrasIndex >= bTextNode.GetIndex())
883 if (auto pFlyFormat = bTextNode.GetFlyFormat())
884 if (const SwPosition* pPos = pFlyFormat->GetAnchor().GetContentAnchor())
885 bPos = *pPos;
886 return aPos < bPos;});
887 }
888
889 SwGetINetAttrs::size_type n = 0;
890 for (auto p : aStableSortINetAttrsArray)
891 {
892 auto pCnt = make_unique<SwURLFieldContent>(this, p->sText,
893 INetURLObject::decode(p->rINetAttr.GetINetFormat().GetValue(),
895 &p->rINetAttr, ++n);
896 m_pMember->insert(std::move(pCnt));
897 }
898 }
899 break;
901 {
902 const sal_uInt16 nCount = m_pWrtShell->GetTOXCount();
903
904 for ( sal_uInt16 nTox = 0; nTox < nCount; nTox++ )
905 {
906 const SwTOXBase* pBase = m_pWrtShell->GetTOX( nTox );
907 OUString sTOXNm( pBase->GetTOXName() );
908
909 SwContent* pCnt = new SwTOXBaseContent(
910 this, sTOXNm, m_bAlphabeticSort ? 0 : nTox, *pBase);
911
912 if(pBase && !pBase->IsVisible())
913 pCnt->SetInvisible();
914
915 m_pMember->insert( std::unique_ptr<SwContent>(pCnt) );
916 const size_t nPos = m_pMember->size() - 1;
917 if (pOldMember)
918 {
919 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
920 if (!*pbContentChanged && nOldMemberCount > nPos &&
921 (*pOldMember)[nPos]->IsInvisible() != pCnt->IsInvisible())
922 *pbContentChanged = true;
923 }
924 }
925 }
926 break;
928 {
930 if (aMgr)
931 {
932 tools::Long nYPos = 0;
933 for(SwPostItMgr::const_iterator i = aMgr->begin(); i != aMgr->end(); ++i)
934 {
935 if (const SwFormatField* pFormatField = dynamic_cast<const SwFormatField *>((*i)->GetBroadcaster())) // SwPostit
936 {
937 if (pFormatField->GetTextField() && pFormatField->IsFieldInDoc())
938 {
939 OUString sEntry = pFormatField->GetField()->GetPar2();
940 sEntry = RemoveNewline(sEntry);
941 std::unique_ptr<SwPostItContent> pCnt(new SwPostItContent(
942 this,
943 sEntry,
944 pFormatField,
945 nYPos));
946 if (!pFormatField->GetTextField()->GetTextNode().getLayoutFrame(
948 pCnt->SetInvisible();
949 if (pOldMember)
950 {
951 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
952 if (!*pbContentChanged &&
953 nOldMemberCount > o3tl::make_unsigned(nYPos) &&
954 (*pOldMember)[nYPos]->IsInvisible() != pCnt->IsInvisible())
955 *pbContentChanged = true;
956 }
957 m_pMember->insert(std::move(pCnt));
958 nYPos++;
959 }
960 }
961 }
962 }
963 }
964 break;
966 {
968 SwDrawModel* pModel = rIDDMA.GetDrawModel();
969 if(pModel)
970 {
971 SdrPage* pPage = pModel->GetPage(0);
972 const size_t nCount = pPage->GetObjCount();
973 for( size_t i=0; i<nCount; ++i )
974 {
975 SdrObject* pTemp = pPage->GetObj(i);
976 // #i51726# - all drawing objects can be named now
977 if (!pTemp->GetName().isEmpty())
978 {
979 tools::Long nYPos = LONG_MIN;
980 const bool bIsVisible = rIDDMA.IsVisibleLayerId(pTemp->GetLayer());
981 if (bIsVisible)
982 nYPos = m_bAlphabeticSort ? 0 : pTemp->GetLogicRect().Top();
983 auto pCnt(std::make_unique<SwContent>(this, pTemp->GetName(), nYPos));
984 if (!bIsVisible)
985 pCnt->SetInvisible();
986 m_pMember->insert(std::move(pCnt));
987 }
988 }
989
990 if (pOldMember)
991 {
992 // need to check visibility (and equal entry number) after
993 // creation due to a sorted list being used here (before,
994 // entries with same index were compared already at creation
995 // time what worked before a sorted list was used)
996 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
997 *pbContentChanged = checkVisibilityChanged(
998 *pOldMember,
999 *m_pMember);
1000 }
1001 }
1002 }
1003 break;
1004 default: break;
1005 }
1006 m_nMemberCount = m_pMember->size();
1007 if (pOldMember)
1008 {
1009 assert(pbContentChanged && "pbContentChanged is always set if pOldMember is");
1010 if (!*pbContentChanged && pOldMember->size() != m_nMemberCount)
1011 *pbContentChanged = true;
1012 }
1013
1014 m_bDataValid = true;
1015}
1016
1017namespace {
1018
1019enum STR_CONTEXT_IDX
1020{
1021 IDX_STR_OUTLINE_LEVEL = 0,
1022 IDX_STR_DRAGMODE = 1,
1023 IDX_STR_HYPERLINK = 2,
1024 IDX_STR_LINK_REGION = 3,
1025 IDX_STR_COPY_REGION = 4,
1026 IDX_STR_DISPLAY = 5,
1027 IDX_STR_ACTIVE_VIEW = 6,
1028 IDX_STR_HIDDEN = 7,
1029 IDX_STR_ACTIVE = 8,
1030 IDX_STR_INACTIVE = 9,
1031 IDX_STR_EDIT_ENTRY = 10,
1032 IDX_STR_DELETE_ENTRY = 11,
1033 IDX_STR_SEND_OUTLINE_TO_CLIPBOARD_ENTRY = 12,
1034 IDX_STR_OUTLINE_TRACKING = 13,
1035 IDX_STR_OUTLINE_TRACKING_DEFAULT = 14,
1036 IDX_STR_OUTLINE_TRACKING_FOCUS = 15,
1037 IDX_STR_OUTLINE_TRACKING_OFF = 16
1038};
1039
1040}
1041
1043{
1044 STR_OUTLINE_LEVEL,
1045 STR_DRAGMODE,
1046 STR_HYPERLINK,
1047 STR_LINK_REGION,
1048 STR_COPY_REGION,
1049 STR_DISPLAY,
1050 STR_ACTIVE_VIEW,
1051 STR_HIDDEN,
1052 STR_ACTIVE,
1053 STR_INACTIVE,
1054 STR_EDIT_ENTRY,
1055 STR_DELETE_ENTRY,
1056 STR_SEND_OUTLINE_TO_CLIPBOARD_ENTRY,
1057 STR_OUTLINE_TRACKING,
1058 STR_OUTLINE_TRACKING_DEFAULT,
1059 STR_OUTLINE_TRACKING_FOCUS,
1060 STR_OUTLINE_TRACKING_OFF
1061};
1062
1063SwContentTree::SwContentTree(std::unique_ptr<weld::TreeView> xTreeView, SwNavigationPI* pDialog)
1064 : m_xTreeView(std::move(xTreeView))
1065 , m_aDropTargetHelper(*this)
1066 , m_pDialog(pDialog)
1067 , m_sSpace(OUString(" "))
1068 , m_aUpdTimer("SwContentTree m_aUpdTimer")
1069 , m_sInvisible(SwResId(STR_INVISIBLE))
1070 , m_pHiddenShell(nullptr)
1071 , m_pActiveShell(nullptr)
1072 , m_pConfig(SW_MOD()->GetNavigationConfig())
1073 , m_nActiveBlock(0)
1074 , m_nHiddenBlock(0)
1075 , m_nEntryCount(0)
1076 , m_nRootType(ContentTypeId::UNKNOWN)
1077 , m_nLastSelType(ContentTypeId::UNKNOWN)
1078 , m_nOutlineLevel(MAXLEVEL)
1079 , m_eState(State::ACTIVE)
1080 , m_bIsRoot(false)
1081 , m_bIsIdleClear(false)
1082 , m_bIsLastReadOnly(false)
1083 , m_bIsOutlineMoveable(true)
1084 , m_bViewHasChanged(false)
1085{
1086 m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 30,
1087 m_xTreeView->get_text_height() * 14);
1088
1089 m_xTreeView->set_help_id(HID_NAVIGATOR_TREELIST);
1090
1091 m_xTreeView->connect_expanding(LINK(this, SwContentTree, ExpandHdl));
1092 m_xTreeView->connect_collapsing(LINK(this, SwContentTree, CollapseHdl));
1093 m_xTreeView->connect_row_activated(LINK(this, SwContentTree, ContentDoubleClickHdl));
1094 m_xTreeView->connect_changed(LINK(this, SwContentTree, SelectHdl));
1095 m_xTreeView->connect_focus_in(LINK(this, SwContentTree, FocusInHdl));
1096 m_xTreeView->connect_key_press(LINK(this, SwContentTree, KeyInputHdl));
1097 m_xTreeView->connect_popup_menu(LINK(this, SwContentTree, CommandHdl));
1098 m_xTreeView->connect_query_tooltip(LINK(this, SwContentTree, QueryTooltipHdl));
1099 m_xTreeView->connect_drag_begin(LINK(this, SwContentTree, DragBeginHdl));
1100
1102 {
1104 mTrackContentType[i] = true;
1105 m_aActiveContentArr[i] = nullptr;
1106 m_aHiddenContentArr[i] = nullptr;
1107 }
1108 for (int i = 0; i < CONTEXT_COUNT; ++i)
1109 {
1111 }
1113
1114 // Restore outline headings expand state (same session persistence only)
1115 if (SwView* pView = GetActiveView(); pView && pView->GetDocShell())
1116 {
1117 OUString sDocTitle = pView->GetDocShell()->GetTitle();
1118 if (lcl_DocOutLineExpandStateMap.find(sDocTitle) != lcl_DocOutLineExpandStateMap.end())
1119 mOutLineNodeMap = lcl_DocOutLineExpandStateMap[sDocTitle];
1120 }
1121
1122 m_aUpdTimer.SetInvokeHandler(LINK(this, SwContentTree, TimerUpdate));
1123 m_aUpdTimer.SetTimeout(1000);
1124}
1125
1127{
1128 if (SwView* pView = GetActiveView(); pView && pView->GetDocShell())
1129 {
1130 OUString sDocTitle = pView->GetDocShell()->GetTitle();
1131 lcl_DocOutLineExpandStateMap[sDocTitle] = mOutLineNodeMap;
1132 }
1133 clear(); // If applicable erase content types previously.
1134 m_aUpdTimer.Stop();
1135 SetActiveShell(nullptr);
1136}
1137
1138// Drag&Drop methods
1139IMPL_LINK(SwContentTree, DragBeginHdl, bool&, rUnsetDragIcon, bool)
1140{
1141 rUnsetDragIcon = true;
1142
1143 bool bDisallow = true;
1144
1145 // don't allow if tree root is selected
1146 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
1147 bool bEntry = m_xTreeView->get_selected(xEntry.get());
1148 if (!bEntry || lcl_IsContentType(*xEntry, *m_xTreeView))
1149 {
1150 return true; // disallow
1151 }
1152
1155
1156 if (FillTransferData(*xContainer, nDragMode))
1157 bDisallow = false;
1158
1159 if (m_bIsRoot && m_nRootType == ContentTypeId::OUTLINE)
1160 {
1161 // Only move drag entry and continuous selected siblings:
1162 m_aDndOutlinesSelected.clear();
1163
1164 std::unique_ptr<weld::TreeIter> xScratch(m_xTreeView->make_iterator());
1165
1166 // Find first selected of continuous siblings
1167 while (true)
1168 {
1169 m_xTreeView->copy_iterator(*xEntry, *xScratch);
1170 if (!m_xTreeView->iter_previous_sibling(*xScratch))
1171 break;
1172 if (!m_xTreeView->is_selected(*xScratch))
1173 break;
1174 m_xTreeView->copy_iterator(*xScratch, *xEntry);
1175 }
1176 // Record continuous selected siblings
1177 do
1178 {
1179 m_aDndOutlinesSelected.push_back(m_xTreeView->make_iterator(xEntry.get()));
1180 }
1181 while (m_xTreeView->iter_next_sibling(*xEntry) && m_xTreeView->is_selected(*xEntry));
1182 bDisallow = false;
1183 }
1184
1185 if (!bDisallow)
1186 m_xTreeView->enable_drag_source(xContainer, nDragMode);
1187 return bDisallow;
1188}
1189
1191 : DropTargetHelper(rTreeView.get_widget().get_drop_target())
1192 , m_rTreeView(rTreeView)
1193{
1194}
1195
1197{
1198 sal_Int8 nAccept = m_rTreeView.AcceptDrop(rEvt);
1199
1200 if (nAccept != DND_ACTION_NONE)
1201 {
1202 // to enable the autoscroll when we're close to the edges
1204 rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true);
1205 }
1206
1207 return nAccept;
1208}
1209
1211{
1212 return m_xTreeView->get_drag_source() == m_xTreeView.get();
1213}
1214
1215// QueryDrop will be executed in the navigator
1217{
1219 if( m_bIsRoot )
1220 {
1222 nRet = rEvt.mnAction;
1223 }
1224 else if (!IsInDrag())
1225 nRet = GetParentWindow()->AcceptDrop();
1226 return nRet;
1227}
1228
1229// Drop will be executed in the navigator
1230static void* lcl_GetOutlineKey(SwContentTree& rTree, SwOutlineContent const * pContent)
1231{
1232 void* key = nullptr;
1233 if (pContent)
1234 {
1235 SwWrtShell* pShell = rTree.GetWrtShell();
1236 auto const nPos = pContent->GetOutlinePos();
1237
1238 key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos ));
1239 }
1240 return key;
1241}
1242
1244{
1245 return m_rTreeView.ExecuteDrop(rEvt);
1246}
1247
1249{
1250 std::unique_ptr<weld::TreeIter> xDropEntry(m_xTreeView->make_iterator());
1251 if (!m_xTreeView->get_dest_row_at_pos(rEvt.maPosPixel, xDropEntry.get(), true))
1252 xDropEntry.reset();
1253
1255 {
1256 if (xDropEntry && lcl_IsContent(*xDropEntry, *m_xTreeView))
1257 {
1258 assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xDropEntry))));
1259 SwOutlineContent* pOutlineContent = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xDropEntry));
1260 assert(pOutlineContent);
1261
1262 void* key = lcl_GetOutlineKey(*this, pOutlineContent);
1263 assert(key);
1264 if (!mOutLineNodeMap[key])
1265 {
1266 while (m_xTreeView->iter_has_child(*xDropEntry))
1267 {
1268 std::unique_ptr<weld::TreeIter> xChildEntry(m_xTreeView->make_iterator(xDropEntry.get()));
1269 bool bChildEntry = m_xTreeView->iter_children(*xChildEntry);
1270 while (bChildEntry)
1271 {
1272 m_xTreeView->copy_iterator(*xChildEntry, *xDropEntry);
1273 bChildEntry = m_xTreeView->iter_next_sibling(*xChildEntry);
1274 }
1275 }
1276 }
1277 }
1278
1279 SwOutlineNodes::size_type nTargetPos = 0;
1280 if (!xDropEntry)
1281 {
1282 // dropped in blank space -> move to bottom
1284 }
1285 else if (!lcl_IsContent(*xDropEntry, *m_xTreeView))
1286 {
1287 // dropped on "heading" parent -> move to start
1288 nTargetPos = SwOutlineNodes::npos;
1289 }
1290 else
1291 {
1292 assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xDropEntry))));
1293 nTargetPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xDropEntry))->GetOutlinePos();
1294 }
1295
1296 if( MAXLEVEL > m_nOutlineLevel && // Not all layers are displayed.
1297 nTargetPos != SwOutlineNodes::npos)
1298 {
1299 std::unique_ptr<weld::TreeIter> xNext(m_xTreeView->make_iterator(xDropEntry.get()));
1300 bool bNext = m_xTreeView->iter_next(*xNext);
1301 if (bNext)
1302 {
1303 assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xNext))));
1304 nTargetPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xNext))->GetOutlinePos() - 1;
1305 }
1306 else
1308 }
1309
1310 // remove the drop highlight before we change the contents of the tree so we don't
1311 // try and dereference a removed entry in post-processing drop
1312 m_xTreeView->unset_drag_dest_row();
1313 MoveOutline(nTargetPos);
1314
1315 }
1317}
1318
1319namespace
1320{
1321 bool IsAllExpanded(const weld::TreeView& rContentTree, const weld::TreeIter& rEntry)
1322 {
1323 if (!rContentTree.get_row_expanded(rEntry))
1324 return false;
1325
1326 if (!rContentTree.iter_has_child(rEntry))
1327 return false;
1328
1329 std::unique_ptr<weld::TreeIter> xChild(rContentTree.make_iterator(&rEntry));
1330 (void)rContentTree.iter_children(*xChild);
1331
1332 do
1333 {
1334 if (rContentTree.iter_has_child(*xChild) || rContentTree.get_children_on_demand(*xChild))
1335 {
1336 if (!IsAllExpanded(rContentTree, *xChild))
1337 return false;
1338 }
1339 }
1340 while (rContentTree.iter_next_sibling(*xChild));
1341 return true;
1342 }
1343
1344 void ExpandOrCollapseAll(weld::TreeView& rContentTree, weld::TreeIter& rEntry)
1345 {
1346 bool bExpand = !IsAllExpanded(rContentTree, rEntry);
1347 bExpand ? rContentTree.expand_row(rEntry) : rContentTree.collapse_row(rEntry);
1348 int nRefDepth = rContentTree.get_iter_depth(rEntry);
1349 while (rContentTree.iter_next(rEntry) && rContentTree.get_iter_depth(rEntry) > nRefDepth)
1350 {
1351 if (rContentTree.iter_has_child(rEntry))
1352 bExpand ? rContentTree.expand_row(rEntry) : rContentTree.collapse_row(rEntry);
1353 }
1354 }
1355}
1356
1357// Handler for Dragging and ContextMenu
1358static bool lcl_InsertExpandCollapseAllItem(const weld::TreeView& rContentTree, const weld::TreeIter& rEntry, weld::Menu& rPop)
1359{
1360 if (rContentTree.iter_has_child(rEntry) || rContentTree.get_children_on_demand(rEntry))
1361 {
1362 rPop.set_label(OString::number(800), IsAllExpanded(rContentTree, rEntry) ? SwResId(STR_COLLAPSEALL) : SwResId(STR_EXPANDALL));
1363 return false;
1364 }
1365 return true;
1366}
1367
1368static void lcl_SetOutlineContentEntriesSensitivities(SwContentTree* pThis, const weld::TreeView& rContentTree, const weld::TreeIter& rEntry, weld::Menu& rPop)
1369{
1370 rPop.set_sensitive(OString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY), false);
1371 rPop.set_sensitive(OString::number(HIDE_OUTLINE_CONTENT_VISIBILITY), false);
1372 rPop.set_sensitive(OString::number(SHOW_OUTLINE_CONTENT_VISIBILITY), false);
1373
1374 // todo: multi selection
1375 if (rContentTree.count_selected_rows() > 1)
1376 return;
1377
1378 bool bIsRoot = lcl_IsContentType(rEntry, rContentTree);
1379
1381 {
1382 if (!bIsRoot)
1383 rPop.set_sensitive(OString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY), true);
1384 return;
1385 }
1386
1387 const SwNodes& rNodes = pThis->GetWrtShell()->GetNodes();
1388 const SwOutlineNodes& rOutlineNodes = rNodes.GetOutLineNds();
1389 size_t nOutlinePos = weld::GetAbsPos(rContentTree, rEntry);
1390
1391 if (!bIsRoot)
1392 --nOutlinePos;
1393
1394 if (nOutlinePos >= rOutlineNodes.size())
1395 return;
1396
1397 int nFirstLevel = pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineLevel(nOutlinePos);
1398 {
1399 // determine if any concerned outline node has content
1400 bool bHasContent(false);
1401 size_t nPos = nOutlinePos;
1402 SwNode* pSttNd = rOutlineNodes[nPos];
1403 SwNode* pEndNd = &rNodes.GetEndOfContent();
1404 if (rOutlineNodes.size() > nPos + 1)
1405 pEndNd = rOutlineNodes[nPos + 1];
1406
1407 // selected
1408 SwNodeIndex aIdx(*pSttNd);
1409 if (rNodes.GoNext(&aIdx) != pEndNd)
1410 bHasContent = true;
1411
1412 // descendants
1413 if (!bHasContent && (rContentTree.iter_has_child(rEntry) || rContentTree.get_children_on_demand(rEntry)))
1414 {
1415 while (++nPos < rOutlineNodes.size() &&
1416 (bIsRoot || pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos) > nFirstLevel))
1417 {
1418 pSttNd = rOutlineNodes[nPos];
1419 pEndNd = &rNodes.GetEndOfContent();
1420 if (rOutlineNodes.size() > nPos + 1)
1421 pEndNd = rOutlineNodes[nPos + 1];
1422
1423 // test for content in outline node
1424 aIdx.Assign(*pSttNd);
1425 if (rNodes.GoNext(&aIdx) != pEndNd)
1426 {
1427 bHasContent = true;
1428 break;
1429 }
1430 }
1431 }
1432
1433 if (!bHasContent)
1434 return; // no content in any of the concerned outline nodes
1435 }
1436
1437 // determine for subs if all are folded or unfolded or if they are mixed
1438 if (rContentTree.iter_has_child(rEntry) || rContentTree.get_children_on_demand(rEntry))
1439 {
1440 // skip no content nodes
1441 // we know there is content from results above so this is presumably safe
1442 size_t nPos = nOutlinePos;
1443 while (true)
1444 {
1445 SwNode* pSttNd = rOutlineNodes[nPos];
1446 SwNode* pEndNd = rOutlineNodes.back();
1447 if (!bIsRoot && rOutlineNodes.size() > nPos + 1)
1448 pEndNd = rOutlineNodes[nPos + 1];
1449
1450 SwNodeIndex aIdx(*pSttNd);
1451 if (rNodes.GoNext(&aIdx) != pEndNd)
1452 break;
1453 nPos++;
1454 }
1455
1456 bool bHasFolded(!pThis->GetWrtShell()->IsOutlineContentVisible(nPos));
1457 bool bHasUnfolded(!bHasFolded);
1458
1459 while ((++nPos < pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineNodesCount()) &&
1460 (bIsRoot || pThis->GetWrtShell()->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos) > nFirstLevel))
1461 {
1462
1463 SwNode* pSttNd = rOutlineNodes[nPos];
1464 SwNode* pEndNd = &rNodes.GetEndOfContent();
1465 if (rOutlineNodes.size() > nPos + 1)
1466 pEndNd = rOutlineNodes[nPos + 1];
1467
1468 SwNodeIndex aIdx(*pSttNd);
1469 if (rNodes.GoNext(&aIdx) == pEndNd)
1470 continue; // skip if no content
1471
1473 bHasFolded = true;
1474 else
1475 bHasUnfolded = true;
1476
1477 if (bHasFolded && bHasUnfolded)
1478 break; // mixed so no need to continue
1479 }
1480
1481 rPop.set_sensitive(OString::number(HIDE_OUTLINE_CONTENT_VISIBILITY), bHasUnfolded);
1482 rPop.set_sensitive(OString::number(SHOW_OUTLINE_CONTENT_VISIBILITY), bHasFolded);
1483 }
1484
1485 rPop.set_sensitive(OString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY), !bIsRoot);
1486}
1487
1488IMPL_LINK(SwContentTree, CommandHdl, const CommandEvent&, rCEvt, bool)
1489{
1490 if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
1491 return false;
1492
1493 grab_focus();
1494
1495 // select clicked entry or limit selection to root entry if needed
1496 if (std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
1497 rCEvt.IsMouseEvent() && m_xTreeView->get_dest_row_at_pos(
1498 rCEvt.GetMousePosPixel(), xEntry.get(), false))
1499 {
1500 // if clicked entry is not currently selected then clear selections and select it
1501 if (!m_xTreeView->is_selected(*xEntry))
1502 m_xTreeView->set_cursor(*xEntry);
1503 // if root entry is selected then clear selections and select it
1504 else if (m_xTreeView->is_selected(0))
1505 m_xTreeView->set_cursor(0);
1506 }
1507
1508 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), "modules/swriter/ui/navigatorcontextmenu.ui"));
1509 std::unique_ptr<weld::Menu> xPop = xBuilder->weld_menu("navmenu");
1510
1511 bool bOutline(false);
1512 std::unique_ptr<weld::Menu> xSubPop1 = xBuilder->weld_menu("outlinelevel");
1513 std::unique_ptr<weld::Menu> xSubPop2 = xBuilder->weld_menu("dragmodemenu");
1514 std::unique_ptr<weld::Menu> xSubPop3 = xBuilder->weld_menu("displaymenu");
1515 std::unique_ptr<weld::Menu> xSubPopOutlineTracking = xBuilder->weld_menu("outlinetracking");
1516
1517 std::unique_ptr<weld::Menu> xSubPopOutlineContent = xBuilder->weld_menu("outlinecontent");
1518
1519 xSubPopOutlineContent->append(OUString::number(TOGGLE_OUTLINE_CONTENT_VISIBILITY),
1520 SwResId(STR_OUTLINE_CONTENT_VISIBILITY_TOGGLE));
1521 xSubPopOutlineContent->append(OUString::number(HIDE_OUTLINE_CONTENT_VISIBILITY),
1522 SwResId(STR_OUTLINE_CONTENT_VISIBILITY_HIDE_ALL));
1523 xSubPopOutlineContent->append(OUString::number(SHOW_OUTLINE_CONTENT_VISIBILITY),
1524 SwResId(STR_OUTLINE_CONTENT_VISIBILITY_SHOW_ALL));
1525
1526 for(int i = 1; i <= 3; ++i)
1527 xSubPopOutlineTracking->append_radio(OUString::number(i + 10), m_aContextStrings[IDX_STR_OUTLINE_TRACKING + i]);
1528 xSubPopOutlineTracking->set_active(OString::number(10 + m_nOutlineTracking), true);
1529
1530 for (int i = 1; i <= MAXLEVEL; ++i)
1531 xSubPop1->append_radio(OUString::number(i + 100), OUString::number(i));
1532 xSubPop1->set_active(OString::number(100 + m_nOutlineLevel), true);
1533
1534 for (int i=0; i < 3; ++i)
1535 xSubPop2->append_radio(OUString::number(i + 201), m_aContextStrings[IDX_STR_HYPERLINK + i]);
1536 xSubPop2->set_active(OString::number(201 + static_cast<int>(GetParentWindow()->GetRegionDropMode())), true);
1537
1538 // Insert the list of the open files
1539 {
1540 sal_uInt16 nId = 301;
1541 SwView *pView = SwModule::GetFirstView();
1542 while (pView)
1543 {
1544 OUString sInsert = pView->GetDocShell()->GetTitle() + " (" +
1545 m_aContextStrings[pView == GetActiveView() ? IDX_STR_ACTIVE :
1546 IDX_STR_INACTIVE] + ")";
1547 xSubPop3->append_radio(OUString::number(nId), sInsert);
1548 if (State::CONSTANT == m_eState && m_pActiveShell == &pView->GetWrtShell())
1549 xSubPop3->set_active(OString::number(nId), true);
1550 pView = SwModule::GetNextView(pView);
1551 nId++;
1552 }
1553 xSubPop3->append_radio(OUString::number(nId++), m_aContextStrings[IDX_STR_ACTIVE_VIEW]);
1554 if (m_pHiddenShell) // can have only one hidden shell
1555 {
1556 OUString sHiddenEntry = m_pHiddenShell->GetView().GetDocShell()->GetTitle() +
1557 " (" +
1558 m_aContextStrings[IDX_STR_HIDDEN] +
1559 ")";
1560 xSubPop3->append_radio(OUString::number(nId), sHiddenEntry);
1561 }
1562 if (State::ACTIVE == m_eState)
1563 xSubPop3->set_active(OString::number(--nId), true);
1564 else if (State::HIDDEN == m_eState)
1565 xSubPop3->set_active(OString::number(nId), true);
1566 }
1567
1568 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
1569 if (!m_xTreeView->get_selected(xEntry.get()))
1570 xEntry.reset();
1571
1572 bool bRemoveGotoEntry = false;
1573 if (State::HIDDEN == m_eState || !xEntry || !lcl_IsContent(*xEntry, *m_xTreeView) ||
1574 weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->IsInvisible())
1575 bRemoveGotoEntry = true;
1576
1577 bool bRemovePostItEntries = true;
1578 bool bRemoveIndexEntries = true;
1579 bool bRemoveCopyEntry = true;
1580 bool bRemoveEditEntry = true;
1581 bool bRemoveUnprotectEntry = true;
1582 bool bRemoveDeleteChapterEntry = true,
1583 bRemoveDeleteTableEntry = true,
1584 bRemoveDeleteFrameEntry = true,
1585 bRemoveDeleteImageEntry = true,
1586 bRemoveDeleteOLEObjectEntry = true,
1587 bRemoveDeleteBookmarkEntry = true,
1588 bRemoveDeleteHyperlinkEntry = true,
1589 bRemoveDeleteIndexEntry= true,
1590 bRemoveDeleteCommentEntry = true,
1591 bRemoveDeleteDrawingObjectEntry = true,
1592 bRemoveDeleteFieldEntry = true;
1593 bool bRemoveRenameEntry = true;
1594 bool bRemoveSelectEntry = true;
1595 bool bRemoveToggleExpandEntry = true;
1596 bool bRemoveChapterEntries = true;
1597 bool bRemoveSendOutlineEntry = true;
1598
1599 bool bRemoveTableTracking = true;
1600 bool bRemoveSectionTracking = true;
1601 bool bRemoveFrameTracking = true;
1602 bool bRemoveImageTracking = true;
1603 bool bRemoveOLEobjectTracking = true;
1604 bool bRemoveBookmarkTracking = true;
1605 bool bRemoveHyperlinkTracking = true;
1606 bool bRemoveReferenceTracking = true;
1607 bool bRemoveIndexTracking = true;
1608 bool bRemoveCommentTracking = true;
1609 bool bRemoveDrawingObjectTracking = true;
1610 bool bRemoveFieldTracking = true;
1611 bool bRemoveFootnoteTracking = true;
1612 bool bRemoveEndnoteTracking = true;
1613
1614 bool bRemoveSortEntry = true;
1615
1616 if (xEntry)
1617 {
1618 const SwContentType* pType;
1619 if (lcl_IsContentType(*xEntry, *m_xTreeView))
1620 pType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry));
1621 else
1622 pType = weld::fromId<SwContent*>(
1623 m_xTreeView->get_id(*xEntry))->GetParent();
1624 const ContentTypeId nContentType = pType->GetType();
1625
1626 if (nContentType != ContentTypeId::FOOTNOTE && nContentType != ContentTypeId::ENDNOTE
1627 && nContentType != ContentTypeId::POSTIT)
1628 {
1629 bRemoveSortEntry = false;
1630 xPop->set_active("sort", pType->GetSortType());
1631 }
1632
1633 OString aIdent;
1634 switch (nContentType)
1635 {
1637 aIdent = "tabletracking";
1638 bRemoveTableTracking = false;
1639 break;
1641 aIdent = "sectiontracking";
1642 bRemoveSectionTracking = false;
1643 break;
1645 aIdent = "frametracking";
1646 bRemoveFrameTracking = false;
1647 break;
1649 aIdent = "imagetracking";
1650 bRemoveImageTracking = false;
1651 break;
1652 case ContentTypeId::OLE:
1653 aIdent = "oleobjecttracking";
1654 bRemoveOLEobjectTracking = false;
1655 break;
1657 aIdent = "bookmarktracking";
1658 bRemoveBookmarkTracking = false;
1659 break;
1661 aIdent = "hyperlinktracking";
1662 bRemoveHyperlinkTracking = false;
1663 break;
1665 aIdent = "referencetracking";
1666 bRemoveReferenceTracking = false;
1667 break;
1669 aIdent = "indextracking";
1670 bRemoveIndexTracking = false;
1671 break;
1673 aIdent = "commenttracking";
1674 bRemoveCommentTracking = false;
1675 break;
1677 aIdent = "drawingobjecttracking";
1678 bRemoveDrawingObjectTracking = false;
1679 break;
1681 aIdent = "fieldtracking";
1682 bRemoveFieldTracking = false;
1683 break;
1685 aIdent = "footnotetracking";
1686 bRemoveFootnoteTracking = false;
1687 break;
1689 aIdent = "endnotetracking";
1690 bRemoveEndnoteTracking = false;
1691 break;
1692 default: break;
1693 }
1694 if (!aIdent.isEmpty())
1695 xPop->set_active(aIdent, mTrackContentType[nContentType]);
1696
1697 // Edit only if the shown content is coming from the current view.
1698 if (State::HIDDEN != m_eState &&
1699 (State::ACTIVE == m_eState || m_pActiveShell == GetActiveView()->GetWrtShellPtr())
1700 && lcl_IsContent(*xEntry, *m_xTreeView))
1701 {
1702 const bool bReadonly = m_pActiveShell->GetView().GetDocShell()->IsReadOnly();
1703 const bool bVisible = !weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->IsInvisible();
1704 const bool bProtected = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->IsProtect();
1705 const bool bProtectBM = (ContentTypeId::BOOKMARK == nContentType)
1706 && m_pActiveShell->getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS);
1707 const bool bEditable = pType->IsEditable() &&
1708 ((bVisible && !bProtected) || ContentTypeId::REGION == nContentType);
1709 const bool bDeletable = pType->IsDeletable() &&
1710 ((bVisible && !bProtected && !bProtectBM) || ContentTypeId::REGION == nContentType);
1711 const bool bRenamable = bEditable && !bReadonly &&
1712 (ContentTypeId::TABLE == nContentType ||
1713 ContentTypeId::FRAME == nContentType ||
1714 ContentTypeId::GRAPHIC == nContentType ||
1715 ContentTypeId::OLE == nContentType ||
1716 (ContentTypeId::BOOKMARK == nContentType && !bProtectBM) ||
1717 ContentTypeId::REGION == nContentType ||
1718 ContentTypeId::INDEX == nContentType ||
1719 ContentTypeId::DRAWOBJECT == nContentType);
1720 // Choose which Delete entry to show.
1721 if (bDeletable)
1722 {
1723 switch (nContentType)
1724 {
1726 bRemoveDeleteChapterEntry = false;
1727 break;
1729 bRemoveDeleteTableEntry = false;
1730 break;
1732 bRemoveDeleteFrameEntry = false;
1733 break;
1735 bRemoveDeleteImageEntry = false;
1736 break;
1737 case ContentTypeId::OLE:
1738 bRemoveDeleteOLEObjectEntry = false;
1739 break;
1741 bRemoveDeleteBookmarkEntry = false;
1742 break;
1744 bRemoveDeleteHyperlinkEntry = false;
1745 break;
1747 bRemoveDeleteIndexEntry = false;
1748 break;
1750 bRemoveDeleteCommentEntry = false;
1751 break;
1753 bRemoveDeleteDrawingObjectEntry = false;
1754 break;
1756 bRemoveDeleteFieldEntry = false;
1757 break;
1758 default: break;
1759 }
1760 }
1761 if (ContentTypeId::FOOTNOTE == nContentType || ContentTypeId::ENDNOTE == nContentType)
1762 {
1763 void* pUserData = weld::fromId<void*>(m_xTreeView->get_id(*xEntry));
1764 const SwTextFootnote* pFootnote =
1765 static_cast<const SwTextFootnoteContent*>(pUserData)->GetTextFootnote();
1766 if (!pFootnote)
1767 bRemoveGotoEntry = true;
1768 }
1769 else if(ContentTypeId::OUTLINE == nContentType)
1770 {
1771 bOutline = true;
1772 lcl_SetOutlineContentEntriesSensitivities(this, *m_xTreeView, *xEntry, *xSubPopOutlineContent);
1773 bRemoveToggleExpandEntry = lcl_InsertExpandCollapseAllItem(*m_xTreeView, *xEntry, *xPop);
1774 if (!bReadonly)
1775 {
1776 bRemoveSelectEntry = false;
1777 bRemoveChapterEntries = false;
1778 }
1779 bRemoveCopyEntry = false;
1780 }
1781 else if (!bReadonly && bEditable)
1782 {
1783 if(ContentTypeId::INDEX == nContentType)
1784 {
1785 bRemoveIndexEntries = false;
1786
1787 const SwTOXBase* pBase = weld::fromId<SwTOXBaseContent*>(m_xTreeView->get_id(*xEntry))->GetTOXBase();
1788 if (!pBase->IsTOXBaseInReadonly())
1789 bRemoveEditEntry = false;
1790
1791 xPop->set_active(OString::number(405), SwEditShell::IsTOXBaseReadonly(*pBase));
1792 }
1793 else if(ContentTypeId::TABLE == nContentType)
1794 {
1795 bRemoveSelectEntry = false;
1796 bRemoveEditEntry = false;
1797 bRemoveUnprotectEntry = false;
1798 bool bFull = false;
1799 OUString sTableName = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->GetName();
1800 bool bProt = m_pActiveShell->HasTableAnyProtection( &sTableName, &bFull );
1801 xPop->set_sensitive(OString::number(403), !bFull);
1802 xPop->set_sensitive(OString::number(404), bProt);
1803 }
1804 else if(ContentTypeId::REGION == nContentType)
1805 {
1806 bRemoveSelectEntry = false;
1807 bRemoveEditEntry = false;
1808 }
1809 else if (bEditable)
1810 bRemoveEditEntry = false;
1811 //Rename object
1812 if (bRenamable)
1813 bRemoveRenameEntry = false;
1814 }
1815 }
1816 else
1817 {
1818 if (lcl_IsContentType(*xEntry, *m_xTreeView))
1819 pType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry));
1820 else
1821 pType = weld::fromId<SwContent*>(
1822 m_xTreeView->get_id(*xEntry))->GetParent();
1823 if (pType)
1824 {
1825 if (ContentTypeId::OUTLINE == nContentType)
1826 {
1827 bOutline = true;
1828 if (State::HIDDEN != m_eState)
1829 {
1830 lcl_SetOutlineContentEntriesSensitivities(this, *m_xTreeView, *xEntry,
1831 *xSubPopOutlineContent);
1832 bRemoveSendOutlineEntry = false;
1833 }
1834 bRemoveToggleExpandEntry = lcl_InsertExpandCollapseAllItem(*m_xTreeView, *xEntry,
1835 *xPop);
1836 }
1837 else if (State::HIDDEN != m_eState &&
1838 nContentType == ContentTypeId::POSTIT &&
1839 !m_pActiveShell->GetView().GetDocShell()->IsReadOnly() &&
1840 pType->GetMemberCount() > 0)
1841 bRemovePostItEntries = false;
1842 }
1843 }
1844 }
1845
1846 if (bRemoveToggleExpandEntry)
1847 xPop->remove(OString::number(800));
1848
1849 if (bRemoveGotoEntry)
1850 xPop->remove(OString::number(900));
1851
1852 if (bRemoveSelectEntry)
1853 xPop->remove(OString::number(805));
1854
1855 if (bRemoveChapterEntries)
1856 {
1857 xPop->remove(OString::number(801));
1858 xPop->remove(OString::number(802));
1859 xPop->remove(OString::number(803));
1860 xPop->remove(OString::number(804));
1861 }
1862
1863 if (bRemoveSendOutlineEntry)
1864 xPop->remove(OString::number(700));
1865
1866 if (bRemovePostItEntries)
1867 {
1868 xPop->remove(OString::number(600));
1869 xPop->remove(OString::number(601));
1870 xPop->remove(OString::number(602));
1871 }
1872
1873 if (bRemoveDeleteChapterEntry)
1874 xPop->remove("deletechapter");
1875 if (bRemoveDeleteTableEntry)
1876 xPop->remove("deletetable");
1877 if (bRemoveDeleteFrameEntry)
1878 xPop->remove("deleteframe");
1879 if (bRemoveDeleteImageEntry)
1880 xPop->remove("deleteimage");
1881 if (bRemoveDeleteOLEObjectEntry)
1882 xPop->remove("deleteoleobject");
1883 if (bRemoveDeleteBookmarkEntry)
1884 xPop->remove("deletebookmark");
1885 if (bRemoveDeleteHyperlinkEntry)
1886 xPop->remove("deletehyperlink");
1887 if (bRemoveDeleteIndexEntry)
1888 xPop->remove("deleteindex");
1889 if (bRemoveDeleteCommentEntry)
1890 xPop->remove("deletecomment");
1891 if (bRemoveDeleteDrawingObjectEntry)
1892 xPop->remove("deletedrawingobject");
1893 if (bRemoveDeleteFieldEntry)
1894 xPop->remove("deletefield");
1895
1896 bool bRemoveDeleteEntry =
1897 bRemoveDeleteChapterEntry &&
1898 bRemoveDeleteTableEntry &&
1899 bRemoveDeleteFrameEntry &&
1900 bRemoveDeleteImageEntry &&
1901 bRemoveDeleteOLEObjectEntry &&
1902 bRemoveDeleteBookmarkEntry &&
1903 bRemoveDeleteHyperlinkEntry &&
1904 bRemoveDeleteIndexEntry &&
1905 bRemoveDeleteCommentEntry &&
1906 bRemoveDeleteDrawingObjectEntry &&
1907 bRemoveDeleteFieldEntry;
1908
1909 if (bRemoveRenameEntry)
1910 xPop->remove(OString::number(502));
1911
1912 if (bRemoveIndexEntries)
1913 {
1914 xPop->remove(OString::number(401));
1915 xPop->remove(OString::number(402));
1916 xPop->remove(OString::number(405));
1917 }
1918
1919 if (bRemoveUnprotectEntry)
1920 xPop->remove(OString::number(404));
1921
1922 if (bRemoveEditEntry)
1923 xPop->remove(OString::number(403));
1924
1925 if (bRemoveToggleExpandEntry &&
1926 bRemoveSendOutlineEntry)
1927 xPop->remove("separator1");
1928
1929 if (bRemoveCopyEntry)
1930 xPop->remove("copy");
1931
1932 if (bRemoveGotoEntry &&
1933 bRemoveCopyEntry &&
1934 bRemoveSelectEntry &&
1935 bRemoveDeleteEntry &&
1936 bRemoveChapterEntries &&
1937 bRemovePostItEntries &&
1938 bRemoveRenameEntry &&
1939 bRemoveIndexEntries &&
1940 bRemoveUnprotectEntry &&
1941 bRemoveEditEntry)
1942 xPop->remove("separator2");
1943
1944 if (!bOutline)
1945 {
1946 xSubPop1.reset();
1947 xPop->remove(OString::number(1)); // outline level menu
1948 }
1949 if (!bOutline || State::HIDDEN == m_eState)
1950 {
1951 xSubPopOutlineTracking.reset();
1952 xPop->remove(OString::number(4)); // outline tracking menu
1953 }
1954 if (!bOutline || State::HIDDEN == m_eState ||
1955 !m_pActiveShell->GetViewOptions()->IsShowOutlineContentVisibilityButton() ||
1956 m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount() == 0)
1957 {
1958 xSubPopOutlineContent.reset();
1959 xPop->remove(OString::number(5)); // outline folding menu
1960 xPop->remove("separator3");
1961 }
1962
1963 if (bRemoveTableTracking)
1964 xPop->remove("tabletracking");
1965 if (bRemoveSectionTracking)
1966 xPop->remove("sectiontracking");
1967 if (bRemoveFrameTracking)
1968 xPop->remove("frametracking");
1969 if (bRemoveImageTracking)
1970 xPop->remove("imagetracking");
1971 if (bRemoveOLEobjectTracking)
1972 xPop->remove("oleobjecttracking");
1973 if (bRemoveBookmarkTracking)
1974 xPop->remove("bookmarktracking");
1975 if (bRemoveHyperlinkTracking)
1976 xPop->remove("hyperlinktracking");
1977 if (bRemoveReferenceTracking)
1978 xPop->remove("referencetracking");
1979 if (bRemoveIndexTracking)
1980 xPop->remove("indextracking");
1981 if (bRemoveCommentTracking)
1982 xPop->remove("commenttracking");
1983 if (bRemoveDrawingObjectTracking)
1984 xPop->remove("drawingobjecttracking");
1985 if (bRemoveFieldTracking)
1986 xPop->remove("fieldtracking");
1987 if (bRemoveFootnoteTracking)
1988 xPop->remove("footnotetracking");
1989 if (bRemoveEndnoteTracking)
1990 xPop->remove("endnotetracking");
1991 if (bRemoveSortEntry)
1992 xPop->remove("sort");
1993
1994 bool bSetSensitiveCollapseAllCategories = false;
1995 if (!m_bIsRoot && xEntry)
1996 {
1997 bool bEntry = m_xTreeView->get_iter_first(*xEntry);
1998 while (bEntry)
1999 {
2000 if (m_xTreeView->get_row_expanded(*xEntry))
2001 {
2002 bSetSensitiveCollapseAllCategories = true;
2003 break;
2004 }
2005 bEntry = m_xTreeView->iter_next_sibling(*xEntry);
2006 }
2007 }
2008 xPop->set_sensitive("collapseallcategories", bSetSensitiveCollapseAllCategories);
2009
2010 OString sCommand = xPop->popup_at_rect(m_xTreeView.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)));
2011 if (!sCommand.isEmpty())
2012 ExecuteContextMenuAction(sCommand);
2013
2014 return true;
2015}
2016
2017void SwContentTree::insert(const weld::TreeIter* pParent, const OUString& rStr, const OUString& rId,
2018 bool bChildrenOnDemand, weld::TreeIter* pRet)
2019{
2020 m_xTreeView->insert(pParent, -1, &rStr, &rId, nullptr, nullptr, bChildrenOnDemand, pRet);
2021 ++m_nEntryCount;
2022}
2023
2025{
2026 if (m_xTreeView->iter_has_child(rIter))
2027 {
2028 std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator(&rIter);
2029 (void)m_xTreeView->iter_children(*xChild);
2030 remove(*xChild);
2031 }
2032 m_xTreeView->remove(rIter);
2033 --m_nEntryCount;
2034}
2035
2036// Content will be integrated into the Box only on demand.
2038{
2039 bool bChild = m_xTreeView->iter_has_child(rParent);
2040 if (bChild || !m_xTreeView->get_children_on_demand(rParent))
2041 return bChild;
2042
2043 // Is this a content type?
2044 if (lcl_IsContentType(rParent, *m_xTreeView))
2045 {
2046 std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator();
2047
2048 assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent))));
2049 SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent));
2050
2051 const size_t nCount = pCntType->GetMemberCount();
2052 // Add for outline plus/minus
2053 if (pCntType->GetType() == ContentTypeId::OUTLINE)
2054 {
2055 std::vector<std::unique_ptr<weld::TreeIter>> aParentCandidates;
2056 for(size_t i = 0; i < nCount; ++i)
2057 {
2058 const SwContent* pCnt = pCntType->GetMember(i);
2059 if(pCnt)
2060 {
2061 const auto nLevel = static_cast<const SwOutlineContent*>(pCnt)->GetOutlineLevel();
2062 OUString sEntry = pCnt->GetName();
2063 if(sEntry.isEmpty())
2064 sEntry = m_sSpace;
2065 OUString sId(weld::toId(pCnt));
2066
2067 auto lamba = [nLevel, this](const std::unique_ptr<weld::TreeIter>& entry)
2068 {
2069 return lcl_IsLowerOutlineContent(*entry, *m_xTreeView, nLevel);
2070 };
2071
2072 // if there is a preceding outline node candidate with a lower outline level use
2073 // that as a parent, otherwise use the root node
2074 auto aFind = std::find_if(aParentCandidates.rbegin(), aParentCandidates.rend(), lamba);
2075 if (aFind != aParentCandidates.rend())
2076 insert(aFind->get(), sEntry, sId, false, xChild.get());
2077 else
2078 insert(&rParent, sEntry, sId, false, xChild.get());
2079 m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible());
2080 m_xTreeView->set_extra_row_indent(*xChild, nLevel + 1 - m_xTreeView->get_iter_depth(*xChild));
2081
2082 // remove any parent candidates equal to or higher than this node
2083 aParentCandidates.erase(std::remove_if(aParentCandidates.begin(), aParentCandidates.end(),
2084 std::not_fn(lamba)), aParentCandidates.end());
2085
2086 // add this node as a parent candidate for any following nodes at a higher outline level
2087 aParentCandidates.emplace_back(m_xTreeView->make_iterator(xChild.get()));
2088
2089 bChild = true;
2090 }
2091 }
2092 }
2093 else
2094 {
2095 bool bRegion = pCntType->GetType() == ContentTypeId::REGION;
2096 for(size_t i = 0; i < nCount; ++i)
2097 {
2098 const SwContent* pCnt = pCntType->GetMember(i);
2099 if (pCnt)
2100 {
2101 OUString sEntry = pCnt->GetName();
2102 if (sEntry.isEmpty())
2103 sEntry = m_sSpace;
2104 OUString sId(weld::toId(pCnt));
2105 insert(&rParent, sEntry, sId, false, xChild.get());
2106 m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible());
2107 if (bRegion)
2108 m_xTreeView->set_extra_row_indent(*xChild, static_cast<const SwRegionContent*>(pCnt)->GetRegionLevel());
2109 bChild = true;
2110 }
2111 }
2112 }
2113 }
2114
2115 return bChild;
2116}
2117
2119{
2120 SdrObject *pRetObj = nullptr;
2121 switch(pCnt->GetParent()->GetType())
2122 {
2124 {
2125 SdrView* pDrawView = m_pActiveShell->GetDrawView();
2126 if (pDrawView)
2127 {
2129 SdrPage* pPage = pDrawModel->GetPage(0);
2130 const size_t nCount = pPage->GetObjCount();
2131
2132 for( size_t i=0; i<nCount; ++i )
2133 {
2134 SdrObject* pTemp = pPage->GetObj(i);
2135 if( pTemp->GetName() == pCnt->GetName())
2136 {
2137 pRetObj = pTemp;
2138 break;
2139 }
2140 }
2141 }
2142 break;
2143 }
2144 default:
2145 pRetObj = nullptr;
2146 }
2147 return pRetObj;
2148}
2149
2150void SwContentTree::Expand(const weld::TreeIter& rParent, std::vector<std::unique_ptr<weld::TreeIter>>* pNodesToExpand)
2151{
2152 if (!(m_xTreeView->iter_has_child(rParent) || m_xTreeView->get_children_on_demand(rParent)))
2153 return;
2154
2155 if (!m_bIsRoot
2156 || (lcl_IsContentType(rParent, *m_xTreeView) &&
2157 weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent))->GetType() == ContentTypeId::OUTLINE)
2159 {
2160 if (lcl_IsContentType(rParent, *m_xTreeView))
2161 {
2162 SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent));
2163 const sal_Int32 nOr = 1 << static_cast<int>(pCntType->GetType()); //linear -> Bitposition
2164 if (State::HIDDEN != m_eState)
2165 {
2166 m_nActiveBlock |= nOr;
2168 }
2169 else
2170 m_nHiddenBlock |= nOr;
2171 if (pCntType->GetType() == ContentTypeId::OUTLINE)
2172 {
2173 std::map< void*, bool > aCurrOutLineNodeMap;
2174
2175 SwWrtShell* pShell = GetWrtShell();
2176 bool bParentHasChild = RequestingChildren(rParent);
2177 if (pNodesToExpand)
2178 pNodesToExpand->emplace_back(m_xTreeView->make_iterator(&rParent));
2179 if (bParentHasChild)
2180 {
2181 std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(&rParent));
2182 bool bChild = m_xTreeView->iter_next(*xChild);
2183 while (bChild && lcl_IsContent(*xChild, *m_xTreeView))
2184 {
2185 if (m_xTreeView->iter_has_child(*xChild))
2186 {
2187 assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xChild))));
2188 auto const nPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xChild))->GetOutlinePos();
2189 void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos ));
2190 aCurrOutLineNodeMap.emplace( key, false );
2191 std::map<void*, bool>::iterator iter = mOutLineNodeMap.find( key );
2192 if( iter != mOutLineNodeMap.end() && mOutLineNodeMap[key])
2193 {
2194 aCurrOutLineNodeMap[key] = true;
2195 RequestingChildren(*xChild);
2196 if (pNodesToExpand)
2197 pNodesToExpand->emplace_back(m_xTreeView->make_iterator(xChild.get()));
2198 m_xTreeView->set_children_on_demand(*xChild, false);
2199 }
2200 }
2201 bChild = m_xTreeView->iter_next(*xChild);
2202 }
2203 }
2204 mOutLineNodeMap = aCurrOutLineNodeMap;
2205 return;
2206 }
2207 }
2208 else
2209 {
2210 if (lcl_IsContent(rParent, *m_xTreeView))
2211 {
2212 SwWrtShell* pShell = GetWrtShell();
2213 // paranoid assert now that outline type is checked
2214 assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent))));
2215 auto const nPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rParent))->GetOutlinePos();
2216 void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos ));
2217 mOutLineNodeMap[key] = true;
2218 }
2219 }
2220 }
2221
2222 RequestingChildren(rParent);
2223 if (pNodesToExpand)
2224 pNodesToExpand->emplace_back(m_xTreeView->make_iterator(&rParent));
2225}
2226
2227IMPL_LINK(SwContentTree, ExpandHdl, const weld::TreeIter&, rParent, bool)
2228{
2229 Expand(rParent, nullptr);
2230 return true;
2231}
2232
2233IMPL_LINK(SwContentTree, CollapseHdl, const weld::TreeIter&, rParent, bool)
2234{
2235 if (!m_xTreeView->iter_has_child(rParent) || m_xTreeView->get_children_on_demand(rParent))
2236 return true;
2237
2238 if (lcl_IsContentType(rParent, *m_xTreeView))
2239 {
2240 if (m_bIsRoot)
2241 {
2242 // collapse to children of root node
2243 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(&rParent));
2244 if (m_xTreeView->iter_children(*xEntry))
2245 {
2246 do
2247 {
2248 m_xTreeView->collapse_row(*xEntry);
2249 }
2250 while (m_xTreeView->iter_next(*xEntry));
2251 }
2252 return false; // return false to notify caller not to do collapse
2253 }
2254 SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(rParent));
2255 const sal_Int32 nAnd = ~(1 << static_cast<int>(pCntType->GetType()));
2256 if (State::HIDDEN != m_eState)
2257 {
2258 m_nActiveBlock &= nAnd;
2259 m_pConfig->SetActiveBlock(m_nActiveBlock);
2260 }
2261 else
2262 m_nHiddenBlock &= nAnd;
2263 }
2264 else if (lcl_IsContent(rParent, *m_xTreeView))
2265 {
2266 SwWrtShell* pShell = GetWrtShell();
2267 assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(rParent))));
2268 auto const nPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rParent))->GetOutlinePos();
2269 void* key = static_cast<void*>(pShell->getIDocumentOutlineNodesAccess()->getOutlineNode( nPos ));
2270 mOutLineNodeMap[key] = false;
2271 }
2272
2273 return true;
2274}
2275
2276// Also on double click will be initially opened only.
2277IMPL_LINK_NOARG(SwContentTree, ContentDoubleClickHdl, weld::TreeView&, bool)
2278{
2279 bool bConsumed = false;
2280
2281 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
2282 bool bEntry = m_xTreeView->get_cursor(xEntry.get());
2283 // Is it a content type?
2284 OSL_ENSURE(bEntry, "no current entry!");
2285 if (bEntry)
2286 {
2287 if (lcl_IsContentType(*xEntry, *m_xTreeView) && !m_xTreeView->iter_has_child(*xEntry))
2288 {
2289 RequestingChildren(*xEntry);
2290 m_xTreeView->set_children_on_demand(*xEntry, false);
2291 }
2292 else if (!lcl_IsContentType(*xEntry, *m_xTreeView) && (State::HIDDEN != m_eState))
2293 {
2294 assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
2295 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry));
2296 assert(pCnt && "no UserData");
2297 if (pCnt && !pCnt->IsInvisible())
2298 {
2299 if (State::CONSTANT == m_eState)
2300 {
2301 m_pActiveShell->GetView().GetViewFrame()->GetWindow().ToTop();
2302 }
2303 //Jump to content type:
2304 GotoContent(pCnt);
2305 // fdo#36308 don't expand outlines on double-click
2306 bConsumed = pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE;
2307 }
2308 }
2309 }
2310
2311 return bConsumed; // false/true == allow/disallow more to be done, i.e. expand/collapse children
2312}
2313
2314namespace
2315{
2316 OUString GetImageIdForContentTypeId(ContentTypeId eType)
2317 {
2318 OUString sResId;
2319
2320 switch (eType)
2321 {
2323 sResId = RID_BMP_NAVI_OUTLINE;
2324 break;
2326 sResId = RID_BMP_NAVI_TABLE;
2327 break;
2329 sResId = RID_BMP_NAVI_FRAME;
2330 break;
2332 sResId = RID_BMP_NAVI_GRAPHIC;
2333 break;
2334 case ContentTypeId::OLE:
2335 sResId = RID_BMP_NAVI_OLE;
2336 break;
2338 sResId = RID_BMP_NAVI_BOOKMARK;
2339 break;
2341 sResId = RID_BMP_NAVI_REGION;
2342 break;
2344 sResId = RID_BMP_NAVI_URLFIELD;
2345 break;
2347 sResId = RID_BMP_NAVI_REFERENCE;
2348 break;
2350 sResId = RID_BMP_NAVI_INDEX;
2351 break;
2353 sResId = RID_BMP_NAVI_POSTIT;
2354 break;
2356 sResId = RID_BMP_NAVI_DRAWOBJECT;
2357 break;
2359 sResId = RID_BMP_NAVI_TEXTFIELD;
2360 break;
2362 sResId = RID_BMP_NAVI_FOOTNOTE;
2363 break;
2365 sResId = RID_BMP_NAVI_ENDNOTE;
2366 break;
2368 SAL_WARN("sw.ui", "ContentTypeId::UNKNOWN has no bitmap preview");
2369 break;
2370 }
2371
2372 return sResId;
2373 };
2374}
2375
2377{
2378 return weld::GetAbsPos(*m_xTreeView, rIter);
2379}
2380
2382{
2383 return m_nEntryCount;
2384}
2385
2387{
2388 if (!m_xTreeView->iter_has_child(rParent))
2389 return 0;
2390
2391 std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rParent));
2392
2393 size_t nCount = 0;
2394 auto nRefDepth = m_xTreeView->get_iter_depth(*xParent);
2395 auto nActDepth = nRefDepth;
2396 do
2397 {
2398 if (!m_xTreeView->iter_next(*xParent))
2399 xParent.reset();
2400 else
2401 nActDepth = m_xTreeView->get_iter_depth(*xParent);
2402 nCount++;
2403 } while(xParent && nRefDepth < nActDepth);
2404
2405 nCount--;
2406 return nCount;
2407}
2408
2409std::unique_ptr<weld::TreeIter> SwContentTree::GetEntryAtAbsPos(size_t nAbsPos) const
2410{
2411 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
2412 if (!m_xTreeView->get_iter_first(*xEntry))
2413 xEntry.reset();
2414
2415 while (nAbsPos && xEntry)
2416 {
2417 if (!m_xTreeView->iter_next(*xEntry))
2418 xEntry.reset();
2419 nAbsPos--;
2420 }
2421 return xEntry;
2422}
2423
2424void SwContentTree::Display( bool bActive )
2425{
2426 // First read the selected entry to select it later again if necessary
2427 // -> the user data here are no longer valid!
2428 std::unique_ptr<weld::TreeIter> xOldSelEntry(m_xTreeView->make_iterator());
2429 if (!m_xTreeView->get_selected(xOldSelEntry.get()))
2430 xOldSelEntry.reset();
2431 OUString sOldSelEntryId;
2432 size_t nEntryRelPos = 0; // relative position to their parent
2433 size_t nOldEntryCount = GetEntryCount();
2434 sal_Int32 nOldScrollPos = 0;
2435 if (xOldSelEntry)
2436 {
2438 sOldSelEntryId = m_xTreeView->get_id(*xOldSelEntry);
2439 nOldScrollPos = m_xTreeView->vadjustment_get_value();
2440 std::unique_ptr<weld::TreeIter> xParentEntry = m_xTreeView->make_iterator(xOldSelEntry.get());
2441 while (m_xTreeView->get_iter_depth(*xParentEntry))
2442 m_xTreeView->iter_parent(*xParentEntry);
2443 if (m_xTreeView->get_iter_depth(*xOldSelEntry))
2444 nEntryRelPos = GetAbsPos(*xOldSelEntry) - GetAbsPos(*xParentEntry);
2445 }
2446
2447 clear();
2448
2449 if (!bActive)
2451 else if (State::HIDDEN == m_eState)
2453 SwWrtShell* pShell = GetWrtShell();
2454 const bool bReadOnly = !pShell || pShell->GetView().GetDocShell()->IsReadOnly();
2456 {
2458 bool bDisable = pShell == nullptr || bReadOnly;
2460 pNavi->m_xContent6ToolBox->set_item_sensitive("chapterup", !bDisable);
2461 pNavi->m_xContent6ToolBox->set_item_sensitive("chapterdown", !bDisable);
2462 pNavi->m_xContent6ToolBox->set_item_sensitive("promote", !bDisable);
2463 pNavi->m_xContent6ToolBox->set_item_sensitive("demote", !bDisable);
2464 pNavi->m_xContent5ToolBox->set_item_sensitive("reminder", !bDisable);
2465 }
2466
2467 if (pShell)
2468 {
2469 std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
2470 std::unique_ptr<weld::TreeIter> xCntTypeEntry;
2471 std::vector<std::unique_ptr<weld::TreeIter>> aNodesToExpand;
2472 // all content navigation view
2474 {
2475 m_xTreeView->freeze();
2476
2478 {
2479 std::unique_ptr<SwContentType>& rpContentT = bActive ?
2480 m_aActiveContentArr[nCntType] :
2481 m_aHiddenContentArr[nCntType];
2482 if(!rpContentT)
2483 rpContentT.reset(new SwContentType(pShell, nCntType, m_nOutlineLevel ));
2484
2485 OUString aImage(GetImageIdForContentTypeId(nCntType));
2486 bool bChOnDemand = 0 != rpContentT->GetMemberCount();
2487 OUString sId(weld::toId(rpContentT.get()));
2488 insert(nullptr, rpContentT->GetName(), sId, bChOnDemand, xEntry.get());
2489 m_xTreeView->set_image(*xEntry, aImage);
2490
2491 m_xTreeView->set_sensitive(*xEntry, bChOnDemand);
2492
2493 if (nCntType == m_nLastSelType)
2494 xCntTypeEntry = m_xTreeView->make_iterator(xEntry.get());
2495
2496 sal_Int32 nExpandOptions = (State::HIDDEN == m_eState)
2499 if (nExpandOptions & (1 << static_cast<int>(nCntType)))
2500 {
2501 // fill contents of to-be expanded entries while frozen
2502 Expand(*xEntry, &aNodesToExpand);
2503 m_xTreeView->set_children_on_demand(*xEntry, false);
2504 }
2505 }
2506
2507 m_xTreeView->thaw();
2508
2509 // restore visual expanded tree state
2510 for (const auto& rNode : aNodesToExpand)
2511 m_xTreeView->expand_row(*rNode);
2512 }
2513 // root content navigation view
2514 else
2515 {
2516 m_xTreeView->freeze();
2517
2518 std::unique_ptr<SwContentType>& rpRootContentT = bActive ?
2521 if(!rpRootContentT)
2522 rpRootContentT.reset(new SwContentType(pShell, m_nRootType, m_nOutlineLevel ));
2523 OUString aImage(GetImageIdForContentTypeId(m_nRootType));
2524 bool bChOnDemand = m_nRootType == ContentTypeId::OUTLINE;
2525 OUString sId(weld::toId(rpRootContentT.get()));
2526 insert(nullptr, rpRootContentT->GetName(), sId, bChOnDemand, xEntry.get());
2527 m_xTreeView->set_image(*xEntry, aImage);
2528
2529 xCntTypeEntry = m_xTreeView->make_iterator(xEntry.get());
2530
2531 if (!bChOnDemand)
2532 {
2533 bool bRegion = rpRootContentT->GetType() == ContentTypeId::REGION;
2534
2535 std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator();
2536 for (size_t i = 0; i < rpRootContentT->GetMemberCount(); ++i)
2537 {
2538 const SwContent* pCnt = rpRootContentT->GetMember(i);
2539 if (pCnt)
2540 {
2541 OUString sEntry = pCnt->GetName();
2542 if(sEntry.isEmpty())
2543 sEntry = m_sSpace;
2544 OUString sSubId(weld::toId(pCnt));
2545 insert(xEntry.get(), sEntry, sSubId, false, xChild.get());
2546 m_xTreeView->set_sensitive(*xChild, !pCnt->IsInvisible());
2547 if (bRegion)
2548 m_xTreeView->set_extra_row_indent(*xChild, static_cast<const SwRegionContent*>(pCnt)->GetRegionLevel());
2549 }
2550 }
2551 }
2552 else
2553 {
2554 // fill contents of to-be expanded entries while frozen
2555 Expand(*xEntry, &aNodesToExpand);
2556 m_xTreeView->set_children_on_demand(*xEntry, false);
2557 }
2558
2559 m_xTreeView->set_sensitive(*xEntry, m_xTreeView->iter_has_child(*xEntry));
2560
2561 m_xTreeView->thaw();
2562
2563 if (bChOnDemand)
2564 {
2565 // restore visual expanded tree state
2566 for (const auto& rNode : aNodesToExpand)
2567 m_xTreeView->expand_row(*rNode);
2568 }
2569 else
2570 m_xTreeView->expand_row(*xEntry);
2571 }
2572
2573 // Reselect the old selected entry. If it is not available, select the entry at the old
2574 // selected entry position unless that entry position is now a content type or is past the
2575 // end of the member list then select the entry at the previous entry position.
2576 if (xOldSelEntry)
2577 {
2578 std::unique_ptr<weld::TreeIter> xSelEntry = m_xTreeView->make_iterator(xCntTypeEntry.get());
2579 if (nEntryRelPos)
2580 {
2581 std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator(xCntTypeEntry.get()));
2582 std::unique_ptr<weld::TreeIter> xTemp(m_xTreeView->make_iterator(xIter.get()));
2583 sal_uLong nPos = 1;
2584 bool bNext;
2585 while ((bNext = m_xTreeView->iter_next(*xIter) && lcl_IsContent(*xIter, *m_xTreeView)))
2586 {
2587 if (m_xTreeView->get_id(*xIter) == sOldSelEntryId || nPos == nEntryRelPos)
2588 {
2589 m_xTreeView->copy_iterator(*xIter, *xSelEntry);
2590 break;
2591 }
2592 m_xTreeView->copy_iterator(*xIter, *xTemp); // note previous entry
2593 nPos++;
2594 }
2595 if (!bNext)
2596 xSelEntry = std::move(xTemp);
2597 }
2598 // set_cursor unselects all entries, makes passed entry visible, and selects it
2599 m_xTreeView->set_cursor(*xSelEntry);
2600 Select();
2601 }
2602 }
2603
2604 if (!m_bIgnoreDocChange && GetEntryCount() == nOldEntryCount)
2605 {
2606 m_xTreeView->vadjustment_set_value(nOldScrollPos);
2607 }
2608}
2609
2611{
2612 m_xTreeView->freeze();
2613 m_xTreeView->clear();
2614 m_nEntryCount = 0;
2615 m_xTreeView->thaw();
2616}
2617
2619 sal_Int8& rDragMode )
2620{
2621 bool bRet = false;
2622 SwWrtShell* pWrtShell = GetWrtShell();
2623 OSL_ENSURE(pWrtShell, "no Shell!");
2624
2625 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
2626 bool bEntry = m_xTreeView->get_cursor(xEntry.get());
2627 if (!bEntry || lcl_IsContentType(*xEntry, *m_xTreeView) || !pWrtShell)
2628 return false;
2629 OUString sEntry;
2630 assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
2631 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry));
2632
2633 const ContentTypeId nActType = pCnt->GetParent()->GetType();
2634 OUString sUrl;
2635 bool bOutline = false;
2636 OUString sOutlineText;
2637 switch( nActType )
2638 {
2640 {
2641 const SwOutlineNodes::size_type nPos = static_cast<SwOutlineContent*>(pCnt)->GetOutlinePos();
2642 OSL_ENSURE(nPos < pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineNodesCount(),
2643 "outlinecnt changed");
2644
2645 // make sure outline may actually be copied
2646 if( pWrtShell->IsOutlineCopyable( nPos ) )
2647 {
2648 const SwNumRule* pOutlRule = pWrtShell->GetOutlineNumRule();
2649 const SwTextNode* pTextNd =
2651 if (pTextNd && pOutlRule && pTextNd->IsNumbered(pWrtShell->GetLayout()))
2652 {
2653 SwNumberTree::tNumberVector aNumVector =
2654 pTextNd->GetNumberVector(pWrtShell->GetLayout());
2655 for( int nLevel = 0;
2656 nLevel <= pTextNd->GetActualListLevel();
2657 nLevel++ )
2658 {
2659 const SwNumberTree::tSwNumTreeNumber nVal = aNumVector[nLevel] + 1;
2660 sEntry += OUString::number( nVal - pOutlRule->Get(nLevel).GetStart() ) + ".";
2661 }
2662 }
2663 sEntry += pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText(nPos, pWrtShell->GetLayout(), false);
2664 sOutlineText = pWrtShell->getIDocumentOutlineNodesAccess()->getOutlineText(nPos, pWrtShell->GetLayout());
2665 m_bIsOutlineMoveable = static_cast<SwOutlineContent*>(pCnt)->IsMoveable();
2666 bOutline = true;
2667 }
2668 }
2669 break;
2674 // cannot be inserted, neither as URL nor as section
2675 break;
2677 sUrl = static_cast<SwURLFieldContent*>(pCnt)->GetURL();
2678 [[fallthrough]];
2679 case ContentTypeId::OLE:
2681 if(GetParentWindow()->GetRegionDropMode() != RegionMode::NONE)
2682 break;
2683 else
2684 rDragMode &= ~( DND_ACTION_MOVE | DND_ACTION_LINK );
2685 [[fallthrough]];
2686 default:
2687 sEntry = m_xTreeView->get_text(*xEntry);
2688 }
2689
2690 if(!sEntry.isEmpty())
2691 {
2692 const SwDocShell* pDocShell = pWrtShell->GetView().GetDocShell();
2693 if(sUrl.isEmpty())
2694 {
2695 if(pDocShell->HasName())
2696 {
2697 SfxMedium* pMedium = pDocShell->GetMedium();
2698 sUrl = pMedium->GetURLObject().GetURLNoMark();
2699 // only if a primarily link shall be integrated.
2700 bRet = true;
2701 }
2702 else if ( nActType == ContentTypeId::REGION || nActType == ContentTypeId::BOOKMARK )
2703 {
2704 // For field and bookmarks a link is also allowed
2705 // without a filename into its own document.
2706 bRet = true;
2707 }
2708 else if (State::CONSTANT == m_eState &&
2709 ( !::GetActiveView() ||
2711 {
2712 // Urls of inactive views cannot dragged without
2713 // file names, also.
2714 bRet = false;
2715 }
2716 else
2717 {
2718 bRet = GetParentWindow()->GetRegionDropMode() == RegionMode::NONE;
2719 rDragMode = DND_ACTION_MOVE;
2720 }
2721
2722 const OUString& rToken = pCnt->GetParent()->GetTypeToken();
2723 sUrl += "#" + sEntry;
2724 if(!rToken.isEmpty())
2725 {
2726 sUrl += OUStringChar(cMarkSeparator) + rToken;
2727 }
2728 }
2729 else
2730 bRet = true;
2731
2732 if( bRet )
2733 {
2734 // In Outlines of heading text must match
2735 // the real number into the description.
2736 if(bOutline)
2737 sEntry = sOutlineText;
2738
2739 {
2740 NaviContentBookmark aBmk( sUrl, sEntry,
2741 GetParentWindow()->GetRegionDropMode(),
2742 pDocShell);
2743 aBmk.Copy( rTransfer );
2744 }
2745
2746 // An INetBookmark must a be delivered to foreign DocShells
2747 if( pDocShell->HasName() )
2748 {
2749 INetBookmark aBkmk( sUrl, sEntry );
2750 rTransfer.CopyINetBookmark( aBkmk );
2751 }
2752 }
2753 }
2754 return bRet;
2755}
2756
2758{
2759 if(!m_bIsRoot)
2760 {
2761 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
2762 bool bEntry = m_xTreeView->get_cursor(xEntry.get());
2763 if (bEntry)
2764 {
2765 const SwContentType* pCntType;
2766 if (lcl_IsContentType(*xEntry, *m_xTreeView))
2767 {
2768 assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
2769 pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry));
2770 }
2771 else
2772 {
2773 assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
2774 pCntType = weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->GetParent();
2775 }
2776 m_nRootType = pCntType->GetType();
2777 m_bIsRoot = true;
2779 {
2780 m_xTreeView->set_selection_mode(SelectionMode::Multiple);
2781 }
2783 }
2784 }
2785 else
2786 {
2787 m_xTreeView->set_selection_mode(SelectionMode::Single);
2790 m_bIsRoot = false;
2791 // Other content type member data could have changed while in root view. Fill the content
2792 // member lists excluding the toggled from root content which should already have the most
2793 // recent data.
2794 if (State::HIDDEN != m_eState)
2795 {
2797 {
2799 m_aActiveContentArr[i]->FillMemberList();
2800 }
2801 }
2803 }
2806 pBox->set_item_active("root", m_bIsRoot);
2807}
2808
2810{
2811 bool bContentChanged = false;
2812
2813// - Run through the local array and the Treelistbox in parallel.
2814// - Are the records not expanded, they are discarded only in the array
2815// and the content type will be set as the new UserData.
2816// - Is the root mode is active only this will be updated.
2817
2818// Valid for the displayed content types is:
2819// the Memberlist will be erased and the membercount will be updated
2820// If content will be checked, the memberlists will be replenished
2821// at the same time. Once a difference occurs it will be only replenished
2822// no longer checked. Finally, the box is filled again.
2823
2824 if (State::HIDDEN == m_eState)
2825 {
2827 {
2829 m_aActiveContentArr[i]->Invalidate();
2830 }
2831 return false;
2832 }
2833
2834 // root content navigation view
2835 if(m_bIsRoot)
2836 {
2837 std::unique_ptr<weld::TreeIter> xRootEntry(m_xTreeView->make_iterator());
2838 if (!m_xTreeView->get_iter_first(*xRootEntry))
2839 return true;
2840
2841 assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xRootEntry))));
2842 const ContentTypeId nType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xRootEntry))->GetType();
2843 SwContentType* pArrType = m_aActiveContentArr[nType].get();
2844 assert(weld::toId(pArrType) == m_xTreeView->get_id(*xRootEntry));
2845 if (!pArrType)
2846 return true;
2847
2848 pArrType->FillMemberList(&bContentChanged);
2849 if (bContentChanged)
2850 return true;
2851
2852 // FillMemberList tests if member count in old member array equals member count in new
2853 // member array. Test here for member count difference between array and tree.
2854 const size_t nChildCount = GetChildCount(*xRootEntry);
2855 if (nChildCount != pArrType->GetMemberCount())
2856 return true;
2857
2858 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(xRootEntry.get()));
2859 for (size_t j = 0; j < nChildCount; ++j)
2860 {
2861 if (!m_xTreeView->iter_next(*xEntry))
2862 {
2863 SAL_WARN("sw.ui", "unexpected missing entry");
2864 return true;
2865 }
2866
2867 // FillMemberList clears the content type member list and refills with new data.
2868 // Treeview entry user data is set here to the string representation of the pointer to
2869 // the member data in the array. The Display function will clear and recreate the
2870 // treeview from the content type member arrays if content change is detected.
2871 const SwContent* pCnt = pArrType->GetMember(j);
2872 OUString sSubId(weld::toId(pCnt));
2873 m_xTreeView->set_id(*xEntry, sSubId);
2874
2875 OUString sEntryText = m_xTreeView->get_text(*xEntry);
2876 if (sEntryText != pCnt->GetName() &&
2877 !(sEntryText == m_sSpace && pCnt->GetName().isEmpty()))
2878 {
2879 return true;
2880 }
2881 }
2882 }
2883 // all content navigation view
2884 else
2885 {
2886 // Fill member list for each content type and check for content change. If content change
2887 // is detected only fill member lists for remaining content types. The Display function
2888 // will clear and recreate the treeview from the content type member arrays if content has
2889 // changed.
2890 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
2891
2892 // lambda function to find the next content type entry
2893 auto lcl_nextContentTypeEntry = [this, &xEntry](){
2894 while (m_xTreeView->get_iter_depth(*xEntry))
2895 m_xTreeView->iter_parent(*xEntry);
2896 return m_xTreeView->iter_next_sibling(*xEntry);
2897 };
2898
2899 for (bool bEntry = m_xTreeView->get_iter_first(*xEntry); bEntry;
2900 bEntry = lcl_nextContentTypeEntry())
2901 {
2902 assert(dynamic_cast<SwContentType*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
2903 SwContentType* pCntType = weld::fromId<SwContentType*>(m_xTreeView->get_id(*xEntry));
2904 const size_t nCntCount = pCntType->GetMemberCount();
2905 const ContentTypeId nType = pCntType->GetType();
2906 SwContentType* pArrType = m_aActiveContentArr[nType].get();
2907 assert(weld::toId(pArrType) == m_xTreeView->get_id(*xEntry));
2908
2909 if (!pArrType)
2910 {
2911 bContentChanged = true;
2912 continue;
2913 }
2914
2915 // all content type member lists must be filled!
2916 if (bContentChanged)
2917 {
2918 // If content change has already been detected there is no need to detect
2919 // other content change so no argument is supplied here to FillMemberList.
2920 pArrType->FillMemberList();
2921 continue;
2922 }
2923
2924 pArrType->FillMemberList(&bContentChanged);
2925 if (bContentChanged)
2926 continue;
2927
2928 // does entry have children?
2929 if (m_xTreeView->get_row_expanded(*xEntry))
2930 {
2931 const size_t nChildCount = GetChildCount(*xEntry);
2932 if(nChildCount != pArrType->GetMemberCount())
2933 {
2934 bContentChanged = true;
2935 continue;
2936 }
2937
2938 for(size_t j = 0; j < nChildCount; ++j)
2939 {
2940 if (!m_xTreeView->iter_next(*xEntry))
2941 {
2942 SAL_WARN("sw.ui", "unexpected missing entry");
2943 bContentChanged = true;
2944 continue;
2945 }
2946
2947 const SwContent* pCnt = pArrType->GetMember(j);
2948 OUString sSubId(weld::toId(pCnt));
2949 m_xTreeView->set_id(*xEntry, sSubId);
2950
2951 OUString sEntryText = m_xTreeView->get_text(*xEntry);
2952 if( sEntryText != pCnt->GetName() &&
2953 !(sEntryText == m_sSpace && pCnt->GetName().isEmpty()))
2954 {
2955 bContentChanged = true;
2956 continue;
2957 }
2958 }
2959 }
2960 // not expanded and has children
2961 else if (m_xTreeView->iter_has_child(*xEntry))
2962 {
2963 bool bRemoveChildren = false;
2964 const size_t nOldChildCount = GetChildCount(*xEntry);
2965 const size_t nNewChildCount = pArrType->GetMemberCount();
2966 if (nOldChildCount != nNewChildCount)
2967 {
2968 bRemoveChildren = true;
2969 }
2970 else
2971 {
2972 std::unique_ptr<weld::TreeIter> xChild(m_xTreeView->make_iterator(xEntry.get()));
2973 (void)m_xTreeView->iter_children(*xChild);
2974 for (size_t j = 0; j < nOldChildCount; ++j)
2975 {
2976 const SwContent* pCnt = pArrType->GetMember(j);
2977 OUString sSubId(weld::toId(pCnt));
2978 m_xTreeView->set_id(*xChild, sSubId);
2979 OUString sEntryText = m_xTreeView->get_text(*xChild);
2980 if( sEntryText != pCnt->GetName() &&
2981 !(sEntryText == m_sSpace && pCnt->GetName().isEmpty()))
2982 {
2983 bRemoveChildren = true;
2984 }
2985 (void)m_xTreeView->iter_next(*xChild);
2986 }
2987 }
2988 if (bRemoveChildren)
2989 {
2990 std::unique_ptr<weld::TreeIter> xRemove(m_xTreeView->make_iterator(xEntry.get()));
2991 while (m_xTreeView->iter_children(*xRemove))
2992 {
2993 remove(*xRemove);
2994 m_xTreeView->copy_iterator(*xEntry, *xRemove);
2995 }
2996 m_xTreeView->set_children_on_demand(*xEntry, nNewChildCount != 0);
2997 }
2998 }
2999 else if((nCntCount != 0)
3000 != (pArrType->GetMemberCount()!=0))
3001 {
3002 bContentChanged = true;
3003 continue;
3004 }
3005 }
3006 }
3007
3008 return bContentChanged;
3009}
3010
3012{
3013 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
3014 if (m_xTreeView->get_selected(xEntry.get()))
3015 {
3016 while (m_xTreeView->get_iter_depth(*xEntry))
3017 m_xTreeView->iter_parent(*xEntry);
3018 void* pId = weld::fromId<void*>(m_xTreeView->get_id(*xEntry));
3019 if (pId && lcl_IsContentType(*xEntry, *m_xTreeView))
3020 {
3021 assert(dynamic_cast<SwContentType*>(static_cast<SwTypeNumber*>(pId)));
3022 m_nLastSelType = static_cast<SwContentType*>(pId)->GetType();
3023 }
3024 }
3025}
3026
3028{
3030
3031 // If clear is called by TimerUpdate:
3032 // Only for root can the validity of the UserData be guaranteed.
3033 m_xTreeView->all_foreach([this](weld::TreeIter& rEntry){
3034 m_xTreeView->set_id(rEntry, "");
3035 return false;
3036 });
3037}
3038
3040{
3041 m_pHiddenShell = pSh;
3045 {
3046 m_aHiddenContentArr[i].reset();
3047 }
3048 Display(false);
3049
3051}
3052
3054{
3055 bool bClear = m_pActiveShell != pSh;
3056 if (State::ACTIVE == m_eState && bClear)
3057 {
3059 m_pActiveShell = pSh;
3061 clear();
3062 }
3063 else if (State::CONSTANT == m_eState)
3064 {
3066 m_pActiveShell = pSh;
3068 bClear = true;
3069 }
3070
3071 // tdf#148432 in LTR UI override the navigator treeview direction based on
3072 // the first page directionality
3074 {
3075 const SwPageDesc& rDesc = m_pActiveShell->GetPageDesc(0);
3076 const SvxFrameDirectionItem& rFrameDir = rDesc.GetMaster().GetFrameDir();
3077 m_xTreeView->set_direction(rFrameDir.GetValue() == SvxFrameDirection::Horizontal_RL_TB);
3078 }
3079
3080 // Only if it is the active view, the array will be deleted and
3081 // the screen filled new.
3082 if (State::ACTIVE == m_eState && bClear)
3083 {
3084 if (m_pActiveShell)
3088 {
3089 m_aActiveContentArr[i].reset();
3090 }
3091 Display(true);
3092 }
3093}
3094
3096{
3098 m_pActiveShell = pSh;
3103 {
3104 m_aActiveContentArr[i].reset();
3105 }
3106 Display(true);
3107}
3108
3110{
3111 SfxViewEventHint const*const pVEHint(dynamic_cast<SfxViewEventHint const*>(&rHint));
3112 SwXTextView* pDyingShell = nullptr;
3113 if (m_pActiveShell && pVEHint && pVEHint->GetEventName() == "OnViewClosed")
3114 pDyingShell = dynamic_cast<SwXTextView*>(pVEHint->GetController().get());
3115 if (pDyingShell && pDyingShell->GetView() == &m_pActiveShell->GetView())
3116 {
3117 SetActiveShell(nullptr); // our view is dying, clear our pointers to it
3118 }
3119 else
3120 {
3121 SfxListener::Notify(rBC, rHint);
3122 }
3123 switch (rHint.GetId())
3124 {
3125 case SfxHintId::SwNavigatorUpdateTracking:
3127 break;
3128 case SfxHintId::SwNavigatorSelectOutlinesWithSelections:
3129 {
3131 {
3133 // make first selected entry visible
3134 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
3135 if (xEntry && m_xTreeView->get_selected(xEntry.get()))
3136 m_xTreeView->scroll_to_row(*xEntry);
3137 }
3139 m_xTreeView->unselect_all();
3140 break;
3141 }
3142 case SfxHintId::DocChanged:
3143 if (!m_bIgnoreDocChange)
3144 {
3145 m_bDocHasChanged = true;
3146 TimerUpdate(&m_aUpdTimer);
3147 }
3148 break;
3149 case SfxHintId::ModeChanged:
3150 if (SwWrtShell* pShell = GetWrtShell())
3151 {
3152 const bool bReadOnly = pShell->GetView().GetDocShell()->IsReadOnly();
3154 {
3156
3157 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
3158 if (m_xTreeView->get_cursor(xEntry.get()))
3159 {
3160 m_xTreeView->select(*xEntry);
3161 Select();
3162 }
3163 else
3164 m_xTreeView->unselect_all();
3165 }
3166 }
3167 break;
3168 default:
3169 break;
3170 }
3171}
3172
3173void SwContentTree::ExecCommand(std::string_view rCmd, bool bOutlineWithChildren)
3174{
3176
3177 const bool bUp = rCmd == "chapterup";
3178 const bool bUpDown = bUp || rCmd == "chapterdown";
3179 const bool bLeft = rCmd == "promote";
3180 const bool bLeftRight = bLeft || rCmd == "demote";
3181 if (!bUpDown && !bLeftRight)
3182 return;
3183 if (GetWrtShell()->GetView().GetDocShell()->IsReadOnly() ||
3184 (State::ACTIVE != m_eState &&
3185 (State::CONSTANT != m_eState || m_pActiveShell != GetParentWindow()->GetCreateView()->GetWrtShellPtr())))
3186 {
3187 return;
3188 }
3189
3190 m_bIgnoreDocChange = true;
3191
3192 SwWrtShell *const pShell = GetWrtShell();
3193 sal_Int8 nActOutlineLevel = m_nOutlineLevel;
3194 SwOutlineNodes::size_type nActPos = pShell->GetOutlinePos(nActOutlineLevel);
3195
3196 std::vector<SwTextNode*> selectedOutlineNodes;
3197 std::vector<std::unique_ptr<weld::TreeIter>> selected;
3198
3199 m_xTreeView->selected_foreach([this, pShell, &bLeftRight, &bOutlineWithChildren, &selected, &selectedOutlineNodes](weld::TreeIter& rEntry){
3200 // it's possible to select the root node too which is a really bad idea
3201 bool bSkip = lcl_IsContentType(rEntry, *m_xTreeView);
3202 // filter out children of selected parents so they don't get promoted
3203 // or moved twice (except if there is Ctrl modifier, since in that
3204 // case children are re-parented)
3205 if ((bLeftRight || bOutlineWithChildren) && !selected.empty())
3206 {
3207 std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rEntry));
3208 for (bool bParent = m_xTreeView->iter_parent(*xParent); bParent; bParent = m_xTreeView->iter_parent(*xParent))
3209 {
3210 if (m_xTreeView->iter_compare(*selected.back(), *xParent) == 0)
3211 {
3212 bSkip = true;
3213 break;
3214 }
3215 }
3216 }
3217 if (!bSkip)
3218 {
3219 selected.emplace_back(m_xTreeView->make_iterator(&rEntry));
3220 const SwNodes& rNodes = pShell->GetNodes();
3221 const size_t nPos = GetAbsPos(rEntry) - 1;
3222 if (nPos < rNodes.GetOutLineNds().size())
3223 {
3224 SwNode* pNode = rNodes.GetOutLineNds()[ nPos ];
3225 if (pNode)
3226 {
3227 selectedOutlineNodes.push_back(pNode->GetTextNode());
3228 }
3229 }
3230 }
3231 return false;
3232 });
3233
3234 if (bUpDown && !bUp)
3235 { // to move down, start at the end!
3236 std::reverse(selected.begin(), selected.end());
3237 }
3238
3239 SwOutlineNodes::difference_type nDirLast = bUp ? -1 : 1;
3240 bool bStartedAction = false;
3241 for (auto const& pCurrentEntry : selected)
3242 {
3243 assert(pCurrentEntry && lcl_IsContent(*pCurrentEntry, *m_xTreeView));
3244 if (lcl_IsContent(*pCurrentEntry, *m_xTreeView))
3245 {
3246 assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*pCurrentEntry))));
3248 weld::fromId<SwContent*>(m_xTreeView->get_id(*pCurrentEntry))->GetParent()->GetType()
3250 {
3251 nActPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*pCurrentEntry))->GetOutlinePos();
3252 }
3253 }
3254 if (nActPos == SwOutlineNodes::npos || (bUpDown && !pShell->IsOutlineMovable(nActPos)))
3255 {
3256 continue;
3257 }
3258
3259 if (!bStartedAction)
3260 {
3261 pShell->StartAllAction();
3262 pShell->StartUndo(bLeftRight ? SwUndoId::OUTLINE_LR : SwUndoId::OUTLINE_UD);
3263 bStartedAction = true;
3264 }
3265 pShell->GotoOutline( nActPos); // If text selection != box selection
3266 pShell->Push();
3267 pShell->MakeOutlineSel(nActPos, nActPos, bOutlineWithChildren);
3268 if (bUpDown)
3269 {
3270 const size_t nEntryAbsPos(GetAbsPos(*pCurrentEntry));
3271 SwOutlineNodes::difference_type nDir = bUp ? -1 : 1;
3272 if (!bOutlineWithChildren && ((nDir == -1 && nActPos > 0) ||
3273 (nDir == 1 && nEntryAbsPos < GetEntryCount() - 2)))
3274 {
3275 pShell->MoveOutlinePara( nDir );
3276 // Set cursor back to the current position
3277 pShell->GotoOutline( nActPos + nDir);
3278 }
3279 else if (bOutlineWithChildren)
3280 {
3281 SwOutlineNodes::size_type nActEndPos = nActPos;
3282 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(pCurrentEntry.get()));
3283 assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*pCurrentEntry))));
3284 const auto nActLevel = weld::fromId<SwOutlineContent*>(
3285 m_xTreeView->get_id(*pCurrentEntry))->GetOutlineLevel();
3286 bool bEntry = m_xTreeView->iter_next(*xEntry);
3287 while (bEntry && lcl_IsContent(*xEntry, *m_xTreeView))
3288 {
3289 assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
3290 if (nActLevel >= weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xEntry))->GetOutlineLevel())
3291 break;
3292 nActEndPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xEntry))->GetOutlinePos();
3293 bEntry = m_xTreeView->iter_next(*xEntry);
3294 }
3295 if (nDir == 1) // move down
3296 {
3297 std::unique_ptr<weld::TreeIter> xNextSibling(m_xTreeView->make_iterator(pCurrentEntry.get()));
3298 if (m_xTreeView->iter_next_sibling(*xNextSibling) && m_xTreeView->is_selected(*xNextSibling))
3299 nDir = nDirLast;
3300 else
3301 {
3302 // If the last entry is to be moved we're done
3303 if (bEntry && lcl_IsContent(*xEntry, *m_xTreeView))
3304 {
3305 // xEntry now points to the entry following the last
3306 // selected entry.
3307 SwOutlineNodes::size_type nDest = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xEntry))->GetOutlinePos();
3308 // here needs to found the next entry after next.
3309 // The selection must be inserted in front of that.
3310 while (bEntry)
3311 {
3312 bEntry = m_xTreeView->iter_next(*xEntry);
3313 assert(!bEntry || !lcl_IsContent(*xEntry, *m_xTreeView)||
3314 dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
3315 // nDest++ may only executed if bEntry
3316 if (bEntry)
3317 {
3318 if (!lcl_IsContent(*xEntry, *m_xTreeView))
3319 break;
3320 else if (nActLevel >= weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xEntry))->GetOutlineLevel())
3321 {
3322 // nDest needs adjusted if there are selected entries (including ancestral lineage)
3323 // immediately before the current moved entry.
3324 std::unique_ptr<weld::TreeIter> xTmp(m_xTreeView->make_iterator(xEntry.get()));
3325 bool bTmp = m_xTreeView->iter_previous(*xTmp);
3326 while (bTmp && lcl_IsContent(*xTmp, *m_xTreeView) &&
3327 nActLevel < weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xTmp))->GetOutlineLevel())
3328 {
3329 while (bTmp && lcl_IsContent(*xTmp, *m_xTreeView) && !m_xTreeView->is_selected(*xTmp) &&
3330 nActLevel < weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xTmp))->GetOutlineLevel())
3331 {
3332 bTmp = m_xTreeView->iter_parent(*xTmp);
3333 }
3334 if (!bTmp || !m_xTreeView->is_selected(*xTmp))
3335 break;
3336 bTmp = m_xTreeView->iter_previous(*xTmp);
3337 nDest = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xTmp))->GetOutlinePos();
3338 }
3339 std::unique_ptr<weld::TreeIter> xPrevSibling(m_xTreeView->make_iterator(xEntry.get()));
3340 if (!m_xTreeView->iter_previous_sibling(*xPrevSibling) || !m_xTreeView->is_selected(*xPrevSibling))
3341 break;
3342 }
3343 else
3344 {
3345 nDest = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xEntry))->GetOutlinePos();
3346 }
3347 }
3348 }
3349 nDirLast = nDir = nDest - nActEndPos;
3350 // If no entry was found that allows insertion before
3351 // it, we just move it to the end.
3352 }
3353 else
3354 nDirLast = nDir = 0;
3355 }
3356 }
3357 else // move up
3358 {
3359 std::unique_ptr<weld::TreeIter> xPrevSibling(m_xTreeView->make_iterator(pCurrentEntry.get()));
3360 if (m_xTreeView->iter_previous_sibling(*xPrevSibling) && m_xTreeView->is_selected(*xPrevSibling))
3361 nDir = nDirLast;
3362 else
3363 {
3364 SwOutlineNodes::size_type nDest = nActPos;
3365 bEntry = true;
3366 m_xTreeView->copy_iterator(*pCurrentEntry, *xEntry);
3367 while (bEntry && nDest)
3368 {
3369 bEntry = m_xTreeView->iter_previous(*xEntry);
3370 assert(!bEntry || !lcl_IsContent(*xEntry, *m_xTreeView) ||
3371 dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
3372 if (bEntry && lcl_IsContent(*xEntry, *m_xTreeView))
3373 {
3374 nDest = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xEntry))->GetOutlinePos();
3375 }
3376 else
3377 {
3378 nDest = 0; // presumably?
3379 }
3380 if (bEntry)
3381 {
3382 if (!lcl_IsContent(*xEntry, *m_xTreeView))
3383 break;
3384 else if (nActLevel >= weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xEntry))->GetOutlineLevel())
3385 {
3386 // nDest needs adjusted if there are selected entries immediately
3387 // after the level change.
3388 std::unique_ptr<weld::TreeIter> xTmp(m_xTreeView->make_iterator(xEntry.get()));
3389 bool bTmp = m_xTreeView->iter_next(*xTmp);
3390 while (bTmp && lcl_IsContent(*xTmp, *m_xTreeView) &&
3391 nActLevel < weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xTmp))->GetOutlineLevel() &&
3392 m_xTreeView->is_selected(*xTmp))
3393 {
3394 nDest = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xTmp))->GetOutlinePos();
3395 const auto nLevel = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xTmp))->GetOutlineLevel();
3396 // account for selected entries' descendent lineage
3397 bTmp = m_xTreeView->iter_next(*xTmp);
3398 while (bTmp && lcl_IsContent(*xTmp, *m_xTreeView) &&
3399 nLevel < weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xTmp))->GetOutlineLevel())
3400 {
3401 nDest = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xTmp))->GetOutlinePos();
3402 bTmp = m_xTreeView->iter_next(*xTmp);
3403 }
3404 }
3405 break;
3406 }
3407 }
3408 }
3409 nDirLast = nDir = nDest - nActPos;
3410 }
3411 }
3412 if (nDir)
3413 {
3414 pShell->MoveOutlinePara( nDir );
3415 // Set cursor back to the current position
3416 pShell->GotoOutline(nActPos + nDir);
3417 }
3418 }
3419 }
3420 else
3421 {
3422 if (!pShell->IsProtectedOutlinePara())
3423 pShell->OutlineUpDown(bLeft ? -1 : 1);
3424 }
3425
3426 pShell->ClearMark();
3427 pShell->Pop(SwCursorShell::PopMode::DeleteCurrent); // Cursor is now back at the current heading.
3428 }
3429
3430 if (bStartedAction)
3431 {
3432 pShell->EndUndo();
3433 pShell->EndAllAction();
3436
3437 // tdf#143547 LO Writer: navigator should stand still on promoting and demoting
3438 // In addition to m_bIgnoreDocChange being true, selections are cleared before the Display
3439 // call. Either of these conditions disable restore of scroll position happening in the
3440 // Display function so it needs to be done here.
3441 auto nOldScrollPos = m_xTreeView->vadjustment_get_value();
3442
3443 // clear all selections to prevent the Display function from trying to reselect selected entries
3444 m_xTreeView->unselect_all();
3445 Display(true);
3446 m_xTreeView->vadjustment_set_value(nOldScrollPos);
3447
3448 // reselect entries
3449 const SwOutlineNodes::size_type nCurrPos = pShell->GetOutlinePos(MAXLEVEL);
3450 std::unique_ptr<weld::TreeIter> xListEntry(m_xTreeView->make_iterator());
3451 bool bListEntry = m_xTreeView->get_iter_first(*xListEntry);
3452 while ((bListEntry = m_xTreeView->iter_next(*xListEntry)) && lcl_IsContent(*xListEntry, *m_xTreeView))
3453 {
3454 assert(dynamic_cast<SwOutlineContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xListEntry))));
3455 if (weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xListEntry))->GetOutlinePos() == nCurrPos)
3456 {
3457 std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(xListEntry.get()));
3458 if (m_xTreeView->iter_parent(*xParent) && !m_xTreeView->get_row_expanded(*xParent))
3459 m_xTreeView->expand_row(*xParent);
3460 m_xTreeView->set_cursor(*xListEntry); // unselect all entries, make entry visible, set focus, and select
3461 Select();
3462 break;
3463 }
3464 }
3465
3466 if (m_bIsRoot)
3467 {
3468 const SwOutlineNodes& rOutLineNds = pShell->GetNodes().GetOutLineNds();
3469 for (SwTextNode* pNode : selectedOutlineNodes)
3470 {
3471 SwOutlineNodes::const_iterator aFndIt = rOutLineNds.find(pNode);
3472 if(aFndIt == rOutLineNds.end())
3473 continue;
3474 const size_t nFndPos = aFndIt - rOutLineNds.begin();
3475 std::unique_ptr<weld::TreeIter> xEntry = GetEntryAtAbsPos(nFndPos + 1);
3476 if (xEntry)
3477 {
3478 m_xTreeView->select(*xEntry);
3479 std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(xEntry.get()));
3480 if (m_xTreeView->iter_parent(*xParent) && !m_xTreeView->get_row_expanded(*xParent))
3481 m_xTreeView->expand_row(*xParent);
3482 }
3483 }
3484 }
3485 }
3486 m_bIgnoreDocChange = false;
3487}
3488
3490{
3491 m_xTreeView->show();
3493}
3494
3496{
3497 // folded together will not be idled
3498 m_aUpdTimer.Stop();
3499 m_xTreeView->hide();
3500}
3501
3503 ContentTypeId nType, const void* ptr)
3504{
3505 if (!ptr)
3506 {
3507 rContentTree.set_cursor(-1);
3508 pThis->Select();
3509 return;
3510 }
3511
3512 // find content type entry
3513 std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator());
3514
3515 bool bFoundEntry = rContentTree.get_iter_first(*xIter);
3516 while (bFoundEntry)
3517 {
3518 void* pUserData = weld::fromId<void*>(rContentTree.get_id(*xIter));
3519 assert(dynamic_cast<SwContentType*>(static_cast<SwTypeNumber*>(pUserData)));
3520 if (nType == static_cast<SwContentType*>(pUserData)->GetType())
3521 break;
3522 bFoundEntry = rContentTree.iter_next_sibling(*xIter);
3523 }
3524
3525 if (!bFoundEntry)
3526 {
3527 rContentTree.set_cursor(-1);
3528 pThis->Select();
3529 return;
3530 }
3531
3532 // assure content type entry is expanded
3533 rContentTree.expand_row(*xIter);
3534
3535 // find content type content entry and select it
3536 const void* p = nullptr;
3537 while (rContentTree.iter_next(*xIter) && lcl_IsContent(*xIter, rContentTree))
3538 {
3539 void* pUserData = weld::fromId<void*>(rContentTree.get_id(*xIter));
3540 switch( nType )
3541 {
3544 {
3545 assert(dynamic_cast<SwTextFootnoteContent*>(static_cast<SwTypeNumber*>(pUserData)));
3546 SwTextFootnoteContent* pCnt = static_cast<SwTextFootnoteContent*>(pUserData);
3547 p = pCnt->GetTextFootnote();
3548 break;
3549 }
3551 {
3552 assert(dynamic_cast<SwURLFieldContent*>(static_cast<SwTypeNumber*>(pUserData)));
3553 SwURLFieldContent* pCnt = static_cast<SwURLFieldContent*>(pUserData);
3554 p = static_cast<const SwTextAttr*>(pCnt->GetINetAttr());
3555 break;
3556 }
3558 {
3559 assert(dynamic_cast<SwTextFieldContent*>(static_cast<SwTypeNumber*>(pUserData)));
3560 SwTextFieldContent* pCnt = static_cast<SwTextFieldContent*>(pUserData);
3561 p = pCnt->GetFormatField()->GetField();
3562 break;
3563 }
3565 {
3566 assert(dynamic_cast<SwPostItContent*>(static_cast<SwTypeNumber*>(pUserData)));
3567 SwPostItContent* pCnt = static_cast<SwPostItContent*>(pUserData);
3568 p = pCnt->GetPostIt()->GetField();
3569 break;
3570 }
3571 default:
3572 break;
3573 }
3574 if (ptr == p)
3575 {
3576 // get first selected for comparison
3577 std::unique_ptr<weld::TreeIter> xFirstSelected(rContentTree.make_iterator());
3578 if (!rContentTree.get_selected(xFirstSelected.get()))
3579 xFirstSelected.reset();
3580 if (rContentTree.count_selected_rows() != 1 || !xFirstSelected ||
3581 rContentTree.iter_compare(*xIter, *xFirstSelected) != 0)
3582 {
3583 // unselect all entries and make passed entry visible and selected
3584 rContentTree.set_cursor(*xIter);
3585 pThis->Select();
3586 }
3587 return;
3588 }
3589 }
3590
3591 rContentTree.set_cursor(-1);
3592 pThis->Select();
3593 return;
3594}
3595
3597 std::u16string_view rContentTypeName, std::u16string_view rName)
3598{
3599 if (rName.empty())
3600 return;
3601
3602 // find content type entry
3603 std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator());
3604 bool bFoundEntry = rContentTree.get_iter_first(*xIter);
3605 while (bFoundEntry && rContentTypeName != rContentTree.get_text(*xIter))
3606 bFoundEntry = rContentTree.iter_next_sibling(*xIter);
3607 // find content type content entry and select it
3608 if (!bFoundEntry)
3609 return;
3610
3611 rContentTree.expand_row(*xIter); // assure content type entry is expanded
3612 while (rContentTree.iter_next(*xIter) && lcl_IsContent(*xIter, rContentTree))
3613 {
3614 if (rName == rContentTree.get_text(*xIter))
3615 {
3616 // get first selected for comparison
3617 std::unique_ptr<weld::TreeIter> xFirstSelected(rContentTree.make_iterator());
3618 if (!rContentTree.get_selected(xFirstSelected.get()))
3619 xFirstSelected.reset();
3620 if (rContentTree.count_selected_rows() != 1 || !xFirstSelected ||
3621 rContentTree.iter_compare(*xIter, *xFirstSelected) != 0)
3622 {
3623 // unselect all entries and make passed entry visible and selected
3624 rContentTree.set_cursor(*xIter);
3625 pThis->Select();
3626 }
3627 break;
3628 }
3629 }
3630}
3631
3632static void lcl_SelectDrawObjectByName(weld::TreeView& rContentTree, std::u16string_view rName)
3633{
3634 if (rName.empty())
3635 return;
3636
3637 // find content type entry
3638 std::unique_ptr<weld::TreeIter> xIter(rContentTree.make_iterator());
3639 bool bFoundEntry = rContentTree.get_iter_first(*xIter);
3640 while (bFoundEntry && SwResId(STR_CONTENT_TYPE_DRAWOBJECT) != rContentTree.get_text(*xIter))
3641 bFoundEntry = rContentTree.iter_next_sibling(*xIter);
3642 // find content type content entry and select it
3643 if (bFoundEntry)
3644 {
3645 rContentTree.expand_row(*xIter); // assure content type entry is expanded
3646 while (rContentTree.iter_next(*xIter) && lcl_IsContent(*xIter, rContentTree))
3647 {
3648 if (rName == rContentTree.get_text(*xIter))
3649 {
3650 if (!rContentTree.is_selected(*xIter))
3651 {
3652 rContentTree.select(*xIter);
3653 rContentTree.scroll_to_row(*xIter);
3654 }
3655 break;
3656 }
3657 }
3658 }
3659}
3660
3663{
3664 // No need to update if content tree is not visible
3665 if (!m_xTreeView->is_visible())
3666 return;
3667
3668 // No update while focus is not in document.
3669 // No update while drag and drop.
3670 // Query view because the Navigator is cleared too late.
3671 SwView* pView = GetParentWindow()->GetCreateView();
3672 if(pView && pView->GetWrtShellPtr() && pView->GetWrtShellPtr()->GetWin() &&
3673 (pView->GetWrtShellPtr()->GetWin()->HasFocus() || m_bDocHasChanged || m_bViewHasChanged) &&
3674 !IsInDrag() && !pView->GetWrtShellPtr()->ActionPend())
3675 {
3676 if (m_bDocHasChanged || m_bViewHasChanged)
3677 {
3678 SwWrtShell* pActShell = pView->GetWrtShellPtr();
3679 if (State::CONSTANT == m_eState && !lcl_FindShell(m_pActiveShell))
3680 {
3681 SetActiveShell(pActShell);
3682 GetParentWindow()->UpdateListBox();
3683 }
3684 if (State::ACTIVE == m_eState && pActShell != GetWrtShell())
3685 {
3686 SetActiveShell(pActShell);
3687 }
3688 else if ((State::ACTIVE == m_eState || (State::CONSTANT == m_eState && pActShell == GetWrtShell())) &&
3689 HasContentChanged())
3690 {
3691 FindActiveTypeAndRemoveUserData();
3692 Display(true);
3693 }
3694 }
3695 UpdateTracking();
3696 m_bIsIdleClear = false;
3697 m_bDocHasChanged = false;
3698 m_bViewHasChanged = false;
3699 }
3700 else if (!pView && State::ACTIVE == m_eState && !m_bIsIdleClear) // this block seems never to be entered
3701 {
3702 if(m_pActiveShell)
3703 {
3704 SetActiveShell(nullptr);
3705 }
3706 clear();
3707 m_bIsIdleClear = true;
3708 }
3709}
3710
3712{
3714 return;
3715
3716 // only when treeview or treeview context menu does not have focus
3717 if (m_xTreeView->has_focus() || m_xTreeView->has_child_focus())
3718 return;
3719
3720 // m_bIgnoreDocChange is set on delete and outline visibility toggle
3722 {
3723 m_bIgnoreDocChange = false;
3724 return;
3725 }
3726
3727 // bTrack is used to disallow tracking after jumping to an outline until the outline position
3728 // that was jumped to is no longer the current outline position.
3729 bool bTrack = true;
3731 {
3733 bTrack = false;
3734 else
3736 }
3737
3738 if (bTrack)
3739 {
3740 // graphic, frame, and ole
3743 {
3744 OUString aContentTypeName;
3747 {
3749 aContentTypeName = SwResId(STR_CONTENT_TYPE_GRAPHIC);
3750 }
3753 {
3755 aContentTypeName = SwResId(STR_CONTENT_TYPE_FRAME);
3756 }
3759 {
3761 aContentTypeName = SwResId(STR_CONTENT_TYPE_OLE);
3762 }
3763 if (!aContentTypeName.isEmpty())
3764 {
3765 OUString aName(m_pActiveShell->GetFlyName());
3766 lcl_SelectByContentTypeAndName(this, *m_xTreeView, aContentTypeName, aName);
3767 return;
3768 }
3769 }
3770 // drawing
3775 {
3777 {
3778 // Multiple selection is possible when in root content navigation view so unselect all
3779 // selected entries before reselecting. This causes a bit of an annoyance when the treeview
3780 // scroll bar is used and focus is in the document by causing the last selected entry to
3781 // scroll back into view.
3782 if (m_bIsRoot)
3783 m_xTreeView->unselect_all();
3784 SdrView* pSdrView = m_pActiveShell->GetDrawView();
3785 if (pSdrView)
3786 {
3787 for (size_t nIdx(0); nIdx < pSdrView->GetMarkedObjectCount(); nIdx++)
3788 {
3789 SdrObject* pSelected = pSdrView->GetMarkedObjectByIndex(nIdx);
3790 OUString aName(pSelected->GetName());
3791 if (!aName.isEmpty())
3793 }
3794 }
3795 else
3796 {
3797 // clear treeview selections
3798 m_xTreeView->unselect_all();
3799 }
3800 Select();
3801 }
3802 return;
3803 }
3804 // footnotes and endnotes
3805 if (SwContentAtPos aContentAtPos(IsAttrAtPos::Ftn);
3807 && aContentAtPos.pFndTextAttr &&
3810 {
3811 if (!aContentAtPos.pFndTextAttr->GetFootnote().IsEndNote())
3812 {
3815 aContentAtPos.pFndTextAttr);
3816 }
3819 aContentAtPos.pFndTextAttr);
3820 return;
3821 }
3822 // bookmarks - track first bookmark at cursor
3825 {
3826 SwPaM* pCursor = m_pActiveShell->GetCursor();
3828 IDocumentMarkAccess::const_iterator_t ppBookmark = pMarkAccess->getBookmarksBegin();
3829 if (pCursor && ppBookmark != pMarkAccess->getBookmarksEnd() &&
3831 {
3832 SwPosition* pCursorPoint = pCursor->GetPoint();
3833 while (ppBookmark != pMarkAccess->getBookmarksEnd())
3834 {
3835 if (lcl_IsUiVisibleBookmark(*ppBookmark) &&
3836 *pCursorPoint >= (*ppBookmark)->GetMarkStart() &&
3837 *pCursorPoint <= (*ppBookmark)->GetMarkEnd())
3838 {
3840 SwResId(STR_CONTENT_TYPE_BOOKMARK),
3841 (*ppBookmark)->GetName());
3842 return;
3843 }
3844 ++ppBookmark;
3845 }
3846 }
3847 }
3848 // references
3849 if (SwContentAtPos aContentAtPos(IsAttrAtPos::RefMark);
3851 aContentAtPos.pFndTextAttr &&
3853 {
3855 {
3856 const SwFormatRefMark& rRefMark = aContentAtPos.pFndTextAttr->GetRefMark();
3857 lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_REFERENCE),
3858 rRefMark.GetRefName());
3859 }
3860 return;
3861 }
3862 // hyperlinks
3863 if (SwContentAtPos aContentAtPos(IsAttrAtPos::InetAttr);
3866 {
3867 // There is no need to search for hyperlinks in ToxContent tdf#148312
3868 if (const SwTextINetFormat* pTextINetFormat =
3869 static_txtattr_cast<const SwTextINetFormat*>(aContentAtPos.pFndTextAttr))
3870 {
3871 if (const SwTextNode* pTextNode = pTextINetFormat->GetpTextNode())
3872 {
3873 if (const SwSectionNode* pSectNd = pTextNode->FindSectionNode())
3874 {
3875 SectionType eType = pSectNd->GetSection().GetType();
3877 {
3878 m_xTreeView->set_cursor(-1);
3879 Select();
3880 return;
3881 }
3882 }
3883 }
3884 }
3885 // Because hyperlink item names do not need to be unique, finding the corresponding item
3886 // in the tree by name may result in incorrect selection. Find the item in the tree by
3887 // comparing the SwTextINetFormat pointer at the document cursor position to that stored
3888 // in the item SwURLFieldContent.
3891 aContentAtPos.pFndTextAttr);
3892 return;
3893 }
3894 // fields, comments
3895 if (SwField* pField = m_pActiveShell->GetCurField(); pField &&
3896 !(m_bIsRoot &&
3899 {
3900 ContentTypeId eCntTypeId =
3901 pField->GetTypeId() == SwFieldTypesEnum::Postit ? ContentTypeId::POSTIT :
3903 if (mTrackContentType[eCntTypeId])
3904 lcl_SelectByContentTypeAndAddress(this, *m_xTreeView, eCntTypeId, pField);
3905 return;
3906 }
3907 // table
3910 {
3912 {
3913 OUString aName = m_pActiveShell->GetTableFormat()->GetName();
3914 lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_TABLE),
3915 aName);
3916 }
3917 return;
3918 }
3919 // indexes
3920 if (const SwTOXBase* pTOX = m_pActiveShell->GetCurTOX(); pTOX &&
3922 {
3924 lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_INDEX),
3925 pTOX->GetTOXName());
3926 return;
3927 }
3928 // section
3929 if (const SwSection* pSection = m_pActiveShell->GetCurrSection(); pSection &&
3931 {
3933 {
3934 lcl_SelectByContentTypeAndName(this, *m_xTreeView, SwResId(STR_CONTENT_TYPE_REGION),
3935 pSection->GetSectionName());
3936 return;
3937 }
3938 else
3939 {
3940 // prevent fall through to outline tracking when section tracking is off and the last
3941 // GotoContent is the current section
3943 m_xTreeView->get_selected_text() == pSection->GetSectionName())
3944 return;
3945 }
3946 // fall through to outline tracking when section tracking is off and the last GotoContent
3947 // is not the current section
3948 }
3949 }
3950 // outline
3951 if (m_nOutlineTracking == 3)
3952 return;
3953 // find out where the cursor is
3956 {
3957 // assure outline content type is expanded
3958 // this assumes outline content type is first in treeview
3959 std::unique_ptr<weld::TreeIter> xFirstEntry(m_xTreeView->make_iterator());
3960 if (m_xTreeView->get_iter_first(*xFirstEntry))
3961 m_xTreeView->expand_row(*xFirstEntry);
3962
3963 m_xTreeView->all_foreach([this, nActPos](weld::TreeIter& rEntry){
3964 bool bRet = false;
3965 if (lcl_IsContent(rEntry, *m_xTreeView) && weld::fromId<SwContent*>(
3966 m_xTreeView->get_id(rEntry))->GetParent()->GetType() ==
3968 {
3969 if (weld::fromId<SwOutlineContent*>(
3970 m_xTreeView->get_id(rEntry))->GetOutlinePos() == nActPos)
3971 {
3972 std::unique_ptr<weld::TreeIter> xFirstSelected(
3973 m_xTreeView->make_iterator());
3974 if (!m_xTreeView->get_selected(xFirstSelected.get()))
3975 xFirstSelected.reset();
3976 // only select if not already selected or tree has multiple entries selected
3977 if (m_xTreeView->count_selected_rows() != 1 || !xFirstSelected ||
3978 m_xTreeView->iter_compare(rEntry, *xFirstSelected) != 0)
3979 {
3980 if (m_nOutlineTracking == 2) // focused outline tracking
3981 {
3982 // collapse to children of root node
3983 std::unique_ptr<weld::TreeIter> xChildEntry(
3984 m_xTreeView->make_iterator());
3985 if (m_xTreeView->get_iter_first(*xChildEntry) &&
3986 m_xTreeView->iter_children(*xChildEntry))
3987 {
3988 do
3989 {
3990 if (weld::fromId<SwContent*>(
3991 m_xTreeView->get_id(*xChildEntry))->
3992 GetParent()->GetType() == ContentTypeId::OUTLINE)
3993 m_xTreeView->collapse_row(*xChildEntry);
3994 else
3995 break;
3996 }
3997 while (m_xTreeView->iter_next(*xChildEntry));
3998 }
3999 }
4000 // unselect all entries, make pEntry visible, and select
4001 m_xTreeView->set_cursor(rEntry);
4002 Select();
4003
4004 // tdf#149279 show at least two outline entries before the set cursor entry
4005 std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator(&rEntry));
4006 for (int i = 0; i < 2; i++)
4007 {
4008 if (m_xTreeView->get_iter_depth(*xIter) == 0)
4009 break;
4010 if (!m_xTreeView->iter_previous(*xIter))
4011 break;
4012 while (!weld::IsEntryVisible(*m_xTreeView, *xIter))
4013 m_xTreeView->iter_parent(*xIter);
4014 }
4015 // Assure the scroll to row is collapsed after scrolling if it was collapsed
4016 // before. This is required here to make gtkinst scroll_to_row behave like
4017 // salinst.
4018 const bool bRowExpanded = m_xTreeView->get_row_expanded(*xIter);
4019 m_xTreeView->scroll_to_row(*xIter);
4020 if (!bRowExpanded)
4021 m_xTreeView->collapse_row(*xIter);
4022 }
4023 bRet = true;
4024 }
4025 }
4026 else
4027 {
4028 // use of this break assumes outline content type is first in tree
4029 if (lcl_IsContentType(rEntry, *m_xTreeView) &&
4030 weld::fromId<SwContentType*>(
4031 m_xTreeView->get_id(rEntry))->GetType() !=
4033 bRet = true;
4034 }
4035 return bRet;
4036 });
4037 }
4038 else
4039 {
4040 // clear treeview selections
4041 if (m_xTreeView->count_selected_rows() > 0)
4042 {
4043 m_xTreeView->unselect_all();
4044 m_xTreeView->set_cursor(-1);
4045 Select();
4046 }
4047 }
4048}
4049
4051{
4052 SwCursor* pFirstCursor = m_pActiveShell->GetCursor();
4053 SwCursor* pCursor = pFirstCursor;
4054 std::vector<SwOutlineNodes::size_type> aOutlinePositions;
4055 do
4056 {
4057 if (pCursor)
4058 {
4059 if (pCursor->HasMark())
4060 {
4061 aOutlinePositions.push_back(m_pActiveShell->GetOutlinePos(UCHAR_MAX, pCursor));
4062 }
4063 pCursor = pCursor->GetNext();
4064 }
4065 } while (pCursor && pCursor != pFirstCursor);
4066
4067 if (aOutlinePositions.empty())
4068 return;
4069
4070 // remove duplicates before selecting
4071 aOutlinePositions.erase(std::unique(aOutlinePositions.begin(), aOutlinePositions.end()),
4072 aOutlinePositions.end());
4073
4074 m_xTreeView->unselect_all();
4075
4076 for (auto nOutlinePosition : aOutlinePositions)
4077 {
4078 m_xTreeView->all_foreach([this, nOutlinePosition](const weld::TreeIter& rEntry){
4079 if (lcl_IsContent(rEntry, *m_xTreeView) &&
4080 weld::fromId<SwContent*>(
4081 m_xTreeView->get_id(rEntry))->GetParent()->GetType() ==
4083 {
4084 if (weld::fromId<SwOutlineContent*>(
4085 m_xTreeView->get_id(rEntry))->GetOutlinePos() ==
4086 nOutlinePosition)
4087 {
4088 std::unique_ptr<weld::TreeIter> xParent =
4089 m_xTreeView->make_iterator(&rEntry);
4090 if (m_xTreeView->iter_parent(*xParent) &&
4091 !m_xTreeView->get_row_expanded(*xParent))
4092 m_xTreeView->expand_row(*xParent);
4093 m_xTreeView->select(rEntry);
4094 return true;
4095 }
4096 }
4097 return false;
4098 });
4099 }
4100
4101 Select();
4102}
4103
4105{
4107
4108 SwWrtShell *const pShell = GetWrtShell();
4109 pShell->StartAllAction();
4111
4113 SwOutlineNodes::size_type nPrevTargetPosOrOffset = SwOutlineNodes::npos;
4114
4115 bool bFirstMove = true;
4116
4117 for (const auto& source : m_aDndOutlinesSelected)
4118 {
4119 SwOutlineNodes::size_type nSourcePos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*source))->GetOutlinePos();
4120
4121 // Done on the first selection move
4122 if (bFirstMove) // only do once
4123 {
4124 if (nTargetPos == SwOutlineNodes::npos || nSourcePos > nTargetPos)
4125 {
4126 // Up moves
4127 // The first up move sets the up move amount for the remaining selected outlines to be moved
4128 if (nTargetPos != SwOutlineNodes::npos)
4129 nPrevTargetPosOrOffset = nSourcePos - nTargetPos;
4130 else
4131 nPrevTargetPosOrOffset = nSourcePos + 1;
4132 }
4133 else if (nSourcePos < nTargetPos)
4134 {
4135 // Down moves
4136 // The first down move sets the source and target positions for the remaining selected outlines to be moved
4137 nPrevSourcePos = nSourcePos;
4138 nPrevTargetPosOrOffset = nTargetPos;
4139 }
4140 bFirstMove = false;
4141 }
4142 else
4143 {
4144 if (nTargetPos == SwOutlineNodes::npos || nSourcePos > nTargetPos)
4145 {
4146 // Move up
4147 nTargetPos = nSourcePos - nPrevTargetPosOrOffset;
4148 }
4149 else if (nSourcePos < nTargetPos)
4150 {
4151 // Move down
4152 nSourcePos = nPrevSourcePos;
4153 nTargetPos = nPrevTargetPosOrOffset;
4154 }
4155 }
4156 GetParentWindow()->MoveOutline(nSourcePos, nTargetPos);
4157 }
4158
4159 pShell->EndUndo();
4160 pShell->EndAllAction();
4162 Display(true);
4163 m_aDndOutlinesSelected.clear();
4164}
4165
4166// Update immediately
4168{
4169 SwView* pActView = GetParentWindow()->GetCreateView();
4170 if(pActView)
4171 {
4172 SwWrtShell* pActShell = pActView->GetWrtShellPtr();
4173 if (State::CONSTANT == m_eState && !lcl_FindShell(m_pActiveShell))
4174 {
4175 SetActiveShell(pActShell);
4176 }
4177
4178 if (State::ACTIVE == m_eState && pActShell != GetWrtShell())
4179 SetActiveShell(pActShell);
4180 // Only call HasContentChanged() if the document has changed since last called
4181 else if ((State::ACTIVE == m_eState || (State::CONSTANT == m_eState && pActShell == GetWrtShell())) &&
4182 m_bDocHasChanged)
4183 {
4184 if (HasContentChanged())
4185 Display(true);
4186 m_bDocHasChanged = false;
4187 }
4188 }
4189 else if (State::ACTIVE == m_eState)
4190 clear();
4191}
4192
4193IMPL_LINK(SwContentTree, KeyInputHdl, const KeyEvent&, rEvent, bool)
4194{
4195 bool bConsumed = true;
4196
4197 const vcl::KeyCode aCode = rEvent.GetKeyCode();
4198 if (aCode.GetCode() == KEY_MULTIPLY && aCode.IsMod1())
4199 {
4200 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
4201 if (m_xTreeView->get_selected(xEntry.get()))
4202 ExpandOrCollapseAll(*m_xTreeView, *xEntry);
4203 }
4204 else if (aCode.GetCode() == KEY_RETURN)
4205 {
4206 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
4207 if (m_xTreeView->get_selected(xEntry.get()))
4208 {
4209 switch(aCode.GetModifier())
4210 {
4211 case KEY_MOD2:
4212 // Switch boxes
4213 GetParentWindow()->ToggleTree();
4214 break;
4215 case KEY_MOD1:
4216 // Switch RootMode
4217 ToggleToRoot();
4218 break;
4219 case 0:
4220 if (lcl_IsContentType(*xEntry, *m_xTreeView))
4221 {
4222 m_xTreeView->get_row_expanded(*xEntry) ? m_xTreeView->collapse_row(*xEntry)
4223 : m_xTreeView->expand_row(*xEntry);
4224 }
4225 else
4226 ContentDoubleClickHdl(*m_xTreeView);
4227 break;
4228 }
4229 }
4230 }
4231 else if(aCode.GetCode() == KEY_DELETE && 0 == aCode.GetModifier())
4232 {
4233 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
4234 if (m_xTreeView->get_selected(xEntry.get()) && lcl_IsContent(*xEntry, *m_xTreeView))
4235 {
4236 assert(dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry))));
4237 if (weld::fromId<SwContent*>(m_xTreeView->get_id(*xEntry))->GetParent()->IsDeletable() &&
4238 !m_pActiveShell->GetView().GetDocShell()->IsReadOnly())
4239 {
4240 EditEntry(*xEntry, EditEntryMode::DELETE);
4241 }
4242 }
4243 }
4244 //Make KEY_SPACE has same function as DoubleClick, and realize
4245 //multi-selection.
4246 else if (aCode.GetCode() == KEY_SPACE && 0 == aCode.GetModifier())
4247 {
4248 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
4249 if (m_xTreeView->get_cursor(xEntry.get()))
4250 {
4251 if (State::HIDDEN != m_eState)
4252 {
4253 if (State::CONSTANT == m_eState)
4254 {
4255 m_pActiveShell->GetView().GetViewFrame()->GetWindow().ToTop();
4256 }
4257
4258 SwContent* pCnt = dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)));
4259
4260 if (pCnt && pCnt->GetParent()->GetType() == ContentTypeId::DRAWOBJECT)
4261 {
4262 SdrView* pDrawView = m_pActiveShell->GetDrawView();
4263 if (pDrawView)
4264 {
4265 pDrawView->SdrEndTextEdit();
4266
4267 SwDrawModel* pDrawModel = m_pActiveShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel();
4268 SdrPage* pPage = pDrawModel->GetPage(0);
4269 const size_t nCount = pPage->GetObjCount();
4270 bool hasObjectMarked = false;
4271
4272 if (SdrObject* pObject = GetDrawingObjectsByContent(pCnt))
4273 {
4274 SdrPageView* pPV = pDrawView->GetSdrPageView/*GetPageViewPvNum*/(/*0*/);
4275 if( pPV )
4276 {
4277 bool bUnMark = pDrawView->IsObjMarked(pObject);
4278 pDrawView->MarkObj( pObject, pPV, bUnMark);
4279
4280 }
4281 }
4282 for( size_t i=0; i<nCount; ++i )
4283 {
4284 SdrObject* pTemp = pPage->GetObj(i);
4285 bool bMark = pDrawView->IsObjMarked(pTemp);
4286 switch( pTemp->GetObjIdentifier() )
4287 {
4288 case SdrObjKind::Group:
4289 case SdrObjKind::Text:
4290 case SdrObjKind::Line:
4291 case SdrObjKind::Rectangle:
4292 case SdrObjKind::CircleOrEllipse:
4293 case SdrObjKind::CircleSection:
4294 case SdrObjKind::CircleArc:
4295 case SdrObjKind::CircleCut:
4296 case SdrObjKind::Polygon:
4297 case SdrObjKind::PolyLine:
4298 case SdrObjKind::PathLine:
4299 case SdrObjKind::PathFill:
4300 case SdrObjKind::FreehandLine:
4301 case SdrObjKind::FreehandFill:
4302 case SdrObjKind::PathPoly:
4303 case SdrObjKind::PathPolyLine:
4304 case SdrObjKind::Caption:
4305 case SdrObjKind::CustomShape:
4306 if( bMark )
4307 hasObjectMarked = true;
4308 break;
4309 default:
4310 if ( bMark )
4311 {
4312 SdrPageView* pPV = pDrawView->GetSdrPageView/*GetPageViewPvNum*/(/*0*/);
4313 if (pPV)
4314 {
4315 pDrawView->MarkObj(pTemp, pPV, true);
4316 }
4317 }
4318 }
4319 //mod end
4320 }
4321 if ( !hasObjectMarked )
4322 {
4323 SwEditWin& rEditWindow = m_pActiveShell->GetView().GetEditWin();
4324 vcl::KeyCode tempKeycode( KEY_ESCAPE );
4325 KeyEvent rKEvt( 0 , tempKeycode );
4326 static_cast<vcl::Window*>(&rEditWindow)->KeyInput( rKEvt );
4327 }
4328 }
4329 }
4330
4331 m_bViewHasChanged = true;
4332 }
4333 }
4334 }
4335 else
4336 {
4337 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
4338 if (m_xTreeView->get_cursor(xEntry.get()))
4339 {
4340 SwContent* pCnt = dynamic_cast<SwContent*>(weld::fromId<SwTypeNumber*>(m_xTreeView->get_id(*xEntry)));
4341 if (pCnt && pCnt->GetParent()->GetType() == ContentTypeId::OUTLINE)
4342 {
4343 if (m_bIsRoot && aCode.GetCode() == KEY_LEFT && aCode.GetModifier() == 0)
4344 {
4345 m_xTreeView->unselect_all();
4346 bConsumed = false;
4347 }
4348 else if (aCode.IsMod1())
4349 {
4350 if (aCode.GetCode() == KEY_LEFT)
4351 ExecCommand("promote", !aCode.IsShift());
4352 else if (aCode.GetCode() == KEY_RIGHT)
4353 ExecCommand("demote", !aCode.IsShift());
4354 else if (aCode.GetCode() == KEY_UP)
4355 ExecCommand("chapterup", !aCode.IsShift());
4356 else if (aCode.GetCode() == KEY_DOWN)
4357 ExecCommand("chapterdown", !aCode.IsShift());
4358 else if (aCode.GetCode() == KEY_C)
4359 CopyOutlineSelections();
4360 else
4361 bConsumed = false;
4362 }
4363 else
4364 bConsumed = false;
4365 }
4366 else
4367 bConsumed = false;
4368 }
4369 else
4370 bConsumed = false;
4371 }
4372 return bConsumed;
4373}
4374
4375IMPL_LINK(SwContentTree, QueryTooltipHdl, const weld::TreeIter&, rEntry, OUString)
4376{
4378 bool bContent = false;
4379 void* pUserData = weld::fromId<void*>(m_xTreeView->get_id(rEntry));
4380 if (lcl_IsContentType(rEntry, *m_xTreeView))
4381 {
4382 assert(dynamic_cast<SwContentType*>(static_cast<SwTypeNumber*>(pUserData)));
4383 nType = static_cast<SwContentType*>(pUserData)->GetType();
4384 }
4385 else
4386 {
4387 assert(dynamic_cast<SwContent*>(static_cast<SwTypeNumber*>(pUserData)));
4388 nType = static_cast<SwContent*>(pUserData)->GetParent()->GetType();
4389 bContent = true;
4390 }
4391 OUString sEntry;
4392 if(bContent)
4393 {
4394 switch( nType )
4395 {
4397 assert(dynamic_cast<SwURLFieldContent*>(static_cast<SwTypeNumber*>(pUserData)));
4398 sEntry = static_cast<SwURLFieldContent*>(pUserData)->GetURL();
4399 break;
4400
4402 assert(dynamic_cast<SwPostItContent*>(static_cast<SwTypeNumber*>(pUserData)));
4403 sEntry = static_cast<SwPostItContent*>(pUserData)->GetName();
4404 break;
4406 assert(dynamic_cast<SwOutlineContent*>(static_cast<SwTypeNumber*>(pUserData)));
4407 sEntry = static_cast<SwOutlineContent*>(pUserData)->GetName();
4408 break;
4410 assert(dynamic_cast<SwGraphicContent*>(static_cast<SwTypeNumber*>(pUserData)));
4411 sEntry = static_cast<SwGraphicContent*>(pUserData)->GetLink();
4412 break;
4414 {
4415 assert(dynamic_cast<SwRegionContent*>(static_cast<SwTypeNumber*>(pUserData)));
4416 sEntry = static_cast<SwRegionContent*>(pUserData)->GetName();
4417 const SwSectionFormats& rFormats = GetWrtShell()->GetDoc()->GetSections();
4418 for (SwSectionFormats::size_type n = rFormats.size(); n;)
4419 {
4420 const SwNodeIndex* pIdx = nullptr;
4421 const SwSectionFormat* pFormat = rFormats[--n];
4422 const SwSection* pSect;
4423 if (nullptr != (pSect = pFormat->GetSection()) &&
4424 pSect->GetSectionName() == sEntry &&
4425 nullptr != (pIdx = pFormat->GetContent().GetContentIdx()) &&
4426 pIdx->GetNode().GetNodes().IsDocNodes())
4427 {
4428 SwDocStat aDocStat;
4429 SwPaM aPaM(pIdx->GetNode(), *pIdx->GetNode().EndOfSectionNode());
4430 SwDoc::CountWords(aPaM, aDocStat);
4431 sEntry = SwResId(STR_REGION_DEFNAME) + ": " + sEntry + "\n" +
4432 SwResId(FLD_STAT_WORD) + ": " + OUString::number(aDocStat.nWord) + "\n" +
4433 SwResId(FLD_STAT_CHAR) + ": " + OUString::number(aDocStat.nChar);
4434 break;
4435 }
4436 }
4437 }
4438 break;
4441 {
4442 assert(dynamic_cast<SwTextFootnoteContent*>(static_cast<SwTypeNumber*>(pUserData)));
4443 const SwTextFootnote* pFootnote =
4444 static_cast<const SwTextFootnoteContent*>(pUserData)->GetTextFootnote();
4445
4446 sEntry = pFootnote->GetFootnote().IsEndNote() ? SwResId(STR_CONTENT_ENDNOTE) :
4447 SwResId(STR_CONTENT_FOOTNOTE);
4448 }
4449 break;
4450 default: break;
4451 }
4452 if(static_cast<SwContent*>(pUserData)->IsInvisible())
4453 {
4454 if(!sEntry.isEmpty())
4455 sEntry += ", ";
4456 sEntry += m_sInvisible;
4457 }
4458 }
4459 else
4460 {
4461 size_t nMemberCount = static_cast<SwContentType*>(pUserData)->GetMemberCount();
4462 sEntry = OUString::number(nMemberCount) + " " +
4463 (nMemberCount == 1
4464 ? static_cast<SwContentType*>(pUserData)->GetSingleName()
4465 : static_cast<SwContentType*>(pUserData)->GetName());
4466 }
4467
4468 return sEntry;
4469}
4470
4471void SwContentTree::ExecuteContextMenuAction(const OString& rSelectedPopupEntry)
4472{
4473 if (rSelectedPopupEntry == "copy")
4474 {
4476 return;
4477 }
4478 if (rSelectedPopupEntry == "collapseallcategories")
4479 {
4480 std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
4481 bool bEntry = m_xTreeView->get_iter_first(*xEntry);
4482 while (bEntry)
4483 {
4484 m_xTreeView->collapse_row(*xEntry);
4485 bEntry = m_xTreeView->iter_next_sibling(*xEntry);
4486 }
4487 return;
4488 }
4489
4490 {
4491 std::map<OString, ContentTypeId> mPopupEntryToContentTypeId
4492 {
4493 {"tabletracking", ContentTypeId::TABLE},
4494 {"frametracking", ContentTypeId::FRAME},
4495 {"imagetracking", ContentTypeId::GRAPHIC},
4496 {"oleobjecttracking", ContentTypeId::OLE},
4497 {"bookmarktracking", ContentTypeId::BOOKMARK},
4498 {"sectiontracking", ContentTypeId::REGION},
4499 {"hyperlinktracking", ContentTypeId::URLFIELD},
4500 {"referencetracking", ContentTypeId::REFERENCE},
4501 {"indextracking", ContentTypeId::INDEX},
4502 {"commenttracking", ContentTypeId::POSTIT},
4503 {"drawingobjecttracking", ContentTypeId::DRAWOBJECT},
4504 {"fieldtracking", ContentTypeId::TEXTFIELD},
4505 {"footnotetracking", ContentTypeId::FOOTNOTE},
4506 {"endnotetracking", ContentTypeId::ENDNOTE}
4507 };
4508
4509 if (mPopupEntryToContentTypeId.count(rSelectedPopupEntry))
4510 {
4511 ContentTypeId eCntTypeId = mPopupEntryToContentTypeId[rSelectedPopupEntry];
4512 SetContentTypeTracking(eCntTypeId, !mTrackContentType[eCntTypeId]);
4513 return;
4514 }
4515 }
4516
4517 std::unique_ptr<weld::TreeIter> xFirst(m_xTreeView->make_iterator());
4518 if (!m_xTreeView->get_selected(xFirst.get()))
4519 return; // this shouldn't happen, but better to be safe than ...
4520
4521 if (rSelectedPopupEntry == "sort")
4522 {
4523 SwContentType* pCntType;
4524 const OUString& rId(m_xTreeView->get_id(*xFirst));
4525 if (lcl_IsContentType(*xFirst, *m_xTreeView))
4526 pCntType = weld::fromId<SwContentType*>(rId);
4527 else
4528 pCntType = const_cast<SwContentType*>(weld::fromId<SwContent*>(rId)->GetParent());
4529 pCntType->SetSortType(!pCntType->GetSortType());
4530 pCntType->FillMemberList();
4531 Display(true);
4532 return;
4533 }
4534 else if (rSelectedPopupEntry == "deletechapter" ||
4535 rSelectedPopupEntry == "deletetable" ||
4536 rSelectedPopupEntry == "deleteframe" ||
4537 rSelectedPopupEntry == "deleteimage" ||
4538 rSelectedPopupEntry == "deleteoleobject" ||
4539 rSelectedPopupEntry == "deletebookmark" ||
4540 rSelectedPopupEntry == "deletehyperlink" ||
4541 rSelectedPopupEntry == "deleteindex" ||
4542 rSelectedPopupEntry == "deletecomment" ||
4543 rSelectedPopupEntry == "deletedrawingobject" ||
4544 rSelectedPopupEntry == "deletefield")
4545 {
4547 return;
4548 }
4549
4550 auto nSelectedPopupEntry = rSelectedPopupEntry.toUInt32();
4551 switch (nSelectedPopupEntry)
4552 {
4553 case TOGGLE_OUTLINE_CONTENT_VISIBILITY:
4554 case HIDE_OUTLINE_CONTENT_VISIBILITY:
4555 case SHOW_OUTLINE_CONTENT_VISIBILITY:
4556 {
4558 m_bIgnoreDocChange = true;
4559 SwOutlineContent* pCntFirst = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(*xFirst));
4560
4561 // toggle the outline node outline content visible attribute
4562 if (nSelectedPopupEntry == TOGGLE_OUTLINE_CONTENT_VISIBILITY)
4563 {
4564 SwNode* pNode = m_pActiveShell->GetDoc()->GetNodes().GetOutLineNds()[pCntFirst->GetOutlinePos()];
4567 }
4568 else
4569 {
4570 // with subs
4572 if (lcl_IsContentType(*xFirst, *m_xTreeView)) // Headings root entry
4575 int nLevel = -1;
4576 if (nPos != SwOutlineNodes::npos) // not root
4578 else
4579 nPos = 0;
4580 bool bShow(nSelectedPopupEntry == SHOW_OUTLINE_CONTENT_VISIBILITY);
4581 do
4582 {
4584 m_pActiveShell->GetDoc()->GetNodes().GetOutLineNds()[nPos]->GetTextNode()->SetAttrOutlineContentVisible(bShow);
4585 } while (++nPos < nOutlineNodesCount
4586 && (nLevel == -1 || m_pActiveShell->getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos) > nLevel));
4587 }
4589 // show in the document what was toggled
4590 if (lcl_IsContentType(*xFirst, *m_xTreeView)) // Headings root entry
4591 m_pActiveShell->GotoPage(1, true);
4592 else
4594 grab_focus();
4595 m_bIgnoreDocChange = false;
4597 m_pActiveShell->GetDoc()->GetDocShell()->Broadcast(SfxHint(SfxHintId::DocChanged));
4598 }
4599 break;
4600 case 11:
4601 case 12:
4602 case 13:
4603 nSelectedPopupEntry -= 10;
4604 if(m_nOutlineTracking != nSelectedPopupEntry)
4605 SetOutlineTracking(static_cast<sal_uInt8>(nSelectedPopupEntry));
4606 break;
4607 //Outlinelevel
4608 case 101:
4609 case 102:
4610 case 103:
4611 case 104:
4612 case 105:
4613 case 106:
4614 case 107:
4615 case 108:
4616 case 109:
4617 case 110:
4618 nSelectedPopupEntry -= 100;
4619 if(m_nOutlineLevel != nSelectedPopupEntry )
4620 SetOutlineLevel(static_cast<sal_Int8>(nSelectedPopupEntry));
4621 break;
4622 case 201:
4623 case 202:
4624 case 203:
4625 GetParentWindow()->SetRegionDropMode(static_cast<RegionMode>(nSelectedPopupEntry - 201));
4626 break;
4627 case 401:
4628 case 402:
4629 EditEntry(*xFirst, nSelectedPopupEntry == 401 ? EditEntryMode::RMV_IDX : EditEntryMode::UPD_IDX);
4630 break;
4631 // Edit entry
4632 case 403:
4634 break;
4635 case 404:
4637 break;
4638 case 405 :
4639 {
4640 const SwTOXBase* pBase = weld::fromId<SwTOXBaseContent*>(m_xTreeView->get_id(*xFirst))
4641 ->GetTOXBase();
4643 }
4644 break;
4645 case 502 :
4647 break;
4648 case 600:
4650 break;
4651 case 601:
4653 break;
4654 case 602:
4655 {
4658 break;
4659 }
4660 case 700:
4661 {
4663 break;
4664 }
4665 case 800:
4666 ExpandOrCollapseAll(*m_xTreeView, *xFirst);
4667 break;
4668 case 801:
4669 ExecCommand("chapterup", true);
4670 break;
4671 case 802:
4672 ExecCommand("chapterdown", true);
4673 break;
4674 case 803:
4675 ExecCommand("promote", true);
4676 break;
4677 case 804:
4678 ExecCommand("demote", true);
4679 break;
4680 case 805: // select document content
4681 {
4685 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xFirst));
4686 const ContentTypeId eTypeId = pCnt->GetParent()->GetType();
4687 if (eTypeId == ContentTypeId::OUTLINE)
4688 {
4689 SwOutlineNodes::size_type nActPos = weld::fromId<SwOutlineContent*>(
4690 m_xTreeView->get_id(*xFirst))->GetOutlinePos();
4691 m_pActiveShell->GotoOutline(nActPos);
4692 m_xTreeView->selected_foreach([this](weld::TreeIter& rEntry){
4693 SwOutlineNodes::size_type nPos = weld::fromId<SwOutlineContent*>(
4694 m_xTreeView->get_id(rEntry))->GetOutlinePos();
4696 // select children if not expanded and don't kill PaMs
4698 !m_xTreeView->get_row_expanded(rEntry), false);
4700 return false;
4701 });
4702 }
4703 else if (eTypeId == ContentTypeId::TABLE)
4704 {
4707 }
4708 else if (eTypeId == ContentTypeId::REGION)
4709 {
4717 }
4719 }
4720 break;
4721 case 900:
4722 {
4723 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(*xFirst));
4724 GotoContent(pCnt);
4725 }
4726 break;
4727 //Display
4728 default:
4729 if(nSelectedPopupEntry > 300 && nSelectedPopupEntry < 400)
4730 {
4731 nSelectedPopupEntry -= 300;
4732 SwView *pView = SwModule::GetFirstView();
4733 while (pView)
4734 {
4735 nSelectedPopupEntry --;
4736 if(nSelectedPopupEntry == 0)
4737 {
4738 SetConstantShell(&pView->GetWrtShell());
4739 break;
4740 }
4741 pView = SwModule::GetNextView(pView);
4742 }
4743 if(nSelectedPopupEntry)
4744 {
4745 m_bViewHasChanged = nSelectedPopupEntry == 1;
4746 m_eState = (nSelectedPopupEntry == 1) ? State::ACTIVE : State::HIDDEN;
4747 Display(nSelectedPopupEntry == 1);
4748 }
4750 }
4751 }
4752}
4753
4755{
4756 auto nChapters(0);
4757
4759
4761 m_xTreeView->selected_foreach([this, &nChapters](weld::TreeIter& rEntry){
4762 ++nChapters;
4763 if (m_xTreeView->iter_has_child(rEntry) &&
4764 !m_xTreeView->get_row_expanded(rEntry)) // only count children if not expanded
4765 {
4766 nChapters += m_xTreeView->iter_n_children(rEntry);
4767 }
4768 SwOutlineNodes::size_type nActPos = weld::fromId<SwOutlineContent*>(m_xTreeView->get_id(rEntry))->GetOutlinePos();
4770 m_pActiveShell->MakeOutlineSel(nActPos, nActPos, !m_xTreeView->get_row_expanded(rEntry), false); // select children if not expanded
4771 // The outline selection may already be to the start of the following outline paragraph
4772 // as happens when a table is the last content of the to be deleted outline. In this case
4773 // do not extend the to be deleted selection right or the first character of the following
4774 // outline paragraph will be removed. Also check if no selection was made which indicates
4775 // an empty paragraph and selection right is needed.
4779 return false;
4780 });
4782
4783 SwRewriter aRewriter;
4784 aRewriter.AddRule(UndoArg1, SwResId(STR_CHAPTERS, nChapters));
4786 m_pActiveShell->Delete(false);
4788
4790}
4791
4793{
4794 if (nSet == m_nOutlineLevel)
4795 return;
4796 m_nOutlineLevel = nSet;
4798 std::unique_ptr<SwContentType>& rpContentT = (State::ACTIVE == m_eState)
4801 if(rpContentT)
4802 {
4803 rpContentT->SetOutlineLevel(m_nOutlineLevel);
4804 rpContentT->FillMemberList();
4805 }
4807}
4808
4810{
4811 m_nOutlineTracking = nSet;
4813}
4814
4816{
4817 mTrackContentType[eCntTypeId] = bSet;
4818 m_pConfig->SetContentTypeTrack(eCntTypeId, bSet);
4819}
4820
4821// Mode Change: Show dropped Doc
4823{
4824 if(m_pHiddenShell)
4825 {
4827 Display(false);
4828 }
4829}
4830
4831// Mode Change: Show active view
4833{
4835 Display(true);
4837}
4838
4840{
4841 if (m_pConfig->IsNavigateOnSelect())
4842 {
4843 ContentDoubleClickHdl(*m_xTreeView);
4844 grab_focus();
4845 }
4846 Select();
4847 if (m_bIsRoot)
4848 return;
4849 // Select the content type in the Navigate By control
4850 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
4851 if (!m_xTreeView->get_selected(xEntry.get()))
4852 return;
4853 while (m_xTreeView->get_iter_depth(*xEntry))
4854 m_xTreeView->iter_parent(*xEntry);
4855 m_pDialog->SelectNavigateByContentType(m_xTreeView->get_text(*xEntry));
4856}
4857
4858// Here the buttons for moving outlines are en-/disabled.
4860{
4861 std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
4862 if (!m_xTreeView->get_selected(xEntry.get()))
4863 return;
4864
4865 bool bEnable = false;
4866 std::unique_ptr<weld::TreeIter> xParentEntry(m_xTreeView->make_iterator(xEntry.get()));
4867 bool bParentEntry = m_xTreeView->iter_parent(*xParentEntry);
4868 while (bParentEntry && (!lcl_IsContentType(*xParentEntry, *m_xTreeView)))
4869 bParentEntry = m_xTreeView->iter_parent(*xParentEntry);
4870 if (!m_bIsLastReadOnly)
4871 {
4872 if (!m_xTreeView->get_visible())
4873 bEnable = true;
4874 else if (bParentEntry)
4875 {
4877 (lcl_IsContent(*xEntry, *m_xTreeView) &&
4878 weld::fromId<SwContentType*>(m_xTreeView->get_id(*xParentEntry))->GetType() == ContentTypeId::OUTLINE))
4879 {
4880 bEnable = true;
4881 }
4882 }
4883 }
4884
4886 pNavi->m_xContent6ToolBox->set_item_sensitive("chapterup", bEnable);
4887 pNavi->m_xContent6ToolBox->set_item_sensitive("chapterdown", bEnable);
4888 pNavi->m_xContent6ToolBox->set_item_sensitive("promote", bEnable);
4889 pNavi->m_xContent6ToolBox->set_item_sensitive("demote", bEnable);
4890}
4891
4893{
4895 m_bIsRoot = true;
4897}
4898
4899OUString SwContentType::RemoveNewline(const OUString& rEntry)
4900{
4901 if (rEntry.isEmpty())
4902 return rEntry;
4903
4904 OUStringBuffer aEntry(rEntry);
4905 for (sal_Int32 i = 0; i < rEntry.getLength(); ++i)
4906 if(aEntry[i] == 10 || aEntry[i] == 13)
4907 aEntry[i] = 0x20;
4908
4909 return aEntry.makeStringAndClear();
4910}
4911
4913{
4914 SwContent* pCnt = weld::fromId<SwContent*>(m_xTreeView->get_id(rEntry));
4915 GotoContent(pCnt);
4916 const ContentTypeId nType = pCnt->GetParent()->GetType();
4917 sal_uInt16 nSlot = 0;
4918
4919 if(EditEntryMode::DELETE == nMode)
4920 m_bIgnoreDocChange = true;
4921
4922 uno::Reference< container::XNameAccess > xNameAccess, xSecond, xThird;
4923 switch(nType)
4924 {
4926 if(nMode == EditEntryMode::DELETE)
4927 {
4929 }
4930 break;
4931
4934 {
4936 GetDoc()->UnProtectCells( pCnt->GetName());
4937 }
4938 else if(nMode == EditEntryMode::DELETE)
4939 {
4941 OUString sTable = SwResId(STR_TABLE_NAME);
4942 SwRewriter aRewriterTableName;
4943 aRewriterTableName.AddRule(UndoArg1, SwResId(STR_START_QUOTE));
4944 aRewriterTableName.AddRule(UndoArg2, pCnt->GetName());
4945 aRewriterTableName.AddRule(UndoArg3, SwResId(STR_END_QUOTE));
4946 sTable = aRewriterTableName.Apply(sTable);
4947
4948 SwRewriter aRewriter;
4949 aRewriter.AddRule(UndoArg1, sTable);
4955 }
4956 else if(nMode == EditEntryMode::RENAME)
4957 {
4958 uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
4959 uno::Reference< text::XTextTablesSupplier > xTables(xModel, uno::UNO_QUERY);
4960 xNameAccess = xTables->getTextTables();
4961 }
4962 else
4963 nSlot = FN_FORMAT_TABLE_DLG;
4964 break;
4965
4967 if(nMode == EditEntryMode::DELETE)
4968 {
4970 }
4971 else if(nMode == EditEntryMode::RENAME)
4972 {
4973 uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
4974 uno::Reference< text::XTextGraphicObjectsSupplier > xGraphics(xModel, uno::UNO_QUERY);
4975 xNameAccess = xGraphics->getGraphicObjects();
4976 uno::Reference< text::XTextFramesSupplier > xFrames(xModel, uno::UNO_QUERY);
4977 xSecond = xFrames->getTextFrames();
4978 uno::Reference< text::XTextEmbeddedObjectsSupplier > xObjs(xModel, uno::UNO_QUERY);
4979 xThird = xObjs->getEmbeddedObjects();
4980 }
4981 else
4982 nSlot = FN_FORMAT_GRAFIC_DLG;
4983 break;
4984
4986 case ContentTypeId::OLE :
4987 if(nMode == EditEntryMode::DELETE)
4988 {
4990 }
4991 else if(nMode == EditEntryMode::RENAME)
4992 {
4993 uno::Reference< frame::XModel > xModel = m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
4994 uno::Reference< text::XTextFramesSupplier > xFrames(xModel, uno::UNO_QUERY);
4995 uno::Reference< text::XTextEmbeddedObjectsSupplier > xObjs(xModel, uno::UNO_QUERY);
4997 {
4998 xNameAccess = xFrames->getTextFrames();
4999 xSecond = xObjs->getEmbeddedObjects();
5000 }
5001 else
5002 {
5003 xNameAccess = xObjs->getEmbeddedObjects();
5004 xSecond = xFrames->getTextFrames();
5005 }
5006 uno::Reference< text::XTextGraphicObjectsSupplier > xGraphics(xModel, uno::UNO_QUERY);
5007 xThird = xGraphics->getGraphicObjects();
5008 }
5009 else
5010 nSlot = FN_FORMAT_FRAME_DLG;
5011 break;
5013 if(nMode == EditEntryMode::DELETE)
5014 {
5017 pMarkAccess->deleteMark(pMarkAccess->