tdf#35361 Add a Quick Look plugins for .od* files on macOS
[LibreOffice.git] / sw / source / core / txtnode / atrftn.cxx
blob761564f0860ba3f235a4d76deaf7873d496d3667
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 <libxml/xmlwriter.h>
21 #include <fmtftn.hxx>
22 #include <doc.hxx>
23 #include <DocumentContentOperationsManager.hxx>
24 #include <IDocumentStylePoolAccess.hxx>
25 #include <cntfrm.hxx>
26 #include <rootfrm.hxx>
27 #include <pagefrm.hxx>
28 #include <txtftn.hxx>
29 #include <ftnidx.hxx>
30 #include <ftninfo.hxx>
31 #include <ndtxt.hxx>
32 #include <poolfmt.hxx>
33 #include <ftnfrm.hxx>
34 #include <ndindex.hxx>
35 #include <fmtftntx.hxx>
36 #include <section.hxx>
37 #include <calbck.hxx>
38 #include <hints.hxx>
39 #include <pam.hxx>
40 #include <rtl/ustrbuf.hxx>
41 #include <vcl/svapp.hxx>
42 #include <unotextrange.hxx>
43 #include <osl/diagnose.h>
44 #include <unofootnote.hxx>
46 namespace {
47 /// Get a sorted list of the used footnote reference numbers.
48 /// @param[in] rDoc The active document.
49 /// @param[in] pExclude A footnote whose reference number should be excluded from the set.
50 /// @param[out] rUsedRef The set of used reference numbers.
51 /// @param[out] rInvalid A returned list of all items that had an invalid reference number.
52 void lcl_FillUsedFootnoteRefNumbers(SwDoc &rDoc,
53 SwTextFootnote const *pExclude,
54 std::set<sal_uInt16> &rUsedRef,
55 std::vector<SwTextFootnote*> &rInvalid)
57 SwFootnoteIdxs& ftnIdxs = rDoc.GetFootnoteIdxs();
59 rInvalid.clear();
61 for( size_t n = 0; n < ftnIdxs.size(); ++n )
63 SwTextFootnote* pTextFootnote = ftnIdxs[ n ];
64 if ( pTextFootnote != pExclude )
66 if ( USHRT_MAX == pTextFootnote->GetSeqRefNo() )
68 rInvalid.push_back(pTextFootnote);
70 else
72 rUsedRef.insert( pTextFootnote->GetSeqRefNo() );
78 /// Check whether a requested reference number is available.
79 /// @param[in] rUsedNums Set of used reference numbers.
80 /// @param[in] requested The requested reference number.
81 /// @returns true if the number is available, false if not.
82 bool lcl_IsRefNumAvailable(std::set<sal_uInt16> const &rUsedNums,
83 sal_uInt16 requested)
85 if ( USHRT_MAX == requested )
86 return false; // Invalid sequence number.
87 if ( rUsedNums.count(requested) )
88 return false; // Number already used.
89 return true;
92 /// Get the first few unused sequential reference numbers.
93 /// @param[out] rLowestUnusedNums The lowest unused sequential reference numbers.
94 /// @param[in] rUsedNums The set of used sequential reference numbers.
95 /// @param[in] numRequired The number of reference number required.
96 void lcl_FillUnusedSeqRefNums(std::vector<sal_uInt16> &rLowestUnusedNums,
97 const std::set<sal_uInt16> &rUsedNums,
98 size_t numRequired)
100 if (!numRequired)
101 return;
103 rLowestUnusedNums.reserve(numRequired);
104 sal_uInt16 newNum = 0;
105 //Start by using numbers from gaps in rUsedNums
106 for( const auto& rNum : rUsedNums )
108 while ( newNum < rNum )
110 rLowestUnusedNums.push_back( newNum++ );
111 if ( --numRequired == 0)
112 return;
114 newNum++;
116 //Filled in all gaps. Fill the rest of the list with new numbers.
119 rLowestUnusedNums.push_back( newNum++ );
121 while ( --numRequired > 0 );
126 SwFormatFootnote::SwFormatFootnote( bool bEndNote )
127 : SfxPoolItem( RES_TXTATR_FTN )
128 , m_pTextAttr(nullptr)
129 , m_nNumber(0)
130 , m_nNumberRLHidden(0)
131 , m_bEndNote(bEndNote)
133 setNonShareable();
136 void SwFormatFootnote::SetXFootnote(rtl::Reference<SwXFootnote> const& xNote)
137 { m_wXFootnote = xNote.get(); }
139 bool SwFormatFootnote::operator==( const SfxPoolItem& rAttr ) const
141 assert(SfxPoolItem::operator==(rAttr));
142 return m_nNumber == static_cast<const SwFormatFootnote&>(rAttr).m_nNumber &&
143 //FIXME?
144 m_aNumber == static_cast<const SwFormatFootnote&>(rAttr).m_aNumber &&
145 m_bEndNote == static_cast<const SwFormatFootnote&>(rAttr).m_bEndNote;
148 SwFormatFootnote* SwFormatFootnote::Clone( SfxItemPool* ) const
150 SwFormatFootnote* pNew = new SwFormatFootnote;
151 pNew->m_aNumber = m_aNumber;
152 pNew->m_nNumber = m_nNumber;
153 pNew->m_nNumberRLHidden = m_nNumberRLHidden;
154 pNew->m_bEndNote = m_bEndNote;
155 return pNew;
158 void SwFormatFootnote::InvalidateFootnote()
160 if (auto xUnoFootnote = m_wXFootnote.get())
162 xUnoFootnote->OnFormatFootnoteDeleted();
163 m_wXFootnote.clear();
167 void SwFormatFootnote::SetEndNote( bool b )
169 if ( b != m_bEndNote )
171 if ( GetTextFootnote() )
173 GetTextFootnote()->DelFrames(nullptr);
175 m_bEndNote = b;
179 SwFormatFootnote::~SwFormatFootnote()
183 OUString SwFormatFootnote::GetFootnoteText(SwRootFrame const& rLayout) const
185 OUStringBuffer buf;
186 if( m_pTextAttr->GetStartNode() )
188 SwNodeIndex aIdx( *m_pTextAttr->GetStartNode(), 1 );
189 SwContentNode* pCNd = aIdx.GetNode().GetTextNode();
190 if( !pCNd )
191 pCNd = SwNodes::GoNext(&aIdx);
193 if( pCNd->IsTextNode() ) {
194 buf.append(static_cast<SwTextNode*>(pCNd)->GetExpandText(&rLayout));
196 ++aIdx;
197 while ( !aIdx.GetNode().IsEndNode() ) {
198 if ( aIdx.GetNode().IsTextNode() )
200 buf.append(" " + aIdx.GetNode().GetTextNode()->GetExpandText(&rLayout));
202 ++aIdx;
206 return buf.makeStringAndClear();
209 /// return the view string of the foot/endnote
210 OUString SwFormatFootnote::GetViewNumStr(const SwDoc& rDoc,
211 SwRootFrame const*const pLayout, bool bInclStrings) const
213 OUString sRet( GetNumStr() );
214 if( sRet.isEmpty() )
216 // in this case the number is needed, get it via SwDoc's FootnoteInfo
217 bool bMakeNum = true;
218 const SwSectionNode* pSectNd = m_pTextAttr
219 ? SwUpdFootnoteEndNtAtEnd::FindSectNdWithEndAttr( *m_pTextAttr )
220 : nullptr;
221 sal_uInt16 const nNumber(pLayout && pLayout->IsHideRedlines()
222 ? GetNumberRLHidden()
223 : GetNumber());
225 if( pSectNd )
227 const SwFormatFootnoteEndAtTextEnd& rFootnoteEnd = static_cast<const SwFormatFootnoteEndAtTextEnd&>(
228 pSectNd->GetSection().GetFormat()->GetFormatAttr(
229 IsEndNote() ?
230 o3tl::narrowing<sal_uInt16>(RES_END_AT_TXTEND) :
231 o3tl::narrowing<sal_uInt16>(RES_FTN_AT_TXTEND) ) );
233 if( FTNEND_ATTXTEND_OWNNUMANDFMT == rFootnoteEnd.GetValue() )
235 bMakeNum = false;
236 sRet = rFootnoteEnd.GetSwNumType().GetNumStr( nNumber );
237 if( bInclStrings )
239 sRet = rFootnoteEnd.GetPrefix() + sRet + rFootnoteEnd.GetSuffix();
244 if( bMakeNum )
246 const SwEndNoteInfo* pInfo;
247 if( IsEndNote() )
248 pInfo = &rDoc.GetEndNoteInfo();
249 else
250 pInfo = &rDoc.GetFootnoteInfo();
251 sRet = pInfo->m_aFormat.GetNumStr( nNumber );
252 if( bInclStrings )
254 sRet = pInfo->GetPrefix() + sRet + pInfo->GetSuffix();
258 return sRet;
261 rtl::Reference<SwXTextRange> SwFormatFootnote::getAnchor(SwDoc& rDoc) const
263 SolarMutexGuard aGuard;
264 if (!m_pTextAttr)
265 return {};
266 SwPaM aPam(m_pTextAttr->GetTextNode(), m_pTextAttr->GetStart());
267 aPam.SetMark();
268 aPam.GetMark()->AdjustContent(+1);
269 rtl::Reference<SwXTextRange> xRet =
270 SwXTextRange::CreateXTextRange(rDoc, *aPam.Start(), aPam.End());
271 return xRet;
274 void SwFormatFootnote::dumpAsXml(xmlTextWriterPtr pWriter) const
276 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatFootnote"));
277 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
278 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("text-attr"), "%p", m_pTextAttr);
279 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("endnote"),
280 BAD_CAST(OString::boolean(m_bEndNote).getStr()));
282 SfxPoolItem::dumpAsXml(pWriter);
284 (void)xmlTextWriterEndElement(pWriter);
287 SwTextFootnote::SwTextFootnote(
288 const SfxPoolItemHolder& rAttr,
289 sal_Int32 nStartPos )
290 : SwTextAttr( rAttr, nStartPos )
291 , m_pTextNode( nullptr )
292 , m_nSeqNo( USHRT_MAX )
294 SwFormatFootnote& rSwFormatFootnote(static_cast<SwFormatFootnote&>(GetAttr()));
295 rSwFormatFootnote.m_pTextAttr = this;
296 SetHasDummyChar(true);
299 SwTextFootnote::~SwTextFootnote()
301 SetStartNode( nullptr );
304 void SwTextFootnote::SetStartNode( const SwNodeIndex *pNewNode, bool bDelNode )
306 if( pNewNode )
308 m_oStartNode = *pNewNode;
310 else if ( m_oStartNode )
312 // need to do 2 things:
313 // 1) unregister footnotes at their pages
314 // 2) delete the footnote section in the Inserts of the nodes-array
315 SwDoc* pDoc;
316 if ( m_pTextNode )
318 pDoc = &m_pTextNode->GetDoc();
320 else
322 //JP 27.01.97: the sw3-Reader creates a StartNode but the
323 // attribute isn't anchored in the TextNode yet.
324 // If it is deleted (e.g. Insert File with footnote
325 // inside fly frame), the content must also be deleted.
326 pDoc = &m_oStartNode->GetNodes().GetDoc();
329 // If called from ~SwDoc(), must not delete the footnote nodes,
330 // and not necessary to delete the footnote frames.
331 if( !pDoc->IsInDtor() )
333 if( bDelNode )
335 // 2) delete the section for the footnote nodes
336 // it's possible that the Inserts have already been deleted (how???)
337 pDoc->getIDocumentContentOperations().DeleteSection( &m_oStartNode->GetNode() );
339 else
340 // If the nodes are not deleted, their frames must be removed
341 // from the page (deleted), there is nothing else that deletes
342 // them (particularly not Undo)
343 DelFrames( nullptr );
345 m_oStartNode.reset();
347 // remove the footnote from the SwDoc's array
348 for( size_t n = 0; n < pDoc->GetFootnoteIdxs().size(); ++n )
349 if( this == pDoc->GetFootnoteIdxs()[n] )
351 pDoc->GetFootnoteIdxs().erase( pDoc->GetFootnoteIdxs().begin() + n );
352 // if necessary, update following footnotes
353 if( !pDoc->IsInDtor() && n < pDoc->GetFootnoteIdxs().size() )
355 pDoc->GetFootnoteIdxs().UpdateFootnote( pDoc->GetFootnoteIdxs()[n]->GetTextNode() );
357 break;
362 void SwTextFootnote::SetNumber(const sal_uInt16 nNewNum,
363 sal_uInt16 const nNumberRLHidden, const OUString &sNumStr)
365 SwFormatFootnote& rFootnote = const_cast<SwFormatFootnote&>(GetFootnote());
367 rFootnote.m_aNumber = sNumStr;
368 if ( sNumStr.isEmpty() )
370 rFootnote.m_nNumber = nNewNum;
371 rFootnote.m_nNumberRLHidden = nNumberRLHidden;
373 InvalidateNumberInLayout();
376 void SwTextFootnote::InvalidateNumberInLayout()
378 assert(m_pTextNode);
379 SwNodes &rNodes = m_pTextNode->GetDoc().GetNodes();
380 const sw::LegacyModifyHint aHint(nullptr, &GetFootnote());
381 m_pTextNode->TriggerNodeUpdate(aHint);
382 if ( m_oStartNode )
384 // must iterate over all TextNodes because of footnotes on other pages
385 SwNodeOffset nSttIdx = m_oStartNode->GetIndex() + 1;
386 SwNodeOffset nEndIdx = m_oStartNode->GetNode().EndOfSectionIndex();
387 for( ; nSttIdx < nEndIdx; ++nSttIdx )
389 SwNode* pNd;
390 if( ( pNd = rNodes[ nSttIdx ] )->IsTextNode() )
391 static_cast<SwTextNode*>(pNd)->TriggerNodeUpdate(aHint);
396 void SwTextFootnote::CopyFootnote(
397 SwTextFootnote & rDest,
398 SwTextNode & rDestNode ) const
400 if (m_oStartNode && !rDest.GetStartNode())
402 // dest missing node section? create it here!
403 // (happens in SwTextNode::CopyText if pDest == this)
404 rDest.MakeNewTextSection( rDestNode.GetNodes() );
406 if (m_oStartNode && rDest.GetStartNode())
408 // footnotes not necessarily in same document!
409 SwDoc& rDstDoc = rDestNode.GetDoc();
410 SwNodes &rDstNodes = rDstDoc.GetNodes();
412 // copy only the content of the section
413 SwNodeRange aRg( m_oStartNode->GetNode(), SwNodeOffset(1),
414 *m_oStartNode->GetNode().EndOfSectionNode() );
416 // insert at the end of rDest, i.e., the nodes are appended.
417 // nDestLen contains number of ContentNodes in rDest _before_ copy.
418 SwNodeIndex aStart( *(rDest.GetStartNode()) );
419 SwNodeIndex aEnd( *aStart.GetNode().EndOfSectionNode() );
420 SwNodeOffset nDestLen = aEnd.GetIndex() - aStart.GetIndex() - 1;
422 m_pTextNode->GetDoc().GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, aEnd.GetNode());
424 // in case the destination section was not empty, delete the old nodes
425 // before: Src: SxxxE, Dst: SnE
426 // now: Src: SxxxE, Dst: SnxxxE
427 // after: Src: SxxxE, Dst: SxxxE
428 ++aStart;
429 rDstNodes.Delete( aStart, nDestLen );
432 // also copy user defined number string
433 if( !GetFootnote().m_aNumber.isEmpty() )
435 const_cast<SwFormatFootnote &>(rDest.GetFootnote()).m_aNumber = GetFootnote().m_aNumber;
439 /// create a new nodes-array section for the footnote
440 void SwTextFootnote::MakeNewTextSection( SwNodes& rNodes )
442 if ( m_oStartNode )
443 return;
445 // set the footnote style on the SwTextNode
446 SwTextFormatColl *pFormatColl;
447 const SwEndNoteInfo* pInfo;
448 sal_uInt16 nPoolId;
450 if( GetFootnote().IsEndNote() )
452 pInfo = &rNodes.GetDoc().GetEndNoteInfo();
453 nPoolId = RES_POOLCOLL_ENDNOTE;
455 else
457 pInfo = &rNodes.GetDoc().GetFootnoteInfo();
458 nPoolId = RES_POOLCOLL_FOOTNOTE;
461 pFormatColl = pInfo->GetFootnoteTextColl();
462 if( nullptr == pFormatColl )
463 pFormatColl = rNodes.GetDoc().getIDocumentStylePoolAccess().GetTextCollFromPool( nPoolId );
465 SwStartNode* pSttNd = rNodes.MakeTextSection( rNodes.GetEndOfInserts(),
466 SwFootnoteStartNode, pFormatColl );
467 m_oStartNode = *pSttNd;
470 void SwTextFootnote::DelFrames(SwRootFrame const*const pRoot)
472 // delete the FootnoteFrames from the pages
473 OSL_ENSURE( m_pTextNode, "SwTextFootnote: where is my TextNode?" );
474 if ( !m_pTextNode )
475 return;
477 bool bFrameFnd = false;
479 SwIterator<SwContentFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*m_pTextNode);
480 for( SwContentFrame* pFnd = aIter.First(); pFnd; pFnd = aIter.Next() )
482 if( pRoot != pFnd->getRootFrame() && pRoot )
483 continue;
484 SwPageFrame* pPage = pFnd->FindPageFrame();
485 if( pPage )
487 // note: we have found the correct frame only if the footnote
488 // was actually removed; in case this is called from
489 // SwTextFrame::DestroyImpl(), then that frame isn't connected
490 // to SwPageFrame any more, and RemoveFootnote on any follow
491 // must not prevent the fall-back to the !bFrameFnd code.
492 bFrameFnd = pPage->RemoveFootnote(pFnd, this);
496 //JP 13.05.97: if the layout is deleted before the footnotes are deleted,
497 // try to delete the footnote's frames by another way
498 if ( bFrameFnd || !m_oStartNode )
499 return;
501 SwNodeIndex aIdx( *m_oStartNode );
502 SwContentNode* pCNd = SwNodes::GoNext(&aIdx);
503 if( !pCNd )
504 return;
506 SwIterator<SwContentFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*pCNd);
507 for( SwContentFrame* pFnd = aIter.First(); pFnd; pFnd = aIter.Next() )
509 if( pRoot != pFnd->getRootFrame() && pRoot )
510 continue;
511 SwPageFrame* pPage = pFnd->FindPageFrame();
513 SwFrame *pFrame = pFnd->GetUpper();
514 while ( pFrame && !pFrame->IsFootnoteFrame() )
515 pFrame = pFrame->GetUpper();
517 SwFootnoteFrame *pFootnote = static_cast<SwFootnoteFrame*>(pFrame);
518 while ( pFootnote && pFootnote->GetMaster() )
519 pFootnote = pFootnote->GetMaster();
520 OSL_ENSURE( pFootnote->GetAttr() == this, "Footnote mismatch error." );
522 while ( pFootnote )
524 SwFootnoteFrame *pFoll = pFootnote->GetFollow();
525 pFootnote->Cut();
526 SwFrame::DestroyFrame(pFootnote);
527 pFootnote = pFoll;
530 // #i20556# During hiding of a section, the connection
531 // to the layout is already lost. pPage may be 0:
532 if ( pPage )
533 pPage->UpdateFootnoteNum();
537 /// Set the sequence number for the current footnote.
538 /// @returns The new sequence number or USHRT_MAX if invalid.
539 void SwTextFootnote::SetSeqRefNo()
541 if( !m_pTextNode )
542 return;
544 SwDoc& rDoc = m_pTextNode->GetDoc();
545 if( rDoc.IsInReading() )
546 return;
548 std::set<sal_uInt16> aUsedNums;
549 std::vector<SwTextFootnote*> badRefNums;
550 ::lcl_FillUsedFootnoteRefNumbers(rDoc, this, aUsedNums, badRefNums);
551 if ( ::lcl_IsRefNumAvailable(aUsedNums, m_nSeqNo) )
552 return;
553 std::vector<sal_uInt16> unused;
554 ::lcl_FillUnusedSeqRefNums(unused, aUsedNums, 1);
555 m_nSeqNo = unused[0];
558 /// Set a unique sequential reference number for every footnote in the document.
559 /// @param[in] rDoc The document to be processed.
560 void SwTextFootnote::SetUniqueSeqRefNo( SwDoc& rDoc )
562 std::set<sal_uInt16> aUsedNums;
563 std::vector<SwTextFootnote*> badRefNums;
564 ::lcl_FillUsedFootnoteRefNumbers(rDoc, nullptr, aUsedNums, badRefNums);
565 std::vector<sal_uInt16> aUnused;
566 ::lcl_FillUnusedSeqRefNums(aUnused, aUsedNums, badRefNums.size());
568 for (size_t i = 0; i < badRefNums.size(); ++i)
570 badRefNums[i]->m_nSeqNo = aUnused[i];
574 void SwTextFootnote::CheckCondColl()
576 if( GetStartNode() )
577 static_cast<SwStartNode&>(GetStartNode()->GetNode()).CheckSectionCondColl();
580 void SwTextFootnote::dumpAsXml(xmlTextWriterPtr pWriter) const
582 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextFootnote"));
583 SwTextAttr::dumpAsXml(pWriter);
585 if (m_oStartNode)
587 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_oStartNode"));
588 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
589 BAD_CAST(OString::number(sal_Int32(m_oStartNode->GetIndex())).getStr()));
590 (void)xmlTextWriterEndElement(pWriter);
592 if (m_pTextNode)
594 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_pTextNode"));
595 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
596 BAD_CAST(OString::number(sal_Int32(m_pTextNode->GetIndex())).getStr()));
597 (void)xmlTextWriterEndElement(pWriter);
599 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_nSeqNo"));
600 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
601 BAD_CAST(OString::number(m_nSeqNo).getStr()));
602 (void)xmlTextWriterEndElement(pWriter);
604 (void)xmlTextWriterEndElement(pWriter);
607 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */