670 lines
25 KiB
C++
670 lines
25 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* 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 .
|
|
*/
|
|
|
|
#include <hintids.hxx>
|
|
#include <comphelper/string.hxx>
|
|
#include <editeng/tstpitem.hxx>
|
|
#include <rtl/ustrbuf.hxx>
|
|
#include <IDocumentSettingAccess.hxx>
|
|
#include <doc.hxx>
|
|
#include <SwPortionHandler.hxx>
|
|
|
|
#include <viewopt.hxx>
|
|
#include "portab.hxx"
|
|
#include "inftxt.hxx"
|
|
#include "itrform2.hxx"
|
|
#include <txtfrm.hxx>
|
|
#include "porfld.hxx"
|
|
#include <memory>
|
|
|
|
/**
|
|
* #i24363# tab stops relative to indent
|
|
*
|
|
* Return the first tab stop that is > nSearchPos.
|
|
* If the tab stop is outside the print area, we
|
|
* return 0 if it is not the first tab stop.
|
|
*/
|
|
const SvxTabStop* SwLineInfo::GetTabStop(const SwTwips nSearchPos, SwTwips& nRight) const
|
|
{
|
|
for( sal_uInt16 i = 0; i < m_oRuler->Count(); ++i )
|
|
{
|
|
const SvxTabStop &rTabStop = m_oRuler->operator[](i);
|
|
if (nRight && rTabStop.GetTabPos() > nRight)
|
|
{
|
|
// Consider the first tabstop to always be in-bounds.
|
|
if (!i)
|
|
nRight = rTabStop.GetTabPos();
|
|
return i ? nullptr : &rTabStop;
|
|
}
|
|
if( rTabStop.GetTabPos() > nSearchPos )
|
|
{
|
|
if (!i && !nRight)
|
|
nRight = rTabStop.GetTabPos();
|
|
return &rTabStop;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
sal_uInt16 SwLineInfo::NumberOfTabStops() const
|
|
{
|
|
return m_oRuler->Count();
|
|
}
|
|
|
|
SwTabPortion *SwTextFormatter::NewTabPortion( SwTextFormatInfo &rInf, bool bAuto ) const
|
|
{
|
|
IDocumentSettingAccess const& rIDSA(rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess());
|
|
const bool bTabOverMargin = rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN);
|
|
const bool bTabOverSpacing = rIDSA.get(DocumentSettingId::TAB_OVER_SPACING);
|
|
const bool bTabsRelativeToIndent = rIDSA.get(DocumentSettingId::TABS_RELATIVE_TO_INDENT);
|
|
|
|
// Update search location - since Center/Decimal tabstops' width is dependent on the following text.
|
|
SwTabPortion* pTmpLastTab = rInf.GetLastTab();
|
|
if (pTmpLastTab && (pTmpLastTab->IsTabCenterPortion() || pTmpLastTab->IsTabDecimalPortion()))
|
|
pTmpLastTab->PostFormat(rInf);
|
|
|
|
sal_Unicode cFill = 0;
|
|
sal_Unicode cDec = 0;
|
|
SvxTabAdjust eAdj;
|
|
|
|
SwTwips nNewTabPos;
|
|
bool bAutoTabStop = true;
|
|
{
|
|
const bool bRTL = m_pFrame->IsRightToLeft();
|
|
// #i24363# tab stops relative to indent
|
|
// nTabLeft: The absolute value, the tab stops are relative to: Tabs origin.
|
|
|
|
// #i91133#
|
|
const SwTwips nTabLeft = bRTL
|
|
? m_pFrame->getFrameArea().Right() -
|
|
( bTabsRelativeToIndent ? GetTabLeft() : 0 )
|
|
: m_pFrame->getFrameArea().Left() +
|
|
( bTabsRelativeToIndent ? GetTabLeft() : 0 );
|
|
|
|
// The absolute position, where we started the line formatting
|
|
SwTwips nLinePos = GetLeftMargin();
|
|
if ( bRTL )
|
|
{
|
|
Point aPoint( nLinePos, 0 );
|
|
m_pFrame->SwitchLTRtoRTL( aPoint );
|
|
nLinePos = aPoint.X();
|
|
}
|
|
|
|
// The current position, relative to the line start
|
|
SwTwips nTabPos = rInf.GetLastTab() ? rInf.GetLastTab()->GetTabPos() : 0;
|
|
if( nTabPos < rInf.X() )
|
|
{
|
|
nTabPos = rInf.X();
|
|
}
|
|
|
|
// The current position in absolute coordinates
|
|
const SwTwips nCurrentAbsPos = bRTL ?
|
|
nLinePos - nTabPos :
|
|
nLinePos + nTabPos;
|
|
|
|
SwTwips nMyRight;
|
|
if ( m_pFrame->IsVertLR() )
|
|
nMyRight = Left();
|
|
else
|
|
nMyRight = Right();
|
|
|
|
if ( m_pFrame->IsVertical() )
|
|
{
|
|
Point aRightTop( nMyRight, m_pFrame->getFrameArea().Top() );
|
|
m_pFrame->SwitchHorizontalToVertical( aRightTop );
|
|
nMyRight = aRightTop.Y();
|
|
}
|
|
|
|
SwTwips nNextPos = 0;
|
|
bool bAbsoluteNextPos = false;
|
|
|
|
// #i24363# tab stops relative to indent
|
|
// nSearchPos: The current position relative to the tabs origin
|
|
const SwTwips nSearchPos = bRTL ?
|
|
nTabLeft - nCurrentAbsPos :
|
|
nCurrentAbsPos - nTabLeft;
|
|
|
|
// 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 SwTwips nOldRight = nMyRight;
|
|
// Accept left-tabstops beyond the paragraph margin for bTabOverSpacing
|
|
if (bTabOverSpacing || bTabOverMargin)
|
|
nMyRight = 0;
|
|
const SvxTabStop* pTabStop = m_aLineInf.GetTabStop( nSearchPos, nMyRight );
|
|
if (!nMyRight)
|
|
nMyRight = nOldRight;
|
|
if (pTabStop)
|
|
{
|
|
cFill = ' ' != pTabStop->GetFill() ? pTabStop->GetFill() : 0;
|
|
cDec = pTabStop->GetDecimal();
|
|
eAdj = pTabStop->GetAdjustment();
|
|
nNextPos = pTabStop->GetTabPos();
|
|
if(!bTabsRelativeToIndent && eAdj == SvxTabAdjust::Default && nSearchPos < 0)
|
|
{
|
|
//calculate default tab position of default tabs in negative indent
|
|
nNextPos = ( nSearchPos / nNextPos ) * nNextPos;
|
|
}
|
|
else if (pTabStop->GetTabPos() > nMyRight
|
|
&& pTabStop->GetAdjustment() != SvxTabAdjust::Left)
|
|
{
|
|
// A rather special situation. The tabstop found is:
|
|
// 1.) in a document compatible with MS formats
|
|
// 2.) not a left tabstop.
|
|
// 3.) not the first tabstop (in that case nMyRight was adjusted to match tabPos).
|
|
// 4.) beyond the end of the text area
|
|
// Therefore, they act like right-tabstops at the edge of the para area.
|
|
// This benefits DOCX 2013+, and doesn't hurt the earlier formats,
|
|
// since up till now these were just treated as automatic tabstops.
|
|
eAdj = SvxTabAdjust::Right;
|
|
bAbsoluteNextPos = true;
|
|
// TODO: unclear if old Word has an upper limit for center/right
|
|
// tabs, UI allows setting 55.87cm max which is still one line
|
|
if (!bTabOverMargin || o3tl::toTwips(558, o3tl::Length::mm) < nNextPos)
|
|
{
|
|
nNextPos = rInf.Width();
|
|
}
|
|
}
|
|
bAutoTabStop = false;
|
|
}
|
|
else
|
|
{
|
|
SwTwips nDefTabDist = m_aLineInf.GetDefTabStop();
|
|
if (std::numeric_limits<SwTwips>::max() == nDefTabDist)
|
|
{
|
|
const SvxTabStopItem& rTab =
|
|
m_pFrame->GetAttrSet()->GetPool()->GetUserOrPoolDefaultItem( RES_PARATR_TABSTOP );
|
|
if( rTab.Count() )
|
|
nDefTabDist = rTab[0].GetTabPos();
|
|
else
|
|
nDefTabDist = SVX_TAB_DEFDIST;
|
|
m_aLineInf.SetDefTabStop( nDefTabDist );
|
|
}
|
|
SwTwips nCount = nSearchPos;
|
|
|
|
// Minimum tab stop width is 1
|
|
if (nDefTabDist <= 0)
|
|
nDefTabDist = 1;
|
|
|
|
nCount /= nDefTabDist;
|
|
nNextPos = ( nCount < 0 || ( !nCount && nSearchPos <= 0 ) )
|
|
? ( nCount * nDefTabDist )
|
|
: ( ( nCount + 1 ) * nDefTabDist );
|
|
|
|
// --> FME 2004-09-21 #117919 Minimum tab stop width is 1 or 51 twips:
|
|
const SwTwips nMinimumTabWidth = m_pFrame->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT) ? 0 : 50;
|
|
if( ( bRTL && nTabLeft - nNextPos >= nCurrentAbsPos - nMinimumTabWidth ) ||
|
|
( !bRTL && nNextPos + nTabLeft <= nCurrentAbsPos + nMinimumTabWidth ) )
|
|
{
|
|
nNextPos += nDefTabDist;
|
|
}
|
|
cFill = 0;
|
|
eAdj = SvxTabAdjust::Left;
|
|
}
|
|
|
|
// #i115705# - correction and refactoring:
|
|
// overrule determined next tab stop position in order to apply
|
|
// a tab stop at the left margin under the following conditions:
|
|
// - the new tab portion is inside the hanging indent
|
|
// - a tab stop at the left margin is allowed
|
|
// - the determined next tab stop is a default tab stop position OR
|
|
// the determined next tab stop is beyond the left margin
|
|
{
|
|
tools::Long nLeftMarginTabPos = 0;
|
|
{
|
|
if ( !bTabsRelativeToIndent )
|
|
{
|
|
if ( bRTL )
|
|
{
|
|
Point aPoint( Left(), 0 );
|
|
m_pFrame->SwitchLTRtoRTL( aPoint );
|
|
nLeftMarginTabPos = m_pFrame->getFrameArea().Right() - aPoint.X();
|
|
}
|
|
else
|
|
{
|
|
nLeftMarginTabPos = Left() - m_pFrame->getFrameArea().Left();
|
|
}
|
|
}
|
|
if( m_pCurr->HasForcedLeftMargin() )
|
|
{
|
|
SwLinePortion* pPor = m_pCurr->GetNextPortion();
|
|
while( pPor && !pPor->IsFlyPortion() )
|
|
{
|
|
pPor = pPor->GetNextPortion();
|
|
}
|
|
if ( pPor )
|
|
{
|
|
nLeftMarginTabPos += pPor->Width();
|
|
}
|
|
}
|
|
}
|
|
const bool bNewTabPortionInsideHangingIndent =
|
|
bRTL ? nCurrentAbsPos > nTabLeft - nLeftMarginTabPos
|
|
: nCurrentAbsPos < nTabLeft + nLeftMarginTabPos;
|
|
if ( bNewTabPortionInsideHangingIndent )
|
|
{
|
|
// If the paragraph is not inside a list having a list tab stop following
|
|
// the list label or no further tab stop found in such a paragraph or
|
|
// the next tab stop position does not equal the list tab stop,
|
|
// a tab stop at the left margin can be applied. If this condition is
|
|
// not hold, it is overruled by compatibility option TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST.
|
|
const bool bTabAtLeftMarginAllowed =
|
|
( !m_aLineInf.IsListTabStopIncluded() ||
|
|
!pTabStop ||
|
|
nNextPos != m_aLineInf.GetListTabStopPosition() ) ||
|
|
// compatibility option TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST:
|
|
m_pFrame->GetDoc().getIDocumentSettingAccess().
|
|
get(DocumentSettingId::TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST);
|
|
if ( bTabAtLeftMarginAllowed )
|
|
{
|
|
if ( !pTabStop || eAdj == SvxTabAdjust::Default ||
|
|
( nNextPos > nLeftMarginTabPos ) )
|
|
{
|
|
eAdj = SvxTabAdjust::Default;
|
|
cFill = 0;
|
|
nNextPos = nLeftMarginTabPos;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bAbsoluteNextPos)
|
|
nNextPos += bRTL ? nLinePos - nTabLeft : nTabLeft - nLinePos;
|
|
OSL_ENSURE( nNextPos >= 0, "GetTabStop: Don't go back!" );
|
|
nNewTabPos = nNextPos;
|
|
}
|
|
|
|
SwTabPortion *pTabPor = nullptr;
|
|
if ( bAuto )
|
|
{
|
|
if ( SvxTabAdjust::Decimal == eAdj &&
|
|
1 == m_aLineInf.NumberOfTabStops() )
|
|
pTabPor = new SwAutoTabDecimalPortion( nNewTabPos, cDec, cFill );
|
|
}
|
|
else
|
|
{
|
|
switch( eAdj )
|
|
{
|
|
case SvxTabAdjust::Right :
|
|
{
|
|
pTabPor = new SwTabRightPortion( nNewTabPos, cFill );
|
|
break;
|
|
}
|
|
case SvxTabAdjust::Center :
|
|
{
|
|
pTabPor = new SwTabCenterPortion( nNewTabPos, cFill );
|
|
break;
|
|
}
|
|
case SvxTabAdjust::Decimal :
|
|
{
|
|
pTabPor = new SwTabDecimalPortion( nNewTabPos, cDec, cFill );
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
OSL_ENSURE( SvxTabAdjust::Left == eAdj || SvxTabAdjust::Default == eAdj,
|
|
"+SwTextFormatter::NewTabPortion: unknown adjustment" );
|
|
pTabPor = new SwTabLeftPortion( nNewTabPos, cFill, bAutoTabStop );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (pTabPor)
|
|
rInf.UpdateTabSeen(pTabPor->GetWhichPor());
|
|
|
|
return pTabPor;
|
|
}
|
|
|
|
/**
|
|
* The base class is initialized without setting anything
|
|
*/
|
|
SwTabPortion::SwTabPortion( const SwTwips nTabPosition, const sal_Unicode cFillChar, const bool bAutoTab )
|
|
: m_nTabPos(nTabPosition), m_cFill(cFillChar), m_bAutoTabStop( bAutoTab )
|
|
{
|
|
mnLineLength = TextFrameIndex(1);
|
|
OSL_ENSURE(!IsFilled() || ' ' != m_cFill, "SwTabPortion::CTOR: blanks ?!");
|
|
SetWhichPor( PortionType::Tab );
|
|
}
|
|
|
|
bool SwTabPortion::Format( SwTextFormatInfo &rInf )
|
|
{
|
|
SwTabPortion *pLastTab = rInf.GetLastTab();
|
|
if( pLastTab == this )
|
|
return PostFormat( rInf );
|
|
if( pLastTab )
|
|
pLastTab->PostFormat( rInf );
|
|
return PreFormat(rInf, pLastTab);
|
|
}
|
|
|
|
void SwTabPortion::FormatEOL( SwTextFormatInfo &rInf )
|
|
{
|
|
if( rInf.GetLastTab() == this )
|
|
PostFormat( rInf );
|
|
}
|
|
|
|
bool SwTabPortion::PreFormat(SwTextFormatInfo &rInf, SwTabPortion const*const pLastTab)
|
|
{
|
|
OSL_ENSURE( rInf.X() <= GetTabPos(), "SwTabPortion::PreFormat: rush hour" );
|
|
|
|
// Here we settle down ...
|
|
SetFix(rInf.X());
|
|
|
|
IDocumentSettingAccess const& rIDSA(rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess());
|
|
const bool bTabCompat = rIDSA.get(DocumentSettingId::TAB_COMPAT);
|
|
const bool bTabOverflow = rIDSA.get(DocumentSettingId::TAB_OVERFLOW);
|
|
const bool bTabOverMargin = rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN);
|
|
const bool bTabOverSpacing = rIDSA.get(DocumentSettingId::TAB_OVER_SPACING);
|
|
const tools::Long nTextFrameWidth = rInf.GetTextFrame()->getFrameArea().Width();
|
|
|
|
// The minimal width of a tab is one blank at least.
|
|
// #i37686# In compatibility mode, the minimum width
|
|
// should be 1, even for non-left tab stops.
|
|
SwTwips nMinimumTabWidth = 1;
|
|
if ( !bTabCompat )
|
|
{
|
|
// #i89179#
|
|
// tab portion representing the list tab of a list label gets the
|
|
// same font as the corresponding number portion
|
|
std::optional< SwFontSave > oSave;
|
|
if ( GetLen() == TextFrameIndex(0) &&
|
|
rInf.GetLast() && rInf.GetLast()->InNumberGrp() &&
|
|
static_cast<SwNumberPortion*>(rInf.GetLast())->HasFont() )
|
|
{
|
|
const SwFont* pNumberPortionFont =
|
|
static_cast<SwNumberPortion*>(rInf.GetLast())->GetFont();
|
|
oSave.emplace( rInf, const_cast<SwFont*>(pNumberPortionFont) );
|
|
}
|
|
OUString aTmp( ' ' );
|
|
SwTextSizeInfo aInf( rInf, &aTmp );
|
|
nMinimumTabWidth = aInf.GetTextSize().Width();
|
|
}
|
|
PrtWidth( nMinimumTabWidth );
|
|
|
|
// Break tab stop to next line if:
|
|
// 1. Minimal width does not fit to line anymore.
|
|
// 2. An underflow event was called for the tab portion.
|
|
bool bFull = ( bTabCompat && rInf.IsUnderflow() ) ||
|
|
(rInf.Width() <= rInf.X() + PrtWidth() && rInf.X() <= rInf.Width()
|
|
&& (!bTabOverMargin || !pLastTab));
|
|
|
|
// #95477# Rotated tab stops get the width of one blank
|
|
const Degree10 nDir = rInf.GetFont()->GetOrientation( rInf.GetTextFrame()->IsVertical() );
|
|
|
|
if( ! bFull && 0_deg10 == nDir )
|
|
{
|
|
const PortionType nWhich = GetWhichPor();
|
|
switch( nWhich )
|
|
{
|
|
case PortionType::TabRight:
|
|
case PortionType::TabDecimal:
|
|
case PortionType::TabCenter:
|
|
{
|
|
if( PortionType::TabDecimal == nWhich )
|
|
rInf.SetTabDecimal(
|
|
static_cast<SwTabDecimalPortion*>(this)->GetTabDecimal());
|
|
rInf.SetLastTab( this );
|
|
break;
|
|
}
|
|
case PortionType::TabLeft:
|
|
{
|
|
// handle this case in PostFormat
|
|
if ((bTabOverMargin || bTabOverSpacing) && GetTabPos() > rInf.Width()
|
|
&& (!m_bAutoTabStop || rInf.Width() <= rInf.X()))
|
|
{
|
|
if (bTabOverMargin || GetTabPos() < nTextFrameWidth)
|
|
{
|
|
rInf.SetLastTab(this);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
assert(!bTabOverMargin && bTabOverSpacing && GetTabPos() >= nTextFrameWidth);
|
|
bFull = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
PrtWidth(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:
|
|
bool bAtParaEnd = rInf.GetIdx() + GetLen() == TextFrameIndex(rInf.GetText().getLength());
|
|
if ( bFull && bTabCompat &&
|
|
( ( bTabOverflow && ( rInf.IsTabOverflow() || !m_bAutoTabStop ) ) || bAtParaEnd ) &&
|
|
GetTabPos() >= nTextFrameWidth)
|
|
{
|
|
bFull = false;
|
|
if ( bTabOverflow && !m_bAutoTabStop )
|
|
rInf.SetTabOverflow( true );
|
|
}
|
|
|
|
break;
|
|
}
|
|
default: OSL_ENSURE( false, "SwTabPortion::PreFormat: unknown adjustment" );
|
|
}
|
|
}
|
|
|
|
if( bFull )
|
|
{
|
|
// We have to look for endless loops, if the width is smaller than one blank
|
|
if( rInf.GetIdx() == rInf.GetLineStart() &&
|
|
// #119175# TabStop should be forced to current
|
|
// line if there is a fly reducing the line width:
|
|
!rInf.GetFly() )
|
|
{
|
|
PrtWidth(rInf.Width() - rInf.X());
|
|
SetFixWidth( PrtWidth() );
|
|
}
|
|
else
|
|
{
|
|
Height( 0 );
|
|
Width( 0 );
|
|
SetLen( TextFrameIndex(0) );
|
|
SetAscent( 0 );
|
|
SetNextPortion( nullptr ); //?????
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// A trick with impact: The new Tabportions now behave like
|
|
// FlyFrames, located in the line - including adjustment !
|
|
SetFixWidth( PrtWidth() );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool SwTabPortion::PostFormat( SwTextFormatInfo &rInf )
|
|
{
|
|
bool bTabOverMargin = rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
|
|
DocumentSettingId::TAB_OVER_MARGIN);
|
|
bool bTabOverSpacing = rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
|
|
DocumentSettingId::TAB_OVER_SPACING);
|
|
if (rInf.GetTextFrame()->IsInSct())
|
|
bTabOverMargin = false;
|
|
|
|
// If the tab position is larger than the right margin, it gets scaled down by default.
|
|
// However, if compat mode enabled, we allow tabs to go over the margin: the rest of the paragraph is not broken into lines.
|
|
const SwTwips nRight
|
|
= bTabOverMargin
|
|
? GetTabPos()
|
|
: bTabOverSpacing
|
|
? std::min(GetTabPos(), rInf.GetTextFrame()->getFrameArea().Right())
|
|
: std::min(GetTabPos(), rInf.Width());
|
|
const SwLinePortion *pPor = GetNextPortion();
|
|
|
|
SwTwips nPorWidth = 0;
|
|
while( pPor )
|
|
{
|
|
nPorWidth = nPorWidth + pPor->Width();
|
|
pPor = pPor->GetNextPortion();
|
|
}
|
|
|
|
const PortionType nWhich = GetWhichPor();
|
|
const bool bTabCompat = rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT);
|
|
|
|
if ((bTabOverMargin || bTabOverSpacing) && PortionType::TabLeft == nWhich)
|
|
{
|
|
nPorWidth = 0;
|
|
}
|
|
|
|
// #127428# Abandon dec. tab position if line is full
|
|
if ( bTabCompat && PortionType::TabDecimal == nWhich )
|
|
{
|
|
SwTwips nPrePorWidth = static_cast<const SwTabDecimalPortion*>(this)->GetWidthOfPortionsUpToDecimalPosition();
|
|
|
|
// no value was set => no decimal character was found
|
|
if (std::numeric_limits<SwTwips>::max() != nPrePorWidth)
|
|
{
|
|
if ( !bTabOverMargin && nPrePorWidth && nPorWidth - nPrePorWidth > rInf.Width() - nRight )
|
|
{
|
|
nPrePorWidth += nPorWidth - nPrePorWidth - ( rInf.Width() - nRight );
|
|
}
|
|
|
|
nPorWidth = nPrePorWidth - 1;
|
|
}
|
|
}
|
|
|
|
if( PortionType::TabCenter == nWhich )
|
|
{
|
|
// centered tabs are problematic:
|
|
// We have to detect how much fits into the line.
|
|
SwTwips nNewWidth = nPorWidth / 2;
|
|
if (!bTabOverMargin && !bTabOverSpacing && nNewWidth > rInf.Width() - nRight)
|
|
nNewWidth = nPorWidth - (rInf.Width() - nRight);
|
|
nPorWidth = nNewWidth;
|
|
}
|
|
|
|
const SwTwips nDiffWidth = nRight - GetFix();
|
|
|
|
if( nDiffWidth > nPorWidth )
|
|
{
|
|
const SwTwips nOldWidth = GetFixWidth();
|
|
const SwTwips nAdjDiff = nDiffWidth - nPorWidth;
|
|
if( nAdjDiff > GetFixWidth() )
|
|
PrtWidth( nAdjDiff );
|
|
// Don't be afraid: we have to move rInf further.
|
|
// The right-tab till now only had the width of one blank.
|
|
// Now that we stretched, the difference had to be added to rInf.X() !
|
|
rInf.X( rInf.X() + PrtWidth() - nOldWidth );
|
|
}
|
|
SetFixWidth( PrtWidth() );
|
|
// reset last values
|
|
rInf.SetLastTab(nullptr);
|
|
if( PortionType::TabDecimal == nWhich )
|
|
rInf.SetTabDecimal(0);
|
|
|
|
return rInf.Width() <= rInf.X();
|
|
}
|
|
|
|
/**
|
|
* Ex: LineIter::DrawTab()
|
|
*/
|
|
void SwTabPortion::Paint( const SwTextPaintInfo &rInf ) const
|
|
{
|
|
// #i89179#
|
|
// tab portion representing the list tab of a list label gets the
|
|
// same font as the corresponding number portion
|
|
std::optional< SwFontSave > oSave;
|
|
bool bAfterNumbering = false;
|
|
if (GetLen() == TextFrameIndex(0))
|
|
{
|
|
const SwLinePortion* pPrevPortion =
|
|
const_cast<SwTabPortion*>(this)->FindPrevPortion( rInf.GetParaPortion() );
|
|
if ( pPrevPortion &&
|
|
pPrevPortion->InNumberGrp() &&
|
|
static_cast<const SwNumberPortion*>(pPrevPortion)->HasFont() )
|
|
{
|
|
const SwFont* pNumberPortionFont =
|
|
static_cast<const SwNumberPortion*>(pPrevPortion)->GetFont();
|
|
oSave.emplace( rInf, const_cast<SwFont*>(pNumberPortionFont) );
|
|
bAfterNumbering = true;
|
|
}
|
|
}
|
|
rInf.DrawBackBrush( *this );
|
|
if( !bAfterNumbering )
|
|
rInf.DrawBorder( *this );
|
|
|
|
// do we have to repaint a post it portion?
|
|
if( rInf.OnWin() && mpNextPortion && !mpNextPortion->Width() )
|
|
mpNextPortion->PrePaint( rInf, this );
|
|
|
|
// display special characters
|
|
if( rInf.OnWin() && rInf.GetOpt().IsTab() )
|
|
{
|
|
// filled tabs are shaded in gray
|
|
if( IsFilled() )
|
|
rInf.DrawViewOpt( *this, PortionType::Tab );
|
|
else
|
|
rInf.DrawTab( *this );
|
|
}
|
|
|
|
// Tabs should be underlined at once
|
|
if( rInf.GetFont()->IsPaintBlank() )
|
|
{
|
|
// Tabs with filling/filled tabs
|
|
const SwTwips nCharWidth = rInf.GetTextSize(OUString(' ')).Width();
|
|
|
|
// Robust:
|
|
if( nCharWidth )
|
|
{
|
|
// Always with kerning, also on printer!
|
|
sal_Int32 nChar = Width() / nCharWidth;
|
|
OUStringBuffer aBuf(nChar);
|
|
comphelper::string::padToLength(aBuf, nChar, ' ');
|
|
rInf.DrawText(aBuf.makeStringAndClear(), *this, TextFrameIndex(0),
|
|
TextFrameIndex(nChar), true);
|
|
}
|
|
}
|
|
|
|
// Display fill characters
|
|
if( !IsFilled() )
|
|
return;
|
|
|
|
// Tabs with filling/filled tabs
|
|
const SwTwips nCharWidth = rInf.GetTextSize(OUString(m_cFill)).Width();
|
|
OSL_ENSURE( nCharWidth, "!SwTabPortion::Paint: sophisticated tabchar" );
|
|
|
|
// Robust:
|
|
if( nCharWidth )
|
|
{
|
|
// Always with kerning, also on printer!
|
|
sal_Int32 nChar = Width() / nCharWidth;
|
|
if ( m_cFill == '_' )
|
|
++nChar; // to avoid gaps
|
|
OUStringBuffer aBuf(nChar);
|
|
comphelper::string::padToLength(aBuf, nChar, m_cFill);
|
|
rInf.DrawText(aBuf.makeStringAndClear(), *this, TextFrameIndex(0),
|
|
TextFrameIndex(nChar), true);
|
|
}
|
|
}
|
|
|
|
void SwAutoTabDecimalPortion::Paint( const SwTextPaintInfo & ) const
|
|
{
|
|
}
|
|
|
|
void SwTabPortion::HandlePortion( SwPortionHandler& rPH ) const
|
|
{
|
|
rPH.Text( GetLen(), GetWhichPor() );
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|