Files
libreoffice/sw/source/core/text/txttab.cxx
Oliver Bolte 440aea99e5 INTEGRATION: CWS thaiblocksatz (1.21.146); FILE MERGED
2005/04/06 06:30:31 fme 1.21.146.1: #i41860# Thai justified alignment needs some more precision to prevent rounding errors
2005-04-18 13:40:30 +00:00

581 lines
19 KiB
C++

/*************************************************************************
*
* $RCSfile: txttab.cxx,v $
*
* $Revision: 1.22 $
*
* last change: $Author: obo $ $Date: 2005-04-18 14:40:30 $
*
* The Contents of this file are made available subject to the terms of
* either of the following licenses
*
* - GNU Lesser General Public License Version 2.1
* - Sun Industry Standards Source License Version 1.1
*
* Sun Microsystems Inc., October, 2000
*
* GNU Lesser General Public License Version 2.1
* =============================================
* Copyright 2000 by Sun Microsystems, Inc.
* 901 San Antonio Road, Palo Alto, CA 94303, USA
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
*
* Sun Industry Standards Source License Version 1.1
* =================================================
* The contents of this file are subject to the Sun Industry Standards
* Source License Version 1.1 (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.openoffice.org/license.html.
*
* Software provided under this License is provided on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
* WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
* MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
* See the License for the specific provisions governing your rights and
* obligations concerning the Software.
*
* The Initial Developer of the Original Code is: Sun Microsystems, Inc.
*
* Copyright: 2000 by Sun Microsystems, Inc.
*
* All Rights Reserved.
*
* Contributor(s): _______________________________________
*
*
************************************************************************/
#pragma hdrstop
#include "hintids.hxx"
#ifndef _SVX_LRSPITEM_HXX //autogen
#include <svx/lrspitem.hxx>
#endif
#ifndef _SVX_TSTPITEM_HXX //autogen
#include <svx/tstpitem.hxx>
#endif
#ifndef _FRMATR_HXX
#include <frmatr.hxx>
#endif
#ifndef _SW_PORTIONHANDLER_HXX
#include <SwPortionHandler.hxx>
#endif
#ifndef _VIEWSH_HXX
#include <viewsh.hxx>
#endif
#include "viewopt.hxx" // SwViewOptions
#include "txtcfg.hxx"
#include "portab.hxx"
#include "inftxt.hxx"
#include "itrform2.hxx"
#include "txtfrm.hxx"
/*************************************************************************
* SwLineInfo::GetTabStop()
*************************************************************************/
/* Die Werte in SvxTabStop::nTabPos liegen immer relativ zum linken PrtRand
* vor. Tabs, die im Bereich des Erstzeileneinzugs liegen, sind also negativ.
* nLeft ist der linke PrtRand
* nRight ist der rechte PrtRand
* nLinePos die aktuelle Position.
* Es wird der erste Tabstop returnt, der groesser ist als nLinePos.
*/
const SvxTabStop *SwLineInfo::GetTabStop( const SwTwips nLinePos,
const SwTwips nLeft, const SwTwips nRight ) const
{
// Mit den KSHORTs aufpassen, falls nLinePos < nLeft
SwTwips nPos = nLinePos;
nPos -= nLeft;
for( MSHORT i = 0; i < pRuler->Count(); ++i )
{
const SvxTabStop &rTabStop = pRuler->operator[](i);
if( rTabStop.GetTabPos() > SwTwips(nRight) )
{
if ( i )
return 0;
else
return &rTabStop;
}
if( rTabStop.GetTabPos() > nPos )
return &rTabStop;
}
return 0;
}
/*************************************************************************
* SwTxtFormatter::NewTabPortion()
*************************************************************************/
SwTabPortion *SwTxtFormatter::NewTabPortion( SwTxtFormatInfo &rInf, bool bAuto ) const
{
SwTabPortion *pTabPor = 0;
SwTabPortion *pLastTab = rInf.GetLastTab();
if( pLastTab && pLastTab->IsTabCntPortion() )
if( pLastTab->PostFormat( rInf ) )
return 0;
// Wir suchen den naechsten Tab. Wenn gerade ein rechts-Tab unterwegs
// ist, so koennen wir uns nicht auf rInf.X() beziehen.
SwTwips nTabPos = rInf.GetLastTab() ? rInf.GetLastTab()->GetTabPos() : 0;
if( nTabPos < rInf.X() )
nTabPos = rInf.X();
xub_Unicode cFill, cDec;
SvxTabAdjust eAdj;
KSHORT nNewTabPos;
{
/*
nPos ist der Offset in der Zeile.
Die Tabulatoren haben ihren 0-Punkt bei Frm().Left().
Die Zeilen beginnen ab Frm.Left() + Prt.Left().
In dieser Methode wird zwischen beiden Koordinatensystemen
konvertiert (vgl. rInf.GetTabPos).
*/
const SwTwips nTabLeft = pFrm->Frm().Left() +
( pFrm->IsRightToLeft() ?
pFrm->GetAttrSet()->GetLRSpace().GetRight() :
pFrm->GetAttrSet()->GetLRSpace().GetTxtLeft() );
const SwTwips nLinePos = GetLeftMargin();
const SwTwips nLineTab = nLinePos + nTabPos;
SwTwips nRight = Right();
if ( pFrm->IsVertical() )
{
Point aRightTop( nRight, pFrm->Frm().Top() );
pFrm->SwitchHorizontalToVertical( aRightTop );
nRight = aRightTop.Y();
}
SwTwips nNextPos;
//
// First, we examine the tab stops set at the paragraph style or
// any hard set tab stops:
// Note: If there are no user defined tab stops, there is always a
// default tab stop.
//
const SvxTabStop* pTabStop =
aLineInf.GetTabStop( nLineTab, nTabLeft, nRight );
if( pTabStop )
{
cFill = ' ' != pTabStop->GetFill() ? pTabStop->GetFill() : 0;
cDec = pTabStop->GetDecimal();
eAdj = pTabStop->GetAdjustment();
nNextPos = pTabStop->GetTabPos();
}
else
{
KSHORT nDefTabDist = aLineInf.GetDefTabStop();
if( KSHRT_MAX == nDefTabDist )
{
const SvxTabStopItem& rTab =
(const SvxTabStopItem &)pFrm->GetAttrSet()->
GetPool()->GetDefaultItem( RES_PARATR_TABSTOP );
if( rTab.Count() )
nDefTabDist = (KSHORT)rTab.GetStart()->GetTabPos();
else
nDefTabDist = SVX_TAB_DEFDIST;
aLineInf.SetDefTabStop( nDefTabDist );
}
SwTwips nCount = nLineTab;
nCount -= nTabLeft;
// Bei negativen Werten rundet "/" auf, "%" liefert negative Reste,
// bei positiven Werten rundet "/" ab, "%" liefert positvie Reste!
if ( nCount < 0 )
nCount = 0;
nCount /= nDefTabDist;
nNextPos = ( nCount + 1 ) * nDefTabDist ;
// --> FME 2004-09-21 #117919 Minimum tab stop width is 1 or 51 twips:
const SwTwips nMinimumTabWidth = rInf.GetVsh()->IsTabCompat() ? 0 : 50;
// <--
if( nNextPos + nTabLeft <= nLineTab + nMinimumTabWidth )
nNextPos += nDefTabDist;
cFill = 0;
eAdj = SVX_TAB_ADJUST_LEFT;
}
long nForced = 0;
if( pCurr->HasForcedLeftMargin() )
{
SwLinePortion* pPor = pCurr->GetPortion();
while( pPor && !pPor->IsFlyPortion() )
pPor = pPor->GetPortion();
if( pPor )
nForced = pPor->Width();
}
if( nTabLeft + nForced > nLineTab && nNextPos > 0 )
{
eAdj = SVX_TAB_ADJUST_DEFAULT;
cFill = 0;
nNextPos = nForced;
}
nNextPos += nTabLeft;
nNextPos -= nLinePos;
ASSERT( nNextPos >= 0, "GetTabStop: Don't go back!" );
nNewTabPos = KSHORT(nNextPos);
}
if ( bAuto )
{
if ( SVX_TAB_ADJUST_DECIMAL == eAdj )
pTabPor = new SwAutoTabDecimalPortion( nNewTabPos, cDec, cFill );
}
else
{
switch( eAdj )
{
case SVX_TAB_ADJUST_RIGHT :
{
pTabPor = new SwTabRightPortion( nNewTabPos, cFill );
break;
}
case SVX_TAB_ADJUST_CENTER :
{
pTabPor = new SwTabCenterPortion( nNewTabPos, cFill );
break;
}
case SVX_TAB_ADJUST_DECIMAL :
{
pTabPor = new SwTabDecimalPortion( nNewTabPos, cDec, cFill );
break;
}
default:
{
ASSERT( SVX_TAB_ADJUST_LEFT == eAdj || SVX_TAB_ADJUST_DEFAULT == eAdj,
"+SwTxtFormatter::NewTabPortion: unknown adjustment" );
pTabPor = new SwTabLeftPortion( nNewTabPos, cFill );
break;
}
}
}
// Vorhandensein von Tabulatoren anzeigen ... ist nicht mehr noetig
// pCurr->SetTabulation();
// Aus Sicherheitsgruenden lassen wir uns die Daten errechnen
// pTabPor->Height( pLast->Height() );
// pTabPor->SetAscent( pLast->GetAscent() );
return pTabPor;
}
/*************************************************************************
* SwTabPortion::SwTabPortion()
*************************************************************************/
// Die Basisklasse wird erstmal ohne alles initialisiert.
SwTabPortion::SwTabPortion( const KSHORT nTabPos, const xub_Unicode cFill )
: SwFixPortion( 0, 0 ), nTabPos(nTabPos), cFill(cFill)
{
nLineLength = 1;
#ifndef PRODUCT
if( IsFilled() )
{
ASSERT( ' ' != cFill, "SwTabPortion::CTOR: blanks ?!" );
}
#endif
SetWhichPor( POR_TAB );
}
/*************************************************************************
* virtual SwTabPortion::Format()
*************************************************************************/
sal_Bool SwTabPortion::Format( SwTxtFormatInfo &rInf )
{
SwTabPortion *pLastTab = rInf.GetLastTab();
if( pLastTab == this )
return PostFormat( rInf );
if( pLastTab )
pLastTab->PostFormat( rInf );
return PreFormat( rInf );
}
/*************************************************************************
* virtual SwTabPortion::FormatEOL()
*************************************************************************/
void SwTabPortion::FormatEOL( SwTxtFormatInfo &rInf )
{
if( rInf.GetLastTab() == this && !IsTabLeftPortion() )
PostFormat( rInf );
}
/*************************************************************************
* SwTabPortion::PreFormat()
*************************************************************************/
sal_Bool SwTabPortion::PreFormat( SwTxtFormatInfo &rInf )
{
ASSERT( rInf.X() <= GetTabPos(), "SwTabPortion::PreFormat: rush hour" );
// Hier lassen wir uns nieder...
Fix( static_cast<USHORT>(rInf.X()) );
const bool bTabCompat = rInf.GetVsh()->IsTabCompat();
// Die Mindestbreite eines Tabs ist immer mindestens ein Blank
// --> FME 2004-11-25 #i37686# In compatibility mode, the minimum width
// should be 1, even for non-left tab stops.
USHORT nMinimumTabWidth = 1;
// <--
if ( !bTabCompat )
{
XubString aTmp( ' ' );
SwTxtSizeInfo aInf( rInf, aTmp );
nMinimumTabWidth = aInf.GetTxtSize().Width();
}
PrtWidth( nMinimumTabWidth );
// Break tab stop to next line if:
// 1. Minmal width does not fit to line anymore.
// 2. An underflow event was called for the tab portion.
sal_Bool bFull = ( bTabCompat && rInf.IsUnderFlow() ) ||
rInf.Width() <= rInf.X() + PrtWidth();
// #95477# Rotated tab stops get the width of one blank
const USHORT nDir = rInf.GetFont()->GetOrientation( rInf.GetTxtFrm()->IsVertical() );
if( ! bFull && 0 == nDir )
{
const MSHORT nWhich = GetWhichPor();
switch( nWhich )
{
case POR_TABRIGHT:
case POR_TABDECIMAL:
case POR_TABCENTER:
{
if( POR_TABDECIMAL == nWhich )
rInf.SetTabDecimal(
((SwTabDecimalPortion*)this)->GetTabDecimal());
rInf.SetLastTab( this );
break;
}
case POR_TABLEFT:
{
PrtWidth( static_cast<USHORT>(GetTabPos() - rInf.X()) );
bFull = rInf.Width() <= rInf.X() + PrtWidth();
// In tabulator compatibility mode, we reset the bFull flag
// if the tabulator is at the end of the paragraph and the
// tab stop position is outside the frame:
if ( bFull && bTabCompat &&
rInf.GetIdx() + GetLen() == rInf.GetTxt().Len() &&
GetTabPos() >= rInf.GetTxtFrm()->Frm().Width() )
bFull = sal_False;
break;
}
default: ASSERT( !this, "SwTabPortion::PreFormat: unknown adjustment" );
}
}
if( bFull )
{
// Wir muessen aufpassen, dass wir nicht endlos schleifen,
// wenn die Breite kleiner ist, als ein Blank ...
if( rInf.GetIdx() == rInf.GetLineStart() &&
// --> FME 2005-01-19 #119175# TabStop should be forced to current
// line if there is a fly reducing the line width:
!rInf.GetFly() )
// <--
{
PrtWidth( static_cast<USHORT>(rInf.Width() - rInf.X()) );
SetFixWidth( PrtWidth() );
}
else
{
Height( 0 );
Width( 0 );
SetLen( 0 );
SetAscent( 0 );
SetPortion( NULL ); //?????
}
return sal_True;
}
else
{
// Ein Kunstgriff mit Effekt: Die neuen Tabportions verhalten sich nun
// so, wie FlyFrms, die in der Zeile stehen - inklusive Adjustment !
SetFixWidth( PrtWidth() );
return sal_False;
}
}
/*************************************************************************
* SwTabPortion::PostFormat()
*************************************************************************/
sal_Bool SwTabPortion::PostFormat( SwTxtFormatInfo &rInf )
{
const KSHORT nRight = Min( GetTabPos(), rInf.Width() );
const SwLinePortion *pPor = GetPortion();
KSHORT nPorWidth = 0;
while( pPor )
{
DBG_LOOP;
nPorWidth += pPor->Width();
pPor = pPor->GetPortion();
}
const MSHORT nWhich = GetWhichPor();
ASSERT( POR_TABLEFT != nWhich, "SwTabPortion::PostFormat: already formatted" );
const KSHORT nDiffWidth = nRight - Fix();
if( POR_TABCENTER == nWhich )
{
// zentrierte Tabs bereiten Probleme:
// Wir muessen den Anteil herausfinden, der noch auf die Zeile passt.
KSHORT nNewWidth = nPorWidth /2;
if( nNewWidth > rInf.Width() - nRight )
nNewWidth = nPorWidth - (rInf.Width() - nRight);
nPorWidth = nNewWidth;
}
if( nDiffWidth > nPorWidth )
{
const KSHORT nOldWidth = GetFixWidth();
const KSHORT nAdjDiff = nDiffWidth - nPorWidth;
if( nAdjDiff > GetFixWidth() )
PrtWidth( nAdjDiff );
// Nicht erschrecken: wir muessen rInf weiterschieben.
// Immerhin waren wir als Rechtstab bislang nur ein Blank breit.
// Da wir uns jetzt aufgespannt haben, muss der Differenzbetrag
// auf rInf.X() addiert werden !
rInf.X( rInf.X() + PrtWidth() - nOldWidth );
}
SetFixWidth( PrtWidth() );
// letzte Werte zuruecksetzen
rInf.SetLastTab(0);
if( POR_TABDECIMAL == nWhich )
rInf.SetTabDecimal(0);
return rInf.Width() <= rInf.X();
}
/*************************************************************************
* virtual SwTabPortion::Paint()
*
* Ex: LineIter::DrawTab()
*************************************************************************/
void SwTabPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
#ifndef PRODUCT
// Wir wollen uns die Fixbreite anzeigen
if( rInf.OnWin() && OPTDBG( rInf ) &&
!rInf.GetOpt().IsPagePreview() && \
!rInf.GetOpt().IsReadonly() && \
SwViewOption::IsFieldShadings() )
{
const KSHORT nWidth = PrtWidth();
((SwTabPortion*)this)->PrtWidth( GetFixWidth() );
rInf.DrawViewOpt( *this, POR_TAB );
((SwTabPortion*)this)->PrtWidth( nWidth );
}
#endif
rInf.DrawBackBrush( *this );
// do we have to repaint a post it portion?
if( rInf.OnWin() && pPortion && !pPortion->Width() )
pPortion->PrePaint( rInf, this );
// Darstellung von Sonderzeichen
if( rInf.OnWin() && rInf.GetOpt().IsTab() )
{
// gefuellte Tabs werden grau hinterlegt.
if( IsFilled() )
rInf.DrawViewOpt( *this, POR_TAB );
else
rInf.DrawTab( *this );
}
// 6842: Tabs sollen auf einmal wieder unterstrichen werden.
if( rInf.GetFont()->IsPaintBlank() )
{
// Tabs mit Fuellung
XubString aTxt( ' ' );
const KSHORT nCharWidth = rInf.GetTxtSize( aTxt ).Width();
// robust:
if( nCharWidth )
{
// 6864: immer mit Kerning, auch auf dem Drucker!
KSHORT nChar = Width() / nCharWidth;
rInf.DrawText( aTxt.Fill( nChar, ' ' ), *this, 0, nChar, sal_True );
}
}
// Ausgabe von Fuellzeichen
if( IsFilled() )
{
// Tabs mit Fuellung
XubString aTxt( cFill );
const KSHORT nCharWidth = rInf.GetTxtSize( aTxt ).Width();
#if OSL_DEBUG_LEVEL > 1
ASSERT( nCharWidth, "!SwTabPortion::Paint: sophisticated tabchar" );
#endif
// robust:
if( nCharWidth )
{
// 6864: immer mit Kerning, auch auf dem Drucker!
KSHORT nChar = Width() / nCharWidth;
if ( cFill == '_' )
++nChar; // damit keine Luecken entstehen (Bug 13430)
rInf.DrawText( aTxt.Fill( nChar, cFill ), *this, 0, nChar, sal_True );
}
}
}
/*************************************************************************
* virtual SwAutoTabDecimalPortion::Paint()
*************************************************************************/
void SwAutoTabDecimalPortion::Paint( const SwTxtPaintInfo &rInf ) const
{
}
/*************************************************************************
* virtual SwTabPortion::HandlePortion()
*************************************************************************/
void SwTabPortion::HandlePortion( SwPortionHandler& rPH ) const
{
rPH.Text( GetLen(), GetWhichPor() );
}