| /************************************************************** |
| * |
| * 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" |
| #ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_ |
| #include <com/sun/star/i18n/ScriptType.hdl> |
| #endif |
| #include <vcl/outdev.hxx> |
| #include <IDocumentSettingAccess.hxx> |
| |
| #include "frame.hxx" // CalcFlyAdjust() |
| #include "paratr.hxx" |
| #include "txtcfg.hxx" |
| #include "itrtxt.hxx" |
| #include "porglue.hxx" |
| #include "porlay.hxx" |
| #include "porfly.hxx" // CalcFlyAdjust() |
| #include "pordrop.hxx" // CalcFlyAdjust() |
| #include "pormulti.hxx" |
| #include <portab.hxx> |
| |
| #define MIN_TAB_WIDTH 60 |
| |
| using namespace ::com::sun::star; |
| |
| /************************************************************************* |
| * SwTxtAdjuster::FormatBlock() |
| *************************************************************************/ |
| |
| void SwTxtAdjuster::FormatBlock( ) |
| { |
| // In der letzten Zeile gibt's keinen Blocksatz. |
| // Und bei Tabulatoren aus Tradition auch nicht. |
| // 7701: wenn Flys im Spiel sind, geht's weiter |
| |
| const SwLinePortion *pFly = 0; |
| |
| sal_Bool bSkip = !IsLastBlock() && |
| nStart + pCurr->GetLen() >= GetInfo().GetTxt().Len(); |
| |
| // ????: mehrzeilige Felder sind fies: wir muessen kontrollieren, |
| // ob es noch andere Textportions im Absatz gibt. |
| if( bSkip ) |
| { |
| const SwLineLayout *pLay = pCurr->GetNext(); |
| while( pLay && !pLay->GetLen() ) |
| { |
| const SwLinePortion *pPor = pCurr->GetFirstPortion(); |
| while( pPor && bSkip ) |
| { |
| if( pPor->InTxtGrp() ) |
| bSkip = sal_False; |
| pPor = pPor->GetPortion(); |
| } |
| pLay = bSkip ? pLay->GetNext() : 0; |
| } |
| } |
| |
| if( bSkip ) |
| { |
| if( !GetInfo().GetParaPortion()->HasFly() ) |
| { |
| if( IsLastCenter() ) |
| CalcFlyAdjust( pCurr ); |
| pCurr->FinishSpaceAdd(); |
| return; |
| } |
| else |
| { |
| const SwLinePortion *pTmpFly = NULL; |
| |
| // 7701: beim letzten Fly soll Schluss sein |
| const SwLinePortion *pPos = pCurr->GetFirstPortion(); |
| while( pPos ) |
| { |
| // Ich suche jetzt den letzten Fly, hinter dem noch Text ist: |
| if( pPos->IsFlyPortion() ) |
| pTmpFly = pPos; // Ein Fly wurde gefunden |
| else if ( pTmpFly && pPos->InTxtGrp() ) |
| { |
| pFly = pTmpFly; // Ein Fly mit nachfolgendem Text! |
| pTmpFly = NULL; |
| } |
| pPos = pPos->GetPortion(); |
| } |
| // 8494: Wenn keiner gefunden wurde, ist sofort Schluss! |
| if( !pFly ) |
| { |
| if( IsLastCenter() ) |
| CalcFlyAdjust( pCurr ); |
| pCurr->FinishSpaceAdd(); |
| return; |
| } |
| } |
| } |
| |
| const xub_StrLen nOldIdx = GetInfo().GetIdx(); |
| GetInfo().SetIdx( nStart ); |
| CalcNewBlock( pCurr, pFly ); |
| GetInfo().SetIdx( nOldIdx ); |
| GetInfo().GetParaPortion()->GetRepaint()->SetOfst(0); |
| } |
| |
| /************************************************************************* |
| * lcl_CheckKashidaPositions() |
| *************************************************************************/ |
| bool lcl_CheckKashidaPositions( SwScriptInfo& rSI, SwTxtSizeInfo& rInf, SwTxtIter& rItr, |
| xub_StrLen& nKashidas, xub_StrLen& nGluePortion ) |
| { |
| // i60594 validate Kashida justification |
| xub_StrLen nIdx = rItr.GetStart(); |
| xub_StrLen nEnd = rItr.GetEnd(); |
| |
| // Note on calling KashidaJustify(): |
| // Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean |
| // total number of kashida positions, or the number of kashida positions after some positions |
| // have been dropped. |
| // Here we want the clean total, which is OK: We have called ClearKashidaInvalid() before. |
| nKashidas = rSI.KashidaJustify ( 0, 0, rItr.GetStart(), rItr.GetLength(), 0 ); |
| |
| if (!nKashidas) // nothing to do |
| return true; |
| |
| // kashida positions found in SwScriptInfo are not necessarily valid in every font |
| // if two characters are replaced by a ligature glyph, there will be no place for a kashida |
| xub_StrLen* pKashidaPos = new xub_StrLen [ nKashidas ]; |
| xub_StrLen* pKashidaPosDropped = new xub_StrLen [ nKashidas ]; |
| rSI.GetKashidaPositions ( nIdx, rItr.GetLength(), pKashidaPos ); |
| xub_StrLen nKashidaIdx = 0; |
| while ( nKashidas && nIdx < nEnd ) |
| { |
| rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() ); |
| xub_StrLen nNext = rItr.GetNextAttr(); |
| |
| // is there also a script change before? |
| // if there is, nNext should point to the script change |
| xub_StrLen nNextScript = rSI.NextScriptChg( nIdx ); |
| if( nNextScript < nNext ) |
| nNext = nNextScript; |
| |
| if ( nNext == STRING_LEN || nNext > nEnd ) |
| nNext = nEnd; |
| xub_StrLen nKashidasInAttr = rSI.KashidaJustify ( 0, 0, nIdx, nNext - nIdx ); |
| if ( nKashidasInAttr ) |
| { |
| xub_StrLen nKashidasDropped = 0; |
| if ( !SwScriptInfo::IsArabicText( rInf.GetTxt(), nIdx, nNext - nIdx ) ) |
| { |
| nKashidasDropped = nKashidasInAttr; |
| nKashidas -= nKashidasDropped; |
| } |
| else |
| { |
| sal_uLong nOldLayout = rInf.GetOut()->GetLayoutMode(); |
| rInf.GetOut()->SetLayoutMode ( nOldLayout | TEXT_LAYOUT_BIDI_RTL ); |
| nKashidasDropped = rInf.GetOut()->ValidateKashidas ( rInf.GetTxt(), nIdx, nNext - nIdx, |
| nKashidasInAttr, pKashidaPos + nKashidaIdx, |
| pKashidaPosDropped ); |
| rInf.GetOut()->SetLayoutMode ( nOldLayout ); |
| if ( nKashidasDropped ) |
| { |
| rSI.MarkKashidasInvalid ( nKashidasDropped, pKashidaPosDropped ); |
| nKashidas -= nKashidasDropped; |
| nGluePortion -= nKashidasDropped; |
| } |
| } |
| nKashidaIdx += nKashidasInAttr; |
| } |
| nIdx = nNext; |
| } |
| delete[] pKashidaPos; |
| delete[] pKashidaPosDropped; |
| |
| // return false if all kashidas have been eliminated |
| return (nKashidas > 0); |
| } |
| |
| /************************************************************************* |
| * lcl_CheckKashidaWidth() |
| *************************************************************************/ |
| bool lcl_CheckKashidaWidth ( SwScriptInfo& rSI, SwTxtSizeInfo& rInf, SwTxtIter& rItr, xub_StrLen& nKashidas, |
| xub_StrLen& nGluePortion, const long nGluePortionWidth, long& nSpaceAdd ) |
| { |
| // check kashida width |
| // if width is smaller than minimal kashida width allowed by fonts in the current line |
| // drop one kashida after the other until kashida width is OK |
| bool bAddSpaceChanged; |
| while ( nKashidas ) |
| { |
| bAddSpaceChanged = false; |
| xub_StrLen nIdx = rItr.GetStart(); |
| xub_StrLen nEnd = rItr.GetEnd(); |
| while ( nIdx < nEnd ) |
| { |
| rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() ); |
| xub_StrLen nNext = rItr.GetNextAttr(); |
| |
| // is there also a script change before? |
| // if there is, nNext should point to the script change |
| xub_StrLen nNextScript = rSI.NextScriptChg( nIdx ); |
| if( nNextScript < nNext ) |
| nNext = nNextScript; |
| |
| if ( nNext == STRING_LEN || nNext > nEnd ) |
| nNext = nEnd; |
| xub_StrLen nKashidasInAttr = rSI.KashidaJustify ( 0, 0, nIdx, nNext - nIdx ); |
| |
| long nFontMinKashida = rInf.GetOut()->GetMinKashida(); |
| if ( nFontMinKashida && nKashidasInAttr && SwScriptInfo::IsArabicText( rInf.GetTxt(), nIdx, nNext - nIdx ) ) |
| { |
| xub_StrLen nKashidasDropped = 0; |
| while ( nKashidas && nGluePortion && nKashidasInAttr && |
| nSpaceAdd / SPACING_PRECISION_FACTOR < nFontMinKashida ) |
| { |
| --nGluePortion; |
| --nKashidas; |
| --nKashidasInAttr; |
| ++nKashidasDropped; |
| if( !nKashidas || !nGluePortion ) // nothing left, return false to |
| return false; // do regular blank justification |
| |
| nSpaceAdd = nGluePortionWidth / nGluePortion; |
| bAddSpaceChanged = true; |
| } |
| if( nKashidasDropped ) |
| rSI.MarkKashidasInvalid( nKashidasDropped, nIdx, nNext - nIdx ); |
| } |
| if ( bAddSpaceChanged ) |
| break; // start all over again |
| nIdx = nNext; |
| } |
| if ( !bAddSpaceChanged ) |
| break; // everything was OK |
| } |
| return true; |
| } |
| |
| /************************************************************************* |
| * SwTxtAdjuster::CalcNewBlock() |
| * |
| * CalcNewBlock() darf erst nach CalcLine() gerufen werden ! |
| * Aufgespannt wird immer zwischen zwei RandPortions oder FixPortions |
| * (Tabs und Flys). Dabei werden die Glues gezaehlt und ExpandBlock gerufen. |
| *************************************************************************/ |
| |
| void SwTxtAdjuster::CalcNewBlock( SwLineLayout *pCurrent, |
| const SwLinePortion *pStopAt, SwTwips nReal, bool bSkipKashida ) |
| { |
| ASSERT( GetInfo().IsMulti() || SVX_ADJUST_BLOCK == GetAdjust(), |
| "CalcNewBlock: Why?" ); |
| ASSERT( pCurrent->Height(), "SwTxtAdjuster::CalcBlockAdjust: missing CalcLine()" ); |
| |
| pCurrent->InitSpaceAdd(); |
| xub_StrLen nGluePortion = 0; |
| xub_StrLen nCharCnt = 0; |
| MSHORT nSpaceIdx = 0; |
| |
| // i60591: hennerdrews |
| SwScriptInfo& rSI = GetInfo().GetParaPortion()->GetScriptInfo(); |
| SwTxtSizeInfo aInf ( GetTxtFrm() ); |
| SwTxtIter aItr ( GetTxtFrm(), &aInf ); |
| |
| if ( rSI.CountKashida() ) |
| { |
| while (aItr.GetCurr() != pCurrent && aItr.GetNext()) |
| aItr.Next(); |
| |
| if( bSkipKashida ) |
| { |
| rSI.SetNoKashidaLine ( aItr.GetStart(), aItr.GetLength()); |
| } |
| else |
| { |
| rSI.ClearKashidaInvalid ( aItr.GetStart(), aItr.GetLength() ); |
| rSI.ClearNoKashidaLine( aItr.GetStart(), aItr.GetLength() ); |
| } |
| } |
| |
| // Nicht vergessen: |
| // CalcRightMargin() setzt pCurrent->Width() auf die Zeilenbreite ! |
| if (!bSkipKashida) |
| CalcRightMargin( pCurrent, nReal ); |
| |
| // --> FME 2005-06-08 #i49277# |
| const sal_Bool bDoNotJustifyLinesWithManualBreak = |
| GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK); |
| // <-- |
| |
| SwLinePortion *pPos = pCurrent->GetPortion(); |
| |
| while( pPos ) |
| { |
| if ( bDoNotJustifyLinesWithManualBreak && |
| pPos->IsBreakPortion() && !IsLastBlock() ) |
| { |
| pCurrent->FinishSpaceAdd(); |
| break; |
| } |
| |
| if ( pPos->InTxtGrp() ) |
| nGluePortion = nGluePortion + ((SwTxtPortion*)pPos)->GetSpaceCnt( GetInfo(), nCharCnt ); |
| else if( pPos->IsMultiPortion() ) |
| { |
| SwMultiPortion* pMulti = (SwMultiPortion*)pPos; |
| // a multiportion with a tabulator inside breaks the text adjustment |
| // a ruby portion will not be stretched by text adjustment |
| // a double line portion takes additional space for each blank |
| // in the wider line |
| if( pMulti->HasTabulator() ) |
| { |
| if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() ) |
| pCurrent->SetLLSpaceAdd( 0, nSpaceIdx ); |
| |
| nSpaceIdx++; |
| nGluePortion = 0; |
| nCharCnt = 0; |
| } |
| else if( pMulti->IsDouble() ) |
| nGluePortion = nGluePortion + ((SwDoubleLinePortion*)pMulti)->GetSpaceCnt(); |
| else if ( pMulti->IsBidi() ) |
| nGluePortion = nGluePortion + ((SwBidiPortion*)pMulti)->GetSpaceCnt( GetInfo() ); // i60594 |
| } |
| |
| if( pPos->InGlueGrp() ) |
| { |
| if( pPos->InFixMargGrp() ) |
| { |
| if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() ) |
| pCurrent->SetLLSpaceAdd( 0, nSpaceIdx ); |
| |
| const long nGluePortionWidth = static_cast<SwGluePortion*>(pPos)->GetPrtGlue() * |
| SPACING_PRECISION_FACTOR; |
| |
| xub_StrLen nKashidas = 0; |
| if( nGluePortion && rSI.CountKashida() && !bSkipKashida ) |
| { |
| // kashida positions found in SwScriptInfo are not necessarily valid in every font |
| // if two characters are replaced by a ligature glyph, there will be no place for a kashida |
| if ( !lcl_CheckKashidaPositions ( rSI, aInf, aItr, nKashidas, nGluePortion )) |
| { |
| // all kashida positions are invalid |
| // do regular blank justification |
| pCurrent->FinishSpaceAdd(); |
| GetInfo().SetIdx( nStart ); |
| CalcNewBlock( pCurrent, pStopAt, nReal, true ); |
| return; |
| } |
| } |
| |
| if( nGluePortion ) |
| { |
| long nSpaceAdd = nGluePortionWidth / nGluePortion; |
| |
| // i60594 |
| if( rSI.CountKashida() && !bSkipKashida ) |
| { |
| if( !lcl_CheckKashidaWidth( rSI, aInf, aItr, nKashidas, nGluePortion, nGluePortionWidth, nSpaceAdd )) |
| { |
| // no kashidas left |
| // do regular blank justification |
| pCurrent->FinishSpaceAdd(); |
| GetInfo().SetIdx( nStart ); |
| CalcNewBlock( pCurrent, pStopAt, nReal, true ); |
| return; |
| } |
| } |
| |
| pCurrent->SetLLSpaceAdd( nSpaceAdd , nSpaceIdx ); |
| pPos->Width( ( (SwGluePortion*)pPos )->GetFixWidth() ); |
| } |
| else if ( IsOneBlock() && nCharCnt > 1 ) |
| { |
| const long nSpaceAdd = - nGluePortionWidth / ( nCharCnt - 1 ); |
| pCurrent->SetLLSpaceAdd( nSpaceAdd, nSpaceIdx ); |
| pPos->Width( ( (SwGluePortion*)pPos )->GetFixWidth() ); |
| } |
| |
| nSpaceIdx++; |
| nGluePortion = 0; |
| nCharCnt = 0; |
| } |
| else |
| ++nGluePortion; |
| } |
| GetInfo().SetIdx( GetInfo().GetIdx() + pPos->GetLen() ); |
| if ( pPos == pStopAt ) |
| { |
| pCurrent->SetLLSpaceAdd( 0, nSpaceIdx ); |
| break; |
| } |
| pPos = pPos->GetPortion(); |
| } |
| } |
| |
| /************************************************************************* |
| * SwTxtAdjuster::CalcKanaAdj() |
| *************************************************************************/ |
| |
| SwTwips SwTxtAdjuster::CalcKanaAdj( SwLineLayout* pCurrent ) |
| { |
| ASSERT( pCurrent->Height(), "SwTxtAdjuster::CalcBlockAdjust: missing CalcLine()" ); |
| ASSERT( !pCurrent->GetpKanaComp(), "pKanaComp already exists!!" ); |
| |
| SvUShorts *pNewKana = new SvUShorts; |
| pCurrent->SetKanaComp( pNewKana ); |
| |
| const sal_uInt16 nNull = 0; |
| MSHORT nKanaIdx = 0; |
| long nKanaDiffSum = 0; |
| SwTwips nRepaintOfst = 0; |
| SwTwips nX = 0; |
| sal_Bool bNoCompression = sal_False; |
| |
| // Nicht vergessen: |
| // CalcRightMargin() setzt pCurrent->Width() auf die Zeilenbreite ! |
| CalcRightMargin( pCurrent, 0 ); |
| |
| SwLinePortion* pPos = pCurrent->GetPortion(); |
| |
| while( pPos ) |
| { |
| if ( pPos->InTxtGrp() ) |
| { |
| // get maximum portion width from info structure, calculated |
| // during text formatting |
| sal_uInt16 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (sal_uLong)pPos ); |
| |
| // check, if information is stored under other key |
| if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() ) |
| nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (sal_uLong)pCurrent ); |
| |
| // calculate difference between portion width and max. width |
| nKanaDiffSum += nMaxWidthDiff; |
| |
| // we store the beginning of the first compressable portion |
| // for repaint |
| if ( nMaxWidthDiff && !nRepaintOfst ) |
| nRepaintOfst = nX + GetLeftMargin(); |
| } |
| else if( pPos->InGlueGrp() && pPos->InFixMargGrp() ) |
| { |
| if ( nKanaIdx == pCurrent->GetKanaComp().Count() ) |
| pCurrent->GetKanaComp().Insert( nNull, nKanaIdx ); |
| |
| sal_uInt16 nRest; |
| |
| if ( pPos->InTabGrp() ) |
| { |
| nRest = ! bNoCompression && |
| ( pPos->Width() > MIN_TAB_WIDTH ) ? |
| pPos->Width() - MIN_TAB_WIDTH : |
| 0; |
| |
| // for simplifying the handling of left, right ... tabs, |
| // we do expand portions, which are lying behind |
| // those special tabs |
| bNoCompression = !pPos->IsTabLeftPortion(); |
| } |
| else |
| { |
| nRest = ! bNoCompression ? |
| ((SwGluePortion*)pPos)->GetPrtGlue() : |
| 0; |
| |
| bNoCompression = sal_False; |
| } |
| |
| if( nKanaDiffSum ) |
| { |
| sal_uLong nCompress = ( 10000 * nRest ) / nKanaDiffSum; |
| |
| if ( nCompress >= 10000 ) |
| // kanas can be expanded to 100%, and there is still |
| // some space remaining |
| nCompress = 0; |
| |
| else |
| nCompress = 10000 - nCompress; |
| |
| ( pCurrent->GetKanaComp() )[ nKanaIdx ] = (sal_uInt16)nCompress; |
| nKanaDiffSum = 0; |
| } |
| |
| nKanaIdx++; |
| } |
| |
| nX += pPos->Width(); |
| pPos = pPos->GetPortion(); |
| } |
| |
| // set portion width |
| nKanaIdx = 0; |
| sal_uInt16 nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ]; |
| pPos = pCurrent->GetPortion(); |
| long nDecompress = 0; |
| nKanaDiffSum = 0; |
| |
| while( pPos ) |
| { |
| if ( pPos->InTxtGrp() ) |
| { |
| const sal_uInt16 nMinWidth = pPos->Width(); |
| |
| // get maximum portion width from info structure, calculated |
| // during text formatting |
| sal_uInt16 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (sal_uLong)pPos ); |
| |
| // check, if information is stored under other key |
| if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() ) |
| nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (sal_uLong)pCurrent ); |
| nKanaDiffSum += nMaxWidthDiff; |
| pPos->Width( nMinWidth + |
| ( ( 10000 - nCompress ) * nMaxWidthDiff ) / 10000 ); |
| nDecompress += pPos->Width() - nMinWidth; |
| } |
| else if( pPos->InGlueGrp() && pPos->InFixMargGrp() ) |
| { |
| if( nCompress ) |
| { |
| nKanaDiffSum *= nCompress; |
| nKanaDiffSum /= 10000; |
| } |
| |
| pPos->Width( static_cast<sal_uInt16>(pPos->Width() - nDecompress) ); |
| |
| if ( pPos->InTabGrp() ) |
| // set fix width to width |
| ((SwTabPortion*)pPos)->SetFixWidth( pPos->Width() ); |
| |
| const SvUShorts& rKanaComp = pCurrent->GetKanaComp(); |
| if ( ++nKanaIdx < rKanaComp.Count() ) |
| nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ]; |
| |
| nKanaDiffSum = 0; |
| nDecompress = 0; |
| } |
| pPos = pPos->GetPortion(); |
| } |
| |
| return nRepaintOfst; |
| } |
| |
| /************************************************************************* |
| * SwTxtAdjuster::CalcRightMargin() |
| *************************************************************************/ |
| |
| SwMarginPortion *SwTxtAdjuster::CalcRightMargin( SwLineLayout *pCurrent, |
| SwTwips nReal ) |
| { |
| long nRealWidth; |
| const sal_uInt16 nRealHeight = GetLineHeight(); |
| const sal_uInt16 nLineHeight = pCurrent->Height(); |
| |
| KSHORT nPrtWidth = pCurrent->PrtWidth(); |
| SwLinePortion *pLast = pCurrent->FindLastPortion(); |
| |
| if( GetInfo().IsMulti() ) |
| nRealWidth = nReal; |
| else |
| { |
| nRealWidth = GetLineWidth(); |
| // Fuer jeden FlyFrm, der in den rechten Rand hineinragt, |
| // wird eine FlyPortion angelegt. |
| const long nLeftMar = GetLeftMargin(); |
| SwRect aCurrRect( nLeftMar + nPrtWidth, Y() + nRealHeight - nLineHeight, |
| nRealWidth - nPrtWidth, nLineHeight ); |
| |
| SwFlyPortion *pFly = CalcFlyPortion( nRealWidth, aCurrRect ); |
| while( pFly && long( nPrtWidth )< nRealWidth ) |
| { |
| pLast->Append( pFly ); |
| pLast = pFly; |
| if( pFly->Fix() > nPrtWidth ) |
| pFly->Width( ( pFly->Fix() - nPrtWidth) + pFly->Width() + 1); |
| nPrtWidth += pFly->Width() + 1; |
| aCurrRect.Left( nLeftMar + nPrtWidth ); |
| pFly = CalcFlyPortion( nRealWidth, aCurrRect ); |
| } |
| if( pFly ) |
| delete pFly; |
| } |
| |
| SwMarginPortion *pRight = new SwMarginPortion( 0 ); |
| pLast->Append( pRight ); |
| |
| if( long( nPrtWidth )< nRealWidth ) |
| pRight->PrtWidth( KSHORT( nRealWidth - nPrtWidth ) ); |
| |
| // pCurrent->Width() wird auf die reale Groesse gesetzt, |
| // da jetzt die MarginPortions eingehaengt sind. |
| // Dieser Trick hat wundersame Auswirkungen. |
| // Wenn pCurrent->Width() == nRealWidth ist, dann wird das gesamte |
| // Adjustment implizit ausgecontert. GetLeftMarginAdjust() und |
| // IsBlocksatz() sind der Meinung, sie haetten eine mit Zeichen |
| // gefuellte Zeile. |
| |
| pCurrent->PrtWidth( KSHORT( nRealWidth ) ); |
| return pRight; |
| } |
| |
| /************************************************************************* |
| * SwTxtAdjuster::CalcFlyAdjust() |
| *************************************************************************/ |
| |
| void SwTxtAdjuster::CalcFlyAdjust( SwLineLayout *pCurrent ) |
| { |
| // 1) Es wird ein linker Rand eingefuegt: |
| SwMarginPortion *pLeft = pCurrent->CalcLeftMargin(); |
| SwGluePortion *pGlue = pLeft; // die letzte GluePortion |
| |
| |
| // 2) Es wird ein rechter Rand angehaengt: |
| // CalcRightMargin berechnet auch eventuelle Ueberlappungen mit |
| // FlyFrms. |
| CalcRightMargin( pCurrent ); |
| |
| SwLinePortion *pPos = pLeft->GetPortion(); |
| xub_StrLen nLen = 0; |
| |
| // Wenn wir nur eine Zeile vorliegen haben und die Textportion zusammen |
| // haengend ist und wenn zentriert wird, dann ... |
| |
| sal_Bool bComplete = 0 == nStart; |
| const sal_Bool bTabCompat = GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT); |
| sal_Bool bMultiTab = sal_False; |
| |
| while( pPos ) |
| { |
| if ( pPos->IsMultiPortion() && ((SwMultiPortion*)pPos)->HasTabulator() ) |
| bMultiTab = sal_True; |
| else if( pPos->InFixMargGrp() && |
| ( bTabCompat ? ! pPos->InTabGrp() : ! bMultiTab ) ) |
| { |
| // in tab compat mode we do not want to change tab portions |
| // in non tab compat mode we do not want to change margins if we |
| // found a multi portion with tabs |
| if( SVX_ADJUST_RIGHT == GetAdjust() ) |
| ((SwGluePortion*)pPos)->MoveAllGlue( pGlue ); |
| else |
| { |
| // Eine schlaue Idee von MA: |
| // Fuer die erste Textportion wird rechtsbuendig eingestellt, |
| // fuer die letzte linksbuendig. |
| |
| // Die erste Textportion kriegt den ganzen Glue |
| // Aber nur, wenn wir mehr als eine Zeile besitzen. |
| if( bComplete && GetInfo().GetTxt().Len() == nLen ) |
| ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue ); |
| else |
| { |
| if ( ! bTabCompat ) |
| { |
| if( pLeft == pGlue ) |
| { |
| // Wenn es nur einen linken und rechten Rand gibt, |
| // dann teilen sich die Raender den Glue. |
| if( nLen + pPos->GetLen() >= pCurrent->GetLen() ) |
| ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue ); |
| else |
| ((SwGluePortion*)pPos)->MoveAllGlue( pGlue ); |
| } |
| else |
| { |
| // Die letzte Textportion behaelt sein Glue |
| if( !pPos->IsMarginPortion() ) |
| ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue ); |
| } |
| } |
| else |
| ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue ); |
| } |
| } |
| |
| pGlue = (SwFlyPortion*)pPos; |
| bComplete = sal_False; |
| } |
| nLen = nLen + pPos->GetLen(); |
| pPos = pPos->GetPortion(); |
| } |
| |
| if( ! bTabCompat && ! bMultiTab && SVX_ADJUST_RIGHT == GetAdjust() ) |
| // portions are moved to the right if possible |
| pLeft->AdjustRight( pCurrent ); |
| } |
| |
| /************************************************************************* |
| * SwTxtAdjuster::CalcAdjLine() |
| *************************************************************************/ |
| |
| void SwTxtAdjuster::CalcAdjLine( SwLineLayout *pCurrent ) |
| { |
| ASSERT( pCurrent->IsFormatAdj(), "CalcAdjLine: Why?" ); |
| |
| pCurrent->SetFormatAdj(sal_False); |
| |
| SwParaPortion* pPara = GetInfo().GetParaPortion(); |
| |
| switch( GetAdjust() ) |
| { |
| case SVX_ADJUST_RIGHT: |
| case SVX_ADJUST_CENTER: |
| { |
| CalcFlyAdjust( pCurrent ); |
| pPara->GetRepaint()->SetOfst( 0 ); |
| break; |
| } |
| case SVX_ADJUST_BLOCK: |
| { |
| // disabled for #i13507# |
| // 8311: In Zeilen mit LineBreaks gibt es keinen Blocksatz! |
| /* if( pCurrent->GetLen() && |
| CH_BREAK == GetInfo().GetChar( nStart + pCurrent->GetLen() - 1 ) && |
| !IsLastBlock() ) |
| { |
| if( IsLastCenter() ) |
| { |
| CalcFlyAdjust( pCurrent ); |
| pPara->GetRepaint()->SetOfst( 0 ); |
| break; |
| } |
| return; |
| } |
| */ FormatBlock(); |
| break; |
| } |
| default : return; |
| } |
| } |
| |
| /************************************************************************* |
| * SwTxtAdjuster::CalcFlyPortion() |
| * |
| * Die Berechnung hat es in sich: nCurrWidth geibt die Breite _vor_ dem |
| * aufaddieren des Wortes das noch auf die Zeile passt! Aus diesem Grund |
| * stimmt die Breite der FlyPortion auch, wenn die Blockierungssituation |
| * bFirstWord && !WORDFITS eintritt. |
| *************************************************************************/ |
| |
| SwFlyPortion *SwTxtAdjuster::CalcFlyPortion( const long nRealWidth, |
| const SwRect &rCurrRect ) |
| { |
| SwTxtFly aTxtFly( GetTxtFrm() ); |
| |
| const KSHORT nCurrWidth = pCurr->PrtWidth(); |
| SwFlyPortion *pFlyPortion = 0; |
| |
| SwRect aLineVert( rCurrRect ); |
| if ( GetTxtFrm()->IsRightToLeft() ) |
| GetTxtFrm()->SwitchLTRtoRTL( aLineVert ); |
| if ( GetTxtFrm()->IsVertical() ) |
| GetTxtFrm()->SwitchHorizontalToVertical( aLineVert ); |
| |
| // aFlyRect ist dokumentglobal ! |
| SwRect aFlyRect( aTxtFly.GetFrm( aLineVert ) ); |
| |
| if ( GetTxtFrm()->IsRightToLeft() ) |
| GetTxtFrm()->SwitchRTLtoLTR( aFlyRect ); |
| if ( GetTxtFrm()->IsVertical() ) |
| GetTxtFrm()->SwitchVerticalToHorizontal( aFlyRect ); |
| |
| // Wenn ein Frame ueberlappt, wird eine Portion eroeffnet. |
| if( aFlyRect.HasArea() ) |
| { |
| // aLocal ist framelokal |
| SwRect aLocal( aFlyRect ); |
| aLocal.Pos( aLocal.Left() - GetLeftMargin(), aLocal.Top() ); |
| if( nCurrWidth > aLocal.Left() ) |
| aLocal.Left( nCurrWidth ); |
| |
| // Wenn das Rechteck breiter als die Zeile ist, stutzen |
| // wir es ebenfalls zurecht. |
| KSHORT nLocalWidth = KSHORT( aLocal.Left() + aLocal.Width() ); |
| if( nRealWidth < long( nLocalWidth ) ) |
| aLocal.Width( nRealWidth - aLocal.Left() ); |
| GetInfo().GetParaPortion()->SetFly( sal_True ); |
| pFlyPortion = new SwFlyPortion( aLocal ); |
| pFlyPortion->Height( KSHORT( rCurrRect.Height() ) ); |
| // Die Width koennte kleiner sein als die FixWidth, daher: |
| pFlyPortion->AdjFixWidth(); |
| } |
| return pFlyPortion; |
| } |
| |
| /************************************************************************* |
| * SwTxtPainter::_CalcDropAdjust() |
| *************************************************************************/ |
| |
| // 6721: Drops und Adjustment |
| // CalcDropAdjust wird ggf. am Ende von Format() gerufen. |
| |
| void SwTxtAdjuster::CalcDropAdjust() |
| { |
| ASSERT( 1<GetDropLines() && SVX_ADJUST_LEFT!=GetAdjust() && SVX_ADJUST_BLOCK!=GetAdjust(), |
| "CalcDropAdjust: No reason for DropAdjustment." ) |
| |
| const MSHORT nLineNumber = GetLineNr(); |
| |
| // 1) Dummies ueberspringen |
| Top(); |
| |
| if( !pCurr->IsDummy() || NextLine() ) |
| { |
| // Erst adjustieren. |
| GetAdjusted(); |
| |
| SwLinePortion *pPor = pCurr->GetFirstPortion(); |
| |
| // 2) Sicherstellen, dass die DropPortion dabei ist. |
| // 3) pLeft: Die GluePor vor der DropPor |
| if( pPor->InGlueGrp() && pPor->GetPortion() |
| && pPor->GetPortion()->IsDropPortion() ) |
| { |
| const SwLinePortion *pDropPor = (SwDropPortion*) pPor->GetPortion(); |
| SwGluePortion *pLeft = (SwGluePortion*) pPor; |
| |
| // 4) pRight: Die GluePor hinter der DropPor suchen |
| pPor = pPor->GetPortion(); |
| while( pPor && !pPor->InFixMargGrp() ) |
| pPor = pPor->GetPortion(); |
| |
| SwGluePortion *pRight = ( pPor && pPor->InGlueGrp() ) ? |
| (SwGluePortion*) pPor : 0; |
| if( pRight && pRight != pLeft ) |
| { |
| // 5) nMinLeft berechnen. Wer steht am weitesten links? |
| const KSHORT nDropLineStart = |
| KSHORT(GetLineStart()) + pLeft->Width() + pDropPor->Width(); |
| KSHORT nMinLeft = nDropLineStart; |
| for( MSHORT i = 1; i < GetDropLines(); ++i ) |
| { |
| if( NextLine() ) |
| { |
| // Erst adjustieren. |
| GetAdjusted(); |
| |
| pPor = pCurr->GetFirstPortion(); |
| const SwMarginPortion *pMar = pPor->IsMarginPortion() ? |
| (SwMarginPortion*)pPor : 0; |
| if( !pMar ) |
| nMinLeft = 0; |
| else |
| { |
| const KSHORT nLineStart = |
| KSHORT(GetLineStart()) + pMar->Width(); |
| if( nMinLeft > nLineStart ) |
| nMinLeft = nLineStart; |
| } |
| } |
| } |
| |
| // 6) Den Glue zwischen pLeft und pRight neu verteilen. |
| if( nMinLeft < nDropLineStart ) |
| { |
| // Glue wird immer von pLeft nach pRight abgegeben, |
| // damit der Text nach links wandert. |
| const short nGlue = nDropLineStart - nMinLeft; |
| if( !nMinLeft ) |
| pLeft->MoveAllGlue( pRight ); |
| else |
| pLeft->MoveGlue( pRight, nGlue ); |
| #ifdef DBGTXT |
| aDbstream << "Drop adjusted: " << nGlue << endl; |
| #endif |
| } |
| } |
| } |
| } |
| |
| if( nLineNumber != GetLineNr() ) |
| { |
| Top(); |
| while( nLineNumber != GetLineNr() && Next() ) |
| ; |
| } |
| } |
| |
| /************************************************************************* |
| * SwTxtAdjuster::CalcDropRepaint() |
| *************************************************************************/ |
| |
| void SwTxtAdjuster::CalcDropRepaint() |
| { |
| Top(); |
| SwRepaint &rRepaint = *GetInfo().GetParaPortion()->GetRepaint(); |
| if( rRepaint.Top() > Y() ) |
| rRepaint.Top( Y() ); |
| for( MSHORT i = 1; i < GetDropLines(); ++i ) |
| NextLine(); |
| const SwTwips nBottom = Y() + GetLineHeight() - 1; |
| if( rRepaint.Bottom() < nBottom ) |
| rRepaint.Bottom( nBottom ); |
| } |
| |
| |