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