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