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