Update ooo320-m1
[ooovba.git] / sw / source / core / text / pormulti.cxx
blobea8b7562127f28633b0c7d58d8ff1d0ba19c7425
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: pormulti.cxx,v $
10 * $Revision: 1.89.112.2 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sw.hxx"
35 #include <hintids.hxx>
37 #include <com/sun/star/i18n/ScriptType.hdl>
38 #include <svx/twolinesitem.hxx>
39 #include <svx/charrotateitem.hxx>
40 #include <vcl/outdev.hxx>
41 #include <fmtfld.hxx>
42 #include <fldbas.hxx> // SwField
43 #include <txatbase.hxx>
44 #include <fmtruby.hxx> // SwFmtRuby
45 #include <txtatr.hxx> // SwTxtRuby
46 #include <charfmt.hxx>
47 #include <txtinet.hxx>
48 #include <fchrfmt.hxx>
49 #include <layfrm.hxx> // GetUpper()
50 #include <SwPortionHandler.hxx>
51 #include <pormulti.hxx> // SwMultiPortion
52 #include <inftxt.hxx> // SwTxtSizeInfo
53 #include <itrpaint.hxx> // SwTxtPainter
54 #include <viewopt.hxx> // SwViewOptions
55 #include <itrform2.hxx> // SwTxtFormatter
56 #include <porfld.hxx> // SwFldPortion
57 #include <porglue.hxx>
58 #include <breakit.hxx>
59 #include <pagefrm.hxx>
60 #include <rowfrm.hxx>
61 #include <pagedesc.hxx> // SwPageDesc
62 #include <tgrditem.hxx>
63 #include <swtable.hxx>
64 #include <fmtfsize.hxx>
66 using namespace ::com::sun::star;
67 extern sal_Bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt );
69 /*-----------------10.10.00 15:23-------------------
70 * class SwMultiPortion
72 * A SwMultiPortion is not a simple portion,
73 * it's a container, which contains almost a SwLineLayoutPortion.
74 * This SwLineLayout could be followed by other textportions via pPortion
75 * and by another SwLineLayout via pNext to realize a doubleline portion.
76 * --------------------------------------------------*/
78 SwMultiPortion::~SwMultiPortion()
80 delete pFldRest;
83 void SwMultiPortion::Paint( const SwTxtPaintInfo & ) const
85 ASSERT( FALSE,
86 "Don't try SwMultiPortion::Paint, try SwTxtPainter::PaintMultiPortion" );
89 /*-----------------13.10.00 16:21-------------------
90 * Summarize the internal lines to calculate the (external) size.
91 * The internal line has to calculate first.
92 * --------------------------------------------------*/
94 void SwMultiPortion::CalcSize( SwTxtFormatter& rLine, SwTxtFormatInfo &rInf )
96 Width( 0 );
97 Height( 0 );
98 SetAscent( 0 );
99 SetFlyInCntnt( sal_False );
100 SwLineLayout *pLay = &GetRoot();
103 pLay->CalcLine( rLine, rInf );
104 if( rLine.IsFlyInCntBase() )
105 SetFlyInCntnt( sal_True );
106 if( IsRuby() && ( OnTop() == ( pLay == &GetRoot() ) ) )
108 // An empty phonetic line don't need an ascent or a height.
109 if( !pLay->Width() )
111 pLay->SetAscent( 0 );
112 pLay->Height( 0 );
114 if( OnTop() )
115 SetAscent( GetAscent() + pLay->Height() );
117 else
118 SetAscent( GetAscent() + pLay->GetAscent() );
119 Height( Height() + pLay->Height() );
120 if( Width() < pLay->Width() )
121 Width( pLay->Width() );
122 pLay = pLay->GetNext();
123 } while ( pLay );
124 if( HasBrackets() )
126 KSHORT nTmp = ((SwDoubleLinePortion*)this)->GetBrackets()->nHeight;
127 if( nTmp > Height() )
129 KSHORT nAdd = ( nTmp - Height() ) / 2;
130 GetRoot().SetAscent( GetRoot().GetAscent() + nAdd );
131 GetRoot().Height( GetRoot().Height() + nAdd );
132 Height( nTmp );
134 nTmp = ((SwDoubleLinePortion*)this)->GetBrackets()->nAscent;
135 if( nTmp > GetAscent() )
136 SetAscent( nTmp );
140 long SwMultiPortion::CalcSpacing( long , const SwTxtSizeInfo & ) const
142 return 0;
145 sal_Bool SwMultiPortion::ChgSpaceAdd( SwLineLayout*, long ) const
147 return sal_False;
150 /*************************************************************************
151 * virtual SwMultiPortion::HandlePortion()
152 *************************************************************************/
154 void SwMultiPortion::HandlePortion( SwPortionHandler& rPH ) const
156 rPH.Text( GetLen(), GetWhichPor() );
159 /*-----------------01.11.00 14:21-------------------
160 * SwMultiPortion::ActualizeTabulator()
161 * sets the tabulator-flag, if there's any tabulator-portion inside.
162 * --------------------------------------------------*/
164 void SwMultiPortion::ActualizeTabulator()
166 SwLinePortion* pPor = GetRoot().GetFirstPortion();
167 // First line
168 for( bTab1 = bTab2 = sal_False; pPor; pPor = pPor->GetPortion() )
169 if( pPor->InTabGrp() )
170 SetTab1( sal_True );
171 if( GetRoot().GetNext() )
173 // Second line
174 pPor = GetRoot().GetNext()->GetFirstPortion();
177 if( pPor->InTabGrp() )
178 SetTab2( sal_True );
179 pPor = pPor->GetPortion();
180 } while ( pPor );
184 /*-----------------16.02.01 12:07-------------------
185 * SwRotatedPortion::SwRotatedPortion(..)
186 * --------------------------------------------------*/
188 SwRotatedPortion::SwRotatedPortion( const SwMultiCreator& rCreate,
189 xub_StrLen nEnd, sal_Bool bRTL ) : SwMultiPortion( nEnd )
191 const SvxCharRotateItem* pRot = (SvxCharRotateItem*)rCreate.pItem;
192 if( !pRot )
194 const SwTxtAttr& rAttr = *rCreate.pAttr;
195 const SfxPoolItem *const pItem =
196 CharFmt::GetItem(rAttr, RES_CHRATR_ROTATE);
197 if ( pItem )
199 pRot = static_cast<const SvxCharRotateItem*>(pItem);
202 if( pRot )
204 sal_uInt8 nDir;
205 if ( bRTL )
206 nDir = pRot->IsBottomToTop() ? 3 : 1;
207 else
208 nDir = pRot->IsBottomToTop() ? 1 : 3;
210 SetDirection( nDir );
214 /*---------------------------------------------------
215 * SwBidiPortion::SwBidiPortion(..)
216 * --------------------------------------------------*/
218 SwBidiPortion::SwBidiPortion( xub_StrLen nEnd, BYTE nLv )
219 : SwMultiPortion( nEnd ), nLevel( nLv )
221 SetBidi();
223 if ( nLevel % 2 )
224 SetDirection( DIR_RIGHT2LEFT );
225 else
226 SetDirection( DIR_LEFT2RIGHT );
230 long SwBidiPortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo& rInf ) const
232 return HasTabulator() ? 0 : GetSpaceCnt(rInf) * nSpaceAdd / SPACING_PRECISION_FACTOR;
235 sal_Bool SwBidiPortion::ChgSpaceAdd( SwLineLayout* pCurr, long nSpaceAdd ) const
237 sal_Bool bRet = sal_False;
238 if( !HasTabulator() && nSpaceAdd > 0 && !pCurr->IsSpaceAdd() )
240 pCurr->CreateSpaceAdd();
241 pCurr->SetLLSpaceAdd( nSpaceAdd, 0 );
242 bRet = sal_True;
245 return bRet;
248 xub_StrLen SwBidiPortion::GetSpaceCnt( const SwTxtSizeInfo &rInf ) const
250 // Calculate number of blanks for justified alignment
251 SwLinePortion* pPor = GetRoot().GetFirstPortion();
252 xub_StrLen nTmpStart = rInf.GetIdx();
253 xub_StrLen nNull = 0;
254 xub_StrLen nBlanks;
256 for( nBlanks = 0; pPor; pPor = pPor->GetPortion() )
258 if( pPor->InTxtGrp() )
259 nBlanks = nBlanks + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull );
260 else if ( pPor->IsMultiPortion() &&
261 ((SwMultiPortion*)pPor)->IsBidi() )
262 nBlanks = nBlanks + ((SwBidiPortion*)pPor)->GetSpaceCnt( rInf );
264 ((SwTxtSizeInfo &)rInf).SetIdx( rInf.GetIdx() + pPor->GetLen() );
266 ((SwTxtSizeInfo &)rInf).SetIdx( nTmpStart );
267 return nBlanks;
270 /*-----------------01.11.00 14:22-------------------
271 * SwDoubleLinePortion::SwDoubleLinePortion(..)
272 * This constructor is for the continuation of a doubleline portion
273 * in the next line.
274 * It takes the same brackets and if the original has no content except
275 * brackets, these will be deleted.
276 * --------------------------------------------------*/
278 SwDoubleLinePortion::SwDoubleLinePortion( SwDoubleLinePortion& rDouble,
279 xub_StrLen nEnd ) :
280 SwMultiPortion( nEnd ),
281 pBracket( 0 )
283 SetDirection( rDouble.GetDirection() );
284 SetDouble();
285 if( rDouble.GetBrackets() )
287 SetBrackets( rDouble );
288 // An empty multiportion needs no brackets.
289 // Notice: GetLen() might be zero, if the multiportion contains
290 // the second part of a field and the width might be zero, if
291 // it contains a note only. In this cases the brackets are okay.
292 // But if the length and the width are both zero, the portion
293 // is really empty.
294 if( rDouble.Width() == rDouble.BracketWidth() )
295 rDouble.ClearBrackets();
299 /*-----------------01.11.00 14:22-------------------
300 * SwDoubleLinePortion::SwDoubleLinePortion(..)
301 * This constructor uses the textattribut to get the right brackets.
302 * The textattribut could be a 2-line-attribute or a character- or
303 * internetstyle, which contains the 2-line-attribute.
304 * --------------------------------------------------*/
306 SwDoubleLinePortion::SwDoubleLinePortion( const SwMultiCreator& rCreate,
307 xub_StrLen nEnd ) : SwMultiPortion( nEnd ), pBracket( new SwBracket() )
309 SetDouble();
310 const SvxTwoLinesItem* pTwo = (SvxTwoLinesItem*)rCreate.pItem;
311 if( pTwo )
312 pBracket->nStart = 0;
313 else
315 const SwTxtAttr& rAttr = *rCreate.pAttr;
316 pBracket->nStart = *rAttr.GetStart();
318 const SfxPoolItem * const pItem =
319 CharFmt::GetItem( rAttr, RES_CHRATR_TWO_LINES );
320 if ( pItem )
322 pTwo = static_cast<const SvxTwoLinesItem*>(pItem);
325 if( pTwo )
327 pBracket->cPre = pTwo->GetStartBracket();
328 pBracket->cPost = pTwo->GetEndBracket();
330 else
332 pBracket->cPre = 0;
333 pBracket->cPost = 0;
335 BYTE nTmp = SW_SCRIPTS;
336 if( pBracket->cPre > 255 )
338 String aTxt( pBracket->cPre );
339 nTmp = SwScriptInfo::WhichFont( 0, &aTxt, 0 );
341 pBracket->nPreScript = nTmp;
342 nTmp = SW_SCRIPTS;
343 if( pBracket->cPost > 255 )
345 String aTxt( pBracket->cPost );
346 nTmp = SwScriptInfo::WhichFont( 0, &aTxt, 0 );
348 pBracket->nPostScript = nTmp;
350 if( !pBracket->cPre && !pBracket->cPost )
352 delete pBracket;
353 pBracket = 0;
356 // double line portions have the same direction as the frame directions
357 if ( rCreate.nLevel % 2 )
358 SetDirection( DIR_RIGHT2LEFT );
359 else
360 SetDirection( DIR_LEFT2RIGHT );
364 /*-----------------25.10.00 09:51-------------------
365 * SwMultiPortion::PaintBracket paints the wished bracket,
366 * if the multiportion has surrounding brackets.
367 * The X-position of the SwTxtPaintInfo will be modified:
368 * the open bracket sets position behind itself,
369 * the close bracket in front of itself.
370 * --------------------------------------------------*/
372 void SwDoubleLinePortion::PaintBracket( SwTxtPaintInfo &rInf,
373 long nSpaceAdd,
374 sal_Bool bOpen ) const
376 sal_Unicode cCh = bOpen ? pBracket->cPre : pBracket->cPost;
377 if( !cCh )
378 return;
379 KSHORT nChWidth = bOpen ? PreWidth() : PostWidth();
380 if( !nChWidth )
381 return;
382 if( !bOpen )
383 rInf.X( rInf.X() + Width() - PostWidth() +
384 ( nSpaceAdd > 0 ? CalcSpacing( nSpaceAdd, rInf ) : 0 ) );
386 SwBlankPortion aBlank( cCh, sal_True );
387 aBlank.SetAscent( pBracket->nAscent );
388 aBlank.Width( nChWidth );
389 aBlank.Height( pBracket->nHeight );
391 SwFont* pTmpFnt = new SwFont( *rInf.GetFont() );
392 BYTE nAct = bOpen ? pBracket->nPreScript : pBracket->nPostScript;
393 if( SW_SCRIPTS > nAct )
394 pTmpFnt->SetActual( nAct );
395 pTmpFnt->SetProportion( 100 );
396 SwFontSave aSave( rInf, pTmpFnt );
397 aBlank.Paint( rInf );
398 delete pTmpFnt;
400 if( bOpen )
401 rInf.X( rInf.X() + PreWidth() );
404 /*-----------------25.10.00 16:26-------------------
405 * SwDoubleLinePortion::SetBrackets creates the bracket-structur
406 * and fills it, if not both characters are 0x00.
407 * --------------------------------------------------*/
409 void SwDoubleLinePortion::SetBrackets( const SwDoubleLinePortion& rDouble )
411 if( rDouble.pBracket )
413 pBracket = new SwBracket;
414 pBracket->cPre = rDouble.pBracket->cPre;
415 pBracket->cPost = rDouble.pBracket->cPost;
416 pBracket->nPreScript = rDouble.pBracket->nPreScript;
417 pBracket->nPostScript = rDouble.pBracket->nPostScript;
418 pBracket->nStart = rDouble.pBracket->nStart;
422 /*-----------------25.10.00 16:29-------------------
423 * SwDoubleLinePortion::FormatBrackets
424 * calculates the size of the brackets => pBracket,
425 * reduces the nMaxWidth-parameter ( minus bracket-width )
426 * and moves the rInf-x-position behind the opening bracket.
427 * --------------------------------------------------*/
429 void SwDoubleLinePortion::FormatBrackets( SwTxtFormatInfo &rInf, SwTwips& nMaxWidth )
431 nMaxWidth -= rInf.X();
432 SwFont* pTmpFnt = new SwFont( *rInf.GetFont() );
433 pTmpFnt->SetProportion( 100 );
434 pBracket->nAscent = 0;
435 pBracket->nHeight = 0;
436 if( pBracket->cPre )
438 String aStr( pBracket->cPre );
439 BYTE nActualScr = pTmpFnt->GetActual();
440 if( SW_SCRIPTS > pBracket->nPreScript )
441 pTmpFnt->SetActual( pBracket->nPreScript );
442 SwFontSave aSave( rInf, pTmpFnt );
443 SwPosSize aSize = rInf.GetTxtSize( aStr );
444 pBracket->nAscent = rInf.GetAscent();
445 pBracket->nHeight = aSize.Height();
446 pTmpFnt->SetActual( nActualScr );
447 if( nMaxWidth > aSize.Width() )
449 pBracket->nPreWidth = aSize.Width();
450 nMaxWidth -= aSize.Width();
451 rInf.X( rInf.X() + aSize.Width() );
453 else
455 pBracket->nPreWidth = 0;
456 nMaxWidth = 0;
459 else
460 pBracket->nPreWidth = 0;
461 if( pBracket->cPost )
463 String aStr( pBracket->cPost );
464 if( SW_SCRIPTS > pBracket->nPostScript )
465 pTmpFnt->SetActual( pBracket->nPostScript );
466 SwFontSave aSave( rInf, pTmpFnt );
467 SwPosSize aSize = rInf.GetTxtSize( aStr );
468 KSHORT nTmpAsc = rInf.GetAscent();
469 if( nTmpAsc > pBracket->nAscent )
471 pBracket->nHeight += nTmpAsc - pBracket->nAscent;
472 pBracket->nAscent = nTmpAsc;
474 if( aSize.Height() > pBracket->nHeight )
475 pBracket->nHeight = aSize.Height();
476 if( nMaxWidth > aSize.Width() )
478 pBracket->nPostWidth = aSize.Width();
479 nMaxWidth -= aSize.Width();
481 else
483 pBracket->nPostWidth = 0;
484 nMaxWidth = 0;
487 else
488 pBracket->nPostWidth = 0;
489 nMaxWidth += rInf.X();
492 /*-----------------26.10.00 10:36-------------------
493 * SwDoubleLinePortion::CalcBlanks
494 * calculates the number of blanks in each line and
495 * the difference of the width of the two lines.
496 * These results are used from the text adjustment.
497 * --------------------------------------------------*/
499 void SwDoubleLinePortion::CalcBlanks( SwTxtFormatInfo &rInf )
501 SwLinePortion* pPor = GetRoot().GetFirstPortion();
502 xub_StrLen nNull = 0;
503 xub_StrLen nStart = rInf.GetIdx();
504 SetTab1( sal_False );
505 SetTab2( sal_False );
506 for( nBlank1 = 0; pPor; pPor = pPor->GetPortion() )
508 if( pPor->InTxtGrp() )
509 nBlank1 = nBlank1 + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull );
510 rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
511 if( pPor->InTabGrp() )
512 SetTab1( sal_True );
514 nLineDiff = GetRoot().Width();
515 if( GetRoot().GetNext() )
517 pPor = GetRoot().GetNext()->GetFirstPortion();
518 nLineDiff -= GetRoot().GetNext()->Width();
520 for( nBlank2 = 0; pPor; pPor = pPor->GetPortion() )
522 if( pPor->InTxtGrp() )
523 nBlank2 = nBlank2 + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull );
524 rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
525 if( pPor->InTabGrp() )
526 SetTab2( sal_True );
528 rInf.SetIdx( nStart );
531 long SwDoubleLinePortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo & ) const
533 return HasTabulator() ? 0 : GetSpaceCnt() * nSpaceAdd / SPACING_PRECISION_FACTOR;
536 /*-----------------01.11.00 14:29-------------------
537 * SwDoubleLinePortion::ChangeSpaceAdd(..)
538 * merges the spaces for text adjustment from the inner and outer part.
539 * Inside the doubleline portion the wider line has no spaceadd-array, the
540 * smaller line has such an array to reach width of the wider line.
541 * If the surrounding line has text adjustment and the doubleline portion
542 * contains no tabulator, it is necessary to create/manipulate the inner
543 * space arrays.
544 * --------------------------------------------------*/
546 sal_Bool SwDoubleLinePortion::ChgSpaceAdd( SwLineLayout* pCurr,
547 long nSpaceAdd ) const
549 sal_Bool bRet = sal_False;
550 if( !HasTabulator() && nSpaceAdd > 0 )
552 if( !pCurr->IsSpaceAdd() )
554 // The wider line gets the spaceadd from the surrounding line direct
555 pCurr->CreateSpaceAdd();
556 pCurr->SetLLSpaceAdd( nSpaceAdd, 0 );
557 bRet = sal_True;
559 else
561 xub_StrLen nMyBlank = GetSmallerSpaceCnt();
562 xub_StrLen nOther = GetSpaceCnt();
563 SwTwips nMultiSpace = pCurr->GetLLSpaceAdd( 0 ) * nMyBlank + nOther * nSpaceAdd;
565 if( nMyBlank )
566 nMultiSpace /= nMyBlank;
568 if( nMultiSpace < KSHRT_MAX * SPACING_PRECISION_FACTOR )
570 // pCurr->SetLLSpaceAdd( nMultiSpace, 0 );
571 // --> FME 2006-07-11 #i65711# SetLLSpaceAdd replaces the first value,
572 // instead we want to insert a new first value:
573 std::vector<long>* pVec = pCurr->GetpLLSpaceAdd();
574 pVec->insert( pVec->begin(), nMultiSpace );
575 // <--
576 bRet = sal_True;
580 return bRet;
582 /*-----------------01.11.00 14:29-------------------
583 * SwDoubleLinePortion::ResetSpaceAdd(..)
584 * cancels the manipulation from SwDoubleLinePortion::ChangeSpaceAdd(..)
585 * --------------------------------------------------*/
587 void SwDoubleLinePortion::ResetSpaceAdd( SwLineLayout* pCurr )
589 pCurr->RemoveFirstLLSpaceAdd();;
590 if( !pCurr->GetLLSpaceAddCount() )
591 pCurr->FinishSpaceAdd();
594 SwDoubleLinePortion::~SwDoubleLinePortion()
596 delete pBracket;
599 /*-----------------13.11.00 14:50-------------------
600 * SwRubyPortion::SwRubyPortion(..)
601 * constructs a ruby portion, i.e. an additional text is displayed
602 * beside the main text, e.g. phonetic characters.
603 * --------------------------------------------------*/
606 SwRubyPortion::SwRubyPortion( const SwRubyPortion& rRuby, xub_StrLen nEnd ) :
607 SwMultiPortion( nEnd ),
608 nRubyOffset( rRuby.GetRubyOffset() ),
609 nAdjustment( rRuby.GetAdjustment() )
611 SetDirection( rRuby.GetDirection() ),
612 SetTop( rRuby.OnTop() );
613 SetRuby();
616 /*-----------------13.11.00 14:50-------------------
617 * SwRubyPortion::SwRubyPortion(..)
618 * constructs a ruby portion, i.e. an additional text is displayed
619 * beside the main text, e.g. phonetic characters.
620 * --------------------------------------------------*/
622 SwRubyPortion::SwRubyPortion( const SwMultiCreator& rCreate, const SwFont& rFnt,
623 const IDocumentSettingAccess& rIDocumentSettingAccess,
624 xub_StrLen nEnd, xub_StrLen nOffs,
625 const sal_Bool* pForceRubyPos )
626 : SwMultiPortion( nEnd )
628 SetRuby();
629 ASSERT( SW_MC_RUBY == rCreate.nId, "Ruby expected" );
630 ASSERT( RES_TXTATR_CJK_RUBY == rCreate.pAttr->Which(), "Wrong attribute" );
631 const SwFmtRuby& rRuby = rCreate.pAttr->GetRuby();
632 nAdjustment = rRuby.GetAdjustment();
633 nRubyOffset = nOffs;
635 // in grid mode we force the ruby text to the upper or lower line
636 if ( pForceRubyPos )
637 SetTop( *pForceRubyPos );
638 else
639 SetTop( ! rRuby.GetPosition() );
641 const SwCharFmt* pFmt = ((SwTxtRuby*)rCreate.pAttr)->GetCharFmt();
642 SwFont *pRubyFont;
643 if( pFmt )
645 const SwAttrSet& rSet = pFmt->GetAttrSet();
646 pRubyFont = new SwFont( rFnt );
647 pRubyFont->SetDiffFnt( &rSet, &rIDocumentSettingAccess );
649 // we do not allow a vertical font for the ruby text
650 pRubyFont->SetVertical( rFnt.GetOrientation() );
652 else
653 pRubyFont = NULL;
655 String aStr( rRuby.GetText(), nOffs, STRING_LEN );
656 SwFldPortion *pFld = new SwFldPortion( aStr, pRubyFont );
657 pFld->SetNextOffset( nOffs );
658 pFld->SetFollow( sal_True );
660 if( OnTop() )
661 GetRoot().SetPortion( pFld );
662 else
664 GetRoot().SetNext( new SwLineLayout() );
665 GetRoot().GetNext()->SetPortion( pFld );
668 // ruby portions have the same direction as the frame directions
669 if ( rCreate.nLevel % 2 )
671 // switch right and left ruby adjustment in rtl environment
672 if ( 0 == nAdjustment )
673 nAdjustment = 2;
674 else if ( 2 == nAdjustment )
675 nAdjustment = 0;
677 SetDirection( DIR_RIGHT2LEFT );
679 else
680 SetDirection( DIR_LEFT2RIGHT );
683 /*-----------------13.11.00 14:56-------------------
684 * SwRubyPortion::_Adjust(..)
685 * In ruby portion there are different alignments for
686 * the ruby text and the main text.
687 * Left, right, centered and two possibilities of block adjustment
688 * The block adjustment is realized by spacing between the characteres,
689 * either with a half space or no space in front of the first letter and
690 * a half space at the end of the last letter.
691 * Notice: the smaller line will be manipulated, normally it's the ruby line,
692 * but it could be the main text, too.
693 * If there is a tabulator in smaller line, no adjustment is possible.
694 * --------------------------------------------------*/
696 void SwRubyPortion::_Adjust( SwTxtFormatInfo &rInf )
698 SwTwips nLineDiff = GetRoot().Width() - GetRoot().GetNext()->Width();
699 xub_StrLen nOldIdx = rInf.GetIdx();
700 if( !nLineDiff )
701 return;
702 SwLineLayout *pCurr;
703 if( nLineDiff < 0 )
704 { // The first line has to be adjusted.
705 if( GetTab1() )
706 return;
707 pCurr = &GetRoot();
708 nLineDiff = -nLineDiff;
710 else
711 { // The second line has to be adjusted.
712 if( GetTab2() )
713 return;
714 pCurr = GetRoot().GetNext();
715 rInf.SetIdx( nOldIdx + GetRoot().GetLen() );
717 KSHORT nLeft = 0; // the space in front of the first letter
718 KSHORT nRight = 0; // the space at the end of the last letter
719 USHORT nSub = 0;
720 switch ( nAdjustment )
722 case 1: nRight = static_cast<USHORT>(nLineDiff / 2); // no break
723 case 2: nLeft = static_cast<USHORT>(nLineDiff - nRight); break;
724 case 3: nSub = 1; // no break
725 case 4:
727 xub_StrLen nCharCnt = 0;
728 SwLinePortion *pPor;
729 for( pPor = pCurr->GetFirstPortion(); pPor; pPor = pPor->GetPortion() )
731 if( pPor->InTxtGrp() )
732 ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nCharCnt );
733 rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
735 if( nCharCnt > nSub )
737 SwTwips nCalc = nLineDiff / ( nCharCnt - nSub );
738 short nTmp;
739 if( nCalc < SHRT_MAX )
740 nTmp = -short(nCalc);
741 else
742 nTmp = SHRT_MIN;
744 pCurr->CreateSpaceAdd( SPACING_PRECISION_FACTOR * nTmp );
745 nLineDiff -= nCalc * ( nCharCnt - 1 );
747 if( nLineDiff > 1 )
749 nRight = static_cast<USHORT>(nLineDiff / 2);
750 nLeft = static_cast<USHORT>(nLineDiff - nRight);
752 break;
754 default: ASSERT( sal_False, "New ruby adjustment" );
756 if( nLeft || nRight )
758 if( !pCurr->GetPortion() )
759 pCurr->SetPortion( new SwTxtPortion( *pCurr ) );
760 SwMarginPortion *pMarg = new SwMarginPortion( 0 );
761 if( nLeft )
763 pMarg->AddPrtWidth( nLeft );
764 pMarg->SetPortion( pCurr->GetPortion() );
765 pCurr->SetPortion( pMarg );
767 if( nRight )
769 pMarg = new SwMarginPortion( 0 );
770 pMarg->AddPrtWidth( nRight );
771 pCurr->FindLastPortion()->Append( pMarg );
775 pCurr->Width( Width() );
776 rInf.SetIdx( nOldIdx );
779 /*-----------------08.11.00 14:14-------------------
780 * CalcRubyOffset()
781 * has to change the nRubyOffset, if there's a fieldportion
782 * in the phonetic line.
783 * The nRubyOffset is the position in the rubystring, where the
784 * next SwRubyPortion has start the displaying of the phonetics.
785 * --------------------------------------------------*/
787 void SwRubyPortion::CalcRubyOffset()
789 const SwLineLayout *pCurr = &GetRoot();
790 if( !OnTop() )
792 pCurr = pCurr->GetNext();
793 if( !pCurr )
794 return;
796 const SwLinePortion *pPor = pCurr->GetFirstPortion();
797 const SwFldPortion *pFld = NULL;
798 while( pPor )
800 if( pPor->InFldGrp() )
801 pFld = (SwFldPortion*)pPor;
802 pPor = pPor->GetPortion();
804 if( pFld )
806 if( pFld->HasFollow() )
807 nRubyOffset = pFld->GetNextOffset();
808 else
809 nRubyOffset = STRING_LEN;
813 /*-----------------13.10.00 16:22-------------------
814 * SwTxtSizeInfo::GetMultiCreator(..)
815 * If we (e.g. the position rPos) are inside a two-line-attribute or
816 * a ruby-attribute, the attribute will be returned in a SwMultiCreator-struct,
817 * otherwise the function returns zero.
818 * The rPos parameter is set to the end of the multiportion,
819 * normally this is the end of the attribute,
820 * but sometimes it is the start of another attribute, which finished or
821 * interrupts the first attribute.
822 * E.g. a ruby portion interrupts a 2-line-attribute, a 2-line-attribute
823 * with different brackets interrupts another 2-line-attribute.
824 * --------------------------------------------------*/
826 /*-----------------13.11.00 15:38-------------------
827 * lcl_Has2Lines(..)
828 * is a little help function for GetMultiCreator(..)
829 * It extracts the 2-line-format from a 2-line-attribute or a character style.
830 * The rValue is set to TRUE, if the 2-line-attribute's value is set and
831 * no 2-line-format reference is passed. If there is a 2-line-format reference,
832 * then the rValue is set only, if the 2-line-attribute's value is set _and_
833 * the 2-line-formats has the same brackets.
834 * --------------------------------------------------*/
836 sal_Bool lcl_Has2Lines( const SwTxtAttr& rAttr, const SvxTwoLinesItem* &rpRef,
837 sal_Bool &rValue )
839 const SfxPoolItem* pItem = CharFmt::GetItem( rAttr, RES_CHRATR_TWO_LINES );
840 if( pItem )
842 rValue = ((SvxTwoLinesItem*)pItem)->GetValue();
843 if( !rpRef )
844 rpRef = (SvxTwoLinesItem*)pItem;
845 else if( ((SvxTwoLinesItem*)pItem)->GetEndBracket() !=
846 rpRef->GetEndBracket() ||
847 ((SvxTwoLinesItem*)pItem)->GetStartBracket() !=
848 rpRef->GetStartBracket() )
849 rValue = sal_False;
850 return sal_True;
852 return sal_False;
855 /*-----------------16.02.01 16:39-------------------
856 * lcl_HasRotation(..)
857 * is a little help function for GetMultiCreator(..)
858 * It extracts the charrotation from a charrotate-attribute or a character style.
859 * The rValue is set to TRUE, if the charrotate-attribute's value is set and
860 * no charrotate-format reference is passed.
861 * If there is a charrotate-format reference, then the rValue is set only,
862 * if the charrotate-attribute's value is set _and_ identical
863 * to the charrotate-format's value.
864 * --------------------------------------------------*/
866 sal_Bool lcl_HasRotation( const SwTxtAttr& rAttr,
867 const SvxCharRotateItem* &rpRef, sal_Bool &rValue )
869 const SfxPoolItem* pItem = CharFmt::GetItem( rAttr, RES_CHRATR_ROTATE );
870 if ( pItem )
872 rValue = 0 != ((SvxCharRotateItem*)pItem)->GetValue();
873 if( !rpRef )
874 rpRef = (SvxCharRotateItem*)pItem;
875 else if( ((SvxCharRotateItem*)pItem)->GetValue() !=
876 rpRef->GetValue() )
877 rValue = sal_False;
878 return sal_True;
881 return sal_False;
884 SwMultiCreator* SwTxtSizeInfo::GetMultiCreator( xub_StrLen &rPos,
885 SwMultiPortion* pMulti ) const
887 SwScriptInfo& rSI = ((SwParaPortion*)GetParaPortion())->GetScriptInfo();
889 // get the last embedding level
890 BYTE nCurrLevel;
891 if ( pMulti )
893 ASSERT( pMulti->IsBidi(), "Nested MultiPortion is not BidiPortion" )
894 // level associated with bidi-portion;
895 nCurrLevel = ((SwBidiPortion*)pMulti)->GetLevel();
897 else
898 // no nested bidi portion required
899 nCurrLevel = GetTxtFrm()->IsRightToLeft() ? 1 : 0;
901 // check if there is a field at rPos:
902 BYTE nNextLevel = nCurrLevel;
903 sal_Bool bFldBidi = sal_False;
905 if ( CH_TXTATR_BREAKWORD == GetChar( rPos ) )
907 bFldBidi = sal_True;
909 // examining the script of the field text should be sufficient
910 // for 99% of all cases
911 XubString aTxt = GetTxtFrm()->GetTxtNode()->GetExpandTxt( rPos, 1 );
913 if ( pBreakIt->GetBreakIter().is() && aTxt.Len() )
915 sal_Bool bFldDir = ( i18n::ScriptType::COMPLEX ==
916 pBreakIt->GetRealScriptOfText( aTxt, 0 ) );
917 sal_Bool bCurrDir = ( 0 != ( nCurrLevel % 2 ) );
918 if ( bFldDir != bCurrDir )
920 nNextLevel = nCurrLevel + 1;
921 bFldBidi = sal_True;
925 else
926 nNextLevel = rSI.DirType( rPos );
928 if ( GetTxt().Len() != rPos && nNextLevel > nCurrLevel )
930 rPos = bFldBidi ? rPos + 1 : rSI.NextDirChg( rPos, &nCurrLevel );
931 if ( STRING_LEN == rPos )
932 return NULL;
933 SwMultiCreator *pRet = new SwMultiCreator;
934 pRet->pItem = NULL;
935 pRet->pAttr = NULL;
936 pRet->nId = SW_MC_BIDI;
937 pRet->nLevel = nCurrLevel + 1;
938 return pRet;
941 // a bidi portion can only contain other bidi portions
942 if ( pMulti )
943 return NULL;
945 const SvxCharRotateItem* pRotate = NULL;
946 const SfxPoolItem* pRotItem;
947 if( SFX_ITEM_SET == pFrm->GetTxtNode()->GetSwAttrSet().
948 GetItemState( RES_CHRATR_ROTATE, TRUE, &pRotItem ) &&
949 ((SvxCharRotateItem*)pRotItem)->GetValue() )
950 pRotate = (SvxCharRotateItem*)pRotItem;
951 else
952 pRotItem = NULL;
953 const SvxTwoLinesItem* p2Lines = NULL;
954 const SfxPoolItem* pItem;
955 if( SFX_ITEM_SET == pFrm->GetTxtNode()->GetSwAttrSet().
956 GetItemState( RES_CHRATR_TWO_LINES, TRUE, &pItem ) &&
957 ((SvxTwoLinesItem*)pItem)->GetValue() )
958 p2Lines = (SvxTwoLinesItem*)pItem;
959 else
960 pItem = NULL;
962 const SwpHints *pHints = pFrm->GetTxtNode()->GetpSwpHints();
963 if( !pHints && !p2Lines && !pRotate )
964 return NULL;
965 const SwTxtAttr *pRuby = NULL;
966 sal_Bool bTwo = sal_False;
967 sal_Bool bRot = sal_False;
968 USHORT n2Lines = USHRT_MAX;
969 USHORT nRotate = USHRT_MAX;
970 USHORT nCount = pHints ? pHints->Count() : 0;
971 USHORT i;
972 for( i = 0; i < nCount; ++i )
974 const SwTxtAttr *pTmp = (*pHints)[i];
975 xub_StrLen nStart = *pTmp->GetStart();
976 if( rPos < nStart )
977 break;
978 if( *pTmp->GetAnyEnd() > rPos )
980 if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
981 pRuby = pTmp;
982 else
984 const SvxCharRotateItem* pRoTmp = NULL;
985 if( lcl_HasRotation( *pTmp, pRoTmp, bRot ) )
987 nRotate = bRot ? i : nCount;
988 pRotate = pRoTmp;
990 const SvxTwoLinesItem* p2Tmp = NULL;
991 if( lcl_Has2Lines( *pTmp, p2Tmp, bTwo ) )
993 n2Lines = bTwo ? i : nCount;
994 p2Lines = p2Tmp;
999 if( pRuby )
1000 { // The winner is ... a ruby attribute and so
1001 // the end of the multiportion is the end of the ruby attribute.
1002 rPos = *pRuby->GetEnd();
1003 SwMultiCreator *pRet = new SwMultiCreator;
1004 pRet->pItem = NULL;
1005 pRet->pAttr = pRuby;
1006 pRet->nId = SW_MC_RUBY;
1007 pRet->nLevel = GetTxtFrm()->IsRightToLeft() ? 1 : 0;
1008 return pRet;
1010 if( n2Lines < nCount || ( pItem && pItem == p2Lines &&
1011 rPos < GetTxt().Len() ) )
1012 { // The winner is a 2-line-attribute,
1013 // the end of the multiportion depends on the following attributes...
1014 SwMultiCreator *pRet = new SwMultiCreator;
1016 // We note the endpositions of the 2-line attributes in aEnd as stack
1017 SvXub_StrLens aEnd;
1019 // The bOn flag signs the state of the last 2-line attribute in the
1020 // aEnd-stack, it is compatible with the winner-attribute or
1021 // it interrupts the other attribute.
1022 sal_Bool bOn = sal_True;
1024 if( n2Lines < nCount )
1026 pRet->pItem = NULL;
1027 pRet->pAttr = (*pHints)[n2Lines];
1028 aEnd.Insert( *pRet->pAttr->GetEnd(), 0 );
1029 if( pItem )
1031 aEnd[ 0 ] = GetTxt().Len();
1032 bOn = ((SvxTwoLinesItem*)pItem)->GetEndBracket() ==
1033 p2Lines->GetEndBracket() &&
1034 ((SvxTwoLinesItem*)pItem)->GetStartBracket() ==
1035 p2Lines->GetStartBracket();
1038 else
1040 pRet->pItem = pItem;
1041 pRet->pAttr = NULL;
1042 aEnd.Insert( GetTxt().Len(), 0 );
1044 pRet->nId = SW_MC_DOUBLE;
1045 pRet->nLevel = GetTxtFrm()->IsRightToLeft() ? 1 : 0;
1047 // n2Lines is the index of the last 2-line-attribute, which contains
1048 // the actual position.
1049 i = 0;
1050 // At this moment we know that at position rPos the "winner"-attribute
1051 // causes a 2-line-portion. The end of the attribute is the end of the
1052 // portion, if there's no interrupting attribute.
1053 // There are two kinds of interruptors:
1054 // - ruby attributes stops the 2-line-attribute, the end of the
1055 // multiline is the start of the ruby attribute
1056 // - 2-line-attributes with value "Off" or with different brackets,
1057 // these attributes may interrupt the winner, but they could be
1058 // neutralized by another 2-line-attribute starting at the same
1059 // position with the same brackets as the winner-attribute.
1061 // In the following loop rPos is the critical position and it will be
1062 // evaluated, if at rPos starts a interrupting or a maintaining
1063 // continuity attribute.
1064 while( i < nCount )
1066 const SwTxtAttr *pTmp = (*pHints)[i++];
1067 if( *pTmp->GetAnyEnd() <= rPos )
1068 continue;
1069 if( rPos < *pTmp->GetStart() )
1071 // If bOn is FALSE and the next attribute starts later than rPos
1072 // the winner attribute is interrupted at rPos.
1073 // If the start of the next atribute is behind the end of
1074 // the last attribute on the aEnd-stack, this is the endposition
1075 // on the stack is the end of the 2-line portion.
1076 if( !bOn || aEnd[ aEnd.Count()-1 ] < *pTmp->GetStart() )
1077 break;
1078 // At this moment, bOn is TRUE and the next attribute starts
1079 // behind rPos, so we could move rPos to the next startpoint
1080 rPos = *pTmp->GetStart();
1081 // We clean up the aEnd-stack, endpositions equal to rPos are
1082 // superfluous.
1083 while( aEnd.Count() && aEnd[ aEnd.Count()-1 ] <= rPos )
1085 bOn = !bOn;
1086 aEnd.Remove( aEnd.Count()-1, 1 );
1088 // If the endstack is empty, we simulate an attribute with
1089 // state TRUE and endposition rPos
1090 if( !aEnd.Count() )
1092 aEnd.Insert( rPos, 0 );
1093 bOn = sal_True;
1096 // A ruby attribute stops the 2-line immediately
1097 if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
1098 return pRet;
1099 if( lcl_Has2Lines( *pTmp, p2Lines, bTwo ) )
1100 { // We have an interesting attribute..
1101 if( bTwo == bOn )
1102 { // .. with the same state, so the last attribute could
1103 // be continued.
1104 if( aEnd[ aEnd.Count()-1 ] < *pTmp->GetEnd() )
1105 aEnd[ aEnd.Count()-1 ] = *pTmp->GetEnd();
1107 else
1108 { // .. with a different state.
1109 bOn = bTwo;
1110 // If this is smaller than the last on the stack, we put
1111 // it on the stack. If it has the same endposition, the last
1112 // could be removed.
1113 if( aEnd[ aEnd.Count()-1 ] > *pTmp->GetEnd() )
1114 aEnd.Insert( *pTmp->GetEnd(), aEnd.Count() );
1115 else if( aEnd.Count() > 1 )
1116 aEnd.Remove( aEnd.Count()-1, 1 );
1117 else
1118 aEnd[ aEnd.Count()-1 ] = *pTmp->GetEnd();
1122 if( bOn && aEnd.Count() )
1123 rPos = aEnd[ aEnd.Count()-1 ];
1124 return pRet;
1126 if( nRotate < nCount || ( pRotItem && pRotItem == pRotate &&
1127 rPos < GetTxt().Len() ) )
1128 { // The winner is a rotate-attribute,
1129 // the end of the multiportion depends on the following attributes...
1130 SwMultiCreator *pRet = new SwMultiCreator;
1131 pRet->nId = SW_MC_ROTATE;
1133 // We note the endpositions of the 2-line attributes in aEnd as stack
1134 SvXub_StrLens aEnd;
1136 // The bOn flag signs the state of the last 2-line attribute in the
1137 // aEnd-stack, which could interrupts the winning rotation attribute.
1138 sal_Bool bOn = pItem ? sal_True : sal_False;
1139 aEnd.Insert( GetTxt().Len(), 0 );
1140 // n2Lines is the index of the last 2-line-attribute, which contains
1141 // the actual position.
1142 i = 0;
1143 xub_StrLen n2Start = rPos;
1144 while( i < nCount )
1146 const SwTxtAttr *pTmp = (*pHints)[i++];
1147 if( *pTmp->GetAnyEnd() <= n2Start )
1148 continue;
1149 if( n2Start < *pTmp->GetStart() )
1151 if( bOn || aEnd[ aEnd.Count()-1 ] < *pTmp->GetStart() )
1152 break;
1153 n2Start = *pTmp->GetStart();
1154 while( aEnd.Count() && aEnd[ aEnd.Count()-1 ] <= n2Start )
1156 bOn = !bOn;
1157 aEnd.Remove( aEnd.Count()-1, 1 );
1159 if( !aEnd.Count() )
1161 aEnd.Insert( n2Start, 0 );
1162 bOn = sal_False;
1165 // A ruby attribute stops immediately
1166 if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
1168 bOn = sal_True;
1169 break;
1171 p2Lines = NULL;
1172 if( lcl_Has2Lines( *pTmp, p2Lines, bTwo ) )
1174 if( bTwo == bOn )
1176 if( aEnd[ aEnd.Count()-1 ] < *pTmp->GetEnd() )
1177 aEnd[ aEnd.Count()-1 ] = *pTmp->GetEnd();
1179 else
1181 bOn = bTwo;
1182 if( aEnd[ aEnd.Count()-1 ] > *pTmp->GetEnd() )
1183 aEnd.Insert( *pTmp->GetEnd(), aEnd.Count() );
1184 else if( aEnd.Count() > 1 )
1185 aEnd.Remove( aEnd.Count()-1, 1 );
1186 else
1187 aEnd[ aEnd.Count()-1 ] = *pTmp->GetEnd();
1191 if( !bOn && aEnd.Count() )
1192 n2Start = aEnd[ aEnd.Count()-1 ];
1194 if( aEnd.Count() )
1195 aEnd.Remove( 0, aEnd.Count() );
1197 bOn = sal_True;
1198 if( nRotate < nCount )
1200 pRet->pItem = NULL;
1201 pRet->pAttr = (*pHints)[nRotate];
1202 aEnd.Insert( *pRet->pAttr->GetEnd(), 0 );
1203 if( pRotItem )
1205 aEnd[ 0 ] = GetTxt().Len();
1206 bOn = ((SvxCharRotateItem*)pRotItem)->GetValue() ==
1207 pRotate->GetValue();
1210 else
1212 pRet->pItem = pRotItem;
1213 pRet->pAttr = NULL;
1214 aEnd.Insert( GetTxt().Len(), 0 );
1216 i = 0;
1217 while( i < nCount )
1219 const SwTxtAttr *pTmp = (*pHints)[i++];
1220 if( *pTmp->GetAnyEnd() <= rPos )
1221 continue;
1222 if( rPos < *pTmp->GetStart() )
1224 if( !bOn || aEnd[ aEnd.Count()-1 ] < *pTmp->GetStart() )
1225 break;
1226 rPos = *pTmp->GetStart();
1227 while( aEnd.Count() && aEnd[ aEnd.Count()-1 ] <= rPos )
1229 bOn = !bOn;
1230 aEnd.Remove( aEnd.Count()-1, 1 );
1232 if( !aEnd.Count() )
1234 aEnd.Insert( rPos, 0 );
1235 bOn = sal_True;
1238 if( RES_TXTATR_CJK_RUBY == pTmp->Which() )
1240 bOn = sal_False;
1241 break;
1243 if( lcl_HasRotation( *pTmp, pRotate, bTwo ) )
1245 if( bTwo == bOn )
1247 if( aEnd[ aEnd.Count()-1 ] < *pTmp->GetEnd() )
1248 aEnd[ aEnd.Count()-1 ] = *pTmp->GetEnd();
1250 else
1252 bOn = bTwo;
1253 if( aEnd[ aEnd.Count()-1 ] > *pTmp->GetEnd() )
1254 aEnd.Insert( *pTmp->GetEnd(), aEnd.Count() );
1255 else if( aEnd.Count() > 1 )
1256 aEnd.Remove( aEnd.Count()-1, 1 );
1257 else
1258 aEnd[ aEnd.Count()-1 ] = *pTmp->GetEnd();
1262 if( bOn && aEnd.Count() )
1263 rPos = aEnd[ aEnd.Count()-1 ];
1264 if( rPos > n2Start )
1265 rPos = n2Start;
1266 return pRet;
1268 return NULL;
1271 /*-----------------01.11.00 14:52-------------------
1272 * SwSpaceManipulator
1273 * is a little helper class to manage the spaceadd-arrays of the text adjustment
1274 * during a PaintMultiPortion.
1275 * The constructor prepares the array for the first line of multiportion,
1276 * the SecondLine-function restores the values for the first line and prepares
1277 * the second line.
1278 * The destructor restores the values of the last manipulation.
1279 * --------------------------------------------------*/
1281 class SwSpaceManipulator
1283 SwTxtPaintInfo& rInfo;
1284 SwMultiPortion& rMulti;
1285 std::vector<long>* pOldSpaceAdd;
1286 MSHORT nOldSpIdx;
1287 long nSpaceAdd;
1288 sal_Bool bSpaceChg : 1;
1289 sal_uInt8 nOldDir : 2;
1290 public:
1291 SwSpaceManipulator( SwTxtPaintInfo& rInf, SwMultiPortion& rMult );
1292 ~SwSpaceManipulator();
1293 void SecondLine();
1294 inline long GetSpaceAdd() const { return nSpaceAdd; }
1297 SwSpaceManipulator::SwSpaceManipulator( SwTxtPaintInfo& rInf,
1298 SwMultiPortion& rMult ) :
1299 rInfo( rInf ), rMulti( rMult )
1301 pOldSpaceAdd = rInfo.GetpSpaceAdd();
1302 nOldSpIdx = rInfo.GetSpaceIdx();
1303 nOldDir = rInfo.GetDirection();
1304 rInfo.SetDirection( rMulti.GetDirection() );
1305 bSpaceChg = sal_False;
1307 if( rMulti.IsDouble() )
1309 nSpaceAdd = ( pOldSpaceAdd && !rMulti.HasTabulator() ) ?
1310 rInfo.GetSpaceAdd() : 0;
1311 if( rMulti.GetRoot().IsSpaceAdd() )
1313 rInfo.SetpSpaceAdd( rMulti.GetRoot().GetpLLSpaceAdd() );
1314 rInfo.ResetSpaceIdx();
1315 bSpaceChg = rMulti.ChgSpaceAdd( &rMulti.GetRoot(), nSpaceAdd );
1317 else if( rMulti.HasTabulator() )
1318 rInfo.SetpSpaceAdd( NULL );
1320 else if ( ! rMulti.IsBidi() )
1322 rInfo.SetpSpaceAdd( rMulti.GetRoot().GetpLLSpaceAdd() );
1323 rInfo.ResetSpaceIdx();
1327 void SwSpaceManipulator::SecondLine()
1329 if( bSpaceChg )
1331 rInfo.RemoveFirstSpaceAdd();
1332 bSpaceChg = sal_False;
1334 SwLineLayout *pLay = rMulti.GetRoot().GetNext();
1335 if( pLay->IsSpaceAdd() )
1337 rInfo.SetpSpaceAdd( pLay->GetpLLSpaceAdd() );
1338 rInfo.ResetSpaceIdx();
1339 bSpaceChg = rMulti.ChgSpaceAdd( pLay, nSpaceAdd );
1341 else
1343 rInfo.SetpSpaceAdd( (!rMulti.IsDouble() || rMulti.HasTabulator() ) ?
1344 0 : pOldSpaceAdd );
1345 rInfo.SetSpaceIdx( nOldSpIdx);
1349 SwSpaceManipulator::~SwSpaceManipulator()
1351 if( bSpaceChg )
1353 rInfo.RemoveFirstSpaceAdd();
1354 bSpaceChg = sal_False;
1356 rInfo.SetpSpaceAdd( pOldSpaceAdd );
1357 rInfo.SetSpaceIdx( nOldSpIdx);
1358 rInfo.SetDirection( nOldDir );
1361 /*-----------------13.10.00 16:24-------------------
1362 * SwTxtPainter::PaintMultiPortion manages the paint for a SwMultiPortion.
1363 * External, for the calling function, it seems to be a normal Paint-function,
1364 * internal it is like a SwTxtFrm::Paint with multiple DrawTextLines
1365 * --------------------------------------------------*/
1367 void SwTxtPainter::PaintMultiPortion( const SwRect &rPaint,
1368 SwMultiPortion& rMulti, const SwMultiPortion* pEnvPor )
1370 GETGRID( pFrm->FindPageFrm() )
1371 const sal_Bool bHasGrid = pGrid && GetInfo().SnapToGrid();
1372 USHORT nGridWidth = 0;
1373 USHORT nRubyHeight = 0;
1374 sal_Bool bRubyTop = sal_False;
1376 if ( bHasGrid )
1378 nGridWidth = pGrid->GetBaseHeight();
1379 nRubyHeight = pGrid->GetRubyHeight();
1380 bRubyTop = ! pGrid->GetRubyTextBelow();
1383 // do not allow grid mode for first line in ruby portion
1384 const sal_Bool bRubyInGrid = bHasGrid && rMulti.IsRuby();
1386 const USHORT nOldHeight = rMulti.Height();
1387 const sal_Bool bOldGridModeAllowed = GetInfo().SnapToGrid();
1389 if ( bRubyInGrid )
1391 GetInfo().SetSnapToGrid( ! bRubyTop );
1392 rMulti.Height( pCurr->Height() );
1395 SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
1396 BYTE nEnvDir = 0;
1397 BYTE nThisDir = 0;
1398 BYTE nFrmDir = 0;
1399 if ( rMulti.IsBidi() )
1401 // these values are needed for the calculation of the x coordinate
1402 // and the layout mode
1403 ASSERT( ! pEnvPor || pEnvPor->IsBidi(),
1404 "Oh no, I expected a BidiPortion" )
1405 nFrmDir = GetInfo().GetTxtFrm()->IsRightToLeft() ? 1 : 0;
1406 nEnvDir = pEnvPor ? ((SwBidiPortion*)pEnvPor)->GetLevel() % 2 : nFrmDir;
1407 nThisDir = ((SwBidiPortion&)rMulti).GetLevel() % 2;
1410 #if OSL_DEBUG_LEVEL > 1
1411 // only paint first level bidi portions
1412 if( rMulti.Width() > 1 && ! pEnvPor )
1413 GetInfo().DrawViewOpt( rMulti, POR_FLD );
1414 #endif
1416 if ( bRubyInGrid )
1417 rMulti.Height( nOldHeight );
1419 // do we have to repaint a post it portion?
1420 if( GetInfo().OnWin() && rMulti.GetPortion() &&
1421 ! rMulti.GetPortion()->Width() )
1422 rMulti.GetPortion()->PrePaint( GetInfo(), &rMulti );
1424 // old values must be saved and restored at the end
1425 xub_StrLen nOldLen = GetInfo().GetLen();
1426 KSHORT nOldX = KSHORT(GetInfo().X());
1427 long nOldY = GetInfo().Y();
1428 xub_StrLen nOldIdx = GetInfo().GetIdx();
1430 SwSpaceManipulator aManip( GetInfo(), rMulti );
1432 SwFontSave *pFontSave;
1433 SwFont* pTmpFnt;
1435 if( rMulti.IsDouble() )
1437 pTmpFnt = new SwFont( *GetInfo().GetFont() );
1438 if( rMulti.IsDouble() )
1440 SetPropFont( 50 );
1441 pTmpFnt->SetProportion( GetPropFont() );
1443 pFontSave = new SwFontSave( GetInfo(), pTmpFnt, this );
1445 else
1447 pFontSave = NULL;
1448 pTmpFnt = NULL;
1451 if( rMulti.HasBrackets() )
1453 xub_StrLen nTmpOldIdx = GetInfo().GetIdx();
1454 GetInfo().SetIdx(((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart);
1455 SeekAndChg( GetInfo() );
1456 ((SwDoubleLinePortion&)rMulti).PaintBracket( GetInfo(), 0, sal_True );
1457 GetInfo().SetIdx( nTmpOldIdx );
1460 KSHORT nTmpX = KSHORT(GetInfo().X());
1462 SwLineLayout* pLay = &rMulti.GetRoot();// the first line of the multiportion
1463 SwLinePortion* pPor = pLay->GetFirstPortion();//first portion of these line
1464 SwTwips nOfst = 0;
1466 // GetInfo().Y() is the baseline from the surrounding line. We must switch
1467 // this temporary to the baseline of the inner lines of the multiportion.
1468 if( rMulti.HasRotation() )
1470 if( rMulti.IsRevers() )
1472 GetInfo().Y( nOldY - rMulti.GetAscent() );
1473 nOfst = nTmpX + rMulti.Width();
1475 else
1477 GetInfo().Y( nOldY - rMulti.GetAscent() + rMulti.Height() );
1478 nOfst = nTmpX;
1481 else if ( rMulti.IsBidi() )
1483 // does the current bidi portion has the same direction
1484 // as its environment?
1485 if ( nEnvDir != nThisDir )
1487 // different directions, we have to adjust the x coordinate
1488 SwTwips nMultiWidth = rMulti.Width() +
1489 rMulti.CalcSpacing( GetInfo().GetSpaceAdd(), GetInfo() );
1491 if ( nFrmDir == nThisDir )
1492 GetInfo().X( GetInfo().X() - nMultiWidth );
1493 else
1494 GetInfo().X( GetInfo().X() + nMultiWidth );
1497 nOfst = nOldY - rMulti.GetAscent();
1499 // set layout mode
1500 aLayoutModeModifier.Modify( nThisDir );
1502 else
1503 nOfst = nOldY - rMulti.GetAscent();
1505 sal_Bool bRest = pLay->IsRest();
1506 sal_Bool bFirst = sal_True;
1508 ASSERT( 0 == GetInfo().GetUnderFnt() || rMulti.IsBidi(),
1509 " Only BiDi portions are allowed to use the common underlining font" )
1513 if ( bHasGrid )
1515 if( rMulti.HasRotation() )
1517 const USHORT nAdjustment = ( pLay->Height() - pPor->Height() ) / 2 +
1518 pPor->GetAscent();
1519 if( rMulti.IsRevers() )
1520 GetInfo().X( nOfst - nAdjustment );
1521 else
1522 GetInfo().X( nOfst + nAdjustment );
1524 else
1526 // special treatment for ruby portions in grid mode
1527 SwTwips nAdjustment = 0;
1528 if ( rMulti.IsRuby() )
1530 if ( bRubyTop != ( pLay == &rMulti.GetRoot() ) )
1531 // adjust base text
1532 nAdjustment = ( pCurr->Height() - nRubyHeight - pPor->Height() ) / 2;
1533 else if ( bRubyTop )
1534 // adjust upper ruby text
1535 nAdjustment = nRubyHeight - pPor->Height();
1536 // else adjust lower ruby text
1539 GetInfo().Y( nOfst + nAdjustment + pPor->GetAscent() );
1542 else if( rMulti.HasRotation() )
1544 if( rMulti.IsRevers() )
1545 GetInfo().X( nOfst - AdjustBaseLine( *pLay, pPor, 0, 0, sal_True ) );
1546 else
1547 GetInfo().X( nOfst + AdjustBaseLine( *pLay, pPor ) );
1549 else
1550 GetInfo().Y( nOfst + AdjustBaseLine( *pLay, pPor ) );
1552 sal_Bool bSeeked = sal_True;
1553 GetInfo().SetLen( pPor->GetLen() );
1555 if( bRest && pPor->InFldGrp() && !pPor->GetLen() )
1557 if( ((SwFldPortion*)pPor)->HasFont() )
1558 bSeeked = sal_False;
1559 else
1560 SeekAndChgBefore( GetInfo() );
1562 else if( pPor->InTxtGrp() || pPor->InFldGrp() || pPor->InTabGrp() )
1563 SeekAndChg( GetInfo() );
1564 else if ( !bFirst && pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() )
1566 if( GetRedln() )
1567 SeekAndChg( GetInfo() );
1568 else
1569 SeekAndChgBefore( GetInfo() );
1571 else
1572 bSeeked = sal_False;
1574 SwLinePortion *pNext = pPor->GetPortion();
1575 if(GetInfo().OnWin() && pNext && !pNext->Width() )
1577 if ( !bSeeked )
1578 SeekAndChg( GetInfo() );
1579 pNext->PrePaint( GetInfo(), pPor );
1582 CheckSpecialUnderline( pPor );
1583 SwUnderlineFont* pUnderLineFnt = GetInfo().GetUnderFnt();
1584 if ( pUnderLineFnt )
1586 if ( rMulti.IsDouble() )
1587 pUnderLineFnt->GetFont().SetProportion( 50 );
1588 pUnderLineFnt->SetPos( GetInfo().GetPos() );
1591 if ( rMulti.IsBidi() )
1593 // we do not allow any rotation inside a bidi portion
1594 SwFont* pTmpFont = GetInfo().GetFont();
1595 pTmpFont->SetVertical( 0, GetInfo().GetTxtFrm()->IsVertical() );
1598 if( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi() )
1600 // but we do allow nested bidi portions
1601 ASSERT( rMulti.IsBidi(), "Only nesting of bidi portions is allowed" )
1602 PaintMultiPortion( rPaint, (SwMultiPortion&)*pPor, &rMulti );
1604 else
1605 pPor->Paint( GetInfo() );
1607 if( GetFnt()->IsURL() && pPor->InTxtGrp() )
1608 GetInfo().NotifyURL( *pPor );
1610 bFirst &= !pPor->GetLen();
1611 if( pNext || !pPor->IsMarginPortion() )
1612 pPor->Move( GetInfo() );
1614 pPor = pNext;
1616 // If there's no portion left, we go to the next line
1617 if( !pPor && pLay->GetNext() )
1619 pLay = pLay->GetNext();
1620 pPor = pLay->GetFirstPortion();
1621 bRest = pLay->IsRest();
1622 aManip.SecondLine();
1624 // delete underline font
1625 delete GetInfo().GetUnderFnt();
1626 GetInfo().SetUnderFnt( 0 );
1628 if( rMulti.HasRotation() )
1630 if( rMulti.IsRevers() )
1632 nOfst += pLay->Height();
1633 GetInfo().Y( nOldY - rMulti.GetAscent() );
1635 else
1637 nOfst -= pLay->Height();
1638 GetInfo().Y( nOldY - rMulti.GetAscent() + rMulti.Height() );
1641 else if ( bHasGrid && rMulti.IsRuby() )
1643 GetInfo().X( nTmpX );
1644 if ( bRubyTop )
1646 nOfst += nRubyHeight;
1647 GetInfo().SetSnapToGrid( sal_True );
1649 else
1651 nOfst += pCurr->Height() - nRubyHeight;
1652 GetInfo().SetSnapToGrid( sal_False );
1654 } else
1656 GetInfo().X( nTmpX );
1657 // We switch to the baseline of the next inner line
1658 nOfst += rMulti.GetRoot().Height();
1661 } while( pPor );
1663 if ( bRubyInGrid )
1664 GetInfo().SetSnapToGrid( bOldGridModeAllowed );
1666 // delete underline font
1667 if ( ! rMulti.IsBidi() )
1669 delete GetInfo().GetUnderFnt();
1670 GetInfo().SetUnderFnt( 0 );
1673 GetInfo().SetIdx( nOldIdx );
1674 GetInfo().Y( nOldY );
1676 if( rMulti.HasBrackets() )
1678 xub_StrLen nTmpOldIdx = GetInfo().GetIdx();
1679 GetInfo().SetIdx(((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart);
1680 SeekAndChg( GetInfo() );
1681 GetInfo().X( nOldX );
1682 ((SwDoubleLinePortion&)rMulti).PaintBracket( GetInfo(),
1683 aManip.GetSpaceAdd(), sal_False );
1684 GetInfo().SetIdx( nTmpOldIdx );
1686 // Restore the saved values
1687 GetInfo().X( nOldX );
1688 GetInfo().SetLen( nOldLen );
1689 delete pFontSave;
1690 delete pTmpFnt;
1691 SetPropFont( 0 );
1694 sal_Bool lcl_ExtractFieldFollow( SwLineLayout* pLine, SwLinePortion* &rpFld )
1696 SwLinePortion* pLast = pLine;
1697 rpFld = pLine->GetPortion();
1698 while( rpFld && !rpFld->InFldGrp() )
1700 pLast = rpFld;
1701 rpFld = rpFld->GetPortion();
1703 sal_Bool bRet = rpFld != 0;
1704 if( bRet )
1706 if( ((SwFldPortion*)rpFld)->IsFollow() )
1708 rpFld->Truncate();
1709 pLast->SetPortion( NULL );
1711 else
1712 rpFld = NULL;
1714 pLine->Truncate();
1715 return bRet;
1718 /*----------------------------------------------------
1719 * lcl_TruncateMultiPortion
1720 * If a multi portion completely has to go to the
1721 * next line, this function is called to trunctate
1722 * the rest of the remaining multi portion
1723 * --------------------------------------------------*/
1725 void lcl_TruncateMultiPortion( SwMultiPortion& rMulti, SwTxtFormatInfo& rInf,
1726 xub_StrLen nStartIdx )
1728 rMulti.GetRoot().Truncate();
1729 rMulti.GetRoot().SetLen(0);
1730 rMulti.GetRoot().Width(0);
1731 // rMulti.CalcSize( *this, aInf );
1732 if ( rMulti.GetRoot().GetNext() )
1734 rMulti.GetRoot().GetNext()->Truncate();
1735 rMulti.GetRoot().GetNext()->SetLen( 0 );
1736 rMulti.GetRoot().GetNext()->Width( 0 );
1738 rMulti.Width( 0 );
1739 rMulti.SetLen(0);
1740 rInf.SetIdx( nStartIdx );
1743 /*-----------------------------------------------------------------------------
1744 * SwTxtFormatter::BuildMultiPortion
1745 * manages the formatting of a SwMultiPortion. External, for the calling
1746 * function, it seems to be a normal Format-function, internal it is like a
1747 * SwTxtFrm::_Format with multiple BuildPortions
1748 *---------------------------------------------------------------------------*/
1750 BOOL SwTxtFormatter::BuildMultiPortion( SwTxtFormatInfo &rInf,
1751 SwMultiPortion& rMulti )
1753 SwTwips nMaxWidth = rInf.Width();
1754 KSHORT nOldX = 0;
1756 if( rMulti.HasBrackets() )
1758 xub_StrLen nOldIdx = rInf.GetIdx();
1759 rInf.SetIdx( ((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart );
1760 SeekAndChg( rInf );
1761 nOldX = KSHORT(GetInfo().X());
1762 ((SwDoubleLinePortion&)rMulti).FormatBrackets( rInf, nMaxWidth );
1763 rInf.SetIdx( nOldIdx );
1766 SeekAndChg( rInf );
1767 SwFontSave *pFontSave;
1768 if( rMulti.IsDouble() )
1770 SwFont* pTmpFnt = new SwFont( *rInf.GetFont() );
1771 if( rMulti.IsDouble() )
1773 SetPropFont( 50 );
1774 pTmpFnt->SetProportion( GetPropFont() );
1776 pFontSave = new SwFontSave( rInf, pTmpFnt, this );
1778 else
1779 pFontSave = NULL;
1781 SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
1782 if ( rMulti.IsBidi() )
1784 // set layout mode
1785 aLayoutModeModifier.Modify( ! rInf.GetTxtFrm()->IsRightToLeft() );
1788 SwTwips nTmpX = 0;
1790 if( rMulti.HasRotation() )
1792 // For nMaxWidth we take the height of the body frame.
1793 // #i25067#: If the current frame is inside a table, we restrict
1794 // nMaxWidth to the current frame height, unless the frame size
1795 // attribute is set to variable size:
1797 // We set nTmpX (which is used for portion calculating) to the
1798 // current Y value
1799 const SwPageFrm* pPage = pFrm->FindPageFrm();
1800 ASSERT( pPage, "No page in frame!");
1801 const SwLayoutFrm* pUpperFrm = pPage;
1803 if ( pFrm->IsInTab() )
1805 pUpperFrm = pFrm->GetUpper();
1806 while ( pUpperFrm && !pUpperFrm->IsCellFrm() )
1807 pUpperFrm = pUpperFrm->GetUpper();
1808 ASSERT( pUpperFrm, "pFrm is in table but does not have an upper cell frame" )
1809 const SwTableLine* pLine = ((SwRowFrm*)pUpperFrm->GetUpper())->GetTabLine();
1810 const SwFmtFrmSize& rFrmFmtSize = pLine->GetFrmFmt()->GetFrmSize();
1811 if ( ATT_VAR_SIZE == rFrmFmtSize.GetHeightSizeType() )
1812 pUpperFrm = pPage;
1814 if ( pUpperFrm == pPage && !pFrm->IsInFtn() )
1815 pUpperFrm = pPage->FindBodyCont();
1817 nMaxWidth = pUpperFrm ?
1818 ( rInf.GetTxtFrm()->IsVertical() ?
1819 pUpperFrm->Prt().Width() :
1820 pUpperFrm->Prt().Height() ) :
1821 USHRT_MAX;
1823 else
1824 nTmpX = rInf.X();
1826 SwMultiPortion* pOldMulti = pMulti;
1828 pMulti = &rMulti;
1829 SwLineLayout *pOldCurr = pCurr;
1830 xub_StrLen nOldStart = GetStart();
1831 SwTwips nMinWidth = nTmpX + 1;
1832 SwTwips nActWidth = nMaxWidth;
1833 const xub_StrLen nStartIdx = rInf.GetIdx();
1834 xub_StrLen nMultiLen = rMulti.GetLen();
1836 SwLinePortion *pFirstRest;
1837 SwLinePortion *pSecondRest;
1838 if( rMulti.IsFormatted() )
1840 if( !lcl_ExtractFieldFollow( &rMulti.GetRoot(), pFirstRest )
1841 && rMulti.IsDouble() && rMulti.GetRoot().GetNext() )
1842 lcl_ExtractFieldFollow( rMulti.GetRoot().GetNext(), pFirstRest );
1843 if( !rMulti.IsDouble() && rMulti.GetRoot().GetNext() )
1844 lcl_ExtractFieldFollow( rMulti.GetRoot().GetNext(), pSecondRest );
1845 else
1846 pSecondRest = NULL;
1848 else
1850 pFirstRest = rMulti.GetRoot().GetPortion();
1851 pSecondRest = rMulti.GetRoot().GetNext() ?
1852 rMulti.GetRoot().GetNext()->GetPortion() : NULL;
1853 if( pFirstRest )
1854 rMulti.GetRoot().SetPortion( NULL );
1855 if( pSecondRest )
1856 rMulti.GetRoot().GetNext()->SetPortion( NULL );
1857 rMulti.SetFormatted();
1858 nMultiLen = nMultiLen - rInf.GetIdx();
1861 // save some values
1862 const XubString* pOldTxt = &(rInf.GetTxt());
1863 const SwTwips nOldPaintOfst = rInf.GetPaintOfst();
1865 XubString aMultiStr( rInf.GetTxt(), 0, nMultiLen + rInf.GetIdx() );
1866 rInf.SetTxt( aMultiStr );
1867 SwTxtFormatInfo aInf( rInf, rMulti.GetRoot(), nActWidth );
1868 // Do we allow break cuts? The FirstMulti-Flag is evaluated during
1869 // line break determination.
1870 sal_Bool bFirstMulti = rInf.GetIdx() != rInf.GetLineStart();
1872 SwLinePortion *pNextFirst = NULL;
1873 SwLinePortion *pNextSecond = NULL;
1874 BOOL bRet = FALSE;
1876 GETGRID( pFrm->FindPageFrm() )
1877 const sal_Bool bHasGrid = pGrid && GRID_LINES_CHARS == pGrid->GetGridType();
1879 USHORT nGridWidth = 0;
1880 USHORT nRubyHeight = 0;
1881 sal_Bool bRubyTop = sal_False;
1883 if ( bHasGrid )
1885 nGridWidth = pGrid->GetBaseHeight();
1886 nRubyHeight = pGrid->GetRubyHeight();
1887 bRubyTop = ! pGrid->GetRubyTextBelow();
1892 pCurr = &rMulti.GetRoot();
1893 nStart = nStartIdx;
1894 bRet = FALSE;
1895 FormatReset( aInf );
1896 aInf.X( nTmpX );
1897 aInf.Width( KSHORT(nActWidth) );
1898 aInf.RealWidth( KSHORT(nActWidth) );
1899 aInf.SetFirstMulti( bFirstMulti );
1900 aInf.SetNumDone( rInf.IsNumDone() );
1901 aInf.SetFtnDone( rInf.IsFtnDone() );
1903 if( pFirstRest )
1905 ASSERT( pFirstRest->InFldGrp(), "BuildMulti: Fieldrest expected");
1906 SwFldPortion *pFld =
1907 ((SwFldPortion*)pFirstRest)->Clone(
1908 ((SwFldPortion*)pFirstRest)->GetExp() );
1909 pFld->SetFollow( sal_True );
1910 aInf.SetRest( pFld );
1912 aInf.SetRuby( rMulti.IsRuby() && rMulti.OnTop() );
1914 // in grid mode we temporarily have to disable the grid for the ruby line
1915 const sal_Bool bOldGridModeAllowed = GetInfo().SnapToGrid();
1916 if ( bHasGrid && aInf.IsRuby() && bRubyTop )
1917 aInf.SetSnapToGrid( sal_False );
1919 // If there's no more rubytext, then buildportion is forbidden
1920 if( pFirstRest || !aInf.IsRuby() )
1921 BuildPortions( aInf );
1923 aInf.SetSnapToGrid( bOldGridModeAllowed );
1925 rMulti.CalcSize( *this, aInf );
1926 pCurr->SetRealHeight( pCurr->Height() );
1928 if( rMulti.IsBidi() )
1930 pNextFirst = aInf.GetRest();
1931 break;
1934 if( rMulti.HasRotation() && !rMulti.IsDouble() )
1935 break;
1936 // second line has to be formatted
1937 else if( pCurr->GetLen()<nMultiLen || rMulti.IsRuby() || aInf.GetRest())
1939 xub_StrLen nFirstLen = pCurr->GetLen();
1940 delete pCurr->GetNext();
1941 pCurr->SetNext( new SwLineLayout() );
1942 pCurr = pCurr->GetNext();
1943 nStart = aInf.GetIdx();
1944 aInf.X( nTmpX );
1945 SwTxtFormatInfo aTmp( aInf, *pCurr, nActWidth );
1946 if( rMulti.IsRuby() )
1948 aTmp.SetRuby( !rMulti.OnTop() );
1949 pNextFirst = aInf.GetRest();
1950 if( pSecondRest )
1952 ASSERT( pSecondRest->InFldGrp(), "Fieldrest expected");
1953 SwFldPortion *pFld = ((SwFldPortion*)pSecondRest)->Clone(
1954 ((SwFldPortion*)pSecondRest)->GetExp() );
1955 pFld->SetFollow( sal_True );
1956 aTmp.SetRest( pFld );
1958 if( !rMulti.OnTop() && nFirstLen < nMultiLen )
1959 bRet = sal_True;
1961 else
1962 aTmp.SetRest( aInf.GetRest() );
1963 aInf.SetRest( NULL );
1965 // in grid mode we temporarily have to disable the grid for the ruby line
1966 if ( bHasGrid && aTmp.IsRuby() && ! bRubyTop )
1967 aTmp.SetSnapToGrid( sal_False );
1969 BuildPortions( aTmp );
1971 aTmp.SetSnapToGrid( bOldGridModeAllowed );
1973 rMulti.CalcSize( *this, aInf );
1974 rMulti.GetRoot().SetRealHeight( rMulti.GetRoot().Height() );
1975 pCurr->SetRealHeight( pCurr->Height() );
1976 if( rMulti.IsRuby() )
1978 pNextSecond = aTmp.GetRest();
1979 if( pNextFirst )
1980 bRet = sal_True;
1982 else
1983 pNextFirst = aTmp.GetRest();
1984 if( ( !aTmp.IsRuby() && nFirstLen + pCurr->GetLen() < nMultiLen )
1985 || aTmp.GetRest() )
1986 // our guess for width of multiportion was too small,
1987 // text did not fit into multiportion
1988 bRet = sal_True;
1990 if( rMulti.IsRuby() )
1991 break;
1992 if( bRet )
1994 // our guess for multiportion width was too small,
1995 // we set min to act
1996 nMinWidth = nActWidth;
1997 nActWidth = ( 3 * nMaxWidth + nMinWidth + 3 ) / 4;
1998 if ( nActWidth == nMaxWidth && rInf.GetLineStart() == rInf.GetIdx() )
1999 // we have too less space, we must allow break cuts
2000 // ( the first multi flag is considered during TxtPortion::_Format() )
2001 bFirstMulti = sal_False;
2002 if( nActWidth <= nMinWidth )
2003 break;
2005 else
2007 // For Solaris, this optimisation can causes trouble:
2008 // Setting this to the portion width ( = rMulti.Width() )
2009 // can make GetTextBreak inside SwTxtGuess::Guess return to small
2010 // values. Therefore we add some extra twips.
2011 if( nActWidth > nTmpX + rMulti.Width() + 6 )
2012 nActWidth = nTmpX + rMulti.Width() + 6;
2013 nMaxWidth = nActWidth;
2014 nActWidth = ( 3 * nMaxWidth + nMinWidth + 3 ) / 4;
2015 if( nActWidth >= nMaxWidth )
2016 break;
2017 // we do not allow break cuts during formatting
2018 bFirstMulti = sal_True;
2020 delete pNextFirst;
2021 pNextFirst = NULL;
2022 } while ( TRUE );
2024 pMulti = pOldMulti;
2026 pCurr = pOldCurr;
2027 nStart = nOldStart;
2028 SetPropFont( 0 );
2030 rMulti.SetLen( rMulti.GetRoot().GetLen() + ( rMulti.GetRoot().GetNext() ?
2031 rMulti.GetRoot().GetNext()->GetLen() : 0 ) );
2033 if( rMulti.IsDouble() )
2035 ((SwDoubleLinePortion&)rMulti).CalcBlanks( rInf );
2036 if( ((SwDoubleLinePortion&)rMulti).GetLineDiff() )
2038 SwLineLayout* pLine = &rMulti.GetRoot();
2039 if( ((SwDoubleLinePortion&)rMulti).GetLineDiff() > 0 )
2041 rInf.SetIdx( nStartIdx + pLine->GetLen() );
2042 pLine = pLine->GetNext();
2044 if( pLine )
2046 GetInfo().SetMulti( sal_True );
2047 CalcNewBlock( pLine, NULL, rMulti.Width() );
2048 GetInfo().SetMulti( sal_False );
2050 rInf.SetIdx( nStartIdx );
2052 if( ((SwDoubleLinePortion&)rMulti).GetBrackets() )
2054 rMulti.Width( rMulti.Width() +
2055 ((SwDoubleLinePortion&)rMulti).BracketWidth() );
2056 GetInfo().X( nOldX );
2059 else
2061 rMulti.ActualizeTabulator();
2062 if( rMulti.IsRuby() )
2064 ((SwRubyPortion&)rMulti).Adjust( rInf );
2065 ((SwRubyPortion&)rMulti).CalcRubyOffset();
2068 if( rMulti.HasRotation() )
2070 SwTwips nH = rMulti.Width();
2071 SwTwips nAsc = rMulti.GetAscent() + ( nH - rMulti.Height() )/2;
2072 if( nAsc > nH )
2073 nAsc = nH;
2074 else if( nAsc < 0 )
2075 nAsc = 0;
2076 rMulti.Width( rMulti.Height() );
2077 rMulti.Height( KSHORT(nH) );
2078 rMulti.SetAscent( KSHORT(nAsc) );
2079 bRet = ( rInf.GetPos().X() + rMulti.Width() > rInf.Width() ) &&
2080 nStartIdx != rInf.GetLineStart();
2082 else if ( rMulti.IsBidi() )
2084 bRet = rMulti.GetLen() < nMultiLen || pNextFirst;
2087 // line break has to be performed!
2088 if( bRet )
2090 ASSERT( !pNextFirst || pNextFirst->InFldGrp(),
2091 "BuildMultiPortion: Surprising restportion, field expected" );
2092 SwMultiPortion *pTmp;
2093 if( rMulti.IsDouble() )
2094 pTmp = new SwDoubleLinePortion( ((SwDoubleLinePortion&)rMulti),
2095 nMultiLen + rInf.GetIdx() );
2096 else if( rMulti.IsRuby() )
2098 ASSERT( !pNextSecond || pNextSecond->InFldGrp(),
2099 "BuildMultiPortion: Surprising restportion, field expected" );
2101 if ( rInf.GetIdx() == rInf.GetLineStart() )
2103 // the ruby portion has to be split in two portions
2104 pTmp = new SwRubyPortion( ((SwRubyPortion&)rMulti),
2105 nMultiLen + rInf.GetIdx() );
2107 if( pNextSecond )
2109 pTmp->GetRoot().SetNext( new SwLineLayout() );
2110 pTmp->GetRoot().GetNext()->SetPortion( pNextSecond );
2112 pTmp->SetFollowFld();
2114 else
2116 // we try to keep our ruby portion together
2117 lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
2118 pTmp = 0;
2121 else if( rMulti.HasRotation() )
2123 // we try to keep our rotated portion together
2124 lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
2125 pTmp = new SwRotatedPortion( nMultiLen + rInf.GetIdx(),
2126 rMulti.GetDirection() );
2128 // during a recursion of BuildMultiPortions we may not build
2129 // a new SwBidiPortion, this would cause a memory leak
2130 else if( rMulti.IsBidi() && ! pMulti )
2132 if ( ! rMulti.GetLen() )
2133 lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
2135 // If there is a HolePortion at the end of the bidi portion,
2136 // it has to be moved behind the bidi portion. Otherwise
2137 // the visual cursor travelling gets into trouble.
2138 SwLineLayout& aRoot = rMulti.GetRoot();
2139 SwLinePortion* pPor = aRoot.GetFirstPortion();
2140 while ( pPor )
2142 if ( pPor->GetPortion() && pPor->GetPortion()->IsHolePortion() )
2144 SwLinePortion* pHolePor = pPor->GetPortion();
2145 pPor->SetPortion( NULL );
2146 aRoot.SetLen( aRoot.GetLen() - pHolePor->GetLen() );
2147 rMulti.SetLen( rMulti.GetLen() - pHolePor->GetLen() );
2148 rMulti.SetPortion( pHolePor );
2149 break;
2151 pPor = pPor->GetPortion();
2154 pTmp = new SwBidiPortion( nMultiLen + rInf.GetIdx(),
2155 ((SwBidiPortion&)rMulti).GetLevel() );
2157 else
2158 pTmp = NULL;
2160 if ( ! rMulti.GetLen() && rInf.GetLast() )
2162 SeekAndChgBefore( rInf );
2163 rInf.GetLast()->FormatEOL( rInf );
2166 if( pNextFirst && pTmp )
2168 pTmp->SetFollowFld();
2169 pTmp->GetRoot().SetPortion( pNextFirst );
2171 else
2172 // A follow field portion is still waiting. If nobody wants it,
2173 // we delete it.
2174 delete pNextFirst;
2176 rInf.SetRest( pTmp );
2179 rInf.SetTxt( *pOldTxt );
2180 rInf.SetPaintOfst( nOldPaintOfst );
2181 rInf.SetStop( aInf.IsStop() );
2182 rInf.SetNumDone( sal_True );
2183 rInf.SetFtnDone( sal_True );
2184 SeekAndChg( rInf );
2185 delete pFirstRest;
2186 delete pSecondRest;
2187 delete pFontSave;
2188 return bRet;
2191 /*-----------------08.11.00 09:29-------------------
2192 * SwTxtFormatter::MakeRestPortion(..)
2193 * When a fieldportion at the end of line breaks and needs a following
2194 * fieldportion in the next line, then the "restportion" of the formatinfo
2195 * has to be set. Normally this happens during the formatting of the first
2196 * part of the fieldportion.
2197 * But sometimes the formatting starts at the line with the following part,
2198 * exspecally when the following part is on the next page.
2199 * In this case the MakeRestPortion-function has to create the following part.
2200 * The first parameter is the line that contains possibly a first part
2201 * of a field. When the function finds such field part, it creates the right
2202 * restportion. This may be a multiportion, e.g. if the field is surrounded by
2203 * a doubleline- or ruby-portion.
2204 * The second parameter is the start index of the line.
2205 * --------------------------------------------------*/
2207 SwLinePortion* SwTxtFormatter::MakeRestPortion( const SwLineLayout* pLine,
2208 xub_StrLen nPosition )
2210 if( !nPosition )
2211 return NULL;
2212 xub_StrLen nMultiPos = nPosition - pLine->GetLen();
2213 const SwMultiPortion *pTmpMulti = NULL;
2214 const SwMultiPortion *pHelpMulti = NULL;
2215 const SwLinePortion* pPor = pLine->GetFirstPortion();
2216 SwFldPortion *pFld = NULL;
2217 while( pPor )
2219 if( pPor->GetLen() )
2221 if( !pHelpMulti )
2223 nMultiPos = nMultiPos + pPor->GetLen();
2224 pTmpMulti = NULL;
2227 if( pPor->InFldGrp() )
2229 if( !pHelpMulti )
2230 pTmpMulti = NULL;
2231 pFld = (SwFldPortion*)pPor;
2233 else if( pPor->IsMultiPortion() )
2235 ASSERT( !pHelpMulti || pHelpMulti->IsBidi(),
2236 "Nested multiportions are forbidden." );
2238 pFld = NULL;
2239 pTmpMulti = (SwMultiPortion*)pPor;
2241 pPor = pPor->GetPortion();
2242 // If the last portion is a multi-portion, we enter it
2243 // and look for a field portion inside.
2244 // If we are already in a multiportion, we could change to the
2245 // next line
2246 if( !pPor && pTmpMulti )
2248 if( pHelpMulti )
2249 { // We're already inside the multiportion, let's take the second
2250 // line, if we are in a double line portion
2251 if( !pHelpMulti->IsRuby() )
2252 pPor = pHelpMulti->GetRoot().GetNext();
2253 pTmpMulti = NULL;
2255 else
2256 { // Now we enter a multiportion, in a ruby portion we take the
2257 // main line, not the phonetic line, in a doublelineportion we
2258 // starts with the first line.
2259 pHelpMulti = pTmpMulti;
2260 nMultiPos = nMultiPos - pHelpMulti->GetLen();
2261 if( pHelpMulti->IsRuby() && pHelpMulti->OnTop() )
2262 pPor = pHelpMulti->GetRoot().GetNext();
2263 else
2264 pPor = pHelpMulti->GetRoot().GetFirstPortion();
2268 if( pFld && !pFld->HasFollow() )
2269 pFld = NULL;
2271 SwLinePortion *pRest = NULL;
2272 if( pFld )
2274 const SwTxtAttr *pHint = GetAttr( nPosition - 1 );
2275 if( pHint && pHint->Which() == RES_TXTATR_FIELD )
2277 pRest = NewFldPortion( GetInfo(), pHint );
2278 if( pRest->InFldGrp() )
2279 ((SwFldPortion*)pRest)->TakeNextOffset( pFld );
2280 else
2282 delete pRest;
2283 pRest = NULL;
2287 if( !pHelpMulti )
2288 return pRest;
2290 nPosition = nMultiPos + pHelpMulti->GetLen();
2291 SwMultiCreator* pCreate = GetInfo().GetMultiCreator( nMultiPos, 0 );
2293 if ( !pCreate )
2295 ASSERT( !pHelpMulti->GetLen(), "Multiportion without attribut?" );
2296 if ( nMultiPos )
2297 --nMultiPos;
2298 pCreate = GetInfo().GetMultiCreator( --nMultiPos, 0 );
2301 if( pRest || nMultiPos > nPosition || ( pHelpMulti->IsRuby() &&
2302 ((SwRubyPortion*)pHelpMulti)->GetRubyOffset() < STRING_LEN ) )
2304 SwMultiPortion* pTmp;
2305 if( pHelpMulti->IsDouble() )
2306 pTmp = new SwDoubleLinePortion( *pCreate, nMultiPos );
2307 else if( pHelpMulti->IsBidi() )
2308 pTmp = new SwBidiPortion( nMultiPos, pCreate->nLevel );
2309 else if( pHelpMulti->IsRuby() )
2311 sal_Bool bRubyTop;
2312 sal_Bool* pRubyPos = 0;
2314 if ( GetInfo().SnapToGrid() )
2316 GETGRID( pFrm->FindPageFrm() )
2317 if ( pGrid )
2319 bRubyTop = ! pGrid->GetRubyTextBelow();
2320 pRubyPos = &bRubyTop;
2324 pTmp = new SwRubyPortion( *pCreate, *GetInfo().GetFont(),
2325 *pFrm->GetTxtNode()->getIDocumentSettingAccess(),
2326 nMultiPos, ((SwRubyPortion*)pHelpMulti)->GetRubyOffset(),
2327 pRubyPos );
2329 else if( pHelpMulti->HasRotation() )
2330 pTmp = new SwRotatedPortion( nMultiPos, pHelpMulti->GetDirection() );
2331 else
2333 delete pCreate;
2334 return pRest;
2336 delete pCreate;
2337 pTmp->SetFollowFld();
2338 if( pRest )
2340 SwLineLayout *pLay = &pTmp->GetRoot();
2341 if( pTmp->IsRuby() && pTmp->OnTop() )
2343 pLay->SetNext( new SwLineLayout() );
2344 pLay = pLay->GetNext();
2346 pLay->SetPortion( pRest );
2348 return pTmp;
2350 return pRest;
2355 /*-----------------23.10.00 10:47-------------------
2356 * SwTxtCursorSave notes the start and current line of a SwTxtCursor,
2357 * sets them to the values for GetCrsrOfst inside a multiportion
2358 * and restores them in the destructor.
2359 * --------------------------------------------------*/
2361 SwTxtCursorSave::SwTxtCursorSave( SwTxtCursor* pTxtCursor,
2362 SwMultiPortion* pMulti,
2363 SwTwips nY,
2364 USHORT& nX,
2365 xub_StrLen nCurrStart,
2366 long nSpaceAdd )
2368 pTxtCrsr = pTxtCursor;
2369 nStart = pTxtCursor->nStart;
2370 pTxtCursor->nStart = nCurrStart;
2371 pCurr = pTxtCursor->pCurr;
2372 pTxtCursor->pCurr = &pMulti->GetRoot();
2373 while( pTxtCursor->Y() + pTxtCursor->GetLineHeight() < nY &&
2374 pTxtCursor->Next() )
2375 ; // nothing
2376 nWidth = pTxtCursor->pCurr->Width();
2377 nOldProp = pTxtCursor->GetPropFont();
2379 if ( pMulti->IsDouble() || pMulti->IsBidi() )
2381 bSpaceChg = pMulti->ChgSpaceAdd( pTxtCursor->pCurr, nSpaceAdd );
2383 USHORT nSpaceCnt;
2384 if ( pMulti->IsDouble() )
2386 pTxtCursor->SetPropFont( 50 );
2387 nSpaceCnt = ((SwDoubleLinePortion*)pMulti)->GetSpaceCnt();
2389 else
2391 const xub_StrLen nOldIdx = pTxtCursor->GetInfo().GetIdx();
2392 pTxtCursor->GetInfo().SetIdx ( nCurrStart );
2393 nSpaceCnt = ((SwBidiPortion*)pMulti)->GetSpaceCnt(pTxtCursor->GetInfo());
2394 pTxtCursor->GetInfo().SetIdx ( nOldIdx );
2397 if( nSpaceAdd > 0 && !pMulti->HasTabulator() )
2398 pTxtCursor->pCurr->Width( static_cast<USHORT>(nWidth + nSpaceAdd * nSpaceCnt / SPACING_PRECISION_FACTOR ) );
2400 // For a BidiPortion we have to calculate the offset from the
2401 // end of the portion
2402 if ( nX && pMulti->IsBidi() )
2403 nX = pTxtCursor->pCurr->Width() - nX;
2405 else
2406 bSpaceChg = sal_False;
2409 SwTxtCursorSave::~SwTxtCursorSave()
2411 if( bSpaceChg )
2412 SwDoubleLinePortion::ResetSpaceAdd( pTxtCrsr->pCurr );
2413 pTxtCrsr->pCurr->Width( KSHORT(nWidth) );
2414 pTxtCrsr->pCurr = pCurr;
2415 pTxtCrsr->nStart = nStart;
2416 pTxtCrsr->SetPropFont( nOldProp );