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