1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <txtftn.hxx>
21 #include <fmtftn.hxx>
22 #include <ftninfo.hxx>
23 #include <doc.hxx>
24 #include <IDocumentLayoutAccess.hxx>
25 #include <IDocumentRedlineAccess.hxx>
26 #include <redline.hxx>
27 #include <ftnidx.hxx>
28 #include <ndtxt.hxx>
29 #include <ndindex.hxx>
30 #include <section.hxx>
31 #include <fmtftntx.hxx>
32 #include <rootfrm.hxx>
33 #include <txtfrm.hxx>
34 
35 namespace sw {
36 
IsFootnoteDeleted(IDocumentRedlineAccess const & rIDRA,SwTextFootnote const & rTextFootnote)37 bool IsFootnoteDeleted(IDocumentRedlineAccess const& rIDRA,
38         SwTextFootnote const& rTextFootnote)
39 {
40     SwRedlineTable::size_type tmp;
41     SwPosition const pos(const_cast<SwTextNode&>(rTextFootnote.GetTextNode()),
42             rTextFootnote.GetStart());
43     SwRangeRedline const*const pRedline(rIDRA.GetRedline(pos, &tmp));
44     return (pRedline
45         && pRedline->GetType() == RedlineType::Delete
46         && *pRedline->GetPoint() != *pRedline->GetMark());
47 }
48 
49 }
50 
51 using sw::IsFootnoteDeleted;
52 
operator ()(SwTextFootnote * const & lhs,SwTextFootnote * const & rhs) const53 bool CompareSwFootnoteIdxs::operator()(SwTextFootnote* const& lhs, SwTextFootnote* const& rhs) const
54 {
55     sal_uLong nIdxLHS = SwTextFootnote_GetIndex( lhs );
56     sal_uLong nIdxRHS = SwTextFootnote_GetIndex( rhs );
57     return ( nIdxLHS == nIdxRHS && lhs->GetStart() < rhs->GetStart() ) || nIdxLHS < nIdxRHS;
58 }
59 
UpdateFootnote(const SwNodeIndex & rStt)60 void SwFootnoteIdxs::UpdateFootnote( const SwNodeIndex& rStt )
61 {
62     if( empty() )
63         return;
64 
65     // Get the NodesArray using the first foot note's StartIndex
66     SwDoc& rDoc = rStt.GetNode().GetDoc();
67     if( rDoc.IsInReading() )
68         return ;
69     SwTextFootnote* pTextFootnote;
70 
71     const SwEndNoteInfo& rEndInfo = rDoc.GetEndNoteInfo();
72     const SwFootnoteInfo& rFootnoteInfo = rDoc.GetFootnoteInfo();
73     IDocumentRedlineAccess const& rIDRA(rDoc.getIDocumentRedlineAccess());
74 
75     // For normal foot notes we treat per-chapter and per-document numbering
76     // separately. For Endnotes we only have per-document numbering.
77     if( FTNNUM_CHAPTER == rFootnoteInfo.m_eNum )
78     {
79         SwRootFrame const* pLayout(nullptr);
80         o3tl::sorted_vector<SwRootFrame*> layouts = rDoc.GetAllLayouts();
81         // sw_redlinehide: here we need to know if there's *any* layout with
82         // IsHideRedlines(), because then the hidden-numbers have to be updated
83         for (SwRootFrame const* pTmp : layouts)
84         {
85             if (pTmp->IsHideRedlines())
86             {
87                 pLayout = pTmp;
88             }
89         }
90 
91         const SwOutlineNodes& rOutlNds = rDoc.GetNodes().GetOutLineNds();
92         const SwNode *pChapterStartHidden(&rDoc.GetNodes().GetEndOfExtras());
93         sal_uLong nChapterStart(pChapterStartHidden->GetIndex());
94         sal_uLong nChapterEnd(rDoc.GetNodes().GetEndOfContent().GetIndex());
95         sal_uLong nChapterEndHidden(nChapterEnd);
96         if( !rOutlNds.empty() )
97         {
98             // Find the Chapter's start, which contains rStt
99             size_t n = 0;
100 
101             for( ; n < rOutlNds.size(); ++n )
102                 if( rOutlNds[ n ]->GetIndex() > rStt.GetIndex() )
103                     break;      // found it!
104                 else if ( rOutlNds[ n ]->GetTextNode()->GetAttrOutlineLevel() == 1 )
105                 {
106                     nChapterStart = rOutlNds[ n ]->GetIndex();
107                     if (!pLayout || sw::IsParaPropsNode(*pLayout, *rOutlNds[n]->GetTextNode()))
108                     {
109                         pChapterStartHidden = rOutlNds[ n ];
110                     }
111                 }
112             // now find the end of the range
113             for( ; n < rOutlNds.size(); ++n )
114                 if ( rOutlNds[ n ]->GetTextNode()->GetAttrOutlineLevel() == 1 )
115                 {
116                     nChapterEnd = rOutlNds[ n ]->GetIndex();
117                     break;
118                 }
119 
120             // continue to find end of hidden-chapter
121             for ( ; n < rOutlNds.size(); ++n)
122             {
123                 if (rOutlNds[n]->GetTextNode()->GetAttrOutlineLevel() == 1
124                     && (!pLayout || sw::IsParaPropsNode(*pLayout, *rOutlNds[n]->GetTextNode())))
125                 {
126                     nChapterEndHidden = rOutlNds[n]->GetIndex();
127                     break;
128                 }
129             }
130         }
131 
132         size_t nPos = 0;
133         size_t nFootnoteNo = 1;
134         size_t nFootnoteNoHidden = 1;
135         if (SeekEntry( *pChapterStartHidden, &nPos ) && nPos)
136         {
137             // Step forward until the Index is not the same anymore
138             const SwNode* pCmpNd = &rStt.GetNode();
139             while( nPos && pCmpNd == &((*this)[ --nPos ]->GetTextNode()) )
140                 ;
141             ++nPos;
142         }
143 
144         if( nPos == size() )       // nothing found
145             return;
146 
147         if( rOutlNds.empty() )
148         {
149             nFootnoteNo = nPos+1;
150             if (nPos)
151             {
152                 nFootnoteNoHidden = (*this)[nPos - 1]->GetFootnote().GetNumberRLHidden() + 1;
153             }
154         }
155 
156         for( ; nPos < size(); ++nPos )
157         {
158             pTextFootnote = (*this)[ nPos ];
159             sal_uLong const nNode(pTextFootnote->GetTextNode().GetIndex());
160             if (nChapterEndHidden <= nNode)
161                 break;
162 
163             const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
164             if( rFootnote.GetNumStr().isEmpty() && !rFootnote.IsEndNote() &&
165                 !SwUpdFootnoteEndNtAtEnd::FindSectNdWithEndAttr( *pTextFootnote ))
166             {
167                 pTextFootnote->SetNumber(
168                     (nChapterStart <= nNode && nNode < nChapterEnd)
169                         ? rFootnoteInfo.m_nFootnoteOffset + nFootnoteNo
170                         : rFootnote.GetNumber(),
171                     rFootnoteInfo.m_nFootnoteOffset + nFootnoteNoHidden,
172                     rFootnote.GetNumStr() );
173                 if (nChapterStart <= nNode && nNode < nChapterEnd)
174                 {
175                     ++nFootnoteNo;
176                 }
177                 if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
178                 {
179                     ++nFootnoteNoHidden;
180                 }
181             }
182         }
183     }
184 
185     SwUpdFootnoteEndNtAtEnd aNumArr;
186 
187     // unless we have per-document numbering, only look at endnotes here
188     const bool bEndNoteOnly = FTNNUM_DOC != rFootnoteInfo.m_eNum;
189 
190     size_t nPos;
191     size_t nFootnoteNo = 1;
192     size_t nEndNo = 1;
193     size_t nFootnoteNoHidden = 1;
194     size_t nEndNoHidden = 1;
195     sal_uLong nUpdNdIdx = rStt.GetIndex();
196     for( nPos = 0; nPos < size(); ++nPos )
197     {
198         pTextFootnote = (*this)[ nPos ];
199         if( nUpdNdIdx <= pTextFootnote->GetTextNode().GetIndex() )
200             break;
201 
202         const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
203         if( rFootnote.GetNumStr().isEmpty() )
204         {
205             if (!aNumArr.ChkNumber(rIDRA, *pTextFootnote).first)
206             {
207                 if( pTextFootnote->GetFootnote().IsEndNote() )
208                 {
209                     nEndNo++;
210                     if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
211                     {
212                         ++nEndNoHidden;
213                     }
214                 }
215                 else
216                 {
217                     nFootnoteNo++;
218                     if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
219                     {
220                         ++nFootnoteNoHidden;
221                     }
222                 }
223             }
224         }
225     }
226 
227     // Set the array number for all footnotes starting from nPos
228     for( ; nPos < size(); ++nPos )
229     {
230         pTextFootnote = (*this)[ nPos ];
231         const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
232         if( rFootnote.GetNumStr().isEmpty() )
233         {
234             std::pair<sal_uInt16, sal_uInt16> nSectNo = aNumArr.ChkNumber(rIDRA, *pTextFootnote);
235             if (!nSectNo.first && (rFootnote.IsEndNote() || !bEndNoteOnly))
236             {
237                 if (rFootnote.IsEndNote())
238                 {
239                     nSectNo.first = rEndInfo.m_nFootnoteOffset + nEndNo;
240                     ++nEndNo;
241                     nSectNo.second = rEndInfo.m_nFootnoteOffset + nEndNoHidden;
242                     if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
243                     {
244                         ++nEndNoHidden;
245                     }
246                 }
247                 else
248                 {
249                     nSectNo.first = rFootnoteInfo.m_nFootnoteOffset + nFootnoteNo;
250                     ++nFootnoteNo;
251                     nSectNo.second = rFootnoteInfo.m_nFootnoteOffset + nFootnoteNoHidden;
252                     if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
253                     {
254                         ++nFootnoteNoHidden;
255                     }
256                 }
257             }
258 
259             if (nSectNo.first)
260             {
261                 pTextFootnote->SetNumber(nSectNo.first, nSectNo.second, rFootnote.GetNumStr());
262             }
263         }
264     }
265 }
266 
UpdateAllFootnote()267 void SwFootnoteIdxs::UpdateAllFootnote()
268 {
269     if( empty() )
270         return;
271 
272     // Get the NodesArray via the StartIndex of the first Footnote
273     SwDoc& rDoc = const_cast<SwDoc&>((*this)[ 0 ]->GetTextNode().GetDoc());
274     SwTextFootnote* pTextFootnote;
275     const SwEndNoteInfo& rEndInfo = rDoc.GetEndNoteInfo();
276     const SwFootnoteInfo& rFootnoteInfo = rDoc.GetFootnoteInfo();
277     IDocumentRedlineAccess const& rIDRA(rDoc.getIDocumentRedlineAccess());
278 
279     SwUpdFootnoteEndNtAtEnd aNumArr;
280 
281     SwRootFrame const* pLayout = rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
282     o3tl::sorted_vector<SwRootFrame*> aAllLayouts = rDoc.GetAllLayouts();
283     // For normal Footnotes per-chapter and per-document numbering are treated separately.
284     // For Endnotes we only have document-wise numbering.
285     if( FTNNUM_CHAPTER == rFootnoteInfo.m_eNum )
286     {
287         // sw_redlinehide: here we need to know if there's *any* layout with
288         // IsHideRedlines(), because then the hidden-numbers have to be updated
289         for (SwRootFrame const* pTmp : aAllLayouts)
290         {
291             if (pTmp->IsHideRedlines())
292             {
293                 pLayout = pTmp;
294             }
295         }
296 
297         const SwOutlineNodes& rOutlNds = rDoc.GetNodes().GetOutLineNds();
298         sal_uInt16 nNo = 1;     // Number for the Footnotes
299         sal_uInt16 nNoNo = 1;
300         size_t nFootnoteIdx = 0;     // Index into theFootnoteIdx array
301         for( size_t n = 0; n < rOutlNds.size(); ++n )
302         {
303             if ( rOutlNds[ n ]->GetTextNode()->GetAttrOutlineLevel() == 1 )
304             {
305                 sal_uLong nCapStt = rOutlNds[ n ]->GetIndex();  // Start of a new chapter
306                 for( ; nFootnoteIdx < size(); ++nFootnoteIdx )
307                 {
308                     pTextFootnote = (*this)[ nFootnoteIdx ];
309                     if( pTextFootnote->GetTextNode().GetIndex() >= nCapStt )
310                         break;
311 
312                     // Endnotes are per-document only
313                     const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
314                     if( !rFootnote.IsEndNote() && rFootnote.GetNumStr().isEmpty() &&
315                         !SwUpdFootnoteEndNtAtEnd::FindSectNdWithEndAttr( *pTextFootnote ))
316                     {
317                         pTextFootnote->SetNumber(
318                             rFootnoteInfo.m_nFootnoteOffset + nNo,
319                             rFootnoteInfo.m_nFootnoteOffset + nNoNo,
320                             rFootnote.GetNumStr() );
321                         ++nNo;
322                         if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
323                         {
324                             ++nNoNo;
325                         }
326                     }
327                 }
328                 if( nFootnoteIdx >= size() )
329                     break;          // ok, everything is updated
330                 nNo = 1;
331                 // sw_redlinehide: this means the numbers are layout dependent in chapter case
332                 if (!pLayout || sw::IsParaPropsNode(*pLayout, *rOutlNds[ n ]->GetTextNode()))
333                 {
334                     nNoNo = 1;
335                 }
336             }
337         }
338 
339         for (nNo = 1, nNoNo = 1; nFootnoteIdx < size(); ++nFootnoteIdx)
340         {
341             // Endnotes are per-document
342             pTextFootnote = (*this)[ nFootnoteIdx ];
343             const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
344             if( !rFootnote.IsEndNote() && rFootnote.GetNumStr().isEmpty() &&
345                 !SwUpdFootnoteEndNtAtEnd::FindSectNdWithEndAttr( *pTextFootnote ))
346             {
347                 pTextFootnote->SetNumber(
348                         rFootnoteInfo.m_nFootnoteOffset + nNo,
349                         rFootnoteInfo.m_nFootnoteOffset + nNoNo,
350                         rFootnote.GetNumStr() );
351                 ++nNo;
352                 if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
353                 {
354                     ++nNoNo;
355                 }
356             }
357         }
358     }
359 
360     // We use bool here, so that we also iterate through the Endnotes with a chapter setting.
361     const bool bEndNoteOnly = FTNNUM_DOC != rFootnoteInfo.m_eNum;
362     sal_uInt16 nFootnoteNo = 1;
363     sal_uInt16 nEndnoteNo = 1;
364     sal_uInt16 nFootnoteNoHidden = 1;
365     sal_uInt16 nEndnoteNoHidden = 1;
366     for( size_t nPos = 0; nPos < size(); ++nPos )
367     {
368         pTextFootnote = (*this)[ nPos ];
369         const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
370         if( rFootnote.GetNumStr().isEmpty() )
371         {
372             std::pair<sal_uInt16, sal_uInt16> nSectNo = aNumArr.ChkNumber(rIDRA, *pTextFootnote);
373             if (!nSectNo.first && (rFootnote.IsEndNote() || !bEndNoteOnly))
374             {
375                 if (rFootnote.IsEndNote())
376                 {
377                     nSectNo.first = rEndInfo.m_nFootnoteOffset + nEndnoteNo;
378                     ++nEndnoteNo;
379                     nSectNo.second = rEndInfo.m_nFootnoteOffset + nEndnoteNoHidden;
380                     if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
381                     {
382                         ++nEndnoteNoHidden;
383                     }
384                 }
385                 else
386                 {
387                     nSectNo.first = rFootnoteInfo.m_nFootnoteOffset + nFootnoteNo;
388                     ++nFootnoteNo;
389                     nSectNo.second = rFootnoteInfo.m_nFootnoteOffset + nFootnoteNoHidden;
390                     if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
391                     {
392                         ++nFootnoteNoHidden;
393                     }
394                 }
395             }
396 
397             if (nSectNo.first)
398             {
399                 pTextFootnote->SetNumber(nSectNo.first, nSectNo.second, rFootnote.GetNumStr());
400             }
401         }
402     }
403 
404     if (pLayout && FTNNUM_PAGE == rFootnoteInfo.m_eNum)
405         for( auto aLayout : aAllLayouts )
406             aLayout->UpdateFootnoteNums();
407 }
408 
SeekEntry(const SwNodeIndex & rPos,size_t * pFndPos) const409 SwTextFootnote* SwFootnoteIdxs::SeekEntry( const SwNodeIndex& rPos, size_t* pFndPos ) const
410 {
411     sal_uLong nIdx = rPos.GetIndex();
412 
413     size_t nO = size();
414     size_t nU = 0;
415     if( nO > 0 )
416     {
417         nO--;
418         while( nU <= nO )
419         {
420             const size_t nM = nU + ( nO - nU ) / 2;
421             sal_uLong nNdIdx = SwTextFootnote_GetIndex( (*this)[ nM ] );
422             if( nNdIdx == nIdx )
423             {
424                 if( pFndPos )
425                     *pFndPos = nM;
426                 return (*this)[ nM ];
427             }
428             else if( nNdIdx < nIdx )
429                 nU = nM + 1;
430             else if( nM == 0 )
431             {
432                 if( pFndPos )
433                     *pFndPos = nU;
434                 return nullptr;
435             }
436             else
437                 nO = nM - 1;
438         }
439     }
440     if( pFndPos )
441         *pFndPos = nU;
442     return nullptr;
443 }
444 
FindSectNdWithEndAttr(const SwTextFootnote & rTextFootnote)445 const SwSectionNode* SwUpdFootnoteEndNtAtEnd::FindSectNdWithEndAttr(
446                 const SwTextFootnote& rTextFootnote )
447 {
448     sal_uInt16 nWh = rTextFootnote.GetFootnote().IsEndNote() ?
449                         sal_uInt16(RES_END_AT_TXTEND) : sal_uInt16(RES_FTN_AT_TXTEND);
450     const SwSectionNode* pNd = rTextFootnote.GetTextNode().FindSectionNode();
451     while( pNd )
452     {
453         sal_uInt16 nVal = static_cast<const SwFormatFootnoteEndAtTextEnd&>(pNd->GetSection().GetFormat()->
454                  GetFormatAttr( nWh )).GetValue();
455         if( FTNEND_ATTXTEND_OWNNUMSEQ == nVal || FTNEND_ATTXTEND_OWNNUMANDFMT == nVal )
456             break;
457         pNd = pNd->StartOfSectionNode()->FindSectionNode();
458     }
459 
460     return pNd;
461 }
462 
GetNumber(IDocumentRedlineAccess const & rIDRA,const SwTextFootnote & rTextFootnote,const SwSectionNode & rNd)463 std::pair<sal_uInt16, sal_uInt16> SwUpdFootnoteEndNtAtEnd::GetNumber(
464         IDocumentRedlineAccess const& rIDRA,
465         const SwTextFootnote& rTextFootnote,
466                                     const SwSectionNode& rNd )
467 {
468     std::pair<sal_uInt16, sal_uInt16> nRet(0, 0);
469     sal_uInt16 nWh;
470     std::vector<const SwSectionNode*>* pArr;
471     std::vector<std::pair<sal_uInt16, sal_uInt16>> *pNum;
472     if( rTextFootnote.GetFootnote().IsEndNote() )
473     {
474         pArr = &m_aEndSections;
475         pNum = &m_aEndNumbers;
476         nWh = RES_END_AT_TXTEND;
477     }
478     else
479     {
480         pArr = &m_aFootnoteSections;
481         pNum = &m_aFootnoteNumbers;
482         nWh = RES_FTN_AT_TXTEND;
483     }
484 
485     for( size_t n = pArr->size(); n; )
486         if( (*pArr)[ --n ] == &rNd )
487         {
488             nRet.first = ++((*pNum)[ n ].first);
489             if (!IsFootnoteDeleted(rIDRA, rTextFootnote))
490             {
491                 ++((*pNum)[ n ].second);
492             }
493             nRet.second = ((*pNum)[ n ].second);
494             break;
495         }
496 
497     if (!nRet.first)
498     {
499         pArr->push_back( &rNd );
500         sal_uInt16 const tmp = static_cast<const SwFormatFootnoteEndAtTextEnd&>(
501                 rNd.GetSection().GetFormat()->
502                                 GetFormatAttr( nWh )).GetOffset();
503         nRet.first = tmp + 1;
504         nRet.second = tmp + 1;
505         pNum->push_back( nRet );
506     }
507     return nRet;
508 }
509 
ChkNumber(IDocumentRedlineAccess const & rIDRA,const SwTextFootnote & rTextFootnote)510 std::pair<sal_uInt16, sal_uInt16> SwUpdFootnoteEndNtAtEnd::ChkNumber(
511         IDocumentRedlineAccess const& rIDRA,
512         const SwTextFootnote& rTextFootnote)
513 {
514     const SwSectionNode* pSectNd = FindSectNdWithEndAttr( rTextFootnote );
515     return pSectNd
516             ? GetNumber(rIDRA, rTextFootnote, *pSectNd)
517             : std::pair<sal_uInt16, sal_uInt16>(0, 0);
518 }
519 
520 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
521