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