Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <hintids.hxx>
21 :
22 : #include <com/sun/star/i18n/ScriptType.hpp>
23 : #include <vcl/graph.hxx>
24 : #include <editeng/brushitem.hxx>
25 : #include <vcl/metric.hxx>
26 : #include <vcl/outdev.hxx>
27 : #include <viewopt.hxx>
28 : #include <SwPortionHandler.hxx>
29 : #include <porlay.hxx>
30 : #include <porfld.hxx>
31 : #include <inftxt.hxx>
32 : #include <blink.hxx>
33 : #include <frmtool.hxx>
34 : #include <viewsh.hxx>
35 : #include <docsh.hxx>
36 : #include <doc.hxx>
37 : #include <IDocumentSettingAccess.hxx>
38 : #include "rootfrm.hxx"
39 : #include <breakit.hxx>
40 : #include <porrst.hxx>
41 : #include <porftn.hxx>
42 : #include <accessibilityoptions.hxx>
43 : #include <editeng/lrspitem.hxx>
44 : #include <unicode/ubidi.h>
45 :
46 : using namespace ::com::sun::star;
47 :
48 20741 : SwLinePortion *SwFieldPortion::Compress()
49 20741 : { return (GetLen() || !aExpand.isEmpty() || SwLinePortion::Compress()) ? this : 0; }
50 :
51 180 : SwFieldPortion *SwFieldPortion::Clone( const OUString &rExpand ) const
52 : {
53 : SwFont *pNewFnt;
54 180 : if( 0 != ( pNewFnt = pFnt ) )
55 : {
56 157 : pNewFnt = new SwFont( *pFnt );
57 : }
58 : // #i107143#
59 : // pass placeholder property to created <SwFieldPortion> instance.
60 180 : SwFieldPortion* pClone = new SwFieldPortion( rExpand, pNewFnt, bPlaceHolder );
61 180 : pClone->SetNextOffset( nNextOffset );
62 180 : pClone->m_bNoLength = this->m_bNoLength;
63 180 : return pClone;
64 : }
65 :
66 0 : void SwFieldPortion::TakeNextOffset( const SwFieldPortion* pField )
67 : {
68 : OSL_ENSURE( pField, "TakeNextOffset: Missing Source" );
69 0 : nNextOffset = pField->GetNextOffset();
70 0 : aExpand = aExpand.replaceAt( 0, nNextOffset, "" );
71 0 : bFollow = true;
72 0 : }
73 :
74 20738 : SwFieldPortion::SwFieldPortion( const OUString &rExpand, SwFont *pFont, bool bPlaceHold )
75 : : aExpand(rExpand), pFnt(pFont), nNextOffset(0), nNextScriptChg(COMPLETE_STRING), nViewWidth(0)
76 : , bFollow( false ), bLeft( false), bHide( false)
77 : , bCenter (false), bHasFollow( false )
78 : , bAnimated( false), bNoPaint( false)
79 : , bReplace( false), bPlaceHolder( bPlaceHold )
80 : , m_bNoLength( false )
81 20738 : , m_nAttrFieldType(0)
82 : {
83 20738 : SetWhichPor( POR_FLD );
84 20738 : }
85 :
86 13 : SwFieldPortion::SwFieldPortion( const SwFieldPortion& rField )
87 : : SwExpandPortion( rField )
88 : , aExpand( rField.GetExp() )
89 13 : , nNextOffset( rField.GetNextOffset() )
90 13 : , nNextScriptChg( rField.GetNextScriptChg() )
91 : , nViewWidth( rField.nViewWidth )
92 13 : , bFollow( rField.IsFollow() )
93 13 : , bLeft( rField.IsLeft() )
94 13 : , bHide( rField.IsHide() )
95 13 : , bCenter( rField.IsCenter() )
96 13 : , bHasFollow( rField.HasFollow() )
97 : , bAnimated ( rField.bAnimated )
98 : , bNoPaint( rField.bNoPaint)
99 : , bReplace( rField.bReplace )
100 : , bPlaceHolder( rField.bPlaceHolder )
101 : , m_bNoLength( rField.m_bNoLength )
102 104 : , m_nAttrFieldType( rField.m_nAttrFieldType)
103 : {
104 13 : if ( rField.HasFont() )
105 13 : pFnt = new SwFont( *rField.GetFont() );
106 : else
107 0 : pFnt = 0;
108 :
109 13 : SetWhichPor( POR_FLD );
110 13 : }
111 :
112 46230 : SwFieldPortion::~SwFieldPortion()
113 : {
114 20751 : delete pFnt;
115 20751 : if( pBlink )
116 34 : pBlink->Delete( this );
117 25479 : }
118 :
119 518 : sal_uInt16 SwFieldPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
120 : {
121 : // even though this is const, nViewWidth should be computed at the very end:
122 518 : SwFieldPortion* pThis = const_cast<SwFieldPortion*>(this);
123 2072 : if( !Width() && rInf.OnWin() && !rInf.GetOpt().IsPagePreview() &&
124 1554 : !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() )
125 : {
126 518 : if( !nViewWidth )
127 242 : pThis->nViewWidth = rInf.GetTextSize(OUString(' ')).Width();
128 : }
129 : else
130 0 : pThis->nViewWidth = 0;
131 518 : return nViewWidth;
132 : }
133 :
134 : /**
135 : * Never just use SetLen(0)
136 : */
137 : class SwFieldSlot
138 : {
139 : std::shared_ptr<vcl::TextLayoutCache> m_pOldCachedVclData;
140 : const OUString *pOldText;
141 : OUString aText;
142 : sal_Int32 nIdx;
143 : sal_Int32 nLen;
144 : SwTextFormatInfo *pInf;
145 : bool bOn;
146 : public:
147 : SwFieldSlot( const SwTextFormatInfo* pNew, const SwFieldPortion *pPor );
148 : ~SwFieldSlot();
149 : };
150 :
151 20565 : SwFieldSlot::SwFieldSlot( const SwTextFormatInfo* pNew, const SwFieldPortion *pPor )
152 : : pOldText(NULL)
153 : , nIdx(0)
154 : , nLen(0)
155 20565 : , pInf(NULL)
156 : {
157 20565 : bOn = pPor->GetExpText( *pNew, aText );
158 :
159 : // The text will be replaced ...
160 20565 : if( bOn )
161 : {
162 20565 : pInf = const_cast<SwTextFormatInfo*>(pNew);
163 20565 : nIdx = pInf->GetIdx();
164 20565 : nLen = pInf->GetLen();
165 20565 : pOldText = &(pInf->GetText());
166 20565 : m_pOldCachedVclData = pInf->GetCachedVclData();
167 20565 : pInf->SetLen( aText.getLength() );
168 20565 : pInf->SetCachedVclData(nullptr);
169 20565 : if( pPor->IsFollow() )
170 : {
171 8904 : pInf->SetFakeLineStart( nIdx > pInf->GetLineStart() );
172 8904 : pInf->SetIdx( 0 );
173 : }
174 : else
175 : {
176 11661 : aText = (*pOldText).replaceAt(nIdx, 1, aText);
177 : }
178 20565 : pInf->SetText( aText );
179 : }
180 20565 : }
181 :
182 41130 : SwFieldSlot::~SwFieldSlot()
183 : {
184 20565 : if( bOn )
185 : {
186 20565 : pInf->SetCachedVclData(m_pOldCachedVclData);
187 20565 : pInf->SetText( *pOldText );
188 20565 : pInf->SetIdx( nIdx );
189 20565 : pInf->SetLen( nLen );
190 20565 : pInf->SetFakeLineStart( false );
191 : }
192 20565 : }
193 :
194 20565 : void SwFieldPortion::CheckScript( const SwTextSizeInfo &rInf )
195 : {
196 20565 : OUString aText;
197 20565 : if( GetExpText( rInf, aText ) && !aText.isEmpty() && g_pBreakIt->GetBreakIter().is() )
198 : {
199 13767 : sal_uInt8 nActual = pFnt ? pFnt->GetActual() : rInf.GetFont()->GetActual();
200 13767 : sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType( aText, 0 );
201 13767 : sal_Int32 nChg = 0;
202 13767 : if( i18n::ScriptType::WEAK == nScript )
203 : {
204 2765 : nChg = g_pBreakIt->GetBreakIter()->endOfScript(aText,0,nScript);
205 2765 : if (nChg < aText.getLength() && nChg >= 0)
206 2365 : nScript = g_pBreakIt->GetBreakIter()->getScriptType( aText, nChg );
207 : }
208 :
209 : // nNextScriptChg will be evaluated during SwFieldPortion::Format()
210 :
211 13767 : if (nChg < aText.getLength() && nChg >= 0)
212 13367 : nNextScriptChg = g_pBreakIt->GetBreakIter()->endOfScript( aText, nChg, nScript );
213 : else
214 400 : nNextScriptChg = aText.getLength();
215 :
216 : sal_uInt8 nTmp;
217 13767 : switch ( nScript ) {
218 11133 : case i18n::ScriptType::LATIN : nTmp = SW_LATIN; break;
219 2216 : case i18n::ScriptType::ASIAN : nTmp = SW_CJK; break;
220 18 : case i18n::ScriptType::COMPLEX : nTmp = SW_CTL; break;
221 400 : default: nTmp = nActual;
222 : }
223 :
224 : // #i16354# Change script type for RTL text to CTL.
225 13767 : const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
226 : // #i98418#
227 20811 : const sal_uInt8 nFieldDir = ( IsNumberPortion() || IsFootnoteNumPortion() ) ?
228 : rSI.GetDefaultDir() :
229 20636 : rSI.DirType( IsFollow() ? rInf.GetIdx() - 1 : rInf.GetIdx() );
230 :
231 13767 : bool bPerformUBA = UBIDI_LTR != nFieldDir || i18n::ScriptType::COMPLEX == nScript;
232 13767 : if (bPerformUBA)
233 : {
234 18 : UErrorCode nError = U_ZERO_ERROR;
235 18 : UBiDi* pBidi = ubidi_openSized( aText.getLength(), 0, &nError );
236 18 : ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aText.getStr()), aText.getLength(), nFieldDir, NULL, &nError );
237 : int32_t nEnd;
238 : UBiDiLevel nCurrDir;
239 18 : ubidi_getLogicalRun( pBidi, 0, &nEnd, &nCurrDir );
240 18 : ubidi_close( pBidi );
241 18 : const sal_Int32 nNextDirChg = nEnd;
242 18 : nNextScriptChg = std::min( nNextScriptChg, nNextDirChg );
243 :
244 : // #i89825# change the script type also to CTL
245 : // if there is no strong LTR char in the LTR run (numbers)
246 18 : if ( nCurrDir != UBIDI_RTL )
247 : {
248 0 : nCurrDir = UBIDI_RTL;
249 0 : for( sal_Int32 nCharIdx = 0; nCharIdx < nEnd; ++nCharIdx )
250 : {
251 0 : UCharDirection nCharDir = u_charDirection ( aText[ nCharIdx ]);
252 0 : if ( nCharDir == U_LEFT_TO_RIGHT ||
253 0 : nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
254 : nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
255 : {
256 0 : nCurrDir = UBIDI_LTR;
257 0 : break;
258 : }
259 : }
260 : }
261 :
262 18 : if (nCurrDir == UBIDI_RTL)
263 : {
264 18 : nTmp = SW_CTL;
265 : // If we decided that this range was RTL after all and the
266 : // previous range was complex but clipped to the start of this
267 : // range, then extend it to be complex over the additional RTL range
268 18 : if (nScript == i18n::ScriptType::COMPLEX)
269 18 : nNextScriptChg = nNextDirChg;
270 : }
271 : }
272 :
273 : // #i98418#
274 : // keep determined script type for footnote portions as preferred script type.
275 : // For footnote portions a font can not be created directly - see footnote
276 : // portion format method.
277 13767 : if ( IsFootnotePortion() )
278 : {
279 145 : static_cast<SwFootnotePortion*>(this)->SetPreferredScriptType( nTmp );
280 : }
281 13622 : else if ( nTmp != nActual )
282 : {
283 4450 : if( !pFnt )
284 0 : pFnt = new SwFont( *rInf.GetFont() );
285 4450 : pFnt->SetActual( nTmp );
286 : }
287 20565 : }
288 20565 : }
289 :
290 20565 : bool SwFieldPortion::Format( SwTextFormatInfo &rInf )
291 : {
292 : // Scope wegen aDiffText::DTOR!
293 : sal_Int32 nRest;
294 20565 : bool bFull = false;
295 20565 : bool bEOL = false;
296 20565 : const sal_Int32 nTextRest = rInf.GetText().getLength() - rInf.GetIdx();
297 : {
298 20565 : SwFieldSlot aDiffText( &rInf, this );
299 41130 : SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
300 20565 : aLayoutModeModifier.SetAuto();
301 :
302 : // Field portion has to be split in several parts if
303 : // 1. There are script/direction changes inside the field
304 : // 2. There are portion breaks (tab, break) inside the field:
305 20565 : const sal_Int32 nOldFullLen = rInf.GetLen();
306 20565 : sal_Int32 nFullLen = rInf.ScanPortionEnd( rInf.GetIdx(), rInf.GetIdx() + nOldFullLen ) - rInf.GetIdx();
307 20565 : if ( nNextScriptChg < nFullLen )
308 : {
309 2244 : nFullLen = nNextScriptChg;
310 2244 : rInf.SetHookChar( 0 );
311 : }
312 20565 : rInf.SetLen( nFullLen );
313 :
314 20624 : if ( COMPLETE_STRING != rInf.GetUnderScorePos() &&
315 59 : rInf.GetUnderScorePos() > rInf.GetIdx() )
316 0 : rInf.SetUnderScorePos( rInf.GetIdx() );
317 :
318 20565 : if( pFnt )
319 15910 : pFnt->GoMagic( rInf.GetVsh(), pFnt->GetActual() );
320 :
321 41130 : SwFontSave aSave( rInf, pFnt );
322 :
323 : // Length must be 0: the length is set for bFull after format
324 : // and passed along in nRest. Or else the old length would be
325 : // retained and be used for nRest!
326 20565 : SetLen(0);
327 20565 : const sal_Int32 nFollow = IsFollow() ? 0 : 1;
328 :
329 : // As odd is may seem: the query for GetLen() must return false due
330 : // to the ExpandPortions _after_ aDiffText (see SoftHyphs), caused
331 : // by SetFull.
332 20565 : if( !nFullLen )
333 : {
334 : // Don't Init(), as we need height and ascent
335 7154 : Width(0);
336 7154 : bFull = rInf.Width() <= rInf.GetPos().X();
337 : }
338 : else
339 : {
340 13411 : sal_Int32 nOldLineStart = rInf.GetLineStart();
341 13411 : if( IsFollow() )
342 2414 : rInf.SetLineStart( 0 );
343 13411 : rInf.SetNotEOL( nFullLen == nOldFullLen && nTextRest > nFollow );
344 :
345 : // the height depending on the fields font is set,
346 : // this is required for SwTextGuess::Guess
347 13411 : Height( rInf.GetTextHeight() + rInf.GetFont()->GetTopBorderSpace() +
348 13411 : rInf.GetFont()->GetBottomBorderSpace() );
349 : // If a kerning portion is inserted after our field portion,
350 : // the ascent and height must be known
351 13411 : SetAscent( rInf.GetAscent() + rInf.GetFont()->GetTopBorderSpace() );
352 13411 : bFull = SwTextPortion::Format( rInf );
353 13411 : rInf.SetNotEOL( false );
354 13411 : rInf.SetLineStart( nOldLineStart );
355 : }
356 20565 : sal_Int32 nTmpLen = GetLen();
357 20565 : bEOL = !nTmpLen && nFollow && bFull;
358 20565 : nRest = nOldFullLen - nTmpLen;
359 :
360 : // The char is held in the first position
361 : // Unconditionally after format!
362 20565 : SetLen( (m_bNoLength) ? 0 : nFollow );
363 :
364 20565 : if( nRest )
365 : {
366 : // aExpand has not yet been shortened; the new Ofst is a
367 : // result of nRest
368 8757 : sal_Int32 nNextOfst = aExpand.getLength() - nRest;
369 :
370 8757 : if ( IsQuoVadisPortion() )
371 0 : nNextOfst = nNextOfst + static_cast<SwQuoVadisPortion*>(this)->GetContText().getLength();
372 :
373 8757 : OUString aNew( aExpand.copy( nNextOfst ) );
374 8757 : aExpand = aExpand.copy( 0, nNextOfst );
375 :
376 : // These characters should not be contained in the follow
377 : // field portion. They are handled via the HookChar mechanism.
378 8757 : switch( aNew[0] )
379 : {
380 0 : case CH_BREAK : bFull = true;
381 : // no break
382 : case ' ' :
383 : case CH_TAB :
384 : case CHAR_HARDHYPHEN: // non-breaking hyphen
385 : case CHAR_SOFTHYPHEN:
386 : case CHAR_HARDBLANK:
387 : case CHAR_ZWSP :
388 : case CHAR_ZWNBSP :
389 : case CH_TXTATR_BREAKWORD:
390 : case CH_TXTATR_INWORD:
391 : {
392 6490 : aNew = aNew.copy( 1 );
393 6490 : ++nNextOfst;
394 6490 : break;
395 : }
396 : default: ;
397 : }
398 :
399 : // Even if there is no more text left for a follow field,
400 : // we have to build a follow field portion (without font),
401 : // otherwise the HookChar mechanism would not work.
402 8757 : SwFieldPortion *pField = Clone( aNew );
403 8757 : if( !aNew.isEmpty() && !pField->GetFont() )
404 : {
405 23 : SwFont *pNewFnt = new SwFont( *rInf.GetFont() );
406 23 : pField->SetFont( pNewFnt );
407 : }
408 8757 : pField->SetFollow( true );
409 8757 : SetHasFollow( true );
410 :
411 : // For a newly created field, nNextOffset contains the Offset
412 : // of it's start of the original string
413 : // If a FollowField is created when formatting, this FollowField's
414 : // Offset is being held in nNextOffset
415 8757 : nNextOffset = nNextOffset + nNextOfst;
416 8757 : pField->SetNextOffset( nNextOffset );
417 8757 : rInf.SetRest( pField );
418 20565 : }
419 : }
420 :
421 20565 : if( bEOL && rInf.GetLast() && !rInf.GetUnderflow() )
422 11 : rInf.GetLast()->FormatEOL( rInf );
423 20565 : return bFull;
424 : }
425 :
426 1479 : void SwFieldPortion::Paint( const SwTextPaintInfo &rInf ) const
427 : {
428 1479 : SwFontSave aSave( rInf, pFnt );
429 :
430 : OSL_ENSURE( GetLen() <= 1, "SwFieldPortion::Paint: rest-portion pollution?" );
431 1479 : if( Width() && ( !bPlaceHolder || rInf.GetOpt().IsShowPlaceHolderFields() ) )
432 : {
433 : // A very liberal use of the background
434 1193 : rInf.DrawViewOpt( *this, POR_FLD );
435 1193 : SwExpandPortion::Paint( rInf );
436 1479 : }
437 1479 : }
438 :
439 47465 : bool SwFieldPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const
440 : {
441 47465 : rText = aExpand;
442 109851 : if( rText.isEmpty() && rInf.OnWin() &&
443 1554 : !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() &&
444 48501 : SwViewOption::IsFieldShadings() &&
445 518 : !HasFollow() )
446 518 : rText = " ";
447 47465 : return true;
448 : }
449 :
450 102 : void SwFieldPortion::HandlePortion( SwPortionHandler& rPH ) const
451 : {
452 102 : sal_Int32 nH = 0;
453 102 : sal_Int32 nW = 0;
454 102 : if (pFnt)
455 : {
456 57 : nH = pFnt->GetSize(pFnt->GetActual()).Height();
457 57 : nW = pFnt->GetSize(pFnt->GetActual()).Width();
458 : }
459 102 : rPH.Special( GetLen(), aExpand, GetWhichPor(), nH, nW, pFnt );
460 102 : if( GetWhichPor() == POR_FLD )
461 : {
462 36 : rPH.SetAttrFieldType(m_nAttrFieldType);
463 : }
464 102 : }
465 :
466 0 : SwPosSize SwFieldPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
467 : {
468 0 : SwFontSave aSave( rInf, pFnt );
469 0 : SwPosSize aSize( SwExpandPortion::GetTextSize( rInf ) );
470 0 : return aSize;
471 : }
472 :
473 0 : SwFieldPortion *SwHiddenPortion::Clone(const OUString &rExpand ) const
474 : {
475 : SwFont *pNewFnt;
476 0 : if( 0 != ( pNewFnt = pFnt ) )
477 0 : pNewFnt = new SwFont( *pFnt );
478 0 : return new SwHiddenPortion( rExpand, pNewFnt );
479 : }
480 :
481 0 : void SwHiddenPortion::Paint( const SwTextPaintInfo &rInf ) const
482 : {
483 0 : if( Width() )
484 : {
485 0 : SwFontSave aSave( rInf, pFnt );
486 0 : rInf.DrawViewOpt( *this, POR_HIDDEN );
487 0 : SwExpandPortion::Paint( rInf );
488 : }
489 0 : }
490 :
491 0 : bool SwHiddenPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const
492 : {
493 : // Do not query for IsHidden()!
494 0 : return SwFieldPortion::GetExpText( rInf, rText );
495 : }
496 :
497 15878 : SwNumberPortion::SwNumberPortion( const OUString &rExpand,
498 : SwFont *pFont,
499 : const bool bLft,
500 : const bool bCntr,
501 : const sal_uInt16 nMinDst,
502 : const bool bLabelAlignmentPosAndSpaceModeActive )
503 : : SwFieldPortion( rExpand, pFont ),
504 : nFixWidth(0),
505 : nMinDist( nMinDst ),
506 15878 : mbLabelAlignmentPosAndSpaceModeActive( bLabelAlignmentPosAndSpaceModeActive )
507 : {
508 15878 : SetWhichPor( POR_NUMBER );
509 15878 : SetLeft( bLft );
510 15878 : SetHide( false );
511 15878 : SetCenter( bCntr );
512 15878 : }
513 :
514 0 : sal_Int32 SwNumberPortion::GetCrsrOfst( const sal_uInt16 ) const
515 : {
516 0 : return 0;
517 : }
518 :
519 8734 : SwFieldPortion *SwNumberPortion::Clone( const OUString &rExpand ) const
520 : {
521 : SwFont *pNewFnt;
522 8734 : if( 0 != ( pNewFnt = pFnt ) )
523 8664 : pNewFnt = new SwFont( *pFnt );
524 :
525 17468 : return new SwNumberPortion( rExpand, pNewFnt, IsLeft(), IsCenter(),
526 17468 : nMinDist, mbLabelAlignmentPosAndSpaceModeActive );
527 : }
528 :
529 : /**
530 : * We can create multiple NumFields
531 : * Tricky, if one enters enough previous-text in the dialog box
532 : * to cause the line to overflow
533 : * We need to keep the Fly's evasion tactics in mind
534 : */
535 15796 : bool SwNumberPortion::Format( SwTextFormatInfo &rInf )
536 : {
537 15796 : SetHide( false );
538 15796 : const bool bFull = SwFieldPortion::Format( rInf );
539 15796 : SetLen( 0 );
540 : // a numbering portion can be contained in a rotated portion!!!
541 15796 : nFixWidth = rInf.IsMulti() ? Height() : Width();
542 15796 : rInf.SetNumDone( !rInf.GetRest() );
543 15796 : if( rInf.IsNumDone() )
544 : {
545 : // SetAscent( rInf.GetAscent() );
546 : OSL_ENSURE( Height() && nAscent, "NumberPortions without Height | Ascent" );
547 :
548 7132 : long nDiff( 0 );
549 :
550 7132 : if ( !mbLabelAlignmentPosAndSpaceModeActive )
551 : {
552 1151 : if ( !rInf.GetTextFrm()->GetTextNode()->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) &&
553 : // #i32902#
554 568 : !IsFootnoteNumPortion() )
555 : {
556 436 : nDiff = rInf.Left()
557 872 : + rInf.GetTextFrm()->GetTextNode()->
558 436 : GetSwAttrSet().GetLRSpace().GetTextFirstLineOfst()
559 436 : - rInf.First()
560 436 : + rInf.ForcedLeftMargin();
561 : }
562 : else
563 : {
564 147 : nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
565 : }
566 : }
567 : // The text part of the numbering should always at least
568 : // start at the left margin
569 7132 : if( nDiff < 0 )
570 3 : nDiff = 0;
571 7129 : else if ( nDiff > rInf.X() )
572 533 : nDiff -= rInf.X();
573 : else
574 6596 : nDiff = 0;
575 :
576 7132 : if( nDiff < nFixWidth + nMinDist )
577 155 : nDiff = nFixWidth + nMinDist;
578 :
579 : // Numbering evades the Fly, no nDiff in the second round
580 : // Tricky special case: FlyFrm is in an Area we're just about to
581 : // acquire
582 : // The NumberPortion is marked as hidden
583 14264 : const bool bFly = rInf.GetFly() ||
584 21396 : ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
585 7132 : if( nDiff > rInf.Width() )
586 : {
587 0 : nDiff = rInf.Width();
588 0 : if ( bFly )
589 0 : SetHide( true );
590 : }
591 :
592 : // A numbering portion can be inside a SwRotatedPortion. Then the
593 : // Height has to be changed
594 7132 : if ( rInf.IsMulti() )
595 : {
596 0 : if ( Height() < nDiff )
597 0 : Height( sal_uInt16( nDiff ) );
598 : }
599 7132 : else if( Width() < nDiff )
600 493 : Width( sal_uInt16(nDiff) );
601 : }
602 15796 : return bFull;
603 : }
604 :
605 :
606 : /**
607 : * A FormatEOL indicates that the subsequent text did not fit onto
608 : * the line anymore. In order for the Numbering to follow through,
609 : * we hide this NumberPortion
610 : */
611 14 : void SwNumberPortion::FormatEOL( SwTextFormatInfo& )
612 : {
613 :
614 : // This caused trouble with flys anchored as characters.
615 : // If one of these is numbered but does not fit to the line,
616 : // it calls this function, causing a loop because both the number
617 : // portion and the fly portion go to the next line
618 : // SetHide( true );
619 14 : }
620 :
621 :
622 : /**
623 : * A hidden NumberPortion is not displayed, unless there are TextPortions in
624 : * this line or there's just one line at all
625 : */
626 681 : void SwNumberPortion::Paint( const SwTextPaintInfo &rInf ) const
627 : {
628 681 : if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
629 : {
630 0 : SwLinePortion *pTmp = GetPortion();
631 0 : while ( pTmp && !pTmp->InTextGrp() )
632 0 : pTmp = pTmp->GetPortion();
633 0 : if ( !pTmp )
634 681 : return;
635 : }
636 :
637 : // calculate the width of the number portion, including follows
638 681 : const sal_uInt16 nOldWidth = Width();
639 681 : sal_uInt16 nSumWidth = 0;
640 681 : sal_uInt16 nOffset = 0;
641 :
642 681 : const SwLinePortion* pTmp = this;
643 1605 : while ( pTmp && pTmp->InNumberGrp() )
644 : {
645 731 : nSumWidth = nSumWidth + pTmp->Width();
646 731 : if ( static_cast<const SwNumberPortion*>(pTmp)->HasFollow() )
647 243 : pTmp = pTmp->GetPortion();
648 : else
649 : {
650 488 : nOffset = pTmp->Width() - static_cast<const SwNumberPortion*>(pTmp)->nFixWidth;
651 488 : break;
652 : }
653 : }
654 :
655 : // The master portion takes care for painting the background of the
656 : // follow field portions
657 681 : if ( ! IsFollow() )
658 : {
659 631 : SwLinePortion *pThis = const_cast<SwLinePortion*>(static_cast<SwLinePortion const *>(this));
660 631 : pThis->Width( nSumWidth );
661 631 : rInf.DrawViewOpt( *this, POR_NUMBER );
662 631 : pThis->Width( nOldWidth );
663 : }
664 :
665 681 : if( !aExpand.isEmpty() )
666 : {
667 681 : const SwFont *pTmpFnt = rInf.GetFont();
668 1362 : bool bPaintSpace = ( UNDERLINE_NONE != pTmpFnt->GetUnderline() ||
669 1362 : UNDERLINE_NONE != pTmpFnt->GetOverline() ||
670 1362 : STRIKEOUT_NONE != pTmpFnt->GetStrikeout() ) &&
671 681 : !pTmpFnt->IsWordLineMode();
672 681 : if( bPaintSpace && pFnt )
673 0 : bPaintSpace = ( UNDERLINE_NONE != pFnt->GetUnderline() ||
674 0 : UNDERLINE_NONE != pFnt->GetOverline() ||
675 0 : STRIKEOUT_NONE != pFnt->GetStrikeout() ) &&
676 0 : !pFnt->IsWordLineMode();
677 :
678 681 : SwFontSave aSave( rInf, pFnt );
679 :
680 681 : if( nFixWidth == Width() && ! HasFollow() )
681 48 : SwExpandPortion::Paint( rInf );
682 : else
683 : {
684 : // logical const: reset width
685 633 : SwLinePortion *pThis = const_cast<SwLinePortion*>(static_cast<SwLinePortion const *>(this));
686 633 : bPaintSpace = bPaintSpace && nFixWidth < nOldWidth;
687 633 : sal_uInt16 nSpaceOffs = nFixWidth;
688 633 : pThis->Width( nFixWidth );
689 :
690 1266 : if( ( IsLeft() && ! rInf.GetTextFrm()->IsRightToLeft() ) ||
691 0 : ( ! IsLeft() && ! IsCenter() && rInf.GetTextFrm()->IsRightToLeft() ) )
692 633 : SwExpandPortion::Paint( rInf );
693 : else
694 : {
695 0 : SwTextPaintInfo aInf( rInf );
696 0 : if( nOffset < nMinDist )
697 0 : nOffset = 0;
698 : else
699 : {
700 0 : if( IsCenter() )
701 : {
702 : /* #110778# a / 2 * 2 == a is not a tautology */
703 0 : sal_uInt16 nTmpOffset = nOffset;
704 0 : nOffset /= 2;
705 0 : if( nOffset < nMinDist )
706 0 : nOffset = nTmpOffset - nMinDist;
707 : }
708 : else
709 0 : nOffset = nOffset - nMinDist;
710 : }
711 0 : aInf.X( aInf.X() + nOffset );
712 0 : SwExpandPortion::Paint( aInf );
713 0 : if( bPaintSpace )
714 0 : nSpaceOffs = nSpaceOffs + nOffset;
715 : }
716 633 : if( bPaintSpace && nOldWidth > nSpaceOffs )
717 : {
718 0 : SwTextPaintInfo aInf( rInf );
719 0 : aInf.X( aInf.X() + nSpaceOffs );
720 :
721 : // #i53199# Adjust position of underline:
722 0 : if ( rInf.GetUnderFnt() )
723 : {
724 0 : const Point aNewPos( aInf.GetPos().X(), rInf.GetUnderFnt()->GetPos().Y() );
725 0 : rInf.GetUnderFnt()->SetPos( aNewPos );
726 : }
727 :
728 0 : pThis->Width( nOldWidth - nSpaceOffs + 12 );
729 : {
730 0 : SwTextSlot aDiffText( &aInf, this, true, false, " " );
731 0 : aInf.DrawText( *this, aInf.GetLen(), true );
732 0 : }
733 : }
734 633 : pThis->Width( nOldWidth );
735 681 : }
736 : }
737 : }
738 :
739 2443 : SwBulletPortion::SwBulletPortion( const sal_Unicode cBullet,
740 : const OUString& rBulletFollowedBy,
741 : SwFont *pFont,
742 : const bool bLft,
743 : const bool bCntr,
744 : const sal_uInt16 nMinDst,
745 : const bool bLabelAlignmentPosAndSpaceModeActive )
746 4886 : : SwNumberPortion( OUString(cBullet) + rBulletFollowedBy,
747 : pFont, bLft, bCntr, nMinDst,
748 7329 : bLabelAlignmentPosAndSpaceModeActive )
749 : {
750 2443 : SetWhichPor( POR_BULLET );
751 2443 : }
752 :
753 : #define GRFNUM_SECURE 10
754 :
755 70 : SwGrfNumPortion::SwGrfNumPortion(
756 : SwFrm*,
757 : const OUString& rGraphicFollowedBy,
758 : const SvxBrushItem* pGrfBrush,
759 : const SwFormatVertOrient* pGrfOrient, const Size& rGrfSize,
760 : const bool bLft, const bool bCntr, const sal_uInt16 nMinDst,
761 : const bool bLabelAlignmentPosAndSpaceModeActive ) :
762 : SwNumberPortion( rGraphicFollowedBy, NULL, bLft, bCntr, nMinDst,
763 : bLabelAlignmentPosAndSpaceModeActive ),
764 70 : pBrush( new SvxBrushItem(RES_BACKGROUND) ), nId( 0 )
765 : {
766 70 : SetWhichPor( POR_GRFNUM );
767 70 : SetAnimated( false );
768 70 : bReplace = false;
769 70 : if( pGrfBrush )
770 : {
771 70 : *pBrush = *pGrfBrush;
772 70 : const Graphic* pGraph = pGrfBrush->GetGraphic();
773 70 : if( pGraph )
774 70 : SetAnimated( pGraph->IsAnimated() );
775 : else
776 0 : bReplace = true;
777 : }
778 70 : if( pGrfOrient )
779 : {
780 0 : nYPos = pGrfOrient->GetPos();
781 0 : eOrient = pGrfOrient->GetVertOrient();
782 : }
783 : else
784 : {
785 70 : nYPos = 0;
786 70 : eOrient = text::VertOrientation::TOP;
787 : }
788 70 : Width( static_cast<sal_uInt16>(rGrfSize.Width() + 2 * GRFNUM_SECURE) );
789 70 : nFixWidth = Width();
790 70 : nGrfHeight = rGrfSize.Height() + 2 * GRFNUM_SECURE;
791 70 : Height( sal_uInt16(nGrfHeight) );
792 70 : bNoPaint = false;
793 70 : }
794 :
795 210 : SwGrfNumPortion::~SwGrfNumPortion()
796 : {
797 70 : if ( IsAnimated() )
798 : {
799 0 : Graphic* pGraph = const_cast<Graphic*>(pBrush->GetGraphic());
800 0 : if (pGraph)
801 0 : pGraph->StopAnimation( 0, nId );
802 : }
803 70 : delete pBrush;
804 140 : }
805 :
806 0 : void SwGrfNumPortion::StopAnimation( OutputDevice* pOut )
807 : {
808 0 : if ( IsAnimated() )
809 : {
810 0 : Graphic* pGraph = const_cast<Graphic*>(pBrush->GetGraphic());
811 0 : if (pGraph)
812 0 : pGraph->StopAnimation( pOut, nId );
813 : }
814 0 : }
815 :
816 70 : bool SwGrfNumPortion::Format( SwTextFormatInfo &rInf )
817 : {
818 70 : SetHide( false );
819 : // Width( nFixWidth );
820 70 : sal_uInt16 nFollowedByWidth( 0 );
821 70 : if ( mbLabelAlignmentPosAndSpaceModeActive )
822 : {
823 70 : SwFieldPortion::Format( rInf );
824 70 : nFollowedByWidth = Width();
825 70 : SetLen( 0 );
826 : }
827 70 : Width( nFixWidth + nFollowedByWidth );
828 70 : const bool bFull = rInf.Width() < rInf.X() + Width();
829 140 : const bool bFly = rInf.GetFly() ||
830 210 : ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
831 70 : SetAscent( static_cast<sal_uInt16>(GetRelPos() > 0 ? GetRelPos() : 0) );
832 70 : if( GetAscent() > Height() )
833 0 : Height( GetAscent() );
834 :
835 70 : if( bFull )
836 : {
837 0 : Width( rInf.Width() - (sal_uInt16)rInf.X() );
838 0 : if( bFly )
839 : {
840 0 : SetLen( 0 );
841 0 : SetNoPaint( true );
842 0 : rInf.SetNumDone( false );
843 0 : return true;
844 : }
845 : }
846 70 : rInf.SetNumDone( true );
847 : // long nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
848 : long nDiff = mbLabelAlignmentPosAndSpaceModeActive
849 : ? 0
850 70 : : rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
851 : // The TextPortion should at least always start on the
852 : // left margin
853 70 : if( nDiff < 0 )
854 0 : nDiff = 0;
855 70 : else if ( nDiff > rInf.X() )
856 0 : nDiff -= rInf.X();
857 70 : if( nDiff < nFixWidth + nMinDist )
858 70 : nDiff = nFixWidth + nMinDist;
859 :
860 : // Numbering evades Fly, no nDiff in the second round
861 : // Tricky special case: FlyFrm is in the Area we were just
862 : // about to get a hold of.
863 : // The NumberPortion is marked as hidden
864 70 : if( nDiff > rInf.Width() )
865 : {
866 0 : nDiff = rInf.Width();
867 0 : if( bFly )
868 0 : SetHide( true );
869 : }
870 :
871 70 : if( Width() < nDiff )
872 0 : Width( sal_uInt16(nDiff) );
873 70 : return bFull;
874 : }
875 :
876 :
877 : /**
878 : * A hidden NumberPortion is not displayed, unless there are TextPortions in
879 : * this line or there's only one line at all
880 : */
881 0 : void SwGrfNumPortion::Paint( const SwTextPaintInfo &rInf ) const
882 : {
883 0 : if( DontPaint() )
884 0 : return;
885 0 : if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
886 : {
887 0 : SwLinePortion *pTmp = GetPortion();
888 0 : while ( pTmp && !pTmp->InTextGrp() )
889 0 : pTmp = pTmp->GetPortion();
890 0 : if ( !pTmp )
891 0 : return;
892 : }
893 0 : Point aPos( rInf.X() + GRFNUM_SECURE, rInf.Y() - GetRelPos() + GRFNUM_SECURE );
894 0 : long nTmpWidth = std::max( (long)0, (long)(nFixWidth - 2 * GRFNUM_SECURE) );
895 0 : Size aSize( nTmpWidth, GetGrfHeight() - 2 * GRFNUM_SECURE );
896 :
897 0 : const bool bTmpLeft = mbLabelAlignmentPosAndSpaceModeActive ||
898 0 : ( IsLeft() && ! rInf.GetTextFrm()->IsRightToLeft() ) ||
899 0 : ( ! IsLeft() && ! IsCenter() && rInf.GetTextFrm()->IsRightToLeft() );
900 :
901 0 : if( nFixWidth < Width() && !bTmpLeft )
902 : {
903 0 : sal_uInt16 nOffset = Width() - nFixWidth;
904 0 : if( nOffset < nMinDist )
905 0 : nOffset = 0;
906 : else
907 : {
908 0 : if( IsCenter() )
909 : {
910 0 : nOffset /= 2;
911 0 : if( nOffset < nMinDist )
912 0 : nOffset = Width() - nFixWidth - nMinDist;
913 : }
914 : else
915 0 : nOffset = nOffset - nMinDist;
916 : }
917 0 : aPos.X() += nOffset;
918 : }
919 :
920 0 : if( bReplace )
921 : {
922 0 : const long nTmpH = GetPortion() ? GetPortion()->GetAscent() : 120;
923 0 : aSize = Size( nTmpH, nTmpH );
924 0 : aPos.Y() = rInf.Y() - nTmpH;
925 : }
926 0 : SwRect aTmp( aPos, aSize );
927 :
928 0 : bool bDraw = true;
929 :
930 0 : if ( IsAnimated() )
931 : {
932 0 : bDraw = !rInf.GetOpt().IsGraphic();
933 0 : if( !nId )
934 : {
935 0 : SetId( sal_IntPtr( rInf.GetTextFrm() ) );
936 0 : rInf.GetTextFrm()->SetAnimation();
937 : }
938 0 : if( aTmp.IsOver( rInf.GetPaintRect() ) && !bDraw )
939 : {
940 0 : rInf.NoteAnimation();
941 0 : const SwViewShell* pViewShell = rInf.GetVsh();
942 :
943 : // virtual device, not pdf export
944 0 : if( OUTDEV_VIRDEV == rInf.GetOut()->GetOutDevType() &&
945 0 : pViewShell && pViewShell->GetWin() )
946 : {
947 0 : Graphic* pGraph = const_cast<Graphic*>(pBrush->GetGraphic());
948 0 : if (pGraph)
949 0 : pGraph->StopAnimation(0,nId);
950 0 : rInf.GetTextFrm()->getRootFrm()->GetCurrShell()->InvalidateWindows( aTmp );
951 : }
952 :
953 0 : else if ( pViewShell &&
954 0 : !pViewShell->GetAccessibilityOptions()->IsStopAnimatedGraphics() &&
955 0 : !pViewShell->IsPreview() &&
956 : // #i9684# Stop animation during printing/pdf export.
957 0 : pViewShell->GetWin() )
958 : {
959 0 : Graphic* pGraph = const_cast<Graphic*>(pBrush->GetGraphic());
960 0 : if (pGraph)
961 : {
962 : pGraph->StartAnimation(
963 0 : const_cast<OutputDevice*>(rInf.GetOut()), aPos, aSize, nId );
964 : }
965 : }
966 :
967 : // pdf export, printing, preview, stop animations...
968 : else
969 0 : bDraw = true;
970 : }
971 0 : if( bDraw )
972 : {
973 :
974 0 : Graphic* pGraph = const_cast<Graphic*>(pBrush->GetGraphic());
975 0 : if (pGraph)
976 0 : pGraph->StopAnimation( 0, nId );
977 : }
978 : }
979 :
980 0 : SwRect aRepaint( rInf.GetPaintRect() );
981 0 : const SwTextFrm& rFrm = *rInf.GetTextFrm();
982 0 : if( rFrm.IsVertical() )
983 : {
984 0 : rFrm.SwitchHorizontalToVertical( aTmp );
985 0 : rFrm.SwitchHorizontalToVertical( aRepaint );
986 : }
987 :
988 0 : if( rFrm.IsRightToLeft() )
989 : {
990 0 : rFrm.SwitchLTRtoRTL( aTmp );
991 0 : rFrm.SwitchLTRtoRTL( aRepaint );
992 : }
993 :
994 0 : if( bDraw && aTmp.HasArea() )
995 : {
996 0 : DrawGraphic( pBrush, const_cast<OutputDevice*>(rInf.GetOut()),
997 0 : aTmp, aRepaint, bReplace ? GRFNUM_REPLACE : GRFNUM_YES );
998 : }
999 : }
1000 :
1001 70 : void SwGrfNumPortion::SetBase( long nLnAscent, long nLnDescent,
1002 : long nFlyAsc, long nFlyDesc )
1003 : {
1004 70 : if ( GetOrient() != text::VertOrientation::NONE )
1005 : {
1006 70 : SetRelPos( 0 );
1007 70 : if ( GetOrient() == text::VertOrientation::CENTER )
1008 0 : SetRelPos( GetGrfHeight() / 2 );
1009 70 : else if ( GetOrient() == text::VertOrientation::TOP )
1010 70 : SetRelPos( GetGrfHeight() - GRFNUM_SECURE );
1011 0 : else if ( GetOrient() == text::VertOrientation::BOTTOM )
1012 : ;
1013 0 : else if ( GetOrient() == text::VertOrientation::CHAR_CENTER )
1014 0 : SetRelPos( ( GetGrfHeight() + nLnAscent - nLnDescent ) / 2 );
1015 0 : else if ( GetOrient() == text::VertOrientation::CHAR_TOP )
1016 0 : SetRelPos( nLnAscent );
1017 0 : else if ( GetOrient() == text::VertOrientation::CHAR_BOTTOM )
1018 0 : SetRelPos( GetGrfHeight() - nLnDescent );
1019 : else
1020 : {
1021 0 : if( GetGrfHeight() >= nFlyAsc + nFlyDesc )
1022 : {
1023 : // If I'm as large as the line, I do not need to adjust
1024 : // at the line; I'll leave the max. ascent unchanged
1025 0 : SetRelPos( nFlyAsc );
1026 : }
1027 0 : else if ( GetOrient() == text::VertOrientation::LINE_CENTER )
1028 0 : SetRelPos( ( GetGrfHeight() + nFlyAsc - nFlyDesc ) / 2 );
1029 0 : else if ( GetOrient() == text::VertOrientation::LINE_TOP )
1030 0 : SetRelPos( nFlyAsc );
1031 0 : else if ( GetOrient() == text::VertOrientation::LINE_BOTTOM )
1032 0 : SetRelPos( GetGrfHeight() - nFlyDesc );
1033 : }
1034 : }
1035 70 : }
1036 :
1037 0 : void SwTextFrm::StopAnimation( OutputDevice* pOut )
1038 : {
1039 : OSL_ENSURE( HasAnimation(), "SwTextFrm::StopAnimation: Which Animation?" );
1040 0 : if( HasPara() )
1041 : {
1042 0 : SwLineLayout *pLine = GetPara();
1043 0 : while( pLine )
1044 : {
1045 0 : SwLinePortion *pPor = pLine->GetPortion();
1046 0 : while( pPor )
1047 : {
1048 0 : if( pPor->IsGrfNumPortion() )
1049 0 : static_cast<SwGrfNumPortion*>(pPor)->StopAnimation( pOut );
1050 : // The NumberPortion is always at the first char,
1051 : // which means we can cancel as soon as we've reached a portion
1052 : // with a length > 0
1053 0 : pPor = pPor->GetLen() ? 0 : pPor->GetPortion();
1054 : }
1055 0 : pLine = pLine->GetLen() ? 0 : pLine->GetNext();
1056 : }
1057 : }
1058 0 : }
1059 :
1060 : /**
1061 : * Initializes the script array and clears the width array
1062 : */
1063 0 : SwCombinedPortion::SwCombinedPortion( const OUString &rText )
1064 : : SwFieldPortion( rText )
1065 : , nUpPos(0)
1066 : , nLowPos(0)
1067 0 : , nProportion(55)
1068 : {
1069 0 : SetLen(1);
1070 0 : SetWhichPor( POR_COMBINED );
1071 0 : if( aExpand.getLength() > 6 )
1072 0 : aExpand = aExpand.copy( 0, 6 );
1073 :
1074 : // Initialization of the scripttype array,
1075 : // the arrays of width and position are filled by the format function
1076 0 : if( g_pBreakIt->GetBreakIter().is() )
1077 : {
1078 0 : sal_uInt8 nScr = SW_SCRIPTS;
1079 0 : for( sal_Int32 i = 0; i < rText.getLength(); ++i )
1080 : {
1081 0 : switch ( g_pBreakIt->GetBreakIter()->getScriptType( rText, i ) ) {
1082 0 : case i18n::ScriptType::LATIN : nScr = SW_LATIN; break;
1083 0 : case i18n::ScriptType::ASIAN : nScr = SW_CJK; break;
1084 0 : case i18n::ScriptType::COMPLEX : nScr = SW_CTL; break;
1085 : }
1086 0 : aScrType[i] = nScr;
1087 : }
1088 : }
1089 : else
1090 : {
1091 0 : for( int i = 0; i < 6; ++i )
1092 0 : aScrType[i] = 0;
1093 : }
1094 0 : memset( &aWidth, 0, sizeof(aWidth) );
1095 0 : }
1096 :
1097 0 : void SwCombinedPortion::Paint( const SwTextPaintInfo &rInf ) const
1098 : {
1099 : OSL_ENSURE( GetLen() <= 1, "SwFieldPortion::Paint: rest-portion pollution?" );
1100 0 : if( Width() )
1101 : {
1102 0 : rInf.DrawBackBrush( *this );
1103 0 : rInf.DrawViewOpt( *this, POR_FLD );
1104 :
1105 : // do we have to repaint a post it portion?
1106 0 : if( rInf.OnWin() && pPortion && !pPortion->Width() )
1107 0 : pPortion->PrePaint( rInf, this );
1108 :
1109 0 : const sal_Int32 nCount = aExpand.getLength();
1110 0 : if( !nCount )
1111 0 : return;
1112 : OSL_ENSURE( nCount < 7, "Too much combined characters" );
1113 :
1114 : // the first character of the second row
1115 0 : const sal_Int32 nTop = ( nCount + 1 ) / 2;
1116 :
1117 0 : SwFont aTmpFont( *rInf.GetFont() );
1118 0 : aTmpFont.SetProportion( nProportion ); // a smaller font
1119 0 : SwFontSave aFontSave( rInf, &aTmpFont );
1120 :
1121 0 : Point aOldPos = rInf.GetPos();
1122 0 : Point aOutPos( aOldPos.X(), aOldPos.Y() - nUpPos );// Y of the first row
1123 0 : for( sal_Int32 i = 0 ; i < nCount; ++i )
1124 : {
1125 0 : if( i == nTop ) // change the row
1126 0 : aOutPos.Y() = aOldPos.Y() + nLowPos; // Y of the second row
1127 0 : aOutPos.X() = aOldPos.X() + aPos[i]; // X position
1128 0 : const sal_uInt8 nAct = aScrType[i]; // script type
1129 0 : aTmpFont.SetActual( nAct );
1130 :
1131 : // if there're more than 4 characters to display, we choose fonts
1132 : // with 2/3 of the original font width.
1133 0 : if( aWidth[ nAct ] )
1134 : {
1135 0 : Size aTmpSz = aTmpFont.GetSize( nAct );
1136 0 : if( aTmpSz.Width() != aWidth[ nAct ] )
1137 : {
1138 0 : aTmpSz.Width() = aWidth[ nAct ];
1139 0 : aTmpFont.SetSize( aTmpSz, nAct );
1140 : }
1141 : }
1142 0 : const_cast<SwTextPaintInfo&>(rInf).SetPos( aOutPos );
1143 0 : rInf.DrawText( aExpand, *this, i, 1 );
1144 : }
1145 : // rInf is const, so we have to take back our manipulations
1146 0 : const_cast<SwTextPaintInfo&>(rInf).SetPos( aOldPos );
1147 : }
1148 : }
1149 :
1150 0 : bool SwCombinedPortion::Format( SwTextFormatInfo &rInf )
1151 : {
1152 0 : const sal_Int32 nCount = aExpand.getLength();
1153 0 : if( !nCount )
1154 : {
1155 0 : Width( 0 );
1156 0 : return false;
1157 : }
1158 :
1159 : OSL_ENSURE( nCount < 7, "Too much combined characters" );
1160 :
1161 : // If there are leading "weak"-scripttyped characters in this portion,
1162 : // they get the actual scripttype.
1163 0 : for( sal_Int32 i = 0; i < nCount && SW_SCRIPTS == aScrType[i]; ++i )
1164 0 : aScrType[i] = rInf.GetFont()->GetActual();
1165 0 : if( nCount > 4 )
1166 : {
1167 : // more than four? Ok, then we need the 2/3 font width
1168 0 : for( sal_Int32 i = 0; i < aExpand.getLength(); ++i )
1169 : {
1170 : OSL_ENSURE( aScrType[i] < SW_SCRIPTS, "Combined: Script fault" );
1171 0 : if( !aWidth[ aScrType[i] ] )
1172 : {
1173 0 : rInf.GetOut()->SetFont( rInf.GetFont()->GetFnt( aScrType[i] ) );
1174 0 : aWidth[ aScrType[i] ] =
1175 0 : static_cast<sal_uInt16>(2 * rInf.GetOut()->GetFontMetric().GetSize().Width() / 3);
1176 : }
1177 : }
1178 : }
1179 :
1180 0 : const sal_Int32 nTop = ( nCount + 1 ) / 2; // the first character of the second line
1181 0 : SwViewShell *pSh = rInf.GetTextFrm()->getRootFrm()->GetCurrShell();
1182 0 : SwFont aTmpFont( *rInf.GetFont() );
1183 0 : SwFontSave aFontSave( rInf, &aTmpFont );
1184 0 : nProportion = 55;
1185 : // In nMainAscent/Descent we store the ascent and descent
1186 : // of the original surrounding font
1187 : sal_uInt16 nMaxDescent, nMaxAscent, nMaxWidth;
1188 0 : sal_uInt16 nMainDescent = rInf.GetFont()->GetHeight( pSh, *rInf.GetOut() );
1189 0 : const sal_uInt16 nMainAscent = rInf.GetFont()->GetAscent( pSh, *rInf.GetOut() );
1190 0 : nMainDescent = nMainDescent - nMainAscent;
1191 : // we start with a 50% font, but if we notice that the combined portion
1192 : // becomes bigger than the surrounding font, we check 45% and maybe 40%.
1193 0 : do
1194 : {
1195 0 : nProportion -= 5;
1196 0 : aTmpFont.SetProportion( nProportion );
1197 0 : memset( &aPos, 0, sizeof(aPos) );
1198 0 : nMaxDescent = 0;
1199 0 : nMaxAscent = 0;
1200 0 : nMaxWidth = 0;
1201 0 : nUpPos = nLowPos = 0;
1202 :
1203 : // Now we get the width of all characters.
1204 : // The ascent and the width of the first line are stored in the
1205 : // ascent member of the portion, the descent in nLowPos.
1206 : // The ascent, descent and width of the second line are stored in the
1207 : // local nMaxAscent, nMaxDescent and nMaxWidth variables.
1208 0 : for( sal_Int32 i = 0; i < nCount; ++i )
1209 : {
1210 0 : sal_uInt8 nScrp = aScrType[i];
1211 0 : aTmpFont.SetActual( nScrp );
1212 0 : if( aWidth[ nScrp ] )
1213 : {
1214 0 : Size aFontSize( aTmpFont.GetSize( nScrp ) );
1215 0 : aFontSize.Width() = aWidth[ nScrp ];
1216 0 : aTmpFont.SetSize( aFontSize, nScrp );
1217 : }
1218 :
1219 0 : SwDrawTextInfo aDrawInf( pSh, *rInf.GetOut(), 0, aExpand, i, 1 );
1220 0 : Size aSize = aTmpFont._GetTextSize( aDrawInf );
1221 0 : const sal_uInt16 nAsc = aTmpFont.GetAscent( pSh, *rInf.GetOut() );
1222 0 : aPos[ i ] = (sal_uInt16)aSize.Width();
1223 0 : if( i == nTop ) // enter the second line
1224 : {
1225 0 : nLowPos = nMaxDescent;
1226 0 : Height( nMaxDescent + nMaxAscent );
1227 0 : Width( nMaxWidth );
1228 0 : SetAscent( nMaxAscent );
1229 0 : nMaxAscent = 0;
1230 0 : nMaxDescent = 0;
1231 0 : nMaxWidth = 0;
1232 : }
1233 0 : nMaxWidth = nMaxWidth + aPos[ i ];
1234 0 : if( nAsc > nMaxAscent )
1235 0 : nMaxAscent = nAsc;
1236 0 : if( aSize.Height() - nAsc > nMaxDescent )
1237 0 : nMaxDescent = static_cast<sal_uInt16>(aSize.Height() - nAsc);
1238 0 : }
1239 : // for one or two characters we double the width of the portion
1240 0 : if( nCount < 3 )
1241 : {
1242 0 : nMaxWidth *= 2;
1243 0 : Width( 2*Width() );
1244 0 : if( nCount < 2 )
1245 : {
1246 0 : Height( nMaxAscent + nMaxDescent );
1247 0 : nLowPos = nMaxDescent;
1248 : }
1249 : }
1250 0 : Height( Height() + nMaxDescent + nMaxAscent );
1251 0 : nUpPos = nMaxAscent;
1252 0 : SetAscent( Height() - nMaxDescent - nLowPos );
1253 0 : } while( nProportion > 40 && ( GetAscent() > nMainAscent ||
1254 0 : Height() - GetAscent() > nMainDescent ) );
1255 : // if the combined portion is smaller than the surrounding text,
1256 : // the portion grows. This looks better, if there's a character background.
1257 0 : if( GetAscent() < nMainAscent )
1258 : {
1259 0 : Height( Height() + nMainAscent - GetAscent() );
1260 0 : SetAscent( nMainAscent );
1261 : }
1262 0 : if( Height() < nMainAscent + nMainDescent )
1263 0 : Height( nMainAscent + nMainDescent );
1264 :
1265 : // We calculate the x positions of the characters in both lines..
1266 0 : sal_uInt16 nTopDiff = 0;
1267 0 : sal_uInt16 nBotDiff = 0;
1268 0 : if( nMaxWidth > Width() )
1269 : {
1270 0 : nTopDiff = ( nMaxWidth - Width() ) / 2;
1271 0 : Width( nMaxWidth );
1272 : }
1273 : else
1274 0 : nBotDiff = ( Width() - nMaxWidth ) / 2;
1275 0 : switch( nTop)
1276 : {
1277 0 : case 3: aPos[1] = aPos[0] + nTopDiff; // no break
1278 0 : case 2: aPos[nTop-1] = Width() - aPos[nTop-1];
1279 : }
1280 0 : aPos[0] = 0;
1281 0 : switch( nCount )
1282 : {
1283 0 : case 5: aPos[4] = aPos[3] + nBotDiff; // no break
1284 0 : case 3: aPos[nTop] = nBotDiff; break;
1285 0 : case 6: aPos[4] = aPos[3] + nBotDiff; // no break
1286 0 : case 4: aPos[nTop] = 0; // no break
1287 0 : case 2: aPos[nCount-1] = Width() - aPos[nCount-1];
1288 : }
1289 :
1290 : // Does the combined portion fit the line?
1291 0 : const bool bFull = rInf.Width() < rInf.X() + Width();
1292 0 : if( bFull )
1293 : {
1294 0 : if( rInf.GetLineStart() == rInf.GetIdx() && (!rInf.GetLast()->InFieldGrp()
1295 0 : || !static_cast<SwFieldPortion*>(rInf.GetLast())->IsFollow() ) )
1296 0 : Width( (sal_uInt16)( rInf.Width() - rInf.X() ) );
1297 : else
1298 : {
1299 0 : Truncate();
1300 0 : Width( 0 );
1301 0 : SetLen( 0 );
1302 0 : if( rInf.GetLast() )
1303 0 : rInf.GetLast()->FormatEOL( rInf );
1304 : }
1305 : }
1306 0 : return bFull;
1307 : }
1308 :
1309 0 : sal_uInt16 SwCombinedPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
1310 : {
1311 0 : if( !GetLen() ) // for the dummy part at the end of the line, where
1312 0 : return 0; // the combined portion doesn't fit.
1313 0 : return SwFieldPortion::GetViewWidth( rInf );
1314 : }
1315 :
1316 0 : SwFieldPortion *SwFieldFormDropDownPortion::Clone(const OUString &rExpand) const
1317 : {
1318 0 : return new SwFieldFormDropDownPortion(rExpand);
1319 177 : }
1320 :
1321 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|