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