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 <deque>
21 :
22 : #include <hintids.hxx>
23 :
24 : #include <com/sun/star/i18n/ScriptType.hpp>
25 : #include <editeng/twolinesitem.hxx>
26 : #include <editeng/charrotateitem.hxx>
27 : #include <vcl/outdev.hxx>
28 : #include <fmtfld.hxx>
29 : #include <fldbas.hxx>
30 : #include <txatbase.hxx>
31 : #include <fmtruby.hxx>
32 : #include <txtatr.hxx>
33 : #include <charfmt.hxx>
34 : #include <txtinet.hxx>
35 : #include <fchrfmt.hxx>
36 : #include <layfrm.hxx>
37 : #include <SwPortionHandler.hxx>
38 : #include <pormulti.hxx>
39 : #include <inftxt.hxx>
40 : #include <itrpaint.hxx>
41 : #include <viewopt.hxx>
42 : #include <itrform2.hxx>
43 : #include <porfld.hxx>
44 : #include <porglue.hxx>
45 : #include <breakit.hxx>
46 : #include <pagefrm.hxx>
47 : #include <rowfrm.hxx>
48 : #include <pagedesc.hxx>
49 : #include <tgrditem.hxx>
50 : #include <swtable.hxx>
51 : #include <fmtfsize.hxx>
52 :
53 : using namespace ::com::sun::star;
54 :
55 : // A SwMultiPortion is not a simple portion,
56 : // it's a container, which contains almost a SwLineLayoutPortion.
57 : // This SwLineLayout could be followed by other textportions via pPortion
58 : // and by another SwLineLayout via pNext to realize a doubleline portion.
59 492 : SwMultiPortion::~SwMultiPortion()
60 : {
61 246 : delete pFieldRest;
62 246 : }
63 :
64 0 : void SwMultiPortion::Paint( const SwTextPaintInfo & ) const
65 : {
66 : OSL_FAIL( "Don't try SwMultiPortion::Paint, try SwTextPainter::PaintMultiPortion" );
67 0 : }
68 :
69 : // Summarize the internal lines to calculate the (external) size.
70 : // The internal line has to calculate first.
71 463 : void SwMultiPortion::CalcSize( SwTextFormatter& rLine, SwTextFormatInfo &rInf )
72 : {
73 463 : Width( 0 );
74 463 : Height( 0 );
75 463 : SetAscent( 0 );
76 463 : SetFlyInContent( false );
77 463 : SwLineLayout *pLay = &GetRoot();
78 689 : do
79 : {
80 689 : pLay->CalcLine( rLine, rInf );
81 689 : if( rLine.IsFlyInCntBase() )
82 0 : SetFlyInContent( true );
83 689 : if( IsRuby() && ( OnTop() == ( pLay == &GetRoot() ) ) )
84 : {
85 : // An empty phonetic line don't need an ascent or a height.
86 314 : if( !pLay->Width() )
87 : {
88 14 : pLay->SetAscent( 0 );
89 14 : pLay->Height( 0 );
90 : }
91 314 : if( OnTop() )
92 286 : SetAscent( GetAscent() + pLay->Height() );
93 : }
94 : else
95 375 : SetAscent( GetAscent() + pLay->GetAscent() );
96 689 : Height( Height() + pLay->Height() );
97 689 : if( Width() < pLay->Width() )
98 469 : Width( pLay->Width() );
99 689 : pLay = pLay->GetNext();
100 : } while ( pLay );
101 463 : if( HasBrackets() )
102 : {
103 52 : sal_uInt16 nTmp = static_cast<SwDoubleLinePortion*>(this)->GetBrackets()->nHeight;
104 52 : if( nTmp > Height() )
105 : {
106 8 : const sal_uInt16 nAdd = ( nTmp - Height() ) / 2;
107 8 : GetRoot().SetAscent( GetRoot().GetAscent() + nAdd );
108 8 : GetRoot().Height( GetRoot().Height() + nAdd );
109 8 : Height( nTmp );
110 : }
111 52 : nTmp = static_cast<SwDoubleLinePortion*>(this)->GetBrackets()->nAscent;
112 52 : if( nTmp > GetAscent() )
113 8 : SetAscent( nTmp );
114 : }
115 463 : }
116 :
117 0 : long SwMultiPortion::CalcSpacing( long , const SwTextSizeInfo & ) const
118 : {
119 0 : return 0;
120 : }
121 :
122 148 : bool SwMultiPortion::ChgSpaceAdd( SwLineLayout*, long ) const
123 : {
124 148 : return false;
125 : }
126 :
127 0 : void SwMultiPortion::HandlePortion( SwPortionHandler& rPH ) const
128 : {
129 0 : rPH.Text( GetLen(), GetWhichPor() );
130 0 : }
131 :
132 : // sets the tabulator-flag, if there's any tabulator-portion inside.
133 241 : void SwMultiPortion::ActualizeTabulator()
134 : {
135 241 : SwLinePortion* pPor = GetRoot().GetFirstPortion();
136 : // First line
137 490 : for( bTab1 = bTab2 = false; pPor; pPor = pPor->GetPortion() )
138 249 : if( pPor->InTabGrp() )
139 0 : SetTab1( true );
140 241 : if( GetRoot().GetNext() )
141 : {
142 : // Second line
143 157 : pPor = GetRoot().GetNext()->GetFirstPortion();
144 250 : do
145 : {
146 250 : if( pPor->InTabGrp() )
147 0 : SetTab2( true );
148 250 : pPor = pPor->GetPortion();
149 : } while ( pPor );
150 : }
151 241 : }
152 :
153 40 : SwRotatedPortion::SwRotatedPortion( const SwMultiCreator& rCreate,
154 40 : sal_Int32 nEnd, bool bRTL ) : SwMultiPortion( nEnd )
155 : {
156 40 : const SvxCharRotateItem* pRot = static_cast<const SvxCharRotateItem*>(rCreate.pItem);
157 40 : if( !pRot )
158 : {
159 2 : const SwTextAttr& rAttr = *rCreate.pAttr;
160 : const SfxPoolItem *const pItem =
161 2 : CharFormat::GetItem(rAttr, RES_CHRATR_ROTATE);
162 2 : if ( pItem )
163 : {
164 2 : pRot = static_cast<const SvxCharRotateItem*>(pItem);
165 : }
166 : }
167 40 : if( pRot )
168 : {
169 : sal_uInt8 nDir;
170 40 : if ( bRTL )
171 0 : nDir = pRot->IsBottomToTop() ? 3 : 1;
172 : else
173 40 : nDir = pRot->IsBottomToTop() ? 1 : 3;
174 :
175 40 : SetDirection( nDir );
176 : }
177 40 : }
178 :
179 43 : SwBidiPortion::SwBidiPortion( sal_Int32 nEnd, sal_uInt8 nLv )
180 43 : : SwMultiPortion( nEnd ), nLevel( nLv )
181 : {
182 43 : SetBidi();
183 :
184 43 : if ( nLevel % 2 )
185 10 : SetDirection( DIR_RIGHT2LEFT );
186 : else
187 33 : SetDirection( DIR_LEFT2RIGHT );
188 43 : }
189 :
190 9 : long SwBidiPortion::CalcSpacing( long nSpaceAdd, const SwTextSizeInfo& rInf ) const
191 : {
192 9 : return HasTabulator() ? 0 : GetSpaceCnt(rInf) * nSpaceAdd / SPACING_PRECISION_FACTOR;
193 : }
194 :
195 0 : bool SwBidiPortion::ChgSpaceAdd( SwLineLayout* pCurr, long nSpaceAdd ) const
196 : {
197 0 : if( !HasTabulator() && nSpaceAdd > 0 && !pCurr->IsSpaceAdd() )
198 : {
199 0 : pCurr->CreateSpaceAdd();
200 0 : pCurr->SetLLSpaceAdd( nSpaceAdd, 0 );
201 0 : return true;
202 : }
203 :
204 0 : return false;
205 : }
206 :
207 9 : sal_Int32 SwBidiPortion::GetSpaceCnt( const SwTextSizeInfo &rInf ) const
208 : {
209 : // Calculate number of blanks for justified alignment
210 9 : SwLinePortion* pPor = GetRoot().GetFirstPortion();
211 9 : sal_Int32 nTmpStart = rInf.GetIdx();
212 9 : sal_Int32 nNull = 0;
213 : sal_Int32 nBlanks;
214 :
215 18 : for( nBlanks = 0; pPor; pPor = pPor->GetPortion() )
216 : {
217 9 : if( pPor->InTextGrp() )
218 9 : nBlanks = nBlanks + static_cast<SwTextPortion*>(pPor)->GetSpaceCnt( rInf, nNull );
219 0 : else if ( pPor->IsMultiPortion() &&
220 0 : static_cast<SwMultiPortion*>(pPor)->IsBidi() )
221 0 : nBlanks = nBlanks + static_cast<SwBidiPortion*>(pPor)->GetSpaceCnt( rInf );
222 :
223 9 : ((SwTextSizeInfo &)rInf).SetIdx( rInf.GetIdx() + pPor->GetLen() );
224 : }
225 9 : ((SwTextSizeInfo &)rInf).SetIdx( nTmpStart );
226 9 : return nBlanks;
227 : }
228 :
229 : // This constructor is for the continuation of a doubleline portion
230 : // in the next line.
231 : // It takes the same brackets and if the original has no content except
232 : // brackets, these will be deleted.
233 0 : SwDoubleLinePortion::SwDoubleLinePortion(SwDoubleLinePortion& rDouble, sal_Int32 nEnd)
234 : : SwMultiPortion(nEnd)
235 : , pBracket(0)
236 : , nLineDiff(0)
237 : , nBlank1(0)
238 0 : , nBlank2(0)
239 : {
240 0 : SetDirection( rDouble.GetDirection() );
241 0 : SetDouble();
242 0 : if( rDouble.GetBrackets() )
243 : {
244 0 : SetBrackets( rDouble );
245 : // An empty multiportion needs no brackets.
246 : // Notice: GetLen() might be zero, if the multiportion contains
247 : // the second part of a field and the width might be zero, if
248 : // it contains a note only. In this cases the brackets are okay.
249 : // But if the length and the width are both zero, the portion
250 : // is really empty.
251 0 : if( rDouble.Width() == rDouble.BracketWidth() )
252 0 : rDouble.ClearBrackets();
253 : }
254 0 : }
255 :
256 : // This constructor uses the textattribute to get the right brackets.
257 : // The textattribute could be a 2-line-attribute or a character- or
258 : // internetstyle, which contains the 2-line-attribute.
259 5 : SwDoubleLinePortion::SwDoubleLinePortion(const SwMultiCreator& rCreate, sal_Int32 nEnd)
260 : : SwMultiPortion(nEnd)
261 : , pBracket(new SwBracket())
262 : , nLineDiff(0)
263 : , nBlank1(0)
264 5 : , nBlank2(0)
265 : {
266 5 : SetDouble();
267 5 : const SvxTwoLinesItem* pTwo = static_cast<const SvxTwoLinesItem*>(rCreate.pItem);
268 5 : if( pTwo )
269 0 : pBracket->nStart = 0;
270 : else
271 : {
272 5 : const SwTextAttr& rAttr = *rCreate.pAttr;
273 5 : pBracket->nStart = rAttr.GetStart();
274 :
275 : const SfxPoolItem * const pItem =
276 5 : CharFormat::GetItem( rAttr, RES_CHRATR_TWO_LINES );
277 5 : if ( pItem )
278 : {
279 5 : pTwo = static_cast<const SvxTwoLinesItem*>(pItem);
280 : }
281 : }
282 5 : if( pTwo )
283 : {
284 5 : pBracket->cPre = pTwo->GetStartBracket();
285 5 : pBracket->cPost = pTwo->GetEndBracket();
286 : }
287 : else
288 : {
289 0 : pBracket->cPre = 0;
290 0 : pBracket->cPost = 0;
291 : }
292 5 : sal_uInt8 nTmp = SW_SCRIPTS;
293 5 : if( pBracket->cPre > 255 )
294 : {
295 0 : OUString aText = OUString(pBracket->cPre);
296 0 : nTmp = SwScriptInfo::WhichFont( 0, &aText, 0 );
297 : }
298 5 : pBracket->nPreScript = nTmp;
299 5 : nTmp = SW_SCRIPTS;
300 5 : if( pBracket->cPost > 255 )
301 : {
302 0 : OUString aText = OUString(pBracket->cPost);
303 0 : nTmp = SwScriptInfo::WhichFont( 0, &aText, 0 );
304 : }
305 5 : pBracket->nPostScript = nTmp;
306 :
307 5 : if( !pBracket->cPre && !pBracket->cPost )
308 : {
309 1 : delete pBracket;
310 1 : pBracket = 0;
311 : }
312 :
313 : // double line portions have the same direction as the frame directions
314 5 : if ( rCreate.nLevel % 2 )
315 0 : SetDirection( DIR_RIGHT2LEFT );
316 : else
317 5 : SetDirection( DIR_LEFT2RIGHT );
318 5 : }
319 :
320 : // paints the wished bracket,
321 : // if the multiportion has surrounding brackets.
322 : // The X-position of the SwTextPaintInfo will be modified:
323 : // the open bracket sets position behind itself,
324 : // the close bracket in front of itself.
325 8 : void SwDoubleLinePortion::PaintBracket( SwTextPaintInfo &rInf,
326 : long nSpaceAdd,
327 : bool bOpen ) const
328 : {
329 8 : sal_Unicode cCh = bOpen ? pBracket->cPre : pBracket->cPost;
330 8 : if( !cCh )
331 2 : return;
332 7 : const sal_uInt16 nChWidth = bOpen ? PreWidth() : PostWidth();
333 7 : if( !nChWidth )
334 0 : return;
335 7 : if( !bOpen )
336 3 : rInf.X( rInf.X() + Width() - PostWidth() +
337 3 : ( nSpaceAdd > 0 ? CalcSpacing( nSpaceAdd, rInf ) : 0 ) );
338 :
339 7 : SwBlankPortion aBlank( cCh, true );
340 7 : aBlank.SetAscent( pBracket->nAscent );
341 7 : aBlank.Width( nChWidth );
342 7 : aBlank.Height( pBracket->nHeight );
343 : {
344 7 : SwFont* pTmpFnt = new SwFont( *rInf.GetFont() );
345 7 : sal_uInt8 nAct = bOpen ? pBracket->nPreScript : pBracket->nPostScript;
346 7 : if( SW_SCRIPTS > nAct )
347 0 : pTmpFnt->SetActual( nAct );
348 7 : pTmpFnt->SetProportion( 100 );
349 7 : SwFontSave aSave( rInf, pTmpFnt );
350 7 : aBlank.Paint( rInf );
351 7 : delete pTmpFnt;
352 : }
353 7 : if( bOpen )
354 4 : rInf.X( rInf.X() + PreWidth() );
355 : }
356 :
357 : // creates the bracket-structure
358 : // and fills it, if not both characters are 0x00.
359 0 : void SwDoubleLinePortion::SetBrackets( const SwDoubleLinePortion& rDouble )
360 : {
361 0 : if( rDouble.pBracket )
362 : {
363 0 : pBracket = new SwBracket;
364 0 : pBracket->cPre = rDouble.pBracket->cPre;
365 0 : pBracket->cPost = rDouble.pBracket->cPost;
366 0 : pBracket->nPreScript = rDouble.pBracket->nPreScript;
367 0 : pBracket->nPostScript = rDouble.pBracket->nPostScript;
368 0 : pBracket->nStart = rDouble.pBracket->nStart;
369 : }
370 0 : }
371 :
372 : // calculates the size of the brackets => pBracket,
373 : // reduces the nMaxWidth-parameter ( minus bracket-width )
374 : // and moves the rInf-x-position behind the opening bracket.
375 4 : void SwDoubleLinePortion::FormatBrackets( SwTextFormatInfo &rInf, SwTwips& nMaxWidth )
376 : {
377 4 : nMaxWidth -= rInf.X();
378 4 : SwFont* pTmpFnt = new SwFont( *rInf.GetFont() );
379 4 : pTmpFnt->SetProportion( 100 );
380 4 : pBracket->nAscent = 0;
381 4 : pBracket->nHeight = 0;
382 4 : if( pBracket->cPre )
383 : {
384 4 : OUString aStr( pBracket->cPre );
385 4 : sal_uInt8 nActualScr = pTmpFnt->GetActual();
386 4 : if( SW_SCRIPTS > pBracket->nPreScript )
387 0 : pTmpFnt->SetActual( pBracket->nPreScript );
388 8 : SwFontSave aSave( rInf, pTmpFnt );
389 4 : SwPosSize aSize = rInf.GetTextSize( aStr );
390 4 : pBracket->nAscent = rInf.GetAscent();
391 4 : pBracket->nHeight = aSize.Height();
392 4 : pTmpFnt->SetActual( nActualScr );
393 4 : if( nMaxWidth > aSize.Width() )
394 : {
395 4 : pBracket->nPreWidth = aSize.Width();
396 4 : nMaxWidth -= aSize.Width();
397 4 : rInf.X( rInf.X() + aSize.Width() );
398 : }
399 : else
400 : {
401 0 : pBracket->nPreWidth = 0;
402 0 : nMaxWidth = 0;
403 4 : }
404 : }
405 : else
406 0 : pBracket->nPreWidth = 0;
407 4 : if( pBracket->cPost )
408 : {
409 3 : OUString aStr( pBracket->cPost );
410 3 : if( SW_SCRIPTS > pBracket->nPostScript )
411 0 : pTmpFnt->SetActual( pBracket->nPostScript );
412 6 : SwFontSave aSave( rInf, pTmpFnt );
413 3 : SwPosSize aSize = rInf.GetTextSize( aStr );
414 3 : const sal_uInt16 nTmpAsc = rInf.GetAscent();
415 3 : if( nTmpAsc > pBracket->nAscent )
416 : {
417 0 : pBracket->nHeight += nTmpAsc - pBracket->nAscent;
418 0 : pBracket->nAscent = nTmpAsc;
419 : }
420 3 : if( aSize.Height() > pBracket->nHeight )
421 0 : pBracket->nHeight = aSize.Height();
422 3 : if( nMaxWidth > aSize.Width() )
423 : {
424 3 : pBracket->nPostWidth = aSize.Width();
425 3 : nMaxWidth -= aSize.Width();
426 : }
427 : else
428 : {
429 0 : pBracket->nPostWidth = 0;
430 0 : nMaxWidth = 0;
431 3 : }
432 : }
433 : else
434 1 : pBracket->nPostWidth = 0;
435 4 : nMaxWidth += rInf.X();
436 4 : delete(pTmpFnt);
437 4 : }
438 :
439 : // calculates the number of blanks in each line and
440 : // the difference of the width of the two lines.
441 : // These results are used from the text adjustment.
442 5 : void SwDoubleLinePortion::CalcBlanks( SwTextFormatInfo &rInf )
443 : {
444 5 : SwLinePortion* pPor = GetRoot().GetFirstPortion();
445 5 : sal_Int32 nNull = 0;
446 5 : sal_Int32 nStart = rInf.GetIdx();
447 5 : SetTab1( false );
448 5 : SetTab2( false );
449 10 : for( nBlank1 = 0; pPor; pPor = pPor->GetPortion() )
450 : {
451 5 : if( pPor->InTextGrp() )
452 5 : nBlank1 = nBlank1 + static_cast<SwTextPortion*>(pPor)->GetSpaceCnt( rInf, nNull );
453 5 : rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
454 5 : if( pPor->InTabGrp() )
455 0 : SetTab1( true );
456 : }
457 5 : nLineDiff = GetRoot().Width();
458 5 : if( GetRoot().GetNext() )
459 : {
460 5 : pPor = GetRoot().GetNext()->GetFirstPortion();
461 5 : nLineDiff -= GetRoot().GetNext()->Width();
462 : }
463 10 : for( nBlank2 = 0; pPor; pPor = pPor->GetPortion() )
464 : {
465 5 : if( pPor->InTextGrp() )
466 5 : nBlank2 = nBlank2 + static_cast<SwTextPortion*>(pPor)->GetSpaceCnt( rInf, nNull );
467 5 : rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
468 5 : if( pPor->InTabGrp() )
469 0 : SetTab2( true );
470 : }
471 5 : rInf.SetIdx( nStart );
472 5 : }
473 :
474 0 : long SwDoubleLinePortion::CalcSpacing( long nSpaceAdd, const SwTextSizeInfo & ) const
475 : {
476 0 : return HasTabulator() ? 0 : GetSpaceCnt() * nSpaceAdd / SPACING_PRECISION_FACTOR;
477 : }
478 :
479 : // Merges the spaces for text adjustment from the inner and outer part.
480 : // Inside the doubleline portion the wider line has no spaceadd-array, the
481 : // smaller line has such an array to reach width of the wider line.
482 : // If the surrounding line has text adjustment and the doubleline portion
483 : // contains no tabulator, it is necessary to create/manipulate the inner
484 : // space arrays.
485 22 : bool SwDoubleLinePortion::ChgSpaceAdd( SwLineLayout* pCurr,
486 : long nSpaceAdd ) const
487 : {
488 22 : bool bRet = false;
489 22 : if( !HasTabulator() && nSpaceAdd > 0 )
490 : {
491 0 : if( !pCurr->IsSpaceAdd() )
492 : {
493 : // The wider line gets the spaceadd from the surrounding line direct
494 0 : pCurr->CreateSpaceAdd();
495 0 : pCurr->SetLLSpaceAdd( nSpaceAdd, 0 );
496 0 : bRet = true;
497 : }
498 : else
499 : {
500 0 : sal_Int32 nMyBlank = GetSmallerSpaceCnt();
501 0 : sal_Int32 nOther = GetSpaceCnt();
502 0 : SwTwips nMultiSpace = pCurr->GetLLSpaceAdd( 0 ) * nMyBlank + nOther * nSpaceAdd;
503 :
504 0 : if( nMyBlank )
505 0 : nMultiSpace /= nMyBlank;
506 :
507 0 : if( nMultiSpace < USHRT_MAX * SPACING_PRECISION_FACTOR )
508 : {
509 : // pCurr->SetLLSpaceAdd( nMultiSpace, 0 );
510 : // #i65711# SetLLSpaceAdd replaces the first value,
511 : // instead we want to insert a new first value:
512 0 : std::vector<long>* pVec = pCurr->GetpLLSpaceAdd();
513 0 : pVec->insert( pVec->begin(), nMultiSpace );
514 0 : bRet = true;
515 : }
516 : }
517 : }
518 22 : return bRet;
519 : }
520 : // cancels the manipulation from SwDoubleLinePortion::ChangeSpaceAdd(..)
521 0 : void SwDoubleLinePortion::ResetSpaceAdd( SwLineLayout* pCurr )
522 : {
523 0 : pCurr->RemoveFirstLLSpaceAdd();
524 0 : if( !pCurr->GetLLSpaceAddCount() )
525 0 : pCurr->FinishSpaceAdd();
526 0 : }
527 :
528 15 : SwDoubleLinePortion::~SwDoubleLinePortion()
529 : {
530 5 : delete pBracket;
531 10 : }
532 :
533 : // constructs a ruby portion, i.e. an additional text is displayed
534 : // beside the main text, e.g. phonetic characters.
535 0 : SwRubyPortion::SwRubyPortion( const SwRubyPortion& rRuby, sal_Int32 nEnd ) :
536 : SwMultiPortion( nEnd ),
537 0 : nRubyOffset( rRuby.GetRubyOffset() ),
538 0 : nAdjustment( rRuby.GetAdjustment() )
539 : {
540 0 : SetDirection( rRuby.GetDirection() ),
541 0 : SetTop( rRuby.OnTop() );
542 0 : SetRuby();
543 0 : }
544 :
545 : // constructs a ruby portion, i.e. an additional text is displayed
546 : // beside the main text, e.g. phonetic characters.
547 157 : SwRubyPortion::SwRubyPortion( const SwMultiCreator& rCreate, const SwFont& rFnt,
548 : const IDocumentSettingAccess& rIDocumentSettingAccess,
549 : sal_Int32 nEnd, sal_Int32 nOffs,
550 : const bool* pForceRubyPos )
551 157 : : SwMultiPortion( nEnd )
552 : {
553 157 : SetRuby();
554 : OSL_ENSURE( SW_MC_RUBY == rCreate.nId, "Ruby expected" );
555 : OSL_ENSURE( RES_TXTATR_CJK_RUBY == rCreate.pAttr->Which(), "Wrong attribute" );
556 157 : const SwFormatRuby& rRuby = rCreate.pAttr->GetRuby();
557 157 : nAdjustment = rRuby.GetAdjustment();
558 157 : nRubyOffset = nOffs;
559 :
560 : // in grid mode we force the ruby text to the upper or lower line
561 157 : if ( pForceRubyPos )
562 0 : SetTop( *pForceRubyPos );
563 : else
564 157 : SetTop( ! rRuby.GetPosition() );
565 :
566 : const SwCharFormat *const pFormat =
567 157 : static_txtattr_cast<SwTextRuby const*>(rCreate.pAttr)->GetCharFormat();
568 : SwFont *pRubyFont;
569 157 : if( pFormat )
570 : {
571 157 : const SwAttrSet& rSet = pFormat->GetAttrSet();
572 157 : pRubyFont = new SwFont( rFnt );
573 157 : pRubyFont->SetDiffFnt( &rSet, &rIDocumentSettingAccess );
574 :
575 : // we do not allow a vertical font for the ruby text
576 157 : pRubyFont->SetVertical( rFnt.GetOrientation() );
577 : }
578 : else
579 0 : pRubyFont = NULL;
580 :
581 157 : OUString aStr = rRuby.GetText().copy( nOffs );
582 157 : SwFieldPortion *pField = new SwFieldPortion( aStr, pRubyFont );
583 157 : pField->SetNextOffset( nOffs );
584 157 : pField->SetFollow( true );
585 :
586 157 : if( OnTop() )
587 143 : GetRoot().SetPortion( pField );
588 : else
589 : {
590 14 : GetRoot().SetNext( new SwLineLayout() );
591 14 : GetRoot().GetNext()->SetPortion( pField );
592 : }
593 :
594 : // ruby portions have the same direction as the frame directions
595 157 : if ( rCreate.nLevel % 2 )
596 : {
597 : // switch right and left ruby adjustment in rtl environment
598 0 : if ( 0 == nAdjustment )
599 0 : nAdjustment = 2;
600 0 : else if ( 2 == nAdjustment )
601 0 : nAdjustment = 0;
602 :
603 0 : SetDirection( DIR_RIGHT2LEFT );
604 : }
605 : else
606 157 : SetDirection( DIR_LEFT2RIGHT );
607 157 : }
608 :
609 : // In ruby portion there are different alignments for
610 : // the ruby text and the main text.
611 : // Left, right, centered and two possibilities of block adjustment
612 : // The block adjustment is realized by spacing between the characteres,
613 : // either with a half space or no space in front of the first letter and
614 : // a half space at the end of the last letter.
615 : // Notice: the smaller line will be manipulated, normally it's the ruby line,
616 : // but it could be the main text, too.
617 : // If there is a tabulator in smaller line, no adjustment is possible.
618 16 : void SwRubyPortion::_Adjust( SwTextFormatInfo &rInf )
619 : {
620 16 : SwTwips nLineDiff = GetRoot().Width() - GetRoot().GetNext()->Width();
621 16 : sal_Int32 nOldIdx = rInf.GetIdx();
622 16 : if( !nLineDiff )
623 0 : return;
624 : SwLineLayout *pCurr;
625 16 : if( nLineDiff < 0 )
626 : { // The first line has to be adjusted.
627 2 : if( GetTab1() )
628 0 : return;
629 2 : pCurr = &GetRoot();
630 2 : nLineDiff = -nLineDiff;
631 : }
632 : else
633 : { // The second line has to be adjusted.
634 14 : if( GetTab2() )
635 0 : return;
636 14 : pCurr = GetRoot().GetNext();
637 14 : rInf.SetIdx( nOldIdx + GetRoot().GetLen() );
638 : }
639 16 : sal_uInt16 nLeft = 0; // the space in front of the first letter
640 16 : sal_uInt16 nRight = 0; // the space at the end of the last letter
641 16 : sal_Int32 nSub = 0;
642 16 : switch ( nAdjustment )
643 : {
644 16 : case 1: nRight = static_cast<sal_uInt16>(nLineDiff / 2); // no break
645 16 : case 2: nLeft = static_cast<sal_uInt16>(nLineDiff - nRight); break;
646 0 : case 3: nSub = 1; // no break
647 : case 4:
648 : {
649 0 : sal_Int32 nCharCnt = 0;
650 : SwLinePortion *pPor;
651 0 : for( pPor = pCurr->GetFirstPortion(); pPor; pPor = pPor->GetPortion() )
652 : {
653 0 : if( pPor->InTextGrp() )
654 0 : static_cast<SwTextPortion*>(pPor)->GetSpaceCnt( rInf, nCharCnt );
655 0 : rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
656 : }
657 0 : if( nCharCnt > nSub )
658 : {
659 0 : SwTwips nCalc = nLineDiff / ( nCharCnt - nSub );
660 : short nTmp;
661 0 : if( nCalc < SHRT_MAX )
662 0 : nTmp = -short(nCalc);
663 : else
664 0 : nTmp = SHRT_MIN;
665 :
666 0 : pCurr->CreateSpaceAdd( SPACING_PRECISION_FACTOR * nTmp );
667 0 : nLineDiff -= nCalc * ( nCharCnt - 1 );
668 : }
669 0 : if( nLineDiff > 1 )
670 : {
671 0 : nRight = static_cast<sal_uInt16>(nLineDiff / 2);
672 0 : nLeft = static_cast<sal_uInt16>(nLineDiff - nRight);
673 : }
674 0 : break;
675 : }
676 : default: OSL_FAIL( "New ruby adjustment" );
677 : }
678 16 : if( nLeft || nRight )
679 : {
680 16 : if( !pCurr->GetPortion() )
681 0 : pCurr->SetPortion(SwTextPortion::CopyLinePortion(*pCurr));
682 16 : if( nLeft )
683 : {
684 16 : SwMarginPortion *pMarg = new SwMarginPortion( 0 );
685 16 : pMarg->AddPrtWidth( nLeft );
686 16 : pMarg->SetPortion( pCurr->GetPortion() );
687 16 : pCurr->SetPortion( pMarg );
688 : }
689 16 : if( nRight )
690 : {
691 16 : SwMarginPortion *pMarg = new SwMarginPortion( 0 );
692 16 : pMarg->AddPrtWidth( nRight );
693 16 : pCurr->FindLastPortion()->Append( pMarg );
694 : }
695 : }
696 :
697 16 : pCurr->Width( Width() );
698 16 : rInf.SetIdx( nOldIdx );
699 : }
700 :
701 : // has to change the nRubyOffset, if there's a fieldportion
702 : // in the phonetic line.
703 : // The nRubyOffset is the position in the rubystring, where the
704 : // next SwRubyPortion has start the displaying of the phonetics.
705 157 : void SwRubyPortion::CalcRubyOffset()
706 : {
707 157 : const SwLineLayout *pCurr = &GetRoot();
708 157 : if( !OnTop() )
709 : {
710 14 : pCurr = pCurr->GetNext();
711 14 : if( !pCurr )
712 157 : return;
713 : }
714 157 : const SwLinePortion *pPor = pCurr->GetFirstPortion();
715 157 : const SwFieldPortion *pField = NULL;
716 503 : while( pPor )
717 : {
718 189 : if( pPor->InFieldGrp() )
719 157 : pField = static_cast<const SwFieldPortion*>(pPor);
720 189 : pPor = pPor->GetPortion();
721 : }
722 157 : if( pField )
723 : {
724 157 : if( pField->HasFollow() )
725 0 : nRubyOffset = pField->GetNextOffset();
726 : else
727 157 : nRubyOffset = COMPLETE_STRING;
728 : }
729 : }
730 :
731 : // A little helper function for GetMultiCreator(..)
732 : // It extracts the 2-line-format from a 2-line-attribute or a character style.
733 : // The rValue is set to true, if the 2-line-attribute's value is set and
734 : // no 2-line-format reference is passed. If there is a 2-line-format reference,
735 : // then the rValue is set only, if the 2-line-attribute's value is set _and_
736 : // the 2-line-formats has the same brackets.
737 55482 : static bool lcl_Has2Lines( const SwTextAttr& rAttr, const SvxTwoLinesItem* &rpRef,
738 : bool &rValue )
739 : {
740 55482 : const SfxPoolItem* pItem = CharFormat::GetItem( rAttr, RES_CHRATR_TWO_LINES );
741 55482 : if( pItem )
742 : {
743 27 : rValue = static_cast<const SvxTwoLinesItem*>(pItem)->GetValue();
744 27 : if( !rpRef )
745 22 : rpRef = static_cast<const SvxTwoLinesItem*>(pItem);
746 10 : else if( static_cast<const SvxTwoLinesItem*>(pItem)->GetEndBracket() !=
747 10 : rpRef->GetEndBracket() ||
748 5 : static_cast<const SvxTwoLinesItem*>(pItem)->GetStartBracket() !=
749 5 : rpRef->GetStartBracket() )
750 0 : rValue = false;
751 27 : return true;
752 : }
753 55455 : return false;
754 : }
755 :
756 : // is a little help function for GetMultiCreator(..)
757 : // It extracts the charrotation from a charrotate-attribute or a character style.
758 : // The rValue is set to true, if the charrotate-attribute's value is set and
759 : // no charrotate-format reference is passed.
760 : // If there is a charrotate-format reference, then the rValue is set only,
761 : // if the charrotate-attribute's value is set _and_ identical
762 : // to the charrotate-format's value.
763 55472 : static bool lcl_HasRotation( const SwTextAttr& rAttr,
764 : const SvxCharRotateItem* &rpRef, bool &rValue )
765 : {
766 55472 : const SfxPoolItem* pItem = CharFormat::GetItem( rAttr, RES_CHRATR_ROTATE );
767 55472 : if ( pItem )
768 : {
769 17 : rValue = static_cast<const SvxCharRotateItem*>(pItem)->GetValue();
770 17 : if( !rpRef )
771 15 : rpRef = static_cast<const SvxCharRotateItem*>(pItem);
772 4 : else if( static_cast<const SvxCharRotateItem*>(pItem)->GetValue() !=
773 2 : rpRef->GetValue() )
774 0 : rValue = false;
775 17 : return true;
776 : }
777 :
778 55455 : return false;
779 : }
780 :
781 : // If we (e.g. the position rPos) are inside a two-line-attribute or
782 : // a ruby-attribute, the attribute will be returned in a SwMultiCreator-struct,
783 : // otherwise the function returns zero.
784 : // The rPos parameter is set to the end of the multiportion,
785 : // normally this is the end of the attribute,
786 : // but sometimes it is the start of another attribute, which finished or
787 : // interrupts the first attribute.
788 : // E.g. a ruby portion interrupts a 2-line-attribute, a 2-line-attribute
789 : // with different brackets interrupts another 2-line-attribute.
790 155450 : SwMultiCreator* SwTextSizeInfo::GetMultiCreator( sal_Int32 &rPos,
791 : SwMultiPortion* pMulti ) const
792 : {
793 155450 : SwScriptInfo& rSI = const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
794 :
795 : // get the last embedding level
796 : sal_uInt8 nCurrLevel;
797 155450 : if ( pMulti )
798 : {
799 : OSL_ENSURE( pMulti->IsBidi(), "Nested MultiPortion is not BidiPortion" );
800 : // level associated with bidi-portion;
801 80 : nCurrLevel = static_cast<SwBidiPortion*>(pMulti)->GetLevel();
802 : }
803 : else
804 : // no nested bidi portion required
805 155370 : nCurrLevel = GetTextFrm()->IsRightToLeft() ? 1 : 0;
806 :
807 : // check if there is a field at rPos:
808 155450 : sal_uInt8 nNextLevel = nCurrLevel;
809 155450 : bool bFieldBidi = false;
810 :
811 155450 : if ( rPos < GetText().getLength() && CH_TXTATR_BREAKWORD == GetChar( rPos ) )
812 : {
813 6793 : bFieldBidi = true;
814 : }
815 : else
816 148657 : nNextLevel = rSI.DirType( rPos );
817 :
818 155450 : if ( GetText().getLength() != rPos && nNextLevel > nCurrLevel )
819 : {
820 37 : rPos = bFieldBidi ? rPos + 1 : rSI.NextDirChg( rPos, &nCurrLevel );
821 37 : if ( COMPLETE_STRING == rPos )
822 0 : return NULL;
823 37 : SwMultiCreator *pRet = new SwMultiCreator;
824 37 : pRet->pItem = NULL;
825 37 : pRet->pAttr = NULL;
826 37 : pRet->nId = SW_MC_BIDI;
827 37 : pRet->nLevel = nCurrLevel + 1;
828 37 : return pRet;
829 : }
830 :
831 : // a bidi portion can only contain other bidi portions
832 155413 : if ( pMulti )
833 80 : return NULL;
834 :
835 155333 : const SvxCharRotateItem* pRotate = NULL;
836 : const SfxPoolItem* pRotItem;
837 310666 : if( SfxItemState::SET == m_pFrm->GetTextNode()->GetSwAttrSet().
838 155565 : GetItemState( RES_CHRATR_ROTATE, true, &pRotItem ) &&
839 232 : static_cast<const SvxCharRotateItem*>(pRotItem)->GetValue() )
840 214 : pRotate = static_cast<const SvxCharRotateItem*>(pRotItem);
841 : else
842 155119 : pRotItem = NULL;
843 155333 : const SvxTwoLinesItem* p2Lines = NULL;
844 155333 : const SwTextNode *pLclTextNode = m_pFrm->GetTextNode();
845 155333 : if( !pLclTextNode )
846 0 : return NULL;
847 : const SfxPoolItem* pItem;
848 310666 : if( SfxItemState::SET == pLclTextNode->GetSwAttrSet().
849 155607 : GetItemState( RES_CHRATR_TWO_LINES, true, &pItem ) &&
850 274 : static_cast<const SvxTwoLinesItem*>(pItem)->GetValue() )
851 256 : p2Lines = static_cast<const SvxTwoLinesItem*>(pItem);
852 : else
853 155077 : pItem = NULL;
854 :
855 155333 : const SwpHints *pHints = pLclTextNode->GetpSwpHints();
856 155333 : if( !pHints && !p2Lines && !pRotate )
857 65520 : return NULL;
858 89813 : const SwTextAttr *pRuby = NULL;
859 89813 : bool bTwo = false;
860 89813 : bool bRot = false;
861 89813 : size_t n2Lines = SAL_MAX_SIZE;
862 89813 : size_t nRotate = SAL_MAX_SIZE;
863 89813 : const size_t nCount = pHints ? pHints->Count() : 0;
864 294649 : for( size_t i = 0; i < nCount; ++i )
865 : {
866 255263 : const SwTextAttr *pTmp = (*pHints)[i];
867 255263 : sal_Int32 nStart = pTmp->GetStart();
868 255263 : if( rPos < nStart )
869 50427 : break;
870 204836 : if( *pTmp->GetAnyEnd() > rPos )
871 : {
872 55563 : if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
873 157 : pRuby = pTmp;
874 : else
875 : {
876 55406 : const SvxCharRotateItem* pRoTmp = NULL;
877 55406 : if( lcl_HasRotation( *pTmp, pRoTmp, bRot ) )
878 : {
879 15 : nRotate = bRot ? i : nCount;
880 15 : pRotate = pRoTmp;
881 : }
882 55406 : const SvxTwoLinesItem* p2Tmp = NULL;
883 55406 : if( lcl_Has2Lines( *pTmp, p2Tmp, bTwo ) )
884 : {
885 22 : n2Lines = bTwo ? i : nCount;
886 22 : p2Lines = p2Tmp;
887 : }
888 : }
889 : }
890 : }
891 89813 : if( pRuby )
892 : { // The winner is ... a ruby attribute and so
893 : // the end of the multiportion is the end of the ruby attribute.
894 157 : rPos = *pRuby->End();
895 157 : SwMultiCreator *pRet = new SwMultiCreator;
896 157 : pRet->pItem = NULL;
897 157 : pRet->pAttr = pRuby;
898 157 : pRet->nId = SW_MC_RUBY;
899 157 : pRet->nLevel = GetTextFrm()->IsRightToLeft() ? 1 : 0;
900 157 : return pRet;
901 : }
902 89912 : if( n2Lines < nCount || ( pItem && pItem == p2Lines &&
903 256 : rPos < GetText().getLength() ) )
904 : { // The winner is a 2-line-attribute,
905 : // the end of the multiportion depends on the following attributes...
906 5 : SwMultiCreator *pRet = new SwMultiCreator;
907 :
908 : // We note the endpositions of the 2-line attributes in aEnd as stack
909 5 : std::deque< sal_Int32 > aEnd;
910 :
911 : // The bOn flag signs the state of the last 2-line attribute in the
912 : // aEnd-stack, it is compatible with the winner-attribute or
913 : // it interrupts the other attribute.
914 5 : bool bOn = true;
915 :
916 5 : if( n2Lines < nCount )
917 : {
918 5 : pRet->pItem = NULL;
919 5 : pRet->pAttr = (*pHints)[n2Lines];
920 5 : aEnd.push_front( *pRet->pAttr->End() );
921 5 : if( pItem )
922 : {
923 0 : aEnd.front() = GetText().getLength();
924 0 : bOn = static_cast<const SvxTwoLinesItem*>(pItem)->GetEndBracket() ==
925 0 : p2Lines->GetEndBracket() &&
926 0 : static_cast<const SvxTwoLinesItem*>(pItem)->GetStartBracket() ==
927 0 : p2Lines->GetStartBracket();
928 : }
929 : }
930 : else
931 : {
932 0 : pRet->pItem = pItem;
933 0 : pRet->pAttr = NULL;
934 0 : aEnd.push_front( GetText().getLength() );
935 : }
936 5 : pRet->nId = SW_MC_DOUBLE;
937 5 : pRet->nLevel = GetTextFrm()->IsRightToLeft() ? 1 : 0;
938 :
939 : // n2Lines is the index of the last 2-line-attribute, which contains
940 : // the actual position.
941 :
942 : // At this moment we know that at position rPos the "winner"-attribute
943 : // causes a 2-line-portion. The end of the attribute is the end of the
944 : // portion, if there's no interrupting attribute.
945 : // There are two kinds of interruptors:
946 : // - ruby attributes stops the 2-line-attribute, the end of the
947 : // multiline is the start of the ruby attribute
948 : // - 2-line-attributes with value "Off" or with different brackets,
949 : // these attributes may interrupt the winner, but they could be
950 : // neutralized by another 2-line-attribute starting at the same
951 : // position with the same brackets as the winner-attribute.
952 :
953 : // In the following loop rPos is the critical position and it will be
954 : // evaluated, if at rPos starts a interrupting or a maintaining
955 : // continuity attribute.
956 15 : for( size_t i = 0; i < nCount; ++i )
957 : {
958 10 : const SwTextAttr *pTmp = (*pHints)[i];
959 10 : if( *pTmp->GetAnyEnd() <= rPos )
960 0 : continue;
961 10 : if( rPos < pTmp->GetStart() )
962 : {
963 : // If bOn is false and the next attribute starts later than rPos
964 : // the winner attribute is interrupted at rPos.
965 : // If the start of the next attribute is behind the end of
966 : // the last attribute on the aEnd-stack, this is the endposition
967 : // on the stack is the end of the 2-line portion.
968 0 : if( !bOn || aEnd.back() < pTmp->GetStart() )
969 0 : break;
970 : // At this moment, bOn is true and the next attribute starts
971 : // behind rPos, so we could move rPos to the next startpoint
972 0 : rPos = pTmp->GetStart();
973 : // We clean up the aEnd-stack, endpositions equal to rPos are
974 : // superfluous.
975 0 : while( !aEnd.empty() && aEnd.back() <= rPos )
976 : {
977 0 : bOn = !bOn;
978 0 : aEnd.pop_back();
979 : }
980 : // If the endstack is empty, we simulate an attribute with
981 : // state true and endposition rPos
982 0 : if( aEnd.empty() )
983 : {
984 0 : aEnd.push_front( rPos );
985 0 : bOn = true;
986 : }
987 : }
988 : // A ruby attribute stops the 2-line immediately
989 10 : if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
990 0 : return pRet;
991 10 : if( lcl_Has2Lines( *pTmp, p2Lines, bTwo ) )
992 : { // We have an interesting attribute..
993 5 : if( bTwo == bOn )
994 : { // .. with the same state, so the last attribute could
995 : // be continued.
996 5 : if( aEnd.back() < *pTmp->End() )
997 0 : aEnd.back() = *pTmp->End();
998 : }
999 : else
1000 : { // .. with a different state.
1001 0 : bOn = bTwo;
1002 : // If this is smaller than the last on the stack, we put
1003 : // it on the stack. If it has the same endposition, the last
1004 : // could be removed.
1005 0 : if( aEnd.back() > *pTmp->End() )
1006 0 : aEnd.push_back( *pTmp->End() );
1007 0 : else if( aEnd.size() > 1 )
1008 0 : aEnd.pop_back();
1009 : else
1010 0 : aEnd.back() = *pTmp->End();
1011 : }
1012 : }
1013 : }
1014 5 : if( bOn && !aEnd.empty() )
1015 5 : rPos = aEnd.back();
1016 5 : return pRet;
1017 : }
1018 89865 : if( nRotate < nCount || ( pRotItem && pRotItem == pRotate &&
1019 214 : rPos < GetText().getLength() ) )
1020 : { // The winner is a rotate-attribute,
1021 : // the end of the multiportion depends on the following attributes...
1022 40 : SwMultiCreator *pRet = new SwMultiCreator;
1023 40 : pRet->nId = SW_MC_ROTATE;
1024 :
1025 : // We note the endpositions of the 2-line attributes in aEnd as stack
1026 40 : std::deque< sal_Int32 > aEnd;
1027 :
1028 : // The bOn flag signs the state of the last 2-line attribute in the
1029 : // aEnd-stack, which could interrupts the winning rotation attribute.
1030 40 : bool bOn = pItem;
1031 40 : aEnd.push_front( GetText().getLength() );
1032 :
1033 40 : sal_Int32 n2Start = rPos;
1034 106 : for( size_t i = 0; i < nCount; ++i )
1035 : {
1036 66 : const SwTextAttr *pTmp = (*pHints)[i];
1037 66 : if( *pTmp->GetAnyEnd() <= n2Start )
1038 0 : continue;
1039 66 : if( n2Start < pTmp->GetStart() )
1040 : {
1041 27 : if( bOn || aEnd.back() < pTmp->GetStart() )
1042 0 : break;
1043 27 : n2Start = pTmp->GetStart();
1044 81 : while( !aEnd.empty() && aEnd.back() <= n2Start )
1045 : {
1046 27 : bOn = !bOn;
1047 27 : aEnd.pop_back();
1048 : }
1049 27 : if( aEnd.empty() )
1050 : {
1051 27 : aEnd.push_front( n2Start );
1052 27 : bOn = false;
1053 : }
1054 : }
1055 : // A ruby attribute stops immediately
1056 66 : if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
1057 : {
1058 0 : bOn = true;
1059 0 : break;
1060 : }
1061 66 : p2Lines = NULL;
1062 66 : if( lcl_Has2Lines( *pTmp, p2Lines, bTwo ) )
1063 : {
1064 0 : if( bTwo == bOn )
1065 : {
1066 0 : if( aEnd.back() < *pTmp->End() )
1067 0 : aEnd.back() = *pTmp->End();
1068 : }
1069 : else
1070 : {
1071 0 : bOn = bTwo;
1072 0 : if( aEnd.back() > *pTmp->End() )
1073 0 : aEnd.push_back( *pTmp->End() );
1074 0 : else if( aEnd.size() > 1 )
1075 0 : aEnd.pop_back();
1076 : else
1077 0 : aEnd.back() = *pTmp->End();
1078 : }
1079 : }
1080 : }
1081 40 : if( !bOn && !aEnd.empty() )
1082 40 : n2Start = aEnd.back();
1083 :
1084 40 : if( !aEnd.empty() )
1085 40 : aEnd.clear();
1086 :
1087 40 : bOn = true;
1088 40 : if( nRotate < nCount )
1089 : {
1090 2 : pRet->pItem = NULL;
1091 2 : pRet->pAttr = (*pHints)[nRotate];
1092 2 : aEnd.push_front( *pRet->pAttr->End() );
1093 2 : if( pRotItem )
1094 : {
1095 0 : aEnd.front() = GetText().getLength();
1096 0 : bOn = static_cast<const SvxCharRotateItem*>(pRotItem)->GetValue() ==
1097 0 : pRotate->GetValue();
1098 : }
1099 : }
1100 : else
1101 : {
1102 38 : pRet->pItem = pRotItem;
1103 38 : pRet->pAttr = NULL;
1104 38 : aEnd.push_front( GetText().getLength() );
1105 : }
1106 106 : for( size_t i = 0; i < nCount; ++i )
1107 : {
1108 66 : const SwTextAttr *pTmp = (*pHints)[i];
1109 66 : if( *pTmp->GetAnyEnd() <= rPos )
1110 0 : continue;
1111 66 : if( rPos < pTmp->GetStart() )
1112 : {
1113 27 : if( !bOn || aEnd.back() < pTmp->GetStart() )
1114 0 : break;
1115 27 : rPos = pTmp->GetStart();
1116 81 : while( !aEnd.empty() && aEnd.back() <= rPos )
1117 : {
1118 27 : bOn = !bOn;
1119 27 : aEnd.pop_back();
1120 : }
1121 27 : if( aEnd.empty() )
1122 : {
1123 27 : aEnd.push_front( rPos );
1124 27 : bOn = true;
1125 : }
1126 : }
1127 66 : if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
1128 : {
1129 0 : bOn = false;
1130 0 : break;
1131 : }
1132 66 : if( lcl_HasRotation( *pTmp, pRotate, bTwo ) )
1133 : {
1134 2 : if( bTwo == bOn )
1135 : {
1136 2 : if( aEnd.back() < *pTmp->End() )
1137 0 : aEnd.back() = *pTmp->End();
1138 : }
1139 : else
1140 : {
1141 0 : bOn = bTwo;
1142 0 : if( aEnd.back() > *pTmp->End() )
1143 0 : aEnd.push_back( *pTmp->End() );
1144 0 : else if( aEnd.size() > 1 )
1145 0 : aEnd.pop_back();
1146 : else
1147 0 : aEnd.back() = *pTmp->End();
1148 : }
1149 : }
1150 : }
1151 40 : if( bOn && !aEnd.empty() )
1152 40 : rPos = aEnd.back();
1153 40 : if( rPos > n2Start )
1154 0 : rPos = n2Start;
1155 40 : return pRet;
1156 : }
1157 89611 : return NULL;
1158 : }
1159 :
1160 : // A little helper class to manage the spaceadd-arrays of the text adjustment
1161 : // during a PaintMultiPortion.
1162 : // The constructor prepares the array for the first line of multiportion,
1163 : // the SecondLine-function restores the values for the first line and prepares
1164 : // the second line.
1165 : // The destructor restores the values of the last manipulation.
1166 : class SwSpaceManipulator
1167 : {
1168 : SwTextPaintInfo& rInfo;
1169 : SwMultiPortion& rMulti;
1170 : std::vector<long>* pOldSpaceAdd;
1171 : sal_uInt16 nOldSpIdx;
1172 : long nSpaceAdd;
1173 : bool bSpaceChg;
1174 : sal_uInt8 nOldDir;
1175 : public:
1176 : SwSpaceManipulator( SwTextPaintInfo& rInf, SwMultiPortion& rMult );
1177 : ~SwSpaceManipulator();
1178 : void SecondLine();
1179 4 : inline long GetSpaceAdd() const { return nSpaceAdd; }
1180 : };
1181 :
1182 169 : SwSpaceManipulator::SwSpaceManipulator( SwTextPaintInfo& rInf,
1183 : SwMultiPortion& rMult )
1184 : : rInfo(rInf)
1185 : , rMulti(rMult)
1186 169 : , nSpaceAdd(0)
1187 : {
1188 169 : pOldSpaceAdd = rInfo.GetpSpaceAdd();
1189 169 : nOldSpIdx = rInfo.GetSpaceIdx();
1190 169 : nOldDir = rInfo.GetDirection();
1191 169 : rInfo.SetDirection( rMulti.GetDirection() );
1192 169 : bSpaceChg = false;
1193 :
1194 169 : if( rMulti.IsDouble() )
1195 : {
1196 0 : nSpaceAdd = ( pOldSpaceAdd && !rMulti.HasTabulator() ) ?
1197 5 : rInfo.GetSpaceAdd() : 0;
1198 5 : if( rMulti.GetRoot().IsSpaceAdd() )
1199 : {
1200 0 : rInfo.SetpSpaceAdd( rMulti.GetRoot().GetpLLSpaceAdd() );
1201 0 : rInfo.ResetSpaceIdx();
1202 0 : bSpaceChg = rMulti.ChgSpaceAdd( &rMulti.GetRoot(), nSpaceAdd );
1203 : }
1204 5 : else if( rMulti.HasTabulator() )
1205 0 : rInfo.SetpSpaceAdd( NULL );
1206 : }
1207 164 : else if ( ! rMulti.IsBidi() )
1208 : {
1209 155 : rInfo.SetpSpaceAdd( rMulti.GetRoot().GetpLLSpaceAdd() );
1210 155 : rInfo.ResetSpaceIdx();
1211 : }
1212 169 : }
1213 :
1214 159 : void SwSpaceManipulator::SecondLine()
1215 : {
1216 159 : if( bSpaceChg )
1217 : {
1218 0 : rInfo.RemoveFirstSpaceAdd();
1219 0 : bSpaceChg = false;
1220 : }
1221 159 : SwLineLayout *pLay = rMulti.GetRoot().GetNext();
1222 159 : if( pLay->IsSpaceAdd() )
1223 : {
1224 5 : rInfo.SetpSpaceAdd( pLay->GetpLLSpaceAdd() );
1225 5 : rInfo.ResetSpaceIdx();
1226 5 : bSpaceChg = rMulti.ChgSpaceAdd( pLay, nSpaceAdd );
1227 : }
1228 : else
1229 : {
1230 154 : rInfo.SetpSpaceAdd( (!rMulti.IsDouble() || rMulti.HasTabulator() ) ?
1231 154 : 0 : pOldSpaceAdd );
1232 154 : rInfo.SetSpaceIdx( nOldSpIdx);
1233 : }
1234 159 : }
1235 :
1236 169 : SwSpaceManipulator::~SwSpaceManipulator()
1237 : {
1238 169 : if( bSpaceChg )
1239 : {
1240 0 : rInfo.RemoveFirstSpaceAdd();
1241 0 : bSpaceChg = false;
1242 : }
1243 169 : rInfo.SetpSpaceAdd( pOldSpaceAdd );
1244 169 : rInfo.SetSpaceIdx( nOldSpIdx);
1245 169 : rInfo.SetDirection( nOldDir );
1246 169 : }
1247 :
1248 : // Manages the paint for a SwMultiPortion.
1249 : // External, for the calling function, it seems to be a normal Paint-function,
1250 : // internal it is like a SwTextFrm::Paint with multiple DrawTextLines
1251 169 : void SwTextPainter::PaintMultiPortion( const SwRect &rPaint,
1252 : SwMultiPortion& rMulti, const SwMultiPortion* pEnvPor )
1253 : {
1254 169 : SwTextGridItem const*const pGrid(GetGridItem(pFrm->FindPageFrm()));
1255 169 : const bool bHasGrid = pGrid && GetInfo().SnapToGrid();
1256 169 : sal_uInt16 nRubyHeight = 0;
1257 169 : bool bRubyTop = false;
1258 :
1259 169 : if ( bHasGrid )
1260 : {
1261 0 : nRubyHeight = pGrid->GetRubyHeight();
1262 0 : bRubyTop = ! pGrid->GetRubyTextBelow();
1263 : }
1264 :
1265 : // do not allow grid mode for first line in ruby portion
1266 169 : const bool bRubyInGrid = bHasGrid && rMulti.IsRuby();
1267 :
1268 169 : const sal_uInt16 nOldHeight = rMulti.Height();
1269 169 : const bool bOldGridModeAllowed = GetInfo().SnapToGrid();
1270 :
1271 169 : if ( bRubyInGrid )
1272 : {
1273 0 : GetInfo().SetSnapToGrid( ! bRubyTop );
1274 0 : rMulti.Height( pCurr->Height() );
1275 : }
1276 :
1277 169 : SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
1278 169 : sal_uInt8 nEnvDir = 0;
1279 169 : sal_uInt8 nThisDir = 0;
1280 169 : sal_uInt8 nFrmDir = 0;
1281 169 : if ( rMulti.IsBidi() )
1282 : {
1283 : // these values are needed for the calculation of the x coordinate
1284 : // and the layout mode
1285 : OSL_ENSURE( ! pEnvPor || pEnvPor->IsBidi(),
1286 : "Oh no, I expected a BidiPortion" );
1287 9 : nFrmDir = GetInfo().GetTextFrm()->IsRightToLeft() ? 1 : 0;
1288 9 : nEnvDir = pEnvPor ? static_cast<const SwBidiPortion*>(pEnvPor)->GetLevel() % 2 : nFrmDir;
1289 9 : nThisDir = static_cast<SwBidiPortion&>(rMulti).GetLevel() % 2;
1290 : }
1291 :
1292 : #if OSL_DEBUG_LEVEL > 1
1293 : // only paint first level bidi portions
1294 : if( rMulti.Width() > 1 && ! pEnvPor )
1295 : GetInfo().DrawViewOpt( rMulti, POR_FLD );
1296 : #endif
1297 :
1298 169 : if ( bRubyInGrid )
1299 0 : rMulti.Height( nOldHeight );
1300 :
1301 : // do we have to repaint a post it portion?
1302 285 : if( GetInfo().OnWin() && rMulti.GetPortion() &&
1303 116 : ! rMulti.GetPortion()->Width() )
1304 23 : rMulti.GetPortion()->PrePaint( GetInfo(), &rMulti );
1305 :
1306 : // old values must be saved and restored at the end
1307 169 : sal_Int32 nOldLen = GetInfo().GetLen();
1308 169 : const SwTwips nOldX = GetInfo().X();
1309 169 : const SwTwips nOldY = GetInfo().Y();
1310 169 : sal_Int32 nOldIdx = GetInfo().GetIdx();
1311 :
1312 338 : SwSpaceManipulator aManip( GetInfo(), rMulti );
1313 :
1314 : SwFontSave *pFontSave;
1315 : SwFont* pTmpFnt;
1316 :
1317 169 : if( rMulti.IsDouble() )
1318 : {
1319 5 : pTmpFnt = new SwFont( *GetInfo().GetFont() );
1320 5 : if( rMulti.IsDouble() )
1321 : {
1322 5 : SetPropFont( 50 );
1323 5 : pTmpFnt->SetProportion( GetPropFont() );
1324 : }
1325 5 : pFontSave = new SwFontSave( GetInfo(), pTmpFnt, this );
1326 : }
1327 : else
1328 : {
1329 164 : pFontSave = NULL;
1330 164 : pTmpFnt = NULL;
1331 : }
1332 :
1333 169 : if( rMulti.HasBrackets() )
1334 : {
1335 4 : sal_Int32 nTmpOldIdx = GetInfo().GetIdx();
1336 4 : GetInfo().SetIdx(static_cast<SwDoubleLinePortion&>(rMulti).GetBrackets()->nStart);
1337 4 : SeekAndChg( GetInfo() );
1338 4 : static_cast<SwDoubleLinePortion&>(rMulti).PaintBracket( GetInfo(), 0, true );
1339 4 : GetInfo().SetIdx( nTmpOldIdx );
1340 : }
1341 :
1342 169 : const SwTwips nTmpX = GetInfo().X();
1343 :
1344 169 : SwLineLayout* pLay = &rMulti.GetRoot();// the first line of the multiportion
1345 169 : SwLinePortion* pPor = pLay->GetFirstPortion();//first portion of these line
1346 169 : SwTwips nOfst = 0;
1347 :
1348 : // GetInfo().Y() is the baseline from the surrounding line. We must switch
1349 : // this temporary to the baseline of the inner lines of the multiportion.
1350 169 : if( rMulti.HasRotation() )
1351 : {
1352 1 : if( rMulti.IsRevers() )
1353 : {
1354 0 : GetInfo().Y( nOldY - rMulti.GetAscent() );
1355 0 : nOfst = nTmpX + rMulti.Width();
1356 : }
1357 : else
1358 : {
1359 1 : GetInfo().Y( nOldY - rMulti.GetAscent() + rMulti.Height() );
1360 1 : nOfst = nTmpX;
1361 : }
1362 : }
1363 168 : else if ( rMulti.IsBidi() )
1364 : {
1365 : // does the current bidi portion has the same direction
1366 : // as its environment?
1367 9 : if ( nEnvDir != nThisDir )
1368 : {
1369 : // different directions, we have to adjust the x coordinate
1370 9 : SwTwips nMultiWidth = rMulti.Width() +
1371 9 : rMulti.CalcSpacing( GetInfo().GetSpaceAdd(), GetInfo() );
1372 :
1373 9 : if ( nFrmDir == nThisDir )
1374 0 : GetInfo().X( GetInfo().X() - nMultiWidth );
1375 : else
1376 9 : GetInfo().X( GetInfo().X() + nMultiWidth );
1377 : }
1378 :
1379 9 : nOfst = nOldY - rMulti.GetAscent();
1380 :
1381 : // set layout mode
1382 9 : aLayoutModeModifier.Modify( nThisDir );
1383 : }
1384 : else
1385 159 : nOfst = nOldY - rMulti.GetAscent();
1386 :
1387 169 : bool bRest = pLay->IsRest();
1388 169 : bool bFirst = true;
1389 :
1390 : OSL_ENSURE( 0 == GetInfo().GetUnderFnt() || rMulti.IsBidi(),
1391 : " Only BiDi portions are allowed to use the common underlining font" );
1392 :
1393 452 : do
1394 : {
1395 452 : if ( bHasGrid )
1396 : {
1397 0 : if( rMulti.HasRotation() )
1398 : {
1399 0 : const sal_uInt16 nAdjustment = ( pLay->Height() - pPor->Height() ) / 2 +
1400 0 : pPor->GetAscent();
1401 0 : if( rMulti.IsRevers() )
1402 0 : GetInfo().X( nOfst - nAdjustment );
1403 : else
1404 0 : GetInfo().X( nOfst + nAdjustment );
1405 : }
1406 : else
1407 : {
1408 : // special treatment for ruby portions in grid mode
1409 0 : SwTwips nAdjustment = 0;
1410 0 : if ( rMulti.IsRuby() )
1411 : {
1412 0 : if ( bRubyTop != ( pLay == &rMulti.GetRoot() ) )
1413 : // adjust base text
1414 0 : nAdjustment = ( pCurr->Height() - nRubyHeight - pPor->Height() ) / 2;
1415 0 : else if ( bRubyTop )
1416 : // adjust upper ruby text
1417 0 : nAdjustment = nRubyHeight - pPor->Height();
1418 : // else adjust lower ruby text
1419 : }
1420 :
1421 0 : GetInfo().Y( nOfst + nAdjustment + pPor->GetAscent() );
1422 : }
1423 : }
1424 452 : else if( rMulti.HasRotation() )
1425 : {
1426 1 : if( rMulti.IsRevers() )
1427 0 : GetInfo().X( nOfst - AdjustBaseLine( *pLay, pPor, 0, 0, true ) );
1428 : else
1429 1 : GetInfo().X( nOfst + AdjustBaseLine( *pLay, pPor ) );
1430 : }
1431 : else
1432 451 : GetInfo().Y( nOfst + AdjustBaseLine( *pLay, pPor ) );
1433 :
1434 452 : bool bSeeked = true;
1435 452 : GetInfo().SetLen( pPor->GetLen() );
1436 :
1437 452 : if( bRest && pPor->InFieldGrp() && !pPor->GetLen() )
1438 : {
1439 154 : if( static_cast<SwFieldPortion*>(pPor)->HasFont() )
1440 154 : bSeeked = false;
1441 : else
1442 0 : SeekAndChgBefore( GetInfo() );
1443 : }
1444 298 : else if( pPor->InTextGrp() || pPor->InFieldGrp() || pPor->InTabGrp() )
1445 262 : SeekAndChg( GetInfo() );
1446 36 : else if ( !bFirst && pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() )
1447 : {
1448 0 : if( GetRedln() )
1449 0 : SeekAndChg( GetInfo() );
1450 : else
1451 0 : SeekAndChgBefore( GetInfo() );
1452 : }
1453 : else
1454 36 : bSeeked = false;
1455 :
1456 452 : SwLinePortion *pNext = pPor->GetPortion();
1457 452 : if(GetInfo().OnWin() && pNext && !pNext->Width() )
1458 : {
1459 15 : if ( !bSeeked )
1460 0 : SeekAndChg( GetInfo() );
1461 15 : pNext->PrePaint( GetInfo(), pPor );
1462 : }
1463 :
1464 452 : CheckSpecialUnderline( pPor );
1465 452 : SwUnderlineFont* pUnderLineFnt = GetInfo().GetUnderFnt();
1466 452 : if ( pUnderLineFnt )
1467 : {
1468 2 : if ( rMulti.IsDouble() )
1469 0 : pUnderLineFnt->GetFont().SetProportion( 50 );
1470 2 : pUnderLineFnt->SetPos( GetInfo().GetPos() );
1471 : }
1472 :
1473 452 : if ( rMulti.IsBidi() )
1474 : {
1475 : // we do not allow any rotation inside a bidi portion
1476 9 : SwFont* pTmpFont = GetInfo().GetFont();
1477 9 : pTmpFont->SetVertical( 0, GetInfo().GetTextFrm()->IsVertical() );
1478 : }
1479 :
1480 452 : if( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->IsBidi() )
1481 : {
1482 : // but we do allow nested bidi portions
1483 : OSL_ENSURE( rMulti.IsBidi(), "Only nesting of bidi portions is allowed" );
1484 0 : PaintMultiPortion( rPaint, static_cast<SwMultiPortion&>(*pPor), &rMulti );
1485 : }
1486 : else
1487 452 : pPor->Paint( GetInfo() );
1488 :
1489 452 : if( GetFnt()->IsURL() && pPor->InTextGrp() )
1490 0 : GetInfo().NotifyURL( *pPor );
1491 :
1492 452 : bFirst &= !pPor->GetLen();
1493 452 : if( pNext || !pPor->IsMarginPortion() )
1494 434 : pPor->Move( GetInfo() );
1495 :
1496 452 : pPor = pNext;
1497 :
1498 : // If there's no portion left, we go to the next line
1499 452 : if( !pPor && pLay->GetNext() )
1500 : {
1501 159 : pLay = pLay->GetNext();
1502 159 : pPor = pLay->GetFirstPortion();
1503 159 : bRest = pLay->IsRest();
1504 159 : aManip.SecondLine();
1505 :
1506 : // delete underline font
1507 159 : delete GetInfo().GetUnderFnt();
1508 159 : GetInfo().SetUnderFnt( 0 );
1509 :
1510 159 : if( rMulti.HasRotation() )
1511 : {
1512 0 : if( rMulti.IsRevers() )
1513 : {
1514 0 : nOfst += pLay->Height();
1515 0 : GetInfo().Y( nOldY - rMulti.GetAscent() );
1516 : }
1517 : else
1518 : {
1519 0 : nOfst -= pLay->Height();
1520 0 : GetInfo().Y( nOldY - rMulti.GetAscent() + rMulti.Height() );
1521 : }
1522 : }
1523 159 : else if ( bHasGrid && rMulti.IsRuby() )
1524 : {
1525 0 : GetInfo().X( nTmpX );
1526 0 : if ( bRubyTop )
1527 : {
1528 0 : nOfst += nRubyHeight;
1529 0 : GetInfo().SetSnapToGrid( true );
1530 : }
1531 : else
1532 : {
1533 0 : nOfst += pCurr->Height() - nRubyHeight;
1534 0 : GetInfo().SetSnapToGrid( false );
1535 : }
1536 : } else
1537 : {
1538 159 : GetInfo().X( nTmpX );
1539 : // We switch to the baseline of the next inner line
1540 159 : nOfst += rMulti.GetRoot().Height();
1541 : }
1542 : }
1543 : } while( pPor );
1544 :
1545 169 : if ( bRubyInGrid )
1546 0 : GetInfo().SetSnapToGrid( bOldGridModeAllowed );
1547 :
1548 : // delete underline font
1549 169 : if ( ! rMulti.IsBidi() )
1550 : {
1551 160 : delete GetInfo().GetUnderFnt();
1552 160 : GetInfo().SetUnderFnt( 0 );
1553 : }
1554 :
1555 169 : GetInfo().SetIdx( nOldIdx );
1556 169 : GetInfo().Y( nOldY );
1557 :
1558 169 : if( rMulti.HasBrackets() )
1559 : {
1560 4 : sal_Int32 nTmpOldIdx = GetInfo().GetIdx();
1561 4 : GetInfo().SetIdx(static_cast<SwDoubleLinePortion&>(rMulti).GetBrackets()->nStart);
1562 4 : SeekAndChg( GetInfo() );
1563 4 : GetInfo().X( nOldX );
1564 4 : static_cast<SwDoubleLinePortion&>(rMulti).PaintBracket( GetInfo(),
1565 8 : aManip.GetSpaceAdd(), false );
1566 4 : GetInfo().SetIdx( nTmpOldIdx );
1567 : }
1568 : // Restore the saved values
1569 169 : GetInfo().X( nOldX );
1570 169 : GetInfo().SetLen( nOldLen );
1571 169 : delete pFontSave;
1572 169 : delete pTmpFnt;
1573 338 : SetPropFont( 0 );
1574 169 : }
1575 :
1576 0 : static bool lcl_ExtractFieldFollow( SwLineLayout* pLine, SwLinePortion* &rpField )
1577 : {
1578 0 : SwLinePortion* pLast = pLine;
1579 0 : rpField = pLine->GetPortion();
1580 0 : while( rpField && !rpField->InFieldGrp() )
1581 : {
1582 0 : pLast = rpField;
1583 0 : rpField = rpField->GetPortion();
1584 : }
1585 0 : bool bRet = rpField != 0;
1586 0 : if( bRet )
1587 : {
1588 0 : if( static_cast<SwFieldPortion*>(rpField)->IsFollow() )
1589 : {
1590 0 : rpField->Truncate();
1591 0 : pLast->SetPortion( NULL );
1592 : }
1593 : else
1594 0 : rpField = NULL;
1595 : }
1596 0 : pLine->Truncate();
1597 0 : return bRet;
1598 : }
1599 :
1600 : // If a multi portion completely has to go to the
1601 : // next line, this function is called to trunctate
1602 : // the rest of the remaining multi portion
1603 1 : static void lcl_TruncateMultiPortion( SwMultiPortion& rMulti, SwTextFormatInfo& rInf,
1604 : sal_Int32 nStartIdx )
1605 : {
1606 1 : rMulti.GetRoot().Truncate();
1607 1 : rMulti.GetRoot().SetLen(0);
1608 1 : rMulti.GetRoot().Width(0);
1609 : // rMulti.CalcSize( *this, aInf );
1610 1 : if ( rMulti.GetRoot().GetNext() )
1611 : {
1612 0 : rMulti.GetRoot().GetNext()->Truncate();
1613 0 : rMulti.GetRoot().GetNext()->SetLen( 0 );
1614 0 : rMulti.GetRoot().GetNext()->Width( 0 );
1615 : }
1616 1 : rMulti.Width( 0 );
1617 1 : rMulti.SetLen(0);
1618 1 : rInf.SetIdx( nStartIdx );
1619 1 : }
1620 :
1621 : // Manages the formatting of a SwMultiPortion. External, for the calling
1622 : // function, it seems to be a normal Format-function, internal it is like a
1623 : // SwTextFrm::_Format with multiple BuildPortions
1624 246 : bool SwTextFormatter::BuildMultiPortion( SwTextFormatInfo &rInf,
1625 : SwMultiPortion& rMulti )
1626 : {
1627 246 : SwTwips nMaxWidth = rInf.Width();
1628 246 : SwTwips nOldX = 0;
1629 :
1630 246 : if( rMulti.HasBrackets() )
1631 : {
1632 4 : sal_Int32 nOldIdx = rInf.GetIdx();
1633 4 : rInf.SetIdx( static_cast<SwDoubleLinePortion&>(rMulti).GetBrackets()->nStart );
1634 4 : SeekAndChg( rInf );
1635 4 : nOldX = GetInfo().X();
1636 4 : static_cast<SwDoubleLinePortion&>(rMulti).FormatBrackets( rInf, nMaxWidth );
1637 4 : rInf.SetIdx( nOldIdx );
1638 : }
1639 :
1640 246 : SeekAndChg( rInf );
1641 246 : boost::scoped_ptr<SwFontSave> xFontSave;
1642 246 : if( rMulti.IsDouble() )
1643 : {
1644 5 : SwFont* pTmpFnt = new SwFont( *rInf.GetFont() );
1645 5 : if( rMulti.IsDouble() )
1646 : {
1647 5 : SetPropFont( 50 );
1648 5 : pTmpFnt->SetProportion( GetPropFont() );
1649 : }
1650 5 : xFontSave.reset(new SwFontSave(rInf, pTmpFnt, this));
1651 : }
1652 :
1653 492 : SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
1654 246 : if ( rMulti.IsBidi() )
1655 : {
1656 : // set layout mode
1657 43 : aLayoutModeModifier.Modify( ! rInf.GetTextFrm()->IsRightToLeft() );
1658 : }
1659 :
1660 246 : SwTwips nTmpX = 0;
1661 :
1662 246 : if( rMulti.HasRotation() )
1663 : {
1664 : // For nMaxWidth we take the height of the body frame.
1665 : // #i25067#: If the current frame is inside a table, we restrict
1666 : // nMaxWidth to the current frame height, unless the frame size
1667 : // attribute is set to variable size:
1668 :
1669 : // We set nTmpX (which is used for portion calculating) to the
1670 : // current Y value
1671 41 : const SwPageFrm* pPage = pFrm->FindPageFrm();
1672 : OSL_ENSURE( pPage, "No page in frame!");
1673 41 : const SwLayoutFrm* pUpperFrm = pPage;
1674 :
1675 41 : if ( pFrm->IsInTab() )
1676 : {
1677 23 : pUpperFrm = pFrm->GetUpper();
1678 46 : while ( pUpperFrm && !pUpperFrm->IsCellFrm() )
1679 0 : pUpperFrm = pUpperFrm->GetUpper();
1680 : assert(pUpperFrm); //pFrm is in table but does not have an upper cell frame
1681 23 : if (!pUpperFrm)
1682 0 : return false;
1683 23 : const SwTableLine* pLine = static_cast<const SwRowFrm*>(pUpperFrm->GetUpper())->GetTabLine();
1684 23 : const SwFormatFrmSize& rFrameFormatSize = pLine->GetFrameFormat()->GetFrmSize();
1685 23 : if ( ATT_VAR_SIZE == rFrameFormatSize.GetHeightSizeType() )
1686 3 : pUpperFrm = pPage;
1687 : }
1688 41 : if ( pUpperFrm == pPage && !pFrm->IsInFootnote() )
1689 21 : pUpperFrm = pPage->FindBodyCont();
1690 :
1691 : nMaxWidth = pUpperFrm ?
1692 41 : ( rInf.GetTextFrm()->IsVertical() ?
1693 0 : pUpperFrm->Prt().Width() :
1694 41 : pUpperFrm->Prt().Height() ) :
1695 123 : USHRT_MAX;
1696 : }
1697 : else
1698 205 : nTmpX = rInf.X();
1699 :
1700 246 : SwMultiPortion* pOldMulti = pMulti;
1701 :
1702 246 : pMulti = &rMulti;
1703 246 : SwLineLayout *pOldCurr = pCurr;
1704 246 : sal_Int32 nOldStart = GetStart();
1705 246 : SwTwips nMinWidth = nTmpX + 1;
1706 246 : SwTwips nActWidth = nMaxWidth;
1707 246 : const sal_Int32 nStartIdx = rInf.GetIdx();
1708 246 : sal_Int32 nMultiLen = rMulti.GetLen();
1709 :
1710 : SwLinePortion *pFirstRest;
1711 : SwLinePortion *pSecondRest;
1712 246 : if( rMulti.IsFormatted() )
1713 : {
1714 0 : if( !lcl_ExtractFieldFollow( &rMulti.GetRoot(), pFirstRest )
1715 0 : && rMulti.IsDouble() && rMulti.GetRoot().GetNext() )
1716 0 : lcl_ExtractFieldFollow( rMulti.GetRoot().GetNext(), pFirstRest );
1717 0 : if( !rMulti.IsDouble() && rMulti.GetRoot().GetNext() )
1718 0 : lcl_ExtractFieldFollow( rMulti.GetRoot().GetNext(), pSecondRest );
1719 : else
1720 0 : pSecondRest = NULL;
1721 : }
1722 : else
1723 : {
1724 246 : pFirstRest = rMulti.GetRoot().GetPortion();
1725 246 : pSecondRest = rMulti.GetRoot().GetNext() ?
1726 246 : rMulti.GetRoot().GetNext()->GetPortion() : NULL;
1727 246 : if( pFirstRest )
1728 143 : rMulti.GetRoot().SetPortion( NULL );
1729 246 : if( pSecondRest )
1730 14 : rMulti.GetRoot().GetNext()->SetPortion( NULL );
1731 246 : rMulti.SetFormatted();
1732 246 : nMultiLen = nMultiLen - rInf.GetIdx();
1733 : }
1734 :
1735 : // save some values
1736 246 : const OUString* pOldText = &(rInf.GetText());
1737 246 : const SwTwips nOldPaintOfst = rInf.GetPaintOfst();
1738 492 : std::shared_ptr<vcl::TextLayoutCache> const pOldCachedVclData(rInf.GetCachedVclData());
1739 246 : rInf.SetCachedVclData(nullptr);
1740 :
1741 492 : OUString const aMultiStr( rInf.GetText().copy(0, nMultiLen + rInf.GetIdx()) );
1742 246 : rInf.SetText( aMultiStr );
1743 492 : SwTextFormatInfo aInf( rInf, rMulti.GetRoot(), nActWidth );
1744 : // Do we allow break cuts? The FirstMulti-Flag is evaluated during
1745 : // line break determination.
1746 246 : bool bFirstMulti = rInf.GetIdx() != rInf.GetLineStart();
1747 :
1748 246 : SwLinePortion *pNextFirst = NULL;
1749 246 : SwLinePortion *pNextSecond = NULL;
1750 246 : bool bRet = false;
1751 :
1752 246 : SwTextGridItem const*const pGrid(GetGridItem(pFrm->FindPageFrm()));
1753 246 : const bool bHasGrid = pGrid && GRID_LINES_CHARS == pGrid->GetGridType();
1754 :
1755 246 : bool bRubyTop = false;
1756 :
1757 246 : if ( bHasGrid )
1758 0 : bRubyTop = ! pGrid->GetRubyTextBelow();
1759 :
1760 : do
1761 : {
1762 286 : pCurr = &rMulti.GetRoot();
1763 286 : nStart = nStartIdx;
1764 286 : bRet = false;
1765 286 : FormatReset( aInf );
1766 286 : aInf.X( nTmpX );
1767 286 : aInf.Width( sal_uInt16(nActWidth) );
1768 286 : aInf.RealWidth( sal_uInt16(nActWidth) );
1769 286 : aInf.SetFirstMulti( bFirstMulti );
1770 286 : aInf.SetNumDone( rInf.IsNumDone() );
1771 286 : aInf.SetFootnoteDone( rInf.IsFootnoteDone() );
1772 :
1773 286 : if( pFirstRest )
1774 : {
1775 : OSL_ENSURE( pFirstRest->InFieldGrp(), "BuildMulti: Fieldrest expected");
1776 : SwFieldPortion *pField =
1777 : static_cast<SwFieldPortion*>(pFirstRest)->Clone(
1778 143 : static_cast<SwFieldPortion*>(pFirstRest)->GetExp() );
1779 143 : pField->SetFollow( true );
1780 143 : aInf.SetRest( pField );
1781 : }
1782 286 : aInf.SetRuby( rMulti.IsRuby() && rMulti.OnTop() );
1783 :
1784 : // in grid mode we temporarily have to disable the grid for the ruby line
1785 286 : const bool bOldGridModeAllowed = GetInfo().SnapToGrid();
1786 286 : if ( bHasGrid && aInf.IsRuby() && bRubyTop )
1787 0 : aInf.SetSnapToGrid( false );
1788 :
1789 : // If there's no more rubytext, then buildportion is forbidden
1790 286 : if( pFirstRest || !aInf.IsRuby() )
1791 286 : BuildPortions( aInf );
1792 :
1793 286 : aInf.SetSnapToGrid( bOldGridModeAllowed );
1794 :
1795 286 : rMulti.CalcSize( *this, aInf );
1796 286 : pCurr->SetRealHeight( pCurr->Height() );
1797 :
1798 286 : if( rMulti.IsBidi() )
1799 : {
1800 43 : pNextFirst = aInf.GetRest();
1801 43 : break;
1802 : }
1803 :
1804 243 : if( rMulti.HasRotation() && !rMulti.IsDouble() )
1805 41 : break;
1806 : // second line has to be formatted
1807 202 : else if( pCurr->GetLen()<nMultiLen || rMulti.IsRuby() || aInf.GetRest())
1808 : {
1809 177 : sal_Int32 nFirstLen = pCurr->GetLen();
1810 177 : delete pCurr->GetNext();
1811 177 : pCurr->SetNext( new SwLineLayout() );
1812 177 : pCurr = pCurr->GetNext();
1813 177 : nStart = aInf.GetIdx();
1814 177 : aInf.X( nTmpX );
1815 177 : SwTextFormatInfo aTmp( aInf, *pCurr, nActWidth );
1816 177 : if( rMulti.IsRuby() )
1817 : {
1818 157 : aTmp.SetRuby( !rMulti.OnTop() );
1819 157 : pNextFirst = aInf.GetRest();
1820 157 : if( pSecondRest )
1821 : {
1822 : OSL_ENSURE( pSecondRest->InFieldGrp(), "Fieldrest expected");
1823 : SwFieldPortion *pField = static_cast<SwFieldPortion*>(pSecondRest)->Clone(
1824 14 : static_cast<SwFieldPortion*>(pSecondRest)->GetExp() );
1825 14 : pField->SetFollow( true );
1826 14 : aTmp.SetRest( pField );
1827 : }
1828 157 : if( !rMulti.OnTop() && nFirstLen < nMultiLen )
1829 0 : bRet = true;
1830 : }
1831 : else
1832 20 : aTmp.SetRest( aInf.GetRest() );
1833 177 : aInf.SetRest( NULL );
1834 :
1835 : // in grid mode we temporarily have to disable the grid for the ruby line
1836 177 : if ( bHasGrid && aTmp.IsRuby() && ! bRubyTop )
1837 0 : aTmp.SetSnapToGrid( false );
1838 :
1839 177 : BuildPortions( aTmp );
1840 :
1841 177 : aTmp.SetSnapToGrid( bOldGridModeAllowed );
1842 :
1843 177 : rMulti.CalcSize( *this, aInf );
1844 177 : rMulti.GetRoot().SetRealHeight( rMulti.GetRoot().Height() );
1845 177 : pCurr->SetRealHeight( pCurr->Height() );
1846 177 : if( rMulti.IsRuby() )
1847 : {
1848 157 : pNextSecond = aTmp.GetRest();
1849 157 : if( pNextFirst )
1850 0 : bRet = true;
1851 : }
1852 : else
1853 20 : pNextFirst = aTmp.GetRest();
1854 517 : if( ( !aTmp.IsRuby() && nFirstLen + pCurr->GetLen() < nMultiLen )
1855 334 : || aTmp.GetRest() )
1856 : // our guess for width of multiportion was too small,
1857 : // text did not fit into multiportion
1858 20 : bRet = true;
1859 : }
1860 202 : if( rMulti.IsRuby() )
1861 157 : break;
1862 45 : if( bRet )
1863 : {
1864 : // our guess for multiportion width was too small,
1865 : // we set min to act
1866 20 : nMinWidth = nActWidth;
1867 20 : nActWidth = ( 3 * nMaxWidth + nMinWidth + 3 ) / 4;
1868 20 : if ( nActWidth == nMaxWidth && rInf.GetLineStart() == rInf.GetIdx() )
1869 : // we have too less space, we must allow break cuts
1870 : // ( the first multi flag is considered during TextPortion::_Format() )
1871 0 : bFirstMulti = false;
1872 20 : if( nActWidth <= nMinWidth )
1873 0 : break;
1874 : }
1875 : else
1876 : {
1877 : // For Solaris, this optimization can causes trouble:
1878 : // Setting this to the portion width ( = rMulti.Width() )
1879 : // can make GetTextBreak inside SwTextGuess::Guess return to small
1880 : // values. Therefore we add some extra twips.
1881 25 : if( nActWidth > nTmpX + rMulti.Width() + 6 )
1882 5 : nActWidth = nTmpX + rMulti.Width() + 6;
1883 25 : nMaxWidth = nActWidth;
1884 25 : nActWidth = ( 3 * nMaxWidth + nMinWidth + 3 ) / 4;
1885 25 : if( nActWidth >= nMaxWidth )
1886 5 : break;
1887 : // we do not allow break cuts during formatting
1888 20 : bFirstMulti = true;
1889 : }
1890 40 : delete pNextFirst;
1891 40 : pNextFirst = NULL;
1892 : } while ( true );
1893 :
1894 246 : pMulti = pOldMulti;
1895 :
1896 246 : pCurr = pOldCurr;
1897 246 : nStart = nOldStart;
1898 246 : SetPropFont( 0 );
1899 :
1900 246 : rMulti.SetLen( rMulti.GetRoot().GetLen() + ( rMulti.GetRoot().GetNext() ?
1901 246 : rMulti.GetRoot().GetNext()->GetLen() : 0 ) );
1902 :
1903 246 : if( rMulti.IsDouble() )
1904 : {
1905 5 : static_cast<SwDoubleLinePortion&>(rMulti).CalcBlanks( rInf );
1906 5 : if( static_cast<SwDoubleLinePortion&>(rMulti).GetLineDiff() )
1907 : {
1908 5 : SwLineLayout* pLine = &rMulti.GetRoot();
1909 5 : if( static_cast<SwDoubleLinePortion&>(rMulti).GetLineDiff() > 0 )
1910 : {
1911 5 : rInf.SetIdx( nStartIdx + pLine->GetLen() );
1912 5 : pLine = pLine->GetNext();
1913 : }
1914 5 : if( pLine )
1915 : {
1916 5 : GetInfo().SetMulti( true );
1917 :
1918 : // If the fourth element bSkipKashida of function CalcNewBlock is true, multiportion will be showed in justification.
1919 : // Kashida (Persian) is a type of justification used in some cursive scripts, particularly Arabic.
1920 : // In contrast to white-space justification, which increases the length of a line of text by expanding spaces between words or individual letters,
1921 : // kashida justification is accomplished by elongating characters at certain chosen points.
1922 : // Kashida justification can be combined with white-space justification to various extents.
1923 : // The default value of bSkipKashida (the 4th parameter passed to 'CalcNewBlock') is false.
1924 : // Only when Adjust is SVX_ADJUST_BLOCK ( alignment is justify ), multiportion will be showed in justification in new code.
1925 5 : CalcNewBlock( pLine, NULL, rMulti.Width(), GetAdjust() != SVX_ADJUST_BLOCK );
1926 :
1927 5 : GetInfo().SetMulti( false );
1928 : }
1929 5 : rInf.SetIdx( nStartIdx );
1930 : }
1931 5 : if( static_cast<SwDoubleLinePortion&>(rMulti).GetBrackets() )
1932 : {
1933 4 : rMulti.Width( rMulti.Width() +
1934 4 : static_cast<SwDoubleLinePortion&>(rMulti).BracketWidth() );
1935 4 : GetInfo().X( nOldX );
1936 : }
1937 : }
1938 : else
1939 : {
1940 241 : rMulti.ActualizeTabulator();
1941 241 : if( rMulti.IsRuby() )
1942 : {
1943 157 : static_cast<SwRubyPortion&>(rMulti).Adjust( rInf );
1944 157 : static_cast<SwRubyPortion&>(rMulti).CalcRubyOffset();
1945 : }
1946 : }
1947 246 : if( rMulti.HasRotation() )
1948 : {
1949 41 : SwTwips nH = rMulti.Width();
1950 41 : SwTwips nAsc = rMulti.GetAscent() + ( nH - rMulti.Height() )/2;
1951 41 : if( nAsc > nH )
1952 0 : nAsc = nH;
1953 41 : else if( nAsc < 0 )
1954 0 : nAsc = 0;
1955 41 : rMulti.Width( rMulti.Height() );
1956 41 : rMulti.Height( sal_uInt16(nH) );
1957 41 : rMulti.SetAscent( sal_uInt16(nAsc) );
1958 42 : bRet = ( rInf.GetPos().X() + rMulti.Width() > rInf.Width() ) &&
1959 42 : nStartIdx != rInf.GetLineStart();
1960 : }
1961 205 : else if ( rMulti.IsBidi() )
1962 : {
1963 43 : bRet = rMulti.GetLen() < nMultiLen || pNextFirst;
1964 : }
1965 :
1966 : // line break has to be performed!
1967 246 : if( bRet )
1968 : {
1969 : OSL_ENSURE( !pNextFirst || pNextFirst->InFieldGrp(),
1970 : "BuildMultiPortion: Surprising restportion, field expected" );
1971 : SwMultiPortion *pTmp;
1972 7 : if( rMulti.IsDouble() )
1973 : pTmp = new SwDoubleLinePortion( static_cast<SwDoubleLinePortion&>(rMulti),
1974 0 : nMultiLen + rInf.GetIdx() );
1975 7 : else if( rMulti.IsRuby() )
1976 : {
1977 : OSL_ENSURE( !pNextSecond || pNextSecond->InFieldGrp(),
1978 : "BuildMultiPortion: Surprising restportion, field expected" );
1979 :
1980 0 : if ( rInf.GetIdx() == rInf.GetLineStart() )
1981 : {
1982 : // the ruby portion has to be split in two portions
1983 : pTmp = new SwRubyPortion( static_cast<SwRubyPortion&>(rMulti),
1984 0 : nMultiLen + rInf.GetIdx() );
1985 :
1986 0 : if( pNextSecond )
1987 : {
1988 0 : pTmp->GetRoot().SetNext( new SwLineLayout() );
1989 0 : pTmp->GetRoot().GetNext()->SetPortion( pNextSecond );
1990 : }
1991 0 : pTmp->SetFollowField();
1992 : }
1993 : else
1994 : {
1995 : // we try to keep our ruby portion together
1996 0 : lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
1997 0 : pTmp = 0;
1998 : }
1999 : }
2000 7 : else if( rMulti.HasRotation() )
2001 : {
2002 : // we try to keep our rotated portion together
2003 1 : lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
2004 1 : pTmp = new SwRotatedPortion( nMultiLen + rInf.GetIdx(),
2005 1 : rMulti.GetDirection() );
2006 : }
2007 : // during a recursion of BuildMultiPortions we may not build
2008 : // a new SwBidiPortion, this would cause a memory leak
2009 6 : else if( rMulti.IsBidi() && ! pMulti )
2010 : {
2011 6 : if ( ! rMulti.GetLen() )
2012 0 : lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
2013 :
2014 : // If there is a HolePortion at the end of the bidi portion,
2015 : // it has to be moved behind the bidi portion. Otherwise
2016 : // the visual cursor travelling gets into trouble.
2017 6 : SwLineLayout& aRoot = rMulti.GetRoot();
2018 6 : SwLinePortion* pPor = aRoot.GetFirstPortion();
2019 12 : while ( pPor )
2020 : {
2021 6 : if ( pPor->GetPortion() && pPor->GetPortion()->IsHolePortion() )
2022 : {
2023 6 : SwLinePortion* pHolePor = pPor->GetPortion();
2024 6 : pPor->SetPortion( NULL );
2025 6 : aRoot.SetLen( aRoot.GetLen() - pHolePor->GetLen() );
2026 6 : rMulti.SetLen( rMulti.GetLen() - pHolePor->GetLen() );
2027 6 : rMulti.SetPortion( pHolePor );
2028 6 : break;
2029 : }
2030 0 : pPor = pPor->GetPortion();
2031 : }
2032 :
2033 6 : pTmp = new SwBidiPortion( nMultiLen + rInf.GetIdx(),
2034 6 : static_cast<SwBidiPortion&>(rMulti).GetLevel() );
2035 : }
2036 : else
2037 0 : pTmp = NULL;
2038 :
2039 7 : if ( ! rMulti.GetLen() && rInf.GetLast() )
2040 : {
2041 1 : SeekAndChgBefore( rInf );
2042 1 : rInf.GetLast()->FormatEOL( rInf );
2043 : }
2044 :
2045 7 : if( pNextFirst && pTmp )
2046 : {
2047 0 : pTmp->SetFollowField();
2048 0 : pTmp->GetRoot().SetPortion( pNextFirst );
2049 : }
2050 : else
2051 : // A follow field portion is still waiting. If nobody wants it,
2052 : // we delete it.
2053 7 : delete pNextFirst;
2054 :
2055 7 : rInf.SetRest( pTmp );
2056 : }
2057 :
2058 246 : rInf.SetCachedVclData(pOldCachedVclData);
2059 246 : rInf.SetText( *pOldText );
2060 246 : rInf.SetPaintOfst( nOldPaintOfst );
2061 246 : rInf.SetStop( aInf.IsStop() );
2062 246 : rInf.SetNumDone( true );
2063 246 : rInf.SetFootnoteDone( true );
2064 246 : SeekAndChg( rInf );
2065 246 : delete pFirstRest;
2066 246 : delete pSecondRest;
2067 246 : xFontSave.reset();
2068 492 : return bRet;
2069 : }
2070 :
2071 : // When a fieldportion at the end of line breaks and needs a following
2072 : // fieldportion in the next line, then the "restportion" of the formatinfo
2073 : // has to be set. Normally this happens during the formatting of the first
2074 : // part of the fieldportion.
2075 : // But sometimes the formatting starts at the line with the following part,
2076 : // especially when the following part is on the next page.
2077 : // In this case the MakeRestPortion-function has to create the following part.
2078 : // The first parameter is the line that contains possibly a first part
2079 : // of a field. When the function finds such field part, it creates the right
2080 : // restportion. This may be a multiportion, e.g. if the field is surrounded by
2081 : // a doubleline- or ruby-portion.
2082 : // The second parameter is the start index of the line.
2083 80 : SwLinePortion* SwTextFormatter::MakeRestPortion( const SwLineLayout* pLine,
2084 : sal_Int32 nPosition )
2085 : {
2086 80 : if( !nPosition )
2087 0 : return NULL;
2088 80 : sal_Int32 nMultiPos = nPosition - pLine->GetLen();
2089 80 : const SwMultiPortion *pTmpMulti = NULL;
2090 80 : const SwMultiPortion *pHelpMulti = NULL;
2091 80 : const SwLinePortion* pPor = pLine->GetFirstPortion();
2092 80 : SwFieldPortion *pField = NULL;
2093 240 : while( pPor )
2094 : {
2095 80 : if( pPor->GetLen() )
2096 : {
2097 80 : if( !pHelpMulti )
2098 : {
2099 80 : nMultiPos = nMultiPos + pPor->GetLen();
2100 80 : pTmpMulti = NULL;
2101 : }
2102 : }
2103 80 : if( pPor->InFieldGrp() )
2104 : {
2105 0 : if( !pHelpMulti )
2106 0 : pTmpMulti = NULL;
2107 0 : pField = const_cast<SwFieldPortion*>(static_cast<const SwFieldPortion*>(pPor));
2108 : }
2109 80 : else if( pPor->IsMultiPortion() )
2110 : {
2111 : OSL_ENSURE( !pHelpMulti || pHelpMulti->IsBidi(),
2112 : "Nested multiportions are forbidden." );
2113 :
2114 0 : pField = NULL;
2115 0 : pTmpMulti = static_cast<const SwMultiPortion*>(pPor);
2116 : }
2117 80 : pPor = pPor->GetPortion();
2118 : // If the last portion is a multi-portion, we enter it
2119 : // and look for a field portion inside.
2120 : // If we are already in a multiportion, we could change to the
2121 : // next line
2122 80 : if( !pPor && pTmpMulti )
2123 : {
2124 0 : if( pHelpMulti )
2125 : { // We're already inside the multiportion, let's take the second
2126 : // line, if we are in a double line portion
2127 0 : if( !pHelpMulti->IsRuby() )
2128 0 : pPor = pHelpMulti->GetRoot().GetNext();
2129 0 : pTmpMulti = NULL;
2130 : }
2131 : else
2132 : { // Now we enter a multiportion, in a ruby portion we take the
2133 : // main line, not the phonetic line, in a doublelineportion we
2134 : // starts with the first line.
2135 0 : pHelpMulti = pTmpMulti;
2136 0 : nMultiPos = nMultiPos - pHelpMulti->GetLen();
2137 0 : if( pHelpMulti->IsRuby() && pHelpMulti->OnTop() )
2138 0 : pPor = pHelpMulti->GetRoot().GetNext();
2139 : else
2140 0 : pPor = pHelpMulti->GetRoot().GetFirstPortion();
2141 : }
2142 : }
2143 : }
2144 80 : if( pField && !pField->HasFollow() )
2145 0 : pField = NULL;
2146 :
2147 80 : SwLinePortion *pRest = NULL;
2148 80 : if( pField )
2149 : {
2150 0 : const SwTextAttr *pHint = GetAttr( nPosition - 1 );
2151 0 : if ( pHint
2152 0 : && ( pHint->Which() == RES_TXTATR_FIELD
2153 0 : || pHint->Which() == RES_TXTATR_ANNOTATION ) )
2154 : {
2155 0 : pRest = NewFieldPortion( GetInfo(), pHint );
2156 0 : if( pRest->InFieldGrp() )
2157 0 : static_cast<SwFieldPortion*>(pRest)->TakeNextOffset( pField );
2158 : else
2159 : {
2160 0 : delete pRest;
2161 0 : pRest = NULL;
2162 : }
2163 : }
2164 : }
2165 80 : if( !pHelpMulti )
2166 80 : return pRest;
2167 :
2168 0 : nPosition = nMultiPos + pHelpMulti->GetLen();
2169 0 : SwMultiCreator* pCreate = GetInfo().GetMultiCreator( nMultiPos, 0 );
2170 :
2171 0 : if ( !pCreate )
2172 : {
2173 : OSL_ENSURE( !pHelpMulti->GetLen(), "Multiportion without attribute?" );
2174 0 : if ( nMultiPos )
2175 0 : --nMultiPos;
2176 0 : pCreate = GetInfo().GetMultiCreator( --nMultiPos, 0 );
2177 : }
2178 :
2179 0 : if (!pCreate)
2180 0 : return pRest;
2181 :
2182 0 : if( pRest || nMultiPos > nPosition || ( pHelpMulti->IsRuby() &&
2183 0 : static_cast<const SwRubyPortion*>(pHelpMulti)->GetRubyOffset() < COMPLETE_STRING ) )
2184 : {
2185 : SwMultiPortion* pTmp;
2186 0 : if( pHelpMulti->IsDouble() )
2187 0 : pTmp = new SwDoubleLinePortion( *pCreate, nMultiPos );
2188 0 : else if( pHelpMulti->IsBidi() )
2189 0 : pTmp = new SwBidiPortion( nMultiPos, pCreate->nLevel );
2190 0 : else if( pHelpMulti->IsRuby() )
2191 : {
2192 : bool bRubyTop;
2193 0 : bool* pRubyPos = 0;
2194 :
2195 0 : if ( GetInfo().SnapToGrid() )
2196 : {
2197 : SwTextGridItem const*const pGrid(
2198 0 : GetGridItem(pFrm->FindPageFrm()));
2199 0 : if ( pGrid )
2200 : {
2201 0 : bRubyTop = ! pGrid->GetRubyTextBelow();
2202 0 : pRubyPos = &bRubyTop;
2203 : }
2204 : }
2205 :
2206 0 : pTmp = new SwRubyPortion( *pCreate, *GetInfo().GetFont(),
2207 0 : *pFrm->GetTextNode()->getIDocumentSettingAccess(),
2208 : nMultiPos, static_cast<const SwRubyPortion*>(pHelpMulti)->GetRubyOffset(),
2209 0 : pRubyPos );
2210 : }
2211 0 : else if( pHelpMulti->HasRotation() )
2212 0 : pTmp = new SwRotatedPortion( nMultiPos, pHelpMulti->GetDirection() );
2213 : else
2214 : {
2215 0 : delete pCreate;
2216 0 : return pRest;
2217 : }
2218 0 : delete pCreate;
2219 0 : pTmp->SetFollowField();
2220 0 : if( pRest )
2221 : {
2222 0 : SwLineLayout *pLay = &pTmp->GetRoot();
2223 0 : if( pTmp->IsRuby() && pTmp->OnTop() )
2224 : {
2225 0 : pLay->SetNext( new SwLineLayout() );
2226 0 : pLay = pLay->GetNext();
2227 : }
2228 0 : pLay->SetPortion( pRest );
2229 : }
2230 0 : return pTmp;
2231 : }
2232 0 : delete (pCreate);
2233 0 : return pRest;
2234 : }
2235 :
2236 : // SwTextCursorSave notes the start and current line of a SwTextCursor,
2237 : // sets them to the values for GetCrsrOfst inside a multiportion
2238 : // and restores them in the destructor.
2239 0 : SwTextCursorSave::SwTextCursorSave( SwTextCursor* pTextCursor,
2240 : SwMultiPortion* pMulti,
2241 : SwTwips nY,
2242 : sal_uInt16& nX,
2243 : sal_Int32 nCurrStart,
2244 : long nSpaceAdd )
2245 : {
2246 0 : pTextCrsr = pTextCursor;
2247 0 : nStart = pTextCursor->nStart;
2248 0 : pTextCursor->nStart = nCurrStart;
2249 0 : pCurr = pTextCursor->pCurr;
2250 0 : pTextCursor->pCurr = &pMulti->GetRoot();
2251 0 : while( pTextCursor->Y() + pTextCursor->GetLineHeight() < nY &&
2252 0 : pTextCursor->Next() )
2253 : ; // nothing
2254 0 : nWidth = pTextCursor->pCurr->Width();
2255 0 : nOldProp = pTextCursor->GetPropFont();
2256 :
2257 0 : if ( pMulti->IsDouble() || pMulti->IsBidi() )
2258 : {
2259 0 : bSpaceChg = pMulti->ChgSpaceAdd( pTextCursor->pCurr, nSpaceAdd );
2260 :
2261 : sal_Int32 nSpaceCnt;
2262 0 : if ( pMulti->IsDouble() )
2263 : {
2264 0 : pTextCursor->SetPropFont( 50 );
2265 0 : nSpaceCnt = static_cast<SwDoubleLinePortion*>(pMulti)->GetSpaceCnt();
2266 : }
2267 : else
2268 : {
2269 0 : const sal_Int32 nOldIdx = pTextCursor->GetInfo().GetIdx();
2270 0 : pTextCursor->GetInfo().SetIdx ( nCurrStart );
2271 0 : nSpaceCnt = static_cast<SwBidiPortion*>(pMulti)->GetSpaceCnt(pTextCursor->GetInfo());
2272 0 : pTextCursor->GetInfo().SetIdx ( nOldIdx );
2273 : }
2274 :
2275 0 : if( nSpaceAdd > 0 && !pMulti->HasTabulator() )
2276 0 : pTextCursor->pCurr->Width( static_cast<sal_uInt16>(nWidth + nSpaceAdd * nSpaceCnt / SPACING_PRECISION_FACTOR ) );
2277 :
2278 : // For a BidiPortion we have to calculate the offset from the
2279 : // end of the portion
2280 0 : if ( nX && pMulti->IsBidi() )
2281 0 : nX = pTextCursor->pCurr->Width() - nX;
2282 : }
2283 : else
2284 0 : bSpaceChg = false;
2285 0 : }
2286 :
2287 0 : SwTextCursorSave::~SwTextCursorSave()
2288 : {
2289 0 : if( bSpaceChg )
2290 0 : SwDoubleLinePortion::ResetSpaceAdd( pTextCrsr->pCurr );
2291 0 : pTextCrsr->pCurr->Width( nWidth );
2292 0 : pTextCrsr->pCurr = pCurr;
2293 0 : pTextCrsr->nStart = nStart;
2294 0 : pTextCrsr->SetPropFont( nOldProp );
2295 177 : }
2296 :
2297 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|