blob: c333e3152c069abe69a99bb39db6fbf7345cdac4 [file] [log] [blame]
/**************************************************************
*
* 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 <ctype.h>
#ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_
#include <com/sun/star/i18n/ScriptType.hdl>
#endif
#include <hintids.hxx> // CH_TXTATR
#include <errhdl.hxx> // ASSERT
#include <SwPortionHandler.hxx>
#include <txtcfg.hxx>
#include <porlay.hxx>
#include <inftxt.hxx>
#include <guess.hxx> // SwTxtGuess, Zeilenumbruch
#include <porglue.hxx>
#include <portab.hxx> // pLastTab->
#include <porfld.hxx> // SwFldPortion
#include <wrong.hxx>
#include <viewsh.hxx>
#include <IDocumentSettingAccess.hxx>
#include <viewopt.hxx> // SwViewOptions
#include <IMark.hxx>
#include <pam.hxx>
#include <doc.hxx>
#include <xmloff/odffields.hxx>
#include <vcl/pdfextoutdevdata.hxx>
#if OSL_DEBUG_LEVEL > 1
const sal_Char *GetLangName( const MSHORT nLang );
#endif
using namespace ::sw::mark;
using namespace ::com::sun::star;
using namespace ::com::sun::star::i18n::ScriptType;
/*************************************************************************
* lcl_AddSpace
* Returns for how many characters an extra space has to be added
* (for justified alignment).
*************************************************************************/
sal_uInt16 lcl_AddSpace( const SwTxtSizeInfo &rInf, const XubString* pStr,
const SwLinePortion& rPor )
{
xub_StrLen nPos, nEnd;
const SwScriptInfo* pSI = 0;
if ( pStr )
{
// passing a string means we are inside a field
nPos = 0;
nEnd = pStr->Len();
}
else
{
nPos = rInf.GetIdx();
nEnd = rInf.GetIdx() + rPor.GetLen();
pStr = &rInf.GetTxt();
pSI = &((SwParaPortion*)rInf.GetParaPortion())->GetScriptInfo();
}
sal_uInt16 nCnt = 0;
sal_uInt8 nScript = 0;
// If portion consists of Asian characters and language is not
// Korean, we add extra space to each character.
// first we get the script type
if ( pSI )
nScript = pSI->ScriptType( nPos );
else if ( pBreakIt->GetBreakIter().is() )
nScript = (sal_uInt8)pBreakIt->GetBreakIter()->getScriptType( *pStr, nPos );
// Note: rInf.GetIdx() can differ from nPos,
// e.g., when rPor is a field portion. nPos referes to the string passed
// to the function, rInf.GetIdx() referes to the original string.
// We try to find out which justification mode is required. This is done by
// evaluating the script type and the language attribute set for this portion
// Asian Justification: Each character get some extra space
if ( nEnd > nPos && ASIAN == nScript )
{
LanguageType aLang =
rInf.GetTxtFrm()->GetTxtNode()->GetLang( rInf.GetIdx(), 1, nScript );
if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang )
{
const SwLinePortion* pPor = rPor.GetPortion();
if ( pPor && ( pPor->IsKernPortion() ||
pPor->IsControlCharPortion() ||
pPor->IsPostItsPortion() ) )
pPor = pPor->GetPortion();
nCnt += nEnd - nPos;
if ( !pPor || pPor->IsHolePortion() || pPor->InFixMargGrp() ||
pPor->IsBreakPortion() )
--nCnt;
return nCnt;
}
}
// Kashida Justification: Insert Kashidas
if ( nEnd > nPos && pSI && COMPLEX == nScript )
{
if ( SwScriptInfo::IsArabicText( *pStr, nPos, nEnd - nPos ) && pSI->CountKashida() )
{
const sal_uInt16 nKashRes = pSI->KashidaJustify( 0, 0, nPos, nEnd - nPos );
// i60591: need to check result of KashidaJustify
// determine if kashida justification is applicable
if( nKashRes != STRING_LEN )
return nKashRes;
}
}
// Thai Justification: Each character cell gets some extra space
if ( nEnd > nPos && COMPLEX == nScript )
{
LanguageType aLang =
rInf.GetTxtFrm()->GetTxtNode()->GetLang( rInf.GetIdx(), 1, nScript );
if ( LANGUAGE_THAI == aLang )
{
nCnt = SwScriptInfo::ThaiJustify( *pStr, 0, 0, nPos, nEnd - nPos );
const SwLinePortion* pPor = rPor.GetPortion();
if ( pPor && ( pPor->IsKernPortion() ||
pPor->IsControlCharPortion() ||
pPor->IsPostItsPortion() ) )
pPor = pPor->GetPortion();
if ( nCnt && ( ! pPor || pPor->IsHolePortion() || pPor->InFixMargGrp() ) )
--nCnt;
return nCnt;
}
}
// Here starts the good old "Look for blanks and add space to them" part.
// Note: We do not want to add space to an isolated latin blank in front
// of some complex characters in RTL environment
const sal_Bool bDoNotAddSpace =
LATIN == nScript && ( nEnd == nPos + 1 ) && pSI &&
( i18n::ScriptType::COMPLEX ==
pSI->ScriptType( nPos + 1 ) ) &&
rInf.GetTxtFrm() && rInf.GetTxtFrm()->IsRightToLeft();
if ( bDoNotAddSpace )
return nCnt;
for ( ; nPos < nEnd; ++nPos )
{
if( CH_BLANK == pStr->GetChar( nPos ) )
++nCnt;
}
// We still have to examine the next character:
// If the next character is ASIAN and not KOREAN we have
// to add an extra space
// nPos referes to the original string, even if a field string has
// been passed to this function
nPos = rInf.GetIdx() + rPor.GetLen();
if ( nPos < rInf.GetTxt().Len() )
{
sal_uInt8 nNextScript = 0;
const SwLinePortion* pPor = rPor.GetPortion();
if ( pPor && pPor->IsKernPortion() )
pPor = pPor->GetPortion();
if ( ! pBreakIt->GetBreakIter().is() || ! pPor || pPor->InFixMargGrp() )
return nCnt;
// next character is inside a field?
if ( CH_TXTATR_BREAKWORD == rInf.GetChar( nPos ) && pPor->InExpGrp() )
{
sal_Bool bOldOnWin = rInf.OnWin();
((SwTxtSizeInfo &)rInf).SetOnWin( sal_False );
XubString aStr( aEmptyStr );
pPor->GetExpTxt( rInf, aStr );
((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin );
nNextScript = (sal_uInt8)pBreakIt->GetBreakIter()->getScriptType( aStr, 0 );
}
else
nNextScript = (sal_uInt8)pBreakIt->GetBreakIter()->getScriptType( rInf.GetTxt(), nPos );
if( ASIAN == nNextScript )
{
LanguageType aLang =
rInf.GetTxtFrm()->GetTxtNode()->GetLang( nPos, 1, nNextScript );
if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang )
++nCnt;
}
}
return nCnt;
}
/*************************************************************************
* class SwTxtPortion
*************************************************************************/
SwTxtPortion::SwTxtPortion( const SwLinePortion &rPortion )
: SwLinePortion( rPortion )
{
SetWhichPor( POR_TXT );
}
/*************************************************************************
* SwTxtPortion::BreakCut()
*************************************************************************/
void SwTxtPortion::BreakCut( SwTxtFormatInfo &rInf, const SwTxtGuess &rGuess )
{
// Das Wort/Zeichen ist groesser als die Zeile
// Sonderfall Nr.1: Das Wort ist groesser als die Zeile
// Wir kappen...
const KSHORT nLineWidth = (KSHORT)(rInf.Width() - rInf.X());
xub_StrLen nLen = rGuess.CutPos() - rInf.GetIdx();
if( nLen )
{
// special case: guess does not always provide the correct
// width, only in common cases.
if ( !rGuess.BreakWidth() )
{
rInf.SetLen( nLen );
SetLen( nLen );
CalcTxtSize( rInf );
// changing these values requires also changing them in
// guess.cxx
KSHORT nItalic = 0;
if( ITALIC_NONE != rInf.GetFont()->GetItalic() && !rInf.NotEOL() )
{
nItalic = Height() / 12;
}
Width( Width() + nItalic );
}
else
{
Width( rGuess.BreakWidth() );
SetLen( nLen );
}
}
// special case: first character does not fit to line
else if ( rGuess.CutPos() == rInf.GetLineStart() )
{
SetLen( 1 );
Width( nLineWidth );
}
else
{
SetLen( 0 );
Width( 0 );
}
}
/*************************************************************************
* SwTxtPortion::BreakUnderflow()
*************************************************************************/
void SwTxtPortion::BreakUnderflow( SwTxtFormatInfo &rInf )
{
Truncate();
Height( 0 );
Width( 0 );
SetLen( 0 );
SetAscent( 0 );
rInf.SetUnderFlow( this );
}
/*************************************************************************
* SwTxtPortion::_Format()
*************************************************************************/
sal_Bool lcl_HasContent( const SwFldPortion& rFld, SwTxtFormatInfo &rInf )
{
String aTxt;
return rFld.GetExpTxt( rInf, aTxt ) && aTxt.Len();
}
sal_Bool SwTxtPortion::_Format( SwTxtFormatInfo &rInf )
{
// 5744: wenn nur der Trennstrich nicht mehr passt,
// muss trotzdem das Wort umgebrochen werden, ansonsten return sal_True!
if( rInf.IsUnderFlow() && rInf.GetSoftHyphPos() )
{
// soft hyphen portion has triggered an underflow event because
// of an alternative spelling position
sal_Bool bFull = sal_False;
const sal_Bool bHyph = rInf.ChgHyph( sal_True );
if( rInf.IsHyphenate() )
{
SwTxtGuess aGuess;
// check for alternative spelling left from the soft hyphen
// this should usually be true but
aGuess.AlternativeSpelling( rInf, rInf.GetSoftHyphPos() - 1 );
bFull = CreateHyphen( rInf, aGuess );
ASSERT( bFull, "Problem with hyphenation!!!" );
}
rInf.ChgHyph( bHyph );
rInf.SetSoftHyphPos( 0 );
return bFull;
}
SwTxtGuess aGuess;
const sal_Bool bFull = !aGuess.Guess( *this, rInf, Height() );
// these are the possible cases:
// A Portion fits to current line
// B Portion does not fit to current line but a possible line break
// within the portion has been found by the break iterator, 2 subcases
// B1 break is hyphen
// B2 break is word end
// C Portion does not fit to current line and no possible line break
// has been found by break iterator, 2 subcases:
// C1 break iterator found a possible line break in portion before us
// ==> this break is used (underflow)
// C2 break iterator does not found a possible line break at all:
// ==> line break
// case A: line not yet full
if ( !bFull )
{
Width( aGuess.BreakWidth() );
// Vorsicht !
if( !InExpGrp() || InFldGrp() )
SetLen( rInf.GetLen() );
short nKern = rInf.GetFont()->CheckKerning();
if( nKern > 0 && rInf.Width() < rInf.X() + Width() + nKern )
{
nKern = (short)(rInf.Width() - rInf.X() - Width() - 1);
if( nKern < 0 )
nKern = 0;
}
if( nKern )
new SwKernPortion( *this, nKern );
}
// special case: hanging portion
else if( bFull && aGuess.GetHangingPortion() )
{
Width( aGuess.BreakWidth() );
SetLen( aGuess.BreakPos() - rInf.GetIdx() );
Insert( aGuess.GetHangingPortion() );
aGuess.GetHangingPortion()->SetAscent( GetAscent() );
aGuess.ClearHangingPortion();
}
// breakPos >= index
else if ( aGuess.BreakPos() >= rInf.GetIdx() && aGuess.BreakPos() != STRING_LEN )
{
// case B1
if( aGuess.HyphWord().is() && aGuess.BreakPos() > rInf.GetLineStart()
&& ( aGuess.BreakPos() > rInf.GetIdx() ||
( rInf.GetLast() && ! rInf.GetLast()->IsFlyPortion() ) ) )
{
CreateHyphen( rInf, aGuess );
if ( rInf.GetFly() )
rInf.GetRoot()->SetMidHyph( sal_True );
else
rInf.GetRoot()->SetEndHyph( sal_True );
}
// case C1
// - Footnote portions with fake line start (i.e., not at beginning of line)
// should keep together with the text portion. (Note: no keep together
// with only footnote portions.
// - TabPortions not at beginning of line should keep together with the
// text portion, if they are not followed by a blank
// (work around different definition of tab stop character - breaking or
// non breaking character - in compatibility mode)
else if ( ( IsFtnPortion() && rInf.IsFakeLineStart() &&
// --> OD 2010-01-29 #b6921213#
rInf.IsOtherThanFtnInside() ) ||
// <--
( rInf.GetLast() &&
rInf.GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) &&
rInf.GetLast()->InTabGrp() &&
rInf.GetLineStart() + rInf.GetLast()->GetLen() < rInf.GetIdx() &&
aGuess.BreakPos() == rInf.GetIdx() &&
CH_BLANK != rInf.GetChar( rInf.GetIdx() ) &&
0x3000 != rInf.GetChar( rInf.GetIdx() ) ) )
BreakUnderflow( rInf );
// case B2
else if( rInf.GetIdx() > rInf.GetLineStart() ||
aGuess.BreakPos() > rInf.GetIdx() ||
// this is weird: during formatting the follow of a field
// the values rInf.GetIdx and rInf.GetLineStart are replaced
// IsFakeLineStart indicates GetIdx > GetLineStart
rInf.IsFakeLineStart() ||
rInf.GetFly() ||
rInf.IsFirstMulti() ||
( rInf.GetLast() &&
( rInf.GetLast()->IsFlyPortion() ||
( rInf.GetLast()->InFldGrp() &&
! rInf.GetLast()->InNumberGrp() &&
! rInf.GetLast()->IsErgoSumPortion() &&
lcl_HasContent(*((SwFldPortion*)rInf.GetLast()),rInf ) ) ) ) )
{
if ( rInf.X() + aGuess.BreakWidth() <= rInf.Width() )
Width( aGuess.BreakWidth() );
else
// this actually should not happen
Width( KSHORT(rInf.Width() - rInf.X()) );
SetLen( aGuess.BreakPos() - rInf.GetIdx() );
ASSERT( aGuess.BreakStart() >= aGuess.FieldDiff(),
"Trouble with expanded field portions during line break" );
const xub_StrLen nRealStart = aGuess.BreakStart() - aGuess.FieldDiff();
if( aGuess.BreakPos() < nRealStart && !InExpGrp() )
{
SwHolePortion *pNew = new SwHolePortion( *this );
pNew->SetLen( nRealStart - aGuess.BreakPos() );
Insert( pNew );
}
}
else // case C2, last exit
BreakCut( rInf, aGuess );
}
// breakPos < index or no breakpos at all
else
{
sal_Bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx();
if( aGuess.BreakPos() != STRING_LEN &&
aGuess.BreakPos() != rInf.GetLineStart() &&
( !bFirstPor || rInf.GetFly() || rInf.GetLast()->IsFlyPortion() ||
rInf.IsFirstMulti() ) &&
( !rInf.GetLast()->IsBlankPortion() || ((SwBlankPortion*)
rInf.GetLast())->MayUnderFlow( rInf, rInf.GetIdx()-1, sal_True )))
{ // case C1 (former BreakUnderflow())
BreakUnderflow( rInf );
}
else
// case C2, last exit
BreakCut( rInf, aGuess );
}
return bFull;
}
/*************************************************************************
* virtual SwTxtPortion::Format()
*************************************************************************/
sal_Bool SwTxtPortion::Format( SwTxtFormatInfo &rInf )
{
#if OSL_DEBUG_LEVEL > 1
const XubString aDbgTxt( rInf.GetTxt().Copy( rInf.GetIdx(), rInf.GetLen() ) );
#endif
if( rInf.X() > rInf.Width() || (!GetLen() && !InExpGrp()) )
{
Height( 0 );
Width( 0 );
SetLen( 0 );
SetAscent( 0 );
SetPortion( NULL ); // ????
return sal_True;
}
ASSERT( rInf.RealWidth() || (rInf.X() == rInf.Width()),
"SwTxtPortion::Format: missing real width" );
ASSERT( Height(), "SwTxtPortion::Format: missing height" );
return _Format( rInf );
}
/*************************************************************************
* virtual SwTxtPortion::FormatEOL()
*************************************************************************/
// Format end of line
// 5083: Es kann schon manchmal unguenstige Faelle geben...
// "vom {Nikolaus}", Nikolaus bricht um "vom " wird im Blocksatz
// zu "vom" und " ", wobei der Glue expandiert wird, statt in die
// MarginPortion aufzugehen.
// rInf.nIdx steht auf dem naechsten Wort, nIdx-1 ist der letzte
// Buchstabe der Portion.
void SwTxtPortion::FormatEOL( SwTxtFormatInfo &rInf )
{
if( ( !GetPortion() || ( GetPortion()->IsKernPortion() &&
!GetPortion()->GetPortion() ) ) && GetLen() &&
rInf.GetIdx() < rInf.GetTxt().Len() &&
1 < rInf.GetIdx() && ' ' == rInf.GetChar( rInf.GetIdx() - 1 )
&& !rInf.GetLast()->IsHolePortion() )
{
// calculate number of blanks
xub_StrLen nX = rInf.GetIdx() - 1;
sal_uInt16 nHoleLen = 1;
while( nX && nHoleLen < GetLen() && CH_BLANK == rInf.GetChar( --nX ) )
nHoleLen++;
// Erst uns einstellen und dann Inserten, weil wir ja auch ein
// SwLineLayout sein koennten.
KSHORT nBlankSize;
if( nHoleLen == GetLen() )
nBlankSize = Width();
else
nBlankSize = nHoleLen * rInf.GetTxtSize( ' ' ).Width();
Width( Width() - nBlankSize );
rInf.X( rInf.X() - nBlankSize );
SetLen( GetLen() - nHoleLen );
SwLinePortion *pHole = new SwHolePortion( *this );
( (SwHolePortion *)pHole )->SetBlankWidth( nBlankSize );
( (SwHolePortion *)pHole )->SetLen( nHoleLen );
Insert( pHole );
}
}
/*************************************************************************
* virtual SwTxtPortion::GetCrsrOfst()
*************************************************************************/
xub_StrLen SwTxtPortion::GetCrsrOfst( const KSHORT nOfst ) const
{
ASSERT( !this, "SwTxtPortion::GetCrsrOfst: don't use this method!" );
return SwLinePortion::GetCrsrOfst( nOfst );
}
/*************************************************************************
* virtual SwTxtPortion::GetTxtSize()
*************************************************************************/
// Das GetTxtSize() geht davon aus, dass die eigene Laenge korrekt ist
SwPosSize SwTxtPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const
{
return rInf.GetTxtSize();
}
/*************************************************************************
* virtual SwTxtPortion::Paint()
*************************************************************************/
void SwTxtPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
if (rInf.OnWin() && 1==rInf.GetLen() && CH_TXT_ATR_FIELDEND==rInf.GetTxt().GetChar(rInf.GetIdx()))
{
rInf.DrawBackBrush( *this );
const XubString aTxt = XubString::CreateFromAscii(CH_TXT_ATR_SUBST_FIELDEND);
rInf.DrawText( aTxt, *this, 0, aTxt.Len(), false );
}
else if (rInf.OnWin() && 1==rInf.GetLen() && CH_TXT_ATR_FIELDSTART==rInf.GetTxt().GetChar(rInf.GetIdx()))
{
rInf.DrawBackBrush( *this );
const XubString aTxt = XubString::CreateFromAscii(CH_TXT_ATR_SUBST_FIELDSTART);
rInf.DrawText( aTxt, *this, 0, aTxt.Len(), false );
}
else if( GetLen() )
{
rInf.DrawBackBrush( *this );
// do we have to repaint a post it portion?
if( rInf.OnWin() && pPortion && !pPortion->Width() )
pPortion->PrePaint( rInf, this );
const SwWrongList *pWrongList = rInf.GetpWrongList();
const SwWrongList *pGrammarCheckList = rInf.GetGrammarCheckList();
// SMARTTAGS
const SwWrongList *pSmarttags = rInf.GetSmartTags();
const bool bWrong = 0 != pWrongList;
const bool bGrammarCheck = 0 != pGrammarCheckList;
const bool bSmartTags = 0 != pSmarttags;
if ( bWrong || bSmartTags || bGrammarCheck )
rInf.DrawMarkedText( *this, rInf.GetLen(), sal_False, bWrong, bSmartTags, bGrammarCheck );
else
rInf.DrawText( *this, rInf.GetLen(), sal_False );
}
}
/*************************************************************************
* virtual SwTxtPortion::GetExpTxt()
*************************************************************************/
sal_Bool SwTxtPortion::GetExpTxt( const SwTxtSizeInfo &, XubString & ) const
{
return sal_False;
}
/*************************************************************************
* xub_StrLen SwTxtPortion::GetSpaceCnt()
* long SwTxtPortion::CalcSpacing()
* sind fuer den Blocksatz zustaendig und ermitteln die Anzahl der Blanks
* und den daraus resultierenden zusaetzlichen Zwischenraum
*************************************************************************/
xub_StrLen SwTxtPortion::GetSpaceCnt( const SwTxtSizeInfo &rInf,
xub_StrLen& rCharCnt ) const
{
xub_StrLen nCnt = 0;
xub_StrLen nPos = 0;
if ( InExpGrp() )
{
if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() )
{
// Bei OnWin() wird anstatt eines Leerstrings gern mal ein Blank
// zurueckgeliefert, das koennen wir hier aber gar nicht gebrauchen
sal_Bool bOldOnWin = rInf.OnWin();
((SwTxtSizeInfo &)rInf).SetOnWin( sal_False );
XubString aStr( aEmptyStr );
GetExpTxt( rInf, aStr );
((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin );
nCnt = nCnt + lcl_AddSpace( rInf, &aStr, *this );
nPos = aStr.Len();
}
}
else if( !IsDropPortion() )
{
nCnt = nCnt + lcl_AddSpace( rInf, 0, *this );
nPos = GetLen();
}
rCharCnt = rCharCnt + nPos;
return nCnt;
}
long SwTxtPortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo &rInf ) const
{
xub_StrLen nCnt = 0;
if ( InExpGrp() )
{
if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() )
{
// Bei OnWin() wird anstatt eines Leerstrings gern mal ein Blank
// zurueckgeliefert, das koennen wir hier aber gar nicht gebrauchen
sal_Bool bOldOnWin = rInf.OnWin();
((SwTxtSizeInfo &)rInf).SetOnWin( sal_False );
XubString aStr( aEmptyStr );
GetExpTxt( rInf, aStr );
((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin );
if( nSpaceAdd > 0 )
nCnt = nCnt + lcl_AddSpace( rInf, &aStr, *this );
else
{
nSpaceAdd = -nSpaceAdd;
nCnt = aStr.Len();
}
}
}
else if( !IsDropPortion() )
{
if( nSpaceAdd > 0 )
nCnt = nCnt + lcl_AddSpace( rInf, 0, *this );
else
{
nSpaceAdd = -nSpaceAdd;
nCnt = GetLen();
SwLinePortion* pPor = GetPortion();
// we do not want an extra space in front of margin portions
if ( nCnt )
{
while ( pPor && !pPor->Width() && ! pPor->IsHolePortion() )
pPor = pPor->GetPortion();
if ( !pPor || pPor->InFixMargGrp() || pPor->IsHolePortion() )
--nCnt;
}
}
}
return nCnt * nSpaceAdd / SPACING_PRECISION_FACTOR;
}
/*************************************************************************
* virtual SwTxtPortion::HandlePortion()
*************************************************************************/
void SwTxtPortion::HandlePortion( SwPortionHandler& rPH ) const
{
rPH.Text( GetLen(), GetWhichPor() );
}
SwTxtInputFldPortion::SwTxtInputFldPortion()
: SwTxtPortion()
, mbContainsInputFieldStart( false )
, mbContainsInputFieldEnd( false )
{
SetWhichPor( POR_INPUTFLD );
}
sal_Bool SwTxtInputFldPortion::Format( SwTxtFormatInfo &rInf )
{
mbContainsInputFieldStart =
rInf.GetChar( rInf.GetIdx() ) == CH_TXT_ATR_INPUTFIELDSTART;
mbContainsInputFieldEnd =
rInf.GetChar( rInf.GetIdx() + rInf.GetLen() - 1 ) == CH_TXT_ATR_INPUTFIELDEND;
sal_Bool bRet = sal_False;
if ( rInf.GetLen() == 1
&& ( mbContainsInputFieldStart || mbContainsInputFieldEnd ) )
{
Width( 0 );
}
else
{
SwTxtSlot aFormatTxt( &rInf, this, true, true, 0 );
if ( rInf.GetLen() == 0 )
{
Width( 0 );
}
else
{
const xub_StrLen nFormerLineStart = rInf.GetLineStart();
if ( !mbContainsInputFieldStart )
{
rInf.SetLineStart( 0 );
}
bRet = SwTxtPortion::Format( rInf );
if ( mbContainsInputFieldEnd )
{
// adjust portion length accordingly, if complete text fits into the portion
if ( GetLen() == rInf.GetLen() )
{
SetLen( GetLen() + 1 );
}
}
if ( mbContainsInputFieldStart )
{
// adjust portion length accordingly
SetLen( GetLen() + 1 );
}
else
{
rInf.SetLineStart( nFormerLineStart );
}
}
}
return bRet;
}
void SwTxtInputFldPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
if ( Width() )
{
rInf.DrawViewOpt( *this, POR_INPUTFLD );
static sal_Char sSpace = ' ';
SwTxtSlot aPaintTxt( &rInf, this, true, true,
ContainsOnlyDummyChars() ? &sSpace : 0 );
SwTxtPortion::Paint( rInf );
}
}
sal_Bool SwTxtInputFldPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const
{
xub_StrLen nIdx = rInf.GetIdx();
xub_StrLen nLen = rInf.GetLen();
if ( rInf.GetChar( rInf.GetIdx() ) == CH_TXT_ATR_INPUTFIELDSTART )
{
++nIdx;
--nLen;
}
if ( rInf.GetChar( rInf.GetIdx() + rInf.GetLen() - 1 ) == CH_TXT_ATR_INPUTFIELDEND )
{
--nLen;
}
rTxt = rInf.GetTxt().Copy( nIdx, nLen );
return sal_True;
}
SwPosSize SwTxtInputFldPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const
{
SwTxtSlot aFormatTxt( &rInf, this, true, false, 0 );
if ( rInf.GetLen() == 0 )
{
return SwPosSize( 0, 0 );
}
return rInf.GetTxtSize();
}
KSHORT SwTxtInputFldPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const
{
if( !Width()
&& ContainsOnlyDummyChars()
&& !rInf.GetOpt().IsPagePreview()
&& !rInf.GetOpt().IsReadonly()
&& SwViewOption::IsFieldShadings() )
{
return rInf.GetTxtSize( ' ' ).Width();
}
return SwTxtPortion::GetViewWidth( rInf );
}
bool SwTxtInputFldPortion::ContainsOnlyDummyChars() const
{
return GetLen() <= 2
&& mbContainsInputFieldStart
&& mbContainsInputFieldEnd;
}
/*************************************************************************
* class SwHolePortion
*************************************************************************/
SwHolePortion::SwHolePortion( const SwTxtPortion &rPor )
: nBlankWidth( 0 )
{
SetLen( 1 );
Height( rPor.Height() );
SetAscent( rPor.GetAscent() );
SetWhichPor( POR_HOLE );
}
SwLinePortion *SwHolePortion::Compress() { return this; }
/*************************************************************************
* virtual SwHolePortion::Paint()
*************************************************************************/
void SwHolePortion::Paint( const SwTxtPaintInfo &rInf ) const
{
if( !rInf.GetOut() )
return;
// #i16816# export stuff only needed for tagged pdf support
const vcl::PDFExtOutDevData* pPDFExt = dynamic_cast<const vcl::PDFExtOutDevData*>( rInf.GetOut()->GetExtOutDevData() );
if( !pPDFExt || !pPDFExt->GetIsExportTaggedPDF())
return;
// #i68503# the hole must have no decoration for a consistent visual appearance
const SwFont* pOrigFont = rInf.GetFont();
SwFont* pHoleFont = NULL;
SwFontSave* pFontSave = NULL;
if( pOrigFont->GetUnderline() != UNDERLINE_NONE
|| pOrigFont->GetOverline() != UNDERLINE_NONE
|| pOrigFont->GetStrikeout() != STRIKEOUT_NONE )
{
pHoleFont = new SwFont( *pOrigFont );
pHoleFont->SetUnderline( UNDERLINE_NONE );
pHoleFont->SetOverline( UNDERLINE_NONE );
pHoleFont->SetStrikeout( STRIKEOUT_NONE );
pFontSave = new SwFontSave( rInf, pHoleFont );
}
const XubString aTxt( ' ' );
rInf.DrawText( aTxt, *this, 0, 1, false );
delete pFontSave;
delete pHoleFont;
}
/*************************************************************************
* virtual SwHolePortion::Format()
*************************************************************************/
sal_Bool SwHolePortion::Format( SwTxtFormatInfo &rInf )
{
return rInf.IsFull() || rInf.X() >= rInf.Width();
}
/*************************************************************************
* virtual SwHolePortion::HandlePortion()
*************************************************************************/
void SwHolePortion::HandlePortion( SwPortionHandler& rPH ) const
{
rPH.Text( GetLen(), GetWhichPor() );
}
void SwFieldMarkPortion::Paint( const SwTxtPaintInfo & /*rInf*/) const
{
// These shouldn't be painted!
//SwTxtPortion::Paint(rInf);
}
sal_Bool SwFieldMarkPortion::Format( SwTxtFormatInfo & )
{
sal_Bool ret=0;
Width(0);
return ret;
}
namespace {
static sal_Int32 getCurrentListIndex( IFieldmark* pBM,
::rtl::OUString* io_pCurrentText = NULL )
{
const IFieldmark::parameter_map_t* const pParameters = pBM->GetParameters();
sal_Int32 nCurrentIdx = 0;
const IFieldmark::parameter_map_t::const_iterator pResult = pParameters->find(::rtl::OUString::createFromAscii(ODF_FORMDROPDOWN_RESULT));
if(pResult != pParameters->end())
pResult->second >>= nCurrentIdx;
if(io_pCurrentText)
{
const IFieldmark::parameter_map_t::const_iterator pListEntries = pParameters->find(::rtl::OUString::createFromAscii(ODF_FORMDROPDOWN_LISTENTRY));
if(pListEntries != pParameters->end())
{
uno::Sequence< ::rtl::OUString > vListEntries;
pListEntries->second >>= vListEntries;
if(nCurrentIdx < vListEntries.getLength())
*io_pCurrentText = vListEntries[nCurrentIdx];
}
}
return nCurrentIdx;
}
}
//FIXME Fieldbk
void SwFieldFormPortion::Paint( const SwTxtPaintInfo& rInf ) const
{
SwTxtNode* pNd = const_cast<SwTxtNode*>(rInf.GetTxtFrm()->GetTxtNode());
const SwDoc *doc=pNd->GetDoc();
SwIndex aIndex( pNd, rInf.GetIdx() );
SwPosition aPosition(*pNd, aIndex);
IFieldmark* pBM = doc->getIDocumentMarkAccess( )->getFieldmarkFor( aPosition );
OSL_ENSURE( pBM,
"SwFieldFormPortion::Paint(..)"
" - Where is my form field bookmark???");
if ( pBM != NULL )
{
if ( pBM->GetFieldname( ).equalsAscii( ODF_FORMCHECKBOX ) )
{ // a checkbox...
ICheckboxFieldmark* pCheckboxFm = dynamic_cast< ICheckboxFieldmark* >(pBM);
bool checked = pCheckboxFm->IsChecked();
rInf.DrawCheckBox(*this, checked);
}
else if ( pBM->GetFieldname( ).equalsAscii( ODF_FORMDROPDOWN ) )
{ // a list...
rtl::OUString aTxt;
rInf.DrawViewOpt( *this, POR_FLD );
rInf.DrawText( aTxt, *this, 0, 0/*aTxt.getLength()*/, false );
}
else
{
assert(0); // unknown type...
}
}
}
sal_Bool SwFieldFormPortion::Format( SwTxtFormatInfo & rInf )
{
sal_Bool ret = 0;
SwTxtNode *pNd = const_cast < SwTxtNode * >( rInf.GetTxtFrm( )->GetTxtNode( ) );
const SwDoc *doc = pNd->GetDoc( );
SwIndex aIndex( pNd, rInf.GetIdx( ) );
SwPosition aPosition( *pNd, aIndex );
IFieldmark *pBM = doc->getIDocumentMarkAccess( )->getFieldmarkFor( aPosition );
ASSERT( pBM != NULL, "Where is my form field bookmark???" );
if ( pBM != NULL )
{
if ( pBM->GetFieldname( ).equalsAscii( ODF_FORMCHECKBOX ) )
{
Width( rInf.GetTxtHeight( ) );
Height( rInf.GetTxtHeight( ) );
SetAscent( rInf.GetAscent( ) );
}
else if ( pBM->GetFieldname( ).equalsAscii( ODF_FORMDROPDOWN ) )
{
::rtl::OUString aTxt;
getCurrentListIndex( pBM, &aTxt );
SwPosSize aPosSize = rInf.GetTxtSize( aTxt );
Width( aPosSize.Width( ) );
Height( aPosSize.Height( ) );
SetAscent( rInf.GetAscent( ) );
}
else
{
assert( 0 ); // unknown type...
}
}
return ret;
}