xref: /trunk/main/sw/source/core/text/txtftn.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
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