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