/************************************************************************* * * $RCSfile: edtspell.cxx,v $ * * $Revision: 1.8 $ * * last change: $Author: rt $ $Date: 2004-09-17 14:15:34 $ * * The Contents of this file are made available subject to the terms of * either of the following licenses * * - GNU Lesser General Public License Version 2.1 * - Sun Industry Standards Source License Version 1.1 * * Sun Microsystems Inc., October, 2000 * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2000 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * * Sun Industry Standards Source License Version 1.1 * ================================================= * The contents of this file are subject to the Sun Industry Standards * Source License Version 1.1 (the "License"); You may not use this file * except in compliance with the License. You may obtain a copy of the * License at http://www.openoffice.org/license.html. * * Software provided under this License is provided on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. * See the License for the specific provisions governing your rights and * obligations concerning the Software. * * The Initial Developer of the Original Code is: Sun Microsystems, Inc. * * Copyright: 2000 by Sun Microsystems, Inc. * * All Rights Reserved. * * Contributor(s): _______________________________________ * * ************************************************************************/ #include #pragma hdrstop #include #include #include #include #include #include #include #ifndef _UNO_LINGU_HXX #include #endif #ifndef _LINGUISTIC_LNGPROPS_HHX_ #include #endif #ifndef _COM_SUN_STAR_BEANS_XPROPERTYSET_HPP_ #include #endif using namespace rtl; using namespace com::sun::star::uno; using namespace com::sun::star::beans; using namespace com::sun::star::linguistic2; EditSpellWrapper::EditSpellWrapper( Window* pWin, Reference< XSpellChecker1 > &xChecker, sal_Bool bIsStart, sal_Bool bIsAllRight, EditView* pView ) : SvxSpellWrapper( pWin, xChecker, bIsStart, bIsAllRight ) { DBG_ASSERT( pView, "Es muss eine View uebergeben werden!" ); // IgnoreList behalten, ReplaceList loeschen... if (SvxGetChangeAllList().is()) SvxGetChangeAllList()->clear(); pEditView = pView; } void __EXPORT EditSpellWrapper::SpellStart( SvxSpellArea eArea ) { ImpEditEngine* pImpEE = pEditView->GetImpEditEngine(); SpellInfo* pSpellInfo = pImpEE->GetSpellInfo(); sal_Bool bForward = sal_True; if ( eArea == SVX_SPELL_BODY_START ) { // Wird gerufen, wenn // a) Spell-Forwad ist am Ende angekomment und soll von vorne beginnen // IsEndDone() liefert auch sal_True, wenn Rueckwaerts-Spelling am Ende gestartet wird! if ( IsEndDone() ) { pSpellInfo->bSpellToEnd = sal_False; pSpellInfo->aSpellTo = pSpellInfo->aSpellStart; pEditView->GetImpEditView()->SetEditSelection( pImpEE->GetEditDoc().GetStartPaM() ); } else { pSpellInfo->bSpellToEnd = sal_True; pSpellInfo->aSpellTo = pImpEE->CreateEPaM( pImpEE->GetEditDoc().GetStartPaM() ); } } else if ( eArea == SVX_SPELL_BODY_END ) { // Wird gerufen, wenn // a) Spell-Forwad wird gestartet // IsStartDone() liefert auch sal_True, wenn Vorwaerts-Spelling am Anfang gestartet wird! if ( !IsStartDone() ) { pSpellInfo->bSpellToEnd = sal_True; pSpellInfo->aSpellTo = pImpEE->CreateEPaM( pImpEE->GetEditDoc().GetEndPaM() ); } else { pSpellInfo->bSpellToEnd = sal_False; pSpellInfo->aSpellTo = pSpellInfo->aSpellStart; pEditView->GetImpEditView()->SetEditSelection( pImpEE->GetEditDoc().GetEndPaM() ); } } else if ( eArea == SVX_SPELL_BODY ) { ; // Wird ueber SpellNextDocument von App gehandelt // pSpellInfo->bSpellToEnd = sal_True; // pSpellInfo->aSpellTo = pImpEE->CreateEPaM( pImpEE->GetEditDoc().GetEndPaM() ); } else { DBG_ERROR( "SpellStart: Unknown Area!" ); } } sal_Bool EditSpellWrapper::SpellContinue() { SetLast( pEditView->GetImpEditEngine()->ImpSpell( pEditView ) ); return GetLast().is(); } void __EXPORT EditSpellWrapper::SpellEnd() { // Base class will show language errors... SvxSpellWrapper::SpellEnd(); } sal_Bool __EXPORT EditSpellWrapper::HasOtherCnt() { return sal_False; } sal_Bool __EXPORT EditSpellWrapper::SpellMore() { ImpEditEngine* pImpEE = pEditView->GetImpEditEngine(); SpellInfo* pSpellInfo = pImpEE->GetSpellInfo(); sal_Bool bMore = sal_False; if ( pSpellInfo->bMultipleDoc ) { bMore = pImpEE->GetEditEnginePtr()->SpellNextDocument(); if ( bMore ) { // Der Text wurde in diese Engine getreten, bei Rueckwaerts // muss die Selektion hinten sein. Reference< XPropertySet > xProp( SvxGetLinguPropertySet() ); pEditView->GetImpEditView()->SetEditSelection( pImpEE->GetEditDoc().GetStartPaM() ); } } return bMore; } void __EXPORT EditSpellWrapper::ScrollArea() { // Keine weitere Aktion noetig... // Es sei denn, der Bereich soll in die Mitte gescrollt werden, // und nicht irgendwo stehen. } void __EXPORT EditSpellWrapper::ReplaceAll( const String &rNewText, sal_Int16 nLanguage ) { // Wird gerufen, wenn Wort in ReplaceList des SpellCheckers pEditView->InsertText( rNewText ); CheckSpellTo(); } void __EXPORT EditSpellWrapper::ChangeWord( const String& rNewWord, const sal_uInt16 nLang ) { // Wird gerufen, wenn Wort Button Change // bzw. intern von mir bei ChangeAll // Wenn Punkt hinterm Wort, wird dieser nicht mitgegeben. // Falls '"' => PreStripped. String aNewWord( rNewWord ); pEditView->InsertText( aNewWord ); CheckSpellTo(); } void __EXPORT EditSpellWrapper::ChangeThesWord( const String& rNewWord ) { pEditView->InsertText( rNewWord ); CheckSpellTo(); } void __EXPORT EditSpellWrapper::AutoCorrect( const String& rOldWord, const String& rNewWord ) { } void EditSpellWrapper::CheckSpellTo() { ImpEditEngine* pImpEE = pEditView->GetImpEditEngine(); SpellInfo* pSpellInfo = pImpEE->GetSpellInfo(); EditPaM aPaM( pEditView->GetImpEditView()->GetEditSelection().Max() ); EPaM aEPaM = pImpEE->CreateEPaM( aPaM ); if ( aEPaM.nPara == pSpellInfo->aSpellTo.nPara ) { // prueffen, ob SpellToEnd noch gueltiger Index, falls in dem Absatz // ersetzt wurde. if ( pSpellInfo->aSpellTo.nIndex > aPaM.GetNode()->Len() ) pSpellInfo->aSpellTo.nIndex = aPaM.GetNode()->Len(); } } SV_IMPL_VARARR( WrongRanges, WrongRange ); WrongList::WrongList() { nInvalidStart = 0; nInvalidEnd = 0xFFFF; } WrongList::~WrongList() { } void WrongList::TextInserted( sal_uInt16 nPos, sal_uInt16 nNew, sal_Bool bPosIsSep ) { if ( !IsInvalid() ) { nInvalidStart = nPos; nInvalidEnd = nPos+nNew; } else { if ( nInvalidStart > nPos ) nInvalidStart = nPos; if ( nInvalidEnd >= nPos ) nInvalidEnd += nNew; else nInvalidEnd = nPos+nNew; } for ( sal_uInt16 n = 0; n < Count(); n++ ) { WrongRange& rWrong = GetObject( n ); sal_Bool bRefIsValid = sal_True; if ( rWrong.nEnd >= nPos ) { // Alle Wrongs hinter der Einfuegeposition verschieben... if ( rWrong.nStart > nPos ) { rWrong.nStart += nNew; rWrong.nEnd += nNew; } // 1: Startet davor, geht bis nPos... else if ( rWrong.nEnd == nPos ) { // Sollte bei einem Blank unterbunden werden! if ( !bPosIsSep ) rWrong.nEnd += nNew; } // 2: Startet davor, geht hinter Pos... else if ( ( rWrong.nStart < nPos ) && ( rWrong.nEnd > nPos ) ) { rWrong.nEnd += nNew; // Bei einem Trenner das Wrong entfernen und neu pruefen if ( bPosIsSep ) { // Wrong aufteilen... WrongRange aNewWrong( rWrong.nStart, nPos ); rWrong.nStart = nPos+1; Insert( aNewWrong, n ); bRefIsValid = sal_False; // Referenz nach Insert nicht mehr gueltig, der andere wurde davor an dessen Position eingefuegt n++; // Diesen nicht nochmal... } } // 3: Attribut startet auf Pos... else if ( rWrong.nStart == nPos ) { rWrong.nEnd += nNew; if ( bPosIsSep ) rWrong.nStart++; } } DBG_ASSERT( !bRefIsValid || ( rWrong.nStart < rWrong.nEnd ), "TextInserted, WrongRange: Start >= End?!" ); } DBG_ASSERT( !DbgIsBuggy(), "InsertWrong: WrongList kaputt!" ); } void WrongList::TextDeleted( sal_uInt16 nPos, sal_uInt16 nDeleted ) { sal_uInt16 nEndChanges = nPos+nDeleted; if ( !IsInvalid() ) { nInvalidStart = nPos; nInvalidEnd = nPos+1; // Nicht nDeleted, weil da ja wegfaellt. } else { if ( nInvalidStart > nPos ) nInvalidStart = nPos; if ( nInvalidEnd > nPos ) { if ( nInvalidEnd > nEndChanges ) nInvalidEnd -=nDeleted; else nInvalidEnd = nPos+1; } } for ( sal_uInt16 n = 0; n < Count(); n++ ) { WrongRange& rWrong = GetObject( n ); sal_Bool bDelWrong = sal_False; if ( rWrong.nEnd >= nPos ) { // Alles Wrongs hinter der Einfuegeposition verschieben... if ( rWrong.nStart >= nEndChanges ) { rWrong.nStart -= nDeleted; rWrong.nEnd -= nDeleted; } // 1. Innenliegende Wrongs loeschen... else if ( ( rWrong.nStart >= nPos ) && ( rWrong.nEnd <= nEndChanges ) ) { bDelWrong = sal_True; } // 2. Wrong beginnt davor, endet drinnen oder dahinter... else if ( ( rWrong.nStart <= nPos ) && ( rWrong.nEnd > nPos ) ) { if ( rWrong.nEnd <= nEndChanges ) // endet drinnen rWrong.nEnd = nPos; else rWrong.nEnd -= nDeleted; // endet dahinter } // 3. Wrong beginnt drinnen, endet dahinter... else if ( ( rWrong.nStart >= nPos ) && ( rWrong.nEnd > nEndChanges ) ) { rWrong.nStart = nEndChanges; rWrong.nStart -= nDeleted; rWrong.nEnd -= nDeleted; } } DBG_ASSERT( rWrong.nStart < rWrong.nEnd, "TextInserted, WrongRange: Start >= End?!" ); if ( bDelWrong ) { Remove( n, 1 ); n--; } } DBG_ASSERT( !DbgIsBuggy(), "InsertWrong: WrongList kaputt!" ); } sal_Bool WrongList::NextWrong( sal_uInt16& rnStart, sal_uInt16& rnEnd ) const { /* rnStart enthaelt die Startposition, wird ggf. auf Wrong-Start korrigiert rnEnd braucht nicht inizialisiert sein. */ for ( sal_uInt16 n = 0; n < Count(); n++ ) { WrongRange& rWrong = GetObject( n ); if ( rWrong.nEnd > rnStart ) { rnStart = rWrong.nStart; rnEnd = rWrong.nEnd; return sal_True; } } return sal_False; } sal_Bool WrongList::HasWrong( sal_uInt16 nStart, sal_uInt16 nEnd ) const { for ( sal_uInt16 n = 0; n < Count(); n++ ) { WrongRange& rWrong = GetObject( n ); if ( ( rWrong.nStart == nStart ) && ( rWrong.nEnd == nEnd ) ) return sal_True; else if ( rWrong.nStart >= nStart ) break; } return sal_False; } sal_Bool WrongList::HasAnyWrong( sal_uInt16 nStart, sal_uInt16 nEnd ) const { for ( sal_uInt16 n = 0; n < Count(); n++ ) { WrongRange& rWrong = GetObject( n ); if ( ( rWrong.nEnd >= nStart ) && ( rWrong.nStart < nEnd ) ) return sal_True; else if ( rWrong.nStart >= nEnd ) break; } return sal_False; } void WrongList::ClearWrongs( sal_uInt16 nStart, sal_uInt16 nEnd, const ContentNode* pNode ) { for ( sal_uInt16 n = 0; n < Count(); n++ ) { WrongRange& rWrong = GetObject( n ); if ( ( rWrong.nEnd > nStart ) && ( rWrong.nStart < nEnd ) ) { if ( rWrong.nEnd > nEnd ) // // Laeuft raus { rWrong.nStart = nEnd; // Blanks? while ( ( rWrong.nStart < pNode->Len() ) && ( ( pNode->GetChar( rWrong.nStart ) == ' ' ) || ( pNode->IsFeature( rWrong.nStart ) ) ) ) { rWrong.nStart++; } } else { Remove( n, 1 ); n--; } } } DBG_ASSERT( !DbgIsBuggy(), "InsertWrong: WrongList kaputt!" ); } void WrongList::InsertWrong( sal_uInt16 nStart, sal_uInt16 nEnd, sal_Bool bClearRange ) { sal_uInt16 nPos = Count(); for ( sal_uInt16 n = 0; n < Count(); n++ ) { WrongRange& rWrong = GetObject( n ); if ( rWrong.nStart >= nStart ) { nPos = n; if ( bClearRange ) { // Es kann eigentlich nur Passieren, dass der Wrong genau // hier beginnt und weiter rauslauft, aber nicht, dass hier // mehrere im Bereich liegen... // Genau im Bereich darf keiner liegen, sonst darf diese Methode // garnicht erst gerufen werden! DBG_ASSERT( ( ( rWrong.nStart == nStart ) && ( rWrong.nEnd > nEnd ) ) || ( rWrong.nStart > nEnd ), "InsertWrong: RangeMismatch!" ); if ( ( rWrong.nStart == nStart ) && ( rWrong.nEnd > nEnd ) ) rWrong.nStart = nEnd+1; } break; } } Insert( WrongRange( nStart, nEnd ), nPos ); DBG_ASSERT( !DbgIsBuggy(), "InsertWrong: WrongList kaputt!" ); } void WrongList::MarkWrongsInvalid() { if ( Count() ) MarkInvalid( GetObject( 0 ).nStart, GetObject( Count()-1 ).nEnd ); } WrongList* WrongList::Clone() const { WrongList* pNew = new WrongList; for ( sal_uInt16 n = 0; n < Count(); n++ ) { WrongRange& rWrong = GetObject( n ); pNew->Insert( rWrong, pNew->Count() ); } return pNew; } #ifdef DBG_UTIL sal_Bool WrongList::DbgIsBuggy() const { // Pruefen, ob sich Bereiche ueberlappen sal_Bool bError = sal_False; for ( sal_uInt16 nA = 0; !bError && ( nA < Count() ); nA++ ) { WrongRange& rWrong = GetObject( nA ); for ( sal_uInt16 nB = nA+1; !bError && ( nB < Count() ); nB++ ) { WrongRange& rNextWrong = GetObject( nB ); // 1) Start davor, End hinterm anderen Start if ( ( rWrong.nStart <= rNextWrong.nStart ) && ( rWrong.nEnd >= rNextWrong.nStart ) ) bError = sal_True; // 2) Start hinter anderen Start, aber noch vorm anderen End else if ( ( rWrong.nStart >= rNextWrong.nStart) && ( rWrong.nStart <= rNextWrong.nEnd ) ) bError = sal_True; } } return bError; } #endif EdtAutoCorrDoc::EdtAutoCorrDoc( ImpEditEngine* pE, ContentNode* pN, sal_uInt16 nCrsr, xub_Unicode cIns ) { pImpEE = pE; pCurNode = pN; nCursor = nCrsr; bUndoAction = sal_False; bAllowUndoAction = cIns ? sal_True : sal_False; } EdtAutoCorrDoc::~EdtAutoCorrDoc() { if ( bUndoAction ) pImpEE->UndoActionEnd( EDITUNDO_INSERT ); } sal_Bool EdtAutoCorrDoc::Delete( sal_uInt16 nStt, sal_uInt16 nEnd ) { EditSelection aSel( EditPaM( pCurNode, nStt ), EditPaM( pCurNode, nEnd ) ); pImpEE->ImpDeleteSelection( aSel ); DBG_ASSERT( nCursor >= nEnd, "Cursor mitten im Geschehen ?!" ); nCursor -= ( nEnd-nStt ); bAllowUndoAction = sal_False; return sal_True; } sal_Bool EdtAutoCorrDoc::Insert( sal_uInt16 nPos, const String& rTxt ) { EditSelection aSel = EditPaM( pCurNode, nPos ); pImpEE->ImpInsertText( aSel, rTxt ); DBG_ASSERT( nCursor >= nPos, "Cursor mitten im Geschehen ?!" ); nCursor += rTxt.Len(); if ( bAllowUndoAction && ( rTxt.Len() == 1 ) ) ImplStartUndoAction(); bAllowUndoAction = sal_False; return sal_True; } sal_Bool EdtAutoCorrDoc::Replace( sal_uInt16 nPos, const String& rTxt ) { // Eigentlich ein Replace einfuehren => Entspr. UNDO sal_uInt16 nEnd = nPos+rTxt.Len(); if ( nEnd > pCurNode->Len() ) nEnd = pCurNode->Len(); // #i5925# First insert new text behind to be deleted text, for keeping attributes. pImpEE->ImpInsertText( EditSelection( EditPaM( pCurNode, nEnd ) ), rTxt ); pImpEE->ImpDeleteSelection( EditSelection( EditPaM( pCurNode, nPos ), EditPaM( pCurNode, nEnd ) ) ); if ( nPos == nCursor ) nCursor += rTxt.Len(); if ( bAllowUndoAction && ( rTxt.Len() == 1 ) ) ImplStartUndoAction(); bAllowUndoAction = sal_False; return sal_True; } sal_Bool EdtAutoCorrDoc::SetAttr( sal_uInt16 nStt, sal_uInt16 nEnd, sal_uInt16 nSlotId, SfxPoolItem& rItem ) { SfxItemPool* pPool = &pImpEE->GetEditDoc().GetItemPool(); while ( pPool->GetSecondaryPool() && pPool->GetName().EqualsAscii( "EditEngineItemPool" ) ) { pPool = pPool->GetSecondaryPool(); } sal_uInt16 nWhich = pPool->GetWhich( nSlotId ); if ( nWhich ) { rItem.SetWhich( nWhich ); SfxItemSet aSet( pImpEE->GetEmptyItemSet() ); aSet.Put( rItem ); EditSelection aSel( EditPaM( pCurNode, nStt ), EditPaM( pCurNode, nEnd ) ); aSel.Max().SetIndex( nEnd ); // ??? pImpEE->SetAttribs( aSel, aSet, ATTRSPECIAL_EDGE ); bAllowUndoAction = sal_False; } return sal_True; } sal_Bool EdtAutoCorrDoc::SetINetAttr( sal_uInt16 nStt, sal_uInt16 nEnd, const String& rURL ) { // Aus dem Text ein Feldbefehl machen... EditSelection aSel( EditPaM( pCurNode, nStt ), EditPaM( pCurNode, nEnd ) ); String aText = pImpEE->GetSelected( aSel ); aSel = pImpEE->ImpDeleteSelection( aSel ); DBG_ASSERT( nCursor >= nEnd, "Cursor mitten im Geschehen ?!" ); nCursor -= ( nEnd-nStt ); SvxFieldItem aField( SvxURLField( rURL, aText, SVXURLFORMAT_REPR ), EE_FEATURE_FIELD ); pImpEE->InsertField( aSel, aField ); nCursor++; pImpEE->UpdateFields(); bAllowUndoAction = sal_False; return sal_True; } sal_Bool EdtAutoCorrDoc::HasSymbolChars( sal_uInt16 nStt, sal_uInt16 nEnd ) { USHORT nScriptType = pImpEE->GetScriptType( EditPaM( pCurNode, nStt ) ); USHORT nScriptFontInfoItemId = GetScriptItemId( EE_CHAR_FONTINFO, nScriptType ); CharAttribArray& rAttribs = pCurNode->GetCharAttribs().GetAttribs(); sal_uInt16 nAttrs = rAttribs.Count(); for ( sal_uInt16 n = 0; n < nAttrs; n++ ) { EditCharAttrib* pAttr = rAttribs.GetObject( n ); if ( pAttr->GetStart() >= nEnd ) return sal_False; if ( ( pAttr->Which() == nScriptFontInfoItemId ) && ( ((SvxFontItem*)pAttr->GetItem())->GetCharSet() == RTL_TEXTENCODING_SYMBOL ) ) { // Pruefen, ob das Attribt im Bereich liegt... if ( pAttr->GetEnd() >= nStt ) return sal_True; } } return sal_False; } const String* EdtAutoCorrDoc::GetPrevPara( sal_Bool bAtNormalPos ) { // Vorherigen Absatz zurueck geben, damit ermittel werden kann, // ob es sich beim aktuellen Wort um einen Satzanfang handelt. bAllowUndoAction = sal_False; // Jetzt nicht mehr... ContentList& rNodes = pImpEE->GetEditDoc(); sal_uInt16 nPos = rNodes.GetPos( pCurNode ); // Sonderbehandlung: Bullet => Absatzanfang => einfach NULL returnen... const SfxUInt16Item& rBulletState = (const SfxUInt16Item&) pImpEE->GetParaAttrib( nPos, EE_PARA_BULLETSTATE ); sal_Bool bBullet = rBulletState.GetValue() ? sal_True : sal_False; if ( !bBullet && ( pImpEE->aStatus.GetControlWord() & EE_CNTRL_OUTLINER ) ) { // Der Outliner hat im Gliederungsmodus auf Ebene 0 immer ein Bullet. const SfxUInt16Item& rLevel = (const SfxUInt16Item&) pImpEE->GetParaAttrib( nPos, EE_PARA_OUTLLEVEL ); if ( rLevel.GetValue() == 0 ) bBullet = sal_True; } if ( bBullet ) return NULL; for ( sal_uInt16 n = nPos; n; ) { n--; ContentNode* pNode = rNodes[n]; if ( pNode->Len() ) return pNode; } return NULL; } sal_Bool EdtAutoCorrDoc::ChgAutoCorrWord( sal_uInt16& rSttPos, sal_uInt16 nEndPos, SvxAutoCorrect& rACorrect, const String** ppPara ) { // Absatz-Anfang oder ein Blank gefunden, suche nach dem Wort // Kuerzel im Auto bAllowUndoAction = sal_False; // Jetzt nicht mehr... String aShort( pCurNode->Copy( rSttPos, nEndPos - rSttPos ) ); sal_Bool bRet = sal_False; if( !aShort.Len() ) return bRet; LanguageType eLang = pImpEE->GetLanguage( EditPaM( pCurNode, rSttPos+1 ) ); const SvxAutocorrWord* pFnd = rACorrect.SearchWordsInList( *pCurNode, rSttPos, nEndPos, *this, eLang ); if( pFnd && pFnd->IsTextOnly() ) { // dann mal ersetzen EditSelection aSel( EditPaM( pCurNode, rSttPos ), EditPaM( pCurNode, nEndPos ) ); aSel = pImpEE->ImpDeleteSelection( aSel ); DBG_ASSERT( nCursor >= nEndPos, "Cursor mitten im Geschehen ?!" ); nCursor -= ( nEndPos-rSttPos ); pImpEE->ImpInsertText( aSel, pFnd->GetLong() ); nCursor += pFnd->GetLong().Len(); if( ppPara ) *ppPara = pCurNode; bRet = sal_True; } return bRet; } LanguageType EdtAutoCorrDoc::GetLanguage( sal_uInt16 nPos, sal_Bool bPrevPara ) const { return pImpEE->GetLanguage( EditPaM( pCurNode, nPos+1 ) ); } void EdtAutoCorrDoc::ImplStartUndoAction() { sal_uInt16 nPara = pImpEE->GetEditDoc().GetPos( pCurNode ); ESelection aSel( nPara, nCursor, nPara, nCursor ); pImpEE->UndoActionStart( EDITUNDO_INSERT, aSel ); bUndoAction = sal_True; bAllowUndoAction = sal_False; }