xref: /aoo42x/main/sw/source/core/text/txtftn.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 #include "viewsh.hxx"
27 #include "doc.hxx"
28 #include "pagefrm.hxx"
29 #include "rootfrm.hxx"
30 #include "ndtxt.hxx"
31 #include "txtatr.hxx"
32 #include <SwPortionHandler.hxx>
33 #include <txtftn.hxx>
34 #include <flyfrm.hxx>
35 #include <fmtftn.hxx>
36 #include <ftninfo.hxx>
37 #include <charfmt.hxx>
38 #include <dflyobj.hxx>
39 #include <rowfrm.hxx>
40 #include <editeng/brshitem.hxx>
41 #include <editeng/charrotateitem.hxx>
42 #include <breakit.hxx>
43 #ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_
44 #include <com/sun/star/i18n/ScriptType.hdl>
45 #endif
46 #include <tabfrm.hxx>
47 // OD 2004-05-24 #i28701#
48 #include <sortedobjs.hxx>
49 
50 #include "txtcfg.hxx"
51 #include "swfont.hxx"	// new SwFont
52 #include "porftn.hxx"
53 #include "porfly.hxx"
54 #include "porlay.hxx"
55 #include "txtfrm.hxx"
56 #include "itrform2.hxx"
57 #include "ftnfrm.hxx"	// FindQuoVadisFrm(),
58 #include "pagedesc.hxx"
59 #include "redlnitr.hxx" // SwRedlnItr
60 #include "sectfrm.hxx"	// SwSectionFrm
61 #include "layouter.hxx" // Endnote-Collection
62 #include "frmtool.hxx"
63 #include "ndindex.hxx"
64 
65 using namespace ::com::sun::star;
66 
67 /*************************************************************************
68  *								_IsFtnNumFrm()
69  *************************************************************************/
70 
71 sal_Bool SwTxtFrm::_IsFtnNumFrm() const
72 {
73 	const SwFtnFrm* pFtn = FindFtnFrm()->GetMaster();
74 	while( pFtn && !pFtn->ContainsCntnt() )
75 		pFtn = pFtn->GetMaster();
76 	return !pFtn;
77 }
78 
79 /*************************************************************************
80  *								FindFtn()
81  *************************************************************************/
82 
83 // Sucht innerhalb einer Master-Follow-Kette den richtigen TxtFrm zum SwTxtFtn
84 
85 SwTxtFrm *SwTxtFrm::FindFtnRef( const SwTxtFtn *pFtn )
86 {
87 	SwTxtFrm *pFrm = this;
88 	const sal_Bool bFwd = *pFtn->GetStart() >= GetOfst();
89 	while( pFrm )
90 	{
91 		if( SwFtnBossFrm::FindFtn( pFrm, pFtn ) )
92 			return pFrm;
93 		pFrm = bFwd ? pFrm->GetFollow() :
94 					  pFrm->IsFollow() ? pFrm->FindMaster() : 0;
95 	}
96 	return pFrm;
97 }
98 
99 /*************************************************************************
100  *								CalcFtnFlag()
101  *************************************************************************/
102 
103 #ifndef DBG_UTIL
104 void SwTxtFrm::CalcFtnFlag()
105 #else
106 void SwTxtFrm::CalcFtnFlag( xub_StrLen nStop )//Fuer den Test von SplitFrm
107 #endif
108 {
109 	bFtn = sal_False;
110 
111 	const SwpHints *pHints = GetTxtNode()->GetpSwpHints();
112 	if( !pHints )
113 		return;
114 
115     const sal_uInt16 nSize = pHints->Count();
116 
117 #ifndef DBG_UTIL
118 	const xub_StrLen nEnd = GetFollow() ? GetFollow()->GetOfst() : STRING_LEN;
119 #else
120 	const xub_StrLen nEnd = nStop != STRING_LEN ? nStop
121 						: GetFollow() ? GetFollow()->GetOfst() : STRING_LEN;
122 #endif
123 
124     for ( sal_uInt16 i = 0; i < nSize; ++i )
125     {
126 		const SwTxtAttr *pHt = (*pHints)[i];
127 		if ( pHt->Which() == RES_TXTATR_FTN )
128 		{
129 			const xub_StrLen nIdx = *pHt->GetStart();
130 			if ( nEnd < nIdx )
131 				break;
132 			if( GetOfst() <= nIdx )
133 			{
134 				bFtn = sal_True;
135 				break;
136 			}
137 		}
138 	}
139 }
140 
141 /*************************************************************************
142  *								CalcPrepFtnAdjust()
143  *************************************************************************/
144 
145 sal_Bool SwTxtFrm::CalcPrepFtnAdjust()
146 {
147 	ASSERT( HasFtn(), "Wer ruft mich da?" );
148 	SwFtnBossFrm *pBoss = FindFtnBossFrm( sal_True );
149 	const SwFtnFrm *pFtn = pBoss->FindFirstFtn( this );
150 	if( pFtn && FTNPOS_CHAPTER != GetNode()->GetDoc()->GetFtnInfo().ePos &&
151 		( !pBoss->GetUpper()->IsSctFrm() ||
152 		!((SwSectionFrm*)pBoss->GetUpper())->IsFtnAtEnd() ) )
153 	{
154 		const SwFtnContFrm *pCont = pBoss->FindFtnCont();
155 		sal_Bool bReArrange = sal_True;
156 
157         SWRECTFN( this )
158         if ( pCont && (*fnRect->fnYDiff)( (pCont->Frm().*fnRect->fnGetTop)(),
159                                           (Frm().*fnRect->fnGetBottom)() ) > 0 )
160 		{
161             pBoss->RearrangeFtns( (Frm().*fnRect->fnGetBottom)(), sal_False,
162 								  pFtn->GetAttr() );
163 			ValidateBodyFrm();
164 			ValidateFrm();
165 			pFtn = pBoss->FindFirstFtn( this );
166 		}
167 		else
168 			bReArrange = sal_False;
169 		if( !pCont || !pFtn || bReArrange != (pFtn->FindFtnBossFrm() == pBoss) )
170 		{
171 			SwTxtFormatInfo aInf( this );
172 			SwTxtFormatter aLine( this, &aInf );
173 			aLine.TruncLines();
174 			SetPara( 0 );		//Wird ggf. geloescht!
175 			ResetPreps();
176 			return sal_False;
177 		}
178 	}
179 	return sal_True;
180 }
181 
182 
183 /*************************************************************************
184  *                      lcl_GetFtnLower()
185  *
186  * Local helper function. Checks if nLower should be taken as the boundary
187  * for the footnote.
188  *************************************************************************/
189 
190 SwTwips lcl_GetFtnLower( const SwTxtFrm* pFrm, SwTwips nLower )
191 {
192     // nLower is an absolute value. It denotes the bottom of the line
193     // containing the footnote.
194     SWRECTFN( pFrm )
195 
196     ASSERT( !pFrm->IsVertical() || !pFrm->IsSwapped(),
197             "lcl_GetFtnLower with swapped frame" );
198 
199     SwTwips nAdd;
200     SwTwips nRet = nLower;
201 
202     //
203     // Check if text is inside a table.
204     //
205     if ( pFrm->IsInTab() )
206     {
207         //
208         // If pFrm is inside a table, we have to check if
209         // a) The table is not allowed to split or
210         // b) The table row is not allowed to split
211         //
212         // Inside a table, there are no footnotes,
213         // see SwFrm::FindFtnBossFrm. So we don't have to check
214         // the case that pFrm is inside a (footnote collecting) section
215         // within the table.
216         //
217         const SwFrm* pRow = pFrm;
218         while( !pRow->IsRowFrm() || !pRow->GetUpper()->IsTabFrm() )
219             pRow = pRow->GetUpper();
220         const SwTabFrm* pTabFrm = (SwTabFrm*)pRow->GetUpper();
221 
222         ASSERT( pTabFrm && pRow &&
223                 pRow->GetUpper()->IsTabFrm(), "Upper of row should be tab" )
224 
225         const sal_Bool bDontSplit = !pTabFrm->IsFollow() &&
226                                 !pTabFrm->IsLayoutSplitAllowed();
227 
228         SwTwips nMin = 0;
229         if ( bDontSplit )
230             nMin = (pTabFrm->Frm().*fnRect->fnGetBottom)();
231         else if ( !((SwRowFrm*)pRow)->IsRowSplitAllowed() )
232             nMin = (pRow->Frm().*fnRect->fnGetBottom)();
233 
234         if ( nMin && (*fnRect->fnYDiff)( nMin, nLower ) > 0 )
235             nRet = nMin;
236 
237         nAdd = (pRow->GetUpper()->*fnRect->fnGetBottomMargin)();
238     }
239     else
240         nAdd = (pFrm->*fnRect->fnGetBottomMargin)();
241 
242     if( nAdd > 0 )
243     {
244         if ( bVert )
245             nRet -= nAdd;
246         else
247             nRet += nAdd;
248     }
249 
250     // #i10770#: If there are fly frames anchored at previous paragraphs,
251     // the deadline should consider their lower borders.
252     const SwFrm* pStartFrm = pFrm->GetUpper()->GetLower();
253     ASSERT( pStartFrm, "Upper has no lower" )
254     SwTwips nFlyLower = bVert ? LONG_MAX : 0;
255     while ( pStartFrm != pFrm )
256     {
257         ASSERT( pStartFrm, "Frame chain is broken" )
258         if ( pStartFrm->GetDrawObjs() )
259         {
260             const SwSortedObjs &rObjs = *pStartFrm->GetDrawObjs();
261             for ( sal_uInt16 i = 0; i < rObjs.Count(); ++i )
262             {
263                 SwAnchoredObject* pAnchoredObj = rObjs[i];
264                 SwRect aRect( pAnchoredObj->GetObjRect() );
265 
266                 if ( !pAnchoredObj->ISA(SwFlyFrm) ||
267                      static_cast<SwFlyFrm*>(pAnchoredObj)->IsValid() )
268                 {
269                     const SwTwips nBottom = (aRect.*fnRect->fnGetBottom)();
270                     if ( (*fnRect->fnYDiff)( nBottom, nFlyLower ) > 0 )
271                         nFlyLower = nBottom;
272                 }
273             }
274         }
275 
276         pStartFrm = pStartFrm->GetNext();
277     }
278 
279     if ( bVert )
280         nRet = Min( nRet, nFlyLower );
281     else
282         nRet = Max( nRet, nFlyLower );
283 
284     return nRet;
285 }
286 
287 
288 /*************************************************************************
289  *						SwTxtFrm::GetFtnLine()
290  *************************************************************************/
291 
292 SwTwips SwTxtFrm::GetFtnLine( const SwTxtFtn *pFtn ) const
293 {
294     ASSERT( ! IsVertical() || ! IsSwapped(),
295             "SwTxtFrm::GetFtnLine with swapped frame" )
296 
297 	SwTxtFrm *pThis = (SwTxtFrm*)this;
298 
299 	if( !HasPara() )
300 	{
301         // #109071# GetFormatted() does not work here, bacause most probably
302         // the frame is currently locked. We return the previous value.
303         return pThis->mnFtnLine > 0 ?
304                pThis->mnFtnLine :
305                IsVertical() ? Frm().Left() : Frm().Bottom();
306 	}
307 
308     SWAP_IF_NOT_SWAPPED( this )
309 
310 	SwTxtInfo aInf( pThis );
311 	SwTxtIter aLine( pThis, &aInf );
312 	const xub_StrLen nPos = *pFtn->GetStart();
313 	aLine.CharToLine( nPos );
314 
315     SwTwips nRet = aLine.Y() + SwTwips(aLine.GetLineHeight());
316     if( IsVertical() )
317         nRet = SwitchHorizontalToVertical( nRet );
318 
319     UNDO_SWAP( this )
320 
321     nRet = lcl_GetFtnLower( pThis, nRet );
322 
323     pThis->mnFtnLine = nRet;
324     return nRet;
325 }
326 
327 /*************************************************************************
328  *						SwTxtFrm::GetFtnRstHeight()
329  *************************************************************************/
330 
331 // Ermittelt die max. erreichbare Hoehe des TxtFrm im Ftn-Bereich.
332 // Sie wird eingeschraenkt durch den unteren Rand der Zeile mit
333 // der Ftn-Referenz.
334 
335 SwTwips SwTxtFrm::_GetFtnFrmHeight() const
336 {
337 	ASSERT( !IsFollow() && IsInFtn(), "SwTxtFrm::SetFtnLine: moon walk" );
338 
339 	const SwFtnFrm *pFtnFrm = FindFtnFrm();
340 	const SwTxtFrm *pRef = (const SwTxtFrm *)pFtnFrm->GetRef();
341 	const SwFtnBossFrm *pBoss = FindFtnBossFrm();
342 	if( pBoss != pRef->FindFtnBossFrm( !pFtnFrm->GetAttr()->
343 										GetFtn().IsEndNote() ) )
344 		return 0;
345 
346     SWAP_IF_SWAPPED( this )
347 
348 	SwTwips nHeight = pRef->IsInFtnConnect() ?
349                             1 : pRef->GetFtnLine( pFtnFrm->GetAttr() );
350 	if( nHeight )
351 	{
352 		// So komisch es aussehen mag: Die erste Ftn auf der Seite darf sich
353 		// nicht mit der Ftn-Referenz beruehren, wenn wir im Ftn-Bereich Text
354 		// eingeben.
355 		const SwFrm *pCont = pFtnFrm->GetUpper();
356 		//Hoehe innerhalb des Cont, die ich mir 'eh noch genehmigen darf.
357         SWRECTFN( pCont )
358         SwTwips nTmp = (*fnRect->fnYDiff)( (pCont->*fnRect->fnGetPrtBottom)(),
359                                            (Frm().*fnRect->fnGetTop)() );
360 
361 #ifdef DBG_UTIL
362 		if( nTmp < 0 )
363 		{
364 			sal_Bool bInvalidPos = sal_False;
365             const SwLayoutFrm* pTmp = GetUpper();
366 			while( !bInvalidPos && pTmp )
367 			{
368                 bInvalidPos = !pTmp->GetValidPosFlag() ||
369                                !pTmp->Lower()->GetValidPosFlag();
370 				if( pTmp == pCont )
371 					break;
372 				pTmp = pTmp->GetUpper();
373 			}
374 			ASSERT( bInvalidPos, "Hanging below FtnCont" );
375 		}
376 #endif
377 
378         if ( (*fnRect->fnYDiff)( (pCont->Frm().*fnRect->fnGetTop)(), nHeight) > 0 )
379 		{
380 			//Wachstumspotential den Containers.
381 			if ( !pRef->IsInFtnConnect() )
382 			{
383 				SwSaveFtnHeight aSave( (SwFtnBossFrm*)pBoss, nHeight  );
384                 nHeight = ((SwFtnContFrm*)pCont)->Grow( LONG_MAX, sal_True );
385 			}
386 			else
387                 nHeight = ((SwFtnContFrm*)pCont)->Grow( LONG_MAX, sal_True );
388 
389 			nHeight += nTmp;
390 			if( nHeight < 0 )
391 				nHeight = 0;
392 		}
393 		else
394 		{   // The container has to shrink
395             nTmp += (*fnRect->fnYDiff)( (pCont->Frm().*fnRect->fnGetTop)(), nHeight);
396 			if( nTmp > 0 )
397 				nHeight = nTmp;
398 			else
399 				nHeight = 0;
400 		}
401 	}
402 
403     UNDO_SWAP( this )
404 
405 	return nHeight;
406 }
407 
408 /*************************************************************************
409  *						SwTxtFrm::FindQuoVadisFrm()
410  *************************************************************************/
411 
412 SwTxtFrm *SwTxtFrm::FindQuoVadisFrm()
413 {
414 	// Erstmal feststellen, ob wir in einem FtnFrm stehen:
415 	if( GetIndPrev() || !IsInFtn() )
416 		return 0;
417 
418 	// Zum Vorgaenger-FtnFrm
419 	SwFtnFrm *pFtnFrm = FindFtnFrm()->GetMaster();
420 	if( !pFtnFrm )
421 		return 0;
422 
423 	// Nun den letzten Cntnt:
424 	const SwCntntFrm *pCnt = pFtnFrm->ContainsCntnt();
425 	if( !pCnt )
426 		return NULL;
427 	const SwCntntFrm *pLast;
428 	do
429 	{	pLast = pCnt;
430 		pCnt = pCnt->GetNextCntntFrm();
431 	} while( pCnt && pFtnFrm->IsAnLower( pCnt ) );
432 	return (SwTxtFrm*)pLast;
433 }
434 
435 /*************************************************************************
436  *						SwTxtFrm::RemoveFtn()
437  *************************************************************************/
438 
439 void SwTxtFrm::RemoveFtn( const xub_StrLen nStart, const xub_StrLen nLen )
440 {
441 	if ( !IsFtnAllowed() )
442 		return;
443 
444 	SwpHints *pHints = GetTxtNode()->GetpSwpHints();
445 	if( !pHints )
446 		return;
447 
448 	sal_Bool bRollBack = nLen != STRING_LEN;
449 	sal_uInt16 nSize = pHints->Count();
450 	xub_StrLen nEnd;
451 	SwTxtFrm* pSource;
452 	if( bRollBack )
453 	{
454 		nEnd = nStart + nLen;
455 		pSource = GetFollow();
456 		if( !pSource )
457 			return;
458 	}
459 	else
460 	{
461 		nEnd = STRING_LEN;
462 		pSource = this;
463 	}
464 
465 	if( nSize )
466 	{
467 		SwPageFrm* pUpdate = NULL;
468 		sal_Bool bRemove = sal_False;
469 		SwFtnBossFrm *pFtnBoss = 0;
470 		SwFtnBossFrm *pEndBoss = 0;
471 		sal_Bool bFtnEndDoc
472 			= FTNPOS_CHAPTER == GetNode()->GetDoc()->GetFtnInfo().ePos;
473         for ( sal_uInt16 i = nSize; i; )
474         {
475 			SwTxtAttr *pHt = pHints->GetTextHint(--i);
476 			if ( RES_TXTATR_FTN != pHt->Which() )
477 				continue;
478 
479 			const xub_StrLen nIdx = *pHt->GetStart();
480 			if( nStart > nIdx )
481 				break;
482 
483 			if( nEnd >= nIdx )
484 			{
485 				SwTxtFtn *pFtn = (SwTxtFtn*)pHt;
486 				sal_Bool bEndn = pFtn->GetFtn().IsEndNote();
487 
488 				if( bEndn )
489 				{
490 					if( !pEndBoss )
491 						pEndBoss = pSource->FindFtnBossFrm();
492 				}
493 				else
494 				{
495 					if( !pFtnBoss )
496 					{
497 						pFtnBoss = pSource->FindFtnBossFrm( sal_True );
498 						if( pFtnBoss->GetUpper()->IsSctFrm() )
499 						{
500 							SwSectionFrm* pSect = (SwSectionFrm*)
501 												  pFtnBoss->GetUpper();
502 							if( pSect->IsFtnAtEnd() )
503 								bFtnEndDoc = sal_False;
504 						}
505 					}
506 				}
507 
508 				// Wir loeschen nicht, sondern wollen die Ftn verschieben.
509 				// Drei Faelle koennen auftreten:
510 				// 1) Es gibt weder Follow noch PrevFollow
511 				//	  -> RemoveFtn()  (vielleicht sogar ein ASSERT wert)
512 				// 2) nStart > GetOfst, ich habe einen Follow
513 				//	  -> Ftn wandert in den Follow
514 				// 3) nStart < GetOfst, ich bin ein Follow
515 				//	  -> Ftn wandert in den PrevFollow
516 				// beide muessen auf einer Seite/in einer Spalte stehen.
517 
518 				SwFtnFrm *pFtnFrm = bEndn ? pEndBoss->FindFtn( pSource, pFtn ) :
519 											pFtnBoss->FindFtn( pSource, pFtn );
520 
521 				if( pFtnFrm )
522 				{
523 					const sal_Bool bEndDoc = bEndn ? sal_True : bFtnEndDoc;
524 					if( bRollBack )
525 					{
526 						while ( pFtnFrm )
527 						{
528 							pFtnFrm->SetRef( this );
529 							pFtnFrm = pFtnFrm->GetFollow();
530 							SetFtn( sal_True );
531 						}
532 					}
533 					else if( GetFollow() )
534 					{
535 						SwCntntFrm *pDest = GetFollow();
536 						while( pDest->GetFollow() && ((SwTxtFrm*)pDest->
537 							   GetFollow())->GetOfst() <= nIdx )
538 							pDest = pDest->GetFollow();
539 						ASSERT( !pDest->FindFtnBossFrm( !bEndn )->FindFtn(
540 							pDest,pFtn),"SwTxtFrm::RemoveFtn: footnote exists");
541 
542 						//Nicht ummelden sondern immer Moven.
543                         // OD 08.11.2002 #104840# - use <SwlayoutFrm::IsBefore(::)>
544                         if ( bEndDoc ||
545                              !pFtnFrm->FindFtnBossFrm()->IsBefore( pDest->FindFtnBossFrm( !bEndn ) )
546                            )
547 						{
548 							SwPageFrm* pTmp = pFtnFrm->FindPageFrm();
549 							if( pUpdate && pUpdate != pTmp )
550 								pUpdate->UpdateFtnNum();
551 							pUpdate = pTmp;
552 							while ( pFtnFrm )
553 							{
554 								pFtnFrm->SetRef( pDest );
555 								pFtnFrm = pFtnFrm->GetFollow();
556 							}
557 						}
558 						else
559 						{
560 							if( bEndn )
561 								pEndBoss->MoveFtns( this, pDest, pFtn );
562 							else
563 								pFtnBoss->MoveFtns( this, pDest, pFtn );
564 							bRemove = sal_True;
565 						}
566 						((SwTxtFrm*)pDest)->SetFtn( sal_True );
567 
568 						ASSERT( pDest->FindFtnBossFrm( !bEndn )->FindFtn( pDest,
569 						   pFtn),"SwTxtFrm::RemoveFtn: footnote ChgRef failed");
570 					}
571 					else
572 					{
573                         if( !bEndDoc || ( bEndn && pEndBoss->IsInSct() &&
574                             !SwLayouter::Collecting( GetNode()->GetDoc(),
575                             pEndBoss->FindSctFrm(), NULL ) ) )
576 						{
577 							if( bEndn )
578 								pEndBoss->RemoveFtn( this, pFtn );
579 							else
580 								pFtnBoss->RemoveFtn( this, pFtn );
581 							bRemove = bRemove || !bEndDoc;
582 							ASSERT( bEndn ? !pEndBoss->FindFtn( this, pFtn ) :
583 									!pFtnBoss->FindFtn( this, pFtn ),
584 							"SwTxtFrm::RemoveFtn: can't get off that footnote" );
585 						}
586 					}
587 				}
588 			}
589 		}
590 		if( pUpdate )
591 			pUpdate->UpdateFtnNum();
592 		// Wir bringen die Oszillation zum stehen:
593 		if( bRemove && !bFtnEndDoc && HasPara() )
594 		{
595 			ValidateBodyFrm();
596 			ValidateFrm();
597 		}
598 	}
599 	// Folgendes Problem: Aus dem FindBreak heraus wird das RemoveFtn aufgerufen,
600 	// weil die letzte Zeile an den Follow abgegeben werden soll. Der Offset
601 	// des Follows ist aber veraltet, er wird demnaechst gesetzt. CalcFntFlag ist
602 	// auf einen richtigen Follow-Offset angewiesen. Deshalb wird hier kurzfristig
603 	// der Follow-Offset manipuliert.
604 	xub_StrLen nOldOfst = STRING_LEN;
605 	if( HasFollow() && nStart > GetOfst() )
606 	{
607 		nOldOfst = GetFollow()->GetOfst();
608 		GetFollow()->ManipOfst( nStart + ( bRollBack ? nLen : 0 ) );
609 	}
610 	pSource->CalcFtnFlag();
611 	if( nOldOfst < STRING_LEN )
612 		GetFollow()->ManipOfst( nOldOfst );
613 }
614 
615 /*************************************************************************
616  *						SwTxtFormatter::ConnectFtn()
617  *************************************************************************/
618 // sal_False, wenn irgendetwas schief gegangen ist.
619 // Es gibt eigentlich nur zwei Moeglichkeiten:
620 // a) Die Ftn ist bereits vorhanden
621 // => dann wird sie gemoved, wenn ein anderer pSrcFrm gefunden wurde
622 // b) Die Ftn ist nicht vorhanden
623 // => dann wird sie fuer uns angelegt.
624 // Ob die Ftn schliesslich auf unserer Spalte/Seite landet oder nicht,
625 // spielt in diesem Zusammenhang keine Rolle.
626 // Optimierungen bei Endnoten.
627 // Noch ein Problem: wenn die Deadline im Ftn-Bereich liegt, muss die
628 // Ftn verschoben werden.
629 
630 void SwTxtFrm::ConnectFtn( SwTxtFtn *pFtn, const SwTwips nDeadLine )
631 {
632     ASSERT( !IsVertical() || !IsSwapped(),
633             "SwTxtFrm::ConnectFtn with swapped frame" );
634 
635 	bFtn = sal_True;
636 	bInFtnConnect = sal_True;	//Bloss zuruecksetzen!
637 	sal_Bool bEnd = pFtn->GetFtn().IsEndNote();
638 
639     //
640     // We want to store this value, because it is needed as a fallback
641     // in GetFtnLine(), if there is no paragraph information available
642     //
643     mnFtnLine = nDeadLine;
644 
645 	// Wir brauchen immer einen Boss (Spalte/Seite)
646 	SwSectionFrm *pSect;
647 	SwCntntFrm *pCntnt = this;
648 	if( bEnd && IsInSct() )
649 	{
650 		pSect = FindSctFrm();
651 		if( pSect->IsEndnAtEnd() )
652 			pCntnt = pSect->FindLastCntnt( FINDMODE_ENDNOTE );
653 		if( !pCntnt )
654 			pCntnt = this;
655 	}
656 
657 	SwFtnBossFrm *pBoss = pCntnt->FindFtnBossFrm( !bEnd );
658 
659 #if OSL_DEBUG_LEVEL > 1
660 	SwTwips nRstHeight = GetRstHeight();
661 #endif
662 
663 	pSect = pBoss->FindSctFrm();
664 	sal_Bool bDocEnd = bEnd ? !( pSect && pSect->IsEndnAtEnd() ) :
665 				   ( !( pSect && pSect->IsFtnAtEnd() ) &&
666 		  			 FTNPOS_CHAPTER == GetNode()->GetDoc()->GetFtnInfo().ePos );
667 	//Ftn kann beim Follow angemeldet sein.
668 	SwCntntFrm *pSrcFrm = FindFtnRef( pFtn );
669 
670 	if( bDocEnd )
671 	{
672 		if( pSect && pSrcFrm )
673 		{
674 			SwFtnFrm *pFtnFrm = pBoss->FindFtn( pSrcFrm, pFtn );
675 			if( pFtnFrm && pFtnFrm->IsInSct() )
676 			{
677 				pBoss->RemoveFtn( pSrcFrm, pFtn );
678 				pSrcFrm = 0;
679 			}
680 		}
681 	}
682 	else if( bEnd && pSect )
683 	{
684 		SwFtnFrm *pFtnFrm = pSrcFrm ? pBoss->FindFtn( pSrcFrm, pFtn ) : NULL;
685 		if( pFtnFrm && !pFtnFrm->GetUpper() )
686 			pFtnFrm = NULL;
687 		SwDoc *pDoc = GetNode()->GetDoc();
688 		if( SwLayouter::Collecting( pDoc, pSect, pFtnFrm ) )
689 		{
690 			if( !pSrcFrm )
691 			{
692 				SwFtnFrm *pNew = new SwFtnFrm(pDoc->GetDfltFrmFmt(),this,this,pFtn);
693 			 	SwNodeIndex aIdx( *pFtn->GetStartNode(), 1 );
694 			 	::_InsertCnt( pNew, pDoc, aIdx.GetIndex() );
695                 GetNode()->getIDocumentLayoutAccess()->GetLayouter()->CollectEndnote( pNew );
696 			}
697 			else if( pSrcFrm != this )
698 				pBoss->ChangeFtnRef( pSrcFrm, pFtn, this );
699 			bInFtnConnect = sal_False;
700 			return;
701 		}
702 		else if( pSrcFrm )
703 		{
704 			SwFtnBossFrm *pFtnBoss = pFtnFrm->FindFtnBossFrm();
705 			if( !pFtnBoss->IsInSct() ||
706 				pFtnBoss->ImplFindSctFrm()->GetSection()!=pSect->GetSection() )
707 			{
708 				pBoss->RemoveFtn( pSrcFrm, pFtn );
709 				pSrcFrm = 0;
710 			}
711 		}
712 	}
713 
714 	if( bDocEnd || bEnd )
715 	{
716 		if( !pSrcFrm )
717 			pBoss->AppendFtn( this, pFtn );
718 		else if( pSrcFrm != this )
719 			pBoss->ChangeFtnRef( pSrcFrm, pFtn, this );
720 		bInFtnConnect = sal_False;
721 		return;
722 	}
723 
724 	SwSaveFtnHeight aHeight( pBoss, nDeadLine );
725 
726 	if( !pSrcFrm )		// Es wurde ueberhaupt keine Ftn gefunden.
727 		pBoss->AppendFtn( this, pFtn );
728 	else
729 	{
730 		SwFtnFrm *pFtnFrm = pBoss->FindFtn( pSrcFrm, pFtn );
731 		SwFtnBossFrm *pFtnBoss = pFtnFrm->FindFtnBossFrm();
732 
733 		sal_Bool bBrutal = sal_False;
734 
735 		if( pFtnBoss == pBoss )	// Ref und Ftn sind auf der selben Seite/Spalte.
736 		{
737 			SwFrm *pCont = pFtnFrm->GetUpper();
738 
739             SWRECTFN ( pCont )
740             long nDiff = (*fnRect->fnYDiff)( (pCont->Frm().*fnRect->fnGetTop)(),
741                                              nDeadLine );
742 
743             if( nDiff >= 0 )
744 			{
745 				//Wenn die Fussnote bei einem Follow angemeldet ist, so ist
746 				//es jetzt an der Zeit sie umzumelden.
747 				if ( pSrcFrm != this )
748 					pBoss->ChangeFtnRef( pSrcFrm, pFtn, this );
749 				//Es steht Platz zur Verfuegung, also kann die Fussnote evtl.
750 				//wachsen.
751                 if ( pFtnFrm->GetFollow() && nDiff > 0 )
752 				{
753                     SwTwips nHeight = (pCont->Frm().*fnRect->fnGetHeight)();
754 					pBoss->RearrangeFtns( nDeadLine, sal_False, pFtn );
755 					ValidateBodyFrm();
756 					ValidateFrm();
757 					ViewShell *pSh = getRootFrm()->GetCurrShell();
758                     if ( pSh && nHeight == (pCont->Frm().*fnRect->fnGetHeight)() )
759 						//Damit uns nix durch die Lappen geht.
760 						pSh->InvalidateWindows( pCont->Frm() );
761 				}
762 				bInFtnConnect = sal_False;
763 				return;
764 			}
765 			else
766 				bBrutal = sal_True;
767 		}
768 		else
769 		{
770 			// Ref und Ftn sind nicht auf einer Seite, Move-Versuch ist noetig.
771             SwFrm* pTmp = this;
772 			while( pTmp->GetNext() && pSrcFrm != pTmp )
773 				pTmp = pTmp->GetNext();
774 			if( pSrcFrm == pTmp )
775 				bBrutal = sal_True;
776 			else
777 			{   // Wenn unser Boss in einem spaltigen Bereich sitzt, es aber auf
778 				// der Seite schon einen FtnContainer gibt, hilft nur die brutale
779 				// Methode
780 				if( pSect && pSect->FindFtnBossFrm( !bEnd )->FindFtnCont() )
781 					bBrutal = sal_True;
782                 // OD 08.11.2002 #104840# - use <SwLayoutFrm::IsBefore(..)>
783                 else if ( !pFtnFrm->GetPrev() ||
784                           pFtnBoss->IsBefore( pBoss )
785                         )
786 				{
787 					SwFtnBossFrm *pSrcBoss = pSrcFrm->FindFtnBossFrm( !bEnd );
788 					pSrcBoss->MoveFtns( pSrcFrm, this, pFtn );
789 				}
790 				else
791 					pBoss->ChangeFtnRef( pSrcFrm, pFtn, this );
792 			}
793 		}
794 
795 		// Die brutale Loesung: Fussnote entfernen und appenden.
796 		// Es muss SetFtnDeadLine() gerufen werden, weil nach
797 		// RemoveFtn die nMaxFtnHeight evtl. besser auf unsere Wuensche
798 		// eingestellt werden kann.
799 		if( bBrutal )
800 		{
801 			pBoss->RemoveFtn( pSrcFrm, pFtn, sal_False );
802 			SwSaveFtnHeight *pHeight = bEnd ? NULL :
803 				new SwSaveFtnHeight( pBoss, nDeadLine );
804 			pBoss->AppendFtn( this, pFtn );
805 			delete pHeight;
806 		}
807 	}
808 
809 	// In spaltigen Bereichen, die noch nicht bis zum Seitenrand gehen,
810 	// ist kein RearrangeFtns sinnvoll, da der Fussnotencontainer noch
811 	// nicht kalkuliert worden ist.
812     if( !pSect || !pSect->Growable() )
813 	{
814 		// Umgebung validieren, um Oszillationen zu verhindern.
815 		SwSaveFtnHeight aNochmal( pBoss, nDeadLine );
816 		ValidateBodyFrm();
817 		pBoss->RearrangeFtns( nDeadLine, sal_True );
818 		ValidateFrm();
819 	}
820 	else if( pSect->IsFtnAtEnd() )
821 	{
822 		ValidateBodyFrm();
823 		ValidateFrm();
824 	}
825 
826 #if OSL_DEBUG_LEVEL > 1
827 	// pFtnFrm kann sich durch Calc veraendert haben ...
828 	SwFtnFrm *pFtnFrm = pBoss->FindFtn( this, pFtn );
829 	if( pFtnFrm && pBoss != pFtnFrm->FindFtnBossFrm( !bEnd ) )
830 	{
831 		int bla = 5;
832         (void)bla;
833 	}
834 	nRstHeight = GetRstHeight();
835 #endif
836 	bInFtnConnect = sal_False;
837 	return;
838 }
839 
840 
841 
842 /*************************************************************************
843  *						SwTxtFormatter::NewFtnPortion()
844  *************************************************************************/
845 
846 // Die Portion fuer die Ftn-Referenz im Text
847 SwFtnPortion *SwTxtFormatter::NewFtnPortion( SwTxtFormatInfo &rInf,
848 											 SwTxtAttr *pHint )
849 {
850     ASSERT( ! pFrm->IsVertical() || pFrm->IsSwapped(),
851             "NewFtnPortion with unswapped frame" );
852 
853 	if( !pFrm->IsFtnAllowed() )
854 		return 0;
855 
856 	SwTxtFtn  *pFtn = (SwTxtFtn*)pHint;
857 	SwFmtFtn& rFtn = (SwFmtFtn&)pFtn->GetFtn();
858 	SwDoc *pDoc = pFrm->GetNode()->GetDoc();
859 
860 	if( rInf.IsTest() )
861 		return new SwFtnPortion( rFtn.GetViewNumStr( *pDoc ), pFrm, pFtn );
862 
863     SWAP_IF_SWAPPED( pFrm )
864 
865 	KSHORT nReal;
866 	{
867 		KSHORT nOldReal = pCurr->GetRealHeight();
868 		KSHORT nOldAscent = pCurr->GetAscent();
869 		KSHORT nOldHeight = pCurr->Height();
870 		((SwTxtFormatter*)this)->CalcRealHeight();
871 		nReal = pCurr->GetRealHeight();
872 		if( nReal < nOldReal )
873 			nReal = nOldReal;
874 		pCurr->SetRealHeight( nOldReal );
875 		pCurr->Height( nOldHeight );
876 		pCurr->SetAscent( nOldAscent );
877 	}
878 
879 	SwTwips nLower = Y() + nReal;
880 
881     const bool bVertical = pFrm->IsVertical();
882     if( bVertical )
883         nLower = pFrm->SwitchHorizontalToVertical( nLower );
884 
885     nLower = lcl_GetFtnLower( pFrm, nLower );
886 
887     //6995: Wir frischen nur auf. Das Connect tut fuer diesen Fall nix
888     //Brauchbares, sondern wuerde stattdessen fuer diesen Fall meist die
889     //Ftn wegwerfen und neu erzeugen.
890 
891 	if( !rInf.IsQuick() )
892 		pFrm->ConnectFtn( pFtn, nLower );
893 
894 	SwTxtFrm *pScrFrm = pFrm->FindFtnRef( pFtn );
895 	SwFtnBossFrm *pBoss = pFrm->FindFtnBossFrm( !rFtn.IsEndNote() );
896 	SwFtnFrm *pFtnFrm = NULL;
897 	if( pScrFrm )
898         pFtnFrm = pBoss->FindFtn( pScrFrm, pFtn );
899 
900     // Wir erkundigen uns, ob durch unser Append irgendeine
901 	// Fussnote noch auf der Seite/Spalte steht. Wenn nicht verschwindet
902 	// auch unsere Zeile. Dies fuehrt zu folgendem erwuenschten
903 	// Verhalten: Ftn1 pass noch auf die Seite/Spalte, Ftn2 nicht mehr.
904 	// Also bleibt die Ftn2-Referenz auf der Seite/Spalte stehen. Die
905 	// Fussnote selbst folgt aber erst auf der naechsten Seite/Spalte.
906 	// Ausnahme: Wenn keine weitere Zeile auf diese Seite/Spalte passt,
907 	// so sollte die Ftn2-Referenz auch auf die naechste wandern.
908 	if( !rFtn.IsEndNote() )
909 	{
910 		SwSectionFrm *pSct = pBoss->FindSctFrm();
911 		sal_Bool bAtSctEnd = pSct && pSct->IsFtnAtEnd();
912 		if( FTNPOS_CHAPTER != pDoc->GetFtnInfo().ePos || bAtSctEnd )
913 		{
914 			SwFrm* pFtnCont = pBoss->FindFtnCont();
915 			// Wenn der Boss in einem Bereich liegt, kann es sich nur um eine
916 			// Spalte dieses Bereichs handeln. Wenn dies nicht die erste Spalte
917 			// ist, duerfen wir ausweichen
918 			if( !pFrm->IsInTab() && ( GetLineNr() > 1 || pFrm->GetPrev() ||
919 				( !bAtSctEnd && pFrm->GetIndPrev() ) ||
920 				( pSct && pBoss->GetPrev() ) ) )
921 			{
922 				if( !pFtnCont )
923 				{
924 					rInf.SetStop( sal_True );
925                     UNDO_SWAP( pFrm )
926 					return 0;
927 				}
928 				else
929 				{
930 					// Es darf keine Fussnotencontainer in spaltigen Bereichen und
931 					// gleichzeitig auf der Seite/Seitenspalte geben
932 					if( pSct && !bAtSctEnd ) // liegt unser Container in einem (spaltigen) Bereich?
933 					{
934 						SwFtnBossFrm* pTmp = pBoss->FindSctFrm()->FindFtnBossFrm( sal_True );
935 						SwFtnContFrm* pFtnC = pTmp->FindFtnCont();
936 						if( pFtnC )
937 						{
938                             SwFtnFrm* pTmpFrm = (SwFtnFrm*)pFtnC->Lower();
939                             if( pTmpFrm && *pTmpFrm < pFtn )
940 							{
941 								rInf.SetStop( sal_True );
942                                 UNDO_SWAP( pFrm )
943 								return 0;
944 							}
945 						}
946 					}
947 					// Ist dies die letzte passende Zeile?
948                     SwTwips nTmpBot = Y() + nReal * 2;
949 
950                     if( bVertical )
951                         nTmpBot = pFrm->SwitchHorizontalToVertical( nTmpBot );
952 
953                     SWRECTFN( pFtnCont )
954 
955                     const long nDiff = (*fnRect->fnYDiff)(
956                                             (pFtnCont->Frm().*fnRect->fnGetTop)(),
957                                              nTmpBot );
958 
959                     if( pScrFrm && nDiff < 0 )
960 					{
961 						if( pFtnFrm )
962 						{
963 							SwFtnBossFrm *pFtnBoss = pFtnFrm->FindFtnBossFrm();
964 							if( pFtnBoss != pBoss )
965 							{
966 								// Wir sind in der letzte Zeile und die Fussnote
967 								// ist auf eine andere Seite gewandert, dann wollen
968 								// wir mit ...
969 								rInf.SetStop( sal_True );
970                                 UNDO_SWAP( pFrm )
971 								return 0;
972 							}
973 						}
974 					}
975 				}
976 			}
977 		}
978 	}
979 	// Endlich: FtnPortion anlegen und raus hier...
980 	SwFtnPortion *pRet = new SwFtnPortion( rFtn.GetViewNumStr( *pDoc ),
981 											pFrm, pFtn, nReal );
982 	rInf.SetFtnInside( sal_True );
983 
984     UNDO_SWAP( pFrm )
985 
986 	return pRet;
987  }
988 
989 /*************************************************************************
990  *						SwTxtFormatter::NewFtnNumPortion()
991  *************************************************************************/
992 
993 // Die Portion fuer die Ftn-Nummerierung im Ftn-Bereich
994 
995 SwNumberPortion *SwTxtFormatter::NewFtnNumPortion( SwTxtFormatInfo &rInf ) const
996 {
997 	ASSERT( pFrm->IsInFtn() && !pFrm->GetIndPrev() && !rInf.IsFtnDone(),
998 			"This is the wrong place for a ftnnumber" );
999 	if( rInf.GetTxtStart() != nStart ||
1000 		rInf.GetTxtStart() != rInf.GetIdx() )
1001 		return 0;
1002 
1003     const SwFtnFrm* pFtnFrm = pFrm->FindFtnFrm();
1004     const SwTxtFtn* pFtn = pFtnFrm->GetAttr();
1005 
1006 	// Aha, wir sind also im Fussnotenbereich
1007 	SwFmtFtn& rFtn = (SwFmtFtn&)pFtn->GetFtn();
1008 
1009 	SwDoc *pDoc = pFrm->GetNode()->GetDoc();
1010 	XubString aFtnTxt( rFtn.GetViewNumStr( *pDoc, sal_True ));
1011 
1012     const SwEndNoteInfo* pInfo;
1013 	if( rFtn.IsEndNote() )
1014 		pInfo = &pDoc->GetEndNoteInfo();
1015 	else
1016 		pInfo = &pDoc->GetFtnInfo();
1017 	const SwAttrSet& rSet = pInfo->GetCharFmt(*pDoc)->GetAttrSet();
1018 
1019 	const SwAttrSet* pParSet = &rInf.GetCharAttr();
1020     const IDocumentSettingAccess* pIDSA = pFrm->GetTxtNode()->getIDocumentSettingAccess();
1021     SwFont *pNumFnt = new SwFont( pParSet, pIDSA );
1022 
1023     // --> FME 2005-02-17 #i37142#
1024     // Underline style of paragraph font should not be considered
1025     // Overline style of paragraph font should not be considered
1026     // Weight style of paragraph font should not be considered
1027     // Posture style of paragraph font should not be considered
1028     // See also #i18463# and SwTxtFormatter::NewNumberPortion()
1029     pNumFnt->SetUnderline( UNDERLINE_NONE );
1030     pNumFnt->SetOverline( UNDERLINE_NONE );
1031     pNumFnt->SetItalic( ITALIC_NONE, SW_LATIN );
1032     pNumFnt->SetItalic( ITALIC_NONE, SW_CJK );
1033     pNumFnt->SetItalic( ITALIC_NONE, SW_CTL );
1034     pNumFnt->SetWeight( WEIGHT_NORMAL, SW_LATIN );
1035     pNumFnt->SetWeight( WEIGHT_NORMAL, SW_CJK );
1036     pNumFnt->SetWeight( WEIGHT_NORMAL, SW_CTL );
1037     // <--
1038 
1039     pNumFnt->SetDiffFnt(&rSet, pIDSA );
1040     pNumFnt->SetVertical( pNumFnt->GetOrientation(), pFrm->IsVertical() );
1041 
1042     SwFtnNumPortion* pNewPor = new SwFtnNumPortion( aFtnTxt, pNumFnt );
1043     pNewPor->SetLeft( !pFrm->IsRightToLeft() );
1044 	return pNewPor;
1045 }
1046 
1047 /*************************************************************************
1048  *					SwTxtFormatter::NewErgoSumPortion()
1049  *************************************************************************/
1050 
1051 XubString lcl_GetPageNumber( const SwPageFrm* pPage )
1052 {
1053 	ASSERT( pPage, "GetPageNumber: Homeless TxtFrm" );
1054 	MSHORT nVirtNum = pPage->GetVirtPageNum();
1055 	const SvxNumberType& rNum = pPage->GetPageDesc()->GetNumType();
1056 	return rNum.GetNumStr( nVirtNum );
1057 }
1058 
1059 SwErgoSumPortion *SwTxtFormatter::NewErgoSumPortion( SwTxtFormatInfo &rInf ) const
1060 {
1061 	// Wir koennen nicht davon ausgehen, dass wir ein Follow sind
1062 	// 7983: GetIdx() nicht nStart
1063 	if( !pFrm->IsInFtn()  || pFrm->GetPrev() ||
1064 		rInf.IsErgoDone() || rInf.GetIdx() != pFrm->GetOfst() ||
1065 		pFrm->ImplFindFtnFrm()->GetAttr()->GetFtn().IsEndNote() )
1066 		return 0;
1067 
1068 	// Aha, wir sind also im Fussnotenbereich
1069 	const SwFtnInfo &rFtnInfo = pFrm->GetNode()->GetDoc()->GetFtnInfo();
1070 	SwTxtFrm *pQuoFrm = pFrm->FindQuoVadisFrm();
1071 	if( !pQuoFrm )
1072 		return 0;
1073 	const SwPageFrm* pPage = pFrm->FindPageFrm();
1074 	const SwPageFrm* pQuoPage = pQuoFrm->FindPageFrm();
1075 	if( pPage == pQuoFrm->FindPageFrm() )
1076 		return 0; // Wenn der QuoVadis auf der selben (spaltigen) Seite steht
1077 	const XubString aPage = lcl_GetPageNumber( pPage );
1078 	SwParaPortion *pPara = pQuoFrm->GetPara();
1079 	if( pPara )
1080 		pPara->SetErgoSumNum( aPage );
1081 	if( !rFtnInfo.aErgoSum.Len() )
1082 		return 0;
1083 	SwErgoSumPortion *pErgo = new SwErgoSumPortion( rFtnInfo.aErgoSum,
1084 								lcl_GetPageNumber( pQuoPage ) );
1085 	return pErgo;
1086 }
1087 
1088 /*************************************************************************
1089  *					SwTxtFormatter::FormatQuoVadis()
1090  *************************************************************************/
1091 
1092 xub_StrLen SwTxtFormatter::FormatQuoVadis( const xub_StrLen nOffset )
1093 {
1094     ASSERT( ! pFrm->IsVertical() || ! pFrm->IsSwapped(),
1095             "SwTxtFormatter::FormatQuoVadis with swapped frame" );
1096 
1097 	if( !pFrm->IsInFtn() || pFrm->ImplFindFtnFrm()->GetAttr()->GetFtn().IsEndNote() )
1098 		return nOffset;
1099 
1100 	const SwFrm* pErgoFrm = pFrm->FindFtnFrm()->GetFollow();
1101 	if( !pErgoFrm && pFrm->HasFollow() )
1102 		pErgoFrm = pFrm->GetFollow();
1103 	if( !pErgoFrm )
1104 		return nOffset;
1105 
1106 	if( pErgoFrm == pFrm->GetNext() )
1107 	{
1108 		SwFrm *pCol = pFrm->FindColFrm();
1109 		while( pCol && !pCol->GetNext() )
1110 			pCol = pCol->GetUpper()->FindColFrm();
1111 		if( pCol )
1112 			return nOffset;
1113 	}
1114 	else
1115 	{
1116 		const SwPageFrm* pPage = pFrm->FindPageFrm();
1117 		const SwPageFrm* pErgoPage = pErgoFrm->FindPageFrm();
1118         if( pPage == pErgoPage )
1119 			return nOffset; // Wenn der ErgoSum auf der selben Seite steht
1120 	}
1121 
1122 	SwTxtFormatInfo &rInf = GetInfo();
1123 	const SwFtnInfo &rFtnInfo = pFrm->GetNode()->GetDoc()->GetFtnInfo();
1124 	if( !rFtnInfo.aQuoVadis.Len() )
1125 		return nOffset;
1126 
1127 	// Ein Wort zu QuoVadis/ErgoSum:
1128 	// Fuer diese Texte wird der am Absatz eingestellte Font verwendet.
1129 	// Wir initialisieren uns also:
1130 //	ResetFont();
1131 	FeedInf( rInf );
1132 	SeekStartAndChg( rInf, sal_True );
1133 	if( GetRedln() && pCurr->HasRedline() )
1134 		GetRedln()->Seek( *pFnt, nOffset, 0 );
1135 
1136 	// Ein fieser Sonderfall: Flyfrms reichen in die Zeile und stehen
1137 	// natuerlich da, wo wir unseren Quovadis Text reinsetzen wollen.
1138 	// Erst mal sehen, ob es so schlimm ist:
1139 	SwLinePortion *pPor = pCurr->GetFirstPortion();
1140 	KSHORT nLastLeft = 0;
1141 	while( pPor )
1142 	{
1143 		if ( pPor->IsFlyPortion() )
1144 			nLastLeft = ( (SwFlyPortion*) pPor)->Fix() +
1145 						( (SwFlyPortion*) pPor)->Width();
1146 		pPor = pPor->GetPortion();
1147 	}
1148 	// Das alte Spiel: wir wollen, dass die Zeile an einer bestimmten
1149 	// Stelle umbricht, also beeinflussen wir die Width.
1150 	// nLastLeft ist jetzt quasi der rechte Rand.
1151 	const KSHORT nOldRealWidth = rInf.RealWidth();
1152 	rInf.RealWidth( nOldRealWidth - nLastLeft );
1153 
1154 	XubString aErgo = lcl_GetPageNumber( pErgoFrm->FindPageFrm() );
1155 	SwQuoVadisPortion *pQuo = new SwQuoVadisPortion(rFtnInfo.aQuoVadis, aErgo );
1156 	pQuo->SetAscent( rInf.GetAscent()  );
1157 	pQuo->Height( rInf.GetTxtHeight() );
1158 	pQuo->Format( rInf );
1159     sal_uInt16 nQuoWidth = pQuo->Width();
1160     SwLinePortion* pCurrPor = pQuo;
1161 
1162     while ( rInf.GetRest() )
1163     {
1164         SwLinePortion* pFollow = rInf.GetRest();
1165 	    rInf.SetRest( 0 );
1166         pCurrPor->Move( rInf );
1167 
1168         ASSERT( pFollow->IsQuoVadisPortion(),
1169                 "Quo Vadis, rest of QuoVadisPortion" )
1170 
1171         // format the rest and append it to the other QuoVadis parts
1172 		pFollow->Format( rInf );
1173         nQuoWidth = nQuoWidth + pFollow->Width();
1174 
1175         pCurrPor->Append( pFollow );
1176         pCurrPor = pFollow;
1177     }
1178 
1179     nLastLeft = nOldRealWidth - nQuoWidth;
1180     Right( Right() - nQuoWidth );
1181 
1182     SWAP_IF_NOT_SWAPPED( pFrm )
1183 
1184 	const xub_StrLen nRet = FormatLine( nStart );
1185 
1186     UNDO_SWAP( pFrm )
1187 
1188 	Right( rInf.Left() + nOldRealWidth - 1 );
1189 
1190 	nLastLeft = nOldRealWidth - pCurr->Width();
1191 	FeedInf( rInf );
1192 
1193 	// Es kann durchaus sein, dass am Ende eine Marginportion steht,
1194 	// die beim erneuten Aufspannen nur Aerger bereiten wuerde.
1195 	pPor = pCurr->FindLastPortion();
1196 	SwGluePortion *pGlue = pPor->IsMarginPortion() ?
1197 		(SwMarginPortion*) pPor : 0;
1198 	if( pGlue )
1199 	{
1200 		pGlue->Height( 0 );
1201 		pGlue->Width( 0 );
1202 		pGlue->SetLen( 0 );
1203 		pGlue->SetAscent( 0 );
1204 		pGlue->SetPortion( NULL );
1205 		pGlue->SetFixWidth(0);
1206 	}
1207 
1208 	// Luxus: Wir sorgen durch das Aufspannen von Glues dafuer,
1209 	// dass der QuoVadis-Text rechts erscheint:
1210     nLastLeft = nLastLeft - nQuoWidth;
1211 	if( nLastLeft )
1212 	{
1213 		if( nLastLeft > pQuo->GetAscent() ) // Mindestabstand
1214 		{
1215 			switch( GetAdjust() )
1216 			{
1217 				case SVX_ADJUST_BLOCK:
1218 				{
1219 					if( !pCurr->GetLen() ||
1220 						CH_BREAK != GetInfo().GetChar(nStart+pCurr->GetLen()-1))
1221 						nLastLeft = pQuo->GetAscent();
1222 					nQuoWidth = nQuoWidth + nLastLeft;
1223 					break;
1224 				}
1225 				case SVX_ADJUST_RIGHT:
1226 				{
1227 					nLastLeft = pQuo->GetAscent();
1228 					nQuoWidth = nQuoWidth + nLastLeft;
1229 					break;
1230 				}
1231 				case SVX_ADJUST_CENTER:
1232 				{
1233 					nQuoWidth = nQuoWidth + pQuo->GetAscent();
1234 					long nDiff = nLastLeft - nQuoWidth;
1235 					if( nDiff < 0 )
1236 					{
1237 						nLastLeft = pQuo->GetAscent();
1238                         nQuoWidth = (sal_uInt16)(-nDiff + nLastLeft);
1239 					}
1240 					else
1241 					{
1242 						nQuoWidth = 0;
1243                         nLastLeft = sal_uInt16(( pQuo->GetAscent() + nDiff ) / 2);
1244 					}
1245 					break;
1246 				}
1247 				default:
1248 					nQuoWidth = nQuoWidth + nLastLeft;
1249 			}
1250 		}
1251 		else
1252 			nQuoWidth = nQuoWidth + nLastLeft;
1253 		if( nLastLeft )
1254 		{
1255 			pGlue = new SwGluePortion(0);
1256 			pGlue->Width( nLastLeft );
1257 			pPor->Append( pGlue );
1258 			pPor = pPor->GetPortion();
1259 		}
1260 	}
1261 
1262 	// Jetzt aber: die QuoVadis-Portion wird angedockt:
1263     pCurrPor = pQuo;
1264     while ( pCurrPor )
1265     {
1266         // pPor->Append deletes the pPortoin pointer of pPor. Therefore
1267         // we have to keep a pointer to the next portion
1268         pQuo = (SwQuoVadisPortion*)pCurrPor->GetPortion();
1269         pPor->Append( pCurrPor );
1270         pPor = pPor->GetPortion();
1271         pCurrPor = pQuo;
1272     }
1273 
1274 	pCurr->Width( pCurr->Width() + KSHORT( nQuoWidth ) );
1275 
1276 	// Und noch einmal adjustieren wegen des Adjustment und nicht zu Letzt
1277 	// wegen folgendem Sonderfall: In der Zeile hat der DummUser durchgaengig
1278 	// einen kleineren Font eingestellt als der vom QuoVadis-Text ...
1279 	CalcAdjustLine( pCurr );
1280 
1281 #if OSL_DEBUG_LEVEL > 1
1282 	if( OPTDBG( rInf ) )
1283 	{
1284 //		  aDbstream << "FormatQuoVadis:" << endl;
1285 //		  pCurr->DebugPortions( aDbstream, rInf.GetTxt(), nStart );
1286 	}
1287 #endif
1288 
1289 	// Uff...
1290 	return nRet;
1291 }
1292 
1293 
1294 /*************************************************************************
1295  *					SwTxtFormatter::MakeDummyLine()
1296  *************************************************************************/
1297 
1298 // MakeDummyLine() erzeugt eine Line, die bis zum unteren Seitenrand
1299 // reicht. DummyLines bzw. DummyPortions sorgen dafuer, dass Oszillationen
1300 // zum stehen kommen, weil Rueckflussmoeglichkeiten genommen werden.
1301 // Sie werden bei absatzgebundenen Frames in Fussnoten und bei Ftn-
1302 // Oszillationen verwendet.
1303 
1304 void SwTxtFormatter::MakeDummyLine()
1305 {
1306 	KSHORT nRstHeight = GetFrmRstHeight();
1307 	if( pCurr && nRstHeight > pCurr->Height() )
1308 	{
1309 		SwLineLayout *pLay = new SwLineLayout;
1310 		nRstHeight = nRstHeight - pCurr->Height();
1311 		pLay->Height( nRstHeight );
1312 		pLay->SetAscent( nRstHeight );
1313 		Insert( pLay );
1314 		Next();
1315 	}
1316 }
1317 
1318 /*************************************************************************
1319  *                      class SwFtnSave
1320   *************************************************************************/
1321 class SwFtnSave
1322 {
1323     SwTxtSizeInfo *pInf;
1324     SwFont       *pFnt;
1325     SwFont       *pOld;
1326 public:
1327     SwFtnSave( const SwTxtSizeInfo &rInf,
1328                const SwTxtFtn *pTxtFtn,
1329                const bool bApplyGivenScriptType,
1330                const sal_uInt8 nGivenScriptType );
1331    ~SwFtnSave();
1332 };
1333 
1334 /*************************************************************************
1335  *                     SwFtnSave::SwFtnSave()
1336  *************************************************************************/
1337 
1338 SwFtnSave::SwFtnSave( const SwTxtSizeInfo &rInf,
1339                       const SwTxtFtn* pTxtFtn,
1340                       const bool bApplyGivenScriptType,
1341                       const sal_uInt8 nGivenScriptType )
1342     : pInf( &((SwTxtSizeInfo&)rInf) )
1343     , pFnt( 0 )
1344     , pOld( 0 )
1345 {
1346 	if( pTxtFtn && rInf.GetTxtFrm() )
1347 	{
1348 		pFnt = ((SwTxtSizeInfo&)rInf).GetFont();
1349 	  	pOld = new SwFont( *pFnt );
1350 		pOld->GetTox() = pFnt->GetTox();
1351 		pFnt->GetTox() = 0;
1352 		SwFmtFtn& rFtn = (SwFmtFtn&)pTxtFtn->GetFtn();
1353 		const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc();
1354 
1355         // --> OD 2009-01-29 #i98418#
1356         if ( bApplyGivenScriptType )
1357         {
1358             pFnt->SetActual( nGivenScriptType );
1359         }
1360         else
1361         {
1362             // examine text and set script
1363             String aTmpStr( rFtn.GetViewNumStr( *pDoc ) );
1364             pFnt->SetActual( SwScriptInfo::WhichFont( 0, &aTmpStr, 0 ) );
1365         }
1366         // <--
1367 
1368         const SwEndNoteInfo* pInfo;
1369 		if( rFtn.IsEndNote() )
1370 			pInfo = &pDoc->GetEndNoteInfo();
1371 		else
1372 			pInfo = &pDoc->GetFtnInfo();
1373 		const SwAttrSet& rSet =	pInfo->GetAnchorCharFmt((SwDoc&)*pDoc)->GetAttrSet();
1374         pFnt->SetDiffFnt( &rSet, rInf.GetTxtFrm()->GetNode()->getIDocumentSettingAccess() );
1375 
1376         // we reduce footnote size, if we are inside a double line portion
1377         if ( ! pOld->GetEscapement() && 50 == pOld->GetPropr() )
1378         {
1379             Size aSize = pFnt->GetSize( pFnt->GetActual() );
1380             pFnt->SetSize( Size( (long)aSize.Width() / 2,
1381                                  (long)aSize.Height() / 2 ),
1382                            pFnt->GetActual() );
1383         }
1384 
1385         // set the correct rotation at the footnote font
1386         const SfxPoolItem* pItem;
1387         if( SFX_ITEM_SET == rSet.GetItemState( RES_CHRATR_ROTATE,
1388 			sal_True, &pItem ))
1389             pFnt->SetVertical( ((SvxCharRotateItem*)pItem)->GetValue(),
1390                                 rInf.GetTxtFrm()->IsVertical() );
1391 
1392         pFnt->ChgPhysFnt( pInf->GetVsh(), *pInf->GetOut() );
1393 
1394 		if( SFX_ITEM_SET == rSet.GetItemState( RES_CHRATR_BACKGROUND,
1395 			sal_True, &pItem ))
1396 			pFnt->SetBackColor( new Color( ((SvxBrushItem*)pItem)->GetColor() ) );
1397 	}
1398 	else
1399 		pFnt = NULL;
1400 }
1401 
1402 /*************************************************************************
1403  *					   SwFtnSave::~SwFtnSave()
1404  *************************************************************************/
1405 
1406 SwFtnSave::~SwFtnSave()
1407 {
1408 	if( pFnt )
1409 	{
1410 		// SwFont zurueckstellen
1411 		*pFnt = *pOld;
1412 		pFnt->GetTox() = pOld->GetTox();
1413         pFnt->ChgPhysFnt( pInf->GetVsh(), *pInf->GetOut() );
1414 		delete pOld;
1415 	}
1416 }
1417 
1418 /*************************************************************************
1419  *						SwFtnPortion::SwFtnPortion()
1420  *************************************************************************/
1421 
1422 SwFtnPortion::SwFtnPortion( const XubString &rExpand, SwTxtFrm *pFrame,
1423                             SwTxtFtn *pFootn, KSHORT nReal )
1424         : SwFldPortion( rExpand, 0 )
1425         , pFrm(pFrame)
1426         , pFtn(pFootn)
1427         , nOrigHeight( nReal )
1428         // --> OD 2009-01-29 #i98418#
1429         , mbPreferredScriptTypeSet( false )
1430         , mnPreferredScriptType( SW_LATIN )
1431         // <--
1432 {
1433 	SetLen(1);
1434 	SetWhichPor( POR_FTN );
1435 }
1436 
1437 /*************************************************************************
1438  *						SwFtnPortion::GetExpTxt()
1439  *************************************************************************/
1440 
1441 sal_Bool SwFtnPortion::GetExpTxt( const SwTxtSizeInfo &, XubString &rTxt ) const
1442 {
1443 	rTxt = aExpand;
1444 	return sal_True;
1445 }
1446 
1447 /*************************************************************************
1448  *				   virtual SwFtnPortion::Format()
1449  *************************************************************************/
1450 
1451 sal_Bool SwFtnPortion::Format( SwTxtFormatInfo &rInf )
1452 {
1453     // --> OD 2009-01-29 #i98418#
1454 //    SwFtnSave aFtnSave( rInf, pFtn );
1455     SwFtnSave aFtnSave( rInf, pFtn, mbPreferredScriptTypeSet, mnPreferredScriptType );
1456     // <--
1457     // the idx is manipulated in SwExpandPortion::Format
1458     // this flag indicates, that a footnote is allowed to trigger
1459     // an underflow during SwTxtGuess::Guess
1460     rInf.SetFakeLineStart( rInf.GetIdx() > rInf.GetLineStart() );
1461     sal_Bool bFull = SwFldPortion::Format( rInf );
1462     rInf.SetFakeLineStart( sal_False );
1463 	SetAscent( rInf.GetAscent() );
1464     Height( rInf.GetTxtHeight() );
1465 	rInf.SetFtnDone( !bFull );
1466 	if( !bFull )
1467 		rInf.SetParaFtn();
1468 	return bFull;
1469 }
1470 
1471 /*************************************************************************
1472  *				 virtual SwFtnPortion::Paint()
1473  *************************************************************************/
1474 
1475 void SwFtnPortion::Paint( const SwTxtPaintInfo &rInf ) const
1476 {
1477     // --> OD 2009-01-29 #i98418#
1478 //    SwFtnSave aFtnSave( rInf, pFtn );
1479     SwFtnSave aFtnSave( rInf, pFtn, mbPreferredScriptTypeSet, mnPreferredScriptType );
1480     // <--
1481 	rInf.DrawViewOpt( *this, POR_FTN );
1482 	SwExpandPortion::Paint( rInf );
1483 }
1484 
1485 /*************************************************************************
1486  *				 virtual SwFtnPortion::GetTxtSize()
1487  *************************************************************************/
1488 
1489 SwPosSize SwFtnPortion::GetTxtSize( const SwTxtSizeInfo &rInfo ) const
1490 {
1491     // --> OD 2009-01-29 #i98418#
1492 //    SwFtnSave aFtnSave( rInfo, pFtn );
1493     SwFtnSave aFtnSave( rInfo, pFtn, mbPreferredScriptTypeSet, mnPreferredScriptType );
1494     // <--
1495 	return SwExpandPortion::GetTxtSize( rInfo );
1496 }
1497 
1498 // --> OD 2009-01-29 #i98418#
1499 void SwFtnPortion::SetPreferredScriptType( sal_uInt8 nPreferredScriptType )
1500 {
1501     mbPreferredScriptTypeSet = true;
1502     mnPreferredScriptType = nPreferredScriptType;
1503 }
1504 // <--
1505 
1506 /*************************************************************************
1507  *						class SwQuoVadisPortion
1508  *************************************************************************/
1509 
1510 SwFldPortion *SwQuoVadisPortion::Clone( const XubString &rExpand ) const
1511 { return new SwQuoVadisPortion( rExpand, aErgo ); }
1512 
1513 SwQuoVadisPortion::SwQuoVadisPortion( const XubString &rExp, const XubString& rStr )
1514 	: SwFldPortion( rExp ), aErgo(rStr)
1515 {
1516 	SetLen(0);
1517 	SetWhichPor( POR_QUOVADIS );
1518 }
1519 
1520 /*************************************************************************
1521  *				   virtual SwQuoVadisPortion::Format()
1522  *************************************************************************/
1523 
1524 sal_Bool SwQuoVadisPortion::Format( SwTxtFormatInfo &rInf )
1525 {
1526 	// erster Versuch, vielleicht passt der Text
1527 	CheckScript( rInf );
1528 	sal_Bool bFull = SwFldPortion::Format( rInf );
1529 	SetLen( 0 );
1530 
1531 	if( bFull )
1532 	{
1533 		// zweiter Versuch, wir kuerzen den String:
1534 		aExpand = XubString( "...", RTL_TEXTENCODING_MS_1252 );
1535 		bFull = SwFldPortion::Format( rInf );
1536 		SetLen( 0 );
1537 		if( bFull  )
1538 			// dritter Versuch, es langt: jetzt wird gestaucht:
1539             Width( sal_uInt16(rInf.Width() - rInf.X()) );
1540 
1541 		// 8317: keine mehrzeiligen Felder bei QuoVadis und ErgoSum
1542 		if( rInf.GetRest() )
1543 		{
1544 			delete rInf.GetRest();
1545 			rInf.SetRest( 0 );
1546 		}
1547 	}
1548 	return bFull;
1549 }
1550 
1551 /*************************************************************************
1552  *				 virtual SwQuoVadisPortion::GetExpTxt()
1553  *************************************************************************/
1554 
1555 sal_Bool SwQuoVadisPortion::GetExpTxt( const SwTxtSizeInfo &, XubString &rTxt ) const
1556 {
1557 	rTxt = aExpand;
1558     // if this QuoVadisPortion has a follow, the follow is responsible for
1559     // the ergo text.
1560     if ( ! HasFollow() )
1561         rTxt += aErgo;
1562 	return sal_True;
1563 }
1564 
1565 /*************************************************************************
1566  *              virtual SwQuoVadisPortion::HandlePortion()
1567  *************************************************************************/
1568 
1569 void SwQuoVadisPortion::HandlePortion( SwPortionHandler& rPH ) const
1570 {
1571     String aString( aExpand );
1572     aString += aErgo;
1573     rPH.Special( GetLen(), aString, GetWhichPor() );
1574 }
1575 
1576 /*************************************************************************
1577  *				 virtual SwQuoVadisPortion::Paint()
1578  *************************************************************************/
1579 
1580 void SwQuoVadisPortion::Paint( const SwTxtPaintInfo &rInf ) const
1581 {
1582 	// Wir wollen _immer_ per DrawStretchText ausgeben,
1583 	// weil nErgo schnell mal wechseln kann.
1584 	if( PrtWidth() )
1585 	{
1586 		rInf.DrawViewOpt( *this, POR_QUOVADIS );
1587         SwTxtSlot aDiffTxt( &rInf, this, true, false );
1588         SwFontSave aSave( rInf, pFnt );
1589 		rInf.DrawText( *this, rInf.GetLen(), sal_True );
1590 	}
1591 }
1592 
1593 /*************************************************************************
1594  *						class SwErgoSumPortion
1595  *************************************************************************/
1596 
1597 SwFldPortion *SwErgoSumPortion::Clone( const XubString &rExpand ) const
1598 {
1599 	UniString aTmp; // = UniString::CreateFromInt32( 0 );
1600 	return new SwErgoSumPortion( rExpand, aTmp );
1601 }
1602 
1603 SwErgoSumPortion::SwErgoSumPortion( const XubString &rExp, const XubString& rStr )
1604 	: SwFldPortion( rExp )
1605 {
1606 	SetLen(0);
1607 	aExpand += rStr;
1608 
1609 	// 7773: sinnvolle Massnahme: ein Blank Abstand zum Text
1610 	aExpand += ' ';
1611 	SetWhichPor( POR_ERGOSUM );
1612 }
1613 
1614 xub_StrLen SwErgoSumPortion::GetCrsrOfst( const KSHORT ) const
1615 {
1616 	return 0;
1617 }
1618 
1619 /*************************************************************************
1620  *				   virtual SwErgoSumPortion::Format()
1621  *************************************************************************/
1622 
1623 sal_Bool SwErgoSumPortion::Format( SwTxtFormatInfo &rInf )
1624 {
1625 	sal_Bool bFull = SwFldPortion::Format( rInf );
1626 	SetLen( 0 );
1627     rInf.SetErgoDone( sal_True );
1628 
1629 	// 8317: keine mehrzeiligen Felder bei QuoVadis und ErgoSum
1630     if( bFull && rInf.GetRest() )
1631     {
1632         delete rInf.GetRest();
1633         rInf.SetRest( 0 );
1634     }
1635 
1636     // We return false in order to get some text into the current line,
1637     // even if it's full (better than looping)
1638     return sal_False;
1639 }
1640 
1641 
1642 /*************************************************************************
1643  *						SwParaPortion::SetErgoSumNum()
1644  *************************************************************************/
1645 
1646 void SwParaPortion::SetErgoSumNum( const XubString& rErgo )
1647 {
1648 	SwLineLayout *pLay = this;
1649 	while( pLay->GetNext() )
1650 	{
1651 		DBG_LOOP;
1652 		pLay = pLay->GetNext();
1653 	}
1654 	SwLinePortion	  *pPor = pLay;
1655 	SwQuoVadisPortion *pQuo = 0;
1656 	while( pPor && !pQuo )
1657 	{
1658 		if ( pPor->IsQuoVadisPortion() )
1659 			pQuo = (SwQuoVadisPortion*)pPor;
1660 		pPor = pPor->GetPortion();
1661 	}
1662 	if( pQuo )
1663 		pQuo->SetNumber( rErgo );
1664 }
1665 
1666 /*************************************************************************
1667  *						SwParaPortion::UpdateQuoVadis()
1668  *
1669  * Wird im SwTxtFrm::Prepare() gerufen
1670  *************************************************************************/
1671 
1672 sal_Bool SwParaPortion::UpdateQuoVadis( const XubString &rQuo )
1673 {
1674 	SwLineLayout *pLay = this;
1675 	while( pLay->GetNext() )
1676 	{
1677 		DBG_LOOP;
1678 		pLay = pLay->GetNext();
1679 	}
1680 	SwLinePortion	  *pPor = pLay;
1681 	SwQuoVadisPortion *pQuo = 0;
1682 	while( pPor && !pQuo )
1683 	{
1684 		if ( pPor->IsQuoVadisPortion() )
1685 			pQuo = (SwQuoVadisPortion*)pPor;
1686 		pPor = pPor->GetPortion();
1687 	}
1688 
1689 	if( !pQuo )
1690 		return sal_False;
1691 
1692 	return pQuo->GetQuoTxt() == rQuo;
1693 }
1694 
1695 
1696 
1697