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