android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / doc / doc.cxx
blobe3b606c6916dee6e19ac186ccb377fbd896bf013
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
22 #include <doc.hxx>
23 #include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
24 #include <com/sun/star/frame/XModel.hpp>
25 #include <DocumentFieldsManager.hxx>
26 #include <DocumentSettingManager.hxx>
27 #include <DocumentDrawModelManager.hxx>
28 #include <DocumentTimerManager.hxx>
29 #include <DocumentDeviceManager.hxx>
30 #include <DocumentChartDataProviderManager.hxx>
31 #include <DocumentLinksAdministrationManager.hxx>
32 #include <DocumentListItemsManager.hxx>
33 #include <DocumentListsManager.hxx>
34 #include <DocumentOutlineNodesManager.hxx>
35 #include <DocumentContentOperationsManager.hxx>
36 #include <DocumentRedlineManager.hxx>
37 #include <DocumentStatisticsManager.hxx>
38 #include <DocumentStateManager.hxx>
39 #include <DocumentStylePoolManager.hxx>
40 #include <DocumentLayoutManager.hxx>
41 #include <DocumentExternalDataManager.hxx>
42 #include <UndoManager.hxx>
43 #include <dbmgr.hxx>
44 #include <hintids.hxx>
46 #include <comphelper/random.hxx>
47 #include <tools/multisel.hxx>
48 #include <rtl/ustring.hxx>
49 #include <svl/poolitem.hxx>
50 #include <unotools/syslocale.hxx>
51 #include <editeng/keepitem.hxx>
52 #include <editeng/formatbreakitem.hxx>
53 #include <editeng/pbinitem.hxx>
54 #include <unotools/localedatawrapper.hxx>
56 #include <officecfg/Office/Writer.hxx>
58 #include <swatrset.hxx>
59 #include <swmodule.hxx>
60 #include <fmtrfmrk.hxx>
61 #include <fmtinfmt.hxx>
62 #include <fmtfld.hxx>
63 #include <txtfld.hxx>
64 #include <dbfld.hxx>
65 #include <txtinet.hxx>
66 #include <txtrfmrk.hxx>
67 #include <frmatr.hxx>
68 #include <pagefrm.hxx>
69 #include <rootfrm.hxx>
70 #include <pam.hxx>
71 #include <ndtxt.hxx>
72 #include <swundo.hxx>
73 #include <rolbck.hxx>
74 #include <UndoAttribute.hxx>
75 #include <UndoCore.hxx>
76 #include <UndoTable.hxx>
77 #include <pagedesc.hxx>
78 #include <doctxm.hxx>
79 #include <poolfmt.hxx>
80 #include <SwGrammarMarkUp.hxx>
81 #include <scriptinfo.hxx>
82 #include <mdiexp.hxx>
83 #include <docary.hxx>
84 #include <printdata.hxx>
85 #include <strings.hrc>
86 #include <SwUndoTOXChange.hxx>
87 #include <unocrsr.hxx>
88 #include <docfld.hxx>
89 #include <docufld.hxx>
90 #include <viewsh.hxx>
91 #include <shellres.hxx>
92 #include <txtfrm.hxx>
93 #include <attrhint.hxx>
95 #include <vector>
96 #include <map>
97 #include <o3tl/string_view.hxx>
98 #include <osl/diagnose.h>
99 #include <osl/interlck.h>
100 #include <vbahelper/vbaaccesshelper.hxx>
101 #include <editeng/langitem.hxx>
102 #include <calbck.hxx>
103 #include <crsrsh.hxx>
105 /* @@@MAINTAINABILITY-HORROR@@@
106 Probably unwanted dependency on SwDocShell
108 #include <docsh.hxx>
110 #include <com/sun/star/text/XTextRange.hpp>
111 #include <editeng/unoprnms.hxx>
112 #include <unotextrange.hxx>
113 #include <unoprnms.hxx>
114 #include <unomap.hxx>
116 using namespace ::com::sun::star;
118 sal_Int32 SwDoc::acquire()
120 assert(mReferenceCount >= 0);
121 return osl_atomic_increment(&mReferenceCount);
124 sal_Int32 SwDoc::release()
126 assert(mReferenceCount >= 1);
127 auto x = osl_atomic_decrement(&mReferenceCount);
128 if (x == 0)
129 delete this;
130 return x;
133 sal_Int32 SwDoc::getReferenceCount() const
135 assert(mReferenceCount >= 0);
136 return mReferenceCount;
139 ::sw::MetaFieldManager & SwDoc::GetMetaFieldManager()
141 return *m_pMetaFieldManager;
144 ::SwContentControlManager& SwDoc::GetContentControlManager()
146 return *m_pContentControlManager;
149 ::sw::UndoManager & SwDoc::GetUndoManager()
151 return *m_pUndoManager;
154 ::sw::UndoManager const & SwDoc::GetUndoManager() const
156 return *m_pUndoManager;
160 IDocumentUndoRedo & SwDoc::GetIDocumentUndoRedo()
162 return *m_pUndoManager;
165 IDocumentUndoRedo const & SwDoc::GetIDocumentUndoRedo() const
167 return *m_pUndoManager;
170 /* IDocumentDrawModelAccess */
171 IDocumentDrawModelAccess const & SwDoc::getIDocumentDrawModelAccess() const
173 return GetDocumentDrawModelManager();
176 IDocumentDrawModelAccess & SwDoc::getIDocumentDrawModelAccess()
178 return GetDocumentDrawModelManager();
181 ::sw::DocumentDrawModelManager const & SwDoc::GetDocumentDrawModelManager() const
183 return *m_pDocumentDrawModelManager;
186 ::sw::DocumentDrawModelManager & SwDoc::GetDocumentDrawModelManager()
188 return *m_pDocumentDrawModelManager;
191 /* IDocumentSettingAccess */
192 IDocumentSettingAccess const & SwDoc::getIDocumentSettingAccess() const
194 return GetDocumentSettingManager();
197 IDocumentSettingAccess & SwDoc::getIDocumentSettingAccess()
199 return GetDocumentSettingManager();
202 ::sw::DocumentSettingManager & SwDoc::GetDocumentSettingManager()
204 return *m_pDocumentSettingManager;
207 ::sw::DocumentSettingManager const & SwDoc::GetDocumentSettingManager() const
209 return *m_pDocumentSettingManager;
212 sal_uInt32 SwDoc::getRsid() const
214 return mnRsid;
217 void SwDoc::setRsid( sal_uInt32 nVal )
219 static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
221 sal_uInt32 nIncrease = 0;
222 if (!bHack)
224 // Increase the rsid with a random number smaller than 2^17. This way we
225 // expect to be able to edit a document 2^12 times before rsid overflows.
226 // start from 1 to ensure the new rsid is not the same
227 nIncrease = comphelper::rng::uniform_uint_distribution(1, (1 << 17) - 1);
229 mnRsid = nVal + nIncrease;
232 sal_uInt32 SwDoc::getRsidRoot() const
234 return mnRsidRoot;
237 void SwDoc::setRsidRoot( sal_uInt32 nVal )
239 mnRsidRoot = nVal;
242 /* IDocumentChartDataProviderAccess */
243 IDocumentChartDataProviderAccess const & SwDoc::getIDocumentChartDataProviderAccess() const
245 return *m_pDocumentChartDataProviderManager;
248 IDocumentChartDataProviderAccess & SwDoc::getIDocumentChartDataProviderAccess()
250 return *m_pDocumentChartDataProviderManager;
253 // IDocumentDeviceAccess
254 IDocumentDeviceAccess const & SwDoc::getIDocumentDeviceAccess() const
256 return *m_pDeviceAccess;
259 IDocumentDeviceAccess & SwDoc::getIDocumentDeviceAccess()
261 return *m_pDeviceAccess;
264 //IDocumentTimerAccess
265 IDocumentTimerAccess const & SwDoc::getIDocumentTimerAccess() const
267 return *m_pDocumentTimerManager;
270 IDocumentTimerAccess & SwDoc::getIDocumentTimerAccess()
272 return *m_pDocumentTimerManager;
275 // IDocumentLinksAdministration
276 IDocumentLinksAdministration const & SwDoc::getIDocumentLinksAdministration() const
278 return *m_pDocumentLinksAdministrationManager;
281 IDocumentLinksAdministration & SwDoc::getIDocumentLinksAdministration()
283 return *m_pDocumentLinksAdministrationManager;
286 ::sw::DocumentLinksAdministrationManager const & SwDoc::GetDocumentLinksAdministrationManager() const
288 return *m_pDocumentLinksAdministrationManager;
291 ::sw::DocumentLinksAdministrationManager & SwDoc::GetDocumentLinksAdministrationManager()
293 return *m_pDocumentLinksAdministrationManager;
296 //IDocumentListItems
297 IDocumentListItems const & SwDoc::getIDocumentListItems() const
299 return *m_pDocumentListItemsManager;
302 //IDocumentListItems
303 IDocumentListItems & SwDoc::getIDocumentListItems()
305 return *m_pDocumentListItemsManager;
308 //IDocumentListsAccess
309 IDocumentListsAccess const & SwDoc::getIDocumentListsAccess() const
311 return *m_pDocumentListsManager;
314 IDocumentListsAccess & SwDoc::getIDocumentListsAccess()
316 return *m_pDocumentListsManager;
319 //IDocumentOutlinesNodes
320 IDocumentOutlineNodes const & SwDoc::getIDocumentOutlineNodes() const
322 return *m_pDocumentOutlineNodesManager;
325 IDocumentOutlineNodes & SwDoc::getIDocumentOutlineNodes()
327 return *m_pDocumentOutlineNodesManager;
330 //IDocumentContentOperations
331 IDocumentContentOperations const & SwDoc::getIDocumentContentOperations() const
333 return *m_pDocumentContentOperationsManager;
336 IDocumentContentOperations & SwDoc::getIDocumentContentOperations()
338 return *m_pDocumentContentOperationsManager;
341 ::sw::DocumentContentOperationsManager const & SwDoc::GetDocumentContentOperationsManager() const
343 return *m_pDocumentContentOperationsManager;
345 ::sw::DocumentContentOperationsManager & SwDoc::GetDocumentContentOperationsManager()
347 return *m_pDocumentContentOperationsManager;
350 //IDocumentRedlineAccess
351 IDocumentRedlineAccess const & SwDoc::getIDocumentRedlineAccess() const
353 return *m_pDocumentRedlineManager;
356 IDocumentRedlineAccess& SwDoc::getIDocumentRedlineAccess()
358 return *m_pDocumentRedlineManager;
361 ::sw::DocumentRedlineManager const & SwDoc::GetDocumentRedlineManager() const
363 return *m_pDocumentRedlineManager;
366 ::sw::DocumentRedlineManager& SwDoc::GetDocumentRedlineManager()
368 return *m_pDocumentRedlineManager;
371 //IDocumentFieldsAccess
373 IDocumentFieldsAccess const & SwDoc::getIDocumentFieldsAccess() const
375 return *m_pDocumentFieldsManager;
378 IDocumentFieldsAccess & SwDoc::getIDocumentFieldsAccess()
380 return *m_pDocumentFieldsManager;
383 ::sw::DocumentFieldsManager & SwDoc::GetDocumentFieldsManager()
385 return *m_pDocumentFieldsManager;
388 //IDocumentStatistics
389 IDocumentStatistics const & SwDoc::getIDocumentStatistics() const
391 return *m_pDocumentStatisticsManager;
394 IDocumentStatistics & SwDoc::getIDocumentStatistics()
396 return *m_pDocumentStatisticsManager;
399 ::sw::DocumentStatisticsManager const & SwDoc::GetDocumentStatisticsManager() const
401 return *m_pDocumentStatisticsManager;
404 ::sw::DocumentStatisticsManager & SwDoc::GetDocumentStatisticsManager()
406 return *m_pDocumentStatisticsManager;
409 //IDocumentState
410 IDocumentState const & SwDoc::getIDocumentState() const
412 return *m_pDocumentStateManager;
415 IDocumentState & SwDoc::getIDocumentState()
417 return *m_pDocumentStateManager;
420 //IDocumentLayoutAccess
421 IDocumentLayoutAccess const & SwDoc::getIDocumentLayoutAccess() const
423 return *m_pDocumentLayoutManager;
426 IDocumentLayoutAccess & SwDoc::getIDocumentLayoutAccess()
428 return *m_pDocumentLayoutManager;
431 ::sw::DocumentLayoutManager const & SwDoc::GetDocumentLayoutManager() const
433 return *m_pDocumentLayoutManager;
436 ::sw::DocumentLayoutManager & SwDoc::GetDocumentLayoutManager()
438 return *m_pDocumentLayoutManager;
441 //IDocumentStylePoolAccess
442 IDocumentStylePoolAccess const & SwDoc::getIDocumentStylePoolAccess() const
444 return *m_pDocumentStylePoolManager;
447 IDocumentStylePoolAccess & SwDoc::getIDocumentStylePoolAccess()
449 return *m_pDocumentStylePoolManager;
452 //IDocumentExternalData
453 IDocumentExternalData const & SwDoc::getIDocumentExternalData() const
455 return *m_pDocumentExternalDataManager;
458 IDocumentExternalData & SwDoc::getIDocumentExternalData()
460 return *m_pDocumentExternalDataManager;
463 /* Implementations the next Interface here */
466 * Document editing (Doc-SS) to fill the document
467 * by the RTF parser and for the EditShell.
469 void SwDoc::ChgDBData(const SwDBData& rNewData)
471 if( rNewData != maDBData )
473 maDBData = rNewData;
474 getIDocumentState().SetModified();
475 if (m_pDBManager)
476 m_pDBManager->CommitLastRegistrations();
478 getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DatabaseName)->UpdateFields();
481 namespace {
483 struct PostItField_ : public SetGetExpField
485 PostItField_( const SwNode& rNd, const SwTextField* pField )
486 : SetGetExpField( rNd, pField, std::nullopt ) {}
488 sal_uInt16 GetPageNo( const StringRangeEnumerator &rRangeEnum,
489 const o3tl::sorted_vector< sal_Int32 > &rPossiblePages,
490 sal_uInt16& rVirtPgNo, sal_Int32& rLineNo );
492 const SwPostItField* GetPostIt() const
494 return static_cast<const SwPostItField*>( GetTextField()->GetFormatField().GetField() );
500 sal_uInt16 PostItField_::GetPageNo(
501 const StringRangeEnumerator &rRangeEnum,
502 const o3tl::sorted_vector< sal_Int32 > &rPossiblePages,
503 /* out */ sal_uInt16& rVirtPgNo, /* out */ sal_Int32& rLineNo )
505 //Problem: If a PostItField is contained in a Node that is represented
506 //by more than one layout instance,
507 //we have to decide whether it should be printed once or n-times.
508 //Probably only once. For the page number we don't select a random one,
509 //but the PostIt's first occurrence in the selected area.
510 rVirtPgNo = 0;
511 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(GetTextField()->GetTextNode());
512 for( SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
514 TextFrameIndex const nPos = pFrame->MapModelToView(
515 &GetTextField()->GetTextNode(), GetContent());
516 if( pFrame->GetOffset() > nPos ||
517 (pFrame->HasFollow() && pFrame->GetFollow()->GetOffset() <= nPos) )
518 continue;
519 sal_uInt16 nPgNo = pFrame->GetPhyPageNum();
520 if( rRangeEnum.hasValue( nPgNo, &rPossiblePages ))
522 rLineNo = o3tl::narrowing<sal_Int32>(pFrame->GetLineCount( nPos ) +
523 pFrame->GetAllLines() - pFrame->GetThisLines());
524 rVirtPgNo = pFrame->GetVirtPageNum();
525 return nPgNo;
528 return 0;
531 bool sw_GetPostIts(const IDocumentFieldsAccess& rIDFA, SetGetExpFields* pSrtLst)
533 SwFieldType* pFieldType = rIDFA.GetSysFieldType(SwFieldIds::Postit);
534 assert(pFieldType);
536 std::vector<SwFormatField*> vFields;
537 pFieldType->GatherFields(vFields);
538 if(pSrtLst)
539 for(auto pField: vFields)
541 auto pTextField = pField->GetTextField();
542 std::unique_ptr<PostItField_> pNew(new PostItField_(pTextField->GetTextNode(), pTextField));
543 pSrtLst->insert(std::move(pNew));
546 return vFields.size()>0;
549 static void lcl_FormatPostIt(
550 IDocumentContentOperations* pIDCO,
551 SwPaM& aPam,
552 const SwPostItField* pField,
553 bool bNewPage, bool bIsFirstPostIt,
554 sal_uInt16 nPageNo, sal_Int32 nLineNo )
556 static char const sTmp[] = " : ";
558 assert(SwViewShell::GetShellRes());
560 if (bNewPage)
562 pIDCO->InsertPoolItem( aPam, SvxFormatBreakItem( SvxBreak::PageAfter, RES_BREAK ) );
563 pIDCO->SplitNode( *aPam.GetPoint(), false );
565 else if (!bIsFirstPostIt)
567 // add an empty line between different notes
568 pIDCO->SplitNode( *aPam.GetPoint(), false );
569 pIDCO->SplitNode( *aPam.GetPoint(), false );
572 OUString aStr = SwViewShell::GetShellRes()->aPostItPage +
573 sTmp +
574 OUString::number( nPageNo ) +
575 " ";
576 if( nLineNo )
578 aStr += SwViewShell::GetShellRes()->aPostItLine +
579 sTmp +
580 OUString::number( nLineNo ) +
581 " ";
583 SvtSysLocale aSysLocale;
584 aStr += SwViewShell::GetShellRes()->aPostItAuthor +
585 sTmp + pField->GetPar1() + " " +
586 /*(LocaleDataWrapper&)*/aSysLocale.GetLocaleData().getDate( pField->GetDate() );
587 if(pField->GetResolved())
588 aStr += " " + SwResId(STR_RESOLVED);
589 pIDCO->InsertString( aPam, aStr );
591 pIDCO->SplitNode( *aPam.GetPoint(), false );
592 aStr = pField->GetPar2();
593 #if defined(_WIN32)
594 // Throw out all CR in Windows
595 aStr = aStr.replaceAll("\r", "");
596 #endif
597 pIDCO->InsertString( aPam, aStr );
600 /// provide the paper tray to use according to the page style in use,
601 /// but do that only if the respective item is NOT just the default item
602 static sal_Int32 lcl_GetPaperBin( const SwPageFrame *pStartFrame )
604 sal_Int32 nRes = -1;
606 const SwFrameFormat &rFormat = pStartFrame->GetPageDesc()->GetMaster();
607 const SfxPoolItem *pItem = nullptr;
608 SfxItemState eState = rFormat.GetItemState( RES_PAPER_BIN, false, &pItem );
609 const SvxPaperBinItem *pPaperBinItem = dynamic_cast< const SvxPaperBinItem * >(pItem);
610 if (eState > SfxItemState::DEFAULT && pPaperBinItem)
611 nRes = pPaperBinItem->GetValue();
613 return nRes;
616 namespace
618 // tdf#:114663 Translates a range string from user input (with page numbering possibly not
619 // taking blank pages into account) to equivalent string which references physical page numbers.
620 // rUIPages2PhyPagesMap must contain a contiguous sequence of UI page numbers
621 OUString UIPages2PhyPages(const OUString& rUIPageRange, const std::map< sal_Int32, sal_Int32 >& rUIPages2PhyPagesMap)
623 if (rUIPages2PhyPagesMap.empty())
624 return OUString();
625 auto iMin = rUIPages2PhyPagesMap.begin();
626 const sal_Int32 nUIPageMin = iMin->first, nPhyPageMin = iMin->second;
627 auto iMax = rUIPages2PhyPagesMap.rbegin();
628 const sal_Int32 nUIPageMax = iMax->first, nPhyPageMax = iMax->second;
629 OUStringBuffer aOut(rUIPageRange.getLength());
630 OUStringBuffer aNumber(16);
631 const sal_Unicode* pInput = rUIPageRange.getStr();
632 while (*pInput)
634 while (*pInput >= '0' && *pInput <= '9')
635 aNumber.append(*pInput++);
636 if (!aNumber.isEmpty())
638 sal_Int32 nNumber = o3tl::toInt32(aNumber);
639 aNumber.setLength(0);
640 if (nNumber < nUIPageMin)
641 nNumber = nPhyPageMin-1;
642 else if (nNumber > nUIPageMax)
643 nNumber = nPhyPageMax+1;
644 else
645 nNumber = rUIPages2PhyPagesMap.at(nNumber);
646 aOut.append(nNumber);
649 while (*pInput && (*pInput < '0' || *pInput > '9'))
650 aOut.append(*pInput++);
653 return aOut.makeStringAndClear();
657 // tdf#52316 remove blank pages from page count and actual page number
658 void SwDoc::CalculateNonBlankPages(
659 const SwRootFrame& rLayout,
660 sal_uInt16& nDocPageCount,
661 sal_uInt16& nActualPage)
663 sal_uInt16 nDocPageCountWithBlank = nDocPageCount;
664 sal_uInt16 nActualPageWithBlank = nActualPage;
665 sal_uInt16 nPageNum = 1;
666 const SwPageFrame *pStPage = dynamic_cast<const SwPageFrame*>( rLayout.Lower() );
667 while (pStPage && nPageNum <= nDocPageCountWithBlank)
669 if ( pStPage->getFrameArea().Height() == 0 )
671 --nDocPageCount;
672 if (nPageNum <= nActualPageWithBlank)
673 --nActualPage;
675 ++nPageNum;
676 pStPage = static_cast<const SwPageFrame*>(pStPage->GetNext());
680 void SwDoc::CalculatePagesForPrinting(
681 const SwRootFrame& rLayout,
682 /* out */ SwRenderData &rData,
683 const SwPrintUIOptions &rOptions,
684 bool bIsPDFExport,
685 sal_Int32 nDocPageCount )
687 const sal_Int64 nContent = rOptions.getIntValue( "PrintContent", 0 );
688 const bool bPrintSelection = nContent == 2;
690 // properties to take into account when calculating the set of pages
691 // (PDF export UI does not allow for selecting left or right pages only)
692 bool bPrintLeftPages = bIsPDFExport || rOptions.IsPrintLeftPages();
693 bool bPrintRightPages = bIsPDFExport || rOptions.IsPrintRightPages();
694 // #i103700# printing selections should not allow for automatic inserting empty pages
695 bool bPrintEmptyPages = !bPrintSelection && rOptions.IsPrintEmptyPages( bIsPDFExport );
697 std::map< sal_Int32, sal_Int32 > &rPrinterPaperTrays = rData.GetPrinterPaperTrays();
698 o3tl::sorted_vector< sal_Int32 > &rValidPages = rData.GetValidPagesSet();
699 // Map page numbers from user input (possibly ignoring blanks) to physical page numbers
700 std::map< sal_Int32, sal_Int32 > aUIPages2PhyPagesMap;
701 rValidPages.clear();
703 sal_Int32 nPageNum = 1, nUIPageNum = 1;
704 const SwPageFrame *pStPage = dynamic_cast<const SwPageFrame*>( rLayout.Lower() );
705 while (pStPage && nPageNum <= nDocPageCount)
707 const bool bNonEmptyPage = pStPage->getFrameArea().Height() != 0;
708 const bool bPrintThisPage =
709 ( (bPrintRightPages && pStPage->OnRightPage()) ||
710 (bPrintLeftPages && !pStPage->OnRightPage()) ) &&
711 ( bPrintEmptyPages || bNonEmptyPage );
713 if (bPrintThisPage)
715 rValidPages.insert( nPageNum );
716 rPrinterPaperTrays[ nPageNum ] = lcl_GetPaperBin( pStPage );
719 if ( bPrintEmptyPages || bNonEmptyPage )
721 aUIPages2PhyPagesMap[nUIPageNum++] = nPageNum;
723 ++nPageNum;
724 pStPage = static_cast<const SwPageFrame*>(pStPage->GetNext());
727 // now that we have identified the valid pages for printing according
728 // to the print settings we need to get the PageRange to use and
729 // use both results to get the actual pages to be printed
730 // (post-it settings need to be taken into account later on!)
732 // get PageRange value to use
733 OUString aPageRange;
734 // #i116085# - adjusting fix for i113919
735 if ( !bIsPDFExport )
737 // PageContent :
738 // 0 -> print all pages (default if aPageRange is empty)
739 // 1 -> print range according to PageRange
740 // 2 -> print selection
741 if (1 == nContent)
742 aPageRange = rOptions.getStringValue( "PageRange" );
744 if (2 == nContent)
746 // note that printing selections is actually implemented by copying
747 // the selection to a new temporary document and printing all of that one.
748 // Thus for Writer "PrintContent" must never be 2.
749 // See SwXTextDocument::GetRenderDoc for evaluating if a selection is to be
750 // printed and for creating the temporary document.
753 // please note
755 if (aPageRange.isEmpty()) // empty string -> print all
757 // set page range to print to 'all pages'
758 aPageRange = OUString::number( 1 ) + "-" + OUString::number( nDocPageCount );
760 else
762 // Convert page numbers from user input to physical page numbers
763 aPageRange = UIPages2PhyPages(aPageRange, aUIPages2PhyPagesMap);
765 rData.SetPageRange( aPageRange );
767 // get vector of pages to print according to PageRange and valid pages set from above
768 // (result may be an empty vector, for example if the range string is not correct)
769 // If excluding empty pages, allow range to specify range of printable pages
770 StringRangeEnumerator::getRangesFromString( aPageRange, rData.GetPagesToPrint(),
771 1, nDocPageCount, 0, &rData.GetValidPagesSet() );
774 void SwDoc::UpdatePagesForPrintingWithPostItData(
775 /* out */ SwRenderData &rData,
776 const SwPrintUIOptions &rOptions,
777 sal_Int32 nDocPageCount )
780 SwPostItMode nPostItMode = static_cast<SwPostItMode>( rOptions.getIntValue( "PrintAnnotationMode", 0 ) );
781 assert((nPostItMode == SwPostItMode::NONE || rData.HasPostItData())
782 && "print post-its without post-it data?");
783 const SetGetExpFields::size_type nPostItCount =
784 rData.HasPostItData() ? rData.m_pPostItFields->size() : 0;
785 if (nPostItMode == SwPostItMode::NONE || nPostItCount <= 0)
786 return;
788 CurrShell aCurr( rData.m_pPostItShell.get() );
790 // clear document and move to end of it
791 SwDoc & rPostItDoc(*rData.m_pPostItShell->GetDoc());
792 SwPaM aPam(rPostItDoc.GetNodes().GetEndOfContent());
793 aPam.Move( fnMoveBackward, GoInDoc );
794 aPam.SetMark();
795 aPam.Move( fnMoveForward, GoInDoc );
796 rPostItDoc.getIDocumentContentOperations().DeleteRange( aPam );
798 const StringRangeEnumerator aRangeEnum( rData.GetPageRange(), 1, nDocPageCount, 0 );
800 // For mode SwPostItMode::EndPage:
801 // maps a physical page number to the page number in post-it document that holds
802 // the first post-it for that physical page . Needed to relate the correct start frames
803 // from the post-it doc to the physical page of the document
804 std::map< sal_Int32, sal_Int32 > aPostItLastStartPageNum;
806 // add all post-its on valid pages within the page range to the
807 // temporary post-it document.
808 // Since the array of post-it fields is sorted by page and line number we will
809 // already get them in the correct order
810 sal_uInt16 nVirtPg = 0, nLastPageNum = 0, nPhyPageNum = 0;
811 sal_Int32 nLineNo = 0;
812 bool bIsFirstPostIt = true;
813 for (SetGetExpFields::size_type i = 0; i < nPostItCount; ++i)
815 PostItField_& rPostIt = static_cast<PostItField_&>(*(*rData.m_pPostItFields)[ i ]);
816 nLastPageNum = nPhyPageNum;
817 nPhyPageNum = rPostIt.GetPageNo(
818 aRangeEnum, rData.GetValidPagesSet(), nVirtPg, nLineNo );
819 if (nPhyPageNum)
821 // need to insert a page break?
822 // In SwPostItMode::EndPage mode for each document page the following
823 // post-it page needs to start on a new page
824 const bool bNewPage = nPostItMode == SwPostItMode::EndPage &&
825 !bIsFirstPostIt && nPhyPageNum != nLastPageNum;
827 lcl_FormatPostIt( &rData.m_pPostItShell->GetDoc()->getIDocumentContentOperations(), aPam,
828 rPostIt.GetPostIt(), bNewPage, bIsFirstPostIt, nVirtPg, nLineNo );
829 bIsFirstPostIt = false;
831 if (nPostItMode == SwPostItMode::EndPage)
833 // get the correct number of current pages for the post-it document
834 rData.m_pPostItShell->CalcLayout();
835 const sal_Int32 nPages = rData.m_pPostItShell->GetPageCount();
836 aPostItLastStartPageNum[ nPhyPageNum ] = nPages;
841 // format post-it doc to get correct number of pages
842 rData.m_pPostItShell->CalcLayout();
844 SwRootFrame* pPostItRoot = rData.m_pPostItShell->GetLayout();
845 //tdf#103313 print dialog maxes out cpu as Idles never get to
846 //complete this postitshell's desire to complete formatting
847 pPostItRoot->ResetIdleFormat();
849 const sal_Int32 nPostItDocPageCount = rData.m_pPostItShell->GetPageCount();
851 if (nPostItMode == SwPostItMode::Only || nPostItMode == SwPostItMode::EndDoc)
853 // now add those post-it pages to the vector of pages to print
854 // or replace them if only post-its should be printed
856 if (nPostItMode == SwPostItMode::Only)
858 // no document page to be printed
859 rData.GetPagesToPrint().clear();
862 // now we just need to add the post-it pages to be printed to the
863 // end of the vector of pages to print
864 sal_Int32 nPageNum = 0;
865 const SwPageFrame * pPageFrame = static_cast<SwPageFrame*>(pPostItRoot->Lower());
866 while( pPageFrame && nPageNum < nPostItDocPageCount )
868 ++nPageNum;
869 // negative page number indicates page is from the post-it doc
870 rData.GetPagesToPrint().push_back( -nPageNum );
871 pPageFrame = static_cast<const SwPageFrame*>(pPageFrame->GetNext());
873 OSL_ENSURE( nPageNum == nPostItDocPageCount, "unexpected number of pages" );
875 else if (nPostItMode == SwPostItMode::EndPage)
877 // the next step is to find all the pages from the post-it
878 // document that should be printed for a given physical page
879 // of the document
881 std::vector< sal_Int32 > aTmpPagesToPrint;
882 sal_Int32 nLastPostItPage(0);
883 const size_t nNum = rData.GetPagesToPrint().size();
884 for (size_t i = 0 ; i < nNum; ++i)
886 // add the physical page to print from the document
887 const sal_Int32 nPhysPage = rData.GetPagesToPrint()[i];
888 aTmpPagesToPrint.push_back( nPhysPage );
890 // add the post-it document pages to print, i.e those
891 // post-it pages that have the data for the above physical page
892 std::map<sal_Int32, sal_Int32>::const_iterator const iter(
893 aPostItLastStartPageNum.find(nPhysPage));
894 if (iter != aPostItLastStartPageNum.end())
896 for (sal_Int32 j = nLastPostItPage + 1;
897 j <= iter->second; ++j)
899 // negative page number indicates page is from the
900 aTmpPagesToPrint.push_back(-j); // post-it document
902 nLastPostItPage = iter->second;
906 // finally we need to assign those vectors to the resulting ones.
907 // swapping the data should be more efficient than assigning since
908 // we won't need the temporary vectors anymore
909 rData.GetPagesToPrint().swap( aTmpPagesToPrint );
914 void SwDoc::CalculatePagePairsForProspectPrinting(
915 const SwRootFrame& rLayout,
916 /* out */ SwRenderData &rData,
917 const SwPrintUIOptions &rOptions,
918 sal_Int32 nDocPageCount )
920 std::map< sal_Int32, sal_Int32 > &rPrinterPaperTrays = rData.GetPrinterPaperTrays();
921 o3tl::sorted_vector< sal_Int32 > &rValidPagesSet = rData.GetValidPagesSet();
922 std::vector< std::pair< sal_Int32, sal_Int32 > > &rPagePairs = rData.GetPagePairsForProspectPrinting();
923 std::map< sal_Int32, const SwPageFrame * > validStartFrames;
925 rPagePairs.clear();
926 rValidPagesSet.clear();
928 OUString aPageRange;
929 // PageContent :
930 // 0 -> print all pages (default if aPageRange is empty)
931 // 1 -> print range according to PageRange
932 // 2 -> print selection
933 const sal_Int64 nContent = rOptions.getIntValue( "PrintContent", 0 );
934 if (nContent == 1)
935 aPageRange = rOptions.getStringValue( "PageRange" );
936 if (aPageRange.isEmpty()) // empty string -> print all
938 // set page range to print to 'all pages'
939 aPageRange = OUString::number( 1 ) + "-" + OUString::number( nDocPageCount );
941 StringRangeEnumerator aRange( aPageRange, 1, nDocPageCount, 0 );
943 if ( aRange.size() <= 0)
944 return;
946 const SwPageFrame *pStPage = dynamic_cast<const SwPageFrame*>( rLayout.Lower() );
947 for ( sal_Int32 i = 1; pStPage && i < nDocPageCount; ++i )
948 pStPage = static_cast<const SwPageFrame*>(pStPage->GetNext());
949 if ( !pStPage ) // Then it was that
950 return;
952 // currently for prospect printing all pages are valid to be printed
953 // thus we add them all to the respective map and set for later use
954 sal_Int32 nPageNum = 0;
955 const SwPageFrame *pPageFrame = dynamic_cast<const SwPageFrame*>( rLayout.Lower() );
956 while( pPageFrame && nPageNum < nDocPageCount )
958 ++nPageNum;
959 rValidPagesSet.insert( nPageNum );
960 validStartFrames[ nPageNum ] = pPageFrame;
961 pPageFrame = static_cast<const SwPageFrame*>(pPageFrame->GetNext());
963 rPrinterPaperTrays[ nPageNum ] = lcl_GetPaperBin( pStPage );
965 OSL_ENSURE( nPageNum == nDocPageCount, "unexpected number of pages" );
967 // properties to take into account when calculating the set of pages
968 // Note: here bPrintLeftPages and bPrintRightPages refer to the (virtual) resulting pages
969 // of the prospect!
970 bool bPrintLeftPages = rOptions.IsPrintLeftPages();
971 bool bPrintRightPages = rOptions.IsPrintRightPages();
972 bool bPrintProspectRTL = rOptions.getIntValue( "PrintProspectRTL", 0 ) != 0;
974 // get pages for prospect printing according to the 'PageRange'
975 // (duplicates and any order allowed!)
976 std::vector< sal_Int32 > aPagesToPrint;
977 StringRangeEnumerator::getRangesFromString(
978 aPageRange, aPagesToPrint, 1, nDocPageCount, 0 );
980 if (aPagesToPrint.empty())
981 return;
983 // now fill the vector for calculating the page pairs with the start frames
984 // from the above obtained vector
985 std::vector< const SwPageFrame * > aVec;
986 for (sal_Int32 nPage : aPagesToPrint)
988 const SwPageFrame *pFrame = validStartFrames[ nPage ];
989 aVec.push_back( pFrame );
992 // just one page is special ...
993 if ( 1 == aVec.size() )
994 aVec.insert( aVec.begin() + 1, nullptr ); // insert a second empty page
995 else
997 // now extend the number of pages to fit a multiple of 4
998 // (4 'normal' pages are needed for a single prospect paper
999 // with back and front)
1000 while( aVec.size() & 3 )
1001 aVec.push_back( nullptr );
1004 // make sure that all pages are in correct order
1005 std::vector< const SwPageFrame * >::size_type nSPg = 0;
1006 std::vector< const SwPageFrame * >::size_type nEPg = aVec.size();
1007 sal_Int32 nStep = 1;
1008 if ( 0 == (nEPg & 1 )) // there are no uneven ones!
1009 --nEPg;
1011 if ( !bPrintLeftPages )
1012 ++nStep;
1013 else if ( !bPrintRightPages )
1015 ++nStep;
1016 ++nSPg;
1017 --nEPg;
1020 // the number of 'virtual' pages to be printed
1021 sal_Int32 nCntPage = (( nEPg - nSPg ) / ( 2 * nStep )) + 1;
1023 for ( sal_Int32 nPrintCount = 0; nSPg < nEPg &&
1024 nPrintCount < nCntPage; ++nPrintCount )
1026 pStPage = aVec[ nSPg ];
1027 const SwPageFrame* pNxtPage = nEPg < aVec.size() ? aVec[ nEPg ] : nullptr;
1029 short nRtlOfs = bPrintProspectRTL ? 1 : 0;
1030 if ( 0 == (( nSPg + nRtlOfs) & 1 ) ) // switch for odd number in LTR, even number in RTL
1032 const SwPageFrame* pTmp = pStPage;
1033 pStPage = pNxtPage;
1034 pNxtPage = pTmp;
1037 sal_Int32 nFirst = -1, nSecond = -1;
1038 for ( int nC = 0; nC < 2; ++nC )
1040 sal_Int32 nPage = -1;
1041 if ( pStPage )
1042 nPage = pStPage->GetPhyPageNum();
1043 if (nC == 0)
1044 nFirst = nPage;
1045 else
1046 nSecond = nPage;
1048 pStPage = pNxtPage;
1050 rPagePairs.emplace_back(nFirst, nSecond );
1052 nSPg = nSPg + nStep;
1053 nEPg = nEPg - nStep;
1055 OSL_ENSURE( size_t(nCntPage) == rPagePairs.size(), "size mismatch for number of page pairs" );
1057 // luckily prospect printing does not make use of post-its so far,
1058 // thus we are done here.
1061 /// @return the reference in the doc for the name
1062 const SwFormatRefMark* SwDoc::GetRefMark( std::u16string_view rName ) const
1064 for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_REFMARK))
1066 auto pFormatRef = dynamic_cast<const SwFormatRefMark*>(pItem);
1067 if(!pFormatRef)
1068 continue;
1070 const SwTextRefMark* pTextRef = pFormatRef->GetTextRefMark();
1071 if( pTextRef && &pTextRef->GetTextNode().GetNodes() == &GetNodes() &&
1072 rName == pFormatRef->GetRefName() )
1073 return pFormatRef;
1075 return nullptr;
1078 /// @return the RefMark per index - for Uno
1079 const SwFormatRefMark* SwDoc::GetRefMark( sal_uInt16 nIndex ) const
1081 const SwFormatRefMark* pRet = nullptr;
1083 sal_uInt32 nCount = 0;
1084 for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_REFMARK))
1086 auto pRefMark = dynamic_cast<const SwFormatRefMark*>(pItem);
1087 if( !pRefMark )
1088 continue;
1089 const SwTextRefMark* pTextRef = pRefMark->GetTextRefMark();
1090 if( pTextRef && &pTextRef->GetTextNode().GetNodes() == &GetNodes() )
1092 if(nCount == nIndex)
1094 pRet = pRefMark;
1095 break;
1097 nCount++;
1100 return pRet;
1103 /// @return the names of all set references in the Doc
1104 //JP 24.06.96: If the array pointer is 0, then just return whether a RefMark is set in the Doc
1105 // OS 25.06.96: From now on we always return the reference count
1106 sal_uInt16 SwDoc::GetRefMarks( std::vector<OUString>* pNames ) const
1108 sal_uInt16 nCount = 0;
1109 for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_REFMARK))
1111 auto pRefMark = dynamic_cast<const SwFormatRefMark*>(pItem);
1112 if( !pRefMark )
1113 continue;
1114 const SwTextRefMark* pTextRef = pRefMark->GetTextRefMark();
1115 if( pTextRef && &pTextRef->GetTextNode().GetNodes() == &GetNodes() )
1117 if( pNames )
1119 OUString aTmp(pRefMark->GetRefName());
1120 pNames->insert(pNames->begin() + nCount, aTmp);
1122 ++nCount;
1126 return nCount;
1129 void SwDoc::DeleteFormatRefMark(const SwFormatRefMark* pFormatRefMark)
1131 const SwTextRefMark* pTextRefMark = pFormatRefMark->GetTextRefMark();
1132 SwTextNode& rTextNd = const_cast<SwTextNode&>(pTextRefMark->GetTextNode());
1133 std::unique_ptr<SwRegHistory> aRegHistory;
1134 if (GetIDocumentUndoRedo().DoesUndo())
1136 SwUndoResetAttr* pUndo = new SwUndoResetAttr(SwPosition(rTextNd, pTextRefMark->GetStart()),
1137 RES_TXTATR_REFMARK);
1138 GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
1139 aRegHistory.reset(new SwRegHistory(rTextNd, &pUndo->GetHistory()));
1140 rTextNd.GetpSwpHints()->Register(aRegHistory.get());
1142 rTextNd.DeleteAttribute(const_cast<SwTextRefMark*>(pTextRefMark));
1143 if (GetIDocumentUndoRedo().DoesUndo())
1145 if (rTextNd.GetpSwpHints())
1146 rTextNd.GetpSwpHints()->DeRegister();
1148 getIDocumentState().SetModified();
1151 static bool lcl_SpellAndGrammarAgain( SwNode* pNd, void* pArgs )
1153 SwTextNode *pTextNode = pNd->GetTextNode();
1154 bool bOnlyWrong = *static_cast<sal_Bool*>(pArgs);
1155 if( pTextNode )
1157 if( bOnlyWrong )
1159 if( pTextNode->GetWrong() &&
1160 pTextNode->GetWrong()->InvalidateWrong() )
1161 pTextNode->SetWrongDirty(sw::WrongState::TODO);
1162 if( pTextNode->GetGrammarCheck() &&
1163 pTextNode->GetGrammarCheck()->InvalidateWrong() )
1164 pTextNode->SetGrammarCheckDirty( true );
1166 else
1168 pTextNode->SetWrongDirty(sw::WrongState::TODO);
1169 if( pTextNode->GetWrong() )
1170 pTextNode->GetWrong()->SetInvalid( 0, COMPLETE_STRING );
1171 pTextNode->SetGrammarCheckDirty( true );
1172 if( pTextNode->GetGrammarCheck() )
1173 pTextNode->GetGrammarCheck()->SetInvalid( 0, COMPLETE_STRING );
1176 return true;
1179 static bool lcl_CheckSmartTagsAgain( SwNode* pNd, void* )
1181 SwTextNode *pTextNode = pNd->GetTextNode();
1182 if( pTextNode )
1184 pTextNode->SetSmartTagDirty( true );
1185 pTextNode->ClearSmartTags();
1187 return true;
1191 * Re-trigger spelling in the idle handler.
1193 * @param bInvalid if <true>, the WrongLists in all nodes are invalidated
1194 * and the SpellInvalid flag is set on all pages.
1195 * @param bOnlyWrong controls whether only the areas with wrong words are
1196 * checked or the whole area.
1197 * @param bSmartTags ???
1199 void SwDoc::SpellItAgainSam( bool bInvalid, bool bOnlyWrong, bool bSmartTags )
1201 o3tl::sorted_vector<SwRootFrame*> aAllLayouts = GetAllLayouts();
1202 assert(getIDocumentLayoutAccess().GetCurrentLayout() && "SpellAgain: Where's my RootFrame?");
1203 if( bInvalid )
1205 for ( auto aLayout : aAllLayouts )
1207 aLayout->AllInvalidateSmartTagsOrSpelling(bSmartTags);
1208 aLayout->SetNeedGrammarCheck(true);
1210 if ( bSmartTags )
1211 GetNodes().ForEach( lcl_CheckSmartTagsAgain, &bOnlyWrong );
1212 GetNodes().ForEach( lcl_SpellAndGrammarAgain, &bOnlyWrong );
1215 for ( auto aLayout : aAllLayouts )
1216 aLayout->SetIdleFlags();
1219 void SwDoc::InvalidateAutoCompleteFlag()
1221 SwRootFrame* pTmpRoot = getIDocumentLayoutAccess().GetCurrentLayout();
1222 if( !pTmpRoot )
1223 return;
1225 o3tl::sorted_vector<SwRootFrame*> aAllLayouts = GetAllLayouts();
1226 for( auto aLayout : aAllLayouts )
1227 aLayout->AllInvalidateAutoCompleteWords();
1228 for( SwNodeOffset nNd(1), nCnt = GetNodes().Count(); nNd < nCnt; ++nNd )
1230 SwTextNode* pTextNode = GetNodes()[ nNd ]->GetTextNode();
1231 if ( pTextNode ) pTextNode->SetAutoCompleteWordDirty( true );
1234 for( auto aLayout : aAllLayouts )
1235 aLayout->SetIdleFlags();
1238 const SwFormatINetFormat* SwDoc::FindINetAttr( std::u16string_view rName ) const
1240 for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_INETFMT))
1242 auto pFormatItem = dynamic_cast<const SwFormatINetFormat*>(pItem);
1243 if( !pFormatItem || pFormatItem->GetName() != rName )
1244 continue;
1245 const SwTextINetFormat* pTextAttr = pFormatItem->GetTextINetFormat();
1246 if( !pTextAttr )
1247 continue;
1248 const SwTextNode* pTextNd = pTextAttr->GetpTextNode();
1249 if( pTextNd && &pTextNd->GetNodes() == &GetNodes() )
1251 return pFormatItem;
1254 return nullptr;
1257 void SwDoc::Summary(SwDoc& rExtDoc, sal_uInt8 nLevel, sal_uInt8 nPara, bool bImpress)
1259 const SwOutlineNodes& rOutNds = GetNodes().GetOutLineNds();
1260 if (rOutNds.empty())
1261 return;
1263 ::StartProgress( STR_STATSTR_SUMMARY, 0, rOutNds.size(), GetDocShell() );
1264 SwNodeIndex aEndOfDoc( rExtDoc.GetNodes().GetEndOfContent(), -1 );
1265 for( SwOutlineNodes::size_type i = 0; i < rOutNds.size(); ++i )
1267 ::SetProgressState( static_cast<tools::Long>(i), GetDocShell() );
1268 const SwNodeOffset nIndex = rOutNds[ i ]->GetIndex();
1270 const int nLvl = GetNodes()[ nIndex ]->GetTextNode()->GetAttrOutlineLevel()-1;
1271 if( nLvl > nLevel )
1272 continue;
1273 SwNodeOffset nEndOfs(1);
1274 sal_uInt8 nWish = nPara;
1275 SwNodeOffset nNextOutNd = i + 1 < rOutNds.size() ?
1276 rOutNds[ i + 1 ]->GetIndex() : GetNodes().Count();
1277 bool bKeep = false;
1278 while( ( nWish || bKeep ) && nIndex + nEndOfs < nNextOutNd &&
1279 GetNodes()[ nIndex + nEndOfs ]->IsTextNode() )
1281 SwTextNode* pTextNode = GetNodes()[ nIndex+nEndOfs ]->GetTextNode();
1282 if (pTextNode->GetText().getLength() && nWish)
1283 --nWish;
1284 bKeep = pTextNode->GetSwAttrSet().GetKeep().GetValue();
1285 ++nEndOfs;
1288 SwNodeRange aRange( *rOutNds[ i ], SwNodeOffset(0), *rOutNds[ i ], nEndOfs );
1289 GetNodes().Copy_( aRange, aEndOfDoc.GetNode() );
1291 const SwTextFormatColls *pColl = rExtDoc.GetTextFormatColls();
1292 for( SwTextFormatColls::size_type i = 0; i < pColl->size(); ++i )
1293 (*pColl)[ i ]->ResetFormatAttr( RES_PAGEDESC, RES_BREAK );
1294 SwNodeIndex aIndx( rExtDoc.GetNodes().GetEndOfExtras() );
1295 ++aEndOfDoc;
1296 while( aIndx < aEndOfDoc )
1298 bool bDelete = false;
1299 SwNode *pNode = &aIndx.GetNode();
1300 if( pNode->IsTextNode() )
1302 SwTextNode *pNd = pNode->GetTextNode();
1303 if( pNd->HasSwAttrSet() )
1304 pNd->ResetAttr( RES_PAGEDESC, RES_BREAK );
1305 if( bImpress )
1307 SwTextFormatColl* pMyColl = pNd->GetTextColl();
1309 const sal_uInt16 nHeadLine = o3tl::narrowing<sal_uInt16>(
1310 !pMyColl->IsAssignedToListLevelOfOutlineStyle()
1311 ? RES_POOLCOLL_HEADLINE2
1312 : RES_POOLCOLL_HEADLINE1 );
1313 pMyColl = rExtDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( nHeadLine );
1314 pNd->ChgFormatColl( pMyColl );
1316 if( !pNd->Len() &&
1317 pNd->StartOfSectionIndex()+SwNodeOffset(2) < pNd->EndOfSectionIndex() )
1319 bDelete = true;
1320 rExtDoc.GetNodes().Delete( aIndx );
1323 if( !bDelete )
1324 ++aIndx;
1326 ::EndProgress( GetDocShell() );
1329 namespace
1331 void RemoveOrDeleteContents(SwTextNode* pTextNd, IDocumentContentOperations& xOperations)
1333 SwPaM aPam(*pTextNd, 0, *pTextNd, pTextNd->GetText().getLength());
1335 // Remove hidden paragraph or delete contents:
1336 // Delete contents if
1337 // 1. removing the paragraph would result in an empty section or
1338 // 2. if the paragraph is the last paragraph in the section and
1339 // there is no paragraph in front of the paragraph:
1340 if ((SwNodeOffset(2) == pTextNd->EndOfSectionIndex() - pTextNd->StartOfSectionIndex())
1341 || (SwNodeOffset(1) == pTextNd->EndOfSectionIndex() - pTextNd->GetIndex()
1342 && !pTextNd->GetNodes()[pTextNd->GetIndex() - 1]->GetTextNode()))
1344 xOperations.DeleteRange(aPam);
1346 else
1348 aPam.DeleteMark();
1349 xOperations.DelFullPara(aPam);
1352 // Returns if the data was actually modified
1353 bool HandleHidingField(SwFormatField& rFormatField, const SwNodes& rNodes,
1354 IDocumentContentOperations& xOperations)
1356 if( !rFormatField.GetTextField() )
1357 return false;
1358 SwTextNode* pTextNd = rFormatField.GetTextField()->GetpTextNode();
1359 if( pTextNd
1360 && pTextNd->GetpSwpHints() && pTextNd->IsHiddenByParaField()
1361 && &pTextNd->GetNodes() == &rNodes)
1363 RemoveOrDeleteContents(pTextNd, xOperations);
1364 return true;
1366 return false;
1370 // The greater the returned value, the more weight has this field type on deciding the final
1371 // paragraph state
1372 int SwDoc::FieldCanHideParaWeight(SwFieldIds eFieldId) const
1374 switch (eFieldId)
1376 case SwFieldIds::HiddenPara:
1377 return 20;
1378 case SwFieldIds::Database:
1379 return GetDocumentSettingManager().get(DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA)
1380 ? 10
1381 : 0;
1382 default:
1383 return 0;
1387 bool SwDoc::FieldHidesPara(const SwField& rField) const
1389 switch (rField.GetTyp()->Which())
1391 case SwFieldIds::HiddenPara:
1392 return static_cast<const SwHiddenParaField&>(rField).IsHidden();
1393 case SwFieldIds::Database:
1394 return FieldCanHideParaWeight(SwFieldIds::Database)
1395 && rField.ExpandField(true, nullptr).isEmpty();
1396 default:
1397 return false;
1401 /// Remove the invisible content from the document e.g. hidden areas, hidden paragraphs
1402 // Returns if the data was actually modified
1403 bool SwDoc::RemoveInvisibleContent()
1405 bool bRet = false;
1406 GetIDocumentUndoRedo().StartUndo( SwUndoId::UI_DELETE_INVISIBLECNTNT, nullptr );
1409 class FieldTypeGuard : public SwClient
1411 public:
1412 explicit FieldTypeGuard(SwFieldType* pType)
1413 : SwClient(pType)
1416 const SwFieldType* get() const
1418 return static_cast<const SwFieldType*>(GetRegisteredIn());
1421 // Removing some nodes for one SwFieldIds::Database type might remove the type from
1422 // document's field types, invalidating iterators. So, we need to create own list of
1423 // matching types prior to processing them.
1424 std::vector<std::unique_ptr<FieldTypeGuard>> aHidingFieldTypes;
1425 for (std::unique_ptr<SwFieldType> const & pType : *getIDocumentFieldsAccess().GetFieldTypes())
1427 if (FieldCanHideParaWeight(pType->Which()))
1428 aHidingFieldTypes.push_back(std::make_unique<FieldTypeGuard>(pType.get()));
1430 for (const auto& pTypeGuard : aHidingFieldTypes)
1432 if (const SwFieldType* pType = pTypeGuard->get())
1434 std::vector<SwFormatField*> vFields;
1435 pType->GatherFields(vFields);
1436 for(auto pFormatField: vFields)
1437 bRet |= HandleHidingField(*pFormatField, GetNodes(), getIDocumentContentOperations());
1442 // Remove any hidden paragraph (hidden text attribute)
1443 for( SwNodeOffset n = GetNodes().Count(); n; )
1445 SwTextNode* pTextNd = GetNodes()[ --n ]->GetTextNode();
1446 if ( pTextNd )
1448 bool bRemoved = false;
1449 if ( pTextNd->HasHiddenCharAttribute( true ) )
1451 bRemoved = true;
1452 bRet = true;
1454 if (SwNodeOffset(2) == pTextNd->EndOfSectionIndex() - pTextNd->StartOfSectionIndex())
1456 SwFrameFormat *const pFormat = pTextNd->StartOfSectionNode()->GetFlyFormat();
1457 if (nullptr != pFormat)
1459 // remove hidden text frame
1460 getIDocumentLayoutAccess().DelLayoutFormat(pFormat);
1462 else
1464 // default, remove hidden paragraph
1465 RemoveOrDeleteContents(pTextNd, getIDocumentContentOperations());
1468 else
1470 // default, remove hidden paragraph
1471 RemoveOrDeleteContents(pTextNd, getIDocumentContentOperations());
1474 else if ( pTextNd->HasHiddenCharAttribute( false ) )
1476 bRemoved = true;
1477 bRet = true;
1478 SwScriptInfo::DeleteHiddenRanges( *pTextNd );
1481 // Footnotes/Frames may have been removed, therefore we have
1482 // to reset n:
1483 if ( bRemoved )
1485 // [n] has to be inside [0 .. GetNodes().Count()] range
1486 if (n > GetNodes().Count())
1487 n = GetNodes().Count();
1493 // Delete/empty all hidden areas
1494 o3tl::sorted_vector<SwSectionFormat*> aSectFormats;
1495 SwSectionFormats& rSectFormats = GetSections();
1497 for( SwSectionFormats::size_type n = rSectFormats.size(); n; )
1499 SwSectionFormat* pSectFormat = rSectFormats[ --n ];
1500 // don't add sections in Undo/Redo
1501 if( !pSectFormat->IsInNodesArr())
1502 continue;
1503 SwSection* pSect = pSectFormat->GetSection();
1504 if( pSect->CalcHiddenFlag() )
1506 SwSection* pParent = pSect, *pTmp;
1507 while( nullptr != (pTmp = pParent->GetParent() ))
1509 if( pTmp->IsHiddenFlag() )
1510 pSect = pTmp;
1511 pParent = pTmp;
1514 aSectFormats.insert( pSect->GetFormat() );
1516 if( !pSect->GetCondition().isEmpty() )
1518 SwSectionData aSectionData( *pSect );
1519 aSectionData.SetCondition( OUString() );
1520 aSectionData.SetHidden( false );
1521 UpdateSection( n, aSectionData );
1525 auto n = aSectFormats.size();
1527 if( 0 != n )
1529 while( n )
1531 SwSectionFormat* pSectFormat = aSectFormats[ --n ];
1532 SwSectionNode* pSectNd = pSectFormat->GetSectionNode();
1533 if( pSectNd )
1535 bRet = true;
1536 SwPaM aPam( *pSectNd );
1538 if( pSectNd->StartOfSectionNode()->StartOfSectionIndex() ==
1539 pSectNd->GetIndex() - 1 &&
1540 pSectNd->StartOfSectionNode()->EndOfSectionIndex() ==
1541 pSectNd->EndOfSectionIndex() + 1 )
1543 // only delete the content
1544 SwContentNode* pCNd = GetNodes().GoNext( aPam.GetPoint() );
1545 aPam.SetMark();
1546 aPam.GetPoint()->Assign( *pSectNd->EndOfSectionNode() );
1547 pCNd = SwNodes::GoPrevious( aPam.GetPoint() );
1548 assert(pCNd); // keep coverity happy
1549 aPam.GetPoint()->SetContent( pCNd->Len() );
1551 getIDocumentContentOperations().DeleteRange( aPam );
1553 else
1555 // delete the whole section
1556 aPam.SetMark();
1557 aPam.GetPoint()->Assign( *pSectNd->EndOfSectionNode() );
1558 getIDocumentContentOperations().DelFullPara( aPam );
1566 if( bRet )
1567 getIDocumentState().SetModified();
1568 GetIDocumentUndoRedo().EndUndo( SwUndoId::UI_DELETE_INVISIBLECNTNT, nullptr );
1569 return bRet;
1572 bool SwDoc::HasInvisibleContent() const
1574 std::vector<SwFormatField*> vFields;
1575 getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::HiddenPara)->GatherFields(vFields);
1576 if(vFields.size())
1577 return true;
1579 // Search for any hidden paragraph (hidden text attribute)
1580 for( SwNodeOffset n = GetNodes().Count()-SwNodeOffset(1); n; --n)
1582 SwTextNode* pTextNd = GetNodes()[ n ]->GetTextNode();
1583 if ( pTextNd &&
1584 ( pTextNd->HasHiddenCharAttribute( true ) || pTextNd->HasHiddenCharAttribute( false ) ) )
1585 return true;
1588 for(auto pSectFormat : GetSections())
1590 // don't add sections in Undo/Redo
1591 if( !pSectFormat->IsInNodesArr())
1592 continue;
1593 SwSection* pSect = pSectFormat->GetSection();
1594 if( pSect->IsHidden() )
1595 return true;
1597 return false;
1600 bool SwDoc::RestoreInvisibleContent()
1602 SwUndoId nLastUndoId(SwUndoId::EMPTY);
1603 if (GetIDocumentUndoRedo().GetLastUndoInfo(nullptr, & nLastUndoId)
1604 && (SwUndoId::UI_DELETE_INVISIBLECNTNT == nLastUndoId))
1606 GetIDocumentUndoRedo().Undo();
1607 GetIDocumentUndoRedo().ClearRedo();
1608 return true;
1610 return false;
1613 static bool IsMailMergeField(SwFieldIds fieldId)
1615 switch (fieldId)
1617 case SwFieldIds::Database: // Mail merge fields
1618 case SwFieldIds::DatabaseName: // Database name
1619 case SwFieldIds::HiddenText: // Hidden text may use database fields in condition
1620 case SwFieldIds::HiddenPara: // Hidden paragraph may use database fields in condition
1621 case SwFieldIds::DbNextSet: // Moving to next mail merge record
1622 case SwFieldIds::DbNumSet: // Moving to a specific mail merge record
1623 case SwFieldIds::DbSetNumber: // Number of current mail merge record
1624 return true;
1625 default:
1626 return false;
1630 bool SwDoc::ConvertFieldsToText(SwRootFrame const& rLayout)
1632 bool bRet = false;
1633 getIDocumentFieldsAccess().LockExpFields();
1634 GetIDocumentUndoRedo().StartUndo( SwUndoId::UI_REPLACE, nullptr );
1636 const bool bOnlyConvertDBFields
1637 = officecfg::Office::Writer::FormLetter::ConvertToTextOnlyMMFields::get();
1639 const SwFieldTypes* pMyFieldTypes = getIDocumentFieldsAccess().GetFieldTypes();
1640 const SwFieldTypes::size_type nCount = pMyFieldTypes->size();
1641 //go backward, field types are removed
1642 for(SwFieldTypes::size_type nType = nCount; nType > 0; --nType)
1644 const SwFieldType *pCurType = (*pMyFieldTypes)[nType - 1].get();
1646 if ( SwFieldIds::Postit == pCurType->Which() )
1647 continue;
1649 if (bOnlyConvertDBFields && !IsMailMergeField(pCurType->Which()))
1650 continue;
1652 std::vector<SwFormatField*> vFieldFormats;
1653 pCurType->GatherFields(vFieldFormats, false);
1654 for(const auto& rpFieldFormat : vFieldFormats)
1656 const SwTextField *pTextField = rpFieldFormat->GetTextField();
1657 // skip fields that are currently not in the document
1658 // e.g. fields in undo or redo array
1660 bool bSkip = !pTextField ||
1661 !pTextField->GetpTextNode()->GetNodes().IsDocNodes();
1662 if (bSkip)
1663 continue;
1665 bool bInHeaderFooter = IsInHeaderFooter(*pTextField->GetpTextNode());
1666 const SwFormatField& rFormatField = pTextField->GetFormatField();
1667 const SwField* pField = rFormatField.GetField();
1669 //#i55595# some fields have to be excluded in headers/footers
1670 SwFieldIds nWhich = pField->GetTyp()->Which();
1671 if(!bInHeaderFooter ||
1672 (nWhich != SwFieldIds::PageNumber &&
1673 nWhich != SwFieldIds::Chapter &&
1674 nWhich != SwFieldIds::GetExp&&
1675 nWhich != SwFieldIds::SetExp&&
1676 nWhich != SwFieldIds::Input&&
1677 nWhich != SwFieldIds::RefPageGet&&
1678 nWhich != SwFieldIds::RefPageSet))
1680 OUString sText = pField->ExpandField(true, &rLayout);
1682 // database fields should not convert their command into text
1683 if( SwFieldIds::Database == pCurType->Which() && !static_cast<const SwDBField*>(pField)->IsInitialized())
1684 sText.clear();
1686 SwPaM aInsertPam(*pTextField->GetpTextNode(), pTextField->GetStart());
1687 aInsertPam.SetMark();
1689 // go to the end of the field
1690 const SwTextField *pFieldAtEnd = sw::DocumentFieldsManager::GetTextFieldAtPos(*aInsertPam.End());
1691 if (pFieldAtEnd && pFieldAtEnd->Which() == RES_TXTATR_INPUTFIELD)
1693 SwPosition &rEndPos = *aInsertPam.GetPoint();
1694 rEndPos.SetContent( SwCursorShell::EndOfInputFieldAtPos( *aInsertPam.End() ) );
1696 else
1698 aInsertPam.Move();
1701 // first insert the text after field to keep the field's attributes,
1702 // then delete the field
1703 if (!sText.isEmpty())
1705 // to keep the position after insert
1706 SwPaM aDelPam( *aInsertPam.GetMark(), *aInsertPam.GetPoint() );
1707 aDelPam.Move( fnMoveBackward );
1708 aInsertPam.DeleteMark();
1710 getIDocumentContentOperations().InsertString( aInsertPam, sText );
1712 aDelPam.Move();
1713 // finally remove the field
1714 getIDocumentContentOperations().DeleteAndJoin( aDelPam );
1716 else
1718 getIDocumentContentOperations().DeleteAndJoin( aInsertPam );
1721 bRet = true;
1726 if( bRet )
1727 getIDocumentState().SetModified();
1728 GetIDocumentUndoRedo().EndUndo( SwUndoId::UI_REPLACE, nullptr );
1729 getIDocumentFieldsAccess().UnlockExpFields();
1730 return bRet;
1734 bool SwDoc::IsInsTableFormatNum() const
1736 return SW_MOD()->IsInsTableFormatNum(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE));
1739 bool SwDoc::IsInsTableChangeNumFormat() const
1741 return SW_MOD()->IsInsTableChangeNumFormat(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE));
1744 bool SwDoc::IsInsTableAlignNum() const
1746 return SW_MOD()->IsInsTableAlignNum(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE));
1749 bool SwDoc::IsSplitVerticalByDefault() const
1751 return SW_MOD()->IsSplitVerticalByDefault(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE));
1754 void SwDoc::SetSplitVerticalByDefault(bool value)
1756 SW_MOD()->SetSplitVerticalByDefault(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE), value);
1759 /// Set up the InsertDB as Undo table
1760 void SwDoc::AppendUndoForInsertFromDB( const SwPaM& rPam, bool bIsTable )
1762 if( bIsTable )
1764 const SwTableNode* pTableNd = rPam.GetPoint()->GetNode().FindTableNode();
1765 if( pTableNd )
1767 std::unique_ptr<SwUndoCpyTable> pUndo(new SwUndoCpyTable(*this));
1768 pUndo->SetTableSttIdx( pTableNd->GetIndex() );
1769 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
1772 else if( rPam.HasMark() )
1774 std::unique_ptr<SwUndoCpyDoc> pUndo(new SwUndoCpyDoc( rPam ));
1775 pUndo->SetInsertRange( rPam, false );
1776 GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
1780 void SwDoc::ChangeTOX(SwTOXBase & rTOX, const SwTOXBase & rNew)
1782 assert(dynamic_cast<const SwTOXBaseSection*>(&rTOX));
1783 SwTOXBaseSection& rTOXSect(static_cast<SwTOXBaseSection&>(rTOX));
1785 if (GetIDocumentUndoRedo().DoesUndo())
1787 GetIDocumentUndoRedo().AppendUndo(
1788 std::make_unique<SwUndoTOXChange>(*this, rTOXSect, rNew));
1791 rTOX = rNew;
1793 // note: do not Update the ToX here - the caller will do it, with a ViewShell!
1796 OUString SwDoc::GetPaMDescr(const SwPaM & rPam)
1798 if (&rPam.GetPointNode() == &rPam.GetMarkNode())
1800 SwTextNode * pTextNode = rPam.GetPointNode().GetTextNode();
1802 if (nullptr != pTextNode)
1804 const sal_Int32 nStart = rPam.Start()->GetContentIndex();
1805 const sal_Int32 nEnd = rPam.End()->GetContentIndex();
1807 return SwResId(STR_START_QUOTE)
1808 + ShortenString(pTextNode->GetText().copy(nStart, nEnd - nStart),
1809 nUndoStringLength,
1810 SwResId(STR_LDOTS))
1811 + SwResId(STR_END_QUOTE);
1814 else
1816 return SwResId(STR_PARAGRAPHS);
1819 return "??";
1822 bool SwDoc::ContainsHiddenChars() const
1824 for( SwNodeOffset n = GetNodes().Count(); n; )
1826 SwNode* pNd = GetNodes()[ --n ];
1827 if ( pNd->IsTextNode() && pNd->GetTextNode()->HasHiddenCharAttribute( false ) )
1828 return true;
1831 return false;
1834 std::shared_ptr<SwUnoCursor> SwDoc::CreateUnoCursor( const SwPosition& rPos, bool bTableCursor )
1836 std::shared_ptr<SwUnoCursor> pNew;
1837 if( bTableCursor )
1838 pNew = std::make_shared<SwUnoTableCursor>(rPos);
1839 else
1840 pNew = std::make_shared<SwUnoCursor>(rPos);
1842 mvUnoCursorTable.push_back( pNew );
1843 return pNew;
1846 void SwDoc::ChkCondColls()
1848 for (SwTextFormatColls::size_type n = 0; n < mpTextFormatCollTable->size(); ++n)
1850 SwTextFormatColl *pColl = (*mpTextFormatCollTable)[n];
1851 if (RES_CONDTXTFMTCOLL == pColl->Which())
1852 pColl->CallSwClientNotify( SwAttrHint() );
1856 uno::Reference< script::vba::XVBAEventProcessor > const &
1857 SwDoc::GetVbaEventProcessor()
1859 return mxVbaEvents;
1862 void SwDoc::SetVbaEventProcessor()
1864 #if HAVE_FEATURE_SCRIPTING
1865 if (mpDocShell && ooo::vba::isAlienWordDoc(*mpDocShell))
1869 uno::Reference< frame::XModel > xModel( mpDocShell->GetModel(), uno::UNO_SET_THROW );
1870 uno::Sequence< uno::Any > aArgs{ uno::Any(xModel) };
1871 mxVbaEvents.set( ooo::vba::createVBAUnoAPIServiceWithArgs( mpDocShell, "com.sun.star.script.vba.VBATextEventProcessor" , aArgs ), uno::UNO_QUERY_THROW );
1873 catch( uno::Exception& )
1877 #endif
1880 void SwDoc::SetMissingDictionaries( bool bIsMissing )
1882 if (!bIsMissing)
1883 meDictionaryMissing = MissingDictionary::False;
1884 else if (meDictionaryMissing == MissingDictionary::Undefined)
1885 meDictionaryMissing = MissingDictionary::True;
1888 void SwDoc::SetLanguage(const LanguageType eLang, const sal_uInt16 nId)
1890 mpAttrPool->SetPoolDefaultItem(SvxLanguageItem(eLang, nId));
1893 bool SwDoc::HasParagraphDirectFormatting(const SwPosition& rPos)
1895 uno::Reference<text::XTextRange> xRange(SwXTextRange::CreateXTextRange(rPos.GetDoc(), rPos,
1896 &rPos));
1897 uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xRange, uno::UNO_QUERY_THROW);
1898 uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
1899 uno::Reference<text::XTextRange> xThisParagraphRange(xParaEnum->nextElement(), uno::UNO_QUERY);
1900 if (xThisParagraphRange.is())
1902 const std::vector<OUString> aHiddenProperties{ UNO_NAME_RSID,
1903 UNO_NAME_PARA_IS_NUMBERING_RESTART,
1904 UNO_NAME_PARA_STYLE_NAME,
1905 UNO_NAME_PARA_CONDITIONAL_STYLE_NAME,
1906 UNO_NAME_PAGE_STYLE_NAME,
1907 UNO_NAME_NUMBERING_START_VALUE,
1908 UNO_NAME_NUMBERING_IS_NUMBER,
1909 UNO_NAME_PARA_CONTINUEING_PREVIOUS_SUB_TREE,
1910 UNO_NAME_CHAR_STYLE_NAME,
1911 UNO_NAME_NUMBERING_LEVEL,
1912 UNO_NAME_SORTED_TEXT_ID,
1913 UNO_NAME_PARRSID,
1914 UNO_NAME_CHAR_COLOR_THEME,
1915 UNO_NAME_CHAR_COLOR_TINT_OR_SHADE };
1917 SfxItemPropertySet const& rPropSet(*aSwMapProvider.GetPropertySet(
1918 PROPERTY_MAP_PARA_AUTO_STYLE));
1919 SfxItemPropertyMap const& rMap(rPropSet.getPropertyMap());
1921 uno::Reference<beans::XPropertySet> xPropertySet(xThisParagraphRange,
1922 uno::UNO_QUERY_THROW);
1923 uno::Reference<beans::XPropertyState> xPropertyState(xThisParagraphRange,
1924 uno::UNO_QUERY_THROW);
1925 const uno::Sequence<beans::Property> aProperties
1926 = xPropertySet->getPropertySetInfo()->getProperties();
1927 for (const beans::Property& rProperty : aProperties)
1929 const OUString& rPropName = rProperty.Name;
1930 if (!rMap.hasPropertyByName(rPropName))
1931 continue;
1932 if (std::find(aHiddenProperties.begin(), aHiddenProperties.end(), rPropName)
1933 != aHiddenProperties.end())
1934 continue;
1935 if (xPropertyState->getPropertyState(rPropName) == beans::PropertyState_DIRECT_VALUE)
1936 return true;
1939 return false;
1942 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */