/* -*- 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 .
*/
// The SvRTF parser expects the Which-mapping passed on in the pool, not // dependent on a secondary.
SfxItemPool* pPool = &maEditDoc.GetItemPool(); while (pPool->GetSecondaryPool() && pPool->GetName() != "EditEngineItemPool")
{
pPool = pPool->GetSecondaryPool();
}
// Write out ColorList ...
SvxColorList aColorList; // COL_AUTO should be the default color, always put it first
aColorList.emplace_back(COL_AUTO);
SvxColorItem const& rDefault(maEditDoc.GetItemPool().GetUserOrPoolDefaultItem(EE_CHAR_COLOR)); if (rDefault.GetValue() != COL_AUTO) // is the default always AUTO?
{
aColorList.push_back(rDefault.GetValue());
} for (const SfxPoolItem* pItem : maEditDoc.GetItemPool().GetItemSurrogates(EE_CHAR_COLOR))
{ auto pColorItem(dynamic_cast<SvxColorItem const*>(pItem)); if (pColorItem && pColorItem->GetValue() != COL_AUTO) // may be null!
{
aColorList.push_back(pColorItem->GetValue());
}
}
rOutput.WriteChar( '{' ).WriteOString( OOO_STRING_SVTOOLS_RTF_COLORTBL ); for ( SvxColorList::size_type j = 0; j < aColorList.size(); j++ )
{
Color const color = aColorList[j]; if (color != COL_AUTO) // auto is represented by "empty" element
{
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_RED );
rOutput.WriteNumberAsString( color.GetRed() );
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_GREEN );
rOutput.WriteNumberAsString( color.GetGreen() );
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_BLUE );
rOutput.WriteNumberAsString( color.GetBlue() );
}
rOutput.WriteChar( ';' );
}
rOutput.WriteChar( '}' );
rOutput << endl;
std::unordered_map<SfxStyleSheetBase*, sal_uInt32> aStyleSheetToIdMap; // StyleSheets... if ( GetStyleSheetPool() )
{ // Collect used paragraph styles when copying to the clipboard.
std::set<SfxStyleSheetBase*> aUsedParagraphStyles; if (bClipboard)
{ for (sal_Int32 nNode = nStartNode; nNode <= nEndNode; nNode++)
{
ContentNode* pNode = maEditDoc.GetObject(nNode); if (!pNode)
{ continue;
}
SfxStyleSheet* pParaStyle = pNode->GetStyleSheet(); if (!pParaStyle)
{ continue;
}
aUsedParagraphStyles.insert(pParaStyle);
// Collect parents of the style recursively.
OUString aParent = pParaStyle->GetParent(); while (!aParent.isEmpty())
{ auto pParent = static_cast<SfxStyleSheet*>(
GetStyleSheetPool()->Find(aParent, pParaStyle->GetFamily())); if (!pParent)
{ break;
}
// Collect follows of the style recursively.
OUString aFollow = pParaStyle->GetFollow(); while (!aFollow.isEmpty())
{ auto pFollow = static_cast<SfxStyleSheet*>(
GetStyleSheetPool()->Find(aFollow, pParaStyle->GetFamily())); if (!pFollow)
{ break;
}
auto it = aUsedParagraphStyles.insert(pFollow); // A style is fine to have itself as a follow. if (!it.second)
{ // No insertion happened, so this is visited already. break;
}
aFollow = pFollow->GetFollow();
}
}
}
std::shared_ptr<SfxStyleSheetIterator> aSSSIterator = std::make_shared<SfxStyleSheetIterator>(GetStyleSheetPool(),
SfxStyleFamily::All); // fill aStyleSheetToIdMap
sal_uInt32 nId = 1; for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle;
pStyle = aSSSIterator->Next() )
{ if (bClipboard && !aUsedParagraphStyles.contains(pStyle))
{ // Don't include unused paragraph styles in the clipboard case. continue;
}
aStyleSheetToIdMap[pStyle] = nId;
nId++;
}
// Parent ... (only if necessary) if ( !pStyle->GetParent().isEmpty() && ( pStyle->GetParent() != pStyle->GetName() ) )
{
SfxStyleSheet* pParent = static_cast<SfxStyleSheet*>(GetStyleSheetPool()->Find( pStyle->GetParent(), pStyle->GetFamily() ));
DBG_ASSERT( pParent, "Parent not found!" );
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SBASEDON ); auto iter = aStyleSheetToIdMap.find(pParent);
assert(iter != aStyleSheetToIdMap.end());
nNumber = iter->second;
rOutput.WriteNumberAsString( nNumber );
}
// Next Style... (more) // we assume that we have only SfxStyleSheet in the pool
SfxStyleSheet* pNext = static_cast<SfxStyleSheet*>(pStyle); if ( !pStyle->GetFollow().isEmpty() && ( pStyle->GetFollow() != pStyle->GetName() ) )
pNext = static_cast<SfxStyleSheet*>(GetStyleSheetPool()->Find( pStyle->GetFollow(), pStyle->GetFamily() ));
DBG_ASSERT( pNext, "Next not found!" );
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SNEXT ); auto iter = aStyleSheetToIdMap.find(pNext);
assert(iter != aStyleSheetToIdMap.end());
nNumber = iter->second;
rOutput.WriteNumberAsString( nNumber );
// Name of the template...
rOutput.WriteOString( " " );
RTFOutFuncs::Out_String( rOutput, pStyle->GetName(), eDestEnc );
rOutput.WriteOString( ";}" );
nStyle++;
}
rOutput.WriteChar( '}' );
rOutput << endl;
}
}
if ( nNode == nStartNode )
{
nStartPos = aSel.Min().GetIndex();
nStartPortion = pParaPortion->GetTextPortions().FindPortion( nStartPos, nPortionStart ); if ( nStartPos != 0 )
{
aAttribItems.Clear();
lcl_FindValidAttribs( aAttribItems, pNode, nStartPos, GetI18NScriptType( EditPaM( pNode, 0 ) ) ); if ( aAttribItems.Count() )
{ // These attributes may not apply to the entire paragraph:
rOutput.WriteChar( '{' );
WriteItemListAsRTF( aAttribItems, rOutput, nNode, nStartPos, aFontTable, aColorList );
bFinishPortion = true;
}
aAttribItems.Clear();
}
} if ( nNode == nEndNode ) // can also be == nStart!
{
nEndPos = aSel.Max().GetIndex();
nEndPortion = pParaPortion->GetTextPortions().FindPortion( nEndPos, nPortionStart );
}
const EditCharAttrib* pNextFeature = pNode->GetCharAttribs().FindFeature(nIndex); // start at 0, so the index is right ... for ( sal_Int32 n = 0; n <= nEndPortion; n++ )
{ const TextPortion& rTextPortion = pParaPortion->GetTextPortions()[n]; if ( n < nStartPortion )
{
nIndex = nIndex + rTextPortion.GetLen(); continue;
}
if ( pNextFeature && ( pNextFeature->GetStart() == nIndex ) && ( pNextFeature->GetItem()->Which() != EE_FEATURE_FIELD ) )
{
WriteItemAsRTF( *pNextFeature->GetItem(), rOutput, nNode, nIndex, aFontTable, aColorList );
pNextFeature = pNode->GetCharAttribs().FindFeature( pNextFeature->GetStart() + 1 );
} else
{
aAttribItems.Clear();
sal_uInt16 nScriptTypeI18N = GetI18NScriptType( EditPaM( pNode, nIndex+1 ) );
SvtScriptType nScriptType = SvtLanguageOptions::FromI18NToSvtScriptType(nScriptTypeI18N);
rtl_TextEncoding actEncoding = eDestEnc; if ( !n || IsScriptChange( EditPaM( pNode, nIndex ) ) )
{
SfxItemSet aAttribs = GetAttribs( nNode, nIndex+1, nIndex+1 ); auto& item = aAttribs.Get(GetScriptItemId(EE_CHAR_FONTINFO, nScriptType));
aAttribItems.Insert(&item); // The actual encoding that RTF uses for the portion is defined by the font if (auto i = GetFontIndex(item, aFontTable);
i < aFontTable.size()
&& aFontTable[i]->GetCharSet() != RTL_TEXTENCODING_DONTKNOW)
actEncoding = aFontTable[i]->GetCharSet();
aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_FONTHEIGHT, nScriptType ) ) );
aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_WEIGHT, nScriptType ) ) );
aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_ITALIC, nScriptType ) ) );
aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType ) ) );
// tdf#119192: Write these font attributes at paragraph scope, so they can be // reused across multiple runs.
WriteItemListAsRTF(aAttribItems, rOutput, nNode, nIndex, aFontTable,
aColorList);
aAttribItems.Clear();
} // Insert hard attribs AFTER CJK attribs...
lcl_FindValidAttribs( aAttribItems, pNode, nIndex, nScriptTypeI18N );
void ImpEditEngine::WriteItemAsRTF( const SfxPoolItem& rItem, SvStream& rOutput, sal_Int32 nPara, sal_Int32 nPos, const std::vector<std::unique_ptr<SvxFontItem>>& rFontTable, SvxColorList& rColorList )
{
sal_uInt16 nWhich = rItem.Which(); switch ( nWhich )
{ case EE_PARA_WRITINGDIR:
{ const SvxFrameDirectionItem& rWritingMode = static_cast<const SvxFrameDirectionItem&>(rItem); if ( rWritingMode.GetValue() == SvxFrameDirection::Horizontal_RL_TB )
rOutput.WriteOString( "\\rtlpar" ); else
rOutput.WriteOString( "\\ltrpar" );
} break; case EE_PARA_OUTLLEVEL:
{
sal_Int32 nLevel = static_cast<const SfxInt16Item&>(rItem).GetValue(); if( nLevel >= 0 )
{
rOutput.WriteOString( "\\level" );
rOutput.WriteNumberAsString( nLevel );
}
} break; case EE_PARA_OUTLLRSPACE: case EE_PARA_LRSPACE:
{
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FI );
sal_Int32 nTxtFirst
= static_cast<const SvxLRSpaceItem&>(rItem).ResolveTextFirstLineOffset({});
nTxtFirst = LogicToTwips( nTxtFirst );
rOutput.WriteNumberAsString( nTxtFirst );
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_LI );
sal_uInt32 nTxtLeft = static_cast<const SvxLRSpaceItem&>(rItem).ResolveTextLeft({});
nTxtLeft = static_cast<sal_uInt32>(LogicToTwips( nTxtLeft ));
rOutput.WriteNumberAsString( nTxtLeft );
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_RI );
sal_uInt32 nTxtRight = static_cast<const SvxLRSpaceItem&>(rItem).ResolveRight({});
nTxtRight = LogicToTwips( nTxtRight);
rOutput.WriteNumberAsString( nTxtRight );
} break; case EE_PARA_ULSPACE:
{
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SB );
sal_uInt32 nUpper = static_cast<const SvxULSpaceItem&>(rItem).GetUpper();
nUpper = static_cast<sal_uInt32>(LogicToTwips( nUpper ));
rOutput.WriteNumberAsString( nUpper );
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SA );
sal_uInt32 nLower = static_cast<const SvxULSpaceItem&>(rItem).GetLower();
nLower = LogicToTwips( nLower );
rOutput.WriteNumberAsString( nLower );
} break; case EE_PARA_SBL:
{
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SL );
sal_Int32 nVal = static_cast<const SvxLineSpacingItem&>(rItem).GetLineHeight(); char cMult = '0'; if ( static_cast<const SvxLineSpacingItem&>(rItem).GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop )
{ // From where do I get the value now? // The SwRTF parser is based on a 240 Font!
nVal = static_cast<const SvxLineSpacingItem&>(rItem).GetPropLineSpace();
nVal *= 240;
nVal /= 100;
cMult = '1';
}
rOutput.WriteNumberAsString( nVal );
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SLMULT ).WriteChar( cMult );
} break; case EE_PARA_JUST:
{
SvxAdjust eJustification = static_cast<const SvxAdjustItem&>(rItem).GetAdjust(); switch ( eJustification )
{ case SvxAdjust::Center: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_QC ); break; case SvxAdjust::Right: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_QR ); break; default: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_QL ); break;
}
} break; case EE_PARA_TABS:
{ const SvxTabStopItem& rTabs = static_cast<const SvxTabStopItem&>(rItem); for ( sal_uInt16 i = 0; i < rTabs.Count(); i++ )
{ const SvxTabStop& rTab = rTabs[i];
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_TX );
rOutput.WriteNumberAsString( LogicToTwips( rTab.GetTabPos() ) );
}
} break; case EE_CHAR_COLOR:
{
SvxColorList::const_iterator const iter = std::find(
rColorList.begin(), rColorList.end(), static_cast<SvxColorItem const&>(rItem).GetValue());
assert(iter != rColorList.end());
sal_uInt32 const n = iter - rColorList.begin();
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_CF );
rOutput.WriteNumberAsString( n );
} break; case EE_CHAR_FONTINFO: case EE_CHAR_FONTINFO_CJK: case EE_CHAR_FONTINFO_CTL:
{
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_F );
rOutput.WriteNumberAsString(GetFontIndex(rItem, rFontTable));
} break; case EE_CHAR_FONTHEIGHT: case EE_CHAR_FONTHEIGHT_CJK: case EE_CHAR_FONTHEIGHT_CTL:
{
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_FS );
sal_Int32 nHeight = static_cast<const SvxFontHeightItem&>(rItem).GetHeight();
nHeight = LogicToTwips( nHeight ); // Twips => HalfPoints
nHeight /= 10;
rOutput.WriteNumberAsString( nHeight );
} break; case EE_CHAR_WEIGHT: case EE_CHAR_WEIGHT_CJK: case EE_CHAR_WEIGHT_CTL:
{
FontWeight e = static_cast<const SvxWeightItem&>(rItem).GetWeight(); switch ( e )
{ case WEIGHT_BOLD: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_B ); break; default: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_B ).WriteChar( '0' ); break;
}
} break; case EE_CHAR_UNDERLINE:
{ // Must underlined if in WordLineMode, but the information is // missing here
FontLineStyle e = static_cast<const SvxUnderlineItem&>(rItem).GetLineStyle(); switch ( e )
{ case LINESTYLE_NONE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ULNONE ); break; case LINESTYLE_SINGLE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_UL ); break; case LINESTYLE_DOUBLE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ULDB ); break; case LINESTYLE_DOTTED: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ULD ); break; default: break;
}
} break; case EE_CHAR_OVERLINE:
{
FontLineStyle e = static_cast<const SvxOverlineItem&>(rItem).GetLineStyle(); switch ( e )
{ case LINESTYLE_NONE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_OLNONE ); break; case LINESTYLE_SINGLE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_OL ); break; case LINESTYLE_DOUBLE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_OLDB ); break; case LINESTYLE_DOTTED: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_OLD ); break; default: break;
}
} break; case EE_CHAR_STRIKEOUT:
{
FontStrikeout e = static_cast<const SvxCrossedOutItem&>(rItem).GetStrikeout(); switch ( e )
{ case STRIKEOUT_SINGLE: case STRIKEOUT_DOUBLE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_STRIKE ); break; case STRIKEOUT_NONE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_STRIKE ).WriteChar( '0' ); break; default: break;
}
} break; case EE_CHAR_ITALIC: case EE_CHAR_ITALIC_CJK: case EE_CHAR_ITALIC_CTL:
{
FontItalic e = static_cast<const SvxPostureItem&>(rItem).GetPosture(); switch ( e )
{ case ITALIC_OBLIQUE: case ITALIC_NORMAL: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_I ); break; case ITALIC_NONE: rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_I ).WriteChar( '0' ); break; default: break;
}
} break; case EE_CHAR_OUTLINE:
{
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_OUTL ); if ( !static_cast<const SvxContourItem&>(rItem).GetValue() )
rOutput.WriteChar( '0' );
} break; case EE_CHAR_RELIEF:
{
FontRelief nRelief = static_cast<const SvxCharReliefItem&>(rItem).GetValue(); if ( nRelief == FontRelief::Embossed )
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_EMBO ); if ( nRelief == FontRelief::Engraved )
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_IMPR );
} break; case EE_CHAR_EMPHASISMARK:
{
FontEmphasisMark nMark = static_cast<const SvxEmphasisMarkItem&>(rItem).GetEmphasisMark(); if ( nMark == FontEmphasisMark::NONE )
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ACCNONE ); elseif ( nMark == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove) )
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ACCCOMMA ); else
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_ACCDOT );
} break; case EE_CHAR_SHADOW:
{
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_SHAD ); if ( !static_cast<const SvxShadowedItem&>(rItem).GetValue() )
rOutput.WriteChar( '0' );
} break; case EE_FEATURE_TAB:
{
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_TAB );
} break; case EE_FEATURE_LINEBR:
{
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_LINE );
} break; case EE_CHAR_KERNING:
{
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_EXPNDTW );
rOutput.WriteNumberAsString( LogicToTwips( static_cast<const SvxKerningItem&>(rItem).GetValue() ) );
} break; case EE_CHAR_PAIRKERNING:
{
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_KERNING );
rOutput.WriteNumberAsString( static_cast<const SvxAutoKernItem&>(rItem).GetValue() ? 1 : 0 );
} break; case EE_CHAR_ESCAPEMENT:
{
SvxFont aFont;
ContentNode* pNode = maEditDoc.GetObject( nPara );
SeekCursor( pNode, nPos, aFont );
MapMode aPntMode( MapUnit::MapPoint );
tools::Long nFontHeight = GetRefDevice()->LogicToLogic(
aFont.GetFontSize(), &GetRefMapMode(), &aPntMode ).Height();
nFontHeight *=2; // Half Points
sal_uInt16 const nProp = static_cast<const SvxEscapementItem&>(rItem).GetProportionalHeight();
sal_uInt16 nProp100 = nProp*100; // For SWG-Token Prop in 100th percent. short nEsc = static_cast<const SvxEscapementItem&>(rItem).GetEsc(); const FontMetric aFontMetric = GetRefDevice()->GetFontMetric(); double fFontHeight = aFontMetric.GetAscent() + aFontMetric.GetDescent(); double fAutoAscent = .8; double fAutoDescent = .2; if ( fFontHeight )
{
fAutoAscent = aFontMetric.GetAscent() / fFontHeight;
fAutoDescent = aFontMetric.GetDescent() / fFontHeight;
} if ( nEsc == DFLT_ESC_AUTO_SUPER )
{
nEsc = fAutoAscent * (100 - nProp);
nProp100++; // A 1 afterwards means 'automatic'.
} elseif ( nEsc == DFLT_ESC_AUTO_SUB )
{
nEsc = fAutoDescent * -(100 - nProp);
nProp100++;
} // SWG: if ( nEsc )
{
rOutput.WriteOString( "{\\*\\updnprop" ).WriteNumberAsString(
nProp100 ).WriteChar( '}' );
}
tools::Long nUpDown = nFontHeight * std::abs( nEsc ) / 100; if ( nEsc < 0 )
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_DN ); elseif ( nEsc > 0 )
rOutput.WriteOString( OOO_STRING_SVTOOLS_RTF_UP );
rOutput.WriteNumberAsString(nUpDown);
} break; case EE_CHAR_CASEMAP:
{ const SvxCaseMapItem& rCaseMap = static_cast<const SvxCaseMapItem&>(rItem); switch (rCaseMap.GetValue())
{ case SvxCaseMap::SmallCaps:
rOutput.WriteOString(OOO_STRING_SVTOOLS_RTF_SCAPS); break; case SvxCaseMap::Uppercase:
rOutput.WriteOString(OOO_STRING_SVTOOLS_RTF_CAPS); break; default: // Something that rtf does not support
rOutput.WriteOString(OOO_STRING_SVTOOLS_RTF_SCAPS);
rOutput.WriteNumberAsString(0);
rOutput.WriteOString(OOO_STRING_SVTOOLS_RTF_CAPS);
rOutput.WriteNumberAsString(0); break;
}
} break;
}
}
// Currently not good enough to be used for a ::Write of EETextFormat::Html, it // only supports hyperlinks over plain text
OString ImpEditEngine::GetSimpleHtml() const
{
OStringBuffer aOutput;
// If possible online spelling if ( bAllowBigObjects && bOnlyFullParagraphs && pNode->GetWrongList() )
pC->SetWrongList( pNode->GetWrongList()->Clone() );
}
// Remember the portions info in case of large text objects: // sleeper set up when Olli paragraphs not hacked! if ( bAllowBigObjects && bOnlyFullParagraphs && IsFormatted() && IsUpdateLayout() && ( nTextPortions >= nBigObjectStart ) )
{
XParaPortionList* pXList = new XParaPortionList(GetRefDevice(), GetColumnWidth(maPaperSize),
maScalingParameters.fFontX, maScalingParameters.fFontY,
maScalingParameters.fSpacingX, maScalingParameters.fSpacingY);
pTxtObj->SetPortionInfo(std::unique_ptr<XParaPortionList>(pXList)); for ( nNode = nStartNode; nNode <= nEndNode; nNode++ )
{
ParaPortion const& rParaPortion = GetParaPortions().getRef(nNode);
XParaPortion* pX = new XParaPortion;
pXList->push_back(pX);
// The TextPortions
sal_uInt16 nCount = rParaPortion.GetTextPortions().Count();
sal_uInt16 n; for ( n = 0; n < nCount; n++ )
{ const TextPortion& rTextPortion = rParaPortion.GetTextPortions()[n];
TextPortion* pNew = new TextPortion( rTextPortion );
pX->aTextPortions.Append(pNew);
}
// The lines
nCount = rParaPortion.GetLines().Count(); for ( n = 0; n < nCount; n++ )
{ const EditLine& rLine = rParaPortion.GetLines()[n];
pX->aLines.Append(std::unique_ptr<EditLine>(rLine.Clone()));
} #ifdef DBG_UTIL
sal_uInt16 nTest; int nTPLen = 0, nTxtLen = 0; for (nTest = rParaPortion.GetTextPortions().Count(); nTest;)
nTPLen += rParaPortion.GetTextPortions()[--nTest].GetLen(); for (nTest = rParaPortion.GetLines().Count(); nTest; )
nTxtLen += rParaPortion.GetLines()[--nTest].GetLen();
DBG_ASSERT(nTPLen == rParaPortion.GetNode()->Len() && nTxtLen == rParaPortion.GetNode()->Len(), "CreateBinTextObject: ParaPortion not completely formatted!"); #endif
}
} return pTxtObj;
}
void ImpEditEngine::SetText( const EditTextObject& rTextObject )
{ // Since setting a text object is not undo-able!
ResetUndoManager(); bool _bUpdate = IsUpdateLayout(); bool _bUndo = IsUndoEnabled();
DBG_ASSERT( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "From where comes the Undo in SetText ?!" );
SetUpdateLayout( _bUpdate );
EnableUndo( _bUndo );
}
// Before, paragraph count was of type sal_uInt16 so if nContents exceeded // 0xFFFF this wouldn't have worked anyway, given that nPara is used to // number paragraphs and is fearlessly incremented.
sal_Int32 nContents = static_cast<sal_Int32>(rTextObjectImpl.GetContents().size());
SAL_WARN_IF( nContents < 0, "editeng", "ImpEditEngine::InsertTextObject - contents overflow " << nContents);
sal_Int32 nPara = maEditDoc.GetPos( aPaM.GetNode() );
for (sal_Int32 n = 0; n < nContents; ++n, ++nPara)
{ const ContentInfo* pC = rTextObjectImpl.GetContents()[n].get(); bool bNewContent = aPaM.GetNode()->Len() == 0; const sal_Int32 nStartPos = aPaM.GetIndex();
// Wrap when followed by other ... if ( n < ( nContents-1) )
{ if ( bNewContent )
aPaM = ImpFastInsertParagraph( nPara+1 ); else
aPaM = ImpInsertParaBreak( aPaM, false );
}
}
void ImpEditEngine::CreateSpellInfo( bool bMultipleDocs )
{ if (!mpSpellInfo)
mpSpellInfo.reset(new SpellInfo); else
*mpSpellInfo = SpellInfo(); // reset to default values
mpSpellInfo->bMultipleDoc = bMultipleDocs; // always spell draw objects completely, starting at the top. // (spelling in only a selection or not starting with the top requires // further changes elsewhere to work properly)
mpSpellInfo->aSpellStart = EPaM();
mpSpellInfo->aSpellTo = EPaM(EE_PARA_MAX, EE_TEXTPOS_MAX);
}
// if the paragraph is not empty we need to increase the index // by one since the attribute of the character left to the // specified position is evaluated. if (nEnd > nStart) // empty para?
++nStart;
LanguageType nLangFound = mpEditEngine->GetLanguage( k, nStart ).nLang; #if OSL_DEBUG_LEVEL >= 2
lang::Locale aLocale( LanguageTag::convertToLocale( nLangFound ) ); #endif
bHasConvTxt = (nSrcLang == nLangFound) ||
(editeng::HangulHanjaConversion::IsChinese( nLangFound ) &&
editeng::HangulHanjaConversion::IsChinese( nSrcLang )); if (bHasConvTxt) return bHasConvTxt;
}
}
// if it is not just a selection and we are about to begin // with the current conversion for the very first time // we need to find the start of the current (initial) // convertible unit in order for the text conversion to give // the correct result for that. Since it is easier to obtain // the start of the word we use that though. if (!aCurSel.HasRange() && ImplGetBreakIterator().is())
{
EditPaM aWordStartPaM( SelectWord( aCurSel, i18n::WordType::DICTIONARY_WORD ).Min() );
// since #118246 / #117803 still occurs if the cursor is placed // between the two chinese characters to be converted (because both // of them are words on their own!) using the word boundary here does // not work. Thus since chinese conversion is not interactive we start // at the begin of the paragraph to solve the problem, i.e. have the // TextConversion service get those characters together in the same call.
mpConvInfo->aConvStart.nIndex = editeng::HangulHanjaConversion::IsChinese( nSrcLang )
? 0 : aWordStartPaM.GetIndex();
}
//!! optimization does not work since when update mode is false //!! the object is 'lying' about it portions, paragraphs, //!! EndPaM... later on. //!! Should not be a great problem since text boxes or cells in //!! Calc usually have only a rather short text. // // disallow formatting, updating the view, ... while // non-interactively converting the document. (saves time) //if (!bIsInteractive) // SetUpdateMode( sal_False );
// set new language attribute
SfxItemSet aNewSet(mpActiveView->GetEmptyItemSet());
aNewSet.Put( SvxLanguageItem( nLang, nLangWhichId ) );
// new font to be set?
DBG_ASSERT( pFont, "target font missing?" ); if (pFont)
{ // set new font attribute
SvxFontItem aFontItem = static_cast<const SvxFontItem&>( aNewSet.Get( nFontWhichId ) );
aFontItem.SetFamilyName( pFont->GetFamilyName());
aFontItem.SetFamily( pFont->GetFamilyType());
aFontItem.SetStyleName( pFont->GetStyleName());
aFontItem.SetPitch( pFont->GetPitch());
aFontItem.SetCharSet( pFont->GetCharSet() );
aNewSet.Put( aFontItem );
}
// apply new attributes
mpActiveView->SetAttribs(aNewSet);
mpActiveView->SetSelection(aOldSel);
}
while (aRes.isEmpty())
{ // empty paragraph found that needs to have language and font set? if (bAllowImplicitChangesForNotConvertibleText &&
mpEditEngine->GetText(mpConvInfo->aConvContinue.nPara).isEmpty())
{
sal_Int32 nPara = mpConvInfo->aConvContinue.nPara;
ESelection aESel(nPara, 0); // see comment for below same function call
SetLanguageAndFont( aESel,
nTargetLang, EE_CHAR_LANGUAGE_CJK,
pTargetFont, EE_CHAR_FONTINFO_CJK );
}
if (mpConvInfo->aConvContinue.nPara == mpConvInfo->aConvTo.nPara &&
mpConvInfo->aConvContinue.nIndex >= mpConvInfo->aConvTo.nIndex) break;
// the language attribute is obtained from the left character // (like usually all other attributes) // thus we usually have to add 1 in order to get the language // of the text right to the cursor position const sal_Int32 nLangIdx = nEnd > nStart ? nStart + 1 : nStart;
LanguageType nLangFound = mpEditEngine->GetLanguage( aCurStart.nPara, nLangIdx ).nLang; #if OSL_DEBUG_LEVEL >= 2
lang::Locale aLocale( LanguageTag::convertToLocale( nLangFound ) ); #endif bool bLangOk = (nLangFound == nSrcLang) ||
(editeng::HangulHanjaConversion::IsChinese( nLangFound ) &&
editeng::HangulHanjaConversion::IsChinese( nSrcLang ));
if (nAttribEnd>=0) // start already found?
{
DBG_ASSERT(nEnd >= aCurStart.nIndex, "error while scanning attributes (a)" );
DBG_ASSERT(nEnd >= nAttribEnd, "error while scanning attributes (b)" ); if (/*nEnd >= aCurStart.nIndex &&*/ nLangFound == nResLang)
nAttribEnd = nEnd; else// language attrib has changed break;
} if (nAttribStart<0 && // start not yet found?
nEnd > aCurStart.nIndex && bLangOk)
{
nAttribStart = nStart;
nAttribEnd = nEnd;
nResLang = nLangFound;
} //! the list of portions may have changed compared to the previous //! call to this function (because of possibly changed language //! attribute!) //! But since we don't want to start in the already processed part //! we clip the start accordingly. if (nAttribStart >= 0 && nAttribStart < aCurStart.nIndex)
{
nAttribStart = aCurStart.nIndex;
}
// check script type to the right of the start of the current portion
EditPaM aPaM( CreateEditPaM( EPaM(aCurStart.nPara, nLangIdx) ) ); bool bIsAsianScript = (i18n::ScriptType::ASIAN == GetI18NScriptType( aPaM )); // not yet processed text part with for conversion // not suitable language found that needs to be changed? if (bAllowImplicitChangesForNotConvertibleText &&
!bLangOk && !bIsAsianScript && nEnd > aCurStart.nIndex)
{
ESelection aESel( aCurStart.nPara, nStart, aCurStart.nPara, nEnd ); // set language and font to target language and font of conversion //! Now this especially includes all non convertible text e.g. //! spaces, empty paragraphs and western text. // This is in order for every *new* text entered at *any* position to // have the correct language and font attributes set.
SetLanguageAndFont( aESel,
nTargetLang, EE_CHAR_LANGUAGE_CJK,
pTargetFont, EE_CHAR_FONTINFO_CJK );
}
nCurPos = nEnd;
}
if (nAttribStart>=0 && nAttribEnd>=0)
{
aCurSel.Min().SetIndex( nAttribStart );
aCurSel.Max().SetIndex( nAttribEnd );
} elseif (nCurPos>=0)
{ // set selection to end of scanned text // (used to set the position where to continue from later on)
aCurSel.Min().SetIndex( nCurPos );
aCurSel.Max().SetIndex( nCurPos );
}
Reference< XSpellAlternatives > xSpellAlt;
Sequence< PropertyValue > aEmptySeq; while (!xSpellAlt.is())
{ // Known (most likely) bug: If SpellToCurrent, the current has to be // corrected at each replacement, otherwise it may not fit exactly in // the end ... if (mpSpellInfo->bSpellToEnd || mpSpellInfo->bMultipleDoc)
{ if ( aCurSel.Max().GetNode() == pLastNode )
{ if ( aCurSel.Max().GetIndex() >= pLastNode->Len() ) break;
}
} elseif (!mpSpellInfo->bSpellToEnd)
{
EPaM aEPaM( CreateEPaM( aCurSel.Max() ) ); if (!(aEPaM < mpSpellInfo->aSpellTo)) break;
}
Reference< XSpellAlternatives > xSpellAlt;
Sequence< PropertyValue > aEmptySeq; while (!xSpellAlt.is())
{ //check if the end of the selection has been reached
{
EPaM aEPaM( CreateEPaM( aCurSel.Max() ) ); if ( !( aEPaM < CreateEPaM( rSelection.Max()) ) ) break;
}
bool ImpEditEngine::SpellSentence(EditView const & rEditView,
svx::SpellPortions& rToFill )
{ bool bRet = false;
EditSelection aCurSel( rEditView.getImpl().GetEditSelection() ); if (!mpSpellInfo)
CreateSpellInfo( true );
mpSpellInfo->aCurSentenceStart = aCurSel.Min();
DBG_ASSERT(mxSpeller.is(), "No spell checker set!");
mpSpellInfo->aLastSpellPortions.clear();
mpSpellInfo->aLastSpellContentSelections.clear();
rToFill.clear(); //if no selection previously exists the range is extended to the end of the object if (!aCurSel.HasRange())
{
ContentNode* pLastNode = maEditDoc.GetObject( maEditDoc.Count()-1);
aCurSel.Max() = EditPaM(pLastNode, pLastNode->Len());
} // check for next error in aCurSel and set aCurSel to that one if any was found
Reference< XSpellAlternatives > xAlt = ImpFindNextError(aCurSel); if (xAlt.is())
{
bRet = true; //find the sentence boundaries
EditSelection aSentencePaM = SelectSentence(aCurSel); //make sure that the sentence is never smaller than the error range! if(aSentencePaM.Max().GetIndex() < aCurSel.Max().GetIndex())
aSentencePaM.Max() = aCurSel.Max(); //add the portion preceding the error
EditSelection aStartSelection(aSentencePaM.Min(), aCurSel.Min()); if(aStartSelection.HasRange())
AddPortionIterated(rEditView, aStartSelection, nullptr, rToFill); //add the error portion
AddPortionIterated(rEditView, aCurSel, xAlt, rToFill); //find the end of the sentence //search for all errors in the rest of the sentence and add all the portions do
{
EditSelection aNextSel(aCurSel.Max(), aSentencePaM.Max());
xAlt = ImpFindNextError(aNextSel); if(xAlt.is())
{ //add the part between the previous and the current error
AddPortionIterated(rEditView, EditSelection(aCurSel.Max(), aNextSel.Min()), nullptr, rToFill); //add the current error
AddPortionIterated(rEditView, aNextSel, xAlt, rToFill);
} else
AddPortionIterated(rEditView, EditSelection(aCurSel.Max(), aSentencePaM.Max()), xAlt, rToFill);
aCurSel = aNextSel;
} while( xAlt.is() );
//set the selection to the end of the current sentence
rEditView.getImpl().SetEditSelection(aSentencePaM.Max());
} return bRet;
}
// Adds one portion to the SpellPortions void ImpEditEngine::AddPortion( const EditSelection& rSel, const uno::Reference< XSpellAlternatives >& xAlt,
svx::SpellPortions& rToFill, bool bIsField)
{ if(!rSel.HasRange()) return;
//save the spelled portions for later use
mpSpellInfo->aLastSpellPortions.push_back(aPortion);
mpSpellInfo->aLastSpellContentSelections.push_back(rSel);
}
// Adds one or more portions of text to the SpellPortions depending on language changes void ImpEditEngine::AddPortionIterated(
EditView const & rEditView, const EditSelection& rSel, const Reference< XSpellAlternatives >& xAlt,
svx::SpellPortions& rToFill)
{ if (!rSel.HasRange()) return;
if(xAlt.is())
{
AddPortion(rSel, xAlt, rToFill, false);
} else
{ //iterate and search for language attribute changes //save the start and end positions bool bTest = rSel.Min().GetIndex() <= rSel.Max().GetIndex();
EditPaM aStart(bTest ? rSel.Min() : rSel.Max());
EditPaM aEnd(bTest ? rSel.Max() : rSel.Min()); //iterate over the text to find changes in language //set the mark equal to the point
EditPaM aCursor(aStart);
rEditView.getImpl().SetEditSelection( aCursor );
LanguageType eStartLanguage = GetLanguage( aCursor ).nLang; //search for a field attribute at the beginning - only the end position //of this field is kept to end a portion at that position const EditCharAttrib* pFieldAttr = aCursor.GetNode()->GetCharAttribs().
FindFeature( aCursor.GetIndex() ); bool bIsField = pFieldAttr &&
pFieldAttr->GetStart() == aCursor.GetIndex() &&
pFieldAttr->GetStart() != pFieldAttr->GetEnd() &&
pFieldAttr->Which() == EE_FEATURE_FIELD;
sal_Int32 nEndField = bIsField ? pFieldAttr->GetEnd() : -1; do
{
aCursor = CursorRight( aCursor); //determine whether a field and has been reached bool bIsEndField = nEndField == aCursor.GetIndex(); //search for a new field attribute const EditCharAttrib* _pFieldAttr = aCursor.GetNode()->GetCharAttribs().
FindFeature( aCursor.GetIndex() );
bIsField = _pFieldAttr &&
_pFieldAttr->GetStart() == aCursor.GetIndex() &&
_pFieldAttr->GetStart() != _pFieldAttr->GetEnd() &&
_pFieldAttr->Which() == EE_FEATURE_FIELD; //on every new field move the end position if (bIsField)
nEndField = _pFieldAttr->GetEnd();
LanguageType eCurLanguage = GetLanguage( aCursor ).nLang; if(eCurLanguage != eStartLanguage || bIsField || bIsEndField)
{
eStartLanguage = eCurLanguage; //go one step back - the cursor currently selects the first character //with a different language //create a selection from start to the current Cursor
EditSelection aSelection(aStart, aCursor);
AddPortion(aSelection, xAlt, rToFill, bIsEndField);
aStart = aCursor;
}
} while(aCursor.GetIndex() < aEnd.GetIndex());
EditSelection aSelection(aStart, aCursor);
AddPortion(aSelection, xAlt, rToFill, bIsField);
}
}
void ImpEditEngine::ApplyChangedSentence(EditView const & rEditView, const svx::SpellPortions& rNewPortions, bool bRecheck )
{ // Note: rNewPortions.size() == 0 is valid and happens when the whole // sentence got removed in the dialog
DBG_ASSERT(mpSpellInfo, "mpSpellInfo not initialized"); if (!mpSpellInfo || mpSpellInfo->aLastSpellPortions.empty()) // no portions -> no text to be changed return;
// get current paragraph length to calculate later on how the sentence length changed, // in order to place the cursor at the end of the sentence again
EditSelection aOldSel( rEditView.getImpl().GetEditSelection() );
sal_Int32 nOldLen = aOldSel.Max().GetNode()->Len();
UndoActionStart( EDITUNDO_INSERT ); if (mpSpellInfo->aLastSpellPortions.size() == rNewPortions.size())
{
DBG_ASSERT(!rNewPortions.empty(), "rNewPortions should not be empty here");
DBG_ASSERT(mpSpellInfo->aLastSpellPortions.size() == mpSpellInfo->aLastSpellContentSelections.size(), "aLastSpellPortions and aLastSpellContentSelections size mismatch");
//the simple case: the same number of elements on both sides //each changed element has to be applied to the corresponding source element
svx::SpellPortions::const_iterator aCurrentNewPortion = rNewPortions.end();
svx::SpellPortions::const_iterator aCurrentOldPortion = mpSpellInfo->aLastSpellPortions.end();
SpellContentSelections::const_iterator aCurrentOldPosition = mpSpellInfo->aLastSpellContentSelections.end(); bool bSetToEnd = false; do
{
--aCurrentNewPortion;
--aCurrentOldPortion;
--aCurrentOldPosition; //set the cursor to the end of the sentence - necessary to //resume there at the next step if(!bSetToEnd)
{
bSetToEnd = true;
rEditView.getImpl().SetEditSelection( aCurrentOldPosition->Max() );
}
SvtScriptType nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( aCurrentNewPortion->eLanguage );
sal_uInt16 nLangWhichId = EE_CHAR_LANGUAGE; switch(nScriptType)
{ case SvtScriptType::ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break; case SvtScriptType::COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break; default: break;
} if(aCurrentNewPortion->sText != aCurrentOldPortion->sText)
{ //change text and apply language
SfxItemSet aSet( maEditDoc.GetItemPool(), nLangWhichId, nLangWhichId );
aSet.Put(SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId));
SetAttribs( *aCurrentOldPosition, aSet );
ImpInsertText( *aCurrentOldPosition, aCurrentNewPortion->sText );
} elseif(aCurrentNewPortion->eLanguage != aCurrentOldPortion->eLanguage)
{ //apply language
SfxItemSet aSet( maEditDoc.GetItemPool(), nLangWhichId, nLangWhichId);
aSet.Put(SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId));
SetAttribs( *aCurrentOldPosition, aSet );
}
} while(aCurrentNewPortion != rNewPortions.begin());
} else
{
DBG_ASSERT( !mpSpellInfo->aLastSpellContentSelections.empty(), "aLastSpellContentSelections should not be empty here" );
//delete the sentence completely
ImpDeleteSelection( aAllSentence );
EditPaM aCurrentPaM = aAllSentence.Min(); for(constauto& rCurrentNewPortion : rNewPortions)
{ //set the language attribute
LanguageType eCurLanguage = GetLanguage( aCurrentPaM ).nLang; if(eCurLanguage != rCurrentNewPortion.eLanguage)
{
SvtScriptType nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( rCurrentNewPortion.eLanguage );
sal_uInt16 nLangWhichId = EE_CHAR_LANGUAGE; switch(nScriptType)
{ case SvtScriptType::ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break; case SvtScriptType::COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break; default: break;
}
SfxItemSet aSet( maEditDoc.GetItemPool(), nLangWhichId, nLangWhichId);
aSet.Put(SvxLanguageItem(rCurrentNewPortion.eLanguage, nLangWhichId));
SetAttribs( EditSelection(aCurrentPaM), aSet );
} //insert the new string and set the cursor to the end of the inserted string
aCurrentPaM = ImpInsertText( EditSelection(aCurrentPaM) , rCurrentNewPortion.sText );
}
}
UndoActionEnd();
EditPaM aNext; if (bRecheck)
aNext = mpSpellInfo->aCurSentenceStart; else
{ // restore cursor position to the end of the modified sentence. // (This will define the continuation position for spell/grammar checking) // First: check if the sentence/para length changed const sal_Int32 nDelta = rEditView.getImpl().GetEditSelection().Max().GetNode()->Len() - nOldLen; const sal_Int32 nEndOfSentence = aOldSel.Max().GetIndex() + nDelta;
aNext = EditPaM( aOldSel.Max().GetNode(), nEndOfSentence );
}
rEditView.getImpl().SetEditSelection( aNext );
if (IsUpdateLayout())
FormatAndLayout();
maEditDoc.SetModified(true);
}
void ImpEditEngine::DoOnlineSpelling( ContentNode* pThisNodeOnly, bool bSpellAtCursorPos, bool bInterruptible )
{ /* It will iterate over all the paragraphs, paragraphs with only invalidated wrong list will be checked ...
All the words are checked in the invalidated region. Is a word wrong, but not in the wrong list, or vice versa, the range of the word will be invalidated (no Invalidate, but if only transitions wrong from right =>, simple Paint, even out properly with VDev on transitions from wrong => right)
*/
EditPaM aPaM( pNode, nInvStart );
EditSelection aSel( aPaM, aPaM ); while ( aSel.Max().GetNode() == pNode )
{ if ( ( o3tl::make_unsigned(aSel.Min().GetIndex()) > nInvEnd )
|| ( ( aSel.Max().GetNode() == pLastNode ) && ( aSel.Max().GetIndex() >= pLastNode->Len() ) ) ) break; // Document end or end of invalid region
aSel = SelectWord( aSel, i18n::WordType::DICTIONARY_WORD ); // If afterwards a dot, this must be handed over! // If an abbreviation ... bool bDottAdded = false; if ( aSel.Max().GetIndex() < aSel.Max().GetNode()->Len() )
{
sal_Unicode cNext = aSel.Max().GetNode()->GetChar( aSel.Max().GetIndex() ); if ( cNext == '.' )
{
aSel.Max().SetIndex( aSel.Max().GetIndex()+1 );
bDottAdded = true;
}
}
OUString aWord = GetSelected(aSel);
bool bChanged = false; if (!aWord.isEmpty())
{ const sal_Int32 nWStart = aSel.Min().GetIndex(); const sal_Int32 nWEnd = aSel.Max().GetIndex(); if (!mxSpeller->isValid( aWord, static_cast<sal_uInt16>(GetLanguage( EditPaM( aSel.Min().GetNode(), nWStart+1 ) ).nLang), aEmptySeq))
{ // Check if already marked correctly... const sal_Int32 nXEnd = bDottAdded ? nWEnd -1 : nWEnd; if ( !pWrongList->HasWrong( nWStart, nXEnd ) )
{ // Mark Word as wrong... // But only when not at Cursor-Position... bool bCursorPos = false; if ( aCursorPos.GetNode() == pNode )
{ if ( ( nWStart <= aCursorPos.GetIndex() ) && nWEnd >= aCursorPos.GetIndex() )
bCursorPos = true;
} if ( bCursorPos )
{ // Then continue to mark as invalid ...
pWrongList->ResetInvalidRange(nWStart, nWEnd);
bRestartTimer = true;
} else
{ // It may be that the Wrongs in the list are not // spanning exactly over words because the // WordDelimiters during expansion are not // evaluated.
pWrongList->InsertWrong(nWStart, nXEnd);
bChanged = true;
}
}
} else
{ // Check if not marked as wrong if ( pWrongList->HasAnyWrong( nWStart, nWEnd ) )
{
pWrongList->ClearWrongs( nWStart, nWEnd, pNode );
bSimpleRepaint = false;
bChanged = true;
}
} if ( bChanged )
{ if ( nPaintFrom<0 )
nPaintFrom = nWStart;
nPaintTo = nWEnd;
}
}
EditPaM aLastEnd( aSel.Max() );
aSel = WordRight( aSel.Max(), i18n::WordType::DICTIONARY_WORD ); if ( bChanged && ( aSel.Min().GetNode() == pNode ) &&
( aSel.Min().GetIndex()-aLastEnd.GetIndex() > 1 ) )
{ // If two words are separated by more than one blank, it // can happen that when splitting a Wrongs the start of // the second word is before the actually word
pWrongList->ClearWrongs( aLastEnd.GetIndex(), aSel.Min().GetIndex(), pNode );
}
}
if (!maEditViews.empty())
{ // For SimpleRepaint one was painted over a range without // reaching VDEV, but then one would have to intersect, c // clipping, ... over all views. Probably not worthwhile.
EditPaM aStartPaM( pNode, nPaintFrom );
EditPaM aEndPaM( pNode, nPaintTo );
tools::Rectangle aStartCursor( PaMtoEditCursor( aStartPaM ) );
tools::Rectangle aEndCursor( PaMtoEditCursor( aEndPaM ) );
DBG_ASSERT(maInvalidRect.IsEmpty(), "InvalidRect set!");
maInvalidRect.SetLeft( 0 );
maInvalidRect.SetRight( GetPaperSize().Width() );
maInvalidRect.SetTop( aStartCursor.Top() );
maInvalidRect.SetBottom( aEndCursor.Bottom() ); if (mpActiveView && mpActiveView->HasSelection())
{ // Then no output through VDev.
UpdateViews();
} elseif ( bSimpleRepaint )
{ for (EditView* pView : maEditViews)
{
tools::Rectangle aClipRect(maInvalidRect);
aClipRect.Intersection( pView->GetVisArea() ); if ( !aClipRect.IsEmpty() )
{ // convert to window coordinates...
aClipRect.SetPos( pView->getImpl().GetWindowPos( aClipRect.TopLeft() ) );
pView->getImpl().InvalidateAtWindow(aClipRect);
}
}
} else
{
UpdateViews(mpActiveView);
}
maInvalidRect = tools::Rectangle();
}
} // After two corrected nodes give up the control...
nInvalids++; if ( bInterruptible && ( nInvalids >= 2 ) )
{
bRestartTimer = true; break;
}
}
if ( pThisNodeOnly ) break;
} if ( bRestartTimer )
maOnlineSpellTimer.Start();
}
EESpellState ImpEditEngine::HasSpellErrors()
{
DBG_ASSERT(mxSpeller.is(), "No spell checker set!");
// FIND_ALL is not possible without multiple selection. if ( ( rSearchItem.GetCommand() == SvxSearchCmd::FIND ) ||
( rSearchItem.GetCommand() == SvxSearchCmd::FIND_ALL ) )
{ if ( Search( rSearchItem, pEditView ) )
nFound++;
} elseif ( rSearchItem.GetCommand() == SvxSearchCmd::REPLACE )
{ // The word is selected if the user not altered the selection // in between: if ( aCurSel.HasRange() )
{
pEditView->InsertText( rSearchItem.GetReplaceString() );
nFound = 1;
} else if( Search( rSearchItem, pEditView ) )
nFound = 1;
} elseif ( rSearchItem.GetCommand() == SvxSearchCmd::REPLACE_ALL )
{ // The Writer replaces all front beginning to end ...
SvxSearchItem aTmpItem( rSearchItem );
aTmpItem.SetBackward( false );
pEditView->getImpl().DrawSelectionXOR(); if ( bFound )
{ // First, set the minimum, so the whole word is in the visible range.
pEditView->getImpl().SetEditSelection( aFoundSel.Min() );
pEditView->ShowCursor( true, false );
pEditView->getImpl().SetEditSelection( aFoundSel );
} else
pEditView->getImpl().SetEditSelection( aSel.Max() );
// since we don't use Hiragana/Katakana or half-width/full-width transliterations here // it is fine to use ANYWORD_IGNOREWHITESPACES. (ANY_WORD btw is broken and will // occasionally miss words in consecutive sentences). Also with ANYWORD_IGNOREWHITESPACES // text like 'just-in-time' will be converted to 'Just-In-Time' which seems to be the // proper thing to do. const sal_Int16 nWordType = i18n::WordType::ANYWORD_IGNOREWHITESPACES;
//! In order to have less trouble with changing text size, e.g. because //! of ligatures or German small sz being resolved, we need to process //! the text replacements from end to start. //! This way the offsets for the yet to be changed words will be //! left unchanged by the already replaced text. //! For this we temporarily save the changes to be done in this vector
std::vector< eeTransliterationChgData > aChanges;
eeTransliterationChgData aChgData;
if (nTransliterationMode == TransliterationFlags::TITLE_CASE)
{ // for 'capitalize every word' we need to iterate over each word
// prevent backtracking to the previous word if selection is at word boundary if (aSttBndry.endPos <= nStartPos)
{
aSttBndry = _xBI->nextWord(
aNodeStr, aSttBndry.endPos,
GetLocale( EditPaM( pNode, aSttBndry.endPos + 1 ) ),
nWordType);
} // prevent advancing to the next word if selection is at word boundary if (aEndBndry.startPos >= nEndPos)
{
aEndBndry = _xBI->previousWord(
aNodeStr, aEndBndry.startPos,
GetLocale( EditPaM( pNode, aEndBndry.startPos + 1 ) ),
nWordType);
}
/* Nothing to do if user selection lies entirely outside of word start and end boundary computed above.
* Skip this node, because otherwise the below logic for constraining to the selection will fail */ if (aSttBndry.startPos >= aSel.Max().GetIndex() || aEndBndry.endPos <= aSel.Min().GetIndex()) { continue;
}
// prevent going outside of the user's selection, which may // start or end in the middle of a word if (nNode == nStartNode) {
aSttBndry.startPos = std::max(aSttBndry.startPos, aSel.Min().GetIndex());
aSttBndry.endPos = std::min(aSttBndry.endPos, aSel.Max().GetIndex());
aEndBndry.startPos = std::max(aEndBndry.startPos, aSttBndry.startPos);
aEndBndry.endPos = std::min(aEndBndry.endPos, aSel.Max().GetIndex());
}
// prevent backtracking to the previous sentence if selection starts at end of a sentence if (nCurrentEnd <= nStartPos)
{ // now nCurrentStart is probably located on a non-letter word. (unless we // are in Asian text with no spaces...) // Thus to get the real sentence start we should locate the next real word, // that is one found by DICTIONARY_WORD
i18n::Boundary aBndry = _xBI->nextWord( aNodeStr, nCurrentEnd,
GetLocale( EditPaM( pNode, nCurrentEnd + 1 ) ),
i18n::WordType::DICTIONARY_WORD);
// now get new current sentence boundaries
nCurrentStart = _xBI->beginOfSentence(
aNodeStr, aBndry.startPos,
GetLocale( EditPaM( pNode, aBndry.startPos + 1 ) ) );
nCurrentEnd = _xBI->endOfSentence(
aNodeStr, nCurrentStart,
GetLocale( EditPaM( pNode, nCurrentStart + 1 ) ) );
} // prevent advancing to the next sentence if selection ends at start of a sentence if (nLastStart >= nEndPos)
{ // now nCurrentStart is probably located on a non-letter word. (unless we // are in Asian text with no spaces...) // Thus to get the real sentence start we should locate the previous real word, // that is one found by DICTIONARY_WORD
i18n::Boundary aBndry = _xBI->previousWord( aNodeStr, nLastStart,
GetLocale( EditPaM( pNode, nLastStart + 1 ) ),
i18n::WordType::DICTIONARY_WORD);
nLastEnd = _xBI->endOfSentence(
aNodeStr, aBndry.startPos,
GetLocale( EditPaM( pNode, aBndry.startPos + 1 ) ) ); if (nCurrentEnd > nLastEnd)
nCurrentEnd = nLastEnd;
}
// prevent making any change outside of the user's selection
nCurrentStart = std::max(aSel.Min().GetIndex(), nCurrentStart);
nCurrentEnd = std::min(aSel.Max().GetIndex(), nCurrentEnd);
nLastStart = std::max(aSel.Min().GetIndex(), nLastStart);
nLastEnd = std::min(aSel.Max().GetIndex(), nLastEnd);
while (nCurrentStart < nLastEnd)
{ const sal_Int32 nLen = nCurrentEnd - nCurrentStart;
DBG_ASSERT( nLen > 0, "invalid word length of 0" );
if (!aChanges.empty())
{ // Create a single UndoAction on Demand for all the changes ... if ( !pUndo && IsUndoEnabled() && !IsInUndo() )
{ // adjust selection to include all changes for (const eeTransliterationChgData & aChange : aChanges)
{ const EditSelection &rSel = aChange.aSelection; if (aSel.Min().GetNode() == rSel.Min().GetNode() &&
aSel.Min().GetIndex() > rSel.Min().GetIndex())
aSel.Min().SetIndex( rSel.Min().GetIndex() ); if (aSel.Max().GetNode() == rSel.Max().GetNode() &&
aSel.Max().GetIndex() < rSel.Max().GetIndex())
aSel.Max().SetIndex( rSel.Max().GetIndex() );
}
aNewSel = aSel;
// now apply the changes from end to start to leave the offsets of the // yet unchanged text parts remain the same. for (size_t i = 0; i < aChanges.size(); ++i)
{
eeTransliterationChgData& rData = aChanges[ aChanges.size() - 1 - i ];
bChanges = true; if (rData.nLen != rData.aNewText.getLength())
bLenChanged = true;
// Change text without losing the attributes const sal_Int32 nDiffs =
ReplaceTextOnly( rData.aSelection.Min().GetNode(),
rData.nStart, rData.aNewText, rData.aOffsets );
// adjust selection in end node to possibly changed size if (aSel.Max().GetNode() == rData.aSelection.Max().GetNode())
aNewSel.Max().SetIndex( aNewSel.Max().GetIndex() + nDiffs );
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.