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