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