| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_sw.hxx" |
| |
| |
| #include <hintids.hxx> |
| |
| #include <com/sun/star/i18n/ScriptType.hdl> |
| #include <editeng/twolinesitem.hxx> |
| #include <editeng/charrotateitem.hxx> |
| #include <vcl/outdev.hxx> |
| #include <fmtfld.hxx> |
| #include <fldbas.hxx> // SwField |
| #include <txatbase.hxx> |
| #include <fmtruby.hxx> // SwFmtRuby |
| #include <txtatr.hxx> // SwTxtRuby |
| #include <charfmt.hxx> |
| #include <txtinet.hxx> |
| #include <fchrfmt.hxx> |
| #include <layfrm.hxx> // GetUpper() |
| #include <SwPortionHandler.hxx> |
| #include <pormulti.hxx> // SwMultiPortion |
| #include <inftxt.hxx> // SwTxtSizeInfo |
| #include <itrpaint.hxx> // SwTxtPainter |
| #include <viewopt.hxx> // SwViewOptions |
| #include <itrform2.hxx> // SwTxtFormatter |
| #include <porfld.hxx> // SwFldPortion |
| #include <porglue.hxx> |
| #include <breakit.hxx> |
| #include <pagefrm.hxx> |
| #include <rowfrm.hxx> |
| #include <pagedesc.hxx> // SwPageDesc |
| #include <tgrditem.hxx> |
| #include <swtable.hxx> |
| #include <fmtfsize.hxx> |
| |
| using namespace ::com::sun::star; |
| extern sal_Bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt ); |
| |
| /*-----------------10.10.00 15:23------------------- |
| * class SwMultiPortion |
| * |
| * A SwMultiPortion is not a simple portion, |
| * it's a container, which contains almost a SwLineLayoutPortion. |
| * This SwLineLayout could be followed by other textportions via pPortion |
| * and by another SwLineLayout via pNext to realize a doubleline portion. |
| * --------------------------------------------------*/ |
| |
| SwMultiPortion::~SwMultiPortion() |
| { |
| delete pFldRest; |
| } |
| |
| void SwMultiPortion::Paint( const SwTxtPaintInfo & ) const |
| { |
| ASSERT( sal_False, |
| "Don't try SwMultiPortion::Paint, try SwTxtPainter::PaintMultiPortion" ); |
| } |
| |
| /*-----------------13.10.00 16:21------------------- |
| * Summarize the internal lines to calculate the (external) size. |
| * The internal line has to calculate first. |
| * --------------------------------------------------*/ |
| |
| void SwMultiPortion::CalcSize( SwTxtFormatter& rLine, SwTxtFormatInfo &rInf ) |
| { |
| Width( 0 ); |
| Height( 0 ); |
| SetAscent( 0 ); |
| SetFlyInCntnt( sal_False ); |
| SwLineLayout *pLay = &GetRoot(); |
| do |
| { |
| pLay->CalcLine( rLine, rInf ); |
| if( rLine.IsFlyInCntBase() ) |
| SetFlyInCntnt( sal_True ); |
| if( IsRuby() && ( OnTop() == ( pLay == &GetRoot() ) ) ) |
| { |
| // An empty phonetic line don't need an ascent or a height. |
| if( !pLay->Width() ) |
| { |
| pLay->SetAscent( 0 ); |
| pLay->Height( 0 ); |
| } |
| if( OnTop() ) |
| SetAscent( GetAscent() + pLay->Height() ); |
| } |
| else |
| SetAscent( GetAscent() + pLay->GetAscent() ); |
| Height( Height() + pLay->Height() ); |
| if( Width() < pLay->Width() ) |
| Width( pLay->Width() ); |
| pLay = pLay->GetNext(); |
| } while ( pLay ); |
| if( HasBrackets() ) |
| { |
| KSHORT nTmp = ((SwDoubleLinePortion*)this)->GetBrackets()->nHeight; |
| if( nTmp > Height() ) |
| { |
| KSHORT nAdd = ( nTmp - Height() ) / 2; |
| GetRoot().SetAscent( GetRoot().GetAscent() + nAdd ); |
| GetRoot().Height( GetRoot().Height() + nAdd ); |
| Height( nTmp ); |
| } |
| nTmp = ((SwDoubleLinePortion*)this)->GetBrackets()->nAscent; |
| if( nTmp > GetAscent() ) |
| SetAscent( nTmp ); |
| } |
| } |
| |
| long SwMultiPortion::CalcSpacing( long , const SwTxtSizeInfo & ) const |
| { |
| return 0; |
| } |
| |
| sal_Bool SwMultiPortion::ChgSpaceAdd( SwLineLayout*, long ) const |
| { |
| return sal_False; |
| } |
| |
| /************************************************************************* |
| * virtual SwMultiPortion::HandlePortion() |
| *************************************************************************/ |
| |
| void SwMultiPortion::HandlePortion( SwPortionHandler& rPH ) const |
| { |
| rPH.Text( GetLen(), GetWhichPor() ); |
| } |
| |
| /*-----------------01.11.00 14:21------------------- |
| * SwMultiPortion::ActualizeTabulator() |
| * sets the tabulator-flag, if there's any tabulator-portion inside. |
| * --------------------------------------------------*/ |
| |
| void SwMultiPortion::ActualizeTabulator() |
| { |
| SwLinePortion* pPor = GetRoot().GetFirstPortion(); |
| // First line |
| for( bTab1 = bTab2 = sal_False; pPor; pPor = pPor->GetPortion() ) |
| if( pPor->InTabGrp() ) |
| SetTab1( sal_True ); |
| if( GetRoot().GetNext() ) |
| { |
| // Second line |
| pPor = GetRoot().GetNext()->GetFirstPortion(); |
| do |
| { |
| if( pPor->InTabGrp() ) |
| SetTab2( sal_True ); |
| pPor = pPor->GetPortion(); |
| } while ( pPor ); |
| } |
| } |
| |
| /*-----------------16.02.01 12:07------------------- |
| * SwRotatedPortion::SwRotatedPortion(..) |
| * --------------------------------------------------*/ |
| |
| SwRotatedPortion::SwRotatedPortion( const SwMultiCreator& rCreate, |
| xub_StrLen nEnd, sal_Bool bRTL ) : SwMultiPortion( nEnd ) |
| { |
| const SvxCharRotateItem* pRot = (SvxCharRotateItem*)rCreate.pItem; |
| if( !pRot ) |
| { |
| const SwTxtAttr& rAttr = *rCreate.pAttr; |
| const SfxPoolItem *const pItem = |
| CharFmt::GetItem(rAttr, RES_CHRATR_ROTATE); |
| if ( pItem ) |
| { |
| pRot = static_cast<const SvxCharRotateItem*>(pItem); |
| } |
| } |
| if( pRot ) |
| { |
| sal_uInt8 nDir; |
| if ( bRTL ) |
| nDir = pRot->IsBottomToTop() ? 3 : 1; |
| else |
| nDir = pRot->IsBottomToTop() ? 1 : 3; |
| |
| SetDirection( nDir ); |
| } |
| } |
| |
| /*--------------------------------------------------- |
| * SwBidiPortion::SwBidiPortion(..) |
| * --------------------------------------------------*/ |
| |
| SwBidiPortion::SwBidiPortion( xub_StrLen nEnd, sal_uInt8 nLv ) |
| : SwMultiPortion( nEnd ), nLevel( nLv ) |
| { |
| SetBidi(); |
| |
| if ( nLevel % 2 ) |
| SetDirection( DIR_RIGHT2LEFT ); |
| else |
| SetDirection( DIR_LEFT2RIGHT ); |
| } |
| |
| |
| long SwBidiPortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo& rInf ) const |
| { |
| return HasTabulator() ? 0 : GetSpaceCnt(rInf) * nSpaceAdd / SPACING_PRECISION_FACTOR; |
| } |
| |
| sal_Bool SwBidiPortion::ChgSpaceAdd( SwLineLayout* pCurr, long nSpaceAdd ) const |
| { |
| sal_Bool bRet = sal_False; |
| if( !HasTabulator() && nSpaceAdd > 0 && !pCurr->IsSpaceAdd() ) |
| { |
| pCurr->CreateSpaceAdd(); |
| pCurr->SetLLSpaceAdd( nSpaceAdd, 0 ); |
| bRet = sal_True; |
| } |
| |
| return bRet; |
| } |
| |
| xub_StrLen SwBidiPortion::GetSpaceCnt( const SwTxtSizeInfo &rInf ) const |
| { |
| // Calculate number of blanks for justified alignment |
| SwLinePortion* pPor = GetRoot().GetFirstPortion(); |
| xub_StrLen nTmpStart = rInf.GetIdx(); |
| xub_StrLen nNull = 0; |
| xub_StrLen nBlanks; |
| |
| for( nBlanks = 0; pPor; pPor = pPor->GetPortion() ) |
| { |
| if( pPor->InTxtGrp() ) |
| nBlanks = nBlanks + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull ); |
| else if ( pPor->IsMultiPortion() && |
| ((SwMultiPortion*)pPor)->IsBidi() ) |
| nBlanks = nBlanks + ((SwBidiPortion*)pPor)->GetSpaceCnt( rInf ); |
| |
| ((SwTxtSizeInfo &)rInf).SetIdx( rInf.GetIdx() + pPor->GetLen() ); |
| } |
| ((SwTxtSizeInfo &)rInf).SetIdx( nTmpStart ); |
| return nBlanks; |
| } |
| |
| /*-----------------01.11.00 14:22------------------- |
| * SwDoubleLinePortion::SwDoubleLinePortion(..) |
| * This constructor is for the continuation of a doubleline portion |
| * in the next line. |
| * It takes the same brackets and if the original has no content except |
| * brackets, these will be deleted. |
| * --------------------------------------------------*/ |
| |
| SwDoubleLinePortion::SwDoubleLinePortion( SwDoubleLinePortion& rDouble, |
| xub_StrLen nEnd ) : |
| SwMultiPortion( nEnd ), |
| pBracket( 0 ) |
| { |
| SetDirection( rDouble.GetDirection() ); |
| SetDouble(); |
| if( rDouble.GetBrackets() ) |
| { |
| SetBrackets( rDouble ); |
| // An empty multiportion needs no brackets. |
| // Notice: GetLen() might be zero, if the multiportion contains |
| // the second part of a field and the width might be zero, if |
| // it contains a note only. In this cases the brackets are okay. |
| // But if the length and the width are both zero, the portion |
| // is really empty. |
| if( rDouble.Width() == rDouble.BracketWidth() ) |
| rDouble.ClearBrackets(); |
| } |
| } |
| |
| /*-----------------01.11.00 14:22------------------- |
| * SwDoubleLinePortion::SwDoubleLinePortion(..) |
| * This constructor uses the textattribut to get the right brackets. |
| * The textattribut could be a 2-line-attribute or a character- or |
| * internetstyle, which contains the 2-line-attribute. |
| * --------------------------------------------------*/ |
| |
| SwDoubleLinePortion::SwDoubleLinePortion( const SwMultiCreator& rCreate, |
| xub_StrLen nEnd ) : SwMultiPortion( nEnd ), pBracket( new SwBracket() ) |
| { |
| SetDouble(); |
| const SvxTwoLinesItem* pTwo = (SvxTwoLinesItem*)rCreate.pItem; |
| if( pTwo ) |
| pBracket->nStart = 0; |
| else |
| { |
| const SwTxtAttr& rAttr = *rCreate.pAttr; |
| pBracket->nStart = *rAttr.GetStart(); |
| |
| const SfxPoolItem * const pItem = |
| CharFmt::GetItem( rAttr, RES_CHRATR_TWO_LINES ); |
| if ( pItem ) |
| { |
| pTwo = static_cast<const SvxTwoLinesItem*>(pItem); |
| } |
| } |
| if( pTwo ) |
| { |
| pBracket->cPre = pTwo->GetStartBracket(); |
| pBracket->cPost = pTwo->GetEndBracket(); |
| } |
| else |
| { |
| pBracket->cPre = 0; |
| pBracket->cPost = 0; |
| } |
| sal_uInt8 nTmp = SW_SCRIPTS; |
| if( pBracket->cPre > 255 ) |
| { |
| String aTxt( pBracket->cPre ); |
| nTmp = SwScriptInfo::WhichFont( 0, &aTxt, 0 ); |
| } |
| pBracket->nPreScript = nTmp; |
| nTmp = SW_SCRIPTS; |
| if( pBracket->cPost > 255 ) |
| { |
| String aTxt( pBracket->cPost ); |
| nTmp = SwScriptInfo::WhichFont( 0, &aTxt, 0 ); |
| } |
| pBracket->nPostScript = nTmp; |
| |
| if( !pBracket->cPre && !pBracket->cPost ) |
| { |
| delete pBracket; |
| pBracket = 0; |
| } |
| |
| // double line portions have the same direction as the frame directions |
| if ( rCreate.nLevel % 2 ) |
| SetDirection( DIR_RIGHT2LEFT ); |
| else |
| SetDirection( DIR_LEFT2RIGHT ); |
| } |
| |
| |
| /*-----------------25.10.00 09:51------------------- |
| * SwMultiPortion::PaintBracket paints the wished bracket, |
| * if the multiportion has surrounding brackets. |
| * The X-position of the SwTxtPaintInfo will be modified: |
| * the open bracket sets position behind itself, |
| * the close bracket in front of itself. |
| * --------------------------------------------------*/ |
| |
| void SwDoubleLinePortion::PaintBracket( SwTxtPaintInfo &rInf, |
| long nSpaceAdd, |
| sal_Bool bOpen ) const |
| { |
| sal_Unicode cCh = bOpen ? pBracket->cPre : pBracket->cPost; |
| if( !cCh ) |
| return; |
| KSHORT nChWidth = bOpen ? PreWidth() : PostWidth(); |
| if( !nChWidth ) |
| return; |
| if( !bOpen ) |
| rInf.X( rInf.X() + Width() - PostWidth() + |
| ( nSpaceAdd > 0 ? CalcSpacing( nSpaceAdd, rInf ) : 0 ) ); |
| |
| SwBlankPortion aBlank( cCh, sal_True ); |
| aBlank.SetAscent( pBracket->nAscent ); |
| aBlank.Width( nChWidth ); |
| aBlank.Height( pBracket->nHeight ); |
| { |
| SwFont* pTmpFnt = new SwFont( *rInf.GetFont() ); |
| sal_uInt8 nAct = bOpen ? pBracket->nPreScript : pBracket->nPostScript; |
| if( SW_SCRIPTS > nAct ) |
| pTmpFnt->SetActual( nAct ); |
| pTmpFnt->SetProportion( 100 ); |
| SwFontSave aSave( rInf, pTmpFnt ); |
| aBlank.Paint( rInf ); |
| delete pTmpFnt; |
| } |
| if( bOpen ) |
| rInf.X( rInf.X() + PreWidth() ); |
| } |
| |
| /*-----------------25.10.00 16:26------------------- |
| * SwDoubleLinePortion::SetBrackets creates the bracket-structur |
| * and fills it, if not both characters are 0x00. |
| * --------------------------------------------------*/ |
| |
| void SwDoubleLinePortion::SetBrackets( const SwDoubleLinePortion& rDouble ) |
| { |
| if( rDouble.pBracket ) |
| { |
| pBracket = new SwBracket; |
| pBracket->cPre = rDouble.pBracket->cPre; |
| pBracket->cPost = rDouble.pBracket->cPost; |
| pBracket->nPreScript = rDouble.pBracket->nPreScript; |
| pBracket->nPostScript = rDouble.pBracket->nPostScript; |
| pBracket->nStart = rDouble.pBracket->nStart; |
| } |
| } |
| |
| /*-----------------25.10.00 16:29------------------- |
| * SwDoubleLinePortion::FormatBrackets |
| * calculates the size of the brackets => pBracket, |
| * reduces the nMaxWidth-parameter ( minus bracket-width ) |
| * and moves the rInf-x-position behind the opening bracket. |
| * --------------------------------------------------*/ |
| |
| void SwDoubleLinePortion::FormatBrackets( SwTxtFormatInfo &rInf, SwTwips& nMaxWidth ) |
| { |
| nMaxWidth -= rInf.X(); |
| SwFont* pTmpFnt = new SwFont( *rInf.GetFont() ); |
| pTmpFnt->SetProportion( 100 ); |
| pBracket->nAscent = 0; |
| pBracket->nHeight = 0; |
| if( pBracket->cPre ) |
| { |
| String aStr( pBracket->cPre ); |
| sal_uInt8 nActualScr = pTmpFnt->GetActual(); |
| if( SW_SCRIPTS > pBracket->nPreScript ) |
| pTmpFnt->SetActual( pBracket->nPreScript ); |
| SwFontSave aSave( rInf, pTmpFnt ); |
| SwPosSize aSize = rInf.GetTxtSize( aStr ); |
| pBracket->nAscent = rInf.GetAscent(); |
| pBracket->nHeight = aSize.Height(); |
| pTmpFnt->SetActual( nActualScr ); |
| if( nMaxWidth > aSize.Width() ) |
| { |
| pBracket->nPreWidth = aSize.Width(); |
| nMaxWidth -= aSize.Width(); |
| rInf.X( rInf.X() + aSize.Width() ); |
| } |
| else |
| { |
| pBracket->nPreWidth = 0; |
| nMaxWidth = 0; |
| } |
| } |
| else |
| pBracket->nPreWidth = 0; |
| if( pBracket->cPost ) |
| { |
| String aStr( pBracket->cPost ); |
| if( SW_SCRIPTS > pBracket->nPostScript ) |
| pTmpFnt->SetActual( pBracket->nPostScript ); |
| SwFontSave aSave( rInf, pTmpFnt ); |
| SwPosSize aSize = rInf.GetTxtSize( aStr ); |
| KSHORT nTmpAsc = rInf.GetAscent(); |
| if( nTmpAsc > pBracket->nAscent ) |
| { |
| pBracket->nHeight += nTmpAsc - pBracket->nAscent; |
| pBracket->nAscent = nTmpAsc; |
| } |
| if( aSize.Height() > pBracket->nHeight ) |
| pBracket->nHeight = aSize.Height(); |
| if( nMaxWidth > aSize.Width() ) |
| { |
| pBracket->nPostWidth = aSize.Width(); |
| nMaxWidth -= aSize.Width(); |
| } |
| else |
| { |
| pBracket->nPostWidth = 0; |
| nMaxWidth = 0; |
| } |
| } |
| else |
| pBracket->nPostWidth = 0; |
| nMaxWidth += rInf.X(); |
| } |
| |
| /*-----------------26.10.00 10:36------------------- |
| * SwDoubleLinePortion::CalcBlanks |
| * calculates the number of blanks in each line and |
| * the difference of the width of the two lines. |
| * These results are used from the text adjustment. |
| * --------------------------------------------------*/ |
| |
| void SwDoubleLinePortion::CalcBlanks( SwTxtFormatInfo &rInf ) |
| { |
| SwLinePortion* pPor = GetRoot().GetFirstPortion(); |
| xub_StrLen nNull = 0; |
| xub_StrLen nStart = rInf.GetIdx(); |
| SetTab1( sal_False ); |
| SetTab2( sal_False ); |
| for( nBlank1 = 0; pPor; pPor = pPor->GetPortion() ) |
| { |
| if( pPor->InTxtGrp() ) |
| nBlank1 = nBlank1 + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull ); |
| rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() ); |
| if( pPor->InTabGrp() ) |
| SetTab1( sal_True ); |
| } |
| nLineDiff = GetRoot().Width(); |
| if( GetRoot().GetNext() ) |
| { |
| pPor = GetRoot().GetNext()->GetFirstPortion(); |
| nLineDiff -= GetRoot().GetNext()->Width(); |
| } |
| for( nBlank2 = 0; pPor; pPor = pPor->GetPortion() ) |
| { |
| if( pPor->InTxtGrp() ) |
| nBlank2 = nBlank2 + ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nNull ); |
| rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() ); |
| if( pPor->InTabGrp() ) |
| SetTab2( sal_True ); |
| } |
| rInf.SetIdx( nStart ); |
| } |
| |
| long SwDoubleLinePortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo & ) const |
| { |
| return HasTabulator() ? 0 : GetSpaceCnt() * nSpaceAdd / SPACING_PRECISION_FACTOR; |
| } |
| |
| /*-----------------01.11.00 14:29------------------- |
| * SwDoubleLinePortion::ChangeSpaceAdd(..) |
| * merges the spaces for text adjustment from the inner and outer part. |
| * Inside the doubleline portion the wider line has no spaceadd-array, the |
| * smaller line has such an array to reach width of the wider line. |
| * If the surrounding line has text adjustment and the doubleline portion |
| * contains no tabulator, it is necessary to create/manipulate the inner |
| * space arrays. |
| * --------------------------------------------------*/ |
| |
| sal_Bool SwDoubleLinePortion::ChgSpaceAdd( SwLineLayout* pCurr, |
| long nSpaceAdd ) const |
| { |
| sal_Bool bRet = sal_False; |
| if( !HasTabulator() && nSpaceAdd > 0 ) |
| { |
| if( !pCurr->IsSpaceAdd() ) |
| { |
| // The wider line gets the spaceadd from the surrounding line direct |
| pCurr->CreateSpaceAdd(); |
| pCurr->SetLLSpaceAdd( nSpaceAdd, 0 ); |
| bRet = sal_True; |
| } |
| else |
| { |
| xub_StrLen nMyBlank = GetSmallerSpaceCnt(); |
| xub_StrLen nOther = GetSpaceCnt(); |
| SwTwips nMultiSpace = pCurr->GetLLSpaceAdd( 0 ) * nMyBlank + nOther * nSpaceAdd; |
| |
| if( nMyBlank ) |
| nMultiSpace /= nMyBlank; |
| |
| if( nMultiSpace < KSHRT_MAX * SPACING_PRECISION_FACTOR ) |
| { |
| // pCurr->SetLLSpaceAdd( nMultiSpace, 0 ); |
| // --> FME 2006-07-11 #i65711# SetLLSpaceAdd replaces the first value, |
| // instead we want to insert a new first value: |
| std::vector<long>* pVec = pCurr->GetpLLSpaceAdd(); |
| pVec->insert( pVec->begin(), nMultiSpace ); |
| // <-- |
| bRet = sal_True; |
| } |
| } |
| } |
| return bRet; |
| } |
| /*-----------------01.11.00 14:29------------------- |
| * SwDoubleLinePortion::ResetSpaceAdd(..) |
| * cancels the manipulation from SwDoubleLinePortion::ChangeSpaceAdd(..) |
| * --------------------------------------------------*/ |
| |
| void SwDoubleLinePortion::ResetSpaceAdd( SwLineLayout* pCurr ) |
| { |
| pCurr->RemoveFirstLLSpaceAdd();; |
| if( !pCurr->GetLLSpaceAddCount() ) |
| pCurr->FinishSpaceAdd(); |
| } |
| |
| SwDoubleLinePortion::~SwDoubleLinePortion() |
| { |
| delete pBracket; |
| } |
| |
| /*-----------------13.11.00 14:50------------------- |
| * SwRubyPortion::SwRubyPortion(..) |
| * constructs a ruby portion, i.e. an additional text is displayed |
| * beside the main text, e.g. phonetic characters. |
| * --------------------------------------------------*/ |
| |
| |
| SwRubyPortion::SwRubyPortion( const SwRubyPortion& rRuby, xub_StrLen nEnd ) : |
| SwMultiPortion( nEnd ), |
| nRubyOffset( rRuby.GetRubyOffset() ), |
| nAdjustment( rRuby.GetAdjustment() ) |
| { |
| SetDirection( rRuby.GetDirection() ), |
| SetTop( rRuby.OnTop() ); |
| SetRuby(); |
| } |
| |
| /*-----------------13.11.00 14:50------------------- |
| * SwRubyPortion::SwRubyPortion(..) |
| * constructs a ruby portion, i.e. an additional text is displayed |
| * beside the main text, e.g. phonetic characters. |
| * --------------------------------------------------*/ |
| |
| SwRubyPortion::SwRubyPortion( const SwMultiCreator& rCreate, const SwFont& rFnt, |
| const IDocumentSettingAccess& rIDocumentSettingAccess, |
| xub_StrLen nEnd, xub_StrLen nOffs, |
| const sal_Bool* pForceRubyPos ) |
| : SwMultiPortion( nEnd ) |
| { |
| SetRuby(); |
| ASSERT( SW_MC_RUBY == rCreate.nId, "Ruby expected" ); |
| ASSERT( RES_TXTATR_CJK_RUBY == rCreate.pAttr->Which(), "Wrong attribute" ); |
| const SwFmtRuby& rRuby = rCreate.pAttr->GetRuby(); |
| nAdjustment = rRuby.GetAdjustment(); |
| nRubyOffset = nOffs; |
| |
| // in grid mode we force the ruby text to the upper or lower line |
| if ( pForceRubyPos ) |
| SetTop( *pForceRubyPos ); |
| else |
| SetTop( ! rRuby.GetPosition() ); |
| |
| const SwCharFmt* pFmt = ((SwTxtRuby*)rCreate.pAttr)->GetCharFmt(); |
| SwFont *pRubyFont; |
| if( pFmt ) |
| { |
| const SwAttrSet& rSet = pFmt->GetAttrSet(); |
| pRubyFont = new SwFont( rFnt ); |
| pRubyFont->SetDiffFnt( &rSet, &rIDocumentSettingAccess ); |
| |
| // we do not allow a vertical font for the ruby text |
| pRubyFont->SetVertical( rFnt.GetOrientation() ); |
| } |
| else |
| pRubyFont = NULL; |
| |
| String aStr( rRuby.GetText(), nOffs, STRING_LEN ); |
| SwFldPortion *pFld = new SwFldPortion( aStr, pRubyFont ); |
| pFld->SetNextOffset( nOffs ); |
| pFld->SetFollow( sal_True ); |
| |
| if( OnTop() ) |
| GetRoot().SetPortion( pFld ); |
| else |
| { |
| GetRoot().SetNext( new SwLineLayout() ); |
| GetRoot().GetNext()->SetPortion( pFld ); |
| } |
| |
| // ruby portions have the same direction as the frame directions |
| if ( rCreate.nLevel % 2 ) |
| { |
| // switch right and left ruby adjustment in rtl environment |
| if ( 0 == nAdjustment ) |
| nAdjustment = 2; |
| else if ( 2 == nAdjustment ) |
| nAdjustment = 0; |
| |
| SetDirection( DIR_RIGHT2LEFT ); |
| } |
| else |
| SetDirection( DIR_LEFT2RIGHT ); |
| } |
| |
| /*-----------------13.11.00 14:56------------------- |
| * SwRubyPortion::_Adjust(..) |
| * In ruby portion there are different alignments for |
| * the ruby text and the main text. |
| * Left, right, centered and two possibilities of block adjustment |
| * The block adjustment is realized by spacing between the characteres, |
| * either with a half space or no space in front of the first letter and |
| * a half space at the end of the last letter. |
| * Notice: the smaller line will be manipulated, normally it's the ruby line, |
| * but it could be the main text, too. |
| * If there is a tabulator in smaller line, no adjustment is possible. |
| * --------------------------------------------------*/ |
| |
| void SwRubyPortion::_Adjust( SwTxtFormatInfo &rInf ) |
| { |
| SwTwips nLineDiff = GetRoot().Width() - GetRoot().GetNext()->Width(); |
| xub_StrLen nOldIdx = rInf.GetIdx(); |
| if( !nLineDiff ) |
| return; |
| SwLineLayout *pCurr; |
| if( nLineDiff < 0 ) |
| { // The first line has to be adjusted. |
| if( GetTab1() ) |
| return; |
| pCurr = &GetRoot(); |
| nLineDiff = -nLineDiff; |
| } |
| else |
| { // The second line has to be adjusted. |
| if( GetTab2() ) |
| return; |
| pCurr = GetRoot().GetNext(); |
| rInf.SetIdx( nOldIdx + GetRoot().GetLen() ); |
| } |
| KSHORT nLeft = 0; // the space in front of the first letter |
| KSHORT nRight = 0; // the space at the end of the last letter |
| sal_uInt16 nSub = 0; |
| switch ( nAdjustment ) |
| { |
| case 1: nRight = static_cast<sal_uInt16>(nLineDiff / 2); // no break |
| case 2: nLeft = static_cast<sal_uInt16>(nLineDiff - nRight); break; |
| case 3: nSub = 1; // no break |
| case 4: |
| { |
| xub_StrLen nCharCnt = 0; |
| SwLinePortion *pPor; |
| for( pPor = pCurr->GetFirstPortion(); pPor; pPor = pPor->GetPortion() ) |
| { |
| if( pPor->InTxtGrp() ) |
| ((SwTxtPortion*)pPor)->GetSpaceCnt( rInf, nCharCnt ); |
| rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() ); |
| } |
| if( nCharCnt > nSub ) |
| { |
| SwTwips nCalc = nLineDiff / ( nCharCnt - nSub ); |
| short nTmp; |
| if( nCalc < SHRT_MAX ) |
| nTmp = -short(nCalc); |
| else |
| nTmp = SHRT_MIN; |
| |
| pCurr->CreateSpaceAdd( SPACING_PRECISION_FACTOR * nTmp ); |
| nLineDiff -= nCalc * ( nCharCnt - 1 ); |
| } |
| if( nLineDiff > 1 ) |
| { |
| nRight = static_cast<sal_uInt16>(nLineDiff / 2); |
| nLeft = static_cast<sal_uInt16>(nLineDiff - nRight); |
| } |
| break; |
| } |
| default: ASSERT( sal_False, "New ruby adjustment" ); |
| } |
| if( nLeft || nRight ) |
| { |
| if( !pCurr->GetPortion() ) |
| pCurr->SetPortion( new SwTxtPortion( *pCurr ) ); |
| SwMarginPortion *pMarg = new SwMarginPortion( 0 ); |
| if( nLeft ) |
| { |
| pMarg->AddPrtWidth( nLeft ); |
| pMarg->SetPortion( pCurr->GetPortion() ); |
| pCurr->SetPortion( pMarg ); |
| } |
| if( nRight ) |
| { |
| pMarg = new SwMarginPortion( 0 ); |
| pMarg->AddPrtWidth( nRight ); |
| pCurr->FindLastPortion()->Append( pMarg ); |
| } |
| } |
| |
| pCurr->Width( Width() ); |
| rInf.SetIdx( nOldIdx ); |
| } |
| |
| /*-----------------08.11.00 14:14------------------- |
| * CalcRubyOffset() |
| * has to change the nRubyOffset, if there's a fieldportion |
| * in the phonetic line. |
| * The nRubyOffset is the position in the rubystring, where the |
| * next SwRubyPortion has start the displaying of the phonetics. |
| * --------------------------------------------------*/ |
| |
| void SwRubyPortion::CalcRubyOffset() |
| { |
| const SwLineLayout *pCurr = &GetRoot(); |
| if( !OnTop() ) |
| { |
| pCurr = pCurr->GetNext(); |
| if( !pCurr ) |
| return; |
| } |
| const SwLinePortion *pPor = pCurr->GetFirstPortion(); |
| const SwFldPortion *pFld = NULL; |
| while( pPor ) |
| { |
| if( pPor->InFldGrp() ) |
| pFld = (SwFldPortion*)pPor; |
| pPor = pPor->GetPortion(); |
| } |
| if( pFld ) |
| { |
| if( pFld->HasFollow() ) |
| nRubyOffset = pFld->GetNextOffset(); |
| else |
| nRubyOffset = STRING_LEN; |
| } |
| } |
| |
| /*-----------------13.10.00 16:22------------------- |
| * SwTxtSizeInfo::GetMultiCreator(..) |
| * If we (e.g. the position rPos) are inside a two-line-attribute or |
| * a ruby-attribute, the attribute will be returned in a SwMultiCreator-struct, |
| * otherwise the function returns zero. |
| * The rPos parameter is set to the end of the multiportion, |
| * normally this is the end of the attribute, |
| * but sometimes it is the start of another attribute, which finished or |
| * interrupts the first attribute. |
| * E.g. a ruby portion interrupts a 2-line-attribute, a 2-line-attribute |
| * with different brackets interrupts another 2-line-attribute. |
| * --------------------------------------------------*/ |
| |
| /*-----------------13.11.00 15:38------------------- |
| * lcl_Has2Lines(..) |
| * is a little help function for GetMultiCreator(..) |
| * It extracts the 2-line-format from a 2-line-attribute or a character style. |
| * The rValue is set to sal_True, if the 2-line-attribute's value is set and |
| * no 2-line-format reference is passed. If there is a 2-line-format reference, |
| * then the rValue is set only, if the 2-line-attribute's value is set _and_ |
| * the 2-line-formats has the same brackets. |
| * --------------------------------------------------*/ |
| |
| sal_Bool lcl_Has2Lines( const SwTxtAttr& rAttr, const SvxTwoLinesItem* &rpRef, |
| sal_Bool &rValue ) |
| { |
| const SfxPoolItem* pItem = CharFmt::GetItem( rAttr, RES_CHRATR_TWO_LINES ); |
| if( pItem ) |
| { |
| rValue = ((SvxTwoLinesItem*)pItem)->GetValue(); |
| if( !rpRef ) |
| rpRef = (SvxTwoLinesItem*)pItem; |
| else if( ((SvxTwoLinesItem*)pItem)->GetEndBracket() != |
| rpRef->GetEndBracket() || |
| ((SvxTwoLinesItem*)pItem)->GetStartBracket() != |
| rpRef->GetStartBracket() ) |
| rValue = sal_False; |
| return sal_True; |
| } |
| return sal_False; |
| } |
| |
| /*-----------------16.02.01 16:39------------------- |
| * lcl_HasRotation(..) |
| * is a little help function for GetMultiCreator(..) |
| * It extracts the charrotation from a charrotate-attribute or a character style. |
| * The rValue is set to sal_True, if the charrotate-attribute's value is set and |
| * no charrotate-format reference is passed. |
| * If there is a charrotate-format reference, then the rValue is set only, |
| * if the charrotate-attribute's value is set _and_ identical |
| * to the charrotate-format's value. |
| * --------------------------------------------------*/ |
| |
| sal_Bool lcl_HasRotation( const SwTxtAttr& rAttr, |
| const SvxCharRotateItem* &rpRef, sal_Bool &rValue ) |
| { |
| const SfxPoolItem* pItem = CharFmt::GetItem( rAttr, RES_CHRATR_ROTATE ); |
| if ( pItem ) |
| { |
| rValue = 0 != ((SvxCharRotateItem*)pItem)->GetValue(); |
| if( !rpRef ) |
| rpRef = (SvxCharRotateItem*)pItem; |
| else if( ((SvxCharRotateItem*)pItem)->GetValue() != |
| rpRef->GetValue() ) |
| rValue = sal_False; |
| return sal_True; |
| } |
| |
| return sal_False; |
| } |
| |
| SwMultiCreator* SwTxtSizeInfo::GetMultiCreator( xub_StrLen &rPos, |
| SwMultiPortion* pMulti ) const |
| { |
| SwScriptInfo& rSI = ((SwParaPortion*)GetParaPortion())->GetScriptInfo(); |
| |
| // get the last embedding level |
| sal_uInt8 nCurrLevel; |
| if ( pMulti ) |
| { |
| ASSERT( pMulti->IsBidi(), "Nested MultiPortion is not BidiPortion" ) |
| // level associated with bidi-portion; |
| nCurrLevel = ((SwBidiPortion*)pMulti)->GetLevel(); |
| } |
| else |
| // no nested bidi portion required |
| nCurrLevel = GetTxtFrm()->IsRightToLeft() ? 1 : 0; |
| |
| // check if there is a field at rPos: |
| sal_uInt8 nNextLevel = nCurrLevel; |
| sal_Bool bFldBidi = sal_False; |
| |
| if ( CH_TXTATR_BREAKWORD == GetChar( rPos ) ) |
| { |
| bFldBidi = sal_True; |
| /* |
| // examining the script of the field text should be sufficient |
| // for 99% of all cases |
| XubString aTxt = GetTxtFrm()->GetTxtNode()->GetExpandTxt( rPos, 1 ); |
| |
| if ( pBreakIt->GetBreakIter().is() && aTxt.Len() ) |
| { |
| sal_Bool bFldDir = ( i18n::ScriptType::COMPLEX == |
| pBreakIt->GetRealScriptOfText( aTxt, 0 ) ); |
| sal_Bool bCurrDir = ( 0 != ( nCurrLevel % 2 ) ); |
| if ( bFldDir != bCurrDir ) |
| { |
| nNextLevel = nCurrLevel + 1; |
| bFldBidi = sal_True; |
| } |
| }*/ |
| } |
| else |
| nNextLevel = rSI.DirType( rPos ); |
| |
| if ( GetTxt().Len() != rPos && nNextLevel > nCurrLevel ) |
| { |
| rPos = bFldBidi ? rPos + 1 : rSI.NextDirChg( rPos, &nCurrLevel ); |
| if ( STRING_LEN == rPos ) |
| return NULL; |
| SwMultiCreator *pRet = new SwMultiCreator; |
| pRet->pItem = NULL; |
| pRet->pAttr = NULL; |
| pRet->nId = SW_MC_BIDI; |
| pRet->nLevel = nCurrLevel + 1; |
| return pRet; |
| } |
| |
| // a bidi portion can only contain other bidi portions |
| if ( pMulti ) |
| return NULL; |
| |
| const SvxCharRotateItem* pRotate = NULL; |
| const SfxPoolItem* pRotItem; |
| if( SFX_ITEM_SET == pFrm->GetTxtNode()->GetSwAttrSet(). |
| GetItemState( RES_CHRATR_ROTATE, sal_True, &pRotItem ) && |
| ((SvxCharRotateItem*)pRotItem)->GetValue() ) |
| pRotate = (SvxCharRotateItem*)pRotItem; |
| else |
| pRotItem = NULL; |
| const SvxTwoLinesItem* p2Lines = NULL; |
| const SfxPoolItem* pItem; |
| if( SFX_ITEM_SET == pFrm->GetTxtNode()->GetSwAttrSet(). |
| GetItemState( RES_CHRATR_TWO_LINES, sal_True, &pItem ) && |
| ((SvxTwoLinesItem*)pItem)->GetValue() ) |
| p2Lines = (SvxTwoLinesItem*)pItem; |
| else |
| pItem = NULL; |
| |
| const SwpHints *pHints = pFrm->GetTxtNode()->GetpSwpHints(); |
| if( !pHints && !p2Lines && !pRotate ) |
| return NULL; |
| const SwTxtAttr *pRuby = NULL; |
| sal_Bool bTwo = sal_False; |
| sal_Bool bRot = sal_False; |
| sal_uInt16 n2Lines = USHRT_MAX; |
| sal_uInt16 nRotate = USHRT_MAX; |
| sal_uInt16 nCount = pHints ? pHints->Count() : 0; |
| sal_uInt16 i; |
| for( i = 0; i < nCount; ++i ) |
| { |
| const SwTxtAttr *pTmp = (*pHints)[i]; |
| xub_StrLen nStart = *pTmp->GetStart(); |
| if( rPos < nStart ) |
| break; |
| if( *pTmp->GetAnyEnd() > rPos ) |
| { |
| if( RES_TXTATR_CJK_RUBY == pTmp->Which() ) |
| pRuby = pTmp; |
| else |
| { |
| const SvxCharRotateItem* pRoTmp = NULL; |
| if( lcl_HasRotation( *pTmp, pRoTmp, bRot ) ) |
| { |
| nRotate = bRot ? i : nCount; |
| pRotate = pRoTmp; |
| } |
| const SvxTwoLinesItem* p2Tmp = NULL; |
| if( lcl_Has2Lines( *pTmp, p2Tmp, bTwo ) ) |
| { |
| n2Lines = bTwo ? i : nCount; |
| p2Lines = p2Tmp; |
| } |
| } |
| } |
| } |
| if( pRuby ) |
| { // The winner is ... a ruby attribute and so |
| // the end of the multiportion is the end of the ruby attribute. |
| rPos = *pRuby->End(); |
| SwMultiCreator *pRet = new SwMultiCreator; |
| pRet->pItem = NULL; |
| pRet->pAttr = pRuby; |
| pRet->nId = SW_MC_RUBY; |
| pRet->nLevel = GetTxtFrm()->IsRightToLeft() ? 1 : 0; |
| return pRet; |
| } |
| if( n2Lines < nCount || ( pItem && pItem == p2Lines && |
| rPos < GetTxt().Len() ) ) |
| { // The winner is a 2-line-attribute, |
| // the end of the multiportion depends on the following attributes... |
| SwMultiCreator *pRet = new SwMultiCreator; |
| |
| // We note the endpositions of the 2-line attributes in aEnd as stack |
| SvXub_StrLens aEnd; |
| |
| // The bOn flag signs the state of the last 2-line attribute in the |
| // aEnd-stack, it is compatible with the winner-attribute or |
| // it interrupts the other attribute. |
| sal_Bool bOn = sal_True; |
| |
| if( n2Lines < nCount ) |
| { |
| pRet->pItem = NULL; |
| pRet->pAttr = (*pHints)[n2Lines]; |
| aEnd.push_front( *pRet->pAttr->End() ); |
| if( pItem ) |
| { |
| aEnd.front() = GetTxt().Len(); |
| bOn = ((SvxTwoLinesItem*)pItem)->GetEndBracket() == |
| p2Lines->GetEndBracket() && |
| ((SvxTwoLinesItem*)pItem)->GetStartBracket() == |
| p2Lines->GetStartBracket(); |
| } |
| } |
| else |
| { |
| pRet->pItem = pItem; |
| pRet->pAttr = NULL; |
| aEnd.push_front( GetTxt().Len() ); |
| } |
| pRet->nId = SW_MC_DOUBLE; |
| pRet->nLevel = GetTxtFrm()->IsRightToLeft() ? 1 : 0; |
| |
| // n2Lines is the index of the last 2-line-attribute, which contains |
| // the actual position. |
| i = 0; |
| // At this moment we know that at position rPos the "winner"-attribute |
| // causes a 2-line-portion. The end of the attribute is the end of the |
| // portion, if there's no interrupting attribute. |
| // There are two kinds of interruptors: |
| // - ruby attributes stops the 2-line-attribute, the end of the |
| // multiline is the start of the ruby attribute |
| // - 2-line-attributes with value "Off" or with different brackets, |
| // these attributes may interrupt the winner, but they could be |
| // neutralized by another 2-line-attribute starting at the same |
| // position with the same brackets as the winner-attribute. |
| |
| // In the following loop rPos is the critical position and it will be |
| // evaluated, if at rPos starts a interrupting or a maintaining |
| // continuity attribute. |
| while( i < nCount ) |
| { |
| const SwTxtAttr *pTmp = (*pHints)[i++]; |
| if( *pTmp->GetAnyEnd() <= rPos ) |
| continue; |
| if( rPos < *pTmp->GetStart() ) |
| { |
| // If bOn is sal_False and the next attribute starts later than rPos |
| // the winner attribute is interrupted at rPos. |
| // If the start of the next attribute is behind the end of |
| // the last attribute on the aEnd-stack, this is the endposition |
| // on the stack is the end of the 2-line portion. |
| if( !bOn || aEnd.back() < *pTmp->GetStart() ) |
| break; |
| // At this moment, bOn is sal_True and the next attribute starts |
| // behind rPos, so we could move rPos to the next startpoint |
| rPos = *pTmp->GetStart(); |
| // We clean up the aEnd-stack, endpositions equal to rPos are |
| // superfluous. |
| while( !aEnd.empty() && aEnd.back() <= rPos ) |
| { |
| bOn = !bOn; |
| aEnd.pop_back(); |
| } |
| // If the endstack is empty, we simulate an attribute with |
| // state sal_True and endposition rPos |
| if( aEnd.empty() ) |
| { |
| aEnd.push_front( rPos ); |
| bOn = sal_True; |
| } |
| } |
| // A ruby attribute stops the 2-line immediately |
| if( RES_TXTATR_CJK_RUBY == pTmp->Which() ) |
| return pRet; |
| if( lcl_Has2Lines( *pTmp, p2Lines, bTwo ) ) |
| { // We have an interesting attribute.. |
| if( bTwo == bOn ) |
| { // .. with the same state, so the last attribute could |
| // be continued. |
| if( aEnd.back() < *pTmp->End() ) |
| aEnd.back() = *pTmp->End(); |
| } |
| else |
| { // .. with a different state. |
| bOn = bTwo; |
| // If this is smaller than the last on the stack, we put |
| // it on the stack. If it has the same endposition, the last |
| // could be removed. |
| if( aEnd.back() > *pTmp->End() ) |
| aEnd.push_back( *pTmp->End() ); |
| else if( aEnd.size() > 1 ) |
| aEnd.pop_back(); |
| else |
| aEnd.back() = *pTmp->End(); |
| } |
| } |
| } |
| if( bOn && !aEnd.empty() ) |
| rPos = aEnd.back(); |
| return pRet; |
| } |
| if( nRotate < nCount || ( pRotItem && pRotItem == pRotate && |
| rPos < GetTxt().Len() ) ) |
| { // The winner is a rotate-attribute, |
| // the end of the multiportion depends on the following attributes... |
| SwMultiCreator *pRet = new SwMultiCreator; |
| pRet->nId = SW_MC_ROTATE; |
| |
| // We note the endpositions of the 2-line attributes in aEnd as stack |
| SvXub_StrLens aEnd; |
| |
| // The bOn flag signs the state of the last 2-line attribute in the |
| // aEnd-stack, which could interrupts the winning rotation attribute. |
| sal_Bool bOn = pItem ? sal_True : sal_False; |
| aEnd.push_front( GetTxt().Len() ); |
| // n2Lines is the index of the last 2-line-attribute, which contains |
| // the actual position. |
| i = 0; |
| xub_StrLen n2Start = rPos; |
| while( i < nCount ) |
| { |
| const SwTxtAttr *pTmp = (*pHints)[i++]; |
| if( *pTmp->GetAnyEnd() <= n2Start ) |
| continue; |
| if( n2Start < *pTmp->GetStart() ) |
| { |
| if( bOn || aEnd.back() < *pTmp->GetStart() ) |
| break; |
| n2Start = *pTmp->GetStart(); |
| while( !aEnd.empty() && aEnd.back() <= n2Start ) |
| { |
| bOn = !bOn; |
| aEnd.pop_back(); |
| } |
| if( aEnd.empty() ) |
| { |
| aEnd.push_front( n2Start ); |
| bOn = sal_False; |
| } |
| } |
| // A ruby attribute stops immediately |
| if( RES_TXTATR_CJK_RUBY == pTmp->Which() ) |
| { |
| bOn = sal_True; |
| break; |
| } |
| p2Lines = NULL; |
| if( lcl_Has2Lines( *pTmp, p2Lines, bTwo ) ) |
| { |
| if( bTwo == bOn ) |
| { |
| if( aEnd.back() < *pTmp->End() ) |
| aEnd.back() = *pTmp->End(); |
| } |
| else |
| { |
| bOn = bTwo; |
| if( aEnd.back() > *pTmp->End() ) |
| aEnd.push_back( *pTmp->End() ); |
| else if( aEnd.size() > 1 ) |
| aEnd.pop_back(); |
| else |
| aEnd.back() = *pTmp->End(); |
| } |
| } |
| } |
| if( !bOn && !aEnd.empty() ) |
| n2Start = aEnd.back(); |
| |
| if( !aEnd.empty() ) |
| aEnd.clear(); |
| |
| bOn = sal_True; |
| if( nRotate < nCount ) |
| { |
| pRet->pItem = NULL; |
| pRet->pAttr = (*pHints)[nRotate]; |
| aEnd.push_front( *pRet->pAttr->End() ); |
| if( pRotItem ) |
| { |
| aEnd.front() = GetTxt().Len(); |
| bOn = ((SvxCharRotateItem*)pRotItem)->GetValue() == |
| pRotate->GetValue(); |
| } |
| } |
| else |
| { |
| pRet->pItem = pRotItem; |
| pRet->pAttr = NULL; |
| aEnd.push_front( GetTxt().Len() ); |
| } |
| i = 0; |
| while( i < nCount ) |
| { |
| const SwTxtAttr *pTmp = (*pHints)[i++]; |
| if( *pTmp->GetAnyEnd() <= rPos ) |
| continue; |
| if( rPos < *pTmp->GetStart() ) |
| { |
| if( !bOn || aEnd.back() < *pTmp->GetStart() ) |
| break; |
| rPos = *pTmp->GetStart(); |
| while( !aEnd.empty() && aEnd.back() <= rPos ) |
| { |
| bOn = !bOn; |
| aEnd.pop_back(); |
| } |
| if( aEnd.empty() ) |
| { |
| aEnd.push_front( rPos ); |
| bOn = sal_True; |
| } |
| } |
| if( RES_TXTATR_CJK_RUBY == pTmp->Which() ) |
| { |
| bOn = sal_False; |
| break; |
| } |
| if( lcl_HasRotation( *pTmp, pRotate, bTwo ) ) |
| { |
| if( bTwo == bOn ) |
| { |
| if( aEnd.back() < *pTmp->End() ) |
| aEnd.back() = *pTmp->End(); |
| } |
| else |
| { |
| bOn = bTwo; |
| if( aEnd.back() > *pTmp->End() ) |
| aEnd.push_back( *pTmp->End() ); |
| else if( aEnd.size() > 1 ) |
| aEnd.pop_back(); |
| else |
| aEnd.back() = *pTmp->End(); |
| } |
| } |
| } |
| if( bOn && !aEnd.empty() ) |
| rPos = aEnd.back(); |
| if( rPos > n2Start ) |
| rPos = n2Start; |
| return pRet; |
| } |
| return NULL; |
| } |
| |
| /*-----------------01.11.00 14:52------------------- |
| * SwSpaceManipulator |
| * is a little helper class to manage the spaceadd-arrays of the text adjustment |
| * during a PaintMultiPortion. |
| * The constructor prepares the array for the first line of multiportion, |
| * the SecondLine-function restores the values for the first line and prepares |
| * the second line. |
| * The destructor restores the values of the last manipulation. |
| * --------------------------------------------------*/ |
| |
| class SwSpaceManipulator |
| { |
| SwTxtPaintInfo& rInfo; |
| SwMultiPortion& rMulti; |
| std::vector<long>* pOldSpaceAdd; |
| MSHORT nOldSpIdx; |
| long nSpaceAdd; |
| sal_Bool bSpaceChg : 1; |
| sal_uInt8 nOldDir : 2; |
| public: |
| SwSpaceManipulator( SwTxtPaintInfo& rInf, SwMultiPortion& rMult ); |
| ~SwSpaceManipulator(); |
| void SecondLine(); |
| inline long GetSpaceAdd() const { return nSpaceAdd; } |
| }; |
| |
| SwSpaceManipulator::SwSpaceManipulator( SwTxtPaintInfo& rInf, |
| SwMultiPortion& rMult ) : |
| rInfo( rInf ), rMulti( rMult ) |
| { |
| pOldSpaceAdd = rInfo.GetpSpaceAdd(); |
| nOldSpIdx = rInfo.GetSpaceIdx(); |
| nOldDir = rInfo.GetDirection(); |
| rInfo.SetDirection( rMulti.GetDirection() ); |
| bSpaceChg = sal_False; |
| |
| if( rMulti.IsDouble() ) |
| { |
| nSpaceAdd = ( pOldSpaceAdd && !rMulti.HasTabulator() ) ? |
| rInfo.GetSpaceAdd() : 0; |
| if( rMulti.GetRoot().IsSpaceAdd() ) |
| { |
| rInfo.SetpSpaceAdd( rMulti.GetRoot().GetpLLSpaceAdd() ); |
| rInfo.ResetSpaceIdx(); |
| bSpaceChg = rMulti.ChgSpaceAdd( &rMulti.GetRoot(), nSpaceAdd ); |
| } |
| else if( rMulti.HasTabulator() ) |
| rInfo.SetpSpaceAdd( NULL ); |
| } |
| else if ( ! rMulti.IsBidi() ) |
| { |
| rInfo.SetpSpaceAdd( rMulti.GetRoot().GetpLLSpaceAdd() ); |
| rInfo.ResetSpaceIdx(); |
| } |
| } |
| |
| void SwSpaceManipulator::SecondLine() |
| { |
| if( bSpaceChg ) |
| { |
| rInfo.RemoveFirstSpaceAdd(); |
| bSpaceChg = sal_False; |
| } |
| SwLineLayout *pLay = rMulti.GetRoot().GetNext(); |
| if( pLay->IsSpaceAdd() ) |
| { |
| rInfo.SetpSpaceAdd( pLay->GetpLLSpaceAdd() ); |
| rInfo.ResetSpaceIdx(); |
| bSpaceChg = rMulti.ChgSpaceAdd( pLay, nSpaceAdd ); |
| } |
| else |
| { |
| rInfo.SetpSpaceAdd( (!rMulti.IsDouble() || rMulti.HasTabulator() ) ? |
| 0 : pOldSpaceAdd ); |
| rInfo.SetSpaceIdx( nOldSpIdx); |
| } |
| } |
| |
| SwSpaceManipulator::~SwSpaceManipulator() |
| { |
| if( bSpaceChg ) |
| { |
| rInfo.RemoveFirstSpaceAdd(); |
| bSpaceChg = sal_False; |
| } |
| rInfo.SetpSpaceAdd( pOldSpaceAdd ); |
| rInfo.SetSpaceIdx( nOldSpIdx); |
| rInfo.SetDirection( nOldDir ); |
| } |
| |
| /*-----------------13.10.00 16:24------------------- |
| * SwTxtPainter::PaintMultiPortion manages the paint for a SwMultiPortion. |
| * External, for the calling function, it seems to be a normal Paint-function, |
| * internal it is like a SwTxtFrm::Paint with multiple DrawTextLines |
| * --------------------------------------------------*/ |
| |
| void SwTxtPainter::PaintMultiPortion( const SwRect &rPaint, |
| SwMultiPortion& rMulti, const SwMultiPortion* pEnvPor ) |
| { |
| GETGRID( pFrm->FindPageFrm() ) |
| const sal_Bool bHasGrid = pGrid && GetInfo().SnapToGrid(); |
| sal_uInt16 nGridWidth = 0; |
| sal_uInt16 nRubyHeight = 0; |
| sal_Bool bRubyTop = sal_False; |
| |
| if ( bHasGrid ) |
| { |
| nGridWidth = pGrid->GetBaseHeight(); |
| nRubyHeight = pGrid->GetRubyHeight(); |
| bRubyTop = ! pGrid->GetRubyTextBelow(); |
| } |
| |
| // do not allow grid mode for first line in ruby portion |
| const sal_Bool bRubyInGrid = bHasGrid && rMulti.IsRuby(); |
| |
| const sal_uInt16 nOldHeight = rMulti.Height(); |
| const sal_Bool bOldGridModeAllowed = GetInfo().SnapToGrid(); |
| |
| if ( bRubyInGrid ) |
| { |
| GetInfo().SetSnapToGrid( ! bRubyTop ); |
| rMulti.Height( pCurr->Height() ); |
| } |
| |
| SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() ); |
| sal_uInt8 nEnvDir = 0; |
| sal_uInt8 nThisDir = 0; |
| sal_uInt8 nFrmDir = 0; |
| if ( rMulti.IsBidi() ) |
| { |
| // these values are needed for the calculation of the x coordinate |
| // and the layout mode |
| ASSERT( ! pEnvPor || pEnvPor->IsBidi(), |
| "Oh no, I expected a BidiPortion" ) |
| nFrmDir = GetInfo().GetTxtFrm()->IsRightToLeft() ? 1 : 0; |
| nEnvDir = pEnvPor ? ((SwBidiPortion*)pEnvPor)->GetLevel() % 2 : nFrmDir; |
| nThisDir = ((SwBidiPortion&)rMulti).GetLevel() % 2; |
| } |
| |
| #if OSL_DEBUG_LEVEL > 1 |
| // only paint first level bidi portions |
| if( rMulti.Width() > 1 && ! pEnvPor ) |
| GetInfo().DrawViewOpt( rMulti, POR_FLD ); |
| #endif |
| |
| if ( bRubyInGrid ) |
| rMulti.Height( nOldHeight ); |
| |
| // do we have to repaint a post it portion? |
| if( GetInfo().OnWin() && rMulti.GetPortion() && |
| ! rMulti.GetPortion()->Width() ) |
| rMulti.GetPortion()->PrePaint( GetInfo(), &rMulti ); |
| |
| // old values must be saved and restored at the end |
| xub_StrLen nOldLen = GetInfo().GetLen(); |
| KSHORT nOldX = KSHORT(GetInfo().X()); |
| long nOldY = GetInfo().Y(); |
| xub_StrLen nOldIdx = GetInfo().GetIdx(); |
| |
| SwSpaceManipulator aManip( GetInfo(), rMulti ); |
| |
| SwFontSave *pFontSave; |
| SwFont* pTmpFnt; |
| |
| if( rMulti.IsDouble() ) |
| { |
| pTmpFnt = new SwFont( *GetInfo().GetFont() ); |
| if( rMulti.IsDouble() ) |
| { |
| SetPropFont( 50 ); |
| pTmpFnt->SetProportion( GetPropFont() ); |
| } |
| pFontSave = new SwFontSave( GetInfo(), pTmpFnt, this ); |
| } |
| else |
| { |
| pFontSave = NULL; |
| pTmpFnt = NULL; |
| } |
| |
| if( rMulti.HasBrackets() ) |
| { |
| xub_StrLen nTmpOldIdx = GetInfo().GetIdx(); |
| GetInfo().SetIdx(((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart); |
| SeekAndChg( GetInfo() ); |
| ((SwDoubleLinePortion&)rMulti).PaintBracket( GetInfo(), 0, sal_True ); |
| GetInfo().SetIdx( nTmpOldIdx ); |
| } |
| |
| KSHORT nTmpX = KSHORT(GetInfo().X()); |
| |
| SwLineLayout* pLay = &rMulti.GetRoot();// the first line of the multiportion |
| SwLinePortion* pPor = pLay->GetFirstPortion();//first portion of these line |
| SwTwips nOfst = 0; |
| |
| // GetInfo().Y() is the baseline from the surrounding line. We must switch |
| // this temporary to the baseline of the inner lines of the multiportion. |
| if( rMulti.HasRotation() ) |
| { |
| if( rMulti.IsRevers() ) |
| { |
| GetInfo().Y( nOldY - rMulti.GetAscent() ); |
| nOfst = nTmpX + rMulti.Width(); |
| } |
| else |
| { |
| GetInfo().Y( nOldY - rMulti.GetAscent() + rMulti.Height() ); |
| nOfst = nTmpX; |
| } |
| } |
| else if ( rMulti.IsBidi() ) |
| { |
| // does the current bidi portion has the same direction |
| // as its environment? |
| if ( nEnvDir != nThisDir ) |
| { |
| // different directions, we have to adjust the x coordinate |
| SwTwips nMultiWidth = rMulti.Width() + |
| rMulti.CalcSpacing( GetInfo().GetSpaceAdd(), GetInfo() ); |
| |
| if ( nFrmDir == nThisDir ) |
| GetInfo().X( GetInfo().X() - nMultiWidth ); |
| else |
| GetInfo().X( GetInfo().X() + nMultiWidth ); |
| } |
| |
| nOfst = nOldY - rMulti.GetAscent(); |
| |
| // set layout mode |
| aLayoutModeModifier.Modify( nThisDir ); |
| } |
| else |
| nOfst = nOldY - rMulti.GetAscent(); |
| |
| sal_Bool bRest = pLay->IsRest(); |
| sal_Bool bFirst = sal_True; |
| |
| ASSERT( 0 == GetInfo().GetUnderFnt() || rMulti.IsBidi(), |
| " Only BiDi portions are allowed to use the common underlining font" ) |
| |
| do |
| { |
| if ( bHasGrid ) |
| { |
| if( rMulti.HasRotation() ) |
| { |
| const sal_uInt16 nAdjustment = ( pLay->Height() - pPor->Height() ) / 2 + |
| pPor->GetAscent(); |
| if( rMulti.IsRevers() ) |
| GetInfo().X( nOfst - nAdjustment ); |
| else |
| GetInfo().X( nOfst + nAdjustment ); |
| } |
| else |
| { |
| // special treatment for ruby portions in grid mode |
| SwTwips nAdjustment = 0; |
| if ( rMulti.IsRuby() ) |
| { |
| if ( bRubyTop != ( pLay == &rMulti.GetRoot() ) ) |
| // adjust base text |
| nAdjustment = ( pCurr->Height() - nRubyHeight - pPor->Height() ) / 2; |
| else if ( bRubyTop ) |
| // adjust upper ruby text |
| nAdjustment = nRubyHeight - pPor->Height(); |
| // else adjust lower ruby text |
| } |
| |
| GetInfo().Y( nOfst + nAdjustment + pPor->GetAscent() ); |
| } |
| } |
| else if( rMulti.HasRotation() ) |
| { |
| if( rMulti.IsRevers() ) |
| GetInfo().X( nOfst - AdjustBaseLine( *pLay, pPor, 0, 0, sal_True ) ); |
| else |
| GetInfo().X( nOfst + AdjustBaseLine( *pLay, pPor ) ); |
| } |
| else |
| GetInfo().Y( nOfst + AdjustBaseLine( *pLay, pPor ) ); |
| |
| sal_Bool bSeeked = sal_True; |
| GetInfo().SetLen( pPor->GetLen() ); |
| |
| if( bRest && pPor->InFldGrp() && !pPor->GetLen() ) |
| { |
| if( ((SwFldPortion*)pPor)->HasFont() ) |
| bSeeked = sal_False; |
| else |
| SeekAndChgBefore( GetInfo() ); |
| } |
| else if( pPor->InTxtGrp() || pPor->InFldGrp() || pPor->InTabGrp() ) |
| SeekAndChg( GetInfo() ); |
| else if ( !bFirst && pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() ) |
| { |
| if( GetRedln() ) |
| SeekAndChg( GetInfo() ); |
| else |
| SeekAndChgBefore( GetInfo() ); |
| } |
| else |
| bSeeked = sal_False; |
| |
| SwLinePortion *pNext = pPor->GetPortion(); |
| if(GetInfo().OnWin() && pNext && !pNext->Width() ) |
| { |
| if ( !bSeeked ) |
| SeekAndChg( GetInfo() ); |
| pNext->PrePaint( GetInfo(), pPor ); |
| } |
| |
| CheckSpecialUnderline( pPor ); |
| SwUnderlineFont* pUnderLineFnt = GetInfo().GetUnderFnt(); |
| if ( pUnderLineFnt ) |
| { |
| if ( rMulti.IsDouble() ) |
| pUnderLineFnt->GetFont().SetProportion( 50 ); |
| pUnderLineFnt->SetPos( GetInfo().GetPos() ); |
| } |
| |
| if ( rMulti.IsBidi() ) |
| { |
| // we do not allow any rotation inside a bidi portion |
| SwFont* pTmpFont = GetInfo().GetFont(); |
| pTmpFont->SetVertical( 0, GetInfo().GetTxtFrm()->IsVertical() ); |
| } |
| |
| if( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi() ) |
| { |
| // but we do allow nested bidi portions |
| ASSERT( rMulti.IsBidi(), "Only nesting of bidi portions is allowed" ) |
| PaintMultiPortion( rPaint, (SwMultiPortion&)*pPor, &rMulti ); |
| } |
| else |
| pPor->Paint( GetInfo() ); |
| |
| if( GetFnt()->IsURL() && pPor->InTxtGrp() ) |
| GetInfo().NotifyURL( *pPor ); |
| |
| bFirst &= !pPor->GetLen(); |
| if( pNext || !pPor->IsMarginPortion() ) |
| pPor->Move( GetInfo() ); |
| |
| pPor = pNext; |
| |
| // If there's no portion left, we go to the next line |
| if( !pPor && pLay->GetNext() ) |
| { |
| pLay = pLay->GetNext(); |
| pPor = pLay->GetFirstPortion(); |
| bRest = pLay->IsRest(); |
| aManip.SecondLine(); |
| |
| // delete underline font |
| delete GetInfo().GetUnderFnt(); |
| GetInfo().SetUnderFnt( 0 ); |
| |
| if( rMulti.HasRotation() ) |
| { |
| if( rMulti.IsRevers() ) |
| { |
| nOfst += pLay->Height(); |
| GetInfo().Y( nOldY - rMulti.GetAscent() ); |
| } |
| else |
| { |
| nOfst -= pLay->Height(); |
| GetInfo().Y( nOldY - rMulti.GetAscent() + rMulti.Height() ); |
| } |
| } |
| else if ( bHasGrid && rMulti.IsRuby() ) |
| { |
| GetInfo().X( nTmpX ); |
| if ( bRubyTop ) |
| { |
| nOfst += nRubyHeight; |
| GetInfo().SetSnapToGrid( sal_True ); |
| } |
| else |
| { |
| nOfst += pCurr->Height() - nRubyHeight; |
| GetInfo().SetSnapToGrid( sal_False ); |
| } |
| } else |
| { |
| GetInfo().X( nTmpX ); |
| // We switch to the baseline of the next inner line |
| nOfst += rMulti.GetRoot().Height(); |
| } |
| } |
| } while( pPor ); |
| |
| if ( bRubyInGrid ) |
| GetInfo().SetSnapToGrid( bOldGridModeAllowed ); |
| |
| // delete underline font |
| if ( ! rMulti.IsBidi() ) |
| { |
| delete GetInfo().GetUnderFnt(); |
| GetInfo().SetUnderFnt( 0 ); |
| } |
| |
| GetInfo().SetIdx( nOldIdx ); |
| GetInfo().Y( nOldY ); |
| |
| if( rMulti.HasBrackets() ) |
| { |
| xub_StrLen nTmpOldIdx = GetInfo().GetIdx(); |
| GetInfo().SetIdx(((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart); |
| SeekAndChg( GetInfo() ); |
| GetInfo().X( nOldX ); |
| ((SwDoubleLinePortion&)rMulti).PaintBracket( GetInfo(), |
| aManip.GetSpaceAdd(), sal_False ); |
| GetInfo().SetIdx( nTmpOldIdx ); |
| } |
| // Restore the saved values |
| GetInfo().X( nOldX ); |
| GetInfo().SetLen( nOldLen ); |
| delete pFontSave; |
| delete pTmpFnt; |
| SetPropFont( 0 ); |
| } |
| |
| sal_Bool lcl_ExtractFieldFollow( SwLineLayout* pLine, SwLinePortion* &rpFld ) |
| { |
| SwLinePortion* pLast = pLine; |
| rpFld = pLine->GetPortion(); |
| while( rpFld && !rpFld->InFldGrp() ) |
| { |
| pLast = rpFld; |
| rpFld = rpFld->GetPortion(); |
| } |
| sal_Bool bRet = rpFld != 0; |
| if( bRet ) |
| { |
| if( ((SwFldPortion*)rpFld)->IsFollow() ) |
| { |
| rpFld->Truncate(); |
| pLast->SetPortion( NULL ); |
| } |
| else |
| rpFld = NULL; |
| } |
| pLine->Truncate(); |
| return bRet; |
| } |
| |
| /*---------------------------------------------------- |
| * lcl_TruncateMultiPortion |
| * If a multi portion completely has to go to the |
| * next line, this function is called to trunctate |
| * the rest of the remaining multi portion |
| * --------------------------------------------------*/ |
| |
| void lcl_TruncateMultiPortion( SwMultiPortion& rMulti, SwTxtFormatInfo& rInf, |
| xub_StrLen nStartIdx ) |
| { |
| rMulti.GetRoot().Truncate(); |
| rMulti.GetRoot().SetLen(0); |
| rMulti.GetRoot().Width(0); |
| // rMulti.CalcSize( *this, aInf ); |
| if ( rMulti.GetRoot().GetNext() ) |
| { |
| rMulti.GetRoot().GetNext()->Truncate(); |
| rMulti.GetRoot().GetNext()->SetLen( 0 ); |
| rMulti.GetRoot().GetNext()->Width( 0 ); |
| } |
| rMulti.Width( 0 ); |
| rMulti.SetLen(0); |
| rInf.SetIdx( nStartIdx ); |
| } |
| |
| /*----------------------------------------------------------------------------- |
| * SwTxtFormatter::BuildMultiPortion |
| * manages the formatting of a SwMultiPortion. External, for the calling |
| * function, it seems to be a normal Format-function, internal it is like a |
| * SwTxtFrm::_Format with multiple BuildPortions |
| *---------------------------------------------------------------------------*/ |
| |
| sal_Bool SwTxtFormatter::BuildMultiPortion( SwTxtFormatInfo &rInf, |
| SwMultiPortion& rMulti ) |
| { |
| SwTwips nMaxWidth = rInf.Width(); |
| KSHORT nOldX = 0; |
| |
| if( rMulti.HasBrackets() ) |
| { |
| xub_StrLen nOldIdx = rInf.GetIdx(); |
| rInf.SetIdx( ((SwDoubleLinePortion&)rMulti).GetBrackets()->nStart ); |
| SeekAndChg( rInf ); |
| nOldX = KSHORT(GetInfo().X()); |
| ((SwDoubleLinePortion&)rMulti).FormatBrackets( rInf, nMaxWidth ); |
| rInf.SetIdx( nOldIdx ); |
| } |
| |
| SeekAndChg( rInf ); |
| SwFontSave *pFontSave; |
| if( rMulti.IsDouble() ) |
| { |
| SwFont* pTmpFnt = new SwFont( *rInf.GetFont() ); |
| if( rMulti.IsDouble() ) |
| { |
| SetPropFont( 50 ); |
| pTmpFnt->SetProportion( GetPropFont() ); |
| } |
| pFontSave = new SwFontSave( rInf, pTmpFnt, this ); |
| } |
| else |
| pFontSave = NULL; |
| |
| SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() ); |
| if ( rMulti.IsBidi() ) |
| { |
| // set layout mode |
| aLayoutModeModifier.Modify( ! rInf.GetTxtFrm()->IsRightToLeft() ); |
| } |
| |
| SwTwips nTmpX = 0; |
| |
| if( rMulti.HasRotation() ) |
| { |
| // For nMaxWidth we take the height of the body frame. |
| // #i25067#: If the current frame is inside a table, we restrict |
| // nMaxWidth to the current frame height, unless the frame size |
| // attribute is set to variable size: |
| |
| // We set nTmpX (which is used for portion calculating) to the |
| // current Y value |
| const SwPageFrm* pPage = pFrm->FindPageFrm(); |
| ASSERT( pPage, "No page in frame!"); |
| const SwLayoutFrm* pUpperFrm = pPage; |
| |
| if ( pFrm->IsInTab() ) |
| { |
| pUpperFrm = pFrm->GetUpper(); |
| while ( pUpperFrm && !pUpperFrm->IsCellFrm() ) |
| pUpperFrm = pUpperFrm->GetUpper(); |
| ASSERT( pUpperFrm, "pFrm is in table but does not have an upper cell frame" ) |
| const SwTableLine* pLine = ((SwRowFrm*)pUpperFrm->GetUpper())->GetTabLine(); |
| const SwFmtFrmSize& rFrmFmtSize = pLine->GetFrmFmt()->GetFrmSize(); |
| if ( ATT_VAR_SIZE == rFrmFmtSize.GetHeightSizeType() ) |
| pUpperFrm = pPage; |
| } |
| if ( pUpperFrm == pPage && !pFrm->IsInFtn() ) |
| pUpperFrm = pPage->FindBodyCont(); |
| |
| nMaxWidth = pUpperFrm ? |
| ( rInf.GetTxtFrm()->IsVertical() ? |
| pUpperFrm->Prt().Width() : |
| pUpperFrm->Prt().Height() ) : |
| USHRT_MAX; |
| } |
| else |
| nTmpX = rInf.X(); |
| |
| SwMultiPortion* pOldMulti = pMulti; |
| |
| pMulti = &rMulti; |
| SwLineLayout *pOldCurr = pCurr; |
| xub_StrLen nOldStart = GetStart(); |
| SwTwips nMinWidth = nTmpX + 1; |
| SwTwips nActWidth = nMaxWidth; |
| const xub_StrLen nStartIdx = rInf.GetIdx(); |
| xub_StrLen nMultiLen = rMulti.GetLen(); |
| |
| SwLinePortion *pFirstRest; |
| SwLinePortion *pSecondRest; |
| if( rMulti.IsFormatted() ) |
| { |
| if( !lcl_ExtractFieldFollow( &rMulti.GetRoot(), pFirstRest ) |
| && rMulti.IsDouble() && rMulti.GetRoot().GetNext() ) |
| lcl_ExtractFieldFollow( rMulti.GetRoot().GetNext(), pFirstRest ); |
| if( !rMulti.IsDouble() && rMulti.GetRoot().GetNext() ) |
| lcl_ExtractFieldFollow( rMulti.GetRoot().GetNext(), pSecondRest ); |
| else |
| pSecondRest = NULL; |
| } |
| else |
| { |
| pFirstRest = rMulti.GetRoot().GetPortion(); |
| pSecondRest = rMulti.GetRoot().GetNext() ? |
| rMulti.GetRoot().GetNext()->GetPortion() : NULL; |
| if( pFirstRest ) |
| rMulti.GetRoot().SetPortion( NULL ); |
| if( pSecondRest ) |
| rMulti.GetRoot().GetNext()->SetPortion( NULL ); |
| rMulti.SetFormatted(); |
| nMultiLen = nMultiLen - rInf.GetIdx(); |
| } |
| |
| // save some values |
| const XubString* pOldTxt = &(rInf.GetTxt()); |
| const SwTwips nOldPaintOfst = rInf.GetPaintOfst(); |
| |
| XubString aMultiStr( rInf.GetTxt(), 0, nMultiLen + rInf.GetIdx() ); |
| rInf.SetTxt( aMultiStr ); |
| SwTxtFormatInfo aInf( rInf, rMulti.GetRoot(), nActWidth ); |
| // Do we allow break cuts? The FirstMulti-Flag is evaluated during |
| // line break determination. |
| sal_Bool bFirstMulti = rInf.GetIdx() != rInf.GetLineStart(); |
| |
| SwLinePortion *pNextFirst = NULL; |
| SwLinePortion *pNextSecond = NULL; |
| sal_Bool bRet = sal_False; |
| |
| GETGRID( pFrm->FindPageFrm() ) |
| const sal_Bool bHasGrid = pGrid && GRID_LINES_CHARS == pGrid->GetGridType(); |
| |
| sal_uInt16 nGridWidth = 0; |
| sal_uInt16 nRubyHeight = 0; |
| sal_Bool bRubyTop = sal_False; |
| |
| if ( bHasGrid ) |
| { |
| nGridWidth = pGrid->GetBaseHeight(); |
| nRubyHeight = pGrid->GetRubyHeight(); |
| bRubyTop = ! pGrid->GetRubyTextBelow(); |
| } |
| |
| do |
| { |
| pCurr = &rMulti.GetRoot(); |
| nStart = nStartIdx; |
| bRet = sal_False; |
| FormatReset( aInf ); |
| aInf.X( nTmpX ); |
| aInf.Width( KSHORT(nActWidth) ); |
| aInf.RealWidth( KSHORT(nActWidth) ); |
| aInf.SetFirstMulti( bFirstMulti ); |
| aInf.SetNumDone( rInf.IsNumDone() ); |
| aInf.SetFtnDone( rInf.IsFtnDone() ); |
| |
| if( pFirstRest ) |
| { |
| ASSERT( pFirstRest->InFldGrp(), "BuildMulti: Fieldrest expected"); |
| SwFldPortion *pFld = |
| ((SwFldPortion*)pFirstRest)->Clone( |
| ((SwFldPortion*)pFirstRest)->GetExp() ); |
| pFld->SetFollow( sal_True ); |
| aInf.SetRest( pFld ); |
| } |
| aInf.SetRuby( rMulti.IsRuby() && rMulti.OnTop() ); |
| |
| // in grid mode we temporarily have to disable the grid for the ruby line |
| const sal_Bool bOldGridModeAllowed = GetInfo().SnapToGrid(); |
| if ( bHasGrid && aInf.IsRuby() && bRubyTop ) |
| aInf.SetSnapToGrid( sal_False ); |
| |
| // If there's no more rubytext, then buildportion is forbidden |
| if( pFirstRest || !aInf.IsRuby() ) |
| BuildPortions( aInf ); |
| |
| aInf.SetSnapToGrid( bOldGridModeAllowed ); |
| |
| rMulti.CalcSize( *this, aInf ); |
| pCurr->SetRealHeight( pCurr->Height() ); |
| |
| if( rMulti.IsBidi() ) |
| { |
| pNextFirst = aInf.GetRest(); |
| break; |
| } |
| |
| if( rMulti.HasRotation() && !rMulti.IsDouble() ) |
| break; |
| // second line has to be formatted |
| else if( pCurr->GetLen()<nMultiLen || rMulti.IsRuby() || aInf.GetRest()) |
| { |
| xub_StrLen nFirstLen = pCurr->GetLen(); |
| delete pCurr->GetNext(); |
| pCurr->SetNext( new SwLineLayout() ); |
| pCurr = pCurr->GetNext(); |
| nStart = aInf.GetIdx(); |
| aInf.X( nTmpX ); |
| SwTxtFormatInfo aTmp( aInf, *pCurr, nActWidth ); |
| if( rMulti.IsRuby() ) |
| { |
| aTmp.SetRuby( !rMulti.OnTop() ); |
| pNextFirst = aInf.GetRest(); |
| if( pSecondRest ) |
| { |
| ASSERT( pSecondRest->InFldGrp(), "Fieldrest expected"); |
| SwFldPortion *pFld = ((SwFldPortion*)pSecondRest)->Clone( |
| ((SwFldPortion*)pSecondRest)->GetExp() ); |
| pFld->SetFollow( sal_True ); |
| aTmp.SetRest( pFld ); |
| } |
| if( !rMulti.OnTop() && nFirstLen < nMultiLen ) |
| bRet = sal_True; |
| } |
| else |
| aTmp.SetRest( aInf.GetRest() ); |
| aInf.SetRest( NULL ); |
| |
| // in grid mode we temporarily have to disable the grid for the ruby line |
| if ( bHasGrid && aTmp.IsRuby() && ! bRubyTop ) |
| aTmp.SetSnapToGrid( sal_False ); |
| |
| BuildPortions( aTmp ); |
| |
| aTmp.SetSnapToGrid( bOldGridModeAllowed ); |
| |
| rMulti.CalcSize( *this, aInf ); |
| rMulti.GetRoot().SetRealHeight( rMulti.GetRoot().Height() ); |
| pCurr->SetRealHeight( pCurr->Height() ); |
| if( rMulti.IsRuby() ) |
| { |
| pNextSecond = aTmp.GetRest(); |
| if( pNextFirst ) |
| bRet = sal_True; |
| } |
| else |
| pNextFirst = aTmp.GetRest(); |
| if( ( !aTmp.IsRuby() && nFirstLen + pCurr->GetLen() < nMultiLen ) |
| || aTmp.GetRest() ) |
| // our guess for width of multiportion was too small, |
| // text did not fit into multiportion |
| bRet = sal_True; |
| } |
| if( rMulti.IsRuby() ) |
| break; |
| if( bRet ) |
| { |
| // our guess for multiportion width was too small, |
| // we set min to act |
| nMinWidth = nActWidth; |
| nActWidth = ( 3 * nMaxWidth + nMinWidth + 3 ) / 4; |
| if ( nActWidth == nMaxWidth && rInf.GetLineStart() == rInf.GetIdx() ) |
| // we have too less space, we must allow break cuts |
| // ( the first multi flag is considered during TxtPortion::_Format() ) |
| bFirstMulti = sal_False; |
| if( nActWidth <= nMinWidth ) |
| break; |
| } |
| else |
| { |
| // For Solaris, this optimisation can causes trouble: |
| // Setting this to the portion width ( = rMulti.Width() ) |
| // can make GetTextBreak inside SwTxtGuess::Guess return to small |
| // values. Therefore we add some extra twips. |
| if( nActWidth > nTmpX + rMulti.Width() + 6 ) |
| nActWidth = nTmpX + rMulti.Width() + 6; |
| nMaxWidth = nActWidth; |
| nActWidth = ( 3 * nMaxWidth + nMinWidth + 3 ) / 4; |
| if( nActWidth >= nMaxWidth ) |
| break; |
| // we do not allow break cuts during formatting |
| bFirstMulti = sal_True; |
| } |
| delete pNextFirst; |
| pNextFirst = NULL; |
| } while ( sal_True ); |
| |
| pMulti = pOldMulti; |
| |
| pCurr = pOldCurr; |
| nStart = nOldStart; |
| SetPropFont( 0 ); |
| |
| rMulti.SetLen( rMulti.GetRoot().GetLen() + ( rMulti.GetRoot().GetNext() ? |
| rMulti.GetRoot().GetNext()->GetLen() : 0 ) ); |
| |
| if( rMulti.IsDouble() ) |
| { |
| ((SwDoubleLinePortion&)rMulti).CalcBlanks( rInf ); |
| if( ((SwDoubleLinePortion&)rMulti).GetLineDiff() ) |
| { |
| SwLineLayout* pLine = &rMulti.GetRoot(); |
| if( ((SwDoubleLinePortion&)rMulti).GetLineDiff() > 0 ) |
| { |
| rInf.SetIdx( nStartIdx + pLine->GetLen() ); |
| pLine = pLine->GetNext(); |
| } |
| if( pLine ) |
| { |
| GetInfo().SetMulti( sal_True ); |
| CalcNewBlock( pLine, NULL, rMulti.Width() ); |
| GetInfo().SetMulti( sal_False ); |
| } |
| rInf.SetIdx( nStartIdx ); |
| } |
| if( ((SwDoubleLinePortion&)rMulti).GetBrackets() ) |
| { |
| rMulti.Width( rMulti.Width() + |
| ((SwDoubleLinePortion&)rMulti).BracketWidth() ); |
| GetInfo().X( nOldX ); |
| } |
| } |
| else |
| { |
| rMulti.ActualizeTabulator(); |
| if( rMulti.IsRuby() ) |
| { |
| ((SwRubyPortion&)rMulti).Adjust( rInf ); |
| ((SwRubyPortion&)rMulti).CalcRubyOffset(); |
| } |
| } |
| if( rMulti.HasRotation() ) |
| { |
| SwTwips nH = rMulti.Width(); |
| SwTwips nAsc = rMulti.GetAscent() + ( nH - rMulti.Height() )/2; |
| if( nAsc > nH ) |
| nAsc = nH; |
| else if( nAsc < 0 ) |
| nAsc = 0; |
| rMulti.Width( rMulti.Height() ); |
| rMulti.Height( KSHORT(nH) ); |
| rMulti.SetAscent( KSHORT(nAsc) ); |
| bRet = ( rInf.GetPos().X() + rMulti.Width() > rInf.Width() ) && |
| nStartIdx != rInf.GetLineStart(); |
| } |
| else if ( rMulti.IsBidi() ) |
| { |
| bRet = rMulti.GetLen() < nMultiLen || pNextFirst; |
| } |
| |
| // line break has to be performed! |
| if( bRet ) |
| { |
| ASSERT( !pNextFirst || pNextFirst->InFldGrp(), |
| "BuildMultiPortion: Surprising restportion, field expected" ); |
| SwMultiPortion *pTmp; |
| if( rMulti.IsDouble() ) |
| pTmp = new SwDoubleLinePortion( ((SwDoubleLinePortion&)rMulti), |
| nMultiLen + rInf.GetIdx() ); |
| else if( rMulti.IsRuby() ) |
| { |
| ASSERT( !pNextSecond || pNextSecond->InFldGrp(), |
| "BuildMultiPortion: Surprising restportion, field expected" ); |
| |
| if ( rInf.GetIdx() == rInf.GetLineStart() ) |
| { |
| // the ruby portion has to be split in two portions |
| pTmp = new SwRubyPortion( ((SwRubyPortion&)rMulti), |
| nMultiLen + rInf.GetIdx() ); |
| |
| if( pNextSecond ) |
| { |
| pTmp->GetRoot().SetNext( new SwLineLayout() ); |
| pTmp->GetRoot().GetNext()->SetPortion( pNextSecond ); |
| } |
| pTmp->SetFollowFld(); |
| } |
| else |
| { |
| // we try to keep our ruby portion together |
| lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx ); |
| pTmp = 0; |
| } |
| } |
| else if( rMulti.HasRotation() ) |
| { |
| // we try to keep our rotated portion together |
| lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx ); |
| pTmp = new SwRotatedPortion( nMultiLen + rInf.GetIdx(), |
| rMulti.GetDirection() ); |
| } |
| // during a recursion of BuildMultiPortions we may not build |
| // a new SwBidiPortion, this would cause a memory leak |
| else if( rMulti.IsBidi() && ! pMulti ) |
| { |
| if ( ! rMulti.GetLen() ) |
| lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx ); |
| |
| // If there is a HolePortion at the end of the bidi portion, |
| // it has to be moved behind the bidi portion. Otherwise |
| // the visual cursor travelling gets into trouble. |
| SwLineLayout& aRoot = rMulti.GetRoot(); |
| SwLinePortion* pPor = aRoot.GetFirstPortion(); |
| while ( pPor ) |
| { |
| if ( pPor->GetPortion() && pPor->GetPortion()->IsHolePortion() ) |
| { |
| SwLinePortion* pHolePor = pPor->GetPortion(); |
| pPor->SetPortion( NULL ); |
| aRoot.SetLen( aRoot.GetLen() - pHolePor->GetLen() ); |
| rMulti.SetLen( rMulti.GetLen() - pHolePor->GetLen() ); |
| rMulti.SetPortion( pHolePor ); |
| break; |
| } |
| pPor = pPor->GetPortion(); |
| } |
| |
| pTmp = new SwBidiPortion( nMultiLen + rInf.GetIdx(), |
| ((SwBidiPortion&)rMulti).GetLevel() ); |
| } |
| else |
| pTmp = NULL; |
| |
| if ( ! rMulti.GetLen() && rInf.GetLast() ) |
| { |
| SeekAndChgBefore( rInf ); |
| rInf.GetLast()->FormatEOL( rInf ); |
| } |
| |
| if( pNextFirst && pTmp ) |
| { |
| pTmp->SetFollowFld(); |
| pTmp->GetRoot().SetPortion( pNextFirst ); |
| } |
| else |
| // A follow field portion is still waiting. If nobody wants it, |
| // we delete it. |
| delete pNextFirst; |
| |
| rInf.SetRest( pTmp ); |
| } |
| |
| rInf.SetTxt( *pOldTxt ); |
| rInf.SetPaintOfst( nOldPaintOfst ); |
| rInf.SetStop( aInf.IsStop() ); |
| rInf.SetNumDone( sal_True ); |
| rInf.SetFtnDone( sal_True ); |
| SeekAndChg( rInf ); |
| delete pFirstRest; |
| delete pSecondRest; |
| delete pFontSave; |
| return bRet; |
| } |
| |
| /*-----------------08.11.00 09:29------------------- |
| * SwTxtFormatter::MakeRestPortion(..) |
| * When a fieldportion at the end of line breaks and needs a following |
| * fieldportion in the next line, then the "restportion" of the formatinfo |
| * has to be set. Normally this happens during the formatting of the first |
| * part of the fieldportion. |
| * But sometimes the formatting starts at the line with the following part, |
| * exspecally when the following part is on the next page. |
| * In this case the MakeRestPortion-function has to create the following part. |
| * The first parameter is the line that contains possibly a first part |
| * of a field. When the function finds such field part, it creates the right |
| * restportion. This may be a multiportion, e.g. if the field is surrounded by |
| * a doubleline- or ruby-portion. |
| * The second parameter is the start index of the line. |
| * --------------------------------------------------*/ |
| |
| SwLinePortion* SwTxtFormatter::MakeRestPortion( const SwLineLayout* pLine, |
| xub_StrLen nPosition ) |
| { |
| if( !nPosition ) |
| return NULL; |
| xub_StrLen nMultiPos = nPosition - pLine->GetLen(); |
| const SwMultiPortion *pTmpMulti = NULL; |
| const SwMultiPortion *pHelpMulti = NULL; |
| const SwLinePortion* pPor = pLine->GetFirstPortion(); |
| SwFldPortion *pFld = NULL; |
| while( pPor ) |
| { |
| if( pPor->GetLen() ) |
| { |
| if( !pHelpMulti ) |
| { |
| nMultiPos = nMultiPos + pPor->GetLen(); |
| pTmpMulti = NULL; |
| } |
| } |
| if( pPor->InFldGrp() ) |
| { |
| if( !pHelpMulti ) |
| pTmpMulti = NULL; |
| pFld = (SwFldPortion*)pPor; |
| } |
| else if( pPor->IsMultiPortion() ) |
| { |
| ASSERT( !pHelpMulti || pHelpMulti->IsBidi(), |
| "Nested multiportions are forbidden." ); |
| |
| pFld = NULL; |
| pTmpMulti = (SwMultiPortion*)pPor; |
| } |
| pPor = pPor->GetPortion(); |
| // If the last portion is a multi-portion, we enter it |
| // and look for a field portion inside. |
| // If we are already in a multiportion, we could change to the |
| // next line |
| if( !pPor && pTmpMulti ) |
| { |
| if( pHelpMulti ) |
| { // We're already inside the multiportion, let's take the second |
| // line, if we are in a double line portion |
| if( !pHelpMulti->IsRuby() ) |
| pPor = pHelpMulti->GetRoot().GetNext(); |
| pTmpMulti = NULL; |
| } |
| else |
| { // Now we enter a multiportion, in a ruby portion we take the |
| // main line, not the phonetic line, in a doublelineportion we |
| // starts with the first line. |
| pHelpMulti = pTmpMulti; |
| nMultiPos = nMultiPos - pHelpMulti->GetLen(); |
| if( pHelpMulti->IsRuby() && pHelpMulti->OnTop() ) |
| pPor = pHelpMulti->GetRoot().GetNext(); |
| else |
| pPor = pHelpMulti->GetRoot().GetFirstPortion(); |
| } |
| } |
| } |
| if( pFld && !pFld->HasFollow() ) |
| pFld = NULL; |
| |
| SwLinePortion *pRest = NULL; |
| if( pFld ) |
| { |
| const SwTxtAttr *pHint = GetAttr( nPosition - 1 ); |
| if ( pHint |
| && ( pHint->Which() == RES_TXTATR_FIELD |
| || pHint->Which() == RES_TXTATR_ANNOTATION ) ) |
| { |
| pRest = NewFldPortion( GetInfo(), pHint ); |
| if( pRest->InFldGrp() ) |
| ((SwFldPortion*)pRest)->TakeNextOffset( pFld ); |
| else |
| { |
| delete pRest; |
| pRest = NULL; |
| } |
| } |
| } |
| if( !pHelpMulti ) |
| return pRest; |
| |
| nPosition = nMultiPos + pHelpMulti->GetLen(); |
| SwMultiCreator* pCreate = GetInfo().GetMultiCreator( nMultiPos, 0 ); |
| |
| if ( !pCreate ) |
| { |
| ASSERT( !pHelpMulti->GetLen(), "Multiportion without attribut?" ); |
| if ( nMultiPos ) |
| --nMultiPos; |
| pCreate = GetInfo().GetMultiCreator( --nMultiPos, 0 ); |
| } |
| |
| if( pRest || nMultiPos > nPosition || ( pHelpMulti->IsRuby() && |
| ((SwRubyPortion*)pHelpMulti)->GetRubyOffset() < STRING_LEN ) ) |
| { |
| SwMultiPortion* pTmp; |
| if( pHelpMulti->IsDouble() ) |
| pTmp = new SwDoubleLinePortion( *pCreate, nMultiPos ); |
| else if( pHelpMulti->IsBidi() ) |
| pTmp = new SwBidiPortion( nMultiPos, pCreate->nLevel ); |
| else if( pHelpMulti->IsRuby() ) |
| { |
| sal_Bool bRubyTop; |
| sal_Bool* pRubyPos = 0; |
| |
| if ( GetInfo().SnapToGrid() ) |
| { |
| GETGRID( pFrm->FindPageFrm() ) |
| if ( pGrid ) |
| { |
| bRubyTop = ! pGrid->GetRubyTextBelow(); |
| pRubyPos = &bRubyTop; |
| } |
| } |
| |
| pTmp = new SwRubyPortion( *pCreate, *GetInfo().GetFont(), |
| *pFrm->GetTxtNode()->getIDocumentSettingAccess(), |
| nMultiPos, ((SwRubyPortion*)pHelpMulti)->GetRubyOffset(), |
| pRubyPos ); |
| } |
| else if( pHelpMulti->HasRotation() ) |
| pTmp = new SwRotatedPortion( nMultiPos, pHelpMulti->GetDirection() ); |
| else |
| { |
| delete pCreate; |
| return pRest; |
| } |
| delete pCreate; |
| pTmp->SetFollowFld(); |
| if( pRest ) |
| { |
| SwLineLayout *pLay = &pTmp->GetRoot(); |
| if( pTmp->IsRuby() && pTmp->OnTop() ) |
| { |
| pLay->SetNext( new SwLineLayout() ); |
| pLay = pLay->GetNext(); |
| } |
| pLay->SetPortion( pRest ); |
| } |
| return pTmp; |
| } |
| return pRest; |
| } |
| |
| |
| |
| /*-----------------23.10.00 10:47------------------- |
| * SwTxtCursorSave notes the start and current line of a SwTxtCursor, |
| * sets them to the values for GetCrsrOfst inside a multiportion |
| * and restores them in the destructor. |
| * --------------------------------------------------*/ |
| |
| SwTxtCursorSave::SwTxtCursorSave( SwTxtCursor* pTxtCursor, |
| SwMultiPortion* pMulti, |
| SwTwips nY, |
| sal_uInt16& nX, |
| xub_StrLen nCurrStart, |
| long nSpaceAdd ) |
| { |
| pTxtCrsr = pTxtCursor; |
| nStart = pTxtCursor->nStart; |
| pTxtCursor->nStart = nCurrStart; |
| pCurr = pTxtCursor->pCurr; |
| pTxtCursor->pCurr = &pMulti->GetRoot(); |
| while( pTxtCursor->Y() + pTxtCursor->GetLineHeight() < nY && |
| pTxtCursor->Next() ) |
| ; // nothing |
| nWidth = pTxtCursor->pCurr->Width(); |
| nOldProp = pTxtCursor->GetPropFont(); |
| |
| if ( pMulti->IsDouble() || pMulti->IsBidi() ) |
| { |
| bSpaceChg = pMulti->ChgSpaceAdd( pTxtCursor->pCurr, nSpaceAdd ); |
| |
| sal_uInt16 nSpaceCnt; |
| if ( pMulti->IsDouble() ) |
| { |
| pTxtCursor->SetPropFont( 50 ); |
| nSpaceCnt = ((SwDoubleLinePortion*)pMulti)->GetSpaceCnt(); |
| } |
| else |
| { |
| const xub_StrLen nOldIdx = pTxtCursor->GetInfo().GetIdx(); |
| pTxtCursor->GetInfo().SetIdx ( nCurrStart ); |
| nSpaceCnt = ((SwBidiPortion*)pMulti)->GetSpaceCnt(pTxtCursor->GetInfo()); |
| pTxtCursor->GetInfo().SetIdx ( nOldIdx ); |
| } |
| |
| if( nSpaceAdd > 0 && !pMulti->HasTabulator() ) |
| pTxtCursor->pCurr->Width( static_cast<sal_uInt16>(nWidth + nSpaceAdd * nSpaceCnt / SPACING_PRECISION_FACTOR ) ); |
| |
| // For a BidiPortion we have to calculate the offset from the |
| // end of the portion |
| if ( nX && pMulti->IsBidi() ) |
| nX = pTxtCursor->pCurr->Width() - nX; |
| } |
| else |
| bSpaceChg = sal_False; |
| } |
| |
| SwTxtCursorSave::~SwTxtCursorSave() |
| { |
| if( bSpaceChg ) |
| SwDoubleLinePortion::ResetSpaceAdd( pTxtCrsr->pCurr ); |
| pTxtCrsr->pCurr->Width( KSHORT(nWidth) ); |
| pTxtCrsr->pCurr = pCurr; |
| pTxtCrsr->nStart = nStart; |
| pTxtCrsr->SetPropFont( nOldProp ); |
| } |
| |