android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / txtnode / atrftn.cxx
blob59eb9b12f8f51a9fc001bb7873c2944a69a6b3ee
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 , sw::BroadcastingModify()
129 , m_pTextAttr(nullptr)
130 , m_nNumber(0)
131 , m_nNumberRLHidden(0)
132 , m_bEndNote(bEndNote)
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::SwClientNotify(const SwModify&, const SfxHint& rHint)
160 if (rHint.GetId() != SfxHintId::SwLegacyModify)
161 return;
162 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
163 CallSwClientNotify(rHint);
164 if(RES_REMOVE_UNO_OBJECT == pLegacy->GetWhich())
165 SetXFootnote(nullptr);
168 void SwFormatFootnote::InvalidateFootnote()
170 SwPtrMsgPoolItem const item(RES_REMOVE_UNO_OBJECT,
171 &static_cast<sw::BroadcastingModify&>(*this)); // cast to base class (void*)
172 CallSwClientNotify(sw::LegacyModifyHint(&item, &item));
175 void SwFormatFootnote::SetEndNote( bool b )
177 if ( b != m_bEndNote )
179 if ( GetTextFootnote() )
181 GetTextFootnote()->DelFrames(nullptr);
183 m_bEndNote = b;
187 SwFormatFootnote::~SwFormatFootnote()
191 OUString SwFormatFootnote::GetFootnoteText(SwRootFrame const& rLayout) const
193 OUStringBuffer buf;
194 if( m_pTextAttr->GetStartNode() )
196 SwNodeIndex aIdx( *m_pTextAttr->GetStartNode(), 1 );
197 SwContentNode* pCNd = aIdx.GetNode().GetTextNode();
198 if( !pCNd )
199 pCNd = aIdx.GetNodes().GoNext( &aIdx );
201 if( pCNd->IsTextNode() ) {
202 buf.append(static_cast<SwTextNode*>(pCNd)->GetExpandText(&rLayout));
204 ++aIdx;
205 while ( !aIdx.GetNode().IsEndNode() ) {
206 if ( aIdx.GetNode().IsTextNode() )
208 buf.append(" " + aIdx.GetNode().GetTextNode()->GetExpandText(&rLayout));
210 ++aIdx;
214 return buf.makeStringAndClear();
217 /// return the view string of the foot/endnote
218 OUString SwFormatFootnote::GetViewNumStr(const SwDoc& rDoc,
219 SwRootFrame const*const pLayout, bool bInclStrings) const
221 OUString sRet( GetNumStr() );
222 if( sRet.isEmpty() )
224 // in this case the number is needed, get it via SwDoc's FootnoteInfo
225 bool bMakeNum = true;
226 const SwSectionNode* pSectNd = m_pTextAttr
227 ? SwUpdFootnoteEndNtAtEnd::FindSectNdWithEndAttr( *m_pTextAttr )
228 : nullptr;
229 sal_uInt16 const nNumber(pLayout && pLayout->IsHideRedlines()
230 ? GetNumberRLHidden()
231 : GetNumber());
233 if( pSectNd )
235 const SwFormatFootnoteEndAtTextEnd& rFootnoteEnd = static_cast<const SwFormatFootnoteEndAtTextEnd&>(
236 pSectNd->GetSection().GetFormat()->GetFormatAttr(
237 IsEndNote() ?
238 o3tl::narrowing<sal_uInt16>(RES_END_AT_TXTEND) :
239 o3tl::narrowing<sal_uInt16>(RES_FTN_AT_TXTEND) ) );
241 if( FTNEND_ATTXTEND_OWNNUMANDFMT == rFootnoteEnd.GetValue() )
243 bMakeNum = false;
244 sRet = rFootnoteEnd.GetSwNumType().GetNumStr( nNumber );
245 if( bInclStrings )
247 sRet = rFootnoteEnd.GetPrefix() + sRet + rFootnoteEnd.GetSuffix();
252 if( bMakeNum )
254 const SwEndNoteInfo* pInfo;
255 if( IsEndNote() )
256 pInfo = &rDoc.GetEndNoteInfo();
257 else
258 pInfo = &rDoc.GetFootnoteInfo();
259 sRet = pInfo->m_aFormat.GetNumStr( nNumber );
260 if( bInclStrings )
262 sRet = pInfo->GetPrefix() + sRet + pInfo->GetSuffix();
266 return sRet;
269 uno::Reference<text::XTextRange> SwFormatFootnote::getAnchor(SwDoc& rDoc) const
271 SolarMutexGuard aGuard;
272 if (!m_pTextAttr)
273 return uno::Reference<text::XTextRange>();
274 SwPaM aPam(m_pTextAttr->GetTextNode(), m_pTextAttr->GetStart());
275 aPam.SetMark();
276 aPam.GetMark()->AdjustContent(+1);
277 const rtl::Reference<SwXTextRange> xRet =
278 SwXTextRange::CreateXTextRange(rDoc, *aPam.Start(), aPam.End());
279 return xRet;
282 void SwFormatFootnote::dumpAsXml(xmlTextWriterPtr pWriter) const
284 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatFootnote"));
285 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
286 (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("text-attr"), "%p", m_pTextAttr);
287 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("endnote"),
288 BAD_CAST(OString::boolean(m_bEndNote).getStr()));
290 SfxPoolItem::dumpAsXml(pWriter);
292 (void)xmlTextWriterEndElement(pWriter);
295 SwTextFootnote::SwTextFootnote( SwFormatFootnote& rAttr, sal_Int32 nStartPos )
296 : SwTextAttr( rAttr, nStartPos )
297 , m_pTextNode( nullptr )
298 , m_nSeqNo( USHRT_MAX )
300 rAttr.m_pTextAttr = this;
301 SetHasDummyChar(true);
304 SwTextFootnote::~SwTextFootnote()
306 SetStartNode( nullptr );
309 void SwTextFootnote::SetStartNode( const SwNodeIndex *pNewNode, bool bDelNode )
311 if( pNewNode )
313 m_oStartNode = *pNewNode;
315 else if ( m_oStartNode )
317 // need to do 2 things:
318 // 1) unregister footnotes at their pages
319 // 2) delete the footnote section in the Inserts of the nodes-array
320 SwDoc* pDoc;
321 if ( m_pTextNode )
323 pDoc = &m_pTextNode->GetDoc();
325 else
327 //JP 27.01.97: the sw3-Reader creates a StartNode but the
328 // attribute isn't anchored in the TextNode yet.
329 // If it is deleted (e.g. Insert File with footnote
330 // inside fly frame), the content must also be deleted.
331 pDoc = &m_oStartNode->GetNodes().GetDoc();
334 // If called from ~SwDoc(), must not delete the footnote nodes,
335 // and not necessary to delete the footnote frames.
336 if( !pDoc->IsInDtor() )
338 if( bDelNode )
340 // 2) delete the section for the footnote nodes
341 // it's possible that the Inserts have already been deleted (how???)
342 pDoc->getIDocumentContentOperations().DeleteSection( &m_oStartNode->GetNode() );
344 else
345 // If the nodes are not deleted, their frames must be removed
346 // from the page (deleted), there is nothing else that deletes
347 // them (particularly not Undo)
348 DelFrames( nullptr );
350 m_oStartNode.reset();
352 // remove the footnote from the SwDoc's array
353 for( size_t n = 0; n < pDoc->GetFootnoteIdxs().size(); ++n )
354 if( this == pDoc->GetFootnoteIdxs()[n] )
356 pDoc->GetFootnoteIdxs().erase( pDoc->GetFootnoteIdxs().begin() + n );
357 // if necessary, update following footnotes
358 if( !pDoc->IsInDtor() && n < pDoc->GetFootnoteIdxs().size() )
360 pDoc->GetFootnoteIdxs().UpdateFootnote( pDoc->GetFootnoteIdxs()[n]->GetTextNode() );
362 break;
367 void SwTextFootnote::SetNumber(const sal_uInt16 nNewNum,
368 sal_uInt16 const nNumberRLHidden, const OUString &sNumStr)
370 SwFormatFootnote& rFootnote = const_cast<SwFormatFootnote&>(GetFootnote());
372 rFootnote.m_aNumber = sNumStr;
373 if ( sNumStr.isEmpty() )
375 rFootnote.m_nNumber = nNewNum;
376 rFootnote.m_nNumberRLHidden = nNumberRLHidden;
378 InvalidateNumberInLayout();
381 void SwTextFootnote::InvalidateNumberInLayout()
383 assert(m_pTextNode);
384 SwNodes &rNodes = m_pTextNode->GetDoc().GetNodes();
385 const sw::LegacyModifyHint aHint(nullptr, &GetFootnote());
386 m_pTextNode->TriggerNodeUpdate(aHint);
387 if ( m_oStartNode )
389 // must iterate over all TextNodes because of footnotes on other pages
390 SwNodeOffset nSttIdx = m_oStartNode->GetIndex() + 1;
391 SwNodeOffset nEndIdx = m_oStartNode->GetNode().EndOfSectionIndex();
392 for( ; nSttIdx < nEndIdx; ++nSttIdx )
394 SwNode* pNd;
395 if( ( pNd = rNodes[ nSttIdx ] )->IsTextNode() )
396 static_cast<SwTextNode*>(pNd)->TriggerNodeUpdate(aHint);
401 void SwTextFootnote::CopyFootnote(
402 SwTextFootnote & rDest,
403 SwTextNode & rDestNode ) const
405 if (m_oStartNode && !rDest.GetStartNode())
407 // dest missing node section? create it here!
408 // (happens in SwTextNode::CopyText if pDest == this)
409 rDest.MakeNewTextSection( rDestNode.GetNodes() );
411 if (m_oStartNode && rDest.GetStartNode())
413 // footnotes not necessarily in same document!
414 SwDoc& rDstDoc = rDestNode.GetDoc();
415 SwNodes &rDstNodes = rDstDoc.GetNodes();
417 // copy only the content of the section
418 SwNodeRange aRg( m_oStartNode->GetNode(), SwNodeOffset(1),
419 *m_oStartNode->GetNode().EndOfSectionNode() );
421 // insert at the end of rDest, i.e., the nodes are appended.
422 // nDestLen contains number of ContentNodes in rDest _before_ copy.
423 SwNodeIndex aStart( *(rDest.GetStartNode()) );
424 SwNodeIndex aEnd( *aStart.GetNode().EndOfSectionNode() );
425 SwNodeOffset nDestLen = aEnd.GetIndex() - aStart.GetIndex() - 1;
427 m_pTextNode->GetDoc().GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, aEnd.GetNode());
429 // in case the destination section was not empty, delete the old nodes
430 // before: Src: SxxxE, Dst: SnE
431 // now: Src: SxxxE, Dst: SnxxxE
432 // after: Src: SxxxE, Dst: SxxxE
433 ++aStart;
434 rDstNodes.Delete( aStart, nDestLen );
437 // also copy user defined number string
438 if( !GetFootnote().m_aNumber.isEmpty() )
440 const_cast<SwFormatFootnote &>(rDest.GetFootnote()).m_aNumber = GetFootnote().m_aNumber;
444 /// create a new nodes-array section for the footnote
445 void SwTextFootnote::MakeNewTextSection( SwNodes& rNodes )
447 if ( m_oStartNode )
448 return;
450 // set the footnote style on the SwTextNode
451 SwTextFormatColl *pFormatColl;
452 const SwEndNoteInfo* pInfo;
453 sal_uInt16 nPoolId;
455 if( GetFootnote().IsEndNote() )
457 pInfo = &rNodes.GetDoc().GetEndNoteInfo();
458 nPoolId = RES_POOLCOLL_ENDNOTE;
460 else
462 pInfo = &rNodes.GetDoc().GetFootnoteInfo();
463 nPoolId = RES_POOLCOLL_FOOTNOTE;
466 pFormatColl = pInfo->GetFootnoteTextColl();
467 if( nullptr == pFormatColl )
468 pFormatColl = rNodes.GetDoc().getIDocumentStylePoolAccess().GetTextCollFromPool( nPoolId );
470 SwStartNode* pSttNd = rNodes.MakeTextSection( rNodes.GetEndOfInserts(),
471 SwFootnoteStartNode, pFormatColl );
472 m_oStartNode = *pSttNd;
475 void SwTextFootnote::DelFrames(SwRootFrame const*const pRoot)
477 // delete the FootnoteFrames from the pages
478 OSL_ENSURE( m_pTextNode, "SwTextFootnote: where is my TextNode?" );
479 if ( !m_pTextNode )
480 return;
482 bool bFrameFnd = false;
484 SwIterator<SwContentFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*m_pTextNode);
485 for( SwContentFrame* pFnd = aIter.First(); pFnd; pFnd = aIter.Next() )
487 if( pRoot != pFnd->getRootFrame() && pRoot )
488 continue;
489 SwPageFrame* pPage = pFnd->FindPageFrame();
490 if( pPage )
492 // note: we have found the correct frame only if the footnote
493 // was actually removed; in case this is called from
494 // SwTextFrame::DestroyImpl(), then that frame isn't connected
495 // to SwPageFrame any more, and RemoveFootnote on any follow
496 // must not prevent the fall-back to the !bFrameFnd code.
497 bFrameFnd = pPage->RemoveFootnote(pFnd, this);
501 //JP 13.05.97: if the layout is deleted before the footnotes are deleted,
502 // try to delete the footnote's frames by another way
503 if ( bFrameFnd || !m_oStartNode )
504 return;
506 SwNodeIndex aIdx( *m_oStartNode );
507 SwContentNode* pCNd = m_pTextNode->GetNodes().GoNext( &aIdx );
508 if( !pCNd )
509 return;
511 SwIterator<SwContentFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*pCNd);
512 for( SwContentFrame* pFnd = aIter.First(); pFnd; pFnd = aIter.Next() )
514 if( pRoot != pFnd->getRootFrame() && pRoot )
515 continue;
516 SwPageFrame* pPage = pFnd->FindPageFrame();
518 SwFrame *pFrame = pFnd->GetUpper();
519 while ( pFrame && !pFrame->IsFootnoteFrame() )
520 pFrame = pFrame->GetUpper();
522 SwFootnoteFrame *pFootnote = static_cast<SwFootnoteFrame*>(pFrame);
523 while ( pFootnote && pFootnote->GetMaster() )
524 pFootnote = pFootnote->GetMaster();
525 OSL_ENSURE( pFootnote->GetAttr() == this, "Footnote mismatch error." );
527 while ( pFootnote )
529 SwFootnoteFrame *pFoll = pFootnote->GetFollow();
530 pFootnote->Cut();
531 SwFrame::DestroyFrame(pFootnote);
532 pFootnote = pFoll;
535 // #i20556# During hiding of a section, the connection
536 // to the layout is already lost. pPage may be 0:
537 if ( pPage )
538 pPage->UpdateFootnoteNum();
542 /// Set the sequence number for the current footnote.
543 /// @returns The new sequence number or USHRT_MAX if invalid.
544 void SwTextFootnote::SetSeqRefNo()
546 if( !m_pTextNode )
547 return;
549 SwDoc& rDoc = m_pTextNode->GetDoc();
550 if( rDoc.IsInReading() )
551 return;
553 std::set<sal_uInt16> aUsedNums;
554 std::vector<SwTextFootnote*> badRefNums;
555 ::lcl_FillUsedFootnoteRefNumbers(rDoc, this, aUsedNums, badRefNums);
556 if ( ::lcl_IsRefNumAvailable(aUsedNums, m_nSeqNo) )
557 return;
558 std::vector<sal_uInt16> unused;
559 ::lcl_FillUnusedSeqRefNums(unused, aUsedNums, 1);
560 m_nSeqNo = unused[0];
563 /// Set a unique sequential reference number for every footnote in the document.
564 /// @param[in] rDoc The document to be processed.
565 void SwTextFootnote::SetUniqueSeqRefNo( SwDoc& rDoc )
567 std::set<sal_uInt16> aUsedNums;
568 std::vector<SwTextFootnote*> badRefNums;
569 ::lcl_FillUsedFootnoteRefNumbers(rDoc, nullptr, aUsedNums, badRefNums);
570 std::vector<sal_uInt16> aUnused;
571 ::lcl_FillUnusedSeqRefNums(aUnused, aUsedNums, badRefNums.size());
573 for (size_t i = 0; i < badRefNums.size(); ++i)
575 badRefNums[i]->m_nSeqNo = aUnused[i];
579 void SwTextFootnote::CheckCondColl()
581 if( GetStartNode() )
582 static_cast<SwStartNode&>(GetStartNode()->GetNode()).CheckSectionCondColl();
585 void SwTextFootnote::dumpAsXml(xmlTextWriterPtr pWriter) const
587 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextFootnote"));
588 SwTextAttr::dumpAsXml(pWriter);
590 if (m_oStartNode)
592 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_oStartNode"));
593 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
594 BAD_CAST(OString::number(sal_Int32(m_oStartNode->GetIndex())).getStr()));
595 (void)xmlTextWriterEndElement(pWriter);
597 if (m_pTextNode)
599 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_pTextNode"));
600 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
601 BAD_CAST(OString::number(sal_Int32(m_pTextNode->GetIndex())).getStr()));
602 (void)xmlTextWriterEndElement(pWriter);
604 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_nSeqNo"));
605 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
606 BAD_CAST(OString::number(m_nSeqNo).getStr()));
607 (void)xmlTextWriterEndElement(pWriter);
609 (void)xmlTextWriterEndElement(pWriter);
612 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */