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