xref: /trunk/main/sw/source/core/text/porfld.cxx (revision ca62e2c2083b5d0995f1245bad6c2edfb455fbec)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sw.hxx"
26 
27 
28 #include <hintids.hxx>
29 
30 #include <com/sun/star/i18n/ScriptType.hdl>
31 #include <vcl/graph.hxx>
32 #include <editeng/brshitem.hxx>
33 #include <vcl/metric.hxx>
34 #include <vcl/outdev.hxx>
35 #include <viewopt.hxx>  // SwViewOptions
36 #include <txtcfg.hxx>
37 #include <SwPortionHandler.hxx>
38 #include <porlay.hxx>
39 #include <porfld.hxx>
40 #include <inftxt.hxx>
41 #include <blink.hxx>    // pBlink
42 #include <frmtool.hxx>  // DrawGraphic
43 #include <viewsh.hxx>
44 #include <docsh.hxx>
45 #include <doc.hxx>
46 #include "rootfrm.hxx"
47 #include <breakit.hxx>
48 #include <porrst.hxx>
49 #include <porftn.hxx>   // SwFtnPortion
50 #include <accessibilityoptions.hxx>
51 #include <editeng/lrspitem.hxx>
52 
53 #include <unicode/ubidi.h>
54 
55 using namespace ::com::sun::star;
56 
57 /*************************************************************************
58  *                      class SwFldPortion
59  *************************************************************************/
60 
61 SwLinePortion *SwFldPortion::Compress()
62 { return (GetLen() || aExpand.Len() || SwLinePortion::Compress()) ? this : 0; }
63 
64 SwFldPortion *SwFldPortion::Clone( const XubString &rExpand ) const
65 {
66     SwFont *pNewFnt;
67     if( 0 != ( pNewFnt = pFnt ) )
68     {
69         pNewFnt = new SwFont( *pFnt );
70     }
71     // --> OD 2009-11-25 #i107143#
72     // pass placeholder property to created <SwFldPortion> instance.
73     SwFldPortion* pClone = new SwFldPortion( rExpand, pNewFnt, bPlaceHolder );
74     // <--
75     pClone->SetNextOffset( nNextOffset );
76     pClone->m_bNoLength = this->m_bNoLength;
77     return pClone;
78 }
79 
80 void SwFldPortion::TakeNextOffset( const SwFldPortion* pFld )
81 {
82     ASSERT( pFld, "TakeNextOffset: Missing Source" );
83     nNextOffset = pFld->GetNextOffset();
84     aExpand.Erase( 0, nNextOffset );
85     bFollow = sal_True;
86 }
87 
88 SwFldPortion::SwFldPortion( const XubString &rExpand, SwFont *pFont, sal_Bool bPlaceHold )
89     : aExpand(rExpand), pFnt(pFont), nNextOffset(0), nNextScriptChg(STRING_LEN), nViewWidth(0),
90       bFollow( sal_False ), bHasFollow( sal_False ), bPlaceHolder( bPlaceHold )
91     , m_bNoLength( sal_False )
92 {
93     SetWhichPor( POR_FLD );
94     //IAccessibility2 Implementation 2009-----
95     m_nAttrFldType = 0;
96     //-----IAccessibility2 Implementation 2009
97 }
98 
99 SwFldPortion::SwFldPortion( const SwFldPortion& rFld )
100     : SwExpandPortion( rFld ),
101       aExpand( rFld.GetExp() ),
102       nNextOffset( rFld.GetNextOffset() ),
103       nNextScriptChg( rFld.GetNextScriptChg() ),
104       bFollow( rFld.IsFollow() ),
105       bLeft( rFld.IsLeft() ),
106       bHide( rFld.IsHide() ),
107       bCenter( rFld.IsCenter() ),
108       bHasFollow( rFld.HasFollow() ),
109       bPlaceHolder( rFld.bPlaceHolder )
110     , m_bNoLength( rFld.m_bNoLength )
111 {
112     if ( rFld.HasFont() )
113         pFnt = new SwFont( *rFld.GetFont() );
114     else
115         pFnt = 0;
116 
117     SetWhichPor( POR_FLD );
118 }
119 
120 SwFldPortion::~SwFldPortion()
121 {
122     delete pFnt;
123     if( pBlink )
124         pBlink->Delete( this );
125 }
126 
127 /*************************************************************************
128  *               virtual SwFldPortion::GetViewWidth()
129  *************************************************************************/
130 
131 KSHORT SwFldPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const
132 {
133     // Wir stehen zwar im const, aber nViewWidth sollte erst im letzten
134     // Moment errechnet werden:
135     SwFldPortion* pThis = (SwFldPortion*)this;
136     if( !Width() && rInf.OnWin() && !rInf.GetOpt().IsPagePreview() &&
137             !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() )
138     {
139         if( !nViewWidth )
140             pThis->nViewWidth = rInf.GetTxtSize( ' ' ).Width();
141     }
142     else
143         pThis->nViewWidth = 0;
144     return nViewWidth;
145 }
146 
147 /*************************************************************************
148  *                 virtual SwFldPortion::Format()
149  *************************************************************************/
150 
151 // 8653: in keinem Fall nur SetLen(0);
152 
153 /*************************************************************************
154  *   Hilfsklasse SwFldSlot
155  **************************************************************************/
156 
157 class SwFldSlot
158 {
159     const XubString *pOldTxt;
160     XubString aTxt;
161     xub_StrLen nIdx;
162     xub_StrLen nLen;
163     sal_Bool bOn;
164     SwTxtFormatInfo *pInf;
165 public:
166     SwFldSlot( const SwTxtFormatInfo* pNew, const SwFldPortion *pPor );
167     ~SwFldSlot();
168 };
169 
170 SwFldSlot::SwFldSlot( const SwTxtFormatInfo* pNew, const SwFldPortion *pPor )
171 {
172     bOn = pPor->GetExpTxt( *pNew, aTxt );
173 
174     // Der Text wird ausgetauscht...
175     if( bOn )
176     {
177         pInf = (SwTxtFormatInfo*)pNew;
178         nIdx = pInf->GetIdx();
179         nLen = pInf->GetLen();
180         pOldTxt = &(pInf->GetTxt());
181         pInf->SetLen( aTxt.Len() );
182         if( pPor->IsFollow() )
183         {
184             pInf->SetFakeLineStart( nIdx > pInf->GetLineStart() );
185             pInf->SetIdx( 0 );
186         }
187         else
188         {
189             XubString aTmp( aTxt );
190             aTxt = *pOldTxt;
191             aTxt.Erase( nIdx, 1 );
192             aTxt.Insert( aTmp, nIdx );
193         }
194         pInf->SetTxt( aTxt );
195     }
196 }
197 
198 SwFldSlot::~SwFldSlot()
199 {
200     if( bOn )
201     {
202         pInf->SetTxt( *pOldTxt );
203         pInf->SetIdx( nIdx );
204         pInf->SetLen( nLen );
205         pInf->SetFakeLineStart( sal_False );
206     }
207 }
208 
209 void SwFldPortion::CheckScript( const SwTxtSizeInfo &rInf )
210 {
211     String aTxt;
212     if( GetExpTxt( rInf, aTxt ) && aTxt.Len() && pBreakIt->GetBreakIter().is() )
213     {
214         sal_uInt8 nActual = pFnt ? pFnt->GetActual() : rInf.GetFont()->GetActual();
215         sal_uInt16 nScript;
216         {
217             nScript = pBreakIt->GetBreakIter()->getScriptType( aTxt, 0 );
218             xub_StrLen nChg = 0;
219             if( i18n::ScriptType::WEAK == nScript )
220             {
221                 nChg =(xub_StrLen)pBreakIt->GetBreakIter()->endOfScript(aTxt,0,nScript);
222                 if( nChg < aTxt.Len() )
223                     nScript = pBreakIt->GetBreakIter()->getScriptType( aTxt, nChg );
224             }
225 
226             //
227             // nNextScriptChg will be evaluated during SwFldPortion::Format()
228             //
229             if ( nChg < aTxt.Len() )
230                 nNextScriptChg = (xub_StrLen)pBreakIt->GetBreakIter()->endOfScript( aTxt, nChg, nScript );
231             else
232                 nNextScriptChg = aTxt.Len();
233 
234         }
235         sal_uInt8 nTmp;
236         switch ( nScript ) {
237             case i18n::ScriptType::LATIN : nTmp = SW_LATIN; break;
238             case i18n::ScriptType::ASIAN : nTmp = SW_CJK; break;
239             case i18n::ScriptType::COMPLEX : nTmp = SW_CTL; break;
240             default: nTmp = nActual;
241         }
242 
243         // #i16354# Change script type for RTL text to CTL.
244         const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
245         // --> OD 2009-01-29 #i98418#
246 //        const sal_uInt8 nFldDir = IsNumberPortion() ?
247         const sal_uInt8 nFldDir = ( IsNumberPortion() || IsFtnNumPortion() ) ?
248                              rSI.GetDefaultDir() :
249                              rSI.DirType( IsFollow() ? rInf.GetIdx() - 1 : rInf.GetIdx() );
250         // <--
251         if ( UBIDI_RTL == nFldDir )
252         {
253             UErrorCode nError = U_ZERO_ERROR;
254             UBiDi* pBidi = ubidi_openSized( aTxt.Len(), 0, &nError );
255             ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aTxt.GetBuffer()), aTxt.Len(), nFldDir, NULL, &nError );
256             int32_t nEnd;
257             UBiDiLevel nCurrDir;
258             ubidi_getLogicalRun( pBidi, 0, &nEnd, &nCurrDir );
259             ubidi_close( pBidi );
260             const xub_StrLen nNextDirChg = (xub_StrLen)nEnd;
261             nNextScriptChg = Min( nNextScriptChg, nNextDirChg );
262 
263             // #i89825# change the script type also to CTL
264             // if there is no strong LTR char in the LTR run (numbers)
265             if ( nCurrDir != UBIDI_RTL )
266             {
267                 nCurrDir = UBIDI_RTL;
268                 for ( xub_StrLen nCharIdx = 0; nCharIdx < nEnd; ++nCharIdx )
269                 {
270                     UCharDirection nCharDir = u_charDirection ( aTxt.GetChar ( nCharIdx ));
271                     if ( nCharDir == U_LEFT_TO_RIGHT ||
272                          nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
273                          nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
274                     {
275                         nCurrDir = UBIDI_LTR;
276                         break;
277                     }
278                 }
279             }
280 
281             if ( nCurrDir == UBIDI_RTL )
282                 nTmp = SW_CTL;
283         }
284 
285         // --> OD 2009-01-29 #i98418#
286         // keep determined script type for footnote portions as preferred script type.
287         // For footnote portions a font can not be created directly - see footnote
288         // portion format method.
289 //         if( !IsFtnPortion() && nTmp != nActual )
290         if ( IsFtnPortion() )
291         {
292             dynamic_cast<SwFtnPortion*>(this)->SetPreferredScriptType( nTmp );
293         }
294         else if ( nTmp != nActual )
295         {
296             if( !pFnt )
297                 pFnt = new SwFont( *rInf.GetFont() );
298             pFnt->SetActual( nTmp );
299         }
300         // <--
301     }
302 }
303 
304 sal_Bool SwFldPortion::Format( SwTxtFormatInfo &rInf )
305 {
306     // Scope wegen aDiffTxt::DTOR!
307     xub_StrLen nRest;
308     sal_Bool bFull;
309     sal_Bool bEOL = sal_False;
310     long nTxtRest = rInf.GetTxt().Len() - rInf.GetIdx();
311     {
312         SwFldSlot aDiffTxt( &rInf, this );
313         SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
314         aLayoutModeModifier.SetAuto();
315 
316         // Field portion has to be split in several parts if
317         // 1. There are script/direction changes inside the field
318         // 2. There are portion breaks (tab, break) inside the field:
319         const xub_StrLen nOldFullLen = rInf.GetLen();
320         xub_StrLen nFullLen = rInf.ScanPortionEnd( rInf.GetIdx(), rInf.GetIdx() + nOldFullLen ) - rInf.GetIdx();
321         if ( nNextScriptChg < nFullLen )
322         {
323             nFullLen = nNextScriptChg;
324             rInf.SetHookChar( 0 );
325         }
326         rInf.SetLen( nFullLen );
327 
328         if ( STRING_LEN != rInf.GetUnderScorePos() &&
329              rInf.GetUnderScorePos() > rInf.GetIdx() )
330              rInf.SetUnderScorePos( rInf.GetIdx() );
331 
332         if( pFnt )
333             pFnt->GoMagic( rInf.GetVsh(), pFnt->GetActual() );
334 
335         SwFontSave aSave( rInf, pFnt );
336 
337         // 8674: Laenge muss 0 sein, bei bFull nach Format ist die Laenge
338         // gesetzt und wird in nRest uebertragen. Ansonsten bleibt die
339         // Laenge erhalten und wuerde auch in nRest einfliessen!
340         SetLen(0);
341         const MSHORT nFollow = IsFollow() ? 0 : 1;
342 
343         // So komisch es aussieht, die Abfrage auf GetLen() muss wegen der
344         // ExpandPortions _hinter_ aDiffTxt (vgl. SoftHyphs)
345         // sal_False returnen wegen SetFull ...
346         if( !nFullLen )
347         {
348             // nicht Init(), weil wir Hoehe und Ascent brauchen
349             Width(0);
350             bFull = rInf.Width() <= rInf.GetPos().X();
351         }
352         else
353         {
354             xub_StrLen nOldLineStart = rInf.GetLineStart();
355             if( IsFollow() )
356                 rInf.SetLineStart( 0 );
357             rInf.SetNotEOL( nFullLen == nOldFullLen && nTxtRest > nFollow );
358 
359             // the height depending on the fields font is set,
360             // this is required for SwTxtGuess::Guess
361             Height( rInf.GetTxtHeight() );
362             // If a kerning portion is inserted after our field portion,
363             // the ascent and height must be known
364             SetAscent( rInf.GetAscent() );
365             bFull = SwTxtPortion::Format( rInf );
366             rInf.SetNotEOL( sal_False );
367             rInf.SetLineStart( nOldLineStart );
368         }
369         xub_StrLen nTmpLen = GetLen();
370         bEOL = !nTmpLen && nFollow && bFull;
371         nRest = nOldFullLen - nTmpLen;
372 
373         // Das Zeichen wird in der ersten Portion gehalten.
374         // Unbedingt nach Format!
375         SetLen( (m_bNoLength) ? 0 : nFollow );
376 
377         if( nRest )
378         {
379             // aExpand ist noch nicht gekuerzt worden, der neue Ofst
380             // ergibt sich durch nRest.
381             xub_StrLen nNextOfst = aExpand.Len() - nRest;
382 
383             if ( IsQuoVadisPortion() )
384                 nNextOfst = nNextOfst + ((SwQuoVadisPortion*)this)->GetContTxt().Len();
385 
386             XubString aNew( aExpand, nNextOfst, STRING_LEN );
387             aExpand.Erase( nNextOfst, STRING_LEN );
388 
389             // These characters should not be contained in the follow
390             // field portion. They are handled via the HookChar mechanism.
391             switch( aNew.GetChar( 0 ))
392             {
393                 case CH_BREAK  : bFull = sal_True;
394                             // kein break;
395                 case ' ' :
396                 case CH_TAB    :
397                 case CHAR_HARDHYPHEN:               // non-breaking hyphen
398                 case CHAR_SOFTHYPHEN:
399                 case CHAR_HARDBLANK:
400                 // --> FME 2006-01-11 #i59759# Erase additional control
401                 // characters from field string, otherwise we get stuck in
402                 // a loop.
403                 case CHAR_ZWSP :
404                 case CHAR_ZWNBSP :
405         //        case CHAR_RLM :
406         //        case CHAR_LRM :
407                 // <--
408                 // --> OD 2010-06-03 #i111750#
409                 // - Erasing further control characters from field string in
410                 // to avoid loop.
411                 case CH_TXTATR_BREAKWORD:
412                 case CH_TXTATR_INWORD:
413                 // <--
414                 {
415                     aNew.Erase( 0, 1 );
416                     ++nNextOfst;
417                     break;
418                 }
419                 default: ;
420             }
421 
422             // Even if there is no more text left for a follow field,
423             // we have to build a follow field portion (without font),
424             // otherwise the HookChar mechanism would not work.
425             SwFldPortion *pFld = Clone( aNew );
426             if( aNew.Len() && !pFld->GetFont() )
427             {
428                 SwFont *pNewFnt = new SwFont( *rInf.GetFont() );
429                 pFld->SetFont( pNewFnt );
430             }
431             pFld->SetFollow( sal_True );
432             SetHasFollow( sal_True );
433             // In nNextOffset steht bei einem neuangelegten Feld zunaechst
434             // der Offset, an dem es selbst im Originalstring beginnt.
435             // Wenn beim Formatieren ein FollowFeld angelegt wird, wird
436             // der Offset dieses FollowFelds in nNextOffset festgehalten.
437             nNextOffset = nNextOffset + nNextOfst;
438             pFld->SetNextOffset( nNextOffset );
439             rInf.SetRest( pFld );
440         }
441     }
442 
443     if( bEOL && rInf.GetLast() && !rInf.GetUnderFlow() )
444         rInf.GetLast()->FormatEOL( rInf );
445     return bFull;
446 }
447 
448 /*************************************************************************
449  *               virtual SwFldPortion::Paint()
450  *************************************************************************/
451 
452 void SwFldPortion::Paint( const SwTxtPaintInfo &rInf ) const
453 {
454     SwFontSave aSave( rInf, pFnt );
455 
456     ASSERT( GetLen() <= 1, "SwFldPortion::Paint: rest-portion polution?" );
457     if( Width() && ( !bPlaceHolder || rInf.GetOpt().IsShowPlaceHolderFields() ) )
458     {
459         // Dies ist eine freizuegige Auslegung der Hintergrundbelegung ...
460         rInf.DrawViewOpt( *this, POR_FLD );
461         SwExpandPortion::Paint( rInf );
462     }
463 }
464 
465 /*************************************************************************
466  *              virtual SwFldPortion::GetExpTxt()
467  *************************************************************************/
468 
469 sal_Bool SwFldPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const
470 {
471     rTxt = aExpand;
472     if( !rTxt.Len() && rInf.OnWin() &&
473         !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() &&
474             SwViewOption::IsFieldShadings() &&
475             !HasFollow() )
476         rTxt = ' ';
477     return sal_True;
478 }
479 
480 /*************************************************************************
481  *              virtual SwFldPortion::HandlePortion()
482  *************************************************************************/
483 
484 void SwFldPortion::HandlePortion( SwPortionHandler& rPH ) const
485 {
486     rPH.Special( GetLen(), aExpand, GetWhichPor() );
487     //IAccessibility2 Implementation 2009-----
488     if( GetWhichPor() == POR_FLD )
489     {
490         rPH.SetAttrFieldType(m_nAttrFldType);
491     }
492     //-----IAccessibility2 Implementation 2009
493 }
494 
495 /*************************************************************************
496  *                virtual SwFldPortion::GetTxtSize()
497  *************************************************************************/
498 
499 SwPosSize SwFldPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const
500 {
501     SwFontSave aSave( rInf, pFnt );
502     SwPosSize aSize( SwExpandPortion::GetTxtSize( rInf ) );
503     return aSize;
504 }
505 
506 /*************************************************************************
507  *                      class SwHiddenPortion
508  *************************************************************************/
509 
510 SwFldPortion *SwHiddenPortion::Clone(const XubString &rExpand ) const
511 {
512     SwFont *pNewFnt;
513     if( 0 != ( pNewFnt = pFnt ) )
514         pNewFnt = new SwFont( *pFnt );
515     return new SwHiddenPortion( rExpand, pNewFnt );
516 }
517 
518 /*************************************************************************
519  *               virtual SwHiddenPortion::Paint()
520  *************************************************************************/
521 
522 void SwHiddenPortion::Paint( const SwTxtPaintInfo &rInf ) const
523 {
524     if( Width() )
525     {
526         SwFontSave aSave( rInf, pFnt );
527         rInf.DrawViewOpt( *this, POR_HIDDEN );
528         SwExpandPortion::Paint( rInf );
529     }
530 }
531 
532 /*************************************************************************
533  *              virtual SwHiddenPortion::GetExpTxt()
534  *************************************************************************/
535 
536 sal_Bool SwHiddenPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const
537 {
538     // Nicht auf IsHidden() abfragen !
539     return SwFldPortion::GetExpTxt( rInf, rTxt );
540 }
541 
542 /*************************************************************************
543  *                      class SwNumberPortion
544  *************************************************************************/
545 
546 // --> OD 2008-01-23 #newlistlevelattrs#
547 SwNumberPortion::SwNumberPortion( const XubString &rExpand,
548                                   SwFont *pFont,
549                                   const sal_Bool bLft,
550                                   const sal_Bool bCntr,
551                                   const KSHORT nMinDst,
552                                   const bool bLabelAlignmentPosAndSpaceModeActive )
553         : SwFldPortion( rExpand, pFont ),
554           nFixWidth(0),
555           nMinDist( nMinDst ),
556           // --> OD 2008-01-23 #newlistlevelattrs#
557           mbLabelAlignmentPosAndSpaceModeActive( bLabelAlignmentPosAndSpaceModeActive )
558           // <--
559 {
560     SetWhichPor( POR_NUMBER );
561     SetLeft( bLft );
562     SetHide( sal_False );
563     SetCenter( bCntr );
564 }
565 
566 xub_StrLen SwNumberPortion::GetCrsrOfst( const MSHORT ) const
567 {
568     return 0;
569 }
570 
571 SwFldPortion *SwNumberPortion::Clone( const XubString &rExpand ) const
572 {
573     SwFont *pNewFnt;
574     if( 0 != ( pNewFnt = pFnt ) )
575         pNewFnt = new SwFont( *pFnt );
576     // --> OD 2008-01-23 #newlistlevelattrs#
577     return new SwNumberPortion( rExpand, pNewFnt, IsLeft(), IsCenter(),
578                                 nMinDist, mbLabelAlignmentPosAndSpaceModeActive );
579     // <--
580 }
581 
582 /*************************************************************************
583  *                 virtual SwNumberPortion::Format()
584  *************************************************************************/
585 
586 // 5010: Wir sind in der Lage, mehrzeilige NumFelder anzulegen!
587 // 3689: Fies ist, wenn man in der Dialogbox soviel Davor-Text
588 // eingibt, bis die Zeile ueberlaeuft.
589 // Man muss die Fly-Ausweichmanoever beachten!
590 
591 sal_Bool SwNumberPortion::Format( SwTxtFormatInfo &rInf )
592 {
593     SetHide( sal_False );
594     const sal_Bool bFull = SwFldPortion::Format( rInf );
595     SetLen( 0 );
596     // a numbering portion can be contained in a rotated portion!!!
597     nFixWidth = rInf.IsMulti() ? Height() : Width();
598     rInf.SetNumDone( !rInf.GetRest() );
599     if( rInf.IsNumDone() )
600     {
601 //        SetAscent( rInf.GetAscent() );
602         ASSERT( Height() && nAscent, "NumberPortions without Height | Ascent" );
603 
604         long nDiff( 0 );
605         // --> OD 2008-01-23 #newlistlevelattrs#
606         if ( !mbLabelAlignmentPosAndSpaceModeActive )
607         {
608             if ( !rInf.GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) &&
609                  // --> FME 2004-08-13 #i32902#
610                  !IsFtnNumPortion() )
611                  // <--
612             {
613                 nDiff = rInf.Left()
614                     + rInf.GetTxtFrm()->GetTxtNode()->
615                     GetSwAttrSet().GetLRSpace().GetTxtFirstLineOfst()
616                     - rInf.First()
617                     + rInf.ForcedLeftMargin();
618             }
619             else
620             {
621                 nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
622             }
623         }
624         // <--
625         // Ein Vorschlag von Juergen und Volkmar:
626         // Der Textteil hinter der Numerierung sollte immer
627         // mindestens beim linken Rand beginnen.
628         if( nDiff < 0 )
629             nDiff = 0;
630         else if ( nDiff > rInf.X() )
631             nDiff -= rInf.X();
632         else
633             nDiff = 0;
634 
635         if( nDiff < nFixWidth + nMinDist )
636             nDiff = nFixWidth + nMinDist;
637         // 2739: Numerierung weicht Fly aus, kein nDiff in der zweiten Runde
638         // fieser Sonderfall: FlyFrm liegt in dem Bereich,
639         // den wir uns gerade unter den Nagel reissen wollen.
640         // Die NumberPortion wird als verborgen markiert.
641         const sal_Bool bFly = rInf.GetFly() ||
642             ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
643         if( nDiff > rInf.Width() )
644         {
645             nDiff = rInf.Width();
646             if ( bFly )
647                 SetHide( sal_True );
648         }
649 
650         // A numbering portion can be inside a SwRotatedPortion. Then the
651         // Height has to be changed
652         if ( rInf.IsMulti() )
653         {
654             if ( Height() < nDiff )
655                 Height( KSHORT( nDiff ) );
656         }
657         else if( Width() < nDiff )
658             Width( KSHORT(nDiff) );
659     }
660     return bFull;
661 }
662 
663 void SwNumberPortion::FormatEOL( SwTxtFormatInfo& )
664 {
665 /*  Ein FormatEOL deutet daraufhin, dass der folgende Text
666  *  nicht mit auf die Zeile passte. Damit die Numerierung mitwandert,
667  *  wird diese NumberPortion verborgen.
668  */
669 
670     // This caused trouble with flys anchored as characters.
671     // If one of these is numbered but does not fit to the line,
672     // it calls this function, causing a loop because both the number
673     // portion and the fly portion go to the next line
674 //    SetHide( sal_True );
675 }
676 
677 /*************************************************************************
678  *               virtual SwNumberPortion::Paint()
679  *************************************************************************/
680 
681 void SwNumberPortion::Paint( const SwTxtPaintInfo &rInf ) const
682 {
683 /*  Eine verborgene NumberPortion wird nicht angezeigt, es sei denn, es gibt
684  *  Textportions in dieser Zeile oder es gibt ueberhaupt nur eine einzige Zeile.
685  */
686 
687     if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
688     {
689         SwLinePortion *pTmp = GetPortion();
690         while ( pTmp && !pTmp->InTxtGrp() )
691             pTmp = pTmp->GetPortion();
692         if ( !pTmp )
693             return;
694     }
695 
696     // calculate the width of the number portion, including follows
697     const KSHORT nOldWidth = Width();
698     sal_uInt16 nSumWidth = 0;
699     sal_uInt16 nOffset = 0;
700 
701     const SwLinePortion* pTmp = this;
702     while ( pTmp && pTmp->InNumberGrp() )
703     {
704         nSumWidth = nSumWidth + pTmp->Width();
705         if ( ((SwNumberPortion*)pTmp)->HasFollow() )
706             pTmp = pTmp->GetPortion();
707         else
708         {
709             nOffset = pTmp->Width() - ((SwNumberPortion*)pTmp)->nFixWidth;
710             break;
711         }
712     }
713 
714     // The master portion takes care for painting the background of the
715     // follow field portions
716     if ( ! IsFollow() )
717     {
718         SwLinePortion *pThis = (SwLinePortion*)this;
719         pThis->Width( nSumWidth );
720         rInf.DrawViewOpt( *this, POR_NUMBER );
721         pThis->Width( nOldWidth );
722     }
723 
724     if( aExpand.Len() )
725     {
726         const SwFont *pTmpFnt = rInf.GetFont();
727         sal_Bool bPaintSpace = ( UNDERLINE_NONE != pTmpFnt->GetUnderline() ||
728                                  UNDERLINE_NONE != pTmpFnt->GetOverline()  ||
729                                  STRIKEOUT_NONE != pTmpFnt->GetStrikeout() ) &&
730                                  !pTmpFnt->IsWordLineMode();
731         if( bPaintSpace && pFnt )
732             bPaintSpace = ( UNDERLINE_NONE != pFnt->GetUnderline() ||
733                             UNDERLINE_NONE != pFnt->GetOverline()  ||
734                             STRIKEOUT_NONE != pFnt->GetStrikeout() ) &&
735                             !pFnt->IsWordLineMode();
736 
737         SwFontSave aSave( rInf, pFnt );
738 
739         if( nFixWidth == Width() && ! HasFollow() )
740             SwExpandPortion::Paint( rInf );
741         else
742         {
743             // logisches const: Width wird wieder zurueckgesetzt
744             SwLinePortion *pThis = (SwLinePortion*)this;
745             bPaintSpace = bPaintSpace && nFixWidth < nOldWidth;
746             KSHORT nSpaceOffs = nFixWidth;
747             pThis->Width( nFixWidth );
748 
749             if( ( IsLeft() && ! rInf.GetTxtFrm()->IsRightToLeft() ) ||
750                 ( ! IsLeft() && ! IsCenter() && rInf.GetTxtFrm()->IsRightToLeft() ) )
751                 SwExpandPortion::Paint( rInf );
752             else
753             {
754                 SwTxtPaintInfo aInf( rInf );
755                 if( nOffset < nMinDist )
756                     nOffset = 0;
757                 else
758                 {
759                     if( IsCenter() )
760                     {
761                         /* #110778# a / 2 * 2 == a is not a tautology */
762                         KSHORT nTmpOffset = nOffset;
763                         nOffset /= 2;
764                         if( nOffset < nMinDist )
765                             nOffset = nTmpOffset - nMinDist;
766                     }
767                     else
768                         nOffset = nOffset - nMinDist;
769                 }
770                 aInf.X( aInf.X() + nOffset );
771                 SwExpandPortion::Paint( aInf );
772                 if( bPaintSpace )
773                     nSpaceOffs = nSpaceOffs + nOffset;
774             }
775             if( bPaintSpace && nOldWidth > nSpaceOffs )
776             {
777                 SwTxtPaintInfo aInf( rInf );
778 static sal_Char __READONLY_DATA sDoubleSpace[] = "  ";
779                 aInf.X( aInf.X() + nSpaceOffs );
780 
781                 // --> FME 2005-08-12 #i53199# Adjust position of underline:
782                 if ( rInf.GetUnderFnt() )
783                 {
784                     const Point aNewPos( aInf.GetPos().X(), rInf.GetUnderFnt()->GetPos().Y() );
785                     rInf.GetUnderFnt()->SetPos( aNewPos );
786                 }
787                 // <--
788 
789                 pThis->Width( nOldWidth - nSpaceOffs + 12 );
790                 {
791                     SwTxtSlot aDiffTxt( &aInf, this, true, false, sDoubleSpace );
792                     aInf.DrawText( *this, aInf.GetLen(), sal_True );
793                 }
794             }
795             pThis->Width( nOldWidth );
796         }
797     }
798 }
799 
800 
801 /*************************************************************************
802  *                      class SwBulletPortion
803  *************************************************************************/
804 
805 // --> OD 2008-01-23 #newlistlevelattrs#
806 SwBulletPortion::SwBulletPortion( const xub_Unicode cBullet,
807                                   const XubString& rBulletFollowedBy,
808                                   SwFont *pFont,
809                                   const sal_Bool bLft,
810                                   const sal_Bool bCntr,
811                                   const KSHORT nMinDst,
812                                   const bool bLabelAlignmentPosAndSpaceModeActive )
813     : SwNumberPortion( XubString( rBulletFollowedBy ).Insert( cBullet, 0 ) ,
814                        pFont, bLft, bCntr, nMinDst,
815                        bLabelAlignmentPosAndSpaceModeActive )
816 // <--
817 {
818     SetWhichPor( POR_BULLET );
819 }
820 
821 /*************************************************************************
822  *                      class SwGrfNumPortion
823  *************************************************************************/
824 
825 #define GRFNUM_SECURE 10
826 
827 // --> OD 2008-01-23 #newlistlevelattrs#
828 SwGrfNumPortion::SwGrfNumPortion(
829         SwFrm*,
830         const XubString& rGraphicFollowedBy,
831         const SvxBrushItem* pGrfBrush,
832         const SwFmtVertOrient* pGrfOrient, const Size& rGrfSize,
833         const sal_Bool bLft, const sal_Bool bCntr, const KSHORT nMinDst,
834         const bool bLabelAlignmentPosAndSpaceModeActive ) :
835     SwNumberPortion( rGraphicFollowedBy, NULL, bLft, bCntr, nMinDst,
836                      bLabelAlignmentPosAndSpaceModeActive ),
837 // <--
838     pBrush( new SvxBrushItem(RES_BACKGROUND) ), nId( 0 )
839 {
840     SetWhichPor( POR_GRFNUM );
841     SetAnimated( sal_False );
842     bReplace = sal_False;
843     if( pGrfBrush )
844     {
845         *pBrush = *pGrfBrush;
846         const Graphic* pGraph = pGrfBrush->GetGraphic();
847         if( pGraph )
848             SetAnimated( pGraph->IsAnimated() );
849         else
850             bReplace = sal_True;
851     }
852     if( pGrfOrient )
853     {
854         nYPos = pGrfOrient->GetPos();
855         eOrient = pGrfOrient->GetVertOrient();
856     }
857     else
858     {
859         nYPos = 0;
860         eOrient = text::VertOrientation::TOP;
861     }
862     Width( static_cast<sal_uInt16>(rGrfSize.Width() + 2 * GRFNUM_SECURE) );
863     nFixWidth = Width();
864     nGrfHeight = rGrfSize.Height() + 2 * GRFNUM_SECURE;
865     Height( KSHORT(nGrfHeight) );
866     bNoPaint = sal_False;
867 }
868 
869 SwGrfNumPortion::~SwGrfNumPortion()
870 {
871     if ( IsAnimated() )
872         ( (Graphic*) pBrush->GetGraphic() )->StopAnimation( 0, nId );
873     delete pBrush;
874 }
875 
876 void SwGrfNumPortion::StopAnimation( OutputDevice* pOut )
877 {
878     if ( IsAnimated() )
879         ( (Graphic*) pBrush->GetGraphic() )->StopAnimation( pOut, nId );
880 }
881 
882 sal_Bool SwGrfNumPortion::Format( SwTxtFormatInfo &rInf )
883 {
884     SetHide( sal_False );
885     // --> OD 2008-01-29 #newlistlevelattrs#
886 //    Width( nFixWidth );
887     KSHORT nFollowedByWidth( 0 );
888     if ( mbLabelAlignmentPosAndSpaceModeActive )
889     {
890         SwFldPortion::Format( rInf );
891         nFollowedByWidth = Width();
892         SetLen( 0 );
893     }
894     Width( nFixWidth + nFollowedByWidth );
895     // <--
896     const sal_Bool bFull = rInf.Width() < rInf.X() + Width();
897     const sal_Bool bFly = rInf.GetFly() ||
898         ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
899     SetAscent( static_cast<sal_uInt16>(GetRelPos() > 0 ? GetRelPos() : 0) );
900     if( GetAscent() > Height() )
901         Height( GetAscent() );
902 
903     if( bFull )
904     {
905         Width( rInf.Width() - (KSHORT)rInf.X() );
906         if( bFly )
907         {
908             SetLen( 0 );
909             SetNoPaint( sal_True );
910             rInf.SetNumDone( sal_False );
911             return sal_True;
912         }
913     }
914     rInf.SetNumDone( sal_True );
915     // --> OD 2008-01-23 #newlistlevelattrs#
916 //    long nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
917     long nDiff = mbLabelAlignmentPosAndSpaceModeActive
918                  ? 0
919                  : rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
920     // <--
921     // Ein Vorschlag von Juergen und Volkmar:
922     // Der Textteil hinter der Numerierung sollte immer
923     // mindestens beim linken Rand beginnen.
924     if( nDiff < 0 )
925         nDiff = 0;
926     else if ( nDiff > rInf.X() )
927         nDiff -= rInf.X();
928     if( nDiff < nFixWidth + nMinDist )
929         nDiff = nFixWidth + nMinDist;
930     // 2739: Numerierung weicht Fly aus, kein nDiff in der zweiten Runde
931     // fieser Sonderfall: FlyFrm liegt in dem Bereich,
932     // den wir uns gerade unter den Nagel reissen wollen.
933     // Die NumberPortion wird als verborgen markiert.
934     if( nDiff > rInf.Width() )
935     {
936         nDiff = rInf.Width();
937         if( bFly )
938             SetHide( sal_True );
939     }
940 
941     if( Width() < nDiff )
942         Width( KSHORT(nDiff) );
943     return bFull;
944 }
945 
946 void SwGrfNumPortion::Paint( const SwTxtPaintInfo &rInf ) const
947 {
948     if( DontPaint() )
949         return;
950 /*  Eine verborgene NumberPortion wird nicht angezeigt, es sei denn, es gibt
951  *  Textportions in dieser Zeile oder es gibt ueberhaupt nur eine einzige Zeile.
952  */
953     if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
954     {
955         SwLinePortion *pTmp = GetPortion();
956         while ( pTmp && !pTmp->InTxtGrp() )
957             pTmp = pTmp->GetPortion();
958         if ( !pTmp )
959             return;
960     }
961     Point aPos( rInf.X() + GRFNUM_SECURE, rInf.Y() - GetRelPos() + GRFNUM_SECURE );
962     long nTmpWidth = Max( (long)0, (long)(nFixWidth - 2 * GRFNUM_SECURE) );
963     Size aSize( nTmpWidth, GetGrfHeight() - 2 * GRFNUM_SECURE );
964 
965     // --> OD 2008-02-05 #newlistlevelattrs#
966     const sal_Bool bTmpLeft = mbLabelAlignmentPosAndSpaceModeActive ||
967                               ( IsLeft() && ! rInf.GetTxtFrm()->IsRightToLeft() ) ||
968                               ( ! IsLeft() && ! IsCenter() && rInf.GetTxtFrm()->IsRightToLeft() );
969     // <--
970 
971     if( nFixWidth < Width() && !bTmpLeft )
972     {
973         KSHORT nOffset = Width() - nFixWidth;
974         if( nOffset < nMinDist )
975             nOffset = 0;
976         else
977         {
978             if( IsCenter() )
979             {
980                 nOffset /= 2;
981                 if( nOffset < nMinDist )
982                     nOffset = Width() - nFixWidth - nMinDist;
983             }
984             else
985                 nOffset = nOffset - nMinDist;
986         }
987         aPos.X() += nOffset;
988     }
989 
990     if( bReplace )
991     {
992         KSHORT nTmpH = GetPortion() ? GetPortion()->GetAscent() : 120;
993         aSize = Size( nTmpH, nTmpH );
994         aPos.Y() = rInf.Y() - nTmpH;
995     }
996     SwRect aTmp( aPos, aSize );
997 
998     sal_Bool bDraw = sal_True;
999 
1000     if ( IsAnimated() )
1001     {
1002         bDraw = !rInf.GetOpt().IsGraphic();
1003         if( !nId )
1004         {
1005             SetId( long( rInf.GetTxtFrm() ) );
1006             rInf.GetTxtFrm()->SetAnimation();
1007         }
1008         if( aTmp.IsOver( rInf.GetPaintRect() ) && !bDraw )
1009         {
1010             rInf.NoteAnimation();
1011             const ViewShell* pViewShell = rInf.GetVsh();
1012 
1013             // virtual device, not pdf export
1014             if( OUTDEV_VIRDEV == rInf.GetOut()->GetOutDevType() &&
1015                 pViewShell && pViewShell->GetWin()  )
1016             {
1017                 ( (Graphic*) pBrush->GetGraphic() )->StopAnimation(0,nId);
1018                 rInf.GetTxtFrm()->getRootFrm()->GetCurrShell()->InvalidateWindows( aTmp );
1019             }
1020 
1021 
1022             else if ( pViewShell &&
1023                      !pViewShell->GetAccessibilityOptions()->IsStopAnimatedGraphics() &&
1024                      !pViewShell->IsPreView() &&
1025                       // --> FME 2004-06-21 #i9684# Stop animation during printing/pdf export.
1026                       pViewShell->GetWin() )
1027                       // <--
1028             {
1029                 ( (Graphic*) pBrush->GetGraphic() )->StartAnimation(
1030                     (OutputDevice*)rInf.GetOut(), aPos, aSize, nId );
1031             }
1032 
1033             // pdf export, printing, preview, stop animations...
1034             else
1035                 bDraw = sal_True;
1036         }
1037         if( bDraw )
1038             ( (Graphic*) pBrush->GetGraphic() )->StopAnimation( 0, nId );
1039     }
1040 
1041     SwRect aRepaint( rInf.GetPaintRect() );
1042     const SwTxtFrm& rFrm = *rInf.GetTxtFrm();
1043     if( rFrm.IsVertical() )
1044     {
1045         rFrm.SwitchHorizontalToVertical( aTmp );
1046         rFrm.SwitchHorizontalToVertical( aRepaint );
1047     }
1048 
1049     if( rFrm.IsRightToLeft() )
1050     {
1051         rFrm.SwitchLTRtoRTL( aTmp );
1052         rFrm.SwitchLTRtoRTL( aRepaint );
1053     }
1054 
1055     if( bDraw && aTmp.HasArea() )
1056         DrawGraphic( pBrush, (OutputDevice*)rInf.GetOut(),
1057             aTmp, aRepaint, bReplace ? GRFNUM_REPLACE : GRFNUM_YES );
1058 }
1059 
1060 void SwGrfNumPortion::SetBase( long nLnAscent, long nLnDescent,
1061                                long nFlyAsc, long nFlyDesc )
1062 {
1063     if ( GetOrient() != text::VertOrientation::NONE )
1064     {
1065         SetRelPos( 0 );
1066         if ( GetOrient() == text::VertOrientation::CENTER )
1067             SetRelPos( GetGrfHeight() / 2 );
1068         else if ( GetOrient() == text::VertOrientation::TOP )
1069             SetRelPos( GetGrfHeight() - GRFNUM_SECURE );
1070         else if ( GetOrient() == text::VertOrientation::BOTTOM )
1071             ;
1072         else if ( GetOrient() == text::VertOrientation::CHAR_CENTER )
1073             SetRelPos( ( GetGrfHeight() + nLnAscent - nLnDescent ) / 2 );
1074         else if ( GetOrient() == text::VertOrientation::CHAR_TOP )
1075             SetRelPos( nLnAscent );
1076         else if ( GetOrient() == text::VertOrientation::CHAR_BOTTOM )
1077             SetRelPos( GetGrfHeight() - nLnDescent );
1078         else
1079         {
1080             if( GetGrfHeight() >= nFlyAsc + nFlyDesc )
1081             {
1082                 // wenn ich genauso gross bin wie die Zeile, brauche ich mich
1083                 // nicht an der Zeile nicht weiter ausrichten, ich lasse
1084                 // dann auch den max. Ascent der Zeile unveraendert
1085 
1086                 SetRelPos( nFlyAsc );
1087             }
1088             else if ( GetOrient() == text::VertOrientation::LINE_CENTER )
1089                 SetRelPos( ( GetGrfHeight() + nFlyAsc - nFlyDesc ) / 2 );
1090             else if ( GetOrient() == text::VertOrientation::LINE_TOP )
1091                 SetRelPos( nFlyAsc );
1092             else if ( GetOrient() == text::VertOrientation::LINE_BOTTOM )
1093                 SetRelPos( GetGrfHeight() - nFlyDesc );
1094         }
1095     }
1096 }
1097 
1098 void SwTxtFrm::StopAnimation( OutputDevice* pOut )
1099 {
1100     ASSERT( HasAnimation(), "SwTxtFrm::StopAnimation: Which Animation?" );
1101     if( HasPara() )
1102     {
1103         SwLineLayout *pLine = GetPara();
1104         while( pLine )
1105         {
1106             SwLinePortion *pPor = pLine->GetPortion();
1107             while( pPor )
1108             {
1109                 if( pPor->IsGrfNumPortion() )
1110                     ((SwGrfNumPortion*)pPor)->StopAnimation( pOut );
1111                 // Die Numerierungsportion sitzt immer vor dem ersten Zeichen,
1112                 // deshalb koennen wir abbrechen, sobald wir eine Portion mit
1113                 // einer Laenge > 0 erreicht haben.
1114                 pPor = pPor->GetLen() ? 0 : pPor->GetPortion();
1115             }
1116             pLine = pLine->GetLen() ? 0 : pLine->GetNext();
1117         }
1118     }
1119 }
1120 
1121 /*************************************************************************
1122  * SwCombinedPortion::SwCombinedPortion(..)
1123  * initializes the script array and clears the width array
1124  *************************************************************************/
1125 
1126 SwCombinedPortion::SwCombinedPortion( const XubString &rTxt )
1127      : SwFldPortion( rTxt )
1128 {
1129     SetLen(1);
1130     SetWhichPor( POR_COMBINED );
1131     if( aExpand.Len() > 6 )
1132         aExpand.Erase( 6 );
1133     // Initialization of the scripttype array,
1134     // the arrays of width and position are filled by the format function
1135     if( pBreakIt->GetBreakIter().is() )
1136     {
1137         sal_uInt8 nScr = SW_SCRIPTS;
1138         for( sal_uInt16 i = 0; i < rTxt.Len(); ++i )
1139         {
1140             sal_uInt16 nScript = pBreakIt->GetBreakIter()->getScriptType( rTxt, i );
1141             switch ( nScript ) {
1142                 case i18n::ScriptType::LATIN : nScr = SW_LATIN; break;
1143                 case i18n::ScriptType::ASIAN : nScr = SW_CJK; break;
1144                 case i18n::ScriptType::COMPLEX : nScr = SW_CTL; break;
1145             }
1146             aScrType[i] = nScr;
1147         }
1148     }
1149     else
1150     {
1151         for( sal_uInt16 i = 0; i < 6; aScrType[i++] = 0 )
1152             ; // nothing
1153     }
1154     memset( &aWidth, 0, sizeof(aWidth) );
1155 }
1156 
1157 /*************************************************************************
1158  * SwCombinedPortion::Paint(..)
1159  *************************************************************************/
1160 
1161 void SwCombinedPortion::Paint( const SwTxtPaintInfo &rInf ) const
1162 {
1163     ASSERT( GetLen() <= 1, "SwFldPortion::Paint: rest-portion polution?" );
1164     if( Width() )
1165     {
1166         rInf.DrawBackBrush( *this );
1167         rInf.DrawViewOpt( *this, POR_FLD );
1168 
1169         // do we have to repaint a post it portion?
1170         if( rInf.OnWin() && pPortion && !pPortion->Width() )
1171             pPortion->PrePaint( rInf, this );
1172 
1173         sal_uInt16 nCount = aExpand.Len();
1174         if( !nCount )
1175             return;
1176         ASSERT( nCount < 7, "Too much combined characters" );
1177 
1178         // the first character of the second row
1179         sal_uInt16 nTop = ( nCount + 1 ) / 2;
1180 
1181         SwFont aTmpFont( *rInf.GetFont() );
1182         aTmpFont.SetProportion( nProportion );  // a smaller font
1183         SwFontSave aFontSave( rInf, &aTmpFont );
1184 
1185         sal_uInt16 i = 0;
1186         Point aOldPos = rInf.GetPos();
1187         Point aOutPos( aOldPos.X(), aOldPos.Y() - nUpPos );// Y of the first row
1188         while( i < nCount )
1189         {
1190             if( i == nTop ) // change the row
1191                 aOutPos.Y() = aOldPos.Y() + nLowPos;    // Y of the second row
1192             aOutPos.X() = aOldPos.X() + aPos[i];        // X position
1193             const sal_uInt8 nAct = aScrType[i];             // script type
1194             aTmpFont.SetActual( nAct );
1195             // if there're more than 4 characters to display, we choose fonts
1196             // with 2/3 of the original font width.
1197             if( aWidth[ nAct ] )
1198             {
1199                 Size aTmpSz = aTmpFont.GetSize( nAct );
1200                 if( aTmpSz.Width() != aWidth[ nAct ] )
1201                 {
1202                     aTmpSz.Width() = aWidth[ nAct ];
1203                     aTmpFont.SetSize( aTmpSz, nAct );
1204                 }
1205             }
1206             ((SwTxtPaintInfo&)rInf).SetPos( aOutPos );
1207             rInf.DrawText( aExpand, *this, i, 1 );
1208             ++i;
1209         }
1210         // rInf is const, so we have to take back our manipulations
1211         ((SwTxtPaintInfo&)rInf).SetPos( aOldPos );
1212     }
1213 }
1214 
1215 /*************************************************************************
1216  * SwCombinedPortion::Format(..)
1217  *************************************************************************/
1218 
1219 sal_Bool SwCombinedPortion::Format( SwTxtFormatInfo &rInf )
1220 {
1221     sal_uInt16 nCount = aExpand.Len();
1222     if( !nCount )
1223     {
1224         Width( 0 );
1225         return sal_False;
1226     }
1227 
1228     ASSERT( nCount < 7, "Too much combined characters" );
1229     // If there are leading "weak"-scripttyped characters in this portion,
1230     // they get the actual scripttype.
1231     sal_uInt16 i = 0;
1232     while( i < nCount && SW_SCRIPTS == aScrType[i] )
1233         aScrType[i++] = rInf.GetFont()->GetActual();
1234     if( nCount > 4 )
1235     {
1236         // more than four? Ok, then we need the 2/3 font width
1237         i = 0;
1238         while( i < aExpand.Len() )
1239         {
1240             ASSERT( aScrType[i] < SW_SCRIPTS, "Combined: Script fault" );
1241             if( !aWidth[ aScrType[i] ] )
1242             {
1243                 rInf.GetOut()->SetFont( rInf.GetFont()->GetFnt( aScrType[i] ) );
1244                 aWidth[ aScrType[i] ] =
1245                         static_cast<sal_uInt16>(2 * rInf.GetOut()->GetFontMetric().GetSize().Width() / 3);
1246             }
1247             ++i;
1248         }
1249     }
1250 
1251     sal_uInt16 nTop = ( nCount + 1 ) / 2; // the first character of the second line
1252     ViewShell *pSh = rInf.GetTxtFrm()->getRootFrm()->GetCurrShell();
1253     SwFont aTmpFont( *rInf.GetFont() );
1254     SwFontSave aFontSave( rInf, &aTmpFont );
1255     nProportion = 55;
1256     // In nMainAscent/Descent we store the ascent and descent
1257     // of the original surrounding font
1258     sal_uInt16 nMaxDescent, nMaxAscent, nMaxWidth;
1259     sal_uInt16 nMainDescent = rInf.GetFont()->GetHeight( pSh, *rInf.GetOut() );
1260     const sal_uInt16 nMainAscent = rInf.GetFont()->GetAscent( pSh, *rInf.GetOut() );
1261     nMainDescent = nMainDescent - nMainAscent;
1262     // we start with a 50% font, but if we notice that the combined portion
1263     // becomes bigger than the surrounding font, we check 45% and maybe 40%.
1264     do
1265     {
1266         nProportion -= 5;
1267         aTmpFont.SetProportion( nProportion );
1268         i = 0;
1269         memset( &aPos, 0, sizeof(aPos) );
1270         nMaxDescent = 0;
1271         nMaxAscent = 0;
1272         nMaxWidth = 0;
1273         nUpPos = nLowPos = 0;
1274 
1275         // Now we get the width of all characters.
1276         // The ascent and the width of the first line are stored in the
1277         // ascent member of the portion, the descent in nLowPos.
1278         // The ascent, descent and width of the second line are stored in the
1279         // local nMaxAscent, nMaxDescent and nMaxWidth variables.
1280         while( i < nCount )
1281         {
1282             sal_uInt8 nScrp = aScrType[i];
1283             aTmpFont.SetActual( nScrp );
1284             if( aWidth[ nScrp ] )
1285             {
1286                 Size aFontSize( aTmpFont.GetSize( nScrp ) );
1287                 aFontSize.Width() = aWidth[ nScrp ];
1288                 aTmpFont.SetSize( aFontSize, nScrp );
1289             }
1290 
1291             SwDrawTextInfo aDrawInf( pSh, *rInf.GetOut(), 0, aExpand, i, 1 );
1292             Size aSize = aTmpFont._GetTxtSize( aDrawInf );
1293             sal_uInt16 nAsc = aTmpFont.GetAscent( pSh, *rInf.GetOut() );
1294             aPos[ i ] = (sal_uInt16)aSize.Width();
1295             if( i == nTop ) // enter the second line
1296             {
1297                 nLowPos = nMaxDescent;
1298                 Height( nMaxDescent + nMaxAscent );
1299                 Width( nMaxWidth );
1300                 SetAscent( nMaxAscent );
1301                 nMaxAscent = 0;
1302                 nMaxDescent = 0;
1303                 nMaxWidth = 0;
1304             }
1305             nMaxWidth = nMaxWidth + aPos[ i++ ];
1306             if( nAsc > nMaxAscent )
1307                 nMaxAscent = nAsc;
1308             if( aSize.Height() - nAsc > nMaxDescent )
1309                 nMaxDescent = static_cast<sal_uInt16>(aSize.Height() - nAsc);
1310         }
1311         // for one or two characters we double the width of the portion
1312         if( nCount < 3 )
1313         {
1314             nMaxWidth *= 2;
1315             Width( 2*Width() );
1316             if( nCount < 2 )
1317             {
1318                 Height( nMaxAscent + nMaxDescent );
1319                 nLowPos = nMaxDescent;
1320             }
1321         }
1322         Height( Height() + nMaxDescent + nMaxAscent );
1323         nUpPos = nMaxAscent;
1324         SetAscent( Height() - nMaxDescent - nLowPos );
1325     } while( nProportion > 40 && ( GetAscent() > nMainAscent ||
1326                                     Height() - GetAscent() > nMainDescent ) );
1327     // if the combined portion is smaller than the surrounding text,
1328     // the portion grows. This looks better, if there's a character background.
1329     if( GetAscent() < nMainAscent )
1330     {
1331         Height( Height() + nMainAscent - GetAscent() );
1332         SetAscent( nMainAscent );
1333     }
1334     if( Height() < nMainAscent + nMainDescent )
1335         Height( nMainAscent + nMainDescent );
1336 
1337     // We calculate the x positions of the characters in both lines..
1338     sal_uInt16 nTopDiff = 0;
1339     sal_uInt16 nBotDiff = 0;
1340     if( nMaxWidth > Width() )
1341     {
1342         nTopDiff = ( nMaxWidth - Width() ) / 2;
1343         Width( nMaxWidth );
1344     }
1345     else
1346         nBotDiff = ( Width() - nMaxWidth ) / 2;
1347     switch( nTop)
1348     {
1349         case 3: aPos[1] = aPos[0] + nTopDiff;  // no break
1350         case 2: aPos[nTop-1] = Width() - aPos[nTop-1];
1351     }
1352     aPos[0] = 0;
1353     switch( nCount )
1354     {
1355         case 5: aPos[4] = aPos[3] + nBotDiff;   // no break
1356         case 3: aPos[nTop] = nBotDiff;          break;
1357         case 6: aPos[4] = aPos[3] + nBotDiff;   // no break
1358         case 4: aPos[nTop] = 0;                 // no break
1359         case 2: aPos[nCount-1] = Width() - aPos[nCount-1];
1360     }
1361 
1362     // Does the combined portion fit the line?
1363     const sal_Bool bFull = rInf.Width() < rInf.X() + Width();
1364     if( bFull )
1365     {
1366         if( rInf.GetLineStart() == rInf.GetIdx() && (!rInf.GetLast()->InFldGrp()
1367             || !((SwFldPortion*)rInf.GetLast())->IsFollow() ) )
1368             Width( (sal_uInt16)( rInf.Width() - rInf.X() ) );
1369         else
1370         {
1371             Truncate();
1372             Width( 0 );
1373             SetLen( 0 );
1374             if( rInf.GetLast() )
1375                 rInf.GetLast()->FormatEOL( rInf );
1376         }
1377     }
1378     return bFull;
1379 }
1380 
1381 /*************************************************************************
1382  * SwCombinedPortion::GetViewWidth(..)
1383  *************************************************************************/
1384 
1385 KSHORT SwCombinedPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const
1386 {
1387     if( !GetLen() ) // for the dummy part at the end of the line, where
1388         return 0;   // the combined portion doesn't fit.
1389     return SwFldPortion::GetViewWidth( rInf );
1390 }
1391