blob: 35da38adc792e389038a6b9af3204d44556cd8a3 [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_editeng.hxx"
#include <vcl/wrkwin.hxx>
#include <vcl/dialog.hxx>
#include <vcl/msgbox.hxx>
#include <vcl/svapp.hxx>
#include <svl/srchitem.hxx>
#include <editeng/lspcitem.hxx>
#include <editeng/adjitem.hxx>
#include <editeng/tstpitem.hxx>
#include <eertfpar.hxx>
#include <editeng/editeng.hxx>
#include <impedit.hxx>
#include <editeng/editview.hxx>
#include <eehtml.hxx>
#include <editobj2.hxx>
#include <i18npool/lang.h>
#include "editxml.hxx"
#include <editeng/akrnitem.hxx>
#include <editeng/cntritem.hxx>
#include <editeng/colritem.hxx>
#include <editeng/crsditem.hxx>
#include <editeng/escpitem.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/fontitem.hxx>
#include <editeng/kernitem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/postitem.hxx>
#include <editeng/shdditem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/wghtitem.hxx>
#include <editeng/langitem.hxx>
#include <editeng/charreliefitem.hxx>
#include <editeng/frmdiritem.hxx>
#include <editeng/emphitem.hxx>
#include <textconv.hxx>
#include <rtl/tencinfo.h>
#include <svtools/rtfout.hxx>
#include <edtspell.hxx>
#include <editeng/scripttypeitem.hxx>
#include <editeng/unolingu.hxx>
#include <linguistic/lngprops.hxx>
#include <com/sun/star/linguistic2/XThesaurus.hpp>
#include <com/sun/star/linguistic2/XMeaning.hpp>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <com/sun/star/i18n/WordType.hpp>
#include <com/sun/star/i18n/TransliterationModules.hpp>
#include <com/sun/star/i18n/TransliterationModulesExtra.hpp>
#include <unotools/transliterationwrapper.hxx>
#include <unotools/textsearch.hxx>
#include <comphelper/processfactory.hxx>
#include <vcl/help.hxx>
#include <svtools/rtfkeywd.hxx>
#include <editeng/edtdlg.hxx>
#include <vector>
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::linguistic2;
void Swapsal_uIt16s( sal_uInt16& rX, sal_uInt16& rY )
{
sal_uInt16 n = rX;
rX = rY;
rY = n;
}
EditPaM ImpEditEngine::Read( SvStream& rInput, const String& rBaseURL, EETextFormat eFormat, EditSelection aSel, SvKeyValueIterator* pHTTPHeaderAttrs )
{
sal_Bool _bUpdate = GetUpdateMode();
SetUpdateMode( sal_False );
EditPaM aPaM;
if ( eFormat == EE_FORMAT_TEXT )
aPaM = ReadText( rInput, aSel );
else if ( eFormat == EE_FORMAT_RTF )
aPaM = ReadRTF( rInput, aSel );
else if ( eFormat == EE_FORMAT_XML )
aPaM = ReadXML( rInput, aSel );
else if ( eFormat == EE_FORMAT_HTML )
aPaM = ReadHTML( rInput, rBaseURL, aSel, pHTTPHeaderAttrs );
else if ( eFormat == EE_FORMAT_BIN)
aPaM = ReadBin( rInput, aSel );
else
{
DBG_ERROR( "Read: Unbekanntes Format" );
}
FormatFullDoc(); // reicht vielleicht auch ein einfaches Format?
SetUpdateMode( _bUpdate );
return aPaM;
}
EditPaM ImpEditEngine::ReadText( SvStream& rInput, EditSelection aSel )
{
if ( aSel.HasRange() )
aSel = ImpDeleteSelection( aSel );
EditPaM aPaM = aSel.Max();
XubString aTmpStr, aStr;
sal_Bool bDone = rInput.ReadByteStringLine( aTmpStr );
while ( bDone )
{
aTmpStr.Erase( MAXCHARSINPARA );
aPaM = ImpInsertText( EditSelection( aPaM, aPaM ), aTmpStr );
aPaM = ImpInsertParaBreak( aPaM );
bDone = rInput.ReadByteStringLine( aTmpStr );
}
return aPaM;
}
EditPaM ImpEditEngine::ReadXML( SvStream& rInput, EditSelection aSel )
{
#ifndef SVX_LIGHT
if ( aSel.HasRange() )
aSel = ImpDeleteSelection( aSel );
ESelection aESel = CreateESel( aSel );
::SvxReadXML( *GetEditEnginePtr(), rInput, aESel );
return aSel.Max();
#else
return EditPaM();
#endif
}
EditPaM ImpEditEngine::ReadRTF( SvStream& rInput, EditSelection aSel )
{
#ifndef SVX_LIGHT
#if defined (EDITDEBUG) && !defined( UNX )
SvFileStream aRTFOut( String( RTL_CONSTASCII_USTRINGPARAM ( "d:\\rtf_in.rtf" ) ), STREAM_WRITE );
aRTFOut << rInput;
aRTFOut.Close();
rInput.Seek( 0 );
#endif
if ( aSel.HasRange() )
aSel = ImpDeleteSelection( aSel );
// sal_Bool bCharsBeforeInsertPos = ( aSel.Min().GetIndex() ) ? sal_True : sal_False;
// sal_Bool bCharsBehindInsertPos = ( aSel.Min().GetIndex() < aSel.Min().GetNode()->Len() ) ? sal_True : sal_False;
// Der SvRTF-Parser erwartet, dass das Which-Mapping am uebergebenen Pool,
// nicht an einem Secondary haengt.
SfxItemPool* pPool = &aEditDoc.GetItemPool();
while ( pPool->GetSecondaryPool() && !pPool->GetName().EqualsAscii( "EditEngineItemPool" ) )
{
pPool = pPool->GetSecondaryPool();
}
DBG_ASSERT( pPool && pPool->GetName().EqualsAscii( "EditEngineItemPool" ), "ReadRTF: Kein EditEnginePool!" );
EditRTFParserRef xPrsr = new EditRTFParser( rInput, aSel, *pPool, this );
SvParserState eState = xPrsr->CallParser();
if ( ( eState != SVPAR_ACCEPTED ) && ( !rInput.GetError() ) )
{
rInput.SetError( EE_READWRITE_WRONGFORMAT );
return aSel.Min();
}
return xPrsr->GetCurPaM();
#else
return EditPaM();
#endif
}
EditPaM ImpEditEngine::ReadHTML( SvStream& rInput, const String& rBaseURL, EditSelection aSel, SvKeyValueIterator* pHTTPHeaderAttrs )
{
#ifndef SVX_LIGHT
if ( aSel.HasRange() )
aSel = ImpDeleteSelection( aSel );
// sal_Bool bCharsBeforeInsertPos = ( aSel.Min().GetIndex() ) ? sal_True : sal_False;
// sal_Bool bCharsBehindInsertPos = ( aSel.Min().GetIndex() < aSel.Min().GetNode()->Len() ) ? sal_True : sal_False;
EditHTMLParserRef xPrsr = new EditHTMLParser( rInput, rBaseURL, pHTTPHeaderAttrs );
SvParserState eState = xPrsr->CallParser( this, aSel.Max() );
if ( ( eState != SVPAR_ACCEPTED ) && ( !rInput.GetError() ) )
{
rInput.SetError( EE_READWRITE_WRONGFORMAT );
return aSel.Min();
}
return xPrsr->GetCurSelection().Max();
#else
return EditPaM();
#endif
}
EditPaM ImpEditEngine::ReadBin( SvStream& rInput, EditSelection aSel )
{
// Einfach ein temporaeres TextObject missbrauchen...
EditTextObject* pObj = EditTextObject::Create( rInput, NULL );
EditPaM aLastPaM = aSel.Max();
if ( pObj )
aLastPaM = InsertText( *pObj, aSel ).Max();
delete pObj;
return aLastPaM;
}
#ifndef SVX_LIGHT
void ImpEditEngine::Write( SvStream& rOutput, EETextFormat eFormat, EditSelection aSel )
{
if ( !rOutput.IsWritable() )
rOutput.SetError( SVSTREAM_WRITE_ERROR );
if ( !rOutput.GetError() )
{
if ( eFormat == EE_FORMAT_TEXT )
WriteText( rOutput, aSel );
else if ( eFormat == EE_FORMAT_RTF )
WriteRTF( rOutput, aSel );
else if ( eFormat == EE_FORMAT_XML )
WriteXML( rOutput, aSel );
else if ( eFormat == EE_FORMAT_HTML )
WriteHTML( rOutput, aSel );
else if ( eFormat == EE_FORMAT_BIN)
WriteBin( rOutput, aSel );
else
{
DBG_ERROR( "Write: Unbekanntes Format" );
}
}
}
#endif
sal_uInt32 ImpEditEngine::WriteText( SvStream& rOutput, EditSelection aSel )
{
sal_uInt16 nStartNode, nEndNode;
sal_Bool bRange = aSel.HasRange();
if ( bRange )
{
aSel.Adjust( aEditDoc );
nStartNode = aEditDoc.GetPos( aSel.Min().GetNode() );
nEndNode = aEditDoc.GetPos( aSel.Max().GetNode() );
}
else
{
nStartNode = 0;
nEndNode = aEditDoc.Count()-1;
}
// ueber die Absaetze iterieren...
for ( sal_uInt16 nNode = nStartNode; nNode <= nEndNode; nNode++ )
{
ContentNode* pNode = aEditDoc.GetObject( nNode );
DBG_ASSERT( pNode, "Node nicht gefunden: Search&Replace" );
sal_uInt16 nStartPos = 0;
sal_uInt16 nEndPos = pNode->Len();
if ( bRange )
{
if ( nNode == nStartNode )
nStartPos = aSel.Min().GetIndex();
if ( nNode == nEndNode ) // kann auch == nStart sein!
nEndPos = aSel.Max().GetIndex();
}
XubString aTmpStr = aEditDoc.GetParaAsString( pNode, nStartPos, nEndPos );
rOutput.WriteByteStringLine( aTmpStr );
}
return rOutput.GetError();
}
sal_Bool ImpEditEngine::WriteItemListAsRTF( ItemList& rLst, SvStream& rOutput, sal_uInt16 nPara, sal_uInt16 nPos,
SvxFontTable& rFontTable, SvxColorList& rColorList )
{
const SfxPoolItem* pAttrItem = rLst.First();
while ( pAttrItem )
{
WriteItemAsRTF( *pAttrItem, rOutput, nPara, nPos,rFontTable, rColorList );
pAttrItem = rLst.Next();
}
return ( rLst.Count() ? sal_True : sal_False );
}
void lcl_FindValidAttribs( ItemList& rLst, ContentNode* pNode, sal_uInt16 nIndex, sal_uInt16 nScriptType )
{
sal_uInt16 nAttr = 0;
EditCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
while ( pAttr && ( pAttr->GetStart() <= nIndex ) )
{
// Start wird in While ueberprueft...
if ( pAttr->GetEnd() > nIndex )
{
if ( IsScriptItemValid( pAttr->GetItem()->Which(), nScriptType ) )
rLst.Insert( pAttr->GetItem(), LIST_APPEND );
}
nAttr++;
pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
}
}
sal_uInt32 ImpEditEngine::WriteBin( SvStream& rOutput, EditSelection aSel, sal_Bool bStoreUnicodeStrings ) const
{
BinTextObject* pObj = (BinTextObject*)CreateBinTextObject( aSel, NULL );
pObj->StoreUnicodeStrings( bStoreUnicodeStrings );
pObj->Store( rOutput );
delete pObj;
return 0;
}
#ifndef SVX_LIGHT
sal_uInt32 ImpEditEngine::WriteXML( SvStream& rOutput, EditSelection aSel )
{
ESelection aESel = CreateESel( aSel );
SvxWriteXML( *GetEditEnginePtr(), rOutput, aESel );
return 0;
}
#endif
static sal_uInt16 getStylePos( const SfxStyles& rStyles, SfxStyleSheet* pSheet )
{
sal_uInt16 nNumber = 0;
SfxStyles::const_iterator iter( rStyles.begin() );
while( iter != rStyles.end() )
{
if( (*iter++).get() == pSheet )
return nNumber;
++nNumber;
}
return 0;
}
sal_uInt32 ImpEditEngine::WriteRTF( SvStream& rOutput, EditSelection aSel )
{
#ifndef SVX_LIGHT
DBG_ASSERT( GetUpdateMode(), "WriteRTF bei UpdateMode = sal_False!" );
CheckIdleFormatter();
if ( !IsFormatted() )
FormatDoc();
sal_uInt16 nStartNode, nEndNode;
aSel.Adjust( aEditDoc );
nStartNode = aEditDoc.GetPos( aSel.Min().GetNode() );
nEndNode = aEditDoc.GetPos( aSel.Max().GetNode() );
// RTF-Vorspann...
rOutput << '{' ;
rOutput << OOO_STRING_SVTOOLS_RTF_RTF;
rOutput << OOO_STRING_SVTOOLS_RTF_ANSI;
rtl_TextEncoding eDestEnc = RTL_TEXTENCODING_MS_1252;
// Fonttabelle erzeugen und rausschreiben...
SvxFontTable aFontTable;
// DefaultFont muss ganz vorne stehen, damit DEF-Font im RTF
aFontTable.Insert( 0, new SvxFontItem( (const SvxFontItem&)aEditDoc.GetItemPool().GetDefaultItem( EE_CHAR_FONTINFO ) ) );
aFontTable.Insert( 1, new SvxFontItem( (const SvxFontItem&)aEditDoc.GetItemPool().GetDefaultItem( EE_CHAR_FONTINFO_CJK ) ) );
aFontTable.Insert( 2, new SvxFontItem( (const SvxFontItem&)aEditDoc.GetItemPool().GetDefaultItem( EE_CHAR_FONTINFO_CTL ) ) );
for ( sal_uInt16 nScriptType = 0; nScriptType < 3; nScriptType++ )
{
sal_uInt16 nWhich = EE_CHAR_FONTINFO;
if ( nScriptType == 1 )
nWhich = EE_CHAR_FONTINFO_CJK;
else if ( nScriptType == 2 )
nWhich = EE_CHAR_FONTINFO_CTL;
sal_uInt32 i = 0;
SvxFontItem* pFontItem = (SvxFontItem*)aEditDoc.GetItemPool().GetItem2( nWhich, i );
while ( pFontItem )
{
bool bAlreadyExist = false;
sal_uLong nTestMax = nScriptType ? aFontTable.Count() : 1;
for ( sal_uLong nTest = 0; !bAlreadyExist && ( nTest < nTestMax ); nTest++ )
{
bAlreadyExist = *aFontTable.Get( nTest ) == *pFontItem;
}
if ( !bAlreadyExist )
aFontTable.Insert( aFontTable.Count(), new SvxFontItem( *pFontItem ) );
pFontItem = (SvxFontItem*)aEditDoc.GetItemPool().GetItem2( nWhich, ++i );
}
}
rOutput << endl << '{' << OOO_STRING_SVTOOLS_RTF_FONTTBL;
sal_uInt16 j;
for ( j = 0; j < aFontTable.Count(); j++ )
{
SvxFontItem* pFontItem = aFontTable.Get( j );
rOutput << '{';
rOutput << OOO_STRING_SVTOOLS_RTF_F;
rOutput.WriteNumber( j );
switch ( pFontItem->GetFamily() )
{
case FAMILY_DONTKNOW: rOutput << OOO_STRING_SVTOOLS_RTF_FNIL;
break;
case FAMILY_DECORATIVE: rOutput << OOO_STRING_SVTOOLS_RTF_FDECOR;
break;
case FAMILY_MODERN: rOutput << OOO_STRING_SVTOOLS_RTF_FMODERN;
break;
case FAMILY_ROMAN: rOutput << OOO_STRING_SVTOOLS_RTF_FROMAN;
break;
case FAMILY_SCRIPT: rOutput << OOO_STRING_SVTOOLS_RTF_FSCRIPT;
break;
case FAMILY_SWISS: rOutput << OOO_STRING_SVTOOLS_RTF_FSWISS;
break;
default:
break;
}
rOutput << OOO_STRING_SVTOOLS_RTF_FPRQ;
sal_uInt16 nVal = 0;
switch( pFontItem->GetPitch() )
{
case PITCH_FIXED: nVal = 1; break;
case PITCH_VARIABLE: nVal = 2; break;
default:
break;
}
rOutput.WriteNumber( nVal );
CharSet eChrSet = pFontItem->GetCharSet();
DBG_ASSERT( eChrSet != 9, "SystemCharSet?!" );
if( RTL_TEXTENCODING_DONTKNOW == eChrSet )
eChrSet = gsl_getSystemTextEncoding();
rOutput << OOO_STRING_SVTOOLS_RTF_FCHARSET;
rOutput.WriteNumber( rtl_getBestWindowsCharsetFromTextEncoding( eChrSet ) );
rOutput << ' ';
RTFOutFuncs::Out_String( rOutput, pFontItem->GetFamilyName(), eDestEnc );
rOutput << ";}";
}
rOutput << '}';
rOutput << endl;
// ColorList rausschreiben...
SvxColorList aColorList;
sal_uInt32 i = 0;
SvxColorItem* pColorItem = (SvxColorItem*)aEditDoc.GetItemPool().GetItem2( EE_CHAR_COLOR, i );
while ( pColorItem )
{
sal_uInt32 nPos = i;
if ( pColorItem->GetValue() == COL_AUTO )
nPos = 0;
aColorList.Insert( new SvxColorItem( *pColorItem ), nPos );
pColorItem = (SvxColorItem*)aEditDoc.GetItemPool().GetItem2( EE_CHAR_COLOR, ++i );
}
aColorList.Insert( new SvxColorItem( (const SvxColorItem&)aEditDoc.GetItemPool().GetDefaultItem( EE_CHAR_COLOR) ), i );
rOutput << '{' << OOO_STRING_SVTOOLS_RTF_COLORTBL;
for ( j = 0; j < aColorList.Count(); j++ )
{
pColorItem = aColorList.GetObject( j );
if ( !j || ( pColorItem->GetValue() != COL_AUTO ) )
{
rOutput << OOO_STRING_SVTOOLS_RTF_RED;
rOutput.WriteNumber( pColorItem->GetValue().GetRed() );
rOutput << OOO_STRING_SVTOOLS_RTF_GREEN;
rOutput.WriteNumber( pColorItem->GetValue().GetGreen() );
rOutput << OOO_STRING_SVTOOLS_RTF_BLUE;
rOutput.WriteNumber( pColorItem->GetValue().GetBlue() );
}
rOutput << ';';
}
rOutput << '}';
rOutput << endl;
// StyleSheets...
if ( GetStyleSheetPool() )
{
sal_uInt16 nStyles = (sal_uInt16)GetStyleSheetPool()->GetStyles().size();
if ( nStyles )
{
rOutput << '{' << OOO_STRING_SVTOOLS_RTF_STYLESHEET;
for ( sal_uInt16 nStyle = 0; nStyle < nStyles; nStyle++ )
{
SfxStyleSheet* pStyle = (SfxStyleSheet*)GetStyleSheetPool()->GetStyles()[ nStyle ].get();
rOutput << endl << '{' << OOO_STRING_SVTOOLS_RTF_S;
sal_uInt16 nNumber = (sal_uInt16) (nStyle + 1);
rOutput.WriteNumber( nNumber );
// Attribute, auch aus Parent!
for ( sal_uInt16 nParAttr = EE_PARA_START; nParAttr <= EE_CHAR_END; nParAttr++ )
{
if ( pStyle->GetItemSet().GetItemState( nParAttr ) == SFX_ITEM_ON )
{
const SfxPoolItem& rItem = pStyle->GetItemSet().Get( nParAttr );
WriteItemAsRTF( rItem, rOutput, 0, 0, aFontTable, aColorList );
}
}
// Parent...(nur wenn noetig)
if ( pStyle->GetParent().Len() && ( pStyle->GetParent() != pStyle->GetName() ) )
{
SfxStyleSheet* pParent = (SfxStyleSheet*)GetStyleSheetPool()->Find( pStyle->GetParent(), pStyle->GetFamily() );
DBG_ASSERT( pParent, "Parent nicht gefunden!" );
rOutput << OOO_STRING_SVTOOLS_RTF_SBASEDON;
nNumber = (sal_uInt16) getStylePos( GetStyleSheetPool()->GetStyles(), pParent ) + 1;
rOutput.WriteNumber( nNumber );
}
// Folgevorlage...(immer)
SfxStyleSheet* pNext = pStyle;
if ( pStyle->GetFollow().Len() && ( pStyle->GetFollow() != pStyle->GetName() ) )
pNext = (SfxStyleSheet*)GetStyleSheetPool()->Find( pStyle->GetFollow(), pStyle->GetFamily() );
DBG_ASSERT( pNext, "Naechsten nicht gefunden!" );
rOutput << OOO_STRING_SVTOOLS_RTF_SNEXT;
nNumber = (sal_uInt16) getStylePos( GetStyleSheetPool()->GetStyles(), pNext ) + 1;
rOutput.WriteNumber( nNumber );
// Namen der Vorlage...
rOutput << " " << ByteString( pStyle->GetName(), eDestEnc ).GetBuffer();
rOutput << ";}";
}
rOutput << '}';
rOutput << endl;
}
}
// Die Pool-Defaults vorweg schreiben...
rOutput << '{' << OOO_STRING_SVTOOLS_RTF_IGNORE << "\\EditEnginePoolDefaults";
for ( sal_uInt16 nPoolDefItem = EE_PARA_START; nPoolDefItem <= EE_CHAR_END; nPoolDefItem++)
{
const SfxPoolItem& rItem = aEditDoc.GetItemPool().GetDefaultItem( nPoolDefItem );
WriteItemAsRTF( rItem, rOutput, 0, 0, aFontTable, aColorList );
}
rOutput << '}' << endl;
// Def-Hoehe vorweg, da sonst 12Pt
// Doch nicht, onst in jedem Absatz hart!
// SfxItemSet aTmpSet( GetEmptyItemSet() );
// const SvxFontHeightItem& rDefFontHeight = (const SvxFontHeightItem&)aTmpSet.Get( EE_CHAR_FONTHEIGHT );
// WriteItemAsRTF( rDefFontHeight, rOutput, aFontTable, aColorList );
// rOutput << '{' << OOO_STRING_SVTOOLS_RTF_IGNORE << "\\EditEnginePoolDefaultHeight}" << endl;
// DefTab:
MapMode aTwpMode( MAP_TWIP );
sal_uInt16 nDefTabTwps = (sal_uInt16) GetRefDevice()->LogicToLogic(
Point( aEditDoc.GetDefTab(), 0 ),
&GetRefMapMode(), &aTwpMode ).X();
rOutput << OOO_STRING_SVTOOLS_RTF_DEFTAB;
rOutput.WriteNumber( nDefTabTwps );
rOutput << endl;
// ueber die Absaetze iterieren...
rOutput << '{' << endl;
for ( sal_uInt16 nNode = nStartNode; nNode <= nEndNode; nNode++ )
{
ContentNode* pNode = aEditDoc.SaveGetObject( nNode );
DBG_ASSERT( pNode, "Node nicht gefunden: Search&Replace" );
// Die Absatzattribute vorweg...
sal_Bool bAttr = sal_False;
// Vorlage ?
if ( pNode->GetStyleSheet() )
{
// Nummer der Vorlage
rOutput << OOO_STRING_SVTOOLS_RTF_S;
sal_uInt16 nNumber = (sal_uInt16) getStylePos( GetStyleSheetPool()->GetStyles(), pNode->GetStyleSheet() ) + 1;
rOutput.WriteNumber( nNumber );
// Alle Attribute
// Attribute, auch aus Parent!
for ( sal_uInt16 nParAttr = EE_PARA_START; nParAttr <= EE_CHAR_END; nParAttr++ )
{
if ( pNode->GetStyleSheet()->GetItemSet().GetItemState( nParAttr ) == SFX_ITEM_ON )
{
const SfxPoolItem& rItem = pNode->GetStyleSheet()->GetItemSet().Get( nParAttr );
WriteItemAsRTF( rItem, rOutput, nNode, 0, aFontTable, aColorList );
bAttr = sal_True;
}
}
}
for ( sal_uInt16 nParAttr = EE_PARA_START; nParAttr <= EE_CHAR_END; nParAttr++ )
{
// const SfxPoolItem& rItem = pNode->GetContentAttribs().GetItem( nParAttr );
// Jetzt, wo StyleSheet-Verarbeitung, nur noch harte Absatzattribute!
if ( pNode->GetContentAttribs().GetItems().GetItemState( nParAttr ) == SFX_ITEM_ON )
{
const SfxPoolItem& rItem = pNode->GetContentAttribs().GetItems().Get( nParAttr );
WriteItemAsRTF( rItem, rOutput, nNode, 0, aFontTable, aColorList );
bAttr = sal_True;
}
}
if ( bAttr )
rOutput << ' '; // Separator
ItemList aAttribItems;
ParaPortion* pParaPortion = FindParaPortion( pNode );
DBG_ASSERT( pParaPortion, "Portion nicht gefunden: WriteRTF" );
sal_uInt16 nIndex = 0;
sal_uInt16 nStartPos = 0;
sal_uInt16 nEndPos = pNode->Len();
sal_uInt16 nStartPortion = 0;
sal_uInt16 nEndPortion = (sal_uInt16)pParaPortion->GetTextPortions().Count() - 1;
sal_Bool bFinishPortion = sal_False;
sal_uInt16 nPortionStart;
if ( nNode == nStartNode )
{
nStartPos = aSel.Min().GetIndex();
nStartPortion = pParaPortion->GetTextPortions().FindPortion( nStartPos, nPortionStart );
if ( nStartPos != 0 )
{
aAttribItems.Clear();
lcl_FindValidAttribs( aAttribItems, pNode, nStartPos, GetScriptType( EditPaM( pNode, 0 ) ) );
if ( aAttribItems.Count() )
{
// Diese Attribute duerfen nicht fuer den gesamten
// Absatz gelten:
rOutput << '{';
WriteItemListAsRTF( aAttribItems, rOutput, nNode, nStartPos, aFontTable, aColorList );
bFinishPortion = sal_True;
}
aAttribItems.Clear();
}
}
if ( nNode == nEndNode ) // kann auch == nStart sein!
{
nEndPos = aSel.Max().GetIndex();
nEndPortion = pParaPortion->GetTextPortions().FindPortion( nEndPos, nPortionStart );
}
EditCharAttrib* pNextFeature = pNode->GetCharAttribs().FindFeature( nIndex );
// Bei 0 anfangen, damit der Index richtig ist...
for ( sal_uInt16 n = 0; n <= nEndPortion; n++ )
{
TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject(n);
if ( n < nStartPortion )
{
nIndex = nIndex + pTextPortion->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 nScriptType = GetScriptType( EditPaM( pNode, nIndex+1 ) );
if ( !n || IsScriptChange( EditPaM( pNode, nIndex ) ) )
{
SfxItemSet aAttribs = GetAttribs( nNode, nIndex+1, nIndex+1 );
aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_FONTINFO, nScriptType ) ), LIST_APPEND );
aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_FONTHEIGHT, nScriptType ) ), LIST_APPEND );
aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_WEIGHT, nScriptType ) ), LIST_APPEND );
aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_ITALIC, nScriptType ) ), LIST_APPEND );
aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType ) ), LIST_APPEND );
}
// #96298# Insert hard attribs AFTER CJK attribs...
lcl_FindValidAttribs( aAttribItems, pNode, nIndex, nScriptType );
rOutput << '{';
if ( WriteItemListAsRTF( aAttribItems, rOutput, nNode, nIndex, aFontTable, aColorList ) )
rOutput << ' ';
sal_uInt16 nS = nIndex;
sal_uInt16 nE = nIndex + pTextPortion->GetLen();
if ( n == nStartPortion )
nS = nStartPos;
if ( n == nEndPortion )
nE = nEndPos;
XubString aRTFStr = aEditDoc.GetParaAsString( pNode, nS, nE);
RTFOutFuncs::Out_String( rOutput, aRTFStr, eDestEnc );
rOutput << '}';
}
if ( bFinishPortion )
{
rOutput << '}';
bFinishPortion = sal_False;
}
nIndex = nIndex + pTextPortion->GetLen();
}
rOutput << OOO_STRING_SVTOOLS_RTF_PAR << OOO_STRING_SVTOOLS_RTF_PARD << OOO_STRING_SVTOOLS_RTF_PLAIN;;
rOutput << endl;
}
// RTF-Nachspann...
rOutput << "}}"; // 1xKlammerung Absaetze, 1x Klammerung RTF-Dokument
rOutput.Flush();
#if defined (EDITDEBUG) && !defined( UNX )
{
SvFileStream aStream( String( RTL_CONSTASCII_USTRINGPARAM ( "d:\\rtf_out.rtf" ) ), STREAM_WRITE|STREAM_TRUNC );
sal_uLong nP = rOutput.Tell();
rOutput.Seek( 0 );
aStream << rOutput;
rOutput.Seek( nP );
}
#endif
return rOutput.GetError();
#else
return 0;
#endif
}
void ImpEditEngine::WriteItemAsRTF( const SfxPoolItem& rItem, SvStream& rOutput, sal_uInt16 nPara, sal_uInt16 nPos,
SvxFontTable& rFontTable, SvxColorList& rColorList )
{
sal_uInt16 nWhich = rItem.Which();
switch ( nWhich )
{
case EE_PARA_WRITINGDIR:
{
const SvxFrameDirectionItem& rWritingMode = (const SvxFrameDirectionItem&)rItem;
if ( rWritingMode.GetValue() == FRMDIR_HORI_RIGHT_TOP )
rOutput << "\\rtlpar";
else
rOutput << "\\ltrpar";
}
break;
case EE_PARA_OUTLLEVEL:
{
sal_Int16 nLevel = ((const SfxInt16Item&)rItem).GetValue();
if( nLevel >= 0 )
{
rOutput << "\\level";
rOutput.WriteNumber( nLevel );
}
}
break;
case EE_PARA_OUTLLRSPACE:
case EE_PARA_LRSPACE:
{
// const ContentNode *pNode = aEditDoc.GetObject( nPara );
rOutput << OOO_STRING_SVTOOLS_RTF_FI;
short nTxtFirst = ((const SvxLRSpaceItem&)rItem).GetTxtFirstLineOfst();
nTxtFirst = (short)LogicToTwips( nTxtFirst );
rOutput.WriteNumber( nTxtFirst );
rOutput << OOO_STRING_SVTOOLS_RTF_LI;
sal_uInt16 nTxtLeft = static_cast< sal_uInt16 >(((const SvxLRSpaceItem&)rItem).GetTxtLeft());
nTxtLeft = (sal_uInt16)LogicToTwips( nTxtLeft );
rOutput.WriteNumber( nTxtLeft );
rOutput << OOO_STRING_SVTOOLS_RTF_RI;
sal_uInt32 nTxtRight = ((const SvxLRSpaceItem&)rItem).GetRight();
nTxtRight = LogicToTwips( nTxtRight);
rOutput.WriteNumber( nTxtRight );
}
break;
case EE_PARA_ULSPACE:
{
rOutput << OOO_STRING_SVTOOLS_RTF_SB;
sal_uInt16 nUpper = ((const SvxULSpaceItem&)rItem).GetUpper();
nUpper = (sal_uInt16)LogicToTwips( nUpper );
rOutput.WriteNumber( nUpper );
rOutput << OOO_STRING_SVTOOLS_RTF_SA;
sal_uInt16 nLower = ((const SvxULSpaceItem&)rItem).GetLower();
nLower = (sal_uInt16)LogicToTwips( nLower );
rOutput.WriteNumber( nLower );
}
break;
case EE_PARA_SBL:
{
rOutput << OOO_STRING_SVTOOLS_RTF_SL;
long nVal = ((const SvxLineSpacingItem&)rItem).GetLineHeight();
char cMult = '0';
if ( ((const SvxLineSpacingItem&)rItem).GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP )
{
// Woher kriege ich jetzt den Wert?
// Der SwRTF-Parser geht von einem 240er Font aus!
nVal = ((const SvxLineSpacingItem&)rItem).GetPropLineSpace();
nVal *= 240;
nVal /= 100;
cMult = '1';
}
rOutput.WriteNumber( nVal );
rOutput << OOO_STRING_SVTOOLS_RTF_SLMULT << cMult;
}
break;
case EE_PARA_JUST:
{
SvxAdjust eJustification = ((const SvxAdjustItem&)rItem).GetAdjust();
switch ( eJustification )
{
case SVX_ADJUST_CENTER: rOutput << OOO_STRING_SVTOOLS_RTF_QC;
break;
case SVX_ADJUST_RIGHT: rOutput << OOO_STRING_SVTOOLS_RTF_QR;
break;
default: rOutput << OOO_STRING_SVTOOLS_RTF_QL;
break;
}
}
break;
case EE_PARA_TABS:
{
const SvxTabStopItem& rTabs = (const SvxTabStopItem&) rItem;
for ( sal_uInt16 i = 0; i < rTabs.Count(); i++ )
{
const SvxTabStop& rTab = rTabs[i];
rOutput << OOO_STRING_SVTOOLS_RTF_TX;
rOutput.WriteNumber( LogicToTwips( rTab.GetTabPos() ) );
}
}
break;
case EE_CHAR_COLOR:
{
sal_uInt32 n = rColorList.GetId( (const SvxColorItem&)rItem );
rOutput << OOO_STRING_SVTOOLS_RTF_CF;
rOutput.WriteNumber( n );
}
break;
case EE_CHAR_FONTINFO:
case EE_CHAR_FONTINFO_CJK:
case EE_CHAR_FONTINFO_CTL:
{
sal_uInt32 n = rFontTable.GetId( (const SvxFontItem&)rItem );
rOutput << OOO_STRING_SVTOOLS_RTF_F;
rOutput.WriteNumber( n );
}
break;
case EE_CHAR_FONTHEIGHT:
case EE_CHAR_FONTHEIGHT_CJK:
case EE_CHAR_FONTHEIGHT_CTL:
{
rOutput << OOO_STRING_SVTOOLS_RTF_FS;
long nHeight = ((const SvxFontHeightItem&)rItem).GetHeight();
nHeight = LogicToTwips( nHeight );
// Twips => HalfPoints
nHeight /= 10;
rOutput.WriteNumber( nHeight );
}
break;
case EE_CHAR_WEIGHT:
case EE_CHAR_WEIGHT_CJK:
case EE_CHAR_WEIGHT_CTL:
{
FontWeight e = ((const SvxWeightItem&)rItem).GetWeight();
switch ( e )
{
case WEIGHT_BOLD: rOutput << OOO_STRING_SVTOOLS_RTF_B; break;
default: rOutput << OOO_STRING_SVTOOLS_RTF_B << '0'; break;
}
}
break;
case EE_CHAR_UNDERLINE:
{
// muesste bei WordLineMode ggf. ulw werden,
// aber die Information fehlt hier
FontUnderline e = ((const SvxUnderlineItem&)rItem).GetLineStyle();
switch ( e )
{
case UNDERLINE_NONE: rOutput << OOO_STRING_SVTOOLS_RTF_ULNONE; break;
case UNDERLINE_SINGLE: rOutput << OOO_STRING_SVTOOLS_RTF_UL; break;
case UNDERLINE_DOUBLE: rOutput << OOO_STRING_SVTOOLS_RTF_ULDB; break;
case UNDERLINE_DOTTED: rOutput << OOO_STRING_SVTOOLS_RTF_ULD; break;
default:
break;
}
}
break;
case EE_CHAR_OVERLINE:
{
FontUnderline e = ((const SvxOverlineItem&)rItem).GetLineStyle();
switch ( e )
{
case UNDERLINE_NONE: rOutput << OOO_STRING_SVTOOLS_RTF_OLNONE; break;
case UNDERLINE_SINGLE: rOutput << OOO_STRING_SVTOOLS_RTF_OL; break;
case UNDERLINE_DOUBLE: rOutput << OOO_STRING_SVTOOLS_RTF_OLDB; break;
case UNDERLINE_DOTTED: rOutput << OOO_STRING_SVTOOLS_RTF_OLD; break;
default:
break;
}
}
break;
case EE_CHAR_STRIKEOUT:
{
FontStrikeout e = ((const SvxCrossedOutItem&)rItem).GetStrikeout();
switch ( e )
{
case STRIKEOUT_SINGLE:
case STRIKEOUT_DOUBLE: rOutput << OOO_STRING_SVTOOLS_RTF_STRIKE; break;
case STRIKEOUT_NONE: rOutput << OOO_STRING_SVTOOLS_RTF_STRIKE << '0'; break;
default:
break;
}
}
break;
case EE_CHAR_ITALIC:
case EE_CHAR_ITALIC_CJK:
case EE_CHAR_ITALIC_CTL:
{
FontItalic e = ((const SvxPostureItem&)rItem).GetPosture();
switch ( e )
{
case ITALIC_OBLIQUE:
case ITALIC_NORMAL: rOutput << OOO_STRING_SVTOOLS_RTF_I; break;
case ITALIC_NONE: rOutput << OOO_STRING_SVTOOLS_RTF_I << '0'; break;
default:
break;
}
}
break;
case EE_CHAR_OUTLINE:
{
rOutput << OOO_STRING_SVTOOLS_RTF_OUTL;
if ( ((const SvxContourItem&)rItem).GetValue() == 0 )
rOutput << '0';
}
break;
case EE_CHAR_RELIEF:
{
sal_uInt16 nRelief = ((const SvxCharReliefItem&)rItem).GetValue();
if ( nRelief == RELIEF_EMBOSSED )
rOutput << OOO_STRING_SVTOOLS_RTF_EMBO;
if ( nRelief == RELIEF_ENGRAVED )
rOutput << OOO_STRING_SVTOOLS_RTF_IMPR;
}
break;
case EE_CHAR_EMPHASISMARK:
{
sal_uInt16 nMark = ((const SvxEmphasisMarkItem&)rItem).GetValue();
if ( nMark == EMPHASISMARK_NONE )
rOutput << OOO_STRING_SVTOOLS_RTF_ACCNONE;
else if ( nMark == EMPHASISMARK_SIDE_DOTS )
rOutput << OOO_STRING_SVTOOLS_RTF_ACCCOMMA;
else
rOutput << OOO_STRING_SVTOOLS_RTF_ACCDOT;
}
break;
case EE_CHAR_SHADOW:
{
rOutput << OOO_STRING_SVTOOLS_RTF_SHAD;
if ( ((const SvxShadowedItem&)rItem).GetValue() == 0 )
rOutput << '0';
}
break;
case EE_FEATURE_TAB:
{
rOutput << OOO_STRING_SVTOOLS_RTF_TAB;
}
break;
case EE_FEATURE_LINEBR:
{
rOutput << OOO_STRING_SVTOOLS_RTF_SL;
}
break;
case EE_CHAR_KERNING:
{
rOutput << OOO_STRING_SVTOOLS_RTF_EXPNDTW;
rOutput.WriteNumber( LogicToTwips(
((const SvxKerningItem&)rItem).GetValue() ) );
}
break;
case EE_CHAR_PAIRKERNING:
{
rOutput << OOO_STRING_SVTOOLS_RTF_KERNING;
rOutput.WriteNumber( ((const SvxAutoKernItem&)rItem).GetValue() ? 1 : 0 );
}
break;
case EE_CHAR_ESCAPEMENT:
{
SvxFont aFont;
ContentNode* pNode = aEditDoc.GetObject( nPara );
SeekCursor( pNode, nPos, aFont );
MapMode aPntMode( MAP_POINT );
long nFontHeight = GetRefDevice()->LogicToLogic(
aFont.GetSize(), &GetRefMapMode(), &aPntMode ).Height();
nFontHeight *=2; // HalfPoints
sal_uInt16 nProp = ((const SvxEscapementItem&)rItem).GetProp();
sal_uInt16 nProp100 = nProp*100; // Fuer SWG-Token Prop in 100tel Prozent.
short nEsc = ((const SvxEscapementItem&)rItem).GetEsc();
if ( nEsc == DFLT_ESC_AUTO_SUPER )
{
nEsc = 100 - nProp;
nProp100++; // Eine 1 hinten bedeutet 'automatisch'.
}
else if ( nEsc == DFLT_ESC_AUTO_SUB )
{
nEsc = sal::static_int_cast< short >( -( 100 - nProp ) );
nProp100++;
}
// SWG:
if ( nEsc )
rOutput << "{\\*\\updnprop" << ByteString::CreateFromInt32( nProp100 ).GetBuffer() << '}';
long nUpDown = nFontHeight * Abs( nEsc ) / 100;
ByteString aUpDown = ByteString::CreateFromInt32( nUpDown );
if ( nEsc < 0 )
rOutput << OOO_STRING_SVTOOLS_RTF_DN << aUpDown.GetBuffer();
else if ( nEsc > 0 )
rOutput << OOO_STRING_SVTOOLS_RTF_UP << aUpDown.GetBuffer();
}
break;
}
}
sal_uInt32 ImpEditEngine::WriteHTML( SvStream&, EditSelection )
{
return 0;
}
EditTextObject* ImpEditEngine::CreateTextObject()
{
EditSelection aCompleteSelection;
aCompleteSelection.Min() = aEditDoc.GetStartPaM();
aCompleteSelection.Max() = aEditDoc.GetEndPaM();
return CreateTextObject( aCompleteSelection );
}
EditTextObject* ImpEditEngine::CreateTextObject( EditSelection aSel )
{
return CreateBinTextObject( aSel, GetEditTextObjectPool(), aStatus.AllowBigObjects(), nBigTextObjectStart );
}
EditTextObject* ImpEditEngine::CreateBinTextObject( EditSelection aSel, SfxItemPool* pPool, sal_Bool bAllowBigObjects, sal_uInt16 nBigObjectStart ) const
{
BinTextObject* pTxtObj = new BinTextObject( pPool );
pTxtObj->SetVertical( IsVertical() );
MapUnit eMapUnit = (MapUnit)aEditDoc.GetItemPool().GetMetric( DEF_METRIC );
pTxtObj->SetMetric( (sal_uInt16) eMapUnit );
if ( pTxtObj->IsOwnerOfPool() )
pTxtObj->GetPool()->SetDefaultMetric( (SfxMapUnit) eMapUnit );
sal_uInt16 nStartNode, nEndNode;
sal_uInt32 nTextPortions = 0;
aSel.Adjust( aEditDoc );
nStartNode = aEditDoc.GetPos( aSel.Min().GetNode() );
nEndNode = aEditDoc.GetPos( aSel.Max().GetNode() );
sal_Bool bOnlyFullParagraphs = ( aSel.Min().GetIndex() ||
( aSel.Max().GetIndex() < aSel.Max().GetNode()->Len() ) ) ?
sal_False : sal_True;
// Vorlagen werden nicht gespeichert!
// ( Nur Name und Familie, Vorlage selbst muss in App stehen! )
pTxtObj->SetScriptType( GetScriptType( aSel ) );
// ueber die Absaetze iterieren...
sal_uInt16 nNode;
for ( nNode = nStartNode; nNode <= nEndNode; nNode++ )
{
ContentNode* pNode = aEditDoc.SaveGetObject( nNode );
DBG_ASSERT( pNode, "Node nicht gefunden: Search&Replace" );
if ( bOnlyFullParagraphs )
{
ParaPortion* pParaPortion = GetParaPortions()[nNode];
nTextPortions += pParaPortion->GetTextPortions().Count();
}
sal_uInt16 nStartPos = 0;
sal_uInt16 nEndPos = pNode->Len();
sal_Bool bEmptyPara = nEndPos ? sal_False : sal_True;
if ( ( nNode == nStartNode ) && !bOnlyFullParagraphs )
nStartPos = aSel.Min().GetIndex();
if ( ( nNode == nEndNode ) && !bOnlyFullParagraphs )
nEndPos = aSel.Max().GetIndex();
ContentInfo* pC = pTxtObj->CreateAndInsertContent();
// Die Absatzattribute...
pC->GetParaAttribs().Set( pNode->GetContentAttribs().GetItems() );
// Das StyleSheet...
if ( pNode->GetStyleSheet() )
{
pC->GetStyle() = pNode->GetStyleSheet()->GetName();
pC->GetFamily() = pNode->GetStyleSheet()->GetFamily();
}
// Der Text...
pC->GetText() = pNode->Copy( nStartPos, nEndPos-nStartPos );
// und die Attribute...
sal_uInt16 nAttr = 0;
EditCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
while ( pAttr )
{
// In einem leeren Absatz die Attribute behalten!
if ( bEmptyPara ||
( ( pAttr->GetEnd() > nStartPos ) && ( pAttr->GetStart() < nEndPos ) ) )
{
XEditAttribute* pX = pTxtObj->CreateAttrib( *pAttr->GetItem(), pAttr->GetStart(), pAttr->GetEnd() );
// Evtl. korrigieren...
if ( ( nNode == nStartNode ) && ( nStartPos != 0 ) )
{
pX->GetStart() = ( pX->GetStart() > nStartPos ) ? pX->GetStart()-nStartPos : 0;
pX->GetEnd() = pX->GetEnd() - nStartPos;
}
if ( nNode == nEndNode )
{
if ( pX->GetEnd() > (nEndPos-nStartPos) )
pX->GetEnd() = nEndPos-nStartPos;
}
DBG_ASSERT( pX->GetEnd() <= (nEndPos-nStartPos), "CreateBinTextObject: Attribut zu lang!" );
if ( !pX->GetLen() && !bEmptyPara )
pTxtObj->DestroyAttrib( pX );
else
pC->GetAttribs().Insert( pX, pC->GetAttribs().Count() );
}
nAttr++;
pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
}
#ifndef SVX_LIGHT
// ggf. Online-Spelling
if ( bAllowBigObjects && bOnlyFullParagraphs && pNode->GetWrongList() )
pC->SetWrongList( pNode->GetWrongList()->Clone() );
#endif // !SVX_LIGHT
}
// Bei grossen Textobjekten die PortionInfos merken:
// Schwelle rauf setzen, wenn Olli die Absaetze nicht mehr zerhackt!
if ( bAllowBigObjects && bOnlyFullParagraphs && IsFormatted() && GetUpdateMode() && ( nTextPortions >= nBigObjectStart ) )
{
XParaPortionList* pXList = new XParaPortionList( GetRefDevice(), aPaperSize.Width() );
pTxtObj->SetPortionInfo( pXList );
for ( nNode = nStartNode; nNode <= nEndNode; nNode++ )
{
ParaPortion* pParaPortion = GetParaPortions()[nNode];
XParaPortion* pX = new XParaPortion;
pXList->Insert( pX, pXList->Count() );
pX->nHeight = pParaPortion->GetHeight();
pX->nFirstLineOffset = pParaPortion->GetFirstLineOffset();
// Die TextPortions
sal_uInt16 nCount = pParaPortion->GetTextPortions().Count();
sal_uInt16 n;
for ( n = 0; n < nCount; n++ )
{
TextPortion* pTextPortion = pParaPortion->GetTextPortions()[n];
TextPortion* pNew = new TextPortion( *pTextPortion );
pX->aTextPortions.Insert( pNew, pX->aTextPortions.Count() );
}
// Die Zeilen
nCount = pParaPortion->GetLines().Count();
for ( n = 0; n < nCount; n++ )
{
EditLine* pLine = pParaPortion->GetLines()[n];
EditLine* pNew = pLine->Clone();
pX->aLines.Insert( pNew, pX->aLines.Count() );
}
#ifdef DBG_UTIL
sal_uInt16 nTest;
int nTPLen = 0, nTxtLen = 0;
for ( nTest = pParaPortion->GetTextPortions().Count(); nTest; )
nTPLen += pParaPortion->GetTextPortions().GetObject( --nTest )->GetLen();
for ( nTest = pParaPortion->GetLines().Count(); nTest; )
nTxtLen += pParaPortion->GetLines().GetObject( --nTest )->GetLen();
DBG_ASSERT( ( nTPLen == pParaPortion->GetNode()->Len() ) && ( nTxtLen == pParaPortion->GetNode()->Len() ), "CreateBinTextObject: ParaPortion not completely formatted!" );
#endif
}
}
return pTxtObj;
}
void ImpEditEngine::SetText( const EditTextObject& rTextObject )
{
// Da Setzen eines TextObject ist nicht Undo-faehig!
ResetUndoManager();
sal_Bool _bUpdate = GetUpdateMode();
sal_Bool _bUndo = IsUndoEnabled();
SetText( XubString() );
EditPaM aPaM = aEditDoc.GetStartPaM();
SetUpdateMode( sal_False );
EnableUndo( sal_False );
InsertText( rTextObject, EditSelection( aPaM, aPaM ) );
SetVertical( rTextObject.IsVertical() );
#ifndef SVX_LIGHT
DBG_ASSERT( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "Woher kommt das Undo in SetText ?!" );
#endif
SetUpdateMode( _bUpdate );
EnableUndo( _bUndo );
}
EditSelection ImpEditEngine::InsertText( const EditTextObject& rTextObject, EditSelection aSel )
{
EnterBlockNotifications();
aSel.Adjust( aEditDoc );
if ( aSel.HasRange() )
aSel = ImpDeleteSelection( aSel );
EditSelection aNewSel = InsertBinTextObject( (BinTextObject&)rTextObject, aSel.Max() );
LeaveBlockNotifications();
return aNewSel;
// MT 05/00: InsertBinTextObject direkt hier machen...
}
EditSelection ImpEditEngine::InsertBinTextObject( BinTextObject& rTextObject, EditPaM aPaM )
{
// Optimieren:
// Kein GetPos undFindParaportion, sondern Index berechnen!
EditSelection aSel( aPaM, aPaM );
DBG_ASSERT( !aSel.DbgIsBuggy( aEditDoc ), "InsertBibTextObject: Selektion kaput!(1)" );
sal_Bool bUsePortionInfo = sal_False;
// sal_Bool bFields = sal_False;
XParaPortionList* pPortionInfo = rTextObject.GetPortionInfo();
if ( pPortionInfo && ( (long)pPortionInfo->GetPaperWidth() == aPaperSize.Width() )
&& ( pPortionInfo->GetRefMapMode() == GetRefDevice()->GetMapMode() ) )
{
if ( ( pPortionInfo->GetRefDevPtr() == (sal_uIntPtr)GetRefDevice() ) ||
( ( pPortionInfo->GetRefDevType() == OUTDEV_VIRDEV ) &&
( GetRefDevice()->GetOutDevType() == OUTDEV_VIRDEV ) ) )
bUsePortionInfo = sal_True;
}
sal_Bool bConvertItems = sal_False;
MapUnit eSourceUnit = MapUnit(), eDestUnit = MapUnit();
if ( rTextObject.HasMetric() )
{
eSourceUnit = (MapUnit)rTextObject.GetMetric();
eDestUnit = (MapUnit)aEditDoc.GetItemPool().GetMetric( DEF_METRIC );
if ( eSourceUnit != eDestUnit )
bConvertItems = sal_True;
}
sal_uInt16 nContents = rTextObject.GetContents().Count();
sal_uInt16 nPara = aEditDoc.GetPos( aPaM.GetNode() );
for ( sal_uInt16 n = 0; n < nContents; n++, nPara++ )
{
ContentInfo* pC = rTextObject.GetContents().GetObject( n );
sal_Bool bNewContent = aPaM.GetNode()->Len() ? sal_False: sal_True;
sal_uInt16 nStartPos = aPaM.GetIndex();
aPaM = ImpFastInsertText( aPaM, pC->GetText() );
ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
DBG_ASSERT( pPortion, "Blinde Portion in FastInsertText" );
pPortion->MarkInvalid( nStartPos, pC->GetText().Len() );
// Zeicheattribute...
sal_Bool bAllreadyHasAttribs = aPaM.GetNode()->GetCharAttribs().Count() ? sal_True : sal_False;
sal_uInt16 nNewAttribs = pC->GetAttribs().Count();
if ( nNewAttribs )
{
sal_Bool bUpdateFields = sal_False;
for ( sal_uInt16 nAttr = 0; nAttr < nNewAttribs; nAttr++ )
{
XEditAttribute* pX = pC->GetAttribs().GetObject( nAttr );
// Kann passieren wenn Absaetze >16K entstehen, dann wird einfach umgebrochen.
if ( pX->GetEnd() <= aPaM.GetNode()->Len() )
{
if ( !bAllreadyHasAttribs || pX->IsFeature() )
{
// Normale Attribute gehen dann schneller...
// Features duerfen nicht ueber EditDoc::InsertAttrib
// eingefuegt werden, sie sind bei FastInsertText schon im TextFluss
DBG_ASSERT( pX->GetEnd() <= aPaM.GetNode()->Len(), "InsertBinTextObject: Attribut zu gross!" );
EditCharAttrib* pAttr;
if ( !bConvertItems )
pAttr = MakeCharAttrib( aEditDoc.GetItemPool(), *(pX->GetItem()), pX->GetStart()+nStartPos, pX->GetEnd()+nStartPos );
else
{
SfxPoolItem* pNew = pX->GetItem()->Clone();
ConvertItem( *pNew, eSourceUnit, eDestUnit );
pAttr = MakeCharAttrib( aEditDoc.GetItemPool(), *pNew, pX->GetStart()+nStartPos, pX->GetEnd()+nStartPos );
delete pNew;
}
DBG_ASSERT( pAttr->GetEnd() <= aPaM.GetNode()->Len(), "InsertBinTextObject: Attribut passt nicht! (1)" );
aPaM.GetNode()->GetCharAttribs().InsertAttrib( pAttr );
if ( pAttr->Which() == EE_FEATURE_FIELD )
bUpdateFields = sal_True;
}
else
{
DBG_ASSERT( pX->GetEnd()+nStartPos <= aPaM.GetNode()->Len(), "InsertBinTextObject: Attribut passt nicht! (2)" );
// Tabs und andere Features koennen nicht ueber InsertAttrib eingefuegt werden:
aEditDoc.InsertAttrib( aPaM.GetNode(), pX->GetStart()+nStartPos, pX->GetEnd()+nStartPos, *pX->GetItem() );
}
}
}
if ( bUpdateFields )
UpdateFields();
// Sonst QuickFormat => Keine Attribute!
pPortion->MarkSelectionInvalid( nStartPos, pC->GetText().Len() );
}
DBG_ASSERT( CheckOrderedList( aPaM.GetNode()->GetCharAttribs().GetAttribs(), sal_True ), "InsertBinTextObject: Start-Liste verdreht" );
sal_Bool bParaAttribs = sal_False;
if ( bNewContent || ( ( n > 0 ) && ( n < (nContents-1) ) ) )
{
bParaAttribs = sal_False;
// #101512# Don't overwrite level/style from existing paragraph in OutlineView
// MT 10/2002: Removed because of #103874#, handled in Outliner::EndPasteOrDropHdl now.
// if ( !aStatus.IsOutliner() || n )
{
// nur dann Style und ParaAttribs, wenn neuer Absatz, oder
// komplett inneliegender...
bParaAttribs = pC->GetParaAttribs().Count() ? sal_True : sal_False;
if ( GetStyleSheetPool() && pC->GetStyle().Len() )
{
SfxStyleSheet* pStyle = (SfxStyleSheet*)GetStyleSheetPool()->Find( pC->GetStyle(), pC->GetFamily() );
DBG_ASSERT( pStyle, "InsertBinTextObject - Style not found!" );
SetStyleSheet( nPara, pStyle );
}
if ( !bConvertItems )
SetParaAttribs( aEditDoc.GetPos( aPaM.GetNode() ), pC->GetParaAttribs() );
else
{
SfxItemSet aAttribs( GetEmptyItemSet() );
ConvertAndPutItems( aAttribs, pC->GetParaAttribs(), &eSourceUnit, &eDestUnit );
SetParaAttribs( aEditDoc.GetPos( aPaM.GetNode() ), aAttribs );
}
}
if ( bNewContent && bUsePortionInfo )
{
XParaPortion* pXP = pPortionInfo->GetObject( n );
DBG_ASSERT( pXP, "InsertBinTextObject: PortionInfo?" );
ParaPortion* pParaPortion = GetParaPortions()[ nPara ];
DBG_ASSERT( pParaPortion, "InsertBinTextObject: ParaPortion?" );
pParaPortion->nHeight = pXP->nHeight;
pParaPortion->nFirstLineOffset = pXP->nFirstLineOffset;
pParaPortion->bForceRepaint = sal_True;
pParaPortion->SetValid(); // Nicht formatieren
// Die TextPortions
pParaPortion->GetTextPortions().Reset();
sal_uInt16 nCount = pXP->aTextPortions.Count();
for ( sal_uInt16 _n = 0; _n < nCount; _n++ )
{
TextPortion* pTextPortion = pXP->aTextPortions[_n];
TextPortion* pNew = new TextPortion( *pTextPortion );
pParaPortion->GetTextPortions().Insert( pNew, _n );
}
// Die Zeilen
pParaPortion->GetLines().Reset();
nCount = pXP->aLines.Count();
for ( sal_uInt16 m = 0; m < nCount; m++ )
{
EditLine* pLine = pXP->aLines[m];
EditLine* pNew = pLine->Clone();
pNew->SetInvalid(); // neu Painten!
pParaPortion->GetLines().Insert( pNew, m );
}
#ifdef DBG_UTIL
sal_uInt16 nTest;
int nTPLen = 0, nTxtLen = 0;
for ( nTest = pParaPortion->GetTextPortions().Count(); nTest; )
nTPLen += pParaPortion->GetTextPortions().GetObject( --nTest )->GetLen();
for ( nTest = pParaPortion->GetLines().Count(); nTest; )
nTxtLen += pParaPortion->GetLines().GetObject( --nTest )->GetLen();
DBG_ASSERT( ( nTPLen == pParaPortion->GetNode()->Len() ) && ( nTxtLen == pParaPortion->GetNode()->Len() ), "InsertBinTextObject: ParaPortion not completely formatted!" );
#endif
}
}
if ( !bParaAttribs ) // DefFont wird bei FastInsertParagraph nicht berechnet
{
aPaM.GetNode()->GetCharAttribs().GetDefFont() = aEditDoc.GetDefFont();
if ( aStatus.UseCharAttribs() )
aPaM.GetNode()->CreateDefFont();
}
#ifndef SVX_LIGHT
if ( bNewContent && GetStatus().DoOnlineSpelling() && pC->GetWrongList() )
{
aPaM.GetNode()->DestroyWrongList(); // otherwise MLK, if list exists...
aPaM.GetNode()->SetWrongList( pC->GetWrongList()->Clone() );
}
#endif // !SVX_LIGHT
// Zeilenumbruch, wenn weitere folgen...
if ( n < ( nContents-1) )
{
if ( bNewContent )
aPaM = ImpFastInsertParagraph( nPara+1 );
else
aPaM = ImpInsertParaBreak( aPaM, sal_False );
}
}
aSel.Max() = aPaM;
DBG_ASSERT( !aSel.DbgIsBuggy( aEditDoc ), "InsertBibTextObject: Selektion kaput!(1)" );
return aSel;
}
LanguageType ImpEditEngine::GetLanguage( const EditPaM& rPaM, sal_uInt16* pEndPos ) const
{
short nScriptType = GetScriptType( rPaM, pEndPos ); // pEndPos will be valid now, pointing to ScriptChange or NodeLen
sal_uInt16 nLangId = GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType );
const SvxLanguageItem* pLangItem = &(const SvxLanguageItem&)rPaM.GetNode()->GetContentAttribs().GetItem( nLangId );
EditCharAttrib* pAttr = rPaM.GetNode()->GetCharAttribs().FindAttrib( nLangId, rPaM.GetIndex() );
if ( pAttr )
pLangItem = (const SvxLanguageItem*)pAttr->GetItem();
if ( pEndPos && pAttr && ( pAttr->GetEnd() < *pEndPos ) )
*pEndPos = pAttr->GetEnd();
return pLangItem->GetLanguage();
}
::com::sun::star::lang::Locale ImpEditEngine::GetLocale( const EditPaM& rPaM ) const
{
return SvxCreateLocale( GetLanguage( rPaM ) );
}
Reference< XSpellChecker1 > ImpEditEngine::GetSpeller()
{
#ifndef SVX_LIGHT
if ( !xSpeller.is() )
xSpeller = SvxGetSpellChecker();
#endif
return xSpeller;
}
SpellInfo * ImpEditEngine::CreateSpellInfo( const EditSelection &rSel, bool bMultipleDocs )
{
if (!pSpellInfo)
pSpellInfo = new SpellInfo;
else
*pSpellInfo = SpellInfo(); // reset to default values
pSpellInfo->bMultipleDoc = bMultipleDocs;
EditSelection aSentenceSel( SelectSentence( rSel ) );
// pSpellInfo->aSpellStart = CreateEPaM( aSentenceSel.Min() );
// pSpellInfo->aSpellTo = CreateEPaM( rSel.HasRange()? aSentenceSel.Max() : aSentenceSel.Min() );
// always spell draw objects completely, startting at the top.
// (spelling in only a selection or not starting with the top requires
// further changes elsewehe to work properly)
pSpellInfo->aSpellStart = EPaM();
pSpellInfo->aSpellTo = EPaM( EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND );
return pSpellInfo;
}
EESpellState ImpEditEngine::Spell( EditView* pEditView, sal_Bool bMultipleDoc )
{
#ifdef SVX_LIGHT
return EE_SPELL_NOSPELLER;
#else
DBG_ASSERTWARNING( xSpeller.is(), "Kein Speller gesetzt!" );
if ( !xSpeller.is() )
return EE_SPELL_NOSPELLER;
aOnlineSpellTimer.Stop();
// Bei MultipleDoc immer von vorne/hinten...
if ( bMultipleDoc )
{
pEditView->pImpEditView->SetEditSelection( aEditDoc.GetStartPaM() );
}
EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
pSpellInfo = CreateSpellInfo( aCurSel, bMultipleDoc );
sal_Bool bIsStart = sal_False;
if ( bMultipleDoc )
bIsStart = sal_True; // Immer von Vorne bzw. von hinten...
else if ( ( CreateEPaM( aEditDoc.GetStartPaM() ) == pSpellInfo->aSpellStart ) )
bIsStart = sal_True;
EditSpellWrapper* pWrp = new EditSpellWrapper( Application::GetDefDialogParent(),
xSpeller, bIsStart, sal_False, pEditView );
pWrp->SpellDocument();
delete pWrp;
if ( !bMultipleDoc )
{
pEditView->pImpEditView->DrawSelection();
if ( aCurSel.Max().GetIndex() > aCurSel.Max().GetNode()->Len() )
aCurSel.Max().GetIndex() = aCurSel.Max().GetNode()->Len();
aCurSel.Min() = aCurSel.Max();
pEditView->pImpEditView->SetEditSelection( aCurSel );
pEditView->pImpEditView->DrawSelection();
pEditView->ShowCursor( sal_True, sal_False );
}
EESpellState eState = pSpellInfo->eState;
delete pSpellInfo;
pSpellInfo = 0;
return eState;
#endif
}
sal_Bool ImpEditEngine::HasConvertibleTextPortion( LanguageType nSrcLang )
{
#ifdef SVX_LIGHT
return sal_False;
#else
sal_Bool bHasConvTxt = sal_False;
sal_uInt16 nParas = pEditEngine->GetParagraphCount();
for (sal_uInt16 k = 0; k < nParas; ++k)
{
SvUShorts aPortions;
pEditEngine->GetPortions( k, aPortions );
for ( sal_uInt16 nPos = 0; nPos < aPortions.Count(); ++nPos )
{
sal_uInt16 nEnd = aPortions.GetObject( nPos );
sal_uInt16 nStart = nPos > 0 ? aPortions.GetObject( nPos - 1 ) : 0;
// 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 = pEditEngine->GetLanguage( k, nStart );
#ifdef DEBUG
lang::Locale aLocale( SvxCreateLocale( nLangFound ) );
#endif
bHasConvTxt = (nSrcLang == nLangFound) ||
(editeng::HangulHanjaConversion::IsChinese( nLangFound ) &&
editeng::HangulHanjaConversion::IsChinese( nSrcLang ));
if (bHasConvTxt)
return bHasConvTxt;
}
}
#endif
return bHasConvTxt;
}
void ImpEditEngine::Convert( EditView* pEditView,
LanguageType nSrcLang, LanguageType nDestLang, const Font *pDestFont,
sal_Int32 nOptions, sal_Bool bIsInteractive, sal_Bool bMultipleDoc )
{
// modified version of ImpEditEngine::Spell
#ifdef SVX_LIGHT
#else
// Bei MultipleDoc immer von vorne/hinten...
if ( bMultipleDoc )
pEditView->pImpEditView->SetEditSelection( aEditDoc.GetStartPaM() );
//
// initialize pConvInfo
//
EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
aCurSel.Adjust( aEditDoc );
pConvInfo = new ConvInfo;
pConvInfo->bMultipleDoc = bMultipleDoc;
pConvInfo->aConvStart = CreateEPaM( aCurSel.Min() );
//
// 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.
sal_uInt16 nStartIdx = ( editeng::HangulHanjaConversion::IsChinese( nSrcLang ) ) ?
0 : aWordStartPaM.GetIndex();
pConvInfo->aConvStart.nIndex = nStartIdx;
}
//
pConvInfo->aConvContinue = pConvInfo->aConvStart;
sal_Bool bIsStart = sal_False;
if ( bMultipleDoc )
bIsStart = sal_True; // Immer von Vorne bzw. von hinten...
else if ( CreateEPaM( aEditDoc.GetStartPaM() ) == pConvInfo->aConvStart )
bIsStart = sal_True;
bImpConvertFirstCall = sal_True; // next ImpConvert call is the very first in this conversion turn
Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
TextConvWrapper aWrp( Application::GetDefDialogParent(), xMSF,
SvxCreateLocale( nSrcLang ), SvxCreateLocale( nDestLang ),
pDestFont,
nOptions, bIsInteractive,
bIsStart, pEditView );
//
//!! 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 );
aWrp.Convert();
//if (!bIsInteractive)
//SetUpdateMode( sal_True, 0, sal_True );
if ( !bMultipleDoc )
{
pEditView->pImpEditView->DrawSelection();
if ( aCurSel.Max().GetIndex() > aCurSel.Max().GetNode()->Len() )
aCurSel.Max().GetIndex() = aCurSel.Max().GetNode()->Len();
aCurSel.Min() = aCurSel.Max();
pEditView->pImpEditView->SetEditSelection( aCurSel );
pEditView->pImpEditView->DrawSelection();
pEditView->ShowCursor( sal_True, sal_False );
}
delete pConvInfo;
pConvInfo = 0;
#endif
}
void ImpEditEngine::SetLanguageAndFont(
const ESelection &rESel,
LanguageType nLang, sal_uInt16 nLangWhichId,
const Font *pFont, sal_uInt16 nFontWhichId )
{
ESelection aOldSel = pActiveView->GetSelection();
pActiveView->SetSelection( rESel );
// set new language attribute
SfxItemSet aNewSet( pActiveView->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 = (SvxFontItem&) aNewSet.Get( nFontWhichId );
aFontItem.SetFamilyName( pFont->GetName());
aFontItem.SetFamily( pFont->GetFamily());
aFontItem.SetStyleName( pFont->GetStyleName());
aFontItem.SetPitch( pFont->GetPitch());
aFontItem.SetCharSet( pFont->GetCharSet() );
aNewSet.Put( aFontItem );
}
// apply new attributes
pActiveView->SetAttribs( aNewSet );
pActiveView->SetSelection( aOldSel );
}
void ImpEditEngine::ImpConvert( rtl::OUString &rConvTxt, LanguageType &rConvTxtLang,
EditView* pEditView, LanguageType nSrcLang, const ESelection &rConvRange,
sal_Bool bAllowImplicitChangesForNotConvertibleText,
LanguageType nTargetLang, const Font *pTargetFont )
{
// modified version of ImpEditEngine::ImpSpell
// looks for next convertible text portion to be passed on to the wrapper
String aRes;
LanguageType nResLang = LANGUAGE_NONE;
#ifdef SVX_LIGHT
rConvTxt = rtl::OUString();
rConvTxtLang = LANGUAGE_NONE;
#else
/* ContentNode* pLastNode = */ aEditDoc.SaveGetObject( aEditDoc.Count()-1 );
EditPaM aPos( CreateEditPaM( pConvInfo->aConvContinue ) );
EditSelection aCurSel = EditSelection( aPos, aPos );
String aWord;
while (!aRes.Len())
{
// empty paragraph found that needs to have language and font set?
if (bAllowImplicitChangesForNotConvertibleText &&
!pEditEngine->GetText( pConvInfo->aConvContinue.nPara ).Len())
{
sal_uInt16 nPara = pConvInfo->aConvContinue.nPara;
ESelection aESel( nPara, 0, nPara, 0 );
// see comment for below same function call
SetLanguageAndFont( aESel,
nTargetLang, EE_CHAR_LANGUAGE_CJK,
pTargetFont, EE_CHAR_FONTINFO_CJK );
}
if (pConvInfo->aConvContinue.nPara == pConvInfo->aConvTo.nPara &&
pConvInfo->aConvContinue.nIndex >= pConvInfo->aConvTo.nIndex)
break;
/*
// Bekannter (wahrscheinlicher) Bug: Wenn SpellToCurrent, muss
// Current bei jeder Ersetzung korrigiert werden, sonst passt
// das Ende evtl. nicht mehr genau...
if ( pConvInfo->bConvToEnd || pConvInfo->bMultipleDoc )
{
if ( aCurSel.Max().GetNode() == pLastNode &&
aCurSel.Max().GetIndex() >= pLastNode->Len() )
break;
}
*/
sal_uInt16 nAttribStart = USHRT_MAX;
sal_uInt16 nAttribEnd = USHRT_MAX;
sal_uInt16 nCurPos = USHRT_MAX;
EPaM aCurStart = CreateEPaM( aCurSel.Min() );
SvUShorts aPortions;
pEditEngine->GetPortions( (sal_uInt16)aCurStart.nPara, aPortions );
for ( sal_uInt16 nPos = 0; nPos < aPortions.Count(); ++nPos )
{
sal_uInt16 nEnd = aPortions.GetObject( nPos );
sal_uInt16 nStart = nPos > 0 ? aPortions.GetObject( nPos - 1 ) : 0;
// 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
sal_uInt16 nLangIdx = nEnd > nStart ? nStart + 1 : nStart;
LanguageType nLangFound = pEditEngine->GetLanguage( aCurStart.nPara, nLangIdx );
#ifdef DEBUG
lang::Locale aLocale( SvxCreateLocale( nLangFound ) );
#endif
sal_Bool bLangOk = (nLangFound == nSrcLang) ||
(editeng::HangulHanjaConversion::IsChinese( nLangFound ) &&
editeng::HangulHanjaConversion::IsChinese( nSrcLang ));
if (nAttribEnd != USHRT_MAX) // 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 == USHRT_MAX && // 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 < 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) ) );
sal_Bool bIsAsianScript = (i18n::ScriptType::ASIAN == GetScriptType( 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 != USHRT_MAX && nAttribEnd != USHRT_MAX)
{
aCurSel.Min().SetIndex( nAttribStart );
aCurSel.Max().SetIndex( nAttribEnd );
}
else if (nCurPos != USHRT_MAX)
{
// 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 );
}
if ( !pConvInfo->bConvToEnd )
{
EPaM aEPaM( CreateEPaM( aCurSel.Min() ) );
if ( !( aEPaM < pConvInfo->aConvTo ) )
break;
}
// clip selected word to the converted area
// (main use when conversion starts/ends **within** a word)
EditPaM aPaM( CreateEditPaM( pConvInfo->aConvStart ) );
if (pConvInfo->bConvToEnd &&
aCurSel.Min().GetNode() == aPaM.GetNode() &&
aCurSel.Min().GetIndex() < aPaM.GetIndex())
aCurSel.Min().SetIndex( aPaM.GetIndex() );
aPaM = CreateEditPaM( pConvInfo->aConvContinue );
if (aCurSel.Min().GetNode() == aPaM.GetNode() &&
aCurSel.Min().GetIndex() < aPaM.GetIndex())
aCurSel.Min().SetIndex( aPaM.GetIndex() );
aPaM = CreateEditPaM( pConvInfo->aConvTo );
if ((!pConvInfo->bConvToEnd || rConvRange.HasRange())&&
aCurSel.Max().GetNode() == aPaM.GetNode() &&
aCurSel.Max().GetIndex() > aPaM.GetIndex())
aCurSel.Max().SetIndex( aPaM.GetIndex() );
aWord = GetSelected( aCurSel );
if ( aWord.Len() > 0 /* && bLangOk */)
aRes = aWord;
// move to next word/paragraph if necessary
if ( !aRes.Len() )
aCurSel = WordRight( aCurSel.Min(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
pConvInfo->aConvContinue = CreateEPaM( aCurSel.Max() );
}
pEditView->pImpEditView->DrawSelection();
pEditView->pImpEditView->SetEditSelection( aCurSel );
pEditView->pImpEditView->DrawSelection();
pEditView->ShowCursor( sal_True, sal_False );
rConvTxt = aRes;
if (rConvTxt.getLength())
rConvTxtLang = nResLang;
#endif
}
Reference< XSpellAlternatives > ImpEditEngine::ImpSpell( EditView* pEditView )
{
#ifdef SVX_LIGHT
return Reference< XSpellAlternatives >();
#else
DBG_ASSERT( xSpeller.is(), "Kein Speller gesetzt!" );
ContentNode* pLastNode = aEditDoc.SaveGetObject( (aEditDoc.Count()-1) );
EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
aCurSel.Min() = aCurSel.Max();
String aWord;
Reference< XSpellAlternatives > xSpellAlt;
Sequence< PropertyValue > aEmptySeq;
while (!xSpellAlt.is())
{
// Bekannter (wahrscheinlicher) Bug: Wenn SpellToCurrent, muss
// Current bei jeder Ersetzung korrigiert werden, sonst passt
// das Ende evtl. nicht mehr genau...
if ( pSpellInfo->bSpellToEnd || pSpellInfo->bMultipleDoc )
{
if ( aCurSel.Max().GetNode() == pLastNode )
{
if ( ( aCurSel.Max().GetIndex() >= pLastNode->Len() ) )
break;
}
}
else if ( !pSpellInfo->bSpellToEnd )
{
EPaM aEPaM( CreateEPaM( aCurSel.Max() ) );
if ( !( aEPaM < pSpellInfo->aSpellTo ) )
break;
}
aCurSel = SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
aWord = GetSelected( aCurSel );
// Wenn Punkt dahinter, muss dieser mit uebergeben werden !
// Falls Abkuerzung...
if ( aWord.Len() && ( aCurSel.Max().GetIndex() < aCurSel.Max().GetNode()->Len() ) )
{
sal_Unicode cNext = aCurSel.Max().GetNode()->GetChar( aCurSel.Max().GetIndex() );
if ( cNext == '.' )
{
aCurSel.Max().GetIndex()++;
aWord += cNext;
}
}
if ( aWord.Len() > 0 )
{
LanguageType eLang = GetLanguage( aCurSel.Max() );
SvxSpellWrapper::CheckSpellLang( xSpeller, eLang );
xSpellAlt = xSpeller->spell( aWord, eLang, aEmptySeq );
}
if ( !xSpellAlt.is() )
aCurSel = WordRight( aCurSel.Min(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
else
pSpellInfo->eState = EE_SPELL_ERRORFOUND;
}
pEditView->pImpEditView->DrawSelection();
pEditView->pImpEditView->SetEditSelection( aCurSel );
pEditView->pImpEditView->DrawSelection();
pEditView->ShowCursor( sal_True, sal_False );
return xSpellAlt;
#endif
}
/*-- 13.10.2003 16:43:27---------------------------------------------------
-----------------------------------------------------------------------*/
void ImpEditEngine::EndSpelling()
{
DELETEZ(pSpellInfo);
}
/*-- 13.10.2003 16:43:27---------------------------------------------------
-----------------------------------------------------------------------*/
void ImpEditEngine::StartSpelling(EditView& rEditView, sal_Bool bMultipleDoc)
{
DBG_ASSERT(!pSpellInfo, "pSpellInfo already set?");
rEditView.pImpEditView->SetEditSelection( aEditDoc.GetStartPaM() );
EditSelection aCurSel( rEditView.pImpEditView->GetEditSelection() );
pSpellInfo = CreateSpellInfo( aCurSel, bMultipleDoc );
}
/*-- 13.10.2003 16:43:27---------------------------------------------------
Search for the next wrong word within the given selection
-----------------------------------------------------------------------*/
Reference< XSpellAlternatives > ImpEditEngine::ImpFindNextError(EditSelection& rSelection)
{
/* ContentNode* pLastNode = */ aEditDoc.SaveGetObject( (aEditDoc.Count()-1) );
EditSelection aCurSel( rSelection.Min() );
String aWord;
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;
}
aCurSel = SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
aWord = GetSelected( aCurSel );
// Wenn Punkt dahinter, muss dieser mit uebergeben werden !
// Falls Abkuerzung...
if ( aWord.Len() && ( aCurSel.Max().GetIndex() < aCurSel.Max().GetNode()->Len() ) )
{
sal_Unicode cNext = aCurSel.Max().GetNode()->GetChar( aCurSel.Max().GetIndex() );
if ( cNext == '.' )
{
aCurSel.Max().GetIndex()++;
aWord += cNext;
}
}
if ( aWord.Len() > 0 )
xSpellAlt = xSpeller->spell( aWord, GetLanguage( aCurSel.Max() ), aEmptySeq );
if ( !xSpellAlt.is() )
aCurSel = WordRight( aCurSel.Min(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
else
{
pSpellInfo->eState = EE_SPELL_ERRORFOUND;
rSelection = aCurSel;
}
}
return xSpellAlt;
}
/*-- 13.10.2003 16:43:27---------------------------------------------------
-----------------------------------------------------------------------*/
bool ImpEditEngine::SpellSentence(EditView& rEditView,
::svx::SpellPortions& rToFill,
bool /*bIsGrammarChecking*/ )
{
#ifdef SVX_LIGHT
#else
bool bRet = false;
EditSelection aCurSel( rEditView.pImpEditView->GetEditSelection() );
if(!pSpellInfo)
pSpellInfo = CreateSpellInfo( aCurSel, true );
pSpellInfo->aCurSentenceStart = aCurSel.Min();
DBG_ASSERT( xSpeller.is(), "Kein Speller gesetzt!" );
pSpellInfo->aLastSpellPortions.clear();
pSpellInfo->aLastSpellContentSelections.clear();
rToFill.clear();
//if no selection previously exists the range is extended to the end of the object
if(aCurSel.Min() == aCurSel.Max())
{
ContentNode* pLastNode = aEditDoc.SaveGetObject( aEditDoc.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 preceeding the error
EditSelection aStartSelection(aSentencePaM.Min(), aCurSel.Min());
if(aStartSelection.HasRange())
AddPortionIterated(rEditView, aStartSelection, 0, 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 = EditSelection(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()), 0, 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.pImpEditView->SetEditSelection(aSentencePaM.Max());
}
#endif
return bRet;
}
/*-- 15.10.2003 16:09:12---------------------------------------------------
adds one portion to the SpellPortions
-----------------------------------------------------------------------*/
void ImpEditEngine::AddPortion(
const EditSelection rSel,
uno::Reference< XSpellAlternatives > xAlt,
::svx::SpellPortions& rToFill,
bool bIsField)
{
#ifdef SVX_LIGHT
#else
if(rSel.HasRange())
{
svx::SpellPortion aPortion;
aPortion.sText = GetSelected( rSel );
aPortion.eLanguage = GetLanguage( rSel.Min() );
aPortion.xAlternatives = xAlt;
aPortion.bIsField = bIsField;
rToFill.push_back(aPortion);
//save the spelled portions for later use
pSpellInfo->aLastSpellPortions.push_back(aPortion);
pSpellInfo->aLastSpellContentSelections.push_back(rSel);
}
#endif
}
/*-- 15.10.2003 16:07:47---------------------------------------------------
adds one or more portions of text to the SpellPortions depending on language changes
-----------------------------------------------------------------------*/
void ImpEditEngine::AddPortionIterated(
EditView& rEditView,
const EditSelection rSel,
Reference< XSpellAlternatives > xAlt,
::svx::SpellPortions& rToFill)
{
#ifdef SVX_LIGHT
#else
if(rSel.Min() != rSel.Max())
{
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.pImpEditView->SetEditSelection( aCursor );
LanguageType eStartLanguage = GetLanguage( aCursor );
//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_uInt16 nEndField = bIsField ? pFieldAttr->GetEnd() : USHRT_MAX;
bool bIsEndField = false;
do
{
aCursor = CursorRight( aCursor);
//determine whether a field and has been reached
bIsEndField = nEndField == aCursor.GetIndex();
//search for a new field attribute
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 = bIsField ? _pFieldAttr->GetEnd() : USHRT_MAX;
LanguageType eCurLanguage = GetLanguage( aCursor );
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);
}
}
#endif
}
/*-- 13.10.2003 16:43:33---------------------------------------------------
-----------------------------------------------------------------------*/
void ImpEditEngine::ApplyChangedSentence(EditView& rEditView,
const ::svx::SpellPortions& rNewPortions,
bool bRecheck )
{
#ifdef SVX_LIGHT
#else
// Note: rNewPortions.size() == 0 is valid and happens when the whole
// sentence got removed in the dialog
DBG_ASSERT(pSpellInfo, "pSpellInfo not initialized");
if (pSpellInfo &&
pSpellInfo->aLastSpellPortions.size() > 0) // no portions -> no text to be changed
{
// 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.pImpEditView->GetEditSelection() );
xub_StrLen nOldLen = aOldSel.Max().GetNode()->Len();
UndoActionStart( EDITUNDO_INSERT );
if(pSpellInfo->aLastSpellPortions.size() == rNewPortions.size())
{
DBG_ASSERT( rNewPortions.size() > 0, "rNewPortions should not be empty here" );
DBG_ASSERT( pSpellInfo->aLastSpellPortions.size() == pSpellInfo->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 = pSpellInfo->aLastSpellPortions.end();
SpellContentSelections::const_iterator aCurrentOldPosition = pSpellInfo->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.pImpEditView->SetEditSelection( aCurrentOldPosition->Max() );
}
sal_uInt16 nScriptType = GetI18NScriptTypeOfLanguage( aCurrentNewPortion->eLanguage );
// LanguageType eTextLanguage = GetLanguage( aCurrentOldPosition->Min() );
sal_uInt16 nLangWhichId = EE_CHAR_LANGUAGE;
switch(nScriptType)
{
case SCRIPTTYPE_ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break;
case SCRIPTTYPE_COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break;
}
if(aCurrentNewPortion->sText != aCurrentOldPortion->sText)
{
//change text and apply language
SfxItemSet aSet( aEditDoc.GetItemPool(), nLangWhichId, nLangWhichId);
aSet.Put(SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId));
SetAttribs( *aCurrentOldPosition, aSet );
ImpInsertText( *aCurrentOldPosition, aCurrentNewPortion->sText );
}
else if(aCurrentNewPortion->eLanguage != aCurrentOldPortion->eLanguage)
{
//apply language
SfxItemSet aSet( aEditDoc.GetItemPool(), nLangWhichId, nLangWhichId);
aSet.Put(SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId));
SetAttribs( *aCurrentOldPosition, aSet );
}
if(aCurrentNewPortion == rNewPortions.begin())
break;
}
while(aCurrentNewPortion != rNewPortions.begin());
}
else
{
DBG_ASSERT( pSpellInfo->aLastSpellContentSelections.size() > 0, "aLastSpellContentSelections should not be empty here" );
//select the complete sentence
SpellContentSelections::const_iterator aCurrentEndPosition = pSpellInfo->aLastSpellContentSelections.end();
--aCurrentEndPosition;
SpellContentSelections::const_iterator aCurrentStartPosition = pSpellInfo->aLastSpellContentSelections.begin();
EditSelection aAllSentence(aCurrentStartPosition->Min(), aCurrentEndPosition->Max());
//delete the sentence completely
ImpDeleteSelection( aAllSentence );
svx::SpellPortions::const_iterator aCurrentNewPortion = rNewPortions.begin();
EditPaM aCurrentPaM = aAllSentence.Min();
while(aCurrentNewPortion != rNewPortions.end())
{
//set the language attribute
LanguageType eCurLanguage = GetLanguage( aCurrentPaM );
if(eCurLanguage != aCurrentNewPortion->eLanguage)
{
sal_uInt16 nScriptType = GetI18NScriptTypeOfLanguage( aCurrentNewPortion->eLanguage );
sal_uInt16 nLangWhichId = EE_CHAR_LANGUAGE;
switch(nScriptType)
{
case SCRIPTTYPE_ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break;
case SCRIPTTYPE_COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break;
}
SfxItemSet aSet( aEditDoc.GetItemPool(), nLangWhichId, nLangWhichId);
aSet.Put(SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId));
SetAttribs( aCurrentPaM, aSet );
}
//insert the new string and set the cursor to the end of the inserted string
aCurrentPaM = ImpInsertText( aCurrentPaM , aCurrentNewPortion->sText );
++aCurrentNewPortion;
}
}
UndoActionEnd( EDITUNDO_INSERT );
EditPaM aNext;
if (bRecheck)
aNext = pSpellInfo->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
sal_Int32 nDelta = rEditView.pImpEditView->GetEditSelection().Max().GetNode()->Len() - nOldLen;
xub_StrLen nEndOfSentence = aOldSel.Max().GetIndex() + nDelta;
aNext = EditPaM( aOldSel.Max().GetNode(), nEndOfSentence );
}
rEditView.pImpEditView->SetEditSelection( aNext );
FormatAndUpdate();
aEditDoc.SetModified(sal_True);
}
#endif
}
/*-- 08.09.2008 11:33:02---------------------------------------------------
-----------------------------------------------------------------------*/
void ImpEditEngine::PutSpellingToSentenceStart( EditView& rEditView )
{
#ifdef SVX_LIGHT
#else
if( pSpellInfo && pSpellInfo->aLastSpellContentSelections.size() )
{
rEditView.pImpEditView->SetEditSelection( pSpellInfo->aLastSpellContentSelections.begin()->Min() );
}
#endif
}
void ImpEditEngine::DoOnlineSpelling( ContentNode* pThisNodeOnly, sal_Bool bSpellAtCursorPos, sal_Bool bInteruptable )
{
#ifndef SVX_LIGHT
/*
Er wird ueber alle Absaetze iteriert, nur Absaetze mit invalidierter
WrongList werden geprueft...
Es werden alle Woerter im invalidierten Bereich geprueft.
Ist ein Wort falsch, aber noch nicht in der WrongList, oder umgekehrt,
wird der Bereich des Wortes invalidiert
( kein Invalidate, sondern wenn nur Uebergaenge von richtig=>falsch,
einfaches Paint, bei Uebergaengen von falsch=>richtig mit VDev
ueberplaetten )
*/
if ( !xSpeller.is() )
return;
EditPaM aCursorPos;
if( pActiveView && !bSpellAtCursorPos )
{
DBG_CHKOBJ( pActiveView, EditView, 0 );
aCursorPos = pActiveView->pImpEditView->GetEditSelection().Max();
}
sal_Bool bRestartTimer = sal_False;
ContentNode* pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count() - 1 );
sal_uInt16 nNodes = GetEditDoc().Count();
sal_uInt16 nInvalids = 0;
Sequence< PropertyValue > aEmptySeq;
for ( sal_uInt16 n = 0; n < nNodes; n++ )
{
ContentNode* pNode = GetEditDoc().GetObject( n );
if ( pThisNodeOnly )
pNode = pThisNodeOnly;
if ( pNode->GetWrongList()->IsInvalid() )
{
WrongList* pWrongList = pNode->GetWrongList();
sal_uInt16 nInvStart = pWrongList->GetInvalidStart();
sal_uInt16 nInvEnd = pWrongList->GetInvalidEnd();
sal_uInt16 nWrongs = 0; // Auch im Absatz mal die Kontrolle abgeben...
// sal_Bool bStop = sal_False;
sal_uInt16 nPaintFrom = 0xFFFF, nPaintTo = 0;
sal_Bool bSimpleRepaint = sal_True;
pWrongList->SetValid();
EditPaM aPaM( pNode, nInvStart );
EditSelection aSel( aPaM, aPaM );
while ( ( aSel.Max().GetNode() == pNode ) /* && !bStop */ )
{
if ( ( aSel.Min().GetIndex() > nInvEnd )
|| ( ( aSel.Max().GetNode() == pLastNode ) && ( aSel.Max().GetIndex() >= pLastNode->Len() ) ) )
break; // Dokument- oder Ungueltigkeitsbereich-Ende
aSel = SelectWord( aSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
String aWord( GetSelected( aSel ) );
// Wenn Punkt dahinter, muss dieser mit uebergeben werden !
// Falls Abkuerzung...
sal_Bool bDottAdded = sal_False;
if ( aSel.Max().GetIndex() < aSel.Max().GetNode()->Len() )
{
sal_Unicode cNext = aSel.Max().GetNode()->GetChar( aSel.Max().GetIndex() );
if ( cNext == '.' )
{
aSel.Max().GetIndex()++;
aWord += cNext;
bDottAdded = sal_True;
}
}
sal_Bool bChanged = sal_False;
if ( aWord.Len() > 0 )
{
sal_uInt16 nWStart = aSel.Min().GetIndex();
sal_uInt16 nWEnd= aSel.Max().GetIndex();
if ( !xSpeller->isValid( aWord, GetLanguage( EditPaM( aSel.Min().GetNode(), nWStart+1 ) ), aEmptySeq ) )
{
// Pruefen, ob schon richtig markiert...
nWrongs++;
// Nur bei SimpleRepaint stoppen, sonst zu oft VDev
// if ( ( nWrongs > 8 ) && bSimpleRepaint )
// {
// bStop = sal_True;
// pWrongList->MarkInvalid( aSel.Max().GetIndex(), nInvEnd );
// }
sal_uInt16 nXEnd = bDottAdded ? nWEnd -1 : nWEnd;
if ( !pWrongList->HasWrong( nWStart, nXEnd ) )
{
// Wort als falsch markieren...
// Aber nur, wenn nicht an Cursor-Position...
sal_Bool bCursorPos = sal_False;
if ( aCursorPos.GetNode() == pNode )
{
if ( ( nWStart <= aCursorPos.GetIndex() ) && nWEnd >= aCursorPos.GetIndex() )
bCursorPos = sal_True;
}
if ( bCursorPos )
{
// Dann weiter als ungueltig markieren...
pWrongList->GetInvalidStart() = nWStart;
pWrongList->GetInvalidEnd() = nWEnd;
bRestartTimer = sal_True;
}
else
{
// Es kann sein, dass die Wrongs in der Liste nicht
// genau ueber Woerter aufgespannt sind, weil die
// WordDelimiters beim Expandieren nicht ausgewrtet werden.
pWrongList->InsertWrong( nWStart, nXEnd, sal_True );
bChanged = sal_True;
}
}
}
else
{
// Pruefen, ob nicht als als falsch markiert....
if ( pWrongList->HasAnyWrong( nWStart, nWEnd ) )
{
pWrongList->ClearWrongs( nWStart, nWEnd, pNode );
bSimpleRepaint = sal_False;
bChanged = sal_True;
}
}
if ( bChanged )
{
if ( nPaintFrom == 0xFFFF )
nPaintFrom = nWStart;
nPaintTo = nWEnd;
}
}
EditPaM aLastEnd( aSel.Max() );
aSel = WordRight( aSel.Max(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
if ( bChanged && ( aSel.Min().GetNode() == pNode ) &&
( ( aSel.Min().GetIndex()-aLastEnd.GetIndex() > 1 ) ) )
{
// Wenn zwei Worte durch mehr Zeichen als ein Blank getrennt
// sind, kann es passieren, dass beim Aufsplitten eines Wrongs
// der Start den zweiten Wortes vor dem tatsaechlich Wort liegt
pWrongList->ClearWrongs( aLastEnd.GetIndex(), aSel.Min().GetIndex(), pNode );
}
}
// Invalidieren?
if ( ( nPaintFrom != 0xFFFF ) )
{
aStatus.GetStatusWord() |= EE_STAT_WRONGWORDCHANGED;
CallStatusHdl();
if ( aEditViews.Count() )
{
// Bei SimpleRepaint wuerde ein uebermalen ohne VDev reichen,
// aber dann muesste ich ueber alle Views, Intersecten,
// Clippen, ...
// Lohnt wahrscheinlich nicht.
EditPaM aStartPaM( pNode, nPaintFrom );
EditPaM aEndPaM( pNode, nPaintTo );
Rectangle aStartCursor( PaMtoEditCursor( aStartPaM ) );
Rectangle aEndCursor( PaMtoEditCursor( aEndPaM ) );
DBG_ASSERT( aInvalidRec.IsEmpty(), "InvalidRect gesetzt!" );
aInvalidRec.Left() = 0;
aInvalidRec.Right() = GetPaperSize().Width();
aInvalidRec.Top() = aStartCursor.Top();
aInvalidRec.Bottom() = aEndCursor.Bottom();
if ( pActiveView && pActiveView->HasSelection() )
{
// Dann darf nicht ueber VDev ausgegeben werden
UpdateViews( NULL );
}
else if ( bSimpleRepaint )
{
for ( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
{
EditView* pView = aEditViews[nView];
Rectangle aClipRec( aInvalidRec );
aClipRec.Intersection( pView->GetVisArea() );
if ( !aClipRec.IsEmpty() )
{
// in Fensterkoordinaten umwandeln....
aClipRec.SetPos( pView->pImpEditView->GetWindowPos( aClipRec.TopLeft() ) );
// Wenn Selektion, dann VDev...
Paint( pView->pImpEditView, aClipRec, 0, pView->HasSelection() );
}
}
}
else
{
UpdateViews( pActiveView );
}
aInvalidRec = Rectangle();
}
}
// Nach zwei korrigierten Nodes die Kontrolle abgeben...
nInvalids++;
if ( bInteruptable && ( nInvalids >= 2 ) )
{
bRestartTimer = sal_True;
break;
}
}
if ( pThisNodeOnly )
break;
}
if ( bRestartTimer )
aOnlineSpellTimer.Start();
#endif // !SVX_LIGHT
}
EESpellState ImpEditEngine::HasSpellErrors()
{
DBG_ASSERT( xSpeller.is(), "Kein Speller gesetzt!" );
#ifndef SVX_LIGHT
ContentNode* pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count() - 1 );
EditSelection aCurSel( aEditDoc.GetStartPaM() );
String aWord;
Reference< XSpellAlternatives > xSpellAlt;
Sequence< PropertyValue > aEmptySeq;
while ( !xSpellAlt.is() )
{
if ( ( aCurSel.Max().GetNode() == pLastNode ) &&
( aCurSel.Max().GetIndex() >= pLastNode->Len() ) )
{
return EE_SPELL_OK;
}
aCurSel = SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
aWord = GetSelected( aCurSel );
if ( aWord.Len() > 0 )
{
LanguageType eLang = GetLanguage( aCurSel.Max() );
SvxSpellWrapper::CheckSpellLang( xSpeller, eLang );
xSpellAlt = xSpeller->spell( aWord, eLang, aEmptySeq );
}
aCurSel = WordRight( aCurSel.Max(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
}
#endif
return EE_SPELL_ERRORFOUND;
}
EESpellState ImpEditEngine::StartThesaurus( EditView* pEditView )
{
#ifndef SVX_LIGHT
EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
if ( !aCurSel.HasRange() )
aCurSel = SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
String aWord( GetSelected( aCurSel ) );
Reference< XThesaurus > xThes( SvxGetThesaurus() );
if (!xThes.is())
return EE_SPELL_ERRORFOUND;
EditAbstractDialogFactory* pFact = EditAbstractDialogFactory::Create();
AbstractThesaurusDialog* pDlg = pFact->CreateThesaurusDialog( pEditView->GetWindow(), xThes, aWord, GetLanguage( aCurSel.Max() ) );
if ( pDlg->Execute() == RET_OK )
{
// Wort ersetzen...
pEditView->pImpEditView->DrawSelection();
pEditView->pImpEditView->SetEditSelection( aCurSel );
pEditView->pImpEditView->DrawSelection();
pEditView->InsertText( pDlg->GetWord() );
pEditView->ShowCursor( sal_True, sal_False );
}
delete pDlg;
return EE_SPELL_OK;
#else
return EE_SPELL_NOSPELLER;
#endif
}
sal_uInt16 ImpEditEngine::StartSearchAndReplace( EditView* pEditView, const SvxSearchItem& rSearchItem )
{
sal_uInt16 nFound = 0;
#ifndef SVX_LIGHT
EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
// FIND_ALL ohne Mehrfachselektion nicht moeglich.
if ( ( rSearchItem.GetCommand() == SVX_SEARCHCMD_FIND ) ||
( rSearchItem.GetCommand() == SVX_SEARCHCMD_FIND_ALL ) )
{
if ( Search( rSearchItem, pEditView ) )
nFound++;
}
else if ( rSearchItem.GetCommand() == SVX_SEARCHCMD_REPLACE )
{
// Das Wort ist selektiert, wenn der Anwender die Selektion
// nicht zwischendurch manipuliert:
if ( aCurSel.HasRange() )
{
pEditView->InsertText( rSearchItem.GetReplaceString() );
nFound = 1;
}
else
if( Search( rSearchItem, pEditView ) )
nFound = 1;
}
else if ( rSearchItem.GetCommand() == SVX_SEARCHCMD_REPLACE_ALL )
{
// Der Writer ersetzt alle, vorn Anfang bis Ende...
SvxSearchItem aTmpItem( rSearchItem );
aTmpItem.SetBackward( sal_False );
pEditView->pImpEditView->DrawSelection();
aCurSel.Adjust( aEditDoc );
EditPaM aStartPaM = aTmpItem.GetSelection() ? aCurSel.Min() : aEditDoc.GetStartPaM();
EditSelection aFoundSel( aCurSel.Max() );
sal_Bool bFound = ImpSearch( aTmpItem, aCurSel, aStartPaM, aFoundSel );
if ( bFound )
UndoActionStart( EDITUNDO_REPLACEALL );
while ( bFound )
{
nFound++;
aStartPaM = ImpInsertText( aFoundSel, rSearchItem.GetReplaceString() );
bFound = ImpSearch( aTmpItem, aCurSel, aStartPaM, aFoundSel );
}
if ( nFound )
{
EditPaM aNewPaM( aFoundSel.Max() );
if ( aNewPaM.GetIndex() > aNewPaM.GetNode()->Len() )
aNewPaM.GetIndex() = aNewPaM.GetNode()->Len();
pEditView->pImpEditView->SetEditSelection( aNewPaM );
FormatAndUpdate( pEditView );
UndoActionEnd( EDITUNDO_REPLACEALL );
}
else
{
pEditView->pImpEditView->DrawSelection();
pEditView->ShowCursor( sal_True, sal_False );
}
}
#endif // !SVX_LIGHT
return nFound;
}
sal_Bool ImpEditEngine::Search( const SvxSearchItem& rSearchItem, EditView* pEditView )
{
EditSelection aSel( pEditView->pImpEditView->GetEditSelection() );
aSel.Adjust( aEditDoc );
EditPaM aStartPaM( aSel.Max() );
if ( rSearchItem.GetSelection() && !rSearchItem.GetBackward() )
aStartPaM = aSel.Min();
EditSelection aFoundSel;
sal_Bool bFound = ImpSearch( rSearchItem, aSel, aStartPaM, aFoundSel );
if ( bFound && ( aFoundSel == aSel ) ) // Bei Rueckwaetssuche
{
aStartPaM = aSel.Min();
bFound = ImpSearch( rSearchItem, aSel, aStartPaM, aFoundSel );
}
pEditView->pImpEditView->DrawSelection();
if ( bFound )
{
// Erstmal das Min einstellen, damit das ganze Wort in den sichtbaren Bereich kommt.
pEditView->pImpEditView->SetEditSelection( aFoundSel.Min() );
pEditView->ShowCursor( sal_True, sal_False );
pEditView->pImpEditView->SetEditSelection( aFoundSel );
}
else
pEditView->pImpEditView->SetEditSelection( aSel.Max() );
pEditView->pImpEditView->DrawSelection();
pEditView->ShowCursor( sal_True, sal_False );
return bFound;
}
sal_Bool ImpEditEngine::ImpSearch( const SvxSearchItem& rSearchItem,
const EditSelection& rSearchSelection, const EditPaM& rStartPos, EditSelection& rFoundSel )
{
#ifndef SVX_LIGHT
util::SearchOptions aSearchOptions( rSearchItem.GetSearchOptions() );
aSearchOptions.Locale = GetLocale( rStartPos );
sal_Bool bBack = rSearchItem.GetBackward();
sal_Bool bSearchInSelection = rSearchItem.GetSelection();
sal_uInt16 nStartNode = aEditDoc.GetPos( rStartPos.GetNode() );
sal_uInt16 nEndNode;
if ( bSearchInSelection )
{
nEndNode = aEditDoc.GetPos( bBack ? rSearchSelection.Min().GetNode() : rSearchSelection.Max().GetNode() );
}
else
{
nEndNode = bBack ? 0 : aEditDoc.Count()-1;
}
utl::TextSearch aSearcher( aSearchOptions );
// ueber die Absaetze iterieren...
for ( sal_uInt16 nNode = nStartNode;
bBack ? ( nNode >= nEndNode ) : ( nNode <= nEndNode) ;
bBack ? nNode-- : nNode++ )
{
// Bei rueckwaertsuche, wenn nEndNode = 0:
if ( nNode >= 0xFFFF )
return sal_False;
ContentNode* pNode = aEditDoc.GetObject( nNode );
sal_uInt16 nStartPos = 0;
sal_uInt16 nEndPos = pNode->Len();
if ( nNode == nStartNode )
{
if ( bBack )
nEndPos = rStartPos.GetIndex();
else
nStartPos = rStartPos.GetIndex();
}
if ( ( nNode == nEndNode ) && bSearchInSelection )
{
if ( bBack )
nStartPos = rSearchSelection.Min().GetIndex();
else
nEndPos = rSearchSelection.Max().GetIndex();
}
// Suchen...
XubString aParaStr( GetEditDoc().GetParaAsString( pNode ) );
bool bFound = false;
if ( bBack )
{
Swapsal_uIt16s( nStartPos, nEndPos );
bFound = aSearcher.SearchBkwrd( aParaStr, &nStartPos, &nEndPos);
}
else
bFound = aSearcher.SearchFrwrd( aParaStr, &nStartPos, &nEndPos);
if ( bFound )
{
rFoundSel.Min().SetNode( pNode );
rFoundSel.Min().SetIndex( nStartPos );
rFoundSel.Max().SetNode( pNode );
rFoundSel.Max().SetIndex( nEndPos );
return sal_True;
}
}
#endif // !SVX_LIGHT
return sal_False;
}
sal_Bool ImpEditEngine::HasText( const SvxSearchItem& rSearchItem )
{
#ifndef SVX_LIGHT
SvxSearchItem aTmpItem( rSearchItem );
aTmpItem.SetBackward( sal_False );
aTmpItem.SetSelection( sal_False );
EditPaM aStartPaM( aEditDoc.GetStartPaM() );
EditSelection aDummySel( aStartPaM );
EditSelection aFoundSel;
return ImpSearch( aTmpItem, aDummySel, aStartPaM, aFoundSel );
#else
return sal_False;
#endif
}
void ImpEditEngine::SetAutoCompleteText( const String& rStr, sal_Bool bClearTipWindow )
{
#ifndef SVX_LIGHT
aAutoCompleteText = rStr;
if ( bClearTipWindow && pActiveView )
Help::ShowQuickHelp( pActiveView->GetWindow(), Rectangle(), String(), 0 );
#endif // !SVX_LIGHT
}
namespace editeng // #i120045# namespace to avoid XCode template-misoptimization
{
struct TransliterationChgData
{
sal_uInt16 nStart;
xub_StrLen nLen;
EditSelection aSelection;
String aNewText;
uno::Sequence< sal_Int32 > aOffsets;
};
}
using editeng::TransliterationChgData;
EditSelection ImpEditEngine::TransliterateText( const EditSelection& rSelection, sal_Int32 nTransliterationMode )
{
uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
if (!_xBI.is())
return rSelection;
EditSelection aSel( rSelection );
aSel.Adjust( aEditDoc );
if ( !aSel.HasRange() )
aSel = SelectWord( aSel );
EditSelection aNewSel( aSel );
const sal_uInt16 nStartNode = aEditDoc.GetPos( aSel.Min().GetNode() );
const sal_uInt16 nEndNode = aEditDoc.GetPos( aSel.Max().GetNode() );
sal_Bool bChanges = sal_False;
sal_Bool bLenChanged = sal_False;
EditUndoTransliteration* pUndo = NULL;
utl::TransliterationWrapper aTranslitarationWrapper( ::comphelper::getProcessServiceFactory(), nTransliterationMode );
sal_Bool bConsiderLanguage = aTranslitarationWrapper.needLanguageForTheMode();
for ( sal_uInt16 nNode = nStartNode; nNode <= nEndNode; nNode++ )
{
ContentNode* pNode = aEditDoc.GetObject( nNode );
xub_StrLen nStartPos = 0;
xub_StrLen nEndPos = pNode->Len();
if ( nNode == nStartNode )
nStartPos = aSel.Min().GetIndex();
if ( nNode == nEndNode ) // kann auch == nStart sein!
nEndPos = aSel.Max().GetIndex();
sal_uInt16 nCurrentStart = nStartPos;
sal_uInt16 nCurrentEnd = nEndPos;
sal_uInt16 nLanguage = LANGUAGE_SYSTEM;
// 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
// occasionaly 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< TransliterationChgData > aChanges;
TransliterationChgData aChgData;
if (nTransliterationMode == i18n::TransliterationModulesExtra::TITLE_CASE)
{
// for 'capitalize every word' we need to iterate over each word
i18n::Boundary aSttBndry;
i18n::Boundary aEndBndry;
aSttBndry = _xBI->getWordBoundary(
*pNode, nStartPos,
SvxCreateLocale( GetLanguage( EditPaM( pNode, nStartPos + 1 ) ) ),
nWordType, sal_True /*prefer forward direction*/);
aEndBndry = _xBI->getWordBoundary(
*pNode, nEndPos,
SvxCreateLocale( GetLanguage( EditPaM( pNode, nEndPos + 1 ) ) ),
nWordType, sal_False /*prefer backward direction*/);
// prevent backtracking to the previous word if selection is at word boundary
if (aSttBndry.endPos <= nStartPos)
{
aSttBndry = _xBI->nextWord(
*pNode, aSttBndry.endPos,
SvxCreateLocale( GetLanguage( 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(
*pNode, aEndBndry.startPos,
SvxCreateLocale( GetLanguage( EditPaM( pNode, aEndBndry.startPos + 1 ) ) ),
nWordType);
}
i18n::Boundary aCurWordBndry( aSttBndry );
while (aCurWordBndry.startPos <= aEndBndry.startPos)
{
nCurrentStart = (xub_StrLen)aCurWordBndry.startPos;
nCurrentEnd = (xub_StrLen)aCurWordBndry.endPos;
sal_Int32 nLen = nCurrentEnd - nCurrentStart;
DBG_ASSERT( nLen > 0, "invalid word length of 0" );
#if OSL_DEBUG_LEVEL > 1
String aText( pNode->Copy( nCurrentStart, nLen ) );
#endif
Sequence< sal_Int32 > aOffsets;
String aNewText( aTranslitarationWrapper.transliterate( *pNode,
GetLanguage( EditPaM( pNode, nCurrentStart + 1 ) ),
nCurrentStart, nLen, &aOffsets ));
if (!pNode->Equals( aNewText, nCurrentStart, nLen ))
{
aChgData.nStart = nCurrentStart;
aChgData.nLen = nLen;
aChgData.aSelection = EditSelection( EditPaM( pNode, nCurrentStart ), EditPaM( pNode, nCurrentEnd ) );
aChgData.aNewText = aNewText;
aChgData.aOffsets = aOffsets;
aChanges.push_back( aChgData );
}
#if OSL_DEBUG_LEVEL > 1
String aSelTxt ( GetSelected( aChgData.aSelection ) );
(void) aSelTxt;
#endif
aCurWordBndry = _xBI->nextWord( *pNode, nCurrentEnd,
SvxCreateLocale( GetLanguage( EditPaM( pNode, nCurrentEnd + 1 ) ) ),
nWordType);
}
DBG_ASSERT( nCurrentEnd >= aEndBndry.endPos, "failed to reach end of transliteration" );
}
else if (nTransliterationMode == i18n::TransliterationModulesExtra::SENTENCE_CASE)
{
// for 'sentence case' we need to iterate sentence by sentence
sal_Int32 nLastStart = _xBI->beginOfSentence(
*pNode, nEndPos,
SvxCreateLocale( GetLanguage( EditPaM( pNode, nEndPos + 1 ) ) ) );
sal_Int32 nLastEnd = _xBI->endOfSentence(
*pNode, nLastStart,
SvxCreateLocale( GetLanguage( EditPaM( pNode, nLastStart + 1 ) ) ) );
// extend nCurrentStart, nCurrentEnd to the current sentence boundaries
nCurrentStart = _xBI->beginOfSentence(
*pNode, nStartPos,
SvxCreateLocale( GetLanguage( EditPaM( pNode, nStartPos + 1 ) ) ) );
nCurrentEnd = _xBI->endOfSentence(
*pNode, nCurrentStart,
SvxCreateLocale( GetLanguage( EditPaM( pNode, nCurrentStart + 1 ) ) ) );
// 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( *pNode, nCurrentEnd,
SvxCreateLocale( GetLanguage( EditPaM( pNode, nCurrentEnd + 1 ) ) ),
i18n::WordType::DICTIONARY_WORD);
// now get new current sentence boundaries
nCurrentStart = _xBI->beginOfSentence(
*pNode, aBndry.startPos,
SvxCreateLocale( GetLanguage( EditPaM( pNode, aBndry.startPos + 1 ) ) ) );
nCurrentEnd = _xBI->endOfSentence(
*pNode, nCurrentStart,
SvxCreateLocale( GetLanguage( 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( *pNode, nLastStart,
SvxCreateLocale( GetLanguage( EditPaM( pNode, nLastStart + 1 ) ) ),
i18n::WordType::DICTIONARY_WORD);
nLastEnd = _xBI->endOfSentence(
*pNode, aBndry.startPos,
SvxCreateLocale( GetLanguage( EditPaM( pNode, aBndry.startPos + 1 ) ) ) );
if (nCurrentEnd > nLastEnd)
nCurrentEnd = nLastEnd;
}
while (nCurrentStart < nLastEnd)
{
sal_Int32 nLen = nCurrentEnd - nCurrentStart;
DBG_ASSERT( nLen > 0, "invalid word length of 0" );
#if OSL_DEBUG_LEVEL > 1
String aText( pNode->Copy( nCurrentStart, nLen ) );
#endif
Sequence< sal_Int32 > aOffsets;
String aNewText( aTranslitarationWrapper.transliterate( *pNode,
GetLanguage( EditPaM( pNode, nCurrentStart + 1 ) ),
nCurrentStart, nLen, &aOffsets ));
if (!pNode->Equals( aNewText, nCurrentStart, nLen ))
{
aChgData.nStart = nCurrentStart;
aChgData.nLen = nLen;
aChgData.aSelection = EditSelection( EditPaM( pNode, nCurrentStart ), EditPaM( pNode, nCurrentEnd ) );
aChgData.aNewText = aNewText;
aChgData.aOffsets = aOffsets;
aChanges.push_back( aChgData );
}
i18n::Boundary aFirstWordBndry;
aFirstWordBndry = _xBI->nextWord(
*pNode, nCurrentEnd,
SvxCreateLocale( GetLanguage( EditPaM( pNode, nCurrentEnd + 1 ) ) ),
nWordType);
nCurrentStart = aFirstWordBndry.startPos;
nCurrentEnd = _xBI->endOfSentence(
*pNode, nCurrentStart,
SvxCreateLocale( GetLanguage( EditPaM( pNode, nCurrentStart + 1 ) ) ) );
}
DBG_ASSERT( nCurrentEnd >= nLastEnd, "failed to reach end of transliteration" );
}
else
{
do
{
if ( bConsiderLanguage )
{
nLanguage = GetLanguage( EditPaM( pNode, nCurrentStart+1 ), &nCurrentEnd );
if ( nCurrentEnd > nEndPos )
nCurrentEnd = nEndPos;
}
xub_StrLen nLen = nCurrentEnd - nCurrentStart;
Sequence< sal_Int32 > aOffsets;
String aNewText( aTranslitarationWrapper.transliterate( *pNode, nLanguage, nCurrentStart, nLen, &aOffsets ) );
if (!pNode->Equals( aNewText, nCurrentStart, nLen ))
{
aChgData.nStart = nCurrentStart;
aChgData.nLen = nLen;
aChgData.aSelection = EditSelection( EditPaM( pNode, nCurrentStart ), EditPaM( pNode, nCurrentEnd ) );
aChgData.aNewText = aNewText;
aChgData.aOffsets = aOffsets;
aChanges.push_back( aChgData );
}
nCurrentStart = nCurrentEnd;
} while( nCurrentEnd < nEndPos );
}
if (aChanges.size() > 0)
{
#ifndef SVX_LIGHT
// Create a single UndoAction on Demand for all the changes ...
if ( !pUndo && IsUndoEnabled() && !IsInUndo() )
{
// adjust selection to include all changes
for (size_t i = 0; i < aChanges.size(); ++i)
{
const EditSelection &rSel = aChanges[i].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;
ESelection aESel( CreateESel( aSel ) );
pUndo = new EditUndoTransliteration( this, aESel, nTransliterationMode );
const bool bSingleNode = aSel.Min().GetNode()== aSel.Max().GetNode();
const bool bHasAttribs = aSel.Min().GetNode()->GetCharAttribs().HasAttrib( aSel.Min().GetIndex(), aSel.Max().GetIndex() );
if (bSingleNode && !bHasAttribs)
pUndo->SetText( aSel.Min().GetNode()->Copy( aSel.Min().GetIndex(), aSel.Max().GetIndex()-aSel.Min().GetIndex() ) );
else
pUndo->SetText( CreateBinTextObject( aSel, NULL ) );
}
#endif
// 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)
{
const TransliterationChgData &rData = aChanges[ aChanges.size() - 1 - i ];
bChanges = sal_True;
if (rData.nLen != rData.aNewText.Len())
bLenChanged = sal_True;
// Change text without loosing the attributes
sal_uInt16 nDiffs = ReplaceTextOnly( rData.aSelection.Min().GetNode(),
rData.nStart, rData.nLen, rData.aNewText, rData.aOffsets );
// adjust selection in end node to possibly changed size
if (aSel.Max().GetNode() == rData.aSelection.Max().GetNode())
aNewSel.Max().GetIndex() = aNewSel.Max().GetIndex() + nDiffs;
sal_uInt16 nSelNode = aEditDoc.GetPos( rData.aSelection.Min().GetNode() );
ParaPortion* pParaPortion = GetParaPortions()[nSelNode];
pParaPortion->MarkSelectionInvalid( rData.nStart,
std::max< sal_uInt16 >( rData.nStart + rData.nLen,
rData.nStart + rData.aNewText.Len() ) );
}
} // if (aChanges.size() > 0)
}
#ifndef SVX_LIGHT
if ( pUndo )
{
ESelection aESel( CreateESel( aNewSel ) );
pUndo->SetNewSelection( aESel );
InsertUndo( pUndo );
}
#endif
if ( bChanges )
{
TextModified();
SetModifyFlag( sal_True );
if ( bLenChanged )
UpdateSelections();
FormatAndUpdate();
}
return aNewSel;
}
short ImpEditEngine::ReplaceTextOnly(
ContentNode* pNode,
sal_uInt16 nCurrentStart, xub_StrLen nLen,
const String& rNewText,
const uno::Sequence< sal_Int32 >& rOffsets )
{
(void) nLen;
// Change text without loosing the attributes
sal_uInt16 nCharsAfterTransliteration =
sal::static_int_cast< sal_uInt16 >(rOffsets.getLength());
const sal_Int32* pOffsets = rOffsets.getConstArray();
short nDiffs = 0;
for ( sal_uInt16 n = 0; n < nCharsAfterTransliteration; n++ )
{
sal_uInt16 nCurrentPos = nCurrentStart+n;
sal_Int32 nDiff = (nCurrentPos-nDiffs) - pOffsets[n];
if ( !nDiff )
{
DBG_ASSERT( nCurrentPos < pNode->Len(), "TransliterateText - String smaller than expected!" );
pNode->SetChar( nCurrentPos, rNewText.GetChar(n) );
}
else if ( nDiff < 0 )
{
// Replace first char, delete the rest...
DBG_ASSERT( nCurrentPos < pNode->Len(), "TransliterateText - String smaller than expected!" );
pNode->SetChar( nCurrentPos, rNewText.GetChar(n) );
DBG_ASSERT( (nCurrentPos+1) < pNode->Len(), "TransliterateText - String smaller than expected!" );
GetEditDoc().RemoveChars( EditPaM( pNode, nCurrentPos+1 ), sal::static_int_cast< sal_uInt16 >(-nDiff) );
}
else
{
DBG_ASSERT( nDiff == 1, "TransliterateText - Diff other than expected! But should work..." );
GetEditDoc().InsertText( EditPaM( pNode, nCurrentPos ), rNewText.GetChar(n) );
}
nDiffs = sal::static_int_cast< short >(nDiffs + nDiff);
}
return nDiffs;
}
void ImpEditEngine::SetAsianCompressionMode( sal_uInt16 n )
{
if ( n != nAsianCompressionMode )
{
nAsianCompressionMode = n;
if ( ImplHasText() )
{
FormatFullDoc();
UpdateViews();
}
}
}
void ImpEditEngine::SetKernAsianPunctuation( sal_Bool b )
{
if ( b != bKernAsianPunctuation )
{
bKernAsianPunctuation = b;
if ( ImplHasText() )
{
FormatFullDoc();
UpdateViews();
}
}
}
void ImpEditEngine::SetAddExtLeading( sal_Bool bExtLeading )
{
if ( IsAddExtLeading() != bExtLeading )
{
bAddExtLeading = bExtLeading;
if ( ImplHasText() )
{
FormatFullDoc();
UpdateViews();
}
}
};
sal_Bool ImpEditEngine::ImplHasText() const
{
return ( ( GetEditDoc().Count() > 1 ) || GetEditDoc().GetObject(0)->Len() );
}
long ImpEditEngine::LogicToTwips( long n )
{
Size aSz( n, 0 );
MapMode aTwipsMode( MAP_TWIP );
aSz = pRefDev->LogicToLogic( aSz, NULL, &aTwipsMode );
return aSz.Width();
}