Line data Source code
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 <doc.hxx>
21 : #include <cntfrm.hxx> // OSL_ENSURE(in ~SwTxtFtn()
22 : #include <pagefrm.hxx> // RemoveFtn()
23 : #include <fmtftn.hxx>
24 : #include <txtftn.hxx>
25 : #include <ftnidx.hxx>
26 : #include <ftninfo.hxx>
27 : #include <swfont.hxx>
28 : #include <ndtxt.hxx>
29 : #include <poolfmt.hxx>
30 : #include <ftnfrm.hxx>
31 : #include <ndindex.hxx>
32 : #include <fmtftntx.hxx>
33 : #include <section.hxx>
34 : #include <switerator.hxx>
35 :
36 : namespace {
37 : /// Get a sorted list of the used footnote reference numbers.
38 : /// @param[in] rDoc The active document.
39 : /// @param[in] pExclude A footnote whose reference number should be excluded from the set.
40 : /// @param[out] rInvalid A returned list of all items that had an invalid reference number.
41 : /// @returns The set of used reference numbers.
42 428 : static std::set<sal_uInt16> lcl_GetUsedFtnRefNumbers(SwDoc &rDoc,
43 : SwTxtFtn *pExclude,
44 : std::vector<SwTxtFtn*> &rInvalid)
45 : {
46 428 : std::set<sal_uInt16> aArr;
47 428 : SwFtnIdxs& ftnIdxs = rDoc.GetFtnIdxs();
48 :
49 428 : rInvalid.clear();
50 :
51 446 : for( sal_uInt16 n = 0; n < ftnIdxs.size(); ++n )
52 : {
53 18 : SwTxtFtn* pTxtFtn = ftnIdxs[ n ];
54 18 : if ( pTxtFtn != pExclude )
55 : {
56 12 : if ( USHRT_MAX == pTxtFtn->GetSeqRefNo() )
57 : {
58 0 : rInvalid.push_back(pTxtFtn);
59 : }
60 : else
61 : {
62 12 : aArr.insert( pTxtFtn->GetSeqRefNo() );
63 : }
64 : }
65 : }
66 428 : return aArr;
67 : }
68 :
69 : /// Check whether a requested reference number is available.
70 : /// @param[in] rUsedNums Set of used reference numbers.
71 : /// @param[in] requested The requested reference number.
72 : /// @returns true if the number is available, false if not.
73 6 : static bool lcl_IsRefNumAvailable(std::set<sal_uInt16> &rUsedNums,
74 : sal_uInt16 requested)
75 : {
76 6 : if ( USHRT_MAX == requested )
77 6 : return false; // Invalid sequence number.
78 0 : if ( rUsedNums.count(requested) )
79 0 : return false; // Number already used.
80 0 : return true;
81 : }
82 :
83 : /// Get the first few unused sequential reference numbers.
84 : /// @param[in] rUsedNums The set of used sequential reference numbers.
85 : /// @param[in] numRequired The number of reference number required.
86 : /// @returns The lowest unused sequential reference numbers.
87 428 : static std::vector<sal_uInt16> lcl_GetUnusedSeqRefNums(std::set<sal_uInt16> &rUsedNums,
88 : size_t numRequired)
89 : {
90 428 : std::vector<sal_uInt16> unusedNums;
91 428 : sal_uInt16 newNum = 0;
92 428 : std::set<sal_uInt16>::iterator it;
93 : //Start by using numbers from gaps in rUsedNums
94 430 : for( it = rUsedNums.begin(); it != rUsedNums.end(); ++it )
95 : {
96 24 : while ( newNum < *it )
97 : {
98 10 : unusedNums.push_back( newNum++ );
99 10 : if ( unusedNums.size() >= numRequired )
100 : return unusedNums;
101 : }
102 2 : newNum++;
103 : }
104 : //Filled in all gaps. Fill the rest of the list with new numbers.
105 842 : while ( unusedNums.size() < numRequired )
106 : {
107 6 : unusedNums.push_back( newNum++ );
108 : }
109 :
110 0 : return unusedNums;
111 : }
112 :
113 : }
114 :
115 : /*************************************************************************
116 : |*
117 : |* class SwFmtFtn
118 : |*
119 : *************************************************************************/
120 :
121 :
122 70 : SwFmtFtn::SwFmtFtn( bool bEndNote )
123 : : SfxPoolItem( RES_TXTATR_FTN ),
124 : pTxtAttr( 0 ),
125 : nNumber( 0 ),
126 70 : m_bEndNote( bEndNote )
127 : {
128 70 : }
129 :
130 :
131 0 : int SwFmtFtn::operator==( const SfxPoolItem& rAttr ) const
132 : {
133 : OSL_ENSURE( SfxPoolItem::operator==( rAttr ), "keine gleichen Attribute" );
134 : return nNumber == ((SwFmtFtn&)rAttr).nNumber &&
135 0 : aNumber == ((SwFmtFtn&)rAttr).aNumber &&
136 0 : m_bEndNote == ((SwFmtFtn&)rAttr).m_bEndNote;
137 : }
138 :
139 :
140 32 : SfxPoolItem* SwFmtFtn::Clone( SfxItemPool* ) const
141 : {
142 32 : SwFmtFtn* pNew = new SwFmtFtn;
143 32 : pNew->aNumber = aNumber;
144 32 : pNew->nNumber = nNumber;
145 32 : pNew->m_bEndNote = m_bEndNote;
146 32 : return pNew;
147 : }
148 :
149 0 : void SwFmtFtn::SetEndNote( bool b )
150 : {
151 0 : if ( b != m_bEndNote )
152 : {
153 0 : if ( GetTxtFtn() )
154 : {
155 0 : GetTxtFtn()->DelFrms(0);
156 : }
157 0 : m_bEndNote = b;
158 : }
159 0 : }
160 :
161 106 : SwFmtFtn::~SwFmtFtn()
162 : {
163 106 : }
164 :
165 :
166 0 : void SwFmtFtn::GetFtnText( XubString& rStr ) const
167 : {
168 0 : if( pTxtAttr->GetStartNode() )
169 : {
170 0 : SwNodeIndex aIdx( *pTxtAttr->GetStartNode(), 1 );
171 0 : SwCntntNode* pCNd = aIdx.GetNode().GetTxtNode();
172 0 : if( !pCNd )
173 0 : pCNd = aIdx.GetNodes().GoNext( &aIdx );
174 :
175 0 : if( pCNd->IsTxtNode() )
176 0 : rStr = ((SwTxtNode*)pCNd)->GetExpandTxt();
177 : }
178 0 : }
179 :
180 : // returnt den anzuzeigenden String der Fuss-/Endnote
181 46 : XubString SwFmtFtn::GetViewNumStr( const SwDoc& rDoc, sal_Bool bInclStrings ) const
182 : {
183 46 : XubString sRet( GetNumStr() );
184 46 : if( !sRet.Len() )
185 : {
186 : // dann ist die Nummer von Interesse, also ueber die Info diese
187 : // besorgen.
188 30 : bool bMakeNum = true;
189 : const SwSectionNode* pSectNd = pTxtAttr
190 30 : ? SwUpdFtnEndNtAtEnd::FindSectNdWithEndAttr( *pTxtAttr )
191 60 : : 0;
192 :
193 30 : if( pSectNd )
194 : {
195 : const SwFmtFtnEndAtTxtEnd& rFtnEnd = (SwFmtFtnEndAtTxtEnd&)
196 0 : pSectNd->GetSection().GetFmt()->GetFmtAttr(
197 0 : IsEndNote() ?
198 : static_cast<sal_uInt16>(RES_END_AT_TXTEND) :
199 0 : static_cast<sal_uInt16>(RES_FTN_AT_TXTEND) );
200 :
201 0 : if( FTNEND_ATTXTEND_OWNNUMANDFMT == rFtnEnd.GetValue() )
202 : {
203 0 : bMakeNum = false;
204 0 : sRet = rFtnEnd.GetSwNumType().GetNumStr( GetNumber() );
205 0 : if( bInclStrings )
206 : {
207 0 : sRet.Insert( rFtnEnd.GetPrefix(), 0 );
208 0 : sRet += rFtnEnd.GetSuffix();
209 : }
210 : }
211 : }
212 :
213 30 : if( bMakeNum )
214 : {
215 : const SwEndNoteInfo* pInfo;
216 30 : if( IsEndNote() )
217 0 : pInfo = &rDoc.GetEndNoteInfo();
218 : else
219 30 : pInfo = &rDoc.GetFtnInfo();
220 30 : sRet = pInfo->aFmt.GetNumStr( GetNumber() );
221 30 : if( bInclStrings )
222 : {
223 10 : sRet.Insert( pInfo->GetPrefix(), 0 );
224 10 : sRet += pInfo->GetSuffix();
225 : }
226 : }
227 : }
228 46 : return sRet;
229 : }
230 :
231 : /*************************************************************************
232 : * class SwTxt/FmtFnt
233 : *************************************************************************/
234 :
235 16 : SwTxtFtn::SwTxtFtn( SwFmtFtn& rAttr, xub_StrLen nStartPos )
236 : : SwTxtAttr( rAttr, nStartPos )
237 : , m_pStartNode( 0 )
238 : , m_pTxtNode( 0 )
239 16 : , m_nSeqNo( USHRT_MAX )
240 : {
241 16 : rAttr.pTxtAttr = this;
242 16 : SetHasDummyChar(true);
243 16 : }
244 :
245 :
246 18 : SwTxtFtn::~SwTxtFtn()
247 : {
248 6 : SetStartNode( 0 );
249 12 : }
250 :
251 :
252 :
253 12 : void SwTxtFtn::SetStartNode( const SwNodeIndex *pNewNode, sal_Bool bDelNode )
254 : {
255 12 : if( pNewNode )
256 : {
257 0 : if ( !m_pStartNode )
258 : {
259 0 : m_pStartNode = new SwNodeIndex( *pNewNode );
260 : }
261 : else
262 : {
263 0 : *m_pStartNode = *pNewNode;
264 : }
265 : }
266 12 : else if ( m_pStartNode )
267 : {
268 : // Zwei Dinge muessen erledigt werden:
269 : // 1) Die Fussnoten muessen bei ihren Seiten abgemeldet werden
270 : // 2) Die Fussnoten-Sektion in den Inserts muss geloescht werden.
271 : SwDoc* pDoc;
272 6 : if ( m_pTxtNode )
273 : {
274 6 : pDoc = m_pTxtNode->GetDoc();
275 : }
276 : else
277 : {
278 : //JP 27.01.97: der sw3-Reader setzt einen StartNode aber das
279 : // Attribut ist noch nicht im TextNode verankert.
280 : // Wird es geloescht (z.B. bei Datei einfuegen mit
281 : // Ftn in einen Rahmen), muss auch der Inhalt
282 : // geloescht werden
283 0 : pDoc = m_pStartNode->GetNodes().GetDoc();
284 : }
285 :
286 : // Wir duerfen die Fussnotennodes nicht loeschen
287 : // und brauchen die Fussnotenframes nicht loeschen, wenn
288 : // wir im ~SwDoc() stehen.
289 6 : if( !pDoc->IsInDtor() )
290 : {
291 0 : if( bDelNode )
292 : {
293 : // 1) Die Section fuer die Fussnote wird beseitigt
294 : // Es kann sein, dass die Inserts schon geloescht wurden.
295 0 : pDoc->DeleteSection( &m_pStartNode->GetNode() );
296 : }
297 : else
298 : // Werden die Nodes nicht geloescht mussen sie bei den Seiten
299 : // abmeldet (Frms loeschen) werden, denn sonst bleiben sie
300 : // stehen (Undo loescht sie nicht!)
301 0 : DelFrms( 0 );
302 : }
303 6 : DELETEZ( m_pStartNode );
304 :
305 : // loesche die Fussnote noch aus dem Array am Dokument
306 6 : for( sal_uInt16 n = 0; n < pDoc->GetFtnIdxs().size(); ++n )
307 0 : if( this == pDoc->GetFtnIdxs()[n] )
308 : {
309 0 : pDoc->GetFtnIdxs().erase( pDoc->GetFtnIdxs().begin() + n );
310 : // gibt noch weitere Fussnoten
311 0 : if( !pDoc->IsInDtor() && n < pDoc->GetFtnIdxs().size() )
312 : {
313 0 : SwNodeIndex aTmp( pDoc->GetFtnIdxs()[n]->GetTxtNode() );
314 0 : pDoc->GetFtnIdxs().UpdateFtn( aTmp );
315 : }
316 0 : break;
317 : }
318 : }
319 12 : }
320 :
321 :
322 10 : void SwTxtFtn::SetNumber( const sal_uInt16 nNewNum, const XubString* pStr )
323 : {
324 10 : SwFmtFtn& rFtn = (SwFmtFtn&)GetFtn();
325 10 : if( pStr && pStr->Len() )
326 0 : rFtn.aNumber = *pStr;
327 : else
328 : {
329 10 : rFtn.nNumber = nNewNum;
330 10 : rFtn.aNumber = aEmptyStr;
331 : }
332 :
333 : OSL_ENSURE( m_pTxtNode, "SwTxtFtn: where is my TxtNode?" );
334 10 : SwNodes &rNodes = m_pTxtNode->GetDoc()->GetNodes();
335 10 : m_pTxtNode->ModifyNotification( 0, &rFtn );
336 10 : if ( m_pStartNode )
337 : {
338 : // must iterate over all TxtNodes because of footnotes on other pages
339 : SwNode* pNd;
340 10 : sal_uLong nSttIdx = m_pStartNode->GetIndex() + 1;
341 10 : sal_uLong nEndIdx = m_pStartNode->GetNode().EndOfSectionIndex();
342 20 : for( ; nSttIdx < nEndIdx; ++nSttIdx )
343 : {
344 : // Es koennen ja auch Grafiken in der Fussnote stehen ...
345 10 : if( ( pNd = rNodes[ nSttIdx ] )->IsTxtNode() )
346 10 : ((SwTxtNode*)pNd)->ModifyNotification( 0, &rFtn );
347 : }
348 : }
349 10 : }
350 :
351 : // Die Fussnoten duplizieren
352 0 : void SwTxtFtn::CopyFtn(SwTxtFtn & rDest, SwTxtNode & rDestNode) const
353 : {
354 0 : if (m_pStartNode && !rDest.GetStartNode())
355 : {
356 : // dest missing node section? create it here!
357 : // (happens in SwTxtNode::CopyText if pDest == this)
358 0 : rDest.MakeNewTextSection( rDestNode.GetNodes() );
359 : }
360 0 : if (m_pStartNode && rDest.GetStartNode())
361 : {
362 : // footnotes not necessarily in same document!
363 0 : SwDoc *const pDstDoc = rDestNode.GetDoc();
364 0 : SwNodes &rDstNodes = pDstDoc->GetNodes();
365 :
366 : // copy only the content of the section
367 : SwNodeRange aRg( *m_pStartNode, 1,
368 0 : *m_pStartNode->GetNode().EndOfSectionNode() );
369 :
370 : // insert at the end of rDest, i.e., the nodes are appended.
371 : // nDestLen contains number of CntntNodes in rDest _before_ copy.
372 0 : SwNodeIndex aStart( *(rDest.GetStartNode()) );
373 0 : SwNodeIndex aEnd( *aStart.GetNode().EndOfSectionNode() );
374 0 : sal_uLong nDestLen = aEnd.GetIndex() - aStart.GetIndex() - 1;
375 :
376 0 : m_pTxtNode->GetDoc()->CopyWithFlyInFly( aRg, 0, aEnd, sal_True );
377 :
378 : // in case the destination section was not empty, delete the old nodes
379 : // before: Src: SxxxE, Dst: SnE
380 : // now: Src: SxxxE, Dst: SnxxxE
381 : // after: Src: SxxxE, Dst: SxxxE
382 0 : ++aStart;
383 0 : rDstNodes.Delete( aStart, nDestLen );
384 : }
385 :
386 : // also copy user defined number string
387 0 : if( GetFtn().aNumber.Len() )
388 : {
389 0 : const_cast<SwFmtFtn &>(rDest.GetFtn()).aNumber = GetFtn().aNumber;
390 : }
391 0 : }
392 :
393 :
394 : // lege eine neue leere TextSection fuer diese Fussnote an
395 16 : void SwTxtFtn::MakeNewTextSection( SwNodes& rNodes )
396 : {
397 16 : if ( m_pStartNode )
398 16 : return;
399 :
400 : // Nun verpassen wir dem TxtNode noch die Fussnotenvorlage.
401 : SwTxtFmtColl *pFmtColl;
402 : const SwEndNoteInfo* pInfo;
403 : sal_uInt16 nPoolId;
404 :
405 16 : if( GetFtn().IsEndNote() )
406 : {
407 0 : pInfo = &rNodes.GetDoc()->GetEndNoteInfo();
408 0 : nPoolId = RES_POOLCOLL_ENDNOTE;
409 : }
410 : else
411 : {
412 16 : pInfo = &rNodes.GetDoc()->GetFtnInfo();
413 16 : nPoolId = RES_POOLCOLL_FOOTNOTE;
414 : }
415 :
416 16 : if( 0 == (pFmtColl = pInfo->GetFtnTxtColl() ) )
417 14 : pFmtColl = rNodes.GetDoc()->GetTxtCollFromPool( nPoolId );
418 :
419 16 : SwStartNode* pSttNd = rNodes.MakeTextSection( SwNodeIndex( rNodes.GetEndOfInserts() ),
420 16 : SwFootnoteStartNode, pFmtColl );
421 16 : m_pStartNode = new SwNodeIndex( *pSttNd );
422 : }
423 :
424 :
425 4 : void SwTxtFtn::DelFrms( const SwFrm* pSib )
426 : {
427 : // delete the FtnFrames from the pages
428 : OSL_ENSURE( m_pTxtNode, "SwTxtFtn: where is my TxtNode?" );
429 4 : if ( !m_pTxtNode )
430 4 : return;
431 :
432 4 : const SwRootFrm* pRoot = pSib ? pSib->getRootFrm() : 0;
433 4 : bool bFrmFnd = false;
434 : {
435 4 : SwIterator<SwCntntFrm,SwTxtNode> aIter( *m_pTxtNode );
436 4 : for( SwCntntFrm* pFnd = aIter.First(); pFnd; pFnd = aIter.Next() )
437 : {
438 0 : if( pRoot != pFnd->getRootFrm() && pRoot )
439 0 : continue;
440 0 : SwPageFrm* pPage = pFnd->FindPageFrm();
441 0 : if( pPage )
442 : {
443 0 : pPage->RemoveFtn( pFnd, this );
444 0 : bFrmFnd = true;
445 : }
446 4 : }
447 : }
448 : //JP 13.05.97: falls das Layout vorm loeschen der Fussnoten entfernt
449 : // wird, sollte man das ueber die Fussnote selbst tun
450 4 : if ( !bFrmFnd && m_pStartNode )
451 : {
452 4 : SwNodeIndex aIdx( *m_pStartNode );
453 4 : SwCntntNode* pCNd = m_pTxtNode->GetNodes().GoNext( &aIdx );
454 4 : if( pCNd )
455 : {
456 4 : SwIterator<SwCntntFrm,SwCntntNode> aIter( *pCNd );
457 4 : for( SwCntntFrm* pFnd = aIter.First(); pFnd; pFnd = aIter.Next() )
458 : {
459 0 : if( pRoot != pFnd->getRootFrm() && pRoot )
460 0 : continue;
461 0 : SwPageFrm* pPage = pFnd->FindPageFrm();
462 :
463 0 : SwFrm *pFrm = pFnd->GetUpper();
464 0 : while ( pFrm && !pFrm->IsFtnFrm() )
465 0 : pFrm = pFrm->GetUpper();
466 :
467 0 : SwFtnFrm *pFtn = (SwFtnFrm*)pFrm;
468 0 : while ( pFtn && pFtn->GetMaster() )
469 0 : pFtn = pFtn->GetMaster();
470 : OSL_ENSURE( pFtn->GetAttr() == this, "Ftn mismatch error." );
471 :
472 0 : while ( pFtn )
473 : {
474 0 : SwFtnFrm *pFoll = pFtn->GetFollow();
475 0 : pFtn->Cut();
476 0 : delete pFtn;
477 0 : pFtn = pFoll;
478 : }
479 :
480 : // #i20556# During hiding of a section, the connection
481 : // to the layout is already lost. pPage may be 0:
482 0 : if ( pPage )
483 0 : pPage->UpdateFtnNum();
484 4 : }
485 4 : }
486 : }
487 : }
488 :
489 : /// Set the sequence number for the current footnote.
490 : /// @returns The new sequence number or USHRT_MAX if invalid.
491 16 : sal_uInt16 SwTxtFtn::SetSeqRefNo()
492 : {
493 16 : if( !m_pTxtNode )
494 0 : return USHRT_MAX;
495 :
496 16 : SwDoc* pDoc = m_pTxtNode->GetDoc();
497 16 : if( pDoc->IsInReading() )
498 10 : return USHRT_MAX;
499 :
500 6 : std::vector<SwTxtFtn*> badRefNums;
501 6 : std::set<sal_uInt16> aUsedNums = ::lcl_GetUsedFtnRefNumbers(*pDoc, this, badRefNums);
502 6 : if ( ::lcl_IsRefNumAvailable(aUsedNums, m_nSeqNo) )
503 0 : return m_nSeqNo;
504 6 : std::vector<sal_uInt16> unused = ::lcl_GetUnusedSeqRefNums(aUsedNums, 1);
505 6 : return m_nSeqNo = unused[0];
506 : }
507 :
508 : /// Set a unique sequential reference number for every footnote in the document.
509 : /// @param[in] rDoc The document to be processed.
510 422 : void SwTxtFtn::SetUniqueSeqRefNo( SwDoc& rDoc )
511 : {
512 422 : std::vector<SwTxtFtn*> badRefNums;
513 422 : std::set<sal_uInt16> aUsedNums = ::lcl_GetUsedFtnRefNumbers(rDoc, NULL, badRefNums);
514 422 : std::vector<sal_uInt16> unused = ::lcl_GetUnusedSeqRefNums(aUsedNums, badRefNums.size());
515 :
516 422 : for (size_t i = 0; i < badRefNums.size(); ++i)
517 : {
518 0 : badRefNums[i]->m_nSeqNo = unused[i];
519 422 : }
520 422 : }
521 :
522 0 : void SwTxtFtn::CheckCondColl()
523 : {
524 : //FEATURE::CONDCOLL
525 0 : if( GetStartNode() )
526 0 : ((SwStartNode&)GetStartNode()->GetNode()).CheckSectionCondColl();
527 : //FEATURE::CONDCOLL
528 0 : }
529 :
530 :
531 :
532 :
533 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|