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