summaryrefslogtreecommitdiff
path: root/editeng/source/editeng/impedit2.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'editeng/source/editeng/impedit2.cxx')
-rw-r--r--editeng/source/editeng/impedit2.cxx4632
1 files changed, 4632 insertions, 0 deletions
diff --git a/editeng/source/editeng/impedit2.cxx b/editeng/source/editeng/impedit2.cxx
new file mode 100644
index 000000000000..bbdfa47e6a57
--- /dev/null
+++ b/editeng/source/editeng/impedit2.cxx
@@ -0,0 +1,4632 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org 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 version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 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 <editeng/lspcitem.hxx>
+#include <editeng/flditem.hxx>
+#include <impedit.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <editdbg.hxx>
+#include <eerdll2.hxx>
+#include <editeng/eerdll.hxx>
+#include <edtspell.hxx>
+#include <eeobj.hxx>
+#include <editeng/txtrange.hxx>
+#include <svl/urlbmk.hxx>
+#include <svtools/colorcfg.hxx>
+#include <svl/ctloptions.hxx>
+#include <editeng/acorrcfg.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/adjitem.hxx>
+#include <editeng/scripttypeitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/fontitem.hxx>
+#include <vcl/cmdevt.h>
+
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/text/CharacterCompressionType.hpp>
+#include <com/sun/star/i18n/InputSequenceCheckMode.hpp>
+
+#include <comphelper/processfactory.hxx>
+
+#include <sot/formats.hxx>
+
+#include <unicode/ubidi.h>
+
+using namespace ::com::sun::star;
+
+USHORT lcl_CalcExtraSpace( ParaPortion*, const SvxLineSpacingItem& rLSItem )
+{
+ USHORT nExtra = 0;
+ /* if ( ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP )
+ && ( rLSItem.GetPropLineSpace() != 100 ) )
+ {
+ // ULONG nH = pPortion->GetNode()->GetCharAttribs().GetDefFont().GetSize().Height();
+ ULONG nH = pPortion->GetLines().GetObject( 0 )->GetHeight();
+ long n = nH * rLSItem.GetPropLineSpace();
+ n /= 100;
+ n -= nH; // nur den Abstand
+ if ( n > 0 )
+ nExtra = (USHORT)n;
+ }
+ else */
+ if ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
+ {
+ nExtra = rLSItem.GetInterLineSpace();
+ }
+
+ return nExtra;
+}
+
+ // ----------------------------------------------------------------------
+// class ImpEditEngine
+// ----------------------------------------------------------------------
+
+ImpEditEngine::ImpEditEngine( EditEngine* pEE, SfxItemPool* pItemPool ) :
+ aPaperSize( 0x7FFFFFFF, 0x7FFFFFFF ),
+ aMinAutoPaperSize( 0x0, 0x0 ),
+ aMaxAutoPaperSize( 0x7FFFFFFF, 0x7FFFFFFF ),
+ aEditDoc( pItemPool ),
+ aWordDelimiters( RTL_CONSTASCII_USTRINGPARAM( " .,;:-'`'?!_=\"{}()[]\0xFF" ) ),
+ aGroupChars( RTL_CONSTASCII_USTRINGPARAM( "{}()[]" ) )
+{
+ pEditEngine = pEE;
+ pRefDev = NULL;
+ pVirtDev = NULL;
+ pEmptyItemSet = NULL;
+ pActiveView = NULL;
+ pSpellInfo = NULL;
+ pConvInfo = NULL;
+ pTextObjectPool = NULL;
+ mpIMEInfos = NULL;
+ pStylePool = NULL;
+ pUndoManager = NULL;
+ pUndoMarkSelection = NULL;
+ pTextRanger = NULL;
+ pColorConfig = NULL;
+ pCTLOptions = NULL;
+
+ nCurTextHeight = 0;
+ nBlockNotifications = 0;
+ nBigTextObjectStart = 20;
+
+ nStretchX = 100;
+ nStretchY = 100;
+
+ bInSelection = FALSE;
+ bOwnerOfRefDev = FALSE;
+ bDowning = FALSE;
+ bIsInUndo = FALSE;
+ bIsFormatting = FALSE;
+ bFormatted = FALSE;
+ bUpdate = TRUE;
+ bUseAutoColor = TRUE;
+ bForceAutoColor = FALSE;
+ bAddExtLeading = FALSE;
+ bUndoEnabled = TRUE;
+ bCallParaInsertedOrDeleted = FALSE;
+ bImpConvertFirstCall= FALSE;
+ bFirstWordCapitalization = TRUE;
+
+ eDefLanguage = LANGUAGE_DONTKNOW;
+ maBackgroundColor = COL_AUTO;
+
+ nAsianCompressionMode = text::CharacterCompressionType::NONE;
+ bKernAsianPunctuation = FALSE;
+
+ eDefaultHorizontalTextDirection = EE_HTEXTDIR_DEFAULT;
+
+
+ aStatus.GetControlWord() = EE_CNTRL_USECHARATTRIBS | EE_CNTRL_DOIDLEFORMAT |
+ EE_CNTRL_PASTESPECIAL | EE_CNTRL_UNDOATTRIBS |
+ EE_CNTRL_ALLOWBIGOBJS | EE_CNTRL_RTFSTYLESHEETS |
+ EE_CNTRL_FORMAT100;
+
+ aSelEngine.SetFunctionSet( &aSelFuncSet );
+
+ aStatusTimer.SetTimeout( 200 );
+ aStatusTimer.SetTimeoutHdl( LINK( this, ImpEditEngine, StatusTimerHdl ) );
+
+ aIdleFormatter.SetTimeout( 5 );
+ aIdleFormatter.SetTimeoutHdl( LINK( this, ImpEditEngine, IdleFormatHdl ) );
+
+ aOnlineSpellTimer.SetTimeout( 100 );
+ aOnlineSpellTimer.SetTimeoutHdl( LINK( this, ImpEditEngine, OnlineSpellHdl ) );
+
+ pRefDev = EE_DLL()->GetGlobalData()->GetStdRefDevice();
+
+ // Ab hier wird schon auf Daten zugegriffen!
+ SetRefDevice( pRefDev );
+ InitDoc( FALSE );
+
+ bCallParaInsertedOrDeleted = TRUE;
+
+ aEditDoc.SetModifyHdl( LINK( this, ImpEditEngine, DocModified ) );
+
+ mbLastTryMerge = FALSE;
+}
+
+ImpEditEngine::~ImpEditEngine()
+{
+ aStatusTimer.Stop();
+ aOnlineSpellTimer.Stop();
+ aIdleFormatter.Stop();
+
+ // das Zerstoeren von Vorlagen kann sonst unnoetiges Formatieren ausloesen,
+ // wenn eine Parent-Vorlage zerstoert wird.
+ // Und das nach dem Zerstoeren der Daten!
+ bDowning = TRUE;
+ SetUpdateMode( FALSE );
+
+ delete pVirtDev;
+ delete pEmptyItemSet;
+ delete pUndoManager;
+ delete pTextRanger;
+ delete mpIMEInfos;
+ delete pColorConfig;
+ delete pCTLOptions;
+ if ( bOwnerOfRefDev )
+ delete pRefDev;
+ delete pSpellInfo;
+}
+
+void ImpEditEngine::SetRefDevice( OutputDevice* pRef )
+{
+ if ( bOwnerOfRefDev )
+ delete pRefDev;
+
+ pRefDev = pRef;
+ bOwnerOfRefDev = FALSE;
+
+ if ( !pRef )
+ pRefDev = EE_DLL()->GetGlobalData()->GetStdRefDevice();
+
+ nOnePixelInRef = (USHORT)pRefDev->PixelToLogic( Size( 1, 0 ) ).Width();
+
+ if ( IsFormatted() )
+ {
+ FormatFullDoc();
+ UpdateViews( (EditView*) 0);
+ }
+}
+
+void ImpEditEngine::SetRefMapMode( const MapMode& rMapMode )
+{
+ if ( GetRefDevice()->GetMapMode() == rMapMode )
+ return;
+
+ // Wenn RefDev == GlobalRefDev => eigenes anlegen!
+ if ( !bOwnerOfRefDev && ( pRefDev == EE_DLL()->GetGlobalData()->GetStdRefDevice() ) )
+ {
+ pRefDev = new VirtualDevice;
+ pRefDev->SetMapMode( MAP_TWIP );
+ SetRefDevice( pRefDev );
+ bOwnerOfRefDev = TRUE;
+ }
+ pRefDev->SetMapMode( rMapMode );
+ nOnePixelInRef = (USHORT)pRefDev->PixelToLogic( Size( 1, 0 ) ).Width();
+ if ( IsFormatted() )
+ {
+ FormatFullDoc();
+ UpdateViews( (EditView*) 0);
+ }
+}
+
+void ImpEditEngine::InitDoc( BOOL bKeepParaAttribs )
+{
+ USHORT nParas = aEditDoc.Count();
+ for ( USHORT n = bKeepParaAttribs ? 1 : 0; n < nParas; n++ )
+ {
+ if ( aEditDoc[n]->GetStyleSheet() )
+ EndListening( *aEditDoc[n]->GetStyleSheet(), FALSE );
+ }
+
+ if ( bKeepParaAttribs )
+ aEditDoc.RemoveText();
+ else
+ aEditDoc.Clear();
+
+ GetParaPortions().Reset();
+
+ ParaPortion* pIniPortion = new ParaPortion( aEditDoc[0] );
+ GetParaPortions().Insert( pIniPortion, 0 );
+
+ bFormatted = FALSE;
+
+ if ( IsCallParaInsertedOrDeleted() )
+ {
+ GetEditEnginePtr()->ParagraphDeleted( EE_PARA_ALL );
+ GetEditEnginePtr()->ParagraphInserted( 0 );
+ }
+
+#ifndef SVX_LIGHT
+ if ( GetStatus().DoOnlineSpelling() )
+ aEditDoc.GetObject( 0 )->CreateWrongList();
+#endif // !SVX_LIGHT
+}
+
+EditPaM ImpEditEngine::DeleteSelected( EditSelection aSel )
+{
+ EditPaM aPaM ( ImpDeleteSelection( aSel ) );
+ return aPaM;
+}
+
+XubString ImpEditEngine::GetSelected( const EditSelection& rSel, const LineEnd eEnd ) const
+{
+ XubString aText;
+ if ( !rSel.HasRange() )
+ return aText;
+
+ String aSep = EditDoc::GetSepStr( eEnd );
+
+ EditSelection aSel( rSel );
+ aSel.Adjust( aEditDoc );
+
+ ContentNode* pStartNode = aSel.Min().GetNode();
+ ContentNode* pEndNode = aSel.Max().GetNode();
+ USHORT nStartNode = aEditDoc.GetPos( pStartNode );
+ USHORT nEndNode = aEditDoc.GetPos( pEndNode );
+
+ DBG_ASSERT( nStartNode <= nEndNode, "Selektion nicht sortiert ?" );
+
+ // ueber die Absaetze iterieren...
+ for ( USHORT nNode = nStartNode; nNode <= nEndNode; nNode++ )
+ {
+ DBG_ASSERT( aEditDoc.SaveGetObject( nNode ), "Node nicht gefunden: GetSelected" );
+ 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();
+
+ aText += aEditDoc.GetParaAsString( pNode, nStartPos, nEndPos );
+ if ( nNode < nEndNode )
+ aText += aSep;
+ }
+ return aText;
+}
+
+BOOL ImpEditEngine::MouseButtonDown( const MouseEvent& rMEvt, EditView* pView )
+{
+ GetSelEngine().SetCurView( pView );
+ SetActiveView( pView );
+
+ if ( GetAutoCompleteText().Len() )
+ SetAutoCompleteText( String(), TRUE );
+
+ GetSelEngine().SelMouseButtonDown( rMEvt );
+ // Sonderbehandlungen
+ EditSelection aCurSel( pView->pImpEditView->GetEditSelection() );
+ if ( !rMEvt.IsShift() )
+ {
+ if ( rMEvt.GetClicks() == 2 )
+ {
+ // damit die SelectionEngine weiss, dass Anker.
+ aSelEngine.CursorPosChanging( TRUE, FALSE );
+
+ EditSelection aNewSelection( SelectWord( aCurSel ) );
+ pView->pImpEditView->DrawSelection();
+ pView->pImpEditView->SetEditSelection( aNewSelection );
+ pView->pImpEditView->DrawSelection();
+ pView->ShowCursor( TRUE, TRUE );
+ }
+ else if ( rMEvt.GetClicks() == 3 )
+ {
+ // damit die SelectionEngine weiss, dass Anker.
+ aSelEngine.CursorPosChanging( TRUE, FALSE );
+
+ EditSelection aNewSelection( aCurSel );
+ aNewSelection.Min().SetIndex( 0 );
+ aNewSelection.Max().SetIndex( aCurSel.Min().GetNode()->Len() );
+ pView->pImpEditView->DrawSelection();
+ pView->pImpEditView->SetEditSelection( aNewSelection );
+ pView->pImpEditView->DrawSelection();
+ pView->ShowCursor( TRUE, TRUE );
+ }
+ }
+ return TRUE;
+}
+
+void ImpEditEngine::Command( const CommandEvent& rCEvt, EditView* pView )
+{
+ GetSelEngine().SetCurView( pView );
+ SetActiveView( pView );
+ if ( rCEvt.GetCommand() == COMMAND_VOICE )
+ {
+ const CommandVoiceData* pData = rCEvt.GetVoiceData();
+ if ( pData->GetType() == VOICECOMMANDTYPE_DICTATION )
+ {
+ // Funktionen auf KeyEvents umbiegen, wenn keine entsprechende
+ // Methode an EditView/EditEngine, damit Undo konsistent bleibt.
+
+ SfxPoolItem* pNewAttr = NULL;
+
+ switch ( pData->GetCommand() )
+ {
+ case DICTATIONCOMMAND_UNKNOWN:
+ {
+ pView->InsertText( pData->GetText() );
+ }
+ break;
+ case DICTATIONCOMMAND_NEWPARAGRAPH:
+ {
+ pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_RETURN, 0 ) ) );
+ }
+ break;
+ case DICTATIONCOMMAND_NEWLINE:
+ {
+ pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_RETURN, KEY_SHIFT ) ) );
+ }
+ break;
+ case DICTATIONCOMMAND_TAB:
+ {
+ pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_TAB, 0 ) ) );
+ }
+ break;
+ case DICTATIONCOMMAND_LEFT:
+ {
+ pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_LEFT, KEY_MOD1 ) ) );
+ }
+ break;
+ case DICTATIONCOMMAND_RIGHT:
+ {
+ pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_RIGHT, KEY_MOD1 ) ) );
+ }
+ break;
+ case DICTATIONCOMMAND_UP:
+ {
+ pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_UP, 0 ) ) );
+ }
+ break;
+ case DICTATIONCOMMAND_DOWN:
+ {
+ pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_UP, 0 ) ) );
+ }
+ break;
+ case DICTATIONCOMMAND_UNDO:
+ {
+ pView->Undo();
+ }
+ break;
+ case DICTATIONCOMMAND_DEL:
+ {
+ pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_LEFT, KEY_MOD1|KEY_SHIFT ) ) );
+ pView->DeleteSelected();
+ }
+ break;
+ case DICTATIONCOMMAND_BOLD_ON:
+ {
+ pNewAttr = new SvxWeightItem( WEIGHT_BOLD, EE_CHAR_WEIGHT );
+ }
+ break;
+ case DICTATIONCOMMAND_BOLD_OFF:
+ {
+ pNewAttr = new SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT );
+ }
+ break;
+ case DICTATIONCOMMAND_ITALIC_ON:
+ {
+ pNewAttr = new SvxPostureItem( ITALIC_NORMAL, EE_CHAR_ITALIC );
+ }
+ break;
+ case DICTATIONCOMMAND_ITALIC_OFF:
+ {
+ pNewAttr = new SvxPostureItem( ITALIC_NORMAL, EE_CHAR_ITALIC );
+ }
+ break;
+ case DICTATIONCOMMAND_UNDERLINE_ON:
+ {
+ pNewAttr = new SvxUnderlineItem( UNDERLINE_SINGLE, EE_CHAR_UNDERLINE );
+ }
+ break;
+ case DICTATIONCOMMAND_UNDERLINE_OFF:
+ {
+ pNewAttr = new SvxUnderlineItem( UNDERLINE_NONE, EE_CHAR_UNDERLINE );
+ }
+ break;
+ }
+
+ if ( pNewAttr )
+ {
+ SfxItemSet aSet( GetEmptyItemSet() );
+ aSet.Put( *pNewAttr );
+ pView->SetAttribs( aSet );
+ delete pNewAttr;
+ }
+ }
+ }
+ else if ( rCEvt.GetCommand() == COMMAND_STARTEXTTEXTINPUT )
+ {
+ pView->DeleteSelected();
+ delete mpIMEInfos;
+ EditPaM aPaM = pView->GetImpEditView()->GetEditSelection().Max();
+ String aOldTextAfterStartPos = aPaM.GetNode()->Copy( aPaM.GetIndex() );
+ USHORT nMax = aOldTextAfterStartPos.Search( CH_FEATURE );
+ if ( nMax != STRING_NOTFOUND ) // don't overwrite features!
+ aOldTextAfterStartPos.Erase( nMax );
+ mpIMEInfos = new ImplIMEInfos( aPaM, aOldTextAfterStartPos );
+ mpIMEInfos->bWasCursorOverwrite = !pView->IsInsertMode();
+ UndoActionStart( EDITUNDO_INSERT );
+ }
+ else if ( rCEvt.GetCommand() == COMMAND_ENDEXTTEXTINPUT )
+ {
+ DBG_ASSERT( mpIMEInfos, "COMMAND_ENDEXTTEXTINPUT => Kein Start ?" );
+ if( mpIMEInfos )
+ {
+ // #102812# convert quotes in IME text
+ // works on the last input character, this is escpecially in Korean text often done
+ // quotes that are inside of the string are not replaced!
+ // Borrowed from sw: edtwin.cxx
+ if ( mpIMEInfos->nLen )
+ {
+ EditSelection aSel( mpIMEInfos->aPos );
+ aSel.Min().GetIndex() += mpIMEInfos->nLen-1;
+ aSel.Max().GetIndex() =
+ aSel.Max().GetIndex() + mpIMEInfos->nLen;
+ // #102812# convert quotes in IME text
+ // works on the last input character, this is escpecially in Korean text often done
+ // quotes that are inside of the string are not replaced!
+ const sal_Unicode nCharCode = aSel.Min().GetNode()->GetChar( aSel.Min().GetIndex() );
+ if ( ( GetStatus().DoAutoCorrect() ) && ( ( nCharCode == '\"' ) || ( nCharCode == '\'' ) ) )
+ {
+ aSel = DeleteSelected( aSel );
+ aSel = AutoCorrect( aSel, nCharCode, mpIMEInfos->bWasCursorOverwrite );
+ pView->pImpEditView->SetEditSelection( aSel );
+ }
+ }
+
+ ParaPortion* pPortion = FindParaPortion( mpIMEInfos->aPos.GetNode() );
+ pPortion->MarkSelectionInvalid( mpIMEInfos->aPos.GetIndex(), 0 );
+
+ BOOL bWasCursorOverwrite = mpIMEInfos->bWasCursorOverwrite;
+
+ delete mpIMEInfos;
+ mpIMEInfos = NULL;
+
+ FormatAndUpdate( pView );
+
+ pView->SetInsertMode( !bWasCursorOverwrite );
+ }
+ UndoActionEnd( EDITUNDO_INSERT );
+ }
+ else if ( rCEvt.GetCommand() == COMMAND_EXTTEXTINPUT )
+ {
+ DBG_ASSERT( mpIMEInfos, "COMMAND_EXTTEXTINPUT => Kein Start ?" );
+ if( mpIMEInfos )
+ {
+ const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();
+
+ if ( !pData->IsOnlyCursorChanged() )
+ {
+ EditSelection aSel( mpIMEInfos->aPos );
+ aSel.Max().GetIndex() =
+ aSel.Max().GetIndex() + mpIMEInfos->nLen;
+ aSel = DeleteSelected( aSel );
+ aSel = ImpInsertText( aSel, pData->GetText() );
+
+ if ( mpIMEInfos->bWasCursorOverwrite )
+ {
+ USHORT nOldIMETextLen = mpIMEInfos->nLen;
+ USHORT nNewIMETextLen = pData->GetText().Len();
+
+ if ( ( nOldIMETextLen > nNewIMETextLen ) &&
+ ( nNewIMETextLen < mpIMEInfos->aOldTextAfterStartPos.Len() ) )
+ {
+ // restore old characters
+ USHORT nRestore = nOldIMETextLen - nNewIMETextLen;
+ EditPaM aPaM( mpIMEInfos->aPos );
+ aPaM.GetIndex() = aPaM.GetIndex() + nNewIMETextLen;
+ ImpInsertText( aPaM, mpIMEInfos->aOldTextAfterStartPos.Copy( nNewIMETextLen, nRestore ) );
+ }
+ else if ( ( nOldIMETextLen < nNewIMETextLen ) &&
+ ( nOldIMETextLen < mpIMEInfos->aOldTextAfterStartPos.Len() ) )
+ {
+ // overwrite
+ USHORT nOverwrite = nNewIMETextLen - nOldIMETextLen;
+ if ( ( nOldIMETextLen + nOverwrite ) > mpIMEInfos->aOldTextAfterStartPos.Len() )
+ nOverwrite = mpIMEInfos->aOldTextAfterStartPos.Len() - nOldIMETextLen;
+ DBG_ASSERT( nOverwrite && (nOverwrite < 0xFF00), "IME Overwrite?!" );
+ EditPaM aPaM( mpIMEInfos->aPos );
+ aPaM.GetIndex() = aPaM.GetIndex() + nNewIMETextLen;
+ EditSelection _aSel( aPaM );
+ _aSel.Max().GetIndex() =
+ _aSel.Max().GetIndex() + nOverwrite;
+ DeleteSelected( _aSel );
+ }
+ }
+ if ( pData->GetTextAttr() )
+ {
+ mpIMEInfos->CopyAttribs( pData->GetTextAttr(), pData->GetText().Len() );
+ mpIMEInfos->bCursor = pData->IsCursorVisible();
+ }
+ else
+ {
+ mpIMEInfos->DestroyAttribs();
+ mpIMEInfos->nLen = pData->GetText().Len();
+ }
+
+ ParaPortion* pPortion = FindParaPortion( mpIMEInfos->aPos.GetNode() );
+ pPortion->MarkSelectionInvalid( mpIMEInfos->aPos.GetIndex(), 0 );
+ FormatAndUpdate( pView );
+ }
+
+ EditSelection aNewSel = EditPaM( mpIMEInfos->aPos.GetNode(), mpIMEInfos->aPos.GetIndex()+pData->GetCursorPos() );
+ pView->SetSelection( CreateESel( aNewSel ) );
+ pView->SetInsertMode( !pData->IsCursorOverwrite() );
+
+ if ( pData->IsCursorVisible() )
+ pView->ShowCursor();
+ else
+ pView->HideCursor();
+ }
+ }
+ else if ( rCEvt.GetCommand() == COMMAND_INPUTCONTEXTCHANGE )
+ {
+ }
+ else if ( rCEvt.GetCommand() == COMMAND_CURSORPOS )
+ {
+ if ( mpIMEInfos && mpIMEInfos->nLen )
+ {
+ EditPaM aPaM( pView->pImpEditView->GetEditSelection().Max() );
+ Rectangle aR1 = PaMtoEditCursor( aPaM, 0 );
+
+ USHORT nInputEnd = mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen;
+
+ if ( !IsFormatted() )
+ FormatDoc();
+
+ ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( GetEditDoc().GetPos( aPaM.GetNode() ) );
+ USHORT nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), sal_True );
+ EditLine* pLine = pParaPortion->GetLines().GetObject( nLine );
+ if ( pLine && ( nInputEnd > pLine->GetEnd() ) )
+ nInputEnd = pLine->GetEnd();
+ Rectangle aR2 = PaMtoEditCursor( EditPaM( aPaM.GetNode(), nInputEnd ), GETCRSR_ENDOFLINE );
+ Rectangle aRect = pView->GetImpEditView()->GetWindowPos( aR1 );
+ pView->GetWindow()->SetCursorRect( &aRect, aR2.Left()-aR1.Right() );
+ }
+ else
+ {
+ pView->GetWindow()->SetCursorRect();
+ }
+ }
+ else if ( rCEvt.GetCommand() == COMMAND_SELECTIONCHANGE )
+ {
+ const CommandSelectionChangeData *pData = rCEvt.GetSelectionChangeData();
+
+ ESelection aSelection = pView->GetSelection();
+ aSelection.Adjust();
+
+ if( pView->HasSelection() )
+ {
+ aSelection.nEndPos = aSelection.nStartPos;
+ aSelection.nStartPos += pData->GetStart();
+ aSelection.nEndPos += pData->GetEnd();
+ }
+ else
+ {
+ aSelection.nStartPos = pData->GetStart();
+ aSelection.nEndPos = pData->GetEnd();
+ }
+ pView->SetSelection( aSelection );
+ }
+ else if ( rCEvt.GetCommand() == COMMAND_PREPARERECONVERSION )
+ {
+ if ( pView->HasSelection() )
+ {
+ ESelection aSelection = pView->GetSelection();
+ aSelection.Adjust();
+
+ if ( aSelection.nStartPara != aSelection.nEndPara )
+ {
+ xub_StrLen aParaLen = pEditEngine->GetTextLen( aSelection.nStartPara );
+ aSelection.nEndPara = aSelection.nStartPara;
+ aSelection.nEndPos = aParaLen;
+ pView->SetSelection( aSelection );
+ }
+ }
+ }
+
+ GetSelEngine().Command( rCEvt );
+}
+
+BOOL ImpEditEngine::MouseButtonUp( const MouseEvent& rMEvt, EditView* pView )
+{
+ GetSelEngine().SetCurView( pView );
+ GetSelEngine().SelMouseButtonUp( rMEvt );
+ bInSelection = FALSE;
+ // Sonderbehandlungen
+ EditSelection aCurSel( pView->pImpEditView->GetEditSelection() );
+ if ( !aCurSel.HasRange() )
+ {
+ if ( ( rMEvt.GetClicks() == 1 ) && rMEvt.IsLeft() && !rMEvt.IsMod2() )
+ {
+ const SvxFieldItem* pFld = pView->GetFieldUnderMousePointer();
+ if ( pFld )
+ {
+ EditPaM aPaM( aCurSel.Max() );
+ USHORT nPara = GetEditDoc().GetPos( aPaM.GetNode() );
+ GetEditEnginePtr()->FieldClicked( *pFld, nPara, aPaM.GetIndex() );
+ }
+ }
+ }
+ return TRUE;
+}
+
+BOOL ImpEditEngine::MouseMove( const MouseEvent& rMEvt, EditView* pView )
+{
+ // MouseMove wird sofort nach ShowQuickHelp() gerufen!
+// if ( GetAutoCompleteText().Len() )
+// SetAutoCompleteText( String(), TRUE );
+ GetSelEngine().SetCurView( pView );
+ GetSelEngine().SelMouseMove( rMEvt );
+ return TRUE;
+}
+
+EditPaM ImpEditEngine::InsertText( EditSelection aSel, const XubString& rStr )
+{
+ EditPaM aPaM = ImpInsertText( aSel, rStr );
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::Clear()
+{
+ InitDoc( FALSE );
+
+ EditPaM aPaM = aEditDoc.GetStartPaM();
+ EditSelection aSel( aPaM );
+
+ nCurTextHeight = 0;
+
+ ResetUndoManager();
+
+ for ( USHORT nView = aEditViews.Count(); nView; )
+ {
+ EditView* pView = aEditViews[--nView];
+ DBG_CHKOBJ( pView, EditView, 0 );
+ pView->pImpEditView->SetEditSelection( aSel );
+ }
+
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::RemoveText()
+{
+ InitDoc( TRUE );
+
+ EditPaM aStartPaM = aEditDoc.GetStartPaM();
+ EditSelection aEmptySel( aStartPaM, aStartPaM );
+ for ( USHORT nView = 0; nView < aEditViews.Count(); nView++ )
+ {
+ EditView* pView = aEditViews.GetObject(nView);
+ DBG_CHKOBJ( pView, EditView, 0 );
+ pView->pImpEditView->SetEditSelection( aEmptySel );
+ }
+ ResetUndoManager();
+ return aEditDoc.GetStartPaM();
+}
+
+
+void ImpEditEngine::SetText( const XubString& rText )
+{
+ // RemoveText loescht die Undo-Liste!
+ EditPaM aStartPaM = RemoveText();
+ BOOL bUndoCurrentlyEnabled = IsUndoEnabled();
+ // Der von Hand reingesteckte Text kann nicht vom Anwender rueckgaengig gemacht werden.
+ EnableUndo( FALSE );
+
+ EditSelection aEmptySel( aStartPaM, aStartPaM );
+ EditPaM aPaM = aStartPaM;
+ if ( rText.Len() )
+ aPaM = ImpInsertText( aEmptySel, rText );
+
+ for ( USHORT nView = 0; nView < aEditViews.Count(); nView++ )
+ {
+ EditView* pView = aEditViews[nView];
+ DBG_CHKOBJ( pView, EditView, 0 );
+ pView->pImpEditView->SetEditSelection( EditSelection( aPaM, aPaM ) );
+ // Wenn kein Text, dann auch Kein Format&Update
+ // => Der Text bleibt stehen.
+ if ( !rText.Len() && GetUpdateMode() )
+ {
+ Rectangle aTmpRec( pView->GetOutputArea().TopLeft(),
+ Size( aPaperSize.Width(), nCurTextHeight ) );
+ aTmpRec.Intersection( pView->GetOutputArea() );
+ pView->GetWindow()->Invalidate( aTmpRec );
+ }
+ }
+ if( !rText.Len() ) // sonst muss spaeter noch invalidiert werden, !bFormatted reicht.
+ nCurTextHeight = 0;
+ EnableUndo( bUndoCurrentlyEnabled );
+#ifndef SVX_LIGHT
+ DBG_ASSERT( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "Undo nach SetText?" );
+#endif
+}
+
+
+const SfxItemSet& ImpEditEngine::GetEmptyItemSet()
+{
+ if ( !pEmptyItemSet )
+ {
+ pEmptyItemSet = new SfxItemSet( aEditDoc.GetItemPool(), EE_ITEMS_START, EE_ITEMS_END );
+ for ( USHORT nWhich = EE_ITEMS_START; nWhich <= EE_CHAR_END; nWhich++)
+ {
+ pEmptyItemSet->ClearItem( nWhich );
+ }
+ }
+ return *pEmptyItemSet;
+}
+
+// ----------------------------------------------------------------------
+// MISC
+// ----------------------------------------------------------------------
+void ImpEditEngine::CursorMoved( ContentNode* pPrevNode )
+{
+ // Leere Attribute loeschen, aber nur, wenn Absatz nicht leer!
+ if ( pPrevNode->GetCharAttribs().HasEmptyAttribs() && pPrevNode->Len() )
+ pPrevNode->GetCharAttribs().DeleteEmptyAttribs( aEditDoc.GetItemPool() );
+}
+
+void ImpEditEngine::TextModified()
+{
+ bFormatted = FALSE;
+
+ if ( GetNotifyHdl().IsSet() )
+ {
+ EENotify aNotify( EE_NOTIFY_TEXTMODIFIED );
+ aNotify.pEditEngine = GetEditEnginePtr();
+ CallNotify( aNotify );
+ }
+}
+
+
+void ImpEditEngine::ParaAttribsChanged( ContentNode* pNode )
+{
+ DBG_ASSERT( pNode, "ParaAttribsChanged: Welcher?" );
+
+ aEditDoc.SetModified( TRUE );
+ bFormatted = FALSE;
+
+ ParaPortion* pPortion = FindParaPortion( pNode );
+ DBG_ASSERT( pPortion, "ParaAttribsChanged: Portion?" );
+ pPortion->MarkSelectionInvalid( 0, pNode->Len() );
+
+ USHORT nPara = aEditDoc.GetPos( pNode );
+ pEditEngine->ParaAttribsChanged( nPara );
+
+ ParaPortion* pNextPortion = GetParaPortions().SaveGetObject( nPara+1 );
+ // => wird sowieso noch formatiert, wenn Invalid.
+ if ( pNextPortion && !pNextPortion->IsInvalid() )
+ CalcHeight( pNextPortion );
+}
+
+// ----------------------------------------------------------------------
+// Cursorbewegungen
+// ----------------------------------------------------------------------
+
+EditSelection ImpEditEngine::MoveCursor( const KeyEvent& rKeyEvent, EditView* pEditView )
+{
+ // Eigentlich nur bei Up/Down noetig, aber was solls.
+ CheckIdleFormatter();
+
+ EditPaM aPaM( pEditView->pImpEditView->GetEditSelection().Max() );
+
+ EditPaM aOldPaM( aPaM );
+
+ TextDirectionality eTextDirection = TextDirectionality_LeftToRight_TopToBottom;
+ if ( IsVertical() )
+ eTextDirection = TextDirectionality_TopToBottom_RightToLeft;
+ else if ( IsRightToLeft( GetEditDoc().GetPos( aPaM.GetNode() ) ) )
+ eTextDirection = TextDirectionality_RightToLeft_TopToBottom;
+
+ KeyEvent aTranslatedKeyEvent = rKeyEvent.LogicalTextDirectionality( eTextDirection );
+
+ BOOL bCtrl = aTranslatedKeyEvent.GetKeyCode().IsMod1() ? TRUE : FALSE;
+ USHORT nCode = aTranslatedKeyEvent.GetKeyCode().GetCode();
+
+ if ( DoVisualCursorTraveling( aPaM.GetNode() ) )
+ {
+ // Only for simple cursor movement...
+ if ( !bCtrl && ( ( nCode == KEY_LEFT ) || ( nCode == KEY_RIGHT ) ) )
+ {
+ aPaM = CursorVisualLeftRight( pEditView, aPaM, rKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL, rKeyEvent.GetKeyCode().GetCode() == KEY_LEFT );
+ nCode = 0; // skip switch statement
+ }
+ /*
+ else if ( !bCtrl && ( ( nCode == KEY_HOME ) || ( nCode == KEY_END ) ) )
+ {
+ aPaM = CursorVisualStartEnd( pEditView, aPaM, nCode == KEY_HOME );
+ nCode = 0; // skip switch statement
+ }
+ */
+ }
+
+ bool bKeyModifySelection = aTranslatedKeyEvent.GetKeyCode().IsShift();
+ switch ( nCode )
+ {
+ case KEY_UP: aPaM = CursorUp( aPaM, pEditView );
+ break;
+ case KEY_DOWN: aPaM = CursorDown( aPaM, pEditView );
+ break;
+ case KEY_LEFT: aPaM = bCtrl ? WordLeft( aPaM ) : CursorLeft( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL );
+ break;
+ case KEY_RIGHT: aPaM = bCtrl ? WordRight( aPaM ) : CursorRight( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL );
+ break;
+ case KEY_HOME: aPaM = bCtrl ? CursorStartOfDoc() : CursorStartOfLine( aPaM );
+ break;
+ case KEY_END: aPaM = bCtrl ? CursorEndOfDoc() : CursorEndOfLine( aPaM );
+ break;
+ case KEY_PAGEUP: aPaM = bCtrl ? CursorStartOfDoc() : PageUp( aPaM, pEditView );
+ break;
+ case KEY_PAGEDOWN: aPaM = bCtrl ? CursorEndOfDoc() : PageDown( aPaM, pEditView );
+ break;
+ case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_LINE:
+ aPaM = CursorStartOfLine( aPaM );
+ bKeyModifySelection = false;
+ break;
+ case com::sun::star::awt::Key::MOVE_TO_END_OF_LINE:
+ aPaM = CursorEndOfLine( aPaM );
+ bKeyModifySelection = false;
+ break;
+ case com::sun::star::awt::Key::MOVE_WORD_BACKWARD:
+ aPaM = WordLeft( aPaM );
+ bKeyModifySelection = false;
+ break;
+ case com::sun::star::awt::Key::MOVE_WORD_FORWARD:
+ aPaM = WordRight( aPaM );
+ bKeyModifySelection = false;
+ break;
+ case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
+ aPaM = CursorStartOfParagraph( aPaM );
+ if( aPaM == aOldPaM )
+ {
+ aPaM = CursorLeft( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
+ aPaM = CursorStartOfParagraph( aPaM );
+ }
+ bKeyModifySelection = false;
+ break;
+ case com::sun::star::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
+ aPaM = CursorEndOfParagraph( aPaM );
+ if( aPaM == aOldPaM )
+ {
+ aPaM = CursorRight( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
+ aPaM = CursorEndOfParagraph( aPaM );
+ }
+ bKeyModifySelection = false;
+ break;
+ case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
+ aPaM = CursorStartOfDoc();
+ bKeyModifySelection = false;
+ break;
+ case com::sun::star::awt::Key::MOVE_TO_END_OF_DOCUMENT:
+ aPaM = CursorEndOfDoc();
+ bKeyModifySelection = false;
+ break;
+ case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_LINE:
+ aPaM = CursorStartOfLine( aPaM );
+ bKeyModifySelection = true;
+ break;
+ case com::sun::star::awt::Key::SELECT_TO_END_OF_LINE:
+ aPaM = CursorEndOfLine( aPaM );
+ bKeyModifySelection = true;
+ break;
+ case com::sun::star::awt::Key::SELECT_BACKWARD:
+ aPaM = CursorLeft( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
+ bKeyModifySelection = true;
+ break;
+ case com::sun::star::awt::Key::SELECT_FORWARD:
+ aPaM = CursorRight( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
+ bKeyModifySelection = true;
+ break;
+ case com::sun::star::awt::Key::SELECT_WORD_BACKWARD:
+ aPaM = WordLeft( aPaM );
+ bKeyModifySelection = true;
+ break;
+ case com::sun::star::awt::Key::SELECT_WORD_FORWARD:
+ aPaM = WordRight( aPaM );
+ bKeyModifySelection = true;
+ break;
+ case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
+ aPaM = CursorStartOfParagraph( aPaM );
+ if( aPaM == aOldPaM )
+ {
+ aPaM = CursorLeft( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
+ aPaM = CursorStartOfParagraph( aPaM );
+ }
+ bKeyModifySelection = true;
+ break;
+ case com::sun::star::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
+ aPaM = CursorEndOfParagraph( aPaM );
+ if( aPaM == aOldPaM )
+ {
+ aPaM = CursorRight( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
+ aPaM = CursorEndOfParagraph( aPaM );
+ }
+ bKeyModifySelection = true;
+ break;
+ case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
+ aPaM = CursorStartOfDoc();
+ bKeyModifySelection = true;
+ break;
+ case com::sun::star::awt::Key::SELECT_TO_END_OF_DOCUMENT:
+ aPaM = CursorEndOfDoc();
+ bKeyModifySelection = true;
+ break;
+ }
+
+ if ( aOldPaM != aPaM )
+ {
+ CursorMoved( aOldPaM.GetNode() );
+ if ( aStatus.NotifyCursorMovements() && ( aOldPaM.GetNode() != aPaM.GetNode() ) )
+ {
+ aStatus.GetStatusWord() = aStatus.GetStatusWord() | EE_STAT_CRSRLEFTPARA;
+ aStatus.GetPrevParagraph() = aEditDoc.GetPos( aOldPaM.GetNode() );
+ }
+ }
+ else
+ aStatus.GetStatusWord() = aStatus.GetStatusWord() | EE_STAT_CRSRMOVEFAIL;
+
+ // Bewirkt evtl. ein CreateAnchor oder Deselection all
+ aSelEngine.SetCurView( pEditView );
+ aSelEngine.CursorPosChanging( bKeyModifySelection, aTranslatedKeyEvent.GetKeyCode().IsMod1() );
+ EditPaM aOldEnd( pEditView->pImpEditView->GetEditSelection().Max() );
+ pEditView->pImpEditView->GetEditSelection().Max() = aPaM;
+ if ( bKeyModifySelection )
+ {
+ // Dann wird die Selektion erweitert...
+ EditSelection aTmpNewSel( aOldEnd, aPaM );
+ pEditView->pImpEditView->DrawSelection( aTmpNewSel );
+ }
+ else
+ pEditView->pImpEditView->GetEditSelection().Min() = aPaM;
+
+ return pEditView->pImpEditView->GetEditSelection();
+}
+
+EditPaM ImpEditEngine::CursorVisualStartEnd( EditView* pEditView, const EditPaM& rPaM, BOOL bStart )
+{
+ EditPaM aPaM( rPaM );
+
+ USHORT nPara = GetEditDoc().GetPos( aPaM.GetNode() );
+ ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
+
+ USHORT nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), sal_False );
+ EditLine* pLine = pParaPortion->GetLines().GetObject( nLine );
+ BOOL bEmptyLine = pLine->GetStart() == pLine->GetEnd();
+
+ pEditView->pImpEditView->nExtraCursorFlags = 0;
+
+ if ( !bEmptyLine )
+ {
+ String aLine( *aPaM.GetNode(), pLine->GetStart(), pLine->GetEnd() - pLine->GetStart() );
+// USHORT nPosInLine = aPaM.GetIndex() - pLine->GetStart();
+
+ const sal_Unicode* pLineString = aLine.GetBuffer();
+
+ UErrorCode nError = U_ZERO_ERROR;
+ UBiDi* pBidi = ubidi_openSized( aLine.Len(), 0, &nError );
+
+ const UBiDiLevel nBidiLevel = IsRightToLeft( nPara ) ? 1 /*RTL*/ : 0 /*LTR*/;
+ ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(pLineString), aLine.Len(), nBidiLevel, NULL, &nError ); // UChar != sal_Unicode in MinGW
+
+ USHORT nVisPos = bStart ? 0 : aLine.Len()-1;
+ USHORT nLogPos = (USHORT)ubidi_getLogicalIndex( pBidi, nVisPos, &nError );
+
+ ubidi_close( pBidi );
+
+ aPaM.GetIndex() = nLogPos + pLine->GetStart();
+
+ USHORT nTmp;
+ USHORT nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nTmp, TRUE );
+ TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
+ USHORT nRTLLevel = pTextPortion->GetRightToLeft();
+// BOOL bParaRTL = IsRightToLeft( nPara );
+ BOOL bPortionRTL = nRTLLevel%2 ? TRUE : FALSE;
+
+ if ( bStart )
+ {
+ pEditView->pImpEditView->SetCursorBidiLevel( bPortionRTL ? 0 : 1 );
+ // Maybe we must be *behind* the character
+ if ( bPortionRTL && pEditView->IsInsertMode() )
+ aPaM.GetIndex()++;
+ }
+ else
+ {
+ pEditView->pImpEditView->SetCursorBidiLevel( bPortionRTL ? 1 : 0 );
+ if ( !bPortionRTL && pEditView->IsInsertMode() )
+ aPaM.GetIndex()++;
+ }
+ }
+
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::CursorVisualLeftRight( EditView* pEditView, const EditPaM& rPaM, USHORT nCharacterIteratorMode, BOOL bVisualToLeft )
+{
+ EditPaM aPaM( rPaM );
+
+ USHORT nPara = GetEditDoc().GetPos( aPaM.GetNode() );
+ ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
+
+ USHORT nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), sal_False );
+ EditLine* pLine = pParaPortion->GetLines().GetObject( nLine );
+ BOOL bEmptyLine = pLine->GetStart() == pLine->GetEnd();
+
+// USHORT nCurrentCursorFlags = pEditView->pImpEditView->nExtraCursorFlags;
+ pEditView->pImpEditView->nExtraCursorFlags = 0;
+
+ BOOL bParaRTL = IsRightToLeft( nPara );
+
+ BOOL bDone = FALSE;
+
+ if ( bEmptyLine )
+ {
+ if ( bVisualToLeft )
+ {
+ aPaM = CursorUp( aPaM, pEditView );
+ if ( aPaM != rPaM )
+ aPaM = CursorVisualStartEnd( pEditView, aPaM, FALSE );
+ }
+ else
+ {
+ aPaM = CursorDown( aPaM, pEditView );
+ if ( aPaM != rPaM )
+ aPaM = CursorVisualStartEnd( pEditView, aPaM, TRUE );
+ }
+
+ bDone = TRUE;
+ }
+
+ BOOL bLogicalBackward = bParaRTL ? !bVisualToLeft : bVisualToLeft;
+
+ if ( !bDone && pEditView->IsInsertMode() )
+ {
+ // Check if we are within a portion and don't have overwrite mode, then it's easy...
+ USHORT nPortionStart;
+ USHORT nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, FALSE );
+ TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
+
+ BOOL bPortionBoundary = ( aPaM.GetIndex() == nPortionStart ) || ( aPaM.GetIndex() == (nPortionStart+pTextPortion->GetLen()) );
+ USHORT nRTLLevel = pTextPortion->GetRightToLeft();
+
+ // Portion boundary doesn't matter if both have same RTL level
+ USHORT nRTLLevelNextPortion = 0xFFFF;
+ if ( bPortionBoundary && aPaM.GetIndex() && ( aPaM.GetIndex() < aPaM.GetNode()->Len() ) )
+ {
+ USHORT nTmp;
+ USHORT nNextTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex()+1, nTmp, bLogicalBackward ? FALSE : TRUE );
+ TextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nNextTextPortion );
+ nRTLLevelNextPortion = pNextTextPortion->GetRightToLeft();
+ }
+
+ if ( !bPortionBoundary || ( nRTLLevel == nRTLLevelNextPortion ) )
+ {
+ if ( ( bVisualToLeft && !(nRTLLevel%2) ) || ( !bVisualToLeft && (nRTLLevel%2) ) )
+ {
+ aPaM = CursorLeft( aPaM, nCharacterIteratorMode );
+ pEditView->pImpEditView->SetCursorBidiLevel( 1 );
+ }
+ else
+ {
+ aPaM = CursorRight( aPaM, nCharacterIteratorMode );
+ pEditView->pImpEditView->SetCursorBidiLevel( 0 );
+ }
+ bDone = TRUE;
+ }
+ }
+
+ if ( !bDone )
+ {
+ BOOL bGotoStartOfNextLine = FALSE;
+ BOOL bGotoEndOfPrevLine = FALSE;
+
+ String aLine( *aPaM.GetNode(), pLine->GetStart(), pLine->GetEnd() - pLine->GetStart() );
+ USHORT nPosInLine = aPaM.GetIndex() - pLine->GetStart();
+
+ const sal_Unicode* pLineString = aLine.GetBuffer();
+
+ UErrorCode nError = U_ZERO_ERROR;
+ UBiDi* pBidi = ubidi_openSized( aLine.Len(), 0, &nError );
+
+ const UBiDiLevel nBidiLevel = IsRightToLeft( nPara ) ? 1 /*RTL*/ : 0 /*LTR*/;
+ ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(pLineString), aLine.Len(), nBidiLevel, NULL, &nError ); // UChar != sal_Unicode in MinGW
+
+ if ( !pEditView->IsInsertMode() )
+ {
+ BOOL bEndOfLine = nPosInLine == aLine.Len();
+ USHORT nVisPos = (USHORT)ubidi_getVisualIndex( pBidi, !bEndOfLine ? nPosInLine : nPosInLine-1, &nError );
+ if ( bVisualToLeft )
+ {
+ bGotoEndOfPrevLine = nVisPos == 0;
+ if ( !bEndOfLine )
+ nVisPos--;
+ }
+ else
+ {
+ bGotoStartOfNextLine = nVisPos == (aLine.Len() - 1);
+ if ( !bEndOfLine )
+ nVisPos++;
+ }
+
+ if ( !bGotoEndOfPrevLine && !bGotoStartOfNextLine )
+ {
+ USHORT nLogPos = (USHORT)ubidi_getLogicalIndex( pBidi, nVisPos, &nError );
+ aPaM.GetIndex() = pLine->GetStart() + nLogPos;
+ pEditView->pImpEditView->SetCursorBidiLevel( 0 );
+ }
+ }
+ else
+ {
+ BOOL bWasBehind = FALSE;
+ BOOL bBeforePortion = !nPosInLine || pEditView->pImpEditView->GetCursorBidiLevel() == 1;
+ if ( nPosInLine && ( !bBeforePortion ) ) // before the next portion
+ bWasBehind = TRUE; // step one back, otherwise visual will be unusable when rtl portion follows.
+
+ USHORT nPortionStart;
+ USHORT nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, bBeforePortion );
+ TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
+ BOOL bRTLPortion = (pTextPortion->GetRightToLeft() % 2) != 0;
+
+ // -1: We are 'behind' the character
+ long nVisPos = (long)ubidi_getVisualIndex( pBidi, bWasBehind ? nPosInLine-1 : nPosInLine, &nError );
+ if ( bVisualToLeft )
+ {
+ if ( !bWasBehind || bRTLPortion )
+ nVisPos--;
+ }
+ else
+ {
+ if ( bWasBehind || bRTLPortion || bBeforePortion )
+ nVisPos++;
+// if ( bWasBehind && bRTLPortion )
+// nVisPos++;
+ }
+
+ bGotoEndOfPrevLine = nVisPos < 0;
+ bGotoStartOfNextLine = nVisPos >= aLine.Len();
+
+ if ( !bGotoEndOfPrevLine && !bGotoStartOfNextLine )
+ {
+ USHORT nLogPos = (USHORT)ubidi_getLogicalIndex( pBidi, nVisPos, &nError );
+
+/*
+ if ( nLogPos == aPaM.GetIndex() )
+ {
+ if ( bVisualToLeft )
+ bGotoEndOfPrevLine = TRUE;
+ else
+ bGotoStartOfNextLine = TRUE;
+ }
+ else
+*/
+ {
+ aPaM.GetIndex() = pLine->GetStart() + nLogPos;
+
+ // RTL portion, stay visually on the left side.
+ USHORT _nPortionStart;
+ // USHORT nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, !bRTLPortion );
+ USHORT _nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), _nPortionStart, TRUE );
+ TextPortion* _pTextPortion = pParaPortion->GetTextPortions().GetObject( _nTextPortion );
+ if ( bVisualToLeft && !bRTLPortion && ( _pTextPortion->GetRightToLeft() % 2 ) )
+ aPaM.GetIndex()++;
+ else if ( !bVisualToLeft && bRTLPortion && ( bWasBehind || !(_pTextPortion->GetRightToLeft() % 2 )) )
+ aPaM.GetIndex()++;
+
+ pEditView->pImpEditView->SetCursorBidiLevel( _nPortionStart );
+ }
+ }
+ }
+
+ ubidi_close( pBidi );
+
+ if ( bGotoEndOfPrevLine )
+ {
+ aPaM = CursorUp( aPaM, pEditView );
+ if ( aPaM != rPaM )
+ aPaM = CursorVisualStartEnd( pEditView, aPaM, FALSE );
+ }
+ else if ( bGotoStartOfNextLine )
+ {
+ aPaM = CursorDown( aPaM, pEditView );
+ if ( aPaM != rPaM )
+ aPaM = CursorVisualStartEnd( pEditView, aPaM, TRUE );
+ }
+ }
+ return aPaM;
+}
+
+
+EditPaM ImpEditEngine::CursorLeft( const EditPaM& rPaM, USHORT nCharacterIteratorMode )
+{
+ EditPaM aCurPaM( rPaM );
+ EditPaM aNewPaM( aCurPaM );
+
+ if ( aCurPaM.GetIndex() )
+ {
+ sal_Int32 nCount = 1;
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ aNewPaM.SetIndex( (USHORT)_xBI->previousCharacters( *aNewPaM.GetNode(), aNewPaM.GetIndex(), GetLocale( aNewPaM ), nCharacterIteratorMode, nCount, nCount ) );
+ }
+ else
+ {
+ ContentNode* pNode = aCurPaM.GetNode();
+ pNode = GetPrevVisNode( pNode );
+ if ( pNode )
+ {
+ aNewPaM.SetNode( pNode );
+ aNewPaM.SetIndex( pNode->Len() );
+ }
+ }
+
+ return aNewPaM;
+}
+
+EditPaM ImpEditEngine::CursorRight( const EditPaM& rPaM, USHORT nCharacterIteratorMode )
+{
+ EditPaM aCurPaM( rPaM );
+ EditPaM aNewPaM( aCurPaM );
+
+ if ( aCurPaM.GetIndex() < aCurPaM.GetNode()->Len() )
+ {
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ sal_Int32 nCount = 1;
+ aNewPaM.SetIndex( (USHORT)_xBI->nextCharacters( *aNewPaM.GetNode(), aNewPaM.GetIndex(), GetLocale( aNewPaM ), nCharacterIteratorMode, nCount, nCount ) );
+ }
+ else
+ {
+ ContentNode* pNode = aCurPaM.GetNode();
+ pNode = GetNextVisNode( pNode );
+ if ( pNode )
+ {
+ aNewPaM.SetNode( pNode );
+ aNewPaM.SetIndex( 0 );
+ }
+ }
+
+ return aNewPaM;
+}
+
+EditPaM ImpEditEngine::CursorUp( const EditPaM& rPaM, EditView* pView )
+{
+ DBG_ASSERT( pView, "Keine View - Keine Cursorbewegung!" );
+
+ ParaPortion* pPPortion = FindParaPortion( rPaM.GetNode() );
+ DBG_ASSERT( pPPortion, "Keine passende Portion gefunden: CursorUp" );
+ USHORT nLine = pPPortion->GetLineNumber( rPaM.GetIndex() );
+ EditLine* pLine = pPPortion->GetLines().GetObject( nLine );
+
+ long nX;
+ if ( pView->pImpEditView->nTravelXPos == TRAVEL_X_DONTKNOW )
+ {
+ nX = GetXPos( pPPortion, pLine, rPaM.GetIndex() );
+ pView->pImpEditView->nTravelXPos = nX+nOnePixelInRef;
+ }
+ else
+ nX = pView->pImpEditView->nTravelXPos;
+
+ EditPaM aNewPaM( rPaM );
+ if ( nLine ) // gleicher Absatz
+ {
+ EditLine* pPrevLine = pPPortion->GetLines().GetObject(nLine-1);
+ aNewPaM.SetIndex( GetChar( pPPortion, pPrevLine, nX ) );
+ // Wenn davor eine autom.Umgebrochene Zeile, und ich muss genau an das
+ // Ende dieser Zeile, landet der Cursor in der aktuellen Zeile am Anfang
+ // Siehe Problem: Letztes Zeichen einer autom.umgebr. Zeile = Cursor
+ if ( aNewPaM.GetIndex() && ( aNewPaM.GetIndex() == pLine->GetStart() ) )
+ aNewPaM = CursorLeft( aNewPaM );
+ }
+ else // vorheriger Absatz
+ {
+ ParaPortion* pPrevPortion = GetPrevVisPortion( pPPortion );
+ if ( pPrevPortion )
+ {
+ pLine = pPrevPortion->GetLines().GetObject( pPrevPortion->GetLines().Count()-1 );
+ DBG_ASSERT( pLine, "Zeile davor nicht gefunden: CursorUp" );
+ aNewPaM.SetNode( pPrevPortion->GetNode() );
+ aNewPaM.SetIndex( GetChar( pPrevPortion, pLine, nX+nOnePixelInRef ) );
+ }
+ }
+
+ return aNewPaM;
+}
+
+EditPaM ImpEditEngine::CursorDown( const EditPaM& rPaM, EditView* pView )
+{
+ DBG_ASSERT( pView, "Keine View - Keine Cursorbewegung!" );
+
+ ParaPortion* pPPortion = FindParaPortion( rPaM.GetNode() );
+ DBG_ASSERT( pPPortion, "Keine passende Portion gefunden: CursorDown" );
+ USHORT nLine = pPPortion->GetLineNumber( rPaM.GetIndex() );
+
+ long nX;
+ if ( pView->pImpEditView->nTravelXPos == TRAVEL_X_DONTKNOW )
+ {
+ EditLine* pLine = pPPortion->GetLines().GetObject(nLine);
+ nX = GetXPos( pPPortion, pLine, rPaM.GetIndex() );
+ pView->pImpEditView->nTravelXPos = nX+nOnePixelInRef;
+ }
+ else
+ nX = pView->pImpEditView->nTravelXPos;
+
+ EditPaM aNewPaM( rPaM );
+ if ( nLine < pPPortion->GetLines().Count()-1 )
+ {
+ EditLine* pNextLine = pPPortion->GetLines().GetObject(nLine+1);
+ aNewPaM.SetIndex( GetChar( pPPortion, pNextLine, nX ) );
+ // Sonderbehandlung siehe CursorUp...
+ if ( ( aNewPaM.GetIndex() == pNextLine->GetEnd() ) && ( aNewPaM.GetIndex() > pNextLine->GetStart() ) && ( aNewPaM.GetIndex() < pPPortion->GetNode()->Len() ) )
+ aNewPaM = CursorLeft( aNewPaM );
+ }
+ else // naechster Absatz
+ {
+ ParaPortion* pNextPortion = GetNextVisPortion( pPPortion );
+ if ( pNextPortion )
+ {
+ EditLine* pLine = pNextPortion->GetLines().GetObject(0);
+ DBG_ASSERT( pLine, "Zeile davor nicht gefunden: CursorUp" );
+ aNewPaM.SetNode( pNextPortion->GetNode() );
+ // Nie ganz ans Ende wenn mehrere Zeilen, da dann eine
+ // Zeile darunter der Cursor angezeigt wird.
+ aNewPaM.SetIndex( GetChar( pNextPortion, pLine, nX+nOnePixelInRef ) );
+ if ( ( aNewPaM.GetIndex() == pLine->GetEnd() ) && ( aNewPaM.GetIndex() > pLine->GetStart() ) && ( pNextPortion->GetLines().Count() > 1 ) )
+ aNewPaM = CursorLeft( aNewPaM );
+ }
+ }
+
+ return aNewPaM;
+}
+
+EditPaM ImpEditEngine::CursorStartOfLine( const EditPaM& rPaM )
+{
+ ParaPortion* pCurPortion = FindParaPortion( rPaM.GetNode() );
+ DBG_ASSERT( pCurPortion, "Keine Portion fuer den PaM ?" );
+ USHORT nLine = pCurPortion->GetLineNumber( rPaM.GetIndex() );
+ EditLine* pLine = pCurPortion->GetLines().GetObject(nLine);
+ DBG_ASSERT( pLine, "Aktuelle Zeile nicht gefunden ?!" );
+
+ EditPaM aNewPaM( rPaM );
+ aNewPaM.SetIndex( pLine->GetStart() );
+ return aNewPaM;
+}
+
+EditPaM ImpEditEngine::CursorEndOfLine( const EditPaM& rPaM )
+{
+ ParaPortion* pCurPortion = FindParaPortion( rPaM.GetNode() );
+ DBG_ASSERT( pCurPortion, "Keine Portion fuer den PaM ?" );
+ USHORT nLine = pCurPortion->GetLineNumber( rPaM.GetIndex() );
+ EditLine* pLine = pCurPortion->GetLines().GetObject(nLine);
+ DBG_ASSERT( pLine, "Aktuelle Zeile nicht gefunden ?!" );
+
+ EditPaM aNewPaM( rPaM );
+ aNewPaM.SetIndex( pLine->GetEnd() );
+ if ( pLine->GetEnd() > pLine->GetStart() )
+ {
+// xub_Unicode cLastChar = aNewPaM.GetNode()->GetChar( aNewPaM.GetIndex()-1 );
+ if ( aNewPaM.GetNode()->IsFeature( aNewPaM.GetIndex() - 1 ) )
+ {
+ // Bei einem weichen Umbruch muss ich davor stehen!
+ EditCharAttrib* pNextFeature = aNewPaM.GetNode()->GetCharAttribs().FindFeature( aNewPaM.GetIndex()-1 );
+ if ( pNextFeature && ( pNextFeature->GetItem()->Which() == EE_FEATURE_LINEBR ) )
+ aNewPaM = CursorLeft( aNewPaM );
+ }
+ else if ( ( aNewPaM.GetNode()->GetChar( aNewPaM.GetIndex() - 1 ) == ' ' ) && ( aNewPaM.GetIndex() != aNewPaM.GetNode()->Len() ) )
+ {
+ // Bei einem Blank in einer autom. umgebrochenen Zeile macht es Sinn,
+ // davor zu stehen, da der Anwender hinter das Wort will.
+ // Wenn diese geaendert wird, Sonderbehandlung fuer Pos1 nach End!
+ aNewPaM = CursorLeft( aNewPaM );
+ }
+ }
+ return aNewPaM;
+}
+
+EditPaM ImpEditEngine::CursorStartOfParagraph( const EditPaM& rPaM )
+{
+ EditPaM aPaM( rPaM.GetNode(), 0 );
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::CursorEndOfParagraph( const EditPaM& rPaM )
+{
+ EditPaM aPaM( rPaM.GetNode(), rPaM.GetNode()->Len() );
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::CursorStartOfDoc()
+{
+ EditPaM aPaM( aEditDoc.SaveGetObject( 0 ), 0 );
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::CursorEndOfDoc()
+{
+ ContentNode* pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count()-1 );
+ ParaPortion* pLastPortion = GetParaPortions().SaveGetObject( aEditDoc.Count()-1 );
+ DBG_ASSERT( pLastNode && pLastPortion, "CursorEndOfDoc: Node oder Portion nicht gefunden" );
+
+ if ( !pLastPortion->IsVisible() )
+ {
+ pLastNode = GetPrevVisNode( pLastPortion->GetNode() );
+ DBG_ASSERT( pLastNode, "Kein sichtbarer Absatz?" );
+ if ( !pLastNode )
+ pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count()-1 );
+ }
+
+ EditPaM aPaM( pLastNode, pLastNode->Len() );
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::PageUp( const EditPaM& rPaM, EditView* pView )
+{
+ Rectangle aRec = PaMtoEditCursor( rPaM );
+ Point aTopLeft = aRec.TopLeft();
+ aTopLeft.Y() -= pView->GetVisArea().GetHeight() *9/10;
+ aTopLeft.X() += nOnePixelInRef;
+ if ( aTopLeft.Y() < 0 )
+ {
+ aTopLeft.Y() = 0;
+ }
+ return GetPaM( aTopLeft );
+}
+
+EditPaM ImpEditEngine::PageDown( const EditPaM& rPaM, EditView* pView )
+{
+ Rectangle aRec = PaMtoEditCursor( rPaM );
+ Point aBottomRight = aRec.BottomRight();
+ aBottomRight.Y() += pView->GetVisArea().GetHeight() *9/10;
+ aBottomRight.X() += nOnePixelInRef;
+ long nHeight = GetTextHeight();
+ if ( aBottomRight.Y() > nHeight )
+ {
+ aBottomRight.Y() = nHeight-2;
+ }
+ return GetPaM( aBottomRight );
+}
+
+EditPaM ImpEditEngine::WordLeft( const EditPaM& rPaM, sal_Int16 nWordType )
+{
+ USHORT nCurrentPos = rPaM.GetIndex();
+ EditPaM aNewPaM( rPaM );
+ if ( nCurrentPos == 0 )
+ {
+ // Vorheriger Absatz...
+ USHORT nCurPara = aEditDoc.GetPos( aNewPaM.GetNode() );
+ ContentNode* pPrevNode = aEditDoc.SaveGetObject( --nCurPara );
+ if ( pPrevNode )
+ {
+ aNewPaM.SetNode( pPrevNode );
+ aNewPaM.SetIndex( pPrevNode->Len() );
+ }
+ }
+ else
+ {
+ // we need to increase the position by 1 when retrieving the locale
+ // since the attribute for the char left to the cursor position is returned
+ EditPaM aTmpPaM( aNewPaM );
+ xub_StrLen nMax = rPaM.GetNode()->Len();
+ if ( aTmpPaM.GetIndex() < nMax )
+ aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
+ lang::Locale aLocale( GetLocale( aTmpPaM ) );
+
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ i18n::Boundary aBoundary = _xBI->getWordBoundary( *aNewPaM.GetNode(), nCurrentPos, aLocale, nWordType, sal_True );
+ if ( aBoundary.startPos >= nCurrentPos )
+ aBoundary = _xBI->previousWord( *aNewPaM.GetNode(), nCurrentPos, aLocale, nWordType );
+ aNewPaM.SetIndex( ( aBoundary.startPos != (-1) ) ? (USHORT)aBoundary.startPos : 0 );
+ }
+
+ return aNewPaM;
+}
+
+EditPaM ImpEditEngine::WordRight( const EditPaM& rPaM, sal_Int16 nWordType )
+{
+ xub_StrLen nMax = rPaM.GetNode()->Len();
+ EditPaM aNewPaM( rPaM );
+ if ( aNewPaM.GetIndex() < nMax )
+ {
+ // we need to increase the position by 1 when retrieving the locale
+ // since the attribute for the char left to the cursor position is returned
+ EditPaM aTmpPaM( aNewPaM );
+ aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
+ lang::Locale aLocale( GetLocale( aTmpPaM ) );
+
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ i18n::Boundary aBoundary = _xBI->nextWord( *aNewPaM.GetNode(), aNewPaM.GetIndex(), aLocale, nWordType );
+ aNewPaM.SetIndex( (USHORT)aBoundary.startPos );
+ }
+ // not 'else', maybe the index reached nMax now...
+ if ( aNewPaM.GetIndex() >= nMax )
+ {
+ // Naechster Absatz...
+ USHORT nCurPara = aEditDoc.GetPos( aNewPaM.GetNode() );
+ ContentNode* pNextNode = aEditDoc.SaveGetObject( ++nCurPara );
+ if ( pNextNode )
+ {
+ aNewPaM.SetNode( pNextNode );
+ aNewPaM.SetIndex( 0 );
+ }
+ }
+ return aNewPaM;
+}
+
+EditPaM ImpEditEngine::StartOfWord( const EditPaM& rPaM, sal_Int16 nWordType )
+{
+ EditPaM aNewPaM( rPaM );
+
+ // we need to increase the position by 1 when retrieving the locale
+ // since the attribute for the char left to the cursor position is returned
+ EditPaM aTmpPaM( aNewPaM );
+ xub_StrLen nMax = rPaM.GetNode()->Len();
+ if ( aTmpPaM.GetIndex() < nMax )
+ aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
+ lang::Locale aLocale( GetLocale( aTmpPaM ) );
+
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ i18n::Boundary aBoundary = _xBI->getWordBoundary( *rPaM.GetNode(), rPaM.GetIndex(), aLocale, nWordType, sal_True );
+ aNewPaM.SetIndex( (USHORT)aBoundary.startPos );
+ return aNewPaM;
+}
+
+EditPaM ImpEditEngine::EndOfWord( const EditPaM& rPaM, sal_Int16 nWordType )
+{
+ EditPaM aNewPaM( rPaM );
+
+ // we need to increase the position by 1 when retrieving the locale
+ // since the attribute for the char left to the cursor position is returned
+ EditPaM aTmpPaM( aNewPaM );
+ xub_StrLen nMax = rPaM.GetNode()->Len();
+ if ( aTmpPaM.GetIndex() < nMax )
+ aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
+ lang::Locale aLocale( GetLocale( aTmpPaM ) );
+
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ i18n::Boundary aBoundary = _xBI->getWordBoundary( *rPaM.GetNode(), rPaM.GetIndex(), aLocale, nWordType, sal_True );
+ aNewPaM.SetIndex( (USHORT)aBoundary.endPos );
+ return aNewPaM;
+}
+
+EditSelection ImpEditEngine::SelectWord( const EditSelection& rCurSel, sal_Int16 nWordType, BOOL bAcceptStartOfWord )
+{
+ EditSelection aNewSel( rCurSel );
+ EditPaM aPaM( rCurSel.Max() );
+
+ // we need to increase the position by 1 when retrieving the locale
+ // since the attribute for the char left to the cursor position is returned
+ EditPaM aTmpPaM( aPaM );
+ xub_StrLen nMax = aPaM.GetNode()->Len();
+ if ( aTmpPaM.GetIndex() < nMax )
+ aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
+ lang::Locale aLocale( GetLocale( aTmpPaM ) );
+
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ sal_Int16 nType = _xBI->getWordType( *aPaM.GetNode(), aPaM.GetIndex(), aLocale );
+ if ( nType == i18n::WordType::ANY_WORD )
+ {
+ i18n::Boundary aBoundary = _xBI->getWordBoundary( *aPaM.GetNode(), aPaM.GetIndex(), aLocale, nWordType, sal_True );
+ // don't select when curser at end of word
+ if ( ( aBoundary.endPos > aPaM.GetIndex() ) &&
+ ( ( aBoundary.startPos < aPaM.GetIndex() ) || ( bAcceptStartOfWord && ( aBoundary.startPos == aPaM.GetIndex() ) ) ) )
+ {
+ aNewSel.Min().SetIndex( (USHORT)aBoundary.startPos );
+ aNewSel.Max().SetIndex( (USHORT)aBoundary.endPos );
+ }
+ }
+
+ return aNewSel;
+}
+
+EditSelection ImpEditEngine::SelectSentence( const EditSelection& rCurSel )
+{
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ const EditPaM& rPaM = rCurSel.Min();
+ const ContentNode* pNode = rPaM.GetNode();
+ // #i50710# line breaks are marked with 0x01 - the break iterator prefers 0x0a for that
+ String sParagraph(*pNode);
+ sParagraph.SearchAndReplaceAll(0x01,0x0a);
+ //return Null if search starts at the beginning of the string
+ long nStart = rPaM.GetIndex() ? _xBI->beginOfSentence( sParagraph, rPaM.GetIndex(), GetLocale( rPaM ) ) : 0;
+
+ long nEnd = _xBI->endOfSentence( *pNode, rPaM.GetIndex(), GetLocale( rPaM ) );
+ EditSelection aNewSel( rCurSel );
+ DBG_ASSERT(nStart < pNode->Len() && nEnd <= pNode->Len(), "sentence indices out of range");
+ aNewSel.Min().SetIndex( (USHORT)nStart );
+ aNewSel.Max().SetIndex( (USHORT)nEnd );
+ return aNewSel;
+}
+
+sal_Bool ImpEditEngine::IsInputSequenceCheckingRequired( sal_Unicode nChar, const EditSelection& rCurSel ) const
+{
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ if (!pCTLOptions)
+ pCTLOptions = new SvtCTLOptions;
+
+ // get the index that really is first
+ USHORT nFirstPos = rCurSel.Min().GetIndex();
+ USHORT nMaxPos = rCurSel.Max().GetIndex();
+ if (nMaxPos < nFirstPos)
+ nFirstPos = nMaxPos;
+
+ sal_Bool bIsSequenceChecking =
+ pCTLOptions->IsCTLFontEnabled() &&
+ pCTLOptions->IsCTLSequenceChecking() &&
+ nFirstPos != 0 && /* first char needs not to be checked */
+ _xBI.is() && i18n::ScriptType::COMPLEX == _xBI->getScriptType( rtl::OUString( nChar ), 0 );
+
+ return bIsSequenceChecking;
+}
+
+/*************************************************************************
+ * lcl_HasStrongLTR
+ *************************************************************************/
+ bool lcl_HasStrongLTR ( const String& rTxt, xub_StrLen nStart, xub_StrLen nEnd )
+ {
+ for ( xub_StrLen nCharIdx = nStart; nCharIdx < nEnd; ++nCharIdx )
+ {
+ const UCharDirection nCharDir = u_charDirection ( rTxt.GetChar ( nCharIdx ));
+ if ( nCharDir == U_LEFT_TO_RIGHT ||
+ nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
+ nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
+ return true;
+ }
+ return false;
+ }
+
+
+
+void ImpEditEngine::InitScriptTypes( USHORT nPara )
+{
+ ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
+ ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
+ rTypes.Remove( 0, rTypes.Count() );
+
+// pParaPortion->aExtraCharInfos.Remove( 0, pParaPortion->aExtraCharInfos.Count() );
+
+ ContentNode* pNode = pParaPortion->GetNode();
+ if ( pNode->Len() )
+ {
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+
+ String aText( *pNode );
+
+ // To handle fields put the character from the field in the string,
+ // because endOfScript( ... ) will skip the CH_FEATURE, because this is WEAK
+ EditCharAttrib* pField = pNode->GetCharAttribs().FindNextAttrib( EE_FEATURE_FIELD, 0 );
+ while ( pField )
+ {
+ ::rtl::OUString aFldText( ((EditCharAttribField*)pField)->GetFieldValue() );
+ if ( aFldText.getLength() )
+ {
+ aText.SetChar( pField->GetStart(), aFldText.getStr()[0] );
+ short nFldScriptType = _xBI->getScriptType( aFldText, 0 );
+
+ for ( USHORT nCharInField = 1; nCharInField < aFldText.getLength(); nCharInField++ )
+ {
+ short nTmpType = _xBI->getScriptType( aFldText, nCharInField );
+
+ // First char from field wins...
+ if ( nFldScriptType == i18n::ScriptType::WEAK )
+ {
+ nFldScriptType = nTmpType;
+ aText.SetChar( pField->GetStart(), aFldText.getStr()[nCharInField] );
+ }
+
+ // ... but if the first one is LATIN, and there are CJK or CTL chars too,
+ // we prefer that ScripType because we need an other font.
+ if ( ( nTmpType == i18n::ScriptType::ASIAN ) || ( nTmpType == i18n::ScriptType::COMPLEX ) )
+ {
+ aText.SetChar( pField->GetStart(), aFldText.getStr()[nCharInField] );
+ break;
+ }
+ }
+ }
+ // #112831# Last Field might go from 0xffff to 0x0000
+ pField = pField->GetEnd() ? pNode->GetCharAttribs().FindNextAttrib( EE_FEATURE_FIELD, pField->GetEnd() ) : NULL;
+ }
+
+ ::rtl::OUString aOUText( aText );
+ USHORT nTextLen = (USHORT)aOUText.getLength();
+
+ sal_Int32 nPos = 0;
+ short nScriptType = _xBI->getScriptType( aOUText, nPos );
+ rTypes.Insert( ScriptTypePosInfo( nScriptType, (USHORT)nPos, nTextLen ), rTypes.Count() );
+ nPos = _xBI->endOfScript( aOUText, nPos, nScriptType );
+ while ( ( nPos != (-1) ) && ( nPos < nTextLen ) )
+ {
+ rTypes[rTypes.Count()-1].nEndPos = (USHORT)nPos;
+
+ nScriptType = _xBI->getScriptType( aOUText, nPos );
+ long nEndPos = _xBI->endOfScript( aOUText, nPos, nScriptType );
+
+ if ( ( nScriptType == i18n::ScriptType::WEAK ) || ( nScriptType == rTypes[rTypes.Count()-1].nScriptType ) )
+ {
+ // Expand last ScriptTypePosInfo, don't create weak or unecessary portions
+ rTypes[rTypes.Count()-1].nEndPos = (USHORT)nEndPos;
+ }
+ else
+ {
+ if ( _xBI->getScriptType( aOUText, nPos - 1 ) == i18n::ScriptType::WEAK )
+ {
+ switch ( u_charType(aOUText.iterateCodePoints(&nPos, 0) ) ) {
+ case U_NON_SPACING_MARK:
+ case U_ENCLOSING_MARK:
+ case U_COMBINING_SPACING_MARK:
+ --nPos;
+ rTypes[rTypes.Count()-1].nEndPos--;
+ break;
+ }
+ }
+ rTypes.Insert( ScriptTypePosInfo( nScriptType, (USHORT)nPos, nTextLen ), rTypes.Count() );
+ }
+
+ nPos = nEndPos;
+ }
+
+ if ( rTypes[0].nScriptType == i18n::ScriptType::WEAK )
+ rTypes[0].nScriptType = ( rTypes.Count() > 1 ) ? rTypes[1].nScriptType : GetI18NScriptTypeOfLanguage( GetDefaultLanguage() );
+
+ // create writing direction information:
+ if ( !pParaPortion->aWritingDirectionInfos.Count() )
+ InitWritingDirections( nPara );
+
+ // i89825: Use CTL font for numbers embedded into an RTL run:
+ WritingDirectionInfos& rDirInfos = pParaPortion->aWritingDirectionInfos;
+ for ( USHORT n = 0; n < rDirInfos.Count(); ++n )
+ {
+ const xub_StrLen nStart = rDirInfos[n].nStartPos;
+ const xub_StrLen nEnd = rDirInfos[n].nEndPos;
+ const BYTE nCurrDirType = rDirInfos[n].nType;
+
+ if ( nCurrDirType % 2 == UBIDI_RTL || // text in RTL run
+ ( nCurrDirType > UBIDI_LTR && !lcl_HasStrongLTR( aText, nStart, nEnd ) ) ) // non-strong text in embedded LTR run
+ {
+ USHORT nIdx = 0;
+
+ // Skip entries in ScriptArray which are not inside the RTL run:
+ while ( nIdx < rTypes.Count() && rTypes[nIdx].nStartPos < nStart )
+ ++nIdx;
+
+ // Remove any entries *inside* the current run:
+ while ( nIdx < rTypes.Count() && rTypes[nIdx].nEndPos <= nEnd )
+ rTypes.Remove( nIdx );
+
+ // special case:
+ if(nIdx < rTypes.Count() && rTypes[nIdx].nStartPos < nStart && rTypes[nIdx].nEndPos > nEnd)
+ {
+ rTypes.Insert( ScriptTypePosInfo( rTypes[nIdx].nScriptType, (USHORT)nEnd, rTypes[nIdx].nEndPos ), nIdx );
+ rTypes[nIdx].nEndPos = nStart;
+ }
+
+ if( nIdx )
+ rTypes[nIdx - 1].nEndPos = nStart;
+
+ rTypes.Insert( ScriptTypePosInfo( i18n::ScriptType::COMPLEX, (USHORT)nStart, (USHORT)nEnd), nIdx );
+ ++nIdx;
+
+ if( nIdx < rTypes.Count() )
+ rTypes[nIdx].nStartPos = nEnd;
+ }
+ }
+
+#if OSL_DEBUG_LEVEL > 1
+ USHORT nDebugStt = 0;
+ USHORT nDebugEnd = 0;
+ short nDebugType = 0;
+ for ( USHORT n = 0; n < rTypes.Count(); ++n )
+ {
+ nDebugStt = rTypes[n].nStartPos;
+ nDebugEnd = rTypes[n].nEndPos;
+ nDebugType = rTypes[n].nScriptType;
+ }
+#endif
+ }
+}
+
+USHORT ImpEditEngine::GetScriptType( const EditPaM& rPaM, USHORT* pEndPos ) const
+{
+ USHORT nScriptType = 0;
+
+ if ( pEndPos )
+ *pEndPos = rPaM.GetNode()->Len();
+
+ if ( rPaM.GetNode()->Len() )
+ {
+ USHORT nPara = GetEditDoc().GetPos( rPaM.GetNode() );
+ ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
+ if ( !pParaPortion->aScriptInfos.Count() )
+ ((ImpEditEngine*)this)->InitScriptTypes( nPara );
+
+ ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
+ USHORT nPos = rPaM.GetIndex();
+ for ( USHORT n = 0; n < rTypes.Count(); n++ )
+ {
+ if ( ( rTypes[n].nStartPos <= nPos ) && ( rTypes[n].nEndPos >= nPos ) )
+ {
+ nScriptType = rTypes[n].nScriptType;
+ if( pEndPos )
+ *pEndPos = rTypes[n].nEndPos;
+ break;
+ }
+ }
+ }
+ return nScriptType ? nScriptType : GetI18NScriptTypeOfLanguage( GetDefaultLanguage() );
+}
+
+USHORT ImpEditEngine::GetScriptType( const EditSelection& rSel ) const
+{
+ EditSelection aSel( rSel );
+ aSel.Adjust( aEditDoc );
+
+ short nScriptType = 0;
+
+ USHORT nStartPara = GetEditDoc().GetPos( aSel.Min().GetNode() );
+ USHORT nEndPara = GetEditDoc().GetPos( aSel.Max().GetNode() );
+
+ for ( USHORT nPara = nStartPara; nPara <= nEndPara; nPara++ )
+ {
+ ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
+ if ( !pParaPortion->aScriptInfos.Count() )
+ ((ImpEditEngine*)this)->InitScriptTypes( nPara );
+
+ ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
+
+ // find the first(!) script type position that holds the
+ // complete selection. Thus it will work for selections as
+ // well as with just moving the cursor from char to char.
+ USHORT nS = ( nPara == nStartPara ) ? aSel.Min().GetIndex() : 0;
+ USHORT nE = ( nPara == nEndPara ) ? aSel.Max().GetIndex() : pParaPortion->GetNode()->Len();
+ for ( USHORT n = 0; n < rTypes.Count(); n++ )
+ {
+ if (rTypes[n].nStartPos <= nS && nE <= rTypes[n].nEndPos)
+ {
+ if ( rTypes[n].nScriptType != i18n::ScriptType::WEAK )
+ {
+ nScriptType |= GetItemScriptType ( rTypes[n].nScriptType );
+ }
+ else
+ {
+ if ( !nScriptType && n )
+ {
+ // #93548# When starting with WEAK, use prev ScriptType...
+ nScriptType = rTypes[n-1].nScriptType;
+ }
+ }
+ break;
+ }
+ }
+ }
+ return nScriptType ? nScriptType : GetI18NScriptTypeOfLanguage( GetDefaultLanguage() );
+}
+
+BOOL ImpEditEngine::IsScriptChange( const EditPaM& rPaM ) const
+{
+ BOOL bScriptChange = FALSE;
+
+ if ( rPaM.GetNode()->Len() )
+ {
+ USHORT nPara = GetEditDoc().GetPos( rPaM.GetNode() );
+ ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
+ if ( !pParaPortion->aScriptInfos.Count() )
+ ((ImpEditEngine*)this)->InitScriptTypes( nPara );
+
+ ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
+ USHORT nPos = rPaM.GetIndex();
+ for ( USHORT n = 0; n < rTypes.Count(); n++ )
+ {
+ if ( rTypes[n].nStartPos == nPos )
+ {
+ bScriptChange = TRUE;
+ break;
+ }
+ }
+ }
+ return bScriptChange;
+}
+
+BOOL ImpEditEngine::HasScriptType( USHORT nPara, USHORT nType ) const
+{
+ BOOL bTypeFound = FALSE;
+
+ ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
+ if ( !pParaPortion->aScriptInfos.Count() )
+ ((ImpEditEngine*)this)->InitScriptTypes( nPara );
+
+ ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
+ for ( USHORT n = rTypes.Count(); n && !bTypeFound; )
+ {
+ if ( rTypes[--n].nScriptType == nType )
+ bTypeFound = TRUE;
+ }
+ return bTypeFound;
+}
+
+void ImpEditEngine::InitWritingDirections( USHORT nPara )
+{
+ ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
+ WritingDirectionInfos& rInfos = pParaPortion->aWritingDirectionInfos;
+ rInfos.Remove( 0, rInfos.Count() );
+
+ BOOL bCTL = FALSE;
+ ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
+ for ( USHORT n = 0; n < rTypes.Count(); n++ )
+ {
+ if ( rTypes[n].nScriptType == i18n::ScriptType::COMPLEX )
+ {
+ bCTL = TRUE;
+ break;
+ }
+ }
+
+ const UBiDiLevel nBidiLevel = IsRightToLeft( nPara ) ? 1 /*RTL*/ : 0 /*LTR*/;
+ if ( ( bCTL || ( nBidiLevel == 1 /*RTL*/ ) ) && pParaPortion->GetNode()->Len() )
+ {
+
+ String aText( *pParaPortion->GetNode() );
+
+ //
+ // Bidi functions from icu 2.0
+ //
+ UErrorCode nError = U_ZERO_ERROR;
+ UBiDi* pBidi = ubidi_openSized( aText.Len(), 0, &nError );
+ nError = U_ZERO_ERROR;
+
+ ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aText.GetBuffer()), aText.Len(), nBidiLevel, NULL, &nError ); // UChar != sal_Unicode in MinGW
+ nError = U_ZERO_ERROR;
+
+ long nCount = ubidi_countRuns( pBidi, &nError );
+
+ int32_t nStart = 0;
+ int32_t nEnd;
+ UBiDiLevel nCurrDir;
+
+ for ( USHORT nIdx = 0; nIdx < nCount; ++nIdx )
+ {
+ ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir );
+ rInfos.Insert( WritingDirectionInfo( nCurrDir, (USHORT)nStart, (USHORT)nEnd ), rInfos.Count() );
+ nStart = nEnd;
+ }
+
+ ubidi_close( pBidi );
+ }
+
+ // No infos mean no CTL and default dir is L2R...
+ if ( !rInfos.Count() )
+ rInfos.Insert( WritingDirectionInfo( 0, 0, (USHORT)pParaPortion->GetNode()->Len() ), rInfos.Count() );
+
+}
+
+BOOL ImpEditEngine::IsRightToLeft( USHORT nPara ) const
+{
+ BOOL bR2L = FALSE;
+ const SvxFrameDirectionItem* pFrameDirItem = NULL;
+
+ if ( !IsVertical() )
+ {
+ bR2L = GetDefaultHorizontalTextDirection() == EE_HTEXTDIR_R2L;
+ pFrameDirItem = &(const SvxFrameDirectionItem&)GetParaAttrib( nPara, EE_PARA_WRITINGDIR );
+ if ( pFrameDirItem->GetValue() == FRMDIR_ENVIRONMENT )
+ {
+ // #103045# if DefaultHorizontalTextDirection is set, use that value, otherwise pool default.
+ if ( GetDefaultHorizontalTextDirection() != EE_HTEXTDIR_DEFAULT )
+ {
+ pFrameDirItem = NULL; // bR2L allready set to default horizontal text direction
+ }
+ else
+ {
+ // Use pool default
+ pFrameDirItem = &(const SvxFrameDirectionItem&)((ImpEditEngine*)this)->GetEmptyItemSet().Get( EE_PARA_WRITINGDIR );
+ }
+ }
+ }
+
+ if ( pFrameDirItem )
+ bR2L = pFrameDirItem->GetValue() == FRMDIR_HORI_RIGHT_TOP;
+
+ return bR2L;
+}
+
+BOOL ImpEditEngine::HasDifferentRTLLevels( const ContentNode* pNode )
+{
+ USHORT nPara = GetEditDoc().GetPos( (ContentNode*)pNode );
+ ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
+
+ BOOL bHasDifferentRTLLevels = FALSE;
+
+ USHORT nRTLLevel = IsRightToLeft( nPara ) ? 1 : 0;
+ for ( USHORT n = 0; n < pParaPortion->GetTextPortions().Count(); n++ )
+ {
+ TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( n );
+ if ( pTextPortion->GetRightToLeft() != nRTLLevel )
+ {
+ bHasDifferentRTLLevels = TRUE;
+ break;
+ }
+ }
+ return bHasDifferentRTLLevels;
+}
+
+
+BYTE ImpEditEngine::GetRightToLeft( USHORT nPara, USHORT nPos, USHORT* pStart, USHORT* pEnd )
+{
+// BYTE nRightToLeft = IsRightToLeft( nPara ) ? 1 : 0;
+ BYTE nRightToLeft = 0;
+
+ ContentNode* pNode = aEditDoc.SaveGetObject( nPara );
+ if ( pNode && pNode->Len() )
+ {
+ ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
+ if ( !pParaPortion->aWritingDirectionInfos.Count() )
+ InitWritingDirections( nPara );
+
+// BYTE nType = 0;
+ WritingDirectionInfos& rDirInfos = pParaPortion->aWritingDirectionInfos;
+ for ( USHORT n = 0; n < rDirInfos.Count(); n++ )
+ {
+ if ( ( rDirInfos[n].nStartPos <= nPos ) && ( rDirInfos[n].nEndPos >= nPos ) )
+ {
+ nRightToLeft = rDirInfos[n].nType;
+ if ( pStart )
+ *pStart = rDirInfos[n].nStartPos;
+ if ( pEnd )
+ *pEnd = rDirInfos[n].nEndPos;
+ break;
+ }
+ }
+ }
+ return nRightToLeft;
+}
+
+SvxAdjust ImpEditEngine::GetJustification( USHORT nPara ) const
+{
+ SvxAdjust eJustification = SVX_ADJUST_LEFT;
+
+ if ( !aStatus.IsOutliner() )
+ {
+ eJustification = ((const SvxAdjustItem&) GetParaAttrib( nPara, EE_PARA_JUST )).GetAdjust();
+
+ if ( IsRightToLeft( nPara ) )
+ {
+ if ( eJustification == SVX_ADJUST_LEFT )
+ eJustification = SVX_ADJUST_RIGHT;
+ else if ( eJustification == SVX_ADJUST_RIGHT )
+ eJustification = SVX_ADJUST_LEFT;
+ }
+ }
+ return eJustification;
+}
+
+
+// ----------------------------------------------------------------------
+// Textaenderung
+// ----------------------------------------------------------------------
+
+void ImpEditEngine::ImpRemoveChars( const EditPaM& rPaM, USHORT nChars, EditUndoRemoveChars* pCurUndo )
+{
+ if ( IsUndoEnabled() && !IsInUndo() )
+ {
+ XubString aStr( rPaM.GetNode()->Copy( rPaM.GetIndex(), nChars ) );
+
+ // Pruefen, ob Attribute geloescht oder geaendert werden:
+ USHORT nStart = rPaM.GetIndex();
+ USHORT nEnd = nStart + nChars;
+ CharAttribArray& rAttribs = rPaM.GetNode()->GetCharAttribs().GetAttribs();
+// USHORT nAttrs = rAttribs.Count();
+ for ( USHORT nAttr = 0; nAttr < rAttribs.Count(); nAttr++ )
+ {
+ EditCharAttrib* pAttr = rAttribs[nAttr];
+ if ( ( pAttr->GetEnd() >= nStart ) && ( pAttr->GetStart() < nEnd ) )
+ {
+#ifndef SVX_LIGHT
+ EditSelection aSel( rPaM );
+ aSel.Max().GetIndex() = aSel.Max().GetIndex() + nChars;
+ EditUndoSetAttribs* pAttrUndo = CreateAttribUndo( aSel, GetEmptyItemSet() );
+ InsertUndo( pAttrUndo );
+#endif
+ break; // for
+ }
+ }
+ if ( pCurUndo && ( CreateEditPaM( pCurUndo->GetEPaM() ) == rPaM ) )
+ pCurUndo->GetStr() += aStr;
+#ifndef SVX_LIGHT
+ else
+ InsertUndo( new EditUndoRemoveChars( this, CreateEPaM( rPaM ), aStr ) );
+#endif
+ }
+
+ aEditDoc.RemoveChars( rPaM, nChars );
+ TextModified();
+}
+
+EditSelection ImpEditEngine::ImpMoveParagraphs( Range aOldPositions, USHORT nNewPos )
+{
+ aOldPositions.Justify();
+ BOOL bValidAction = ( (long)nNewPos < aOldPositions.Min() ) || ( (long)nNewPos > aOldPositions.Max() );
+ DBG_ASSERT( bValidAction, "Move in sich selbst ?" );
+ DBG_ASSERT( aOldPositions.Max() <= (long)GetParaPortions().Count(), "Voll drueber weg: MoveParagraphs" );
+
+ EditSelection aSelection;
+
+ if ( !bValidAction )
+ {
+ aSelection = aEditDoc.GetStartPaM();
+ return aSelection;
+ }
+
+ ULONG nParaCount = GetParaPortions().Count();
+
+ if ( nNewPos >= nParaCount )
+ nNewPos = GetParaPortions().Count();
+
+ // Height may change when moving first or last Paragraph
+ ParaPortion* pRecalc1 = NULL;
+ ParaPortion* pRecalc2 = NULL;
+ ParaPortion* pRecalc3 = NULL;
+ ParaPortion* pRecalc4 = NULL;
+
+ if ( nNewPos == 0 ) // Move to Start
+ {
+ pRecalc1 = GetParaPortions().GetObject( 0 );
+ pRecalc2 = GetParaPortions().GetObject( (USHORT)aOldPositions.Min() );
+
+ }
+ else if ( nNewPos == nParaCount )
+ {
+ pRecalc1 = GetParaPortions().GetObject( (USHORT)(nParaCount-1) );
+ pRecalc2 = GetParaPortions().GetObject( (USHORT)aOldPositions.Max() );
+ }
+
+ if ( aOldPositions.Min() == 0 ) // Move from Start
+ {
+ pRecalc3 = GetParaPortions().GetObject( 0 );
+ pRecalc4 = GetParaPortions().GetObject(
+ sal::static_int_cast< USHORT >( aOldPositions.Max()+1 ) );
+ }
+ else if ( (USHORT)aOldPositions.Max() == (nParaCount-1) )
+ {
+ pRecalc3 = GetParaPortions().GetObject( (USHORT)aOldPositions.Max() );
+ pRecalc4 = GetParaPortions().GetObject( (USHORT)(aOldPositions.Min()-1) );
+ }
+
+ MoveParagraphsInfo aMoveParagraphsInfo( sal::static_int_cast< USHORT >(aOldPositions.Min()), sal::static_int_cast< USHORT >(aOldPositions.Max()), nNewPos );
+ aBeginMovingParagraphsHdl.Call( &aMoveParagraphsInfo );
+
+ if ( IsUndoEnabled() && !IsInUndo())
+ InsertUndo( new EditUndoMoveParagraphs( this, aOldPositions, nNewPos ) );
+
+ // Position nicht aus dem Auge verlieren!
+ ParaPortion* pDestPortion = GetParaPortions().SaveGetObject( nNewPos );
+
+ ParaPortionList aTmpPortionList;
+ USHORT i;
+ for ( i = (USHORT)aOldPositions.Min(); i <= (USHORT)aOldPositions.Max(); i++ )
+ {
+ // Immer aOldPositions.Min(), da Remove().
+ ParaPortion* pTmpPortion = GetParaPortions().GetObject( (USHORT)aOldPositions.Min() );
+ GetParaPortions().Remove( (USHORT)aOldPositions.Min() );
+ aEditDoc.Remove( (USHORT)aOldPositions.Min() );
+ aTmpPortionList.Insert( pTmpPortion, aTmpPortionList.Count() );
+ }
+
+ USHORT nRealNewPos = pDestPortion ? GetParaPortions().GetPos( pDestPortion ) : GetParaPortions().Count();
+ DBG_ASSERT( nRealNewPos != USHRT_MAX, "ImpMoveParagraphs: Ungueltige Position!" );
+
+ for ( i = 0; i < (USHORT)aTmpPortionList.Count(); i++ )
+ {
+ ParaPortion* pTmpPortion = aTmpPortionList.GetObject( i );
+ if ( i == 0 )
+ aSelection.Min().SetNode( pTmpPortion->GetNode() );
+
+ aSelection.Max().SetNode( pTmpPortion->GetNode() );
+ aSelection.Max().SetIndex( pTmpPortion->GetNode()->Len() );
+
+ ContentNode* pN = pTmpPortion->GetNode();
+ aEditDoc.Insert( pN, nRealNewPos+i );
+
+ GetParaPortions().Insert( pTmpPortion, nRealNewPos+i );
+ }
+
+ aEndMovingParagraphsHdl.Call( &aMoveParagraphsInfo );
+
+ if ( GetNotifyHdl().IsSet() )
+ {
+ EENotify aNotify( EE_NOTIFY_PARAGRAPHSMOVED );
+ aNotify.pEditEngine = GetEditEnginePtr();
+ aNotify.nParagraph = nNewPos;
+ aNotify.nParam1 = sal::static_int_cast< USHORT >(aOldPositions.Min());
+ aNotify.nParam2 = sal::static_int_cast< USHORT >(aOldPositions.Max());
+ CallNotify( aNotify );
+ }
+
+ aEditDoc.SetModified( TRUE );
+
+ if ( pRecalc1 )
+ CalcHeight( pRecalc1 );
+ if ( pRecalc2 )
+ CalcHeight( pRecalc2 );
+ if ( pRecalc3 )
+ CalcHeight( pRecalc3 );
+ if ( pRecalc4 )
+ CalcHeight( pRecalc4 );
+
+ aTmpPortionList.Remove( 0, aTmpPortionList.Count() ); // wichtig !
+
+#ifdef EDITDEBUG
+ GetParaPortions().DbgCheck(aEditDoc);
+#endif
+ return aSelection;
+}
+
+
+EditPaM ImpEditEngine::ImpConnectParagraphs( ContentNode* pLeft, ContentNode* pRight, BOOL bBackward )
+{
+ DBG_ASSERT( pLeft != pRight, "Den gleichen Absatz zusammenfuegen ?" );
+ DBG_ASSERT( aEditDoc.GetPos( pLeft ) != USHRT_MAX, "Einzufuegenden Node nicht gefunden(1)" );
+ DBG_ASSERT( aEditDoc.GetPos( pRight ) != USHRT_MAX, "Einzufuegenden Node nicht gefunden(2)" );
+
+ USHORT nParagraphTobeDeleted = aEditDoc.GetPos( pRight );
+ DeletedNodeInfo* pInf = new DeletedNodeInfo( (ULONG)pRight, nParagraphTobeDeleted );
+ aDeletedNodes.Insert( pInf, aDeletedNodes.Count() );
+
+ GetEditEnginePtr()->ParagraphConnected( aEditDoc.GetPos( pLeft ), aEditDoc.GetPos( pRight ) );
+
+#ifndef SVX_LIGHT
+ if ( IsUndoEnabled() && !IsInUndo() )
+ {
+ InsertUndo( new EditUndoConnectParas( this,
+ aEditDoc.GetPos( pLeft ), pLeft->Len(),
+ pLeft->GetContentAttribs().GetItems(), pRight->GetContentAttribs().GetItems(),
+ pLeft->GetStyleSheet(), pRight->GetStyleSheet(), bBackward ) );
+ }
+#endif
+
+ if ( bBackward )
+ {
+ pLeft->SetStyleSheet( pRight->GetStyleSheet(), TRUE );
+ pLeft->GetContentAttribs().GetItems().Set( pRight->GetContentAttribs().GetItems() );
+ pLeft->GetCharAttribs().GetDefFont() = pRight->GetCharAttribs().GetDefFont();
+ }
+
+ ParaAttribsChanged( pLeft );
+
+ // Erstmal Portions suchen, da pRight nach ConnectParagraphs weg.
+ ParaPortion* pLeftPortion = FindParaPortion( pLeft );
+ ParaPortion* pRightPortion = FindParaPortion( pRight );
+ DBG_ASSERT( pLeftPortion, "Blinde Portion in ImpConnectParagraphs(1)" );
+ DBG_ASSERT( pRightPortion, "Blinde Portion in ImpConnectParagraphs(2)" );
+ DBG_ASSERT( nParagraphTobeDeleted == GetParaPortions().GetPos( pRightPortion ), "NodePos != PortionPos?" );
+
+#ifndef SVX_LIGHT
+ if ( GetStatus().DoOnlineSpelling() )
+ {
+ xub_StrLen nEnd = pLeft->Len();
+ xub_StrLen nInv = nEnd ? nEnd-1 : nEnd;
+ pLeft->GetWrongList()->ClearWrongs( nInv, 0xFFFF, pLeft ); // Evtl. einen wegnehmen
+ pLeft->GetWrongList()->MarkInvalid( nInv, nEnd+1 );
+ // Falschgeschriebene Woerter ruebernehmen:
+ USHORT nRWrongs = pRight->GetWrongList()->Count();
+ for ( USHORT nW = 0; nW < nRWrongs; nW++ )
+ {
+ WrongRange aWrong = pRight->GetWrongList()->GetObject( nW );
+ if ( aWrong.nStart != 0 ) // Nicht ein anschliessender
+ {
+ aWrong.nStart = aWrong.nStart + nEnd;
+ aWrong.nEnd = aWrong.nEnd + nEnd;
+ pLeft->GetWrongList()->InsertWrong( aWrong, pLeft->GetWrongList()->Count() );
+ }
+ }
+ }
+#endif
+
+ if ( IsCallParaInsertedOrDeleted() )
+ GetEditEnginePtr()->ParagraphDeleted( nParagraphTobeDeleted );
+
+ EditPaM aPaM = aEditDoc.ConnectParagraphs( pLeft, pRight );
+ GetParaPortions().Remove( nParagraphTobeDeleted );
+ delete pRightPortion;
+
+ pLeftPortion->MarkSelectionInvalid( aPaM.GetIndex(), pLeft->Len() );
+
+ // der rechte Node wird von EditDoc::ConnectParagraphs() geloescht.
+
+ if ( GetTextRanger() )
+ {
+ // Durch das zusammenfuegen wird der linke zwar neu formatiert, aber
+ // wenn sich dessen Hoehe nicht aendert bekommt die Formatierung die
+ // Aenderung der Gesaamthoehe des Textes zu spaet mit...
+ for ( USHORT n = nParagraphTobeDeleted; n < GetParaPortions().Count(); n++ )
+ {
+ ParaPortion* pPP = GetParaPortions().GetObject( n );
+ pPP->MarkSelectionInvalid( 0, pPP->GetNode()->Len() );
+ pPP->GetLines().Reset();
+ }
+ }
+
+ TextModified();
+
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::DeleteLeftOrRight( const EditSelection& rSel, BYTE nMode, BYTE nDelMode )
+{
+ DBG_ASSERT( !EditSelection( rSel ).DbgIsBuggy( aEditDoc ), "Index im Wald in DeleteLeftOrRight" );
+
+ if ( rSel.HasRange() ) // dann nur Sel. loeschen
+ return ImpDeleteSelection( rSel );
+
+ const EditPaM aCurPos( rSel.Max() );
+ EditPaM aDelStart( aCurPos );
+ EditPaM aDelEnd( aCurPos );
+ if ( nMode == DEL_LEFT )
+ {
+ if ( nDelMode == DELMODE_SIMPLE )
+ {
+ aDelStart = CursorLeft( aCurPos, i18n::CharacterIteratorMode::SKIPCHARACTER );
+ }
+ else if ( nDelMode == DELMODE_RESTOFWORD )
+ {
+ aDelStart = StartOfWord( aCurPos );
+ if ( aDelStart.GetIndex() == aCurPos.GetIndex() )
+ aDelStart = WordLeft( aCurPos );
+ }
+ else // DELMODE_RESTOFCONTENT
+ {
+ aDelStart.SetIndex( 0 );
+ if ( aDelStart == aCurPos )
+ {
+ // kompletter Absatz davor
+ ContentNode* pPrev = GetPrevVisNode( aCurPos.GetNode() );
+ if ( pPrev )
+ aDelStart = EditPaM( pPrev, 0 );
+ }
+ }
+ }
+ else
+ {
+ if ( nDelMode == DELMODE_SIMPLE )
+ {
+ aDelEnd = CursorRight( aCurPos );
+ }
+ else if ( nDelMode == DELMODE_RESTOFWORD )
+ {
+ aDelEnd = EndOfWord( aCurPos );
+ if (aDelEnd.GetIndex() == aCurPos.GetIndex())
+ {
+ xub_StrLen nLen = aCurPos.GetNode()->Len();
+ // end of para?
+ if (aDelEnd.GetIndex() == nLen)
+ aDelEnd = WordLeft( aCurPos );
+ else // there's still sth to delete on the right
+ {
+ aDelEnd = EndOfWord( WordRight( aCurPos ) );
+ // if there'n no next word...
+ if (aDelEnd.GetIndex() == nLen )
+ aDelEnd.SetIndex( nLen );
+ }
+ }
+ }
+ else // DELMODE_RESTOFCONTENT
+ {
+ aDelEnd.SetIndex( aCurPos.GetNode()->Len() );
+ if ( aDelEnd == aCurPos )
+ {
+ // kompletter Absatz dahinter
+ ContentNode* pNext = GetNextVisNode( aCurPos.GetNode() );
+ if ( pNext )
+ aDelEnd = EditPaM( pNext, pNext->Len() );
+ }
+ }
+ }
+
+ // Bei DELMODE_RESTOFCONTENT reicht bei verschiedenen Nodes
+ // kein ConnectParagraphs.
+ if ( ( nDelMode == DELMODE_RESTOFCONTENT ) || ( aDelStart.GetNode() == aDelEnd.GetNode() ) )
+ return ImpDeleteSelection( EditSelection( aDelStart, aDelEnd ) );
+
+ // Jetzt entscheiden, ob noch Selektion loeschen (RESTOFCONTENTS)
+ BOOL bSpecialBackward = ( ( nMode == DEL_LEFT ) && ( nDelMode == DELMODE_SIMPLE ) )
+ ? TRUE : FALSE;
+ if ( aStatus.IsAnyOutliner() )
+ bSpecialBackward = FALSE;
+
+ return ImpConnectParagraphs( aDelStart.GetNode(), aDelEnd.GetNode(), bSpecialBackward );
+}
+
+EditPaM ImpEditEngine::ImpDeleteSelection( EditSelection aSel )
+{
+ if ( !aSel.HasRange() )
+ return aSel.Min();
+
+ aSel.Adjust( aEditDoc );
+ EditPaM aStartPaM( aSel.Min() );
+ EditPaM aEndPaM( aSel.Max() );
+
+ CursorMoved( aStartPaM.GetNode() ); // nur damit neu eingestellte Attribute verschwinden...
+ CursorMoved( aEndPaM.GetNode() ); // nur damit neu eingestellte Attribute verschwinden...
+
+ DBG_ASSERT( aStartPaM.GetIndex() <= aStartPaM.GetNode()->Len(), "Index im Wald in ImpDeleteSelection" );
+ DBG_ASSERT( aEndPaM.GetIndex() <= aEndPaM.GetNode()->Len(), "Index im Wald in ImpDeleteSelection" );
+
+ USHORT nStartNode = aEditDoc.GetPos( aStartPaM.GetNode() );
+ USHORT nEndNode = aEditDoc.GetPos( aEndPaM.GetNode() );
+
+ DBG_ASSERT( nEndNode != USHRT_MAX, "Start > End ?!" );
+ DBG_ASSERT( nStartNode <= nEndNode, "Start > End ?!" );
+
+ // Alle Nodes dazwischen entfernen....
+ for ( ULONG z = nStartNode+1; z < nEndNode; z++ )
+ {
+ // Immer nStartNode+1, wegen Remove()!
+ ImpRemoveParagraph( nStartNode+1 );
+ }
+
+ if ( aStartPaM.GetNode() != aEndPaM.GetNode() )
+ {
+ // Den Rest des StartNodes...
+ USHORT nChars;
+ nChars = aStartPaM.GetNode()->Len() - aStartPaM.GetIndex();
+ ImpRemoveChars( aStartPaM, nChars );
+ ParaPortion* pPortion = FindParaPortion( aStartPaM.GetNode() );
+ DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteSelection(3)" );
+ pPortion->MarkSelectionInvalid( aStartPaM.GetIndex(), aStartPaM.GetNode()->Len() );
+
+ // Den Anfang des EndNodes....
+ nChars = aEndPaM.GetIndex();
+ aEndPaM.SetIndex( 0 );
+ ImpRemoveChars( aEndPaM, nChars );
+ pPortion = FindParaPortion( aEndPaM.GetNode() );
+ DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteSelection(4)" );
+ pPortion->MarkSelectionInvalid( 0, aEndPaM.GetNode()->Len() );
+ // Zusammenfuegen....
+ aStartPaM = ImpConnectParagraphs( aStartPaM.GetNode(), aEndPaM.GetNode() );
+ }
+ else
+ {
+ USHORT nChars;
+ nChars = aEndPaM.GetIndex() - aStartPaM.GetIndex();
+ ImpRemoveChars( aStartPaM, nChars );
+ ParaPortion* pPortion = FindParaPortion( aStartPaM.GetNode() );
+ DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteSelection(5)" );
+ pPortion->MarkInvalid( aEndPaM.GetIndex(), aStartPaM.GetIndex() - aEndPaM.GetIndex() );
+ }
+
+ UpdateSelections();
+ TextModified();
+ return aStartPaM;
+}
+
+void ImpEditEngine::ImpRemoveParagraph( USHORT nPara )
+{
+ ContentNode* pNode = aEditDoc.SaveGetObject( nPara );
+ ContentNode* pNextNode = aEditDoc.SaveGetObject( nPara+1 );
+ ParaPortion* pPortion = GetParaPortions().SaveGetObject( nPara );
+
+ DBG_ASSERT( pNode, "Blinder Node in ImpRemoveParagraph" );
+ DBG_ASSERT( pPortion, "Blinde Portion in ImpRemoveParagraph(2)" );
+
+ DeletedNodeInfo* pInf = new DeletedNodeInfo( (ULONG)pNode, nPara );
+ aDeletedNodes.Insert( pInf, aDeletedNodes.Count() );
+
+ // Der Node wird vom Undo verwaltet und ggf. zerstoert!
+ /* delete */ aEditDoc.Remove( nPara );
+ GetParaPortions().Remove( nPara );
+ delete pPortion;
+
+ if ( IsCallParaInsertedOrDeleted() )
+ {
+ GetEditEnginePtr()->ParagraphDeleted( nPara );
+ }
+
+ // Im folgenden muss ggf. Extra-Space neu ermittelt werden.
+ // Bei ParaAttribsChanged wird leider der Absatz neu formatiert,
+ // aber diese Methode sollte nicht Zeitkritsch sein!
+ if ( pNextNode )
+ ParaAttribsChanged( pNextNode );
+
+#ifndef SVX_LIGHT
+ if ( IsUndoEnabled() && !IsInUndo() )
+ InsertUndo( new EditUndoDelContent( this, pNode, nPara ) );
+ else
+#endif
+ {
+ aEditDoc.RemoveItemsFromPool( pNode );
+ if ( pNode->GetStyleSheet() )
+ EndListening( *pNode->GetStyleSheet(), FALSE );
+ delete pNode;
+ }
+}
+
+EditPaM ImpEditEngine::AutoCorrect( const EditSelection& rCurSel, xub_Unicode c, BOOL bOverwrite )
+{
+ EditSelection aSel( rCurSel );
+#ifndef SVX_LIGHT
+ SvxAutoCorrect* pAutoCorrect = SvxAutoCorrCfg::Get()->GetAutoCorrect();
+ if ( pAutoCorrect )
+ {
+ if ( aSel.HasRange() )
+ aSel = ImpDeleteSelection( rCurSel );
+
+ // #i78661 allow application to turn off capitalization of
+ // start sentence explicitly.
+ // (This is done by setting IsFirstWordCapitalization to FALSE.)
+ BOOL bOldCptlSttSntnc = pAutoCorrect->IsAutoCorrFlag( CptlSttSntnc );
+ if (!IsFirstWordCapitalization())
+ {
+ ESelection aESel( CreateESel(aSel) );
+ EditSelection aFirstWordSel;
+ EditSelection aSecondWordSel;
+ if (aESel.nEndPara == 0) // is this the first para?
+ {
+ // select first word...
+ // start by checking if para starts with word.
+ aFirstWordSel = SelectWord( CreateSel(ESelection()) );
+ if (aFirstWordSel.Min().GetIndex() == 0 && aFirstWordSel.Max().GetIndex() == 0)
+ {
+ // para does not start with word -> select next/first word
+ EditPaM aRightWord( WordRight( aFirstWordSel.Max(), 1 ) );
+ aFirstWordSel = SelectWord( EditSelection( aRightWord ) );
+ }
+
+ // select second word
+ // (sometimes aSel mightnot point to the end of the first word
+ // but to some following char like '.'. ':', ...
+ // In those cases we need aSecondWordSel to see if aSel
+ // will actually effect the first word.)
+ EditPaM aRight2Word( WordRight( aFirstWordSel.Max(), 1 ) );
+ aSecondWordSel = SelectWord( EditSelection( aRight2Word ) );
+ }
+ BOOL bIsFirstWordInFirstPara = aESel.nEndPara == 0 &&
+ aFirstWordSel.Max().GetIndex() <= aSel.Max().GetIndex() &&
+ aSel.Max().GetIndex() <= aSecondWordSel.Min().GetIndex();
+
+ if (bIsFirstWordInFirstPara)
+ pAutoCorrect->SetAutoCorrFlag( CptlSttSntnc, IsFirstWordCapitalization() );
+ }
+
+ ContentNode* pNode = aSel.Max().GetNode();
+ USHORT nIndex = aSel.Max().GetIndex();
+ EdtAutoCorrDoc aAuto( this, pNode, nIndex, c );
+ pAutoCorrect->AutoCorrect( aAuto, *pNode, nIndex, c, !bOverwrite );
+ aSel.Max().SetIndex( aAuto.GetCursor() );
+
+ // #i78661 since the SvxAutoCorrect object used here is
+ // shared we need to reset the value to it's original state.
+ pAutoCorrect->SetAutoCorrFlag( CptlSttSntnc, bOldCptlSttSntnc );
+ }
+#endif // !SVX_LIGHT
+ return aSel.Max();
+}
+
+
+EditPaM ImpEditEngine::InsertText( const EditSelection& rCurSel,
+ xub_Unicode c, BOOL bOverwrite, sal_Bool bIsUserInput )
+{
+ DBG_ASSERT( c != '\t', "Tab bei InsertText ?" );
+ DBG_ASSERT( c != '\n', "Zeilenumbruch bei InsertText ?" );
+
+ EditPaM aPaM( rCurSel.Min() );
+
+ BOOL bDoOverwrite = ( bOverwrite &&
+ ( aPaM.GetIndex() < aPaM.GetNode()->Len() ) ) ? TRUE : FALSE;
+
+ BOOL bUndoAction = ( rCurSel.HasRange() || bDoOverwrite );
+
+ if ( bUndoAction )
+ UndoActionStart( EDITUNDO_INSERT );
+
+ if ( rCurSel.HasRange() )
+ {
+ aPaM = ImpDeleteSelection( rCurSel );
+ }
+ else if ( bDoOverwrite )
+ {
+ // Wenn Selektion, dann nicht auch noch ein Zeichen ueberschreiben!
+ EditSelection aTmpSel( aPaM );
+ aTmpSel.Max().GetIndex()++;
+ DBG_ASSERT( !aTmpSel.DbgIsBuggy( aEditDoc ), "Overwrite: Fehlerhafte Selektion!" );
+ ImpDeleteSelection( aTmpSel );
+ }
+
+ if ( aPaM.GetNode()->Len() < MAXCHARSINPARA )
+ {
+ if (bIsUserInput && IsInputSequenceCheckingRequired( c, rCurSel ))
+ {
+ uno::Reference < i18n::XExtendedInputSequenceChecker > _xISC( ImplGetInputSequenceChecker() );
+ if (!pCTLOptions)
+ pCTLOptions = new SvtCTLOptions;
+
+ if (_xISC.is() || pCTLOptions)
+ {
+ xub_StrLen nTmpPos = aPaM.GetIndex();
+ sal_Int16 nCheckMode = pCTLOptions->IsCTLSequenceCheckingRestricted() ?
+ i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC;
+
+ // the text that needs to be checked is only the one
+ // before the current cursor position
+ rtl::OUString aOldText( aPaM.GetNode()->Copy(0, nTmpPos) );
+ rtl::OUString aNewText( aOldText );
+ if (pCTLOptions->IsCTLSequenceCheckingTypeAndReplace())
+ {
+ /*const xub_StrLen nPrevPos = static_cast< xub_StrLen >*/( _xISC->correctInputSequence( aNewText, nTmpPos - 1, c, nCheckMode ) );
+
+ // find position of first character that has changed
+ sal_Int32 nOldLen = aOldText.getLength();
+ sal_Int32 nNewLen = aNewText.getLength();
+ const sal_Unicode *pOldTxt = aOldText.getStr();
+ const sal_Unicode *pNewTxt = aNewText.getStr();
+ sal_Int32 nChgPos = 0;
+ while ( nChgPos < nOldLen && nChgPos < nNewLen &&
+ pOldTxt[nChgPos] == pNewTxt[nChgPos] )
+ ++nChgPos;
+
+ xub_StrLen nChgLen = static_cast< xub_StrLen >( nNewLen - nChgPos );
+ String aChgText( aNewText.copy( nChgPos ), nChgLen );
+
+ // select text from first pos to be changed to current pos
+ EditSelection aSel( EditPaM( aPaM.GetNode(), (USHORT) nChgPos ), aPaM );
+
+ if (aChgText.Len())
+ return InsertText( aSel, aChgText ); // implicitly handles undo
+ else
+ return aPaM;
+ }
+ else
+ {
+ // should the character be ignored (i.e. not get inserted) ?
+ if (!_xISC->checkInputSequence( aOldText, nTmpPos - 1, c, nCheckMode ))
+ return aPaM; // nothing to be done -> no need for undo
+ }
+ }
+
+ // at this point now we will insert the character 'normally' some lines below...
+ }
+
+ if ( IsUndoEnabled() && !IsInUndo() )
+ {
+ EditUndoInsertChars* pNewUndo = new EditUndoInsertChars( this, CreateEPaM( aPaM ), c );
+ BOOL bTryMerge = ( !bDoOverwrite && ( c != ' ' ) ) ? TRUE : FALSE;
+ InsertUndo( pNewUndo, bTryMerge );
+ }
+
+ aEditDoc.InsertText( (const EditPaM&)aPaM, c );
+ ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
+ DBG_ASSERT( pPortion, "Blinde Portion in InsertText" );
+ pPortion->MarkInvalid( aPaM.GetIndex(), 1 );
+ aPaM.GetIndex()++; // macht EditDoc-Methode nicht mehr
+ }
+
+ TextModified();
+
+ if ( bUndoAction )
+ UndoActionEnd( EDITUNDO_INSERT );
+
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::ImpInsertText( EditSelection aCurSel, const XubString& rStr )
+{
+ UndoActionStart( EDITUNDO_INSERT );
+
+ EditPaM aPaM;
+ if ( aCurSel.HasRange() )
+ aPaM = ImpDeleteSelection( aCurSel );
+ else
+ aPaM = aCurSel.Max();
+
+ EditPaM aCurPaM( aPaM ); // fuers Invalidieren
+
+ XubString aText( rStr );
+ aText.ConvertLineEnd( LINEEND_LF );
+ SfxVoidItem aTabItem( EE_FEATURE_TAB );
+
+ // Konvertiert nach LineSep = \n
+ // Token mit LINE_SEP abfragen,
+ // da der MAC-Compiler aus \n etwas anderes macht!
+
+ USHORT nStart = 0;
+ while ( nStart < aText.Len() )
+ {
+ USHORT nEnd = aText.Search( LINE_SEP, nStart );
+ if ( nEnd == STRING_NOTFOUND )
+ nEnd = aText.Len(); // nicht dereferenzieren!
+
+ // Start == End => Leerzeile
+ if ( nEnd > nStart )
+ {
+ XubString aLine( aText, nStart, nEnd-nStart );
+ xub_StrLen nChars = aPaM.GetNode()->Len() + aLine.Len();
+ if ( nChars > MAXCHARSINPARA )
+ {
+ USHORT nMaxNewChars = MAXCHARSINPARA-aPaM.GetNode()->Len();
+ nEnd -= ( aLine.Len() - nMaxNewChars ); // Dann landen die Zeichen im naechsten Absatz.
+ aLine.Erase( nMaxNewChars ); // Del Rest...
+ }
+#ifndef SVX_LIGHT
+ if ( IsUndoEnabled() && !IsInUndo() )
+ InsertUndo( new EditUndoInsertChars( this, CreateEPaM( aPaM ), aLine ) );
+#endif
+ // Tabs ?
+ if ( aLine.Search( '\t' ) == STRING_NOTFOUND )
+ aPaM = aEditDoc.InsertText( aPaM, aLine );
+ else
+ {
+ USHORT nStart2 = 0;
+ while ( nStart2 < aLine.Len() )
+ {
+ USHORT nEnd2 = aLine.Search( '\t', nStart2 );
+ if ( nEnd2 == STRING_NOTFOUND )
+ nEnd2 = aLine.Len(); // nicht dereferenzieren!
+
+ if ( nEnd2 > nStart2 )
+ aPaM = aEditDoc.InsertText( aPaM, XubString( aLine, nStart2, nEnd2-nStart2 ) );
+ if ( nEnd2 < aLine.Len() )
+ {
+ // aPaM = ImpInsertFeature( EditSelection( aPaM, aPaM ), );
+ aPaM = aEditDoc.InsertFeature( aPaM, aTabItem );
+ }
+ nStart2 = nEnd2+1;
+ }
+ }
+ ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
+ DBG_ASSERT( pPortion, "Blinde Portion in InsertText" );
+ pPortion->MarkInvalid( aCurPaM.GetIndex(), aLine.Len() );
+ }
+ if ( nEnd < aText.Len() )
+ aPaM = ImpInsertParaBreak( aPaM );
+
+ nStart = nEnd+1;
+ }
+
+ UndoActionEnd( EDITUNDO_INSERT );
+
+ TextModified();
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::ImpFastInsertText( EditPaM aPaM, const XubString& rStr )
+{
+ DBG_ASSERT( rStr.Search( 0x0A ) == STRING_NOTFOUND, "FastInsertText: Zeilentrenner nicht erlaubt!" );
+ DBG_ASSERT( rStr.Search( 0x0D ) == STRING_NOTFOUND, "FastInsertText: Zeilentrenner nicht erlaubt!" );
+ DBG_ASSERT( rStr.Search( '\t' ) == STRING_NOTFOUND, "FastInsertText: Features nicht erlaubt!" );
+
+ if ( ( aPaM.GetNode()->Len() + rStr.Len() ) < MAXCHARSINPARA )
+ {
+#ifndef SVX_LIGHT
+ if ( IsUndoEnabled() && !IsInUndo() )
+ InsertUndo( new EditUndoInsertChars( this, CreateEPaM( aPaM ), rStr ) );
+#endif
+
+ aPaM = aEditDoc.InsertText( aPaM, rStr );
+ TextModified();
+ }
+ else
+ {
+ aPaM = ImpInsertText( aPaM, rStr );
+ }
+
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::ImpInsertFeature( EditSelection aCurSel, const SfxPoolItem& rItem )
+{
+ EditPaM aPaM;
+ if ( aCurSel.HasRange() )
+ aPaM = ImpDeleteSelection( aCurSel );
+ else
+ aPaM = aCurSel.Max();
+
+ if ( aPaM.GetIndex() >= 0xfffe )
+ return aPaM;
+
+#ifndef SVX_LIGHT
+ if ( IsUndoEnabled() && !IsInUndo() )
+ InsertUndo( new EditUndoInsertFeature( this, CreateEPaM( aPaM ), rItem ) );
+#endif
+ aPaM = aEditDoc.InsertFeature( aPaM, rItem );
+
+ ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
+ DBG_ASSERT( pPortion, "Blinde Portion in InsertFeature" );
+ pPortion->MarkInvalid( aPaM.GetIndex()-1, 1 );
+
+ TextModified();
+
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::ImpInsertParaBreak( const EditSelection& rCurSel, BOOL bKeepEndingAttribs )
+{
+ EditPaM aPaM;
+ if ( rCurSel.HasRange() )
+ aPaM = ImpDeleteSelection( rCurSel );
+ else
+ aPaM = rCurSel.Max();
+
+ return ImpInsertParaBreak( aPaM, bKeepEndingAttribs );
+}
+
+EditPaM ImpEditEngine::ImpInsertParaBreak( const EditPaM& rPaM, BOOL bKeepEndingAttribs )
+{
+ if ( aEditDoc.Count() >= 0xFFFE )
+ {
+ DBG_ERROR( "Can't process more than 64K paragraphs!" );
+ return rPaM;
+ }
+
+#ifndef SVX_LIGHT
+ if ( IsUndoEnabled() && !IsInUndo() )
+ InsertUndo( new EditUndoSplitPara( this, aEditDoc.GetPos( rPaM.GetNode() ), rPaM.GetIndex() ) );
+#endif
+
+ EditPaM aPaM( aEditDoc.InsertParaBreak( rPaM, bKeepEndingAttribs ) );
+
+#ifndef SVX_LIGHT
+ if ( GetStatus().DoOnlineSpelling() )
+ {
+ xub_StrLen nEnd = rPaM.GetNode()->Len();
+ aPaM.GetNode()->CreateWrongList();
+ WrongList* pLWrongs = rPaM.GetNode()->GetWrongList();
+ WrongList* pRWrongs = aPaM.GetNode()->GetWrongList();
+ // Falschgeschriebene Woerter ruebernehmen:
+ USHORT nLWrongs = pLWrongs->Count();
+ for ( USHORT nW = 0; nW < nLWrongs; nW++ )
+ {
+ WrongRange& rWrong = pLWrongs->GetObject( nW );
+ // Nur wenn wirklich dahinter, ein ueberlappendes wird beim Spell korrigiert
+ if ( rWrong.nStart > nEnd )
+ {
+ pRWrongs->InsertWrong( rWrong, pRWrongs->Count() );
+ WrongRange& rRWrong = pRWrongs->GetObject( pRWrongs->Count() - 1 );
+ rRWrong.nStart = rRWrong.nStart - nEnd;
+ rRWrong.nEnd = rRWrong.nEnd - nEnd;
+ }
+ else if ( ( rWrong.nStart < nEnd ) && ( rWrong.nEnd > nEnd ) )
+ rWrong.nEnd = nEnd;
+ }
+ USHORT nInv = nEnd ? nEnd-1 : nEnd;
+ if ( nEnd )
+ pLWrongs->MarkInvalid( nInv, nEnd );
+ else
+ pLWrongs->SetValid();
+ pRWrongs->SetValid(); // sonst 0 - 0xFFFF
+ pRWrongs->MarkInvalid( 0, 1 ); // Nur das erste Wort testen
+ }
+#endif // !SVX_LIGHT
+
+
+ ParaPortion* pPortion = FindParaPortion( rPaM.GetNode() );
+ DBG_ASSERT( pPortion, "Blinde Portion in ImpInsertParaBreak" );
+ pPortion->MarkInvalid( rPaM.GetIndex(), 0 );
+
+ // Optimieren: Nicht unnoetig viele GetPos auf die Listen ansetzen!
+ // Hier z.B. bei Undo, aber auch in allen anderen Methoden.
+ USHORT nPos = GetParaPortions().GetPos( pPortion );
+ ParaPortion* pNewPortion = new ParaPortion( aPaM.GetNode() );
+ GetParaPortions().Insert( pNewPortion, nPos + 1 );
+ ParaAttribsChanged( pNewPortion->GetNode() );
+ if ( IsCallParaInsertedOrDeleted() )
+ GetEditEnginePtr()->ParagraphInserted( nPos+1 );
+
+ CursorMoved( rPaM.GetNode() ); // falls leeres Attribut entstanden.
+ TextModified();
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::ImpFastInsertParagraph( USHORT nPara )
+{
+#ifndef SVX_LIGHT
+ if ( IsUndoEnabled() && !IsInUndo() )
+ {
+ if ( nPara )
+ {
+ DBG_ASSERT( aEditDoc.SaveGetObject( nPara-1 ), "FastInsertParagraph: Prev existiert nicht" );
+ InsertUndo( new EditUndoSplitPara( this, nPara-1, aEditDoc.GetObject( nPara-1 )->Len() ) );
+ }
+ else
+ InsertUndo( new EditUndoSplitPara( this, 0, 0 ) );
+ }
+#endif
+
+ ContentNode* pNode = new ContentNode( aEditDoc.GetItemPool() );
+ // Falls FlatMode, wird spaeter kein Font eingestellt:
+ pNode->GetCharAttribs().GetDefFont() = aEditDoc.GetDefFont();
+
+#ifndef SVX_LIGHT
+ if ( GetStatus().DoOnlineSpelling() )
+ pNode->CreateWrongList();
+#endif // !SVX_LIGHT
+
+ aEditDoc.Insert( pNode, nPara );
+
+ ParaPortion* pNewPortion = new ParaPortion( pNode );
+ GetParaPortions().Insert( pNewPortion, nPara );
+ if ( IsCallParaInsertedOrDeleted() )
+ GetEditEnginePtr()->ParagraphInserted( nPara );
+
+ return EditPaM( pNode, 0 );
+}
+
+EditPaM ImpEditEngine::InsertParaBreak( EditSelection aCurSel )
+{
+ EditPaM aPaM( ImpInsertParaBreak( aCurSel ) );
+ if ( aStatus.DoAutoIndenting() )
+ {
+ USHORT nPara = aEditDoc.GetPos( aPaM.GetNode() );
+ DBG_ASSERT( nPara > 0, "AutoIndenting: Fehler!" );
+ XubString aPrevParaText( GetEditDoc().GetParaAsString( nPara-1 ) );
+ USHORT n = 0;
+ while ( ( n < aPrevParaText.Len() ) &&
+ ( ( aPrevParaText.GetChar(n) == ' ' ) || ( aPrevParaText.GetChar(n) == '\t' ) ) )
+ {
+ if ( aPrevParaText.GetChar(n) == '\t' )
+ aPaM = ImpInsertFeature( aPaM, SfxVoidItem( EE_FEATURE_TAB ) );
+ else
+ aPaM = ImpInsertText( aPaM, aPrevParaText.GetChar(n) );
+ n++;
+ }
+
+ }
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::InsertTab( EditSelection aCurSel )
+{
+ EditPaM aPaM( ImpInsertFeature( aCurSel, SfxVoidItem( EE_FEATURE_TAB ) ) );
+ return aPaM;
+}
+
+EditPaM ImpEditEngine::InsertField( EditSelection aCurSel, const SvxFieldItem& rFld )
+{
+ EditPaM aPaM( ImpInsertFeature( aCurSel, rFld ) );
+ return aPaM;
+}
+
+BOOL ImpEditEngine::UpdateFields()
+{
+ BOOL bChanges = FALSE;
+ USHORT nParas = GetEditDoc().Count();
+ for ( USHORT nPara = 0; nPara < nParas; nPara++ )
+ {
+ BOOL bChangesInPara = FALSE;
+ ContentNode* pNode = GetEditDoc().GetObject( nPara );
+ DBG_ASSERT( pNode, "NULL-Pointer im Doc" );
+ CharAttribArray& rAttribs = pNode->GetCharAttribs().GetAttribs();
+// USHORT nAttrs = rAttribs.Count();
+ for ( USHORT nAttr = 0; nAttr < rAttribs.Count(); nAttr++ )
+ {
+ EditCharAttrib* pAttr = rAttribs[nAttr];
+ if ( pAttr->Which() == EE_FEATURE_FIELD )
+ {
+ EditCharAttribField* pField = (EditCharAttribField*)pAttr;
+ EditCharAttribField* pCurrent = new EditCharAttribField( *pField );
+ pField->Reset();
+
+ if ( aStatus.MarkFields() )
+ pField->GetFldColor() = new Color( GetColorConfig().GetColorValue( svtools::WRITERFIELDSHADINGS ).nColor );
+
+ XubString aFldValue = GetEditEnginePtr()->CalcFieldValue(
+ (const SvxFieldItem&)*pField->GetItem(),
+ nPara, pField->GetStart(),
+ pField->GetTxtColor(), pField->GetFldColor() );
+ pField->GetFieldValue() = aFldValue;
+ if ( *pField != *pCurrent )
+ {
+ bChanges = TRUE;
+ bChangesInPara = TRUE;
+ }
+ delete pCurrent;
+ }
+ }
+ if ( bChangesInPara )
+ {
+ // ggf. etwas genauer invalidieren.
+ ParaPortion* pPortion = GetParaPortions().GetObject( nPara );
+ DBG_ASSERT( pPortion, "NULL-Pointer im Doc" );
+ pPortion->MarkSelectionInvalid( 0, pNode->Len() );
+ }
+ }
+ return bChanges;
+}
+
+EditPaM ImpEditEngine::InsertLineBreak( EditSelection aCurSel )
+{
+ EditPaM aPaM( ImpInsertFeature( aCurSel, SfxVoidItem( EE_FEATURE_LINEBR ) ) );
+ return aPaM;
+}
+
+// ----------------------------------------------------------------------
+// Hilfsfunktionen
+// ----------------------------------------------------------------------
+Rectangle ImpEditEngine::PaMtoEditCursor( EditPaM aPaM, USHORT nFlags )
+{
+ DBG_ASSERT( GetUpdateMode(), "Darf bei Update=FALSE nicht erreicht werden: PaMtoEditCursor" );
+
+ Rectangle aEditCursor;
+ long nY = 0;
+ for ( USHORT nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
+ {
+ ParaPortion* pPortion = GetParaPortions().GetObject(nPortion);
+ ContentNode* pNode = pPortion->GetNode();
+ DBG_ASSERT( pNode, "Ungueltiger Node in Portion!" );
+ if ( pNode != aPaM.GetNode() )
+ {
+ nY += pPortion->GetHeight();
+ }
+ else
+ {
+ aEditCursor = GetEditCursor( pPortion, aPaM.GetIndex(), nFlags );
+ aEditCursor.Top() += nY;
+ aEditCursor.Bottom() += nY;
+ return aEditCursor;
+ }
+ }
+ DBG_ERROR( "Portion nicht gefunden!" );
+ return aEditCursor;
+}
+
+EditPaM ImpEditEngine::GetPaM( Point aDocPos, BOOL bSmart )
+{
+ DBG_ASSERT( GetUpdateMode(), "Darf bei Update=FALSE nicht erreicht werden: GetPaM" );
+
+ long nY = 0;
+ long nTmpHeight;
+ EditPaM aPaM;
+ USHORT nPortion;
+ for ( nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
+ {
+ ParaPortion* pPortion = GetParaPortions().GetObject(nPortion);
+ nTmpHeight = pPortion->GetHeight(); // sollte auch bei !bVisible richtig sein!
+ nY += nTmpHeight;
+ if ( nY > aDocPos.Y() )
+ {
+ nY -= nTmpHeight;
+ aDocPos.Y() -= nY;
+ // unsichtbare Portions ueberspringen:
+ while ( pPortion && !pPortion->IsVisible() )
+ {
+ nPortion++;
+ pPortion = GetParaPortions().SaveGetObject( nPortion );
+ }
+ DBG_ASSERT( pPortion, "Keinen sichtbaren Absatz gefunden: GetPaM" );
+ aPaM = GetPaM( pPortion, aDocPos, bSmart );
+ return aPaM;
+
+ }
+ }
+ // Dann den letzten sichtbaren Suchen:
+ nPortion = GetParaPortions().Count()-1;
+ while ( nPortion && !GetParaPortions()[nPortion]->IsVisible() )
+ nPortion--;
+
+ DBG_ASSERT( GetParaPortions()[nPortion]->IsVisible(), "Keinen sichtbaren Absatz gefunden: GetPaM" );
+ aPaM.SetNode( GetParaPortions()[nPortion]->GetNode() );
+ aPaM.SetIndex( GetParaPortions()[nPortion]->GetNode()->Len() );
+ return aPaM;
+}
+
+sal_uInt32 ImpEditEngine::GetTextHeight() const
+{
+ DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=FALSE nicht verwendet werden: GetTextHeight" );
+ DBG_ASSERT( IsFormatted() || IsFormatting(), "GetTextHeight: Nicht formatiert" );
+ return nCurTextHeight;
+}
+
+sal_uInt32 ImpEditEngine::CalcTextWidth( BOOL bIgnoreExtraSpace )
+{
+ // Wenn noch nicht formatiert und nicht gerade dabei.
+ // Wird in der Formatierung bei AutoPageSize gerufen.
+ if ( !IsFormatted() && !IsFormatting() )
+ FormatDoc();
+
+ EditLine* pLine;
+
+ long nMaxWidth = 0;
+ long nCurWidth = 0;
+
+ // --------------------------------------------------
+ // Ueber alle Absaetze...
+ // --------------------------------------------------
+ USHORT nParas = GetParaPortions().Count();
+// USHORT nBiggestPara = 0;
+// USHORT nBiggestLine = 0;
+ for ( USHORT nPara = 0; nPara < nParas; nPara++ )
+ {
+ ParaPortion* pPortion = GetParaPortions().GetObject( nPara );
+ if ( pPortion->IsVisible() )
+ {
+ const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pPortion->GetNode() );
+ sal_Int32 nSpaceBeforeAndMinLabelWidth = GetSpaceBeforeAndMinLabelWidth( pPortion->GetNode() );
+
+ // --------------------------------------------------
+ // Ueber die Zeilen des Absatzes...
+ // --------------------------------------------------
+ ULONG nLines = pPortion->GetLines().Count();
+ for ( USHORT nLine = 0; nLine < nLines; nLine++ )
+ {
+ pLine = pPortion->GetLines().GetObject( nLine );
+ DBG_ASSERT( pLine, "NULL-Pointer im Zeileniterator in CalcWidth" );
+ // nCurWidth = pLine->GetStartPosX();
+ // Bei Center oder Right haengt die breite von der
+ // Papierbreite ab, hier nicht erwuenscht.
+ // Am besten generell nicht auf StartPosX verlassen,
+ // es muss auch die rechte Einrueckung beruecksichtigt werden!
+ nCurWidth = GetXValue( rLRItem.GetTxtLeft() + nSpaceBeforeAndMinLabelWidth );
+ if ( nLine == 0 )
+ {
+ long nFI = GetXValue( rLRItem.GetTxtFirstLineOfst() );
+ nCurWidth -= nFI;
+ if ( pPortion->GetBulletX() > nCurWidth )
+ {
+ nCurWidth += nFI; // LI?
+ if ( pPortion->GetBulletX() > nCurWidth )
+ nCurWidth = pPortion->GetBulletX();
+ }
+ }
+ nCurWidth += GetXValue( rLRItem.GetRight() );
+ nCurWidth += CalcLineWidth( pPortion, pLine, bIgnoreExtraSpace );
+ if ( nCurWidth > nMaxWidth )
+ {
+ nMaxWidth = nCurWidth;
+ }
+ }
+ }
+ }
+ if ( nMaxWidth < 0 )
+ nMaxWidth = 0;
+
+ nMaxWidth++; // Ein breiter, da in CreateLines bei >= umgebrochen wird.
+ return (sal_uInt32)nMaxWidth;
+}
+
+sal_uInt32 ImpEditEngine::CalcLineWidth( ParaPortion* pPortion, EditLine* pLine, BOOL bIgnoreExtraSpace )
+{
+ USHORT nPara = GetEditDoc().GetPos( pPortion->GetNode() );
+
+ // #114278# Saving both layout mode and language (since I'm
+ // potentially changing both)
+ GetRefDevice()->Push( PUSH_TEXTLAYOUTMODE|PUSH_TEXTLANGUAGE );
+
+ ImplInitLayoutMode( GetRefDevice(), nPara, 0xFFFF );
+
+ SvxAdjust eJustification = GetJustification( nPara );
+
+ // Berechnung der Breite ohne die Indents...
+ sal_uInt32 nWidth = 0;
+ USHORT nPos = pLine->GetStart();
+ for ( USHORT nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ )
+ {
+ TextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( nTP );
+ switch ( pTextPortion->GetKind() )
+ {
+ case PORTIONKIND_FIELD:
+ case PORTIONKIND_HYPHENATOR:
+ case PORTIONKIND_TAB:
+ {
+ nWidth += pTextPortion->GetSize().Width();
+ }
+ break;
+ case PORTIONKIND_TEXT:
+ {
+ if ( ( eJustification != SVX_ADJUST_BLOCK ) || ( !bIgnoreExtraSpace ) )
+ {
+ nWidth += pTextPortion->GetSize().Width();
+ }
+ else
+ {
+ SvxFont aTmpFont( pPortion->GetNode()->GetCharAttribs().GetDefFont() );
+ SeekCursor( pPortion->GetNode(), nPos+1, aTmpFont );
+ aTmpFont.SetPhysFont( GetRefDevice() );
+ ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
+ nWidth += aTmpFont.QuickGetTextSize( GetRefDevice(), *pPortion->GetNode(), nPos, pTextPortion->GetLen(), NULL ).Width();
+ }
+ }
+ break;
+ }
+ nPos = nPos + pTextPortion->GetLen();
+ }
+
+ GetRefDevice()->Pop();
+
+ return nWidth;
+}
+
+sal_uInt32 ImpEditEngine::CalcTextHeight()
+{
+ DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=FALSE nicht verwendet werden: CalcTextHeight" );
+ sal_uInt32 nY = 0;
+ for ( USHORT nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
+ nY += GetParaPortions()[nPortion]->GetHeight();
+ return nY;
+}
+
+USHORT ImpEditEngine::GetLineCount( USHORT nParagraph ) const
+{
+ DBG_ASSERT( nParagraph < GetParaPortions().Count(), "GetLineCount: Out of range" );
+ ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
+ DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetLineCount" );
+ if ( pPPortion )
+ return pPPortion->GetLines().Count();
+
+ return 0xFFFF;
+}
+
+xub_StrLen ImpEditEngine::GetLineLen( USHORT nParagraph, USHORT nLine ) const
+{
+ DBG_ASSERT( nParagraph < GetParaPortions().Count(), "GetLineLen: Out of range" );
+ ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
+ DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetLineLen" );
+ if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) )
+ {
+ EditLine* pLine = pPPortion->GetLines().GetObject( nLine );
+ DBG_ASSERT( pLine, "Zeile nicht gefunden: GetLineHeight" );
+ return pLine->GetLen();
+ }
+
+ return 0xFFFF;
+}
+
+void ImpEditEngine::GetLineBoundaries( /*out*/USHORT &rStart, /*out*/USHORT &rEnd, USHORT nParagraph, USHORT nLine ) const
+{
+ DBG_ASSERT( nParagraph < GetParaPortions().Count(), "GetLineCount: Out of range" );
+ ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
+ DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetLineBoundaries" );
+ rStart = rEnd = 0xFFFF; // default values in case of error
+ if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) )
+ {
+ EditLine* pLine = pPPortion->GetLines().GetObject( nLine );
+ DBG_ASSERT( pLine, "Zeile nicht gefunden: GetLineBoundaries" );
+ rStart = pLine->GetStart();
+ rEnd = pLine->GetEnd();
+ }
+}
+
+USHORT ImpEditEngine::GetLineNumberAtIndex( USHORT nPara, USHORT nIndex ) const
+{
+ USHORT nLineNo = 0xFFFF;
+ ContentNode* pNode = GetEditDoc().SaveGetObject( nPara );
+ DBG_ASSERT( pNode, "GetLineNumberAtIndex: invalid paragraph index" );
+ if (pNode)
+ {
+ // we explicitly allow for the index to point at the character right behind the text
+ const bool bValidIndex = /*0 <= nIndex &&*/ nIndex <= pNode->Len();
+ DBG_ASSERT( bValidIndex, "GetLineNumberAtIndex: invalid index" );
+ const USHORT nLineCount = GetLineCount( nPara );
+ if (nIndex == pNode->Len())
+ nLineNo = nLineCount > 0 ? nLineCount - 1 : 0;
+ else if (bValidIndex) // nIndex < pNode->Len()
+ {
+ USHORT nStart = USHRT_MAX, nEnd = USHRT_MAX;
+ for (USHORT i = 0; i < nLineCount && nLineNo == 0xFFFF; ++i)
+ {
+ GetLineBoundaries( nStart, nEnd, nPara, i );
+ if (nStart <= nIndex && nIndex < nEnd)
+ nLineNo = i;
+ }
+ }
+ }
+ return nLineNo;
+}
+
+USHORT ImpEditEngine::GetLineHeight( USHORT nParagraph, USHORT nLine )
+{
+ DBG_ASSERT( nParagraph < GetParaPortions().Count(), "GetLineCount: Out of range" );
+ ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
+ DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetLineHeight" );
+ if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) )
+ {
+ EditLine* pLine = pPPortion->GetLines().GetObject( nLine );
+ DBG_ASSERT( pLine, "Zeile nicht gefunden: GetLineHeight" );
+ return pLine->GetHeight();
+ }
+
+ return 0xFFFF;
+}
+
+sal_uInt32 ImpEditEngine::GetParaHeight( USHORT nParagraph )
+{
+ sal_uInt32 nHeight = 0;
+
+ ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
+ DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetParaHeight" );
+
+ if ( pPPortion )
+ nHeight = pPPortion->GetHeight();
+
+ return nHeight;
+}
+
+void ImpEditEngine::UpdateSelections()
+{
+ USHORT nInvNodes = aDeletedNodes.Count();
+
+ // Pruefen, ob eine der Selektionen auf einem geloeschten Node steht...
+ // Wenn der Node gueltig ist, muss noch der Index geprueft werden!
+ for ( USHORT nView = 0; nView < aEditViews.Count(); nView++ )
+ {
+ EditView* pView = aEditViews.GetObject(nView);
+ DBG_CHKOBJ( pView, EditView, 0 );
+ EditSelection aCurSel( pView->pImpEditView->GetEditSelection() );
+ BOOL bChanged = FALSE;
+ for ( USHORT n = 0; n < nInvNodes; n++ )
+ {
+ DeletedNodeInfo* pInf = aDeletedNodes.GetObject( n );
+ if ( ( ( ULONG )(aCurSel.Min().GetNode()) == pInf->GetInvalidAdress() ) ||
+ ( ( ULONG )(aCurSel.Max().GetNode()) == pInf->GetInvalidAdress() ) )
+ {
+ // ParaPortions verwenden, da jetzt auch versteckte
+ // Absaetze beruecksichtigt werden muessen!
+ USHORT nPara = pInf->GetPosition();
+ ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nPara );
+ if ( !pPPortion ) // letzter Absatz
+ {
+ nPara = GetParaPortions().Count()-1;
+ pPPortion = GetParaPortions().GetObject( nPara );
+ }
+ DBG_ASSERT( pPPortion, "Leeres Document in UpdateSelections ?" );
+ // Nicht aus einem verstecktem Absatz landen:
+ USHORT nCurPara = nPara;
+ USHORT nLastPara = GetParaPortions().Count()-1;
+ while ( nPara <= nLastPara && !GetParaPortions()[nPara]->IsVisible() )
+ nPara++;
+ if ( nPara > nLastPara ) // dann eben rueckwaerts...
+ {
+ nPara = nCurPara;
+ while ( nPara && !GetParaPortions()[nPara]->IsVisible() )
+ nPara--;
+ }
+ DBG_ASSERT( GetParaPortions()[nPara]->IsVisible(), "Keinen sichtbaren Absatz gefunden: UpdateSelections" );
+
+ ParaPortion* pParaPortion = GetParaPortions()[nPara];
+ EditSelection aTmpSelection( EditPaM( pParaPortion->GetNode(), 0 ) );
+ pView->pImpEditView->SetEditSelection( aTmpSelection );
+ bChanged=TRUE;
+ break; // for-Schleife
+ }
+ }
+ if ( !bChanged )
+ {
+ // Index prueffen, falls Node geschrumpft.
+ if ( aCurSel.Min().GetIndex() > aCurSel.Min().GetNode()->Len() )
+ {
+ aCurSel.Min().GetIndex() = aCurSel.Min().GetNode()->Len();
+ pView->pImpEditView->SetEditSelection( aCurSel );
+ }
+ if ( aCurSel.Max().GetIndex() > aCurSel.Max().GetNode()->Len() )
+ {
+ aCurSel.Max().GetIndex() = aCurSel.Max().GetNode()->Len();
+ pView->pImpEditView->SetEditSelection( aCurSel );
+ }
+ }
+ }
+
+ // Loeschen...
+ for ( USHORT n = 0; n < nInvNodes; n++ )
+ {
+ DeletedNodeInfo* pInf = aDeletedNodes.GetObject( n );
+ delete pInf;
+ }
+ aDeletedNodes.Remove( 0, aDeletedNodes.Count() );
+}
+
+EditSelection ImpEditEngine::ConvertSelection( USHORT nStartPara, USHORT nStartPos,
+ USHORT nEndPara, USHORT nEndPos ) const
+{
+ EditSelection aNewSelection;
+
+ // Start...
+ ContentNode* pNode = aEditDoc.SaveGetObject( nStartPara );
+ USHORT nIndex = nStartPos;
+ if ( !pNode )
+ {
+ pNode = aEditDoc[ aEditDoc.Count()-1 ];
+ nIndex = pNode->Len();
+ }
+ else if ( nIndex > pNode->Len() )
+ nIndex = pNode->Len();
+
+ aNewSelection.Min().SetNode( pNode );
+ aNewSelection.Min().SetIndex( nIndex );
+
+ // End...
+ pNode = aEditDoc.SaveGetObject( nEndPara );
+ nIndex = nEndPos;
+ if ( !pNode )
+ {
+ pNode = aEditDoc[ aEditDoc.Count()-1 ];
+ nIndex = pNode->Len();
+ }
+ else if ( nIndex > pNode->Len() )
+ nIndex = pNode->Len();
+
+ aNewSelection.Max().SetNode( pNode );
+ aNewSelection.Max().SetIndex( nIndex );
+
+ return aNewSelection;
+}
+
+EditSelection ImpEditEngine::MatchGroup( const EditSelection& rSel )
+{
+ EditSelection aMatchSel;
+ EditSelection aTmpSel( rSel );
+ aTmpSel.Adjust( GetEditDoc() );
+ if ( ( aTmpSel.Min().GetNode() != aTmpSel.Max().GetNode() ) ||
+ ( ( aTmpSel.Max().GetIndex() - aTmpSel.Min().GetIndex() ) > 1 ) )
+ {
+ return aMatchSel;
+ }
+
+ USHORT nPos = aTmpSel.Min().GetIndex();
+ ContentNode* pNode = aTmpSel.Min().GetNode();
+ if ( nPos >= pNode->Len() )
+ return aMatchSel;
+
+ USHORT nMatchChar = aGroupChars.Search( pNode->GetChar( nPos ) );
+ if ( nMatchChar != STRING_NOTFOUND )
+ {
+ USHORT nNode = aEditDoc.GetPos( pNode );
+ if ( ( nMatchChar % 2 ) == 0 )
+ {
+ // Vorwaerts suchen...
+ xub_Unicode nSC = aGroupChars.GetChar( nMatchChar );
+ DBG_ASSERT( aGroupChars.Len() > (nMatchChar+1), "Ungueltige Gruppe von MatchChars!" );
+ xub_Unicode nEC = aGroupChars.GetChar( nMatchChar+1 );
+
+ USHORT nCur = aTmpSel.Min().GetIndex()+1;
+ USHORT nLevel = 1;
+ while ( pNode && nLevel )
+ {
+ XubString& rStr = *pNode;
+ while ( nCur < rStr.Len() )
+ {
+ if ( rStr.GetChar( nCur ) == nSC )
+ nLevel++;
+ else if ( rStr.GetChar( nCur ) == nEC )
+ {
+ nLevel--;
+ if ( !nLevel )
+ break; // while nCur...
+ }
+ nCur++;
+ }
+
+ if ( nLevel )
+ {
+ nNode++;
+ pNode = nNode < aEditDoc.Count() ? aEditDoc.GetObject( nNode ) : 0;
+ nCur = 0;
+ }
+ }
+ if ( nLevel == 0 ) // gefunden
+ {
+ aMatchSel.Min() = aTmpSel.Min();
+ aMatchSel.Max() = EditPaM( pNode, nCur+1 );
+ }
+ }
+ else
+ {
+ // Rueckwaerts suchen...
+ xub_Unicode nEC = aGroupChars.GetChar( nMatchChar );
+ xub_Unicode nSC = aGroupChars.GetChar( nMatchChar-1 );
+
+ USHORT nCur = aTmpSel.Min().GetIndex()-1;
+ USHORT nLevel = 1;
+ while ( pNode && nLevel )
+ {
+ if ( pNode->Len() )
+ {
+ XubString& rStr = *pNode;
+ while ( nCur )
+ {
+ if ( rStr.GetChar( nCur ) == nSC )
+ {
+ nLevel--;
+ if ( !nLevel )
+ break; // while nCur...
+ }
+ else if ( rStr.GetChar( nCur ) == nEC )
+ nLevel++;
+
+ nCur--;
+ }
+ }
+
+ if ( nLevel )
+ {
+ pNode = nNode ? aEditDoc.GetObject( --nNode ) : 0;
+ if ( pNode )
+ nCur = pNode->Len()-1; // egal ob negativ, weil if Len()
+ }
+ }
+
+ if ( nLevel == 0 ) // gefunden
+ {
+ aMatchSel.Min() = aTmpSel.Min();
+ aMatchSel.Min().GetIndex()++; // hinter das Zeichen
+ aMatchSel.Max() = EditPaM( pNode, nCur );
+ }
+ }
+ }
+ return aMatchSel;
+}
+
+void ImpEditEngine::StopSelectionMode()
+{
+ if ( ( IsInSelectionMode() || aSelEngine.IsInSelection() ) && pActiveView )
+ {
+ pActiveView->pImpEditView->DrawSelection(); // Wegzeichnen...
+ EditSelection aSel( pActiveView->pImpEditView->GetEditSelection() );
+ aSel.Min() = aSel.Max();
+ pActiveView->pImpEditView->SetEditSelection( aSel );
+ pActiveView->ShowCursor();
+ aSelEngine.Reset();
+ bInSelection = FALSE;
+ }
+}
+
+void ImpEditEngine::SetActiveView( EditView* pView )
+{
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ // Eigentlich waere jetzt ein bHasVisSel und HideSelection notwendig !!!
+
+ if ( pView == pActiveView )
+ return;
+
+ if ( pActiveView && pActiveView->HasSelection() )
+ pActiveView->pImpEditView->DrawSelection(); // Wegzeichnen...
+
+ pActiveView = pView;
+
+ if ( pActiveView && pActiveView->HasSelection() )
+ pActiveView->pImpEditView->DrawSelection(); // Wegzeichnen...
+
+ // NN: Quick fix for #78668#:
+ // When editing of a cell in Calc is ended, the edit engine is not deleted,
+ // only the edit views are removed. If mpIMEInfos is still set in that case,
+ // mpIMEInfos->aPos points to an invalid selection.
+ // -> reset mpIMEInfos now
+ // (probably something like this is necessary whenever the content is modified
+ // from the outside)
+
+ if ( !pView && mpIMEInfos )
+ {
+ delete mpIMEInfos;
+ mpIMEInfos = NULL;
+ }
+}
+
+uno::Reference< datatransfer::XTransferable > ImpEditEngine::CreateTransferable( const EditSelection& rSelection ) const
+{
+#ifndef SVX_LIGHT
+ EditSelection aSelection( rSelection );
+ aSelection.Adjust( GetEditDoc() );
+
+ EditDataObject* pDataObj = new EditDataObject;
+ uno::Reference< datatransfer::XTransferable > xDataObj;
+ xDataObj = pDataObj;
+
+ XubString aText( GetSelected( aSelection ) );
+ aText.ConvertLineEnd(); // Systemspezifisch
+ pDataObj->GetString() = aText;
+
+ SvxFontItem::EnableStoreUnicodeNames( TRUE );
+ WriteBin( pDataObj->GetStream(), aSelection, TRUE );
+ pDataObj->GetStream().Seek( 0 );
+ SvxFontItem::EnableStoreUnicodeNames( FALSE );
+
+ ((ImpEditEngine*)this)->WriteRTF( pDataObj->GetRTFStream(), aSelection );
+ pDataObj->GetRTFStream().Seek( 0 );
+
+ if ( ( aSelection.Min().GetNode() == aSelection.Max().GetNode() )
+ && ( aSelection.Max().GetIndex() == (aSelection.Min().GetIndex()+1) ) )
+ {
+ const EditCharAttrib* pAttr = aSelection.Min().GetNode()->GetCharAttribs().
+ FindFeature( aSelection.Min().GetIndex() );
+ if ( pAttr &&
+ ( pAttr->GetStart() == aSelection.Min().GetIndex() ) &&
+ ( pAttr->Which() == EE_FEATURE_FIELD ) )
+ {
+ const SvxFieldItem* pField = (const SvxFieldItem*)pAttr->GetItem();
+ const SvxFieldData* pFld = pField->GetField();
+ if ( pFld && pFld->ISA( SvxURLField ) )
+ {
+ // Office-Bookmark
+ String aURL( ((const SvxURLField*)pFld)->GetURL() );
+ String aTxt( ((const SvxURLField*)pFld)->GetRepresentation() );
+ pDataObj->GetURL() = aURL;
+ }
+ }
+ }
+
+ return xDataObj;
+#else
+ return uno::Reference< datatransfer::XTransferable >();
+#endif
+}
+
+EditSelection ImpEditEngine::InsertText( uno::Reference< datatransfer::XTransferable >& rxDataObj, const String& rBaseURL, const EditPaM& rPaM, BOOL bUseSpecial )
+{
+ EditSelection aNewSelection( rPaM );
+
+ if ( rxDataObj.is() )
+ {
+ datatransfer::DataFlavor aFlavor;
+ BOOL bDone = FALSE;
+
+ if ( bUseSpecial )
+ {
+ // BIN
+ SotExchange::GetFormatDataFlavor( SOT_FORMATSTR_ID_EDITENGINE, aFlavor );
+ if ( rxDataObj->isDataFlavorSupported( aFlavor ) )
+ {
+ try
+ {
+ uno::Any aData = rxDataObj->getTransferData( aFlavor );
+ uno::Sequence< sal_Int8 > aSeq;
+ aData >>= aSeq;
+ {
+ SvMemoryStream aBinStream( aSeq.getArray(), aSeq.getLength(), STREAM_READ );
+ aNewSelection = Read( aBinStream, rBaseURL, EE_FORMAT_BIN, rPaM );
+ }
+ bDone = TRUE;
+ }
+ catch( const ::com::sun::star::uno::Exception& )
+ {
+ }
+ }
+
+ if ( !bDone )
+ {
+ // Bookmark
+ /*
+ String aURL = ...;
+ String aTxt = ...;
+ // Feld nur einfuegen, wenn Factory vorhanden.
+ if ( ITEMDATA() && ITEMDATA()->GetClassManager().Get( SVX_URLFIELD ) )
+ {
+ SvxFieldItem aField( SvxURLField( aURL, aTxt, SVXURLFORMAT_URL ), EE_FEATURE_FIELD );
+ aNewSelection = InsertField( aPaM, aField );
+ UpdateFields();
+ }
+ else
+ aNewSelection = ImpInsertText( aPaM, aURL );
+ }
+ */
+ }
+ if ( !bDone )
+ {
+ // RTF
+ SotExchange::GetFormatDataFlavor( SOT_FORMAT_RTF, aFlavor );
+ if ( rxDataObj->isDataFlavorSupported( aFlavor ) )
+ {
+ try
+ {
+ uno::Any aData = rxDataObj->getTransferData( aFlavor );
+ uno::Sequence< sal_Int8 > aSeq;
+ aData >>= aSeq;
+ {
+ SvMemoryStream aRTFStream( aSeq.getArray(), aSeq.getLength(), STREAM_READ );
+ aNewSelection = Read( aRTFStream, rBaseURL, EE_FORMAT_RTF, rPaM );
+ }
+ bDone = TRUE;
+ }
+ catch( const ::com::sun::star::uno::Exception& )
+ {
+ }
+ }
+ }
+ if ( !bDone )
+ {
+ // XML ?
+ // Currently, there is nothing like "The" XML format, StarOffice doesn't offer plain XML in Clipboard...
+ }
+ }
+ if ( !bDone )
+ {
+ SotExchange::GetFormatDataFlavor( SOT_FORMAT_STRING, aFlavor );
+ if ( rxDataObj->isDataFlavorSupported( aFlavor ) )
+ {
+ try
+ {
+ uno::Any aData = rxDataObj->getTransferData( aFlavor );
+ ::rtl::OUString aText;
+ aData >>= aText;
+ aNewSelection = ImpInsertText( rPaM, aText );
+ bDone = TRUE;
+ }
+ catch( ... )
+ {
+ ; // #i9286# can happen, even if isDataFlavorSupported returns true...
+ }
+ }
+ }
+ }
+
+ return aNewSelection;
+}
+
+Range ImpEditEngine::GetInvalidYOffsets( ParaPortion* pPortion )
+{
+ Range aRange( 0, 0 );
+
+ if ( pPortion->IsVisible() )
+ {
+ const SvxULSpaceItem& rULSpace = (const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
+ const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
+ USHORT nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
+ ? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
+
+ // erst von vorne...
+ USHORT nFirstInvalid = 0xFFFF;
+ USHORT nLine;
+ for ( nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
+ {
+ EditLine* pL = pPortion->GetLines().GetObject( nLine );
+ if ( pL->IsInvalid() )
+ {
+ nFirstInvalid = nLine;
+ break;
+ }
+ if ( nLine && !aStatus.IsOutliner() ) // nicht die erste Zeile
+ aRange.Min() += nSBL;
+ aRange.Min() += pL->GetHeight();
+ }
+ DBG_ASSERT( nFirstInvalid != 0xFFFF, "Keine ungueltige Zeile gefunden in GetInvalidYOffset(1)" );
+
+
+ // Abgleichen und weiter...
+ aRange.Max() = aRange.Min();
+ aRange.Max() += pPortion->GetFirstLineOffset();
+ if ( nFirstInvalid != 0 ) // Nur wenn nicht die erste Zeile ungueltig
+ aRange.Min() = aRange.Max();
+
+ USHORT nLastInvalid = pPortion->GetLines().Count()-1;
+ for ( nLine = nFirstInvalid; nLine < pPortion->GetLines().Count(); nLine++ )
+ {
+ EditLine* pL = pPortion->GetLines().GetObject( nLine );
+ if ( pL->IsValid() )
+ {
+ nLastInvalid = nLine;
+ break;
+ }
+
+ if ( nLine && !aStatus.IsOutliner() )
+ aRange.Max() += nSBL;
+ aRange.Max() += pL->GetHeight();
+ }
+
+ // MT 07/00 SBL kann jetzt kleiner 100% sein => ggf. die Zeile davor neu ausgeben.
+ if( ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP ) && rLSItem.GetPropLineSpace() &&
+ ( rLSItem.GetPropLineSpace() < 100 ) )
+ {
+ EditLine* pL = pPortion->GetLines().GetObject( nFirstInvalid );
+ long n = pL->GetTxtHeight() * ( 100 - rLSItem.GetPropLineSpace() );
+ n /= 100;
+ aRange.Min() -= n;
+ aRange.Max() += n;
+ }
+
+ if ( ( nLastInvalid == pPortion->GetLines().Count()-1 ) && ( !aStatus.IsOutliner() ) )
+ aRange.Max() += GetYValue( rULSpace.GetLower() );
+ }
+ return aRange;
+}
+
+EditPaM ImpEditEngine::GetPaM( ParaPortion* pPortion, Point aDocPos, BOOL bSmart )
+{
+ DBG_ASSERT( pPortion->IsVisible(), "Wozu GetPaM() bei einem unsichtbaren Absatz?" );
+ DBG_ASSERT( IsFormatted(), "GetPaM: Nicht formatiert" );
+
+ USHORT nCurIndex = 0;
+ EditPaM aPaM;
+ aPaM.SetNode( pPortion->GetNode() );
+
+ const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
+ USHORT nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
+ ? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
+
+ long nY = pPortion->GetFirstLineOffset();
+
+ DBG_ASSERT( pPortion->GetLines().Count(), "Leere ParaPortion in GetPaM!" );
+
+ EditLine* pLine = 0;
+ for ( USHORT nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
+ {
+ EditLine* pTmpLine = pPortion->GetLines().GetObject( nLine );
+ nY += pTmpLine->GetHeight();
+ if ( !aStatus.IsOutliner() )
+ nY += nSBL;
+ if ( nY > aDocPos.Y() ) // das war 'se
+ {
+ pLine = pTmpLine;
+ break; // richtige Y-Position intressiert nicht
+ }
+
+ nCurIndex = nCurIndex + pTmpLine->GetLen();
+ }
+
+ if ( !pLine ) // darf nur im Bereich von SA passieren!
+ {
+ #ifdef DBG_UTIL
+ const SvxULSpaceItem& rULSpace =(const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
+ DBG_ASSERT( nY+GetYValue( rULSpace.GetLower() ) >= aDocPos.Y() , "Index in keiner Zeile, GetPaM ?" );
+ #endif
+ aPaM.SetIndex( pPortion->GetNode()->Len() );
+ return aPaM;
+ }
+
+ // Wenn Zeile gefunden, nur noch X-Position => Index
+ nCurIndex = GetChar( pPortion, pLine, aDocPos.X(), bSmart );
+ aPaM.SetIndex( nCurIndex );
+
+ if ( nCurIndex && ( nCurIndex == pLine->GetEnd() ) &&
+ ( pLine != pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1) ) )
+ {
+ aPaM = CursorLeft( aPaM, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL );
+ }
+
+ return aPaM;
+}
+
+USHORT ImpEditEngine::GetChar( ParaPortion* pParaPortion, EditLine* pLine, long nXPos, BOOL bSmart )
+{
+ DBG_ASSERT( pLine, "Keine Zeile erhalten: GetChar" );
+
+ USHORT nChar = 0xFFFF;
+ USHORT nCurIndex = pLine->GetStart();
+
+
+ // Search best matching portion with GetPortionXOffset()
+ for ( USHORT i = pLine->GetStartPortion(); i <= pLine->GetEndPortion(); i++ )
+ {
+ TextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( i );
+ long nXLeft = GetPortionXOffset( pParaPortion, pLine, i );
+ long nXRight = nXLeft + pPortion->GetSize().Width();
+ if ( ( nXLeft <= nXPos ) && ( nXRight >= nXPos ) )
+ {
+ nChar = nCurIndex;
+
+ // Search within Portion...
+
+ // Don't search within special portions...
+ if ( pPortion->GetKind() != PORTIONKIND_TEXT )
+ {
+ // ...but check on which side
+ if ( bSmart )
+ {
+ long nLeftDiff = nXPos-nXLeft;
+ long nRightDiff = nXRight-nXPos;
+ if ( nRightDiff < nLeftDiff )
+ nChar++;
+ }
+ }
+ else
+ {
+ USHORT nMax = pPortion->GetLen();
+ USHORT nOffset = 0xFFFF;
+ USHORT nTmpCurIndex = nChar - pLine->GetStart();
+
+ long nXInPortion = nXPos - nXLeft;
+ if ( pPortion->IsRightToLeft() )
+ nXInPortion = nXRight - nXPos;
+
+ // Search in Array...
+ for ( USHORT x = 0; x < nMax; x++ )
+ {
+ long nTmpPosMax = pLine->GetCharPosArray().GetObject( nTmpCurIndex+x );
+ if ( nTmpPosMax > nXInPortion )
+ {
+ // pruefen, ob dieser oder der davor...
+ long nTmpPosMin = x ? pLine->GetCharPosArray().GetObject( nTmpCurIndex+x-1 ) : 0;
+ long nDiffLeft = nXInPortion - nTmpPosMin;
+ long nDiffRight = nTmpPosMax - nXInPortion;
+ DBG_ASSERT( nDiffLeft >= 0, "DiffLeft negativ" );
+ DBG_ASSERT( nDiffRight >= 0, "DiffRight negativ" );
+ nOffset = ( bSmart && ( nDiffRight < nDiffLeft ) ) ? x+1 : x;
+ // I18N: If there are character position with the length of 0,
+ // they belong to the same character, we can not use this position as an index.
+ // Skip all 0-positions, cheaper than using XBreakIterator:
+ if ( nOffset < nMax )
+ {
+ const long nX = pLine->GetCharPosArray().GetObject(nOffset);
+ while ( ( (nOffset+1) < nMax ) && ( pLine->GetCharPosArray().GetObject(nOffset+1) == nX ) )
+ nOffset++;
+ }
+ break;
+ }
+ }
+
+ // Bei Verwendung des CharPosArray duerfte es keine Ungenauigkeiten geben!
+ // Vielleicht bei Kerning ?
+ // 0xFFF passiert z.B. bei Outline-Font, wenn ganz hinten.
+ if ( nOffset == 0xFFFF )
+ nOffset = nMax;
+
+ DBG_ASSERT( nOffset <= nMax, "nOffset > nMax" );
+
+ nChar = nChar + nOffset;
+
+ // Check if index is within a cell:
+ if ( nChar && ( nChar < pParaPortion->GetNode()->Len() ) )
+ {
+ EditPaM aPaM( pParaPortion->GetNode(), nChar+1 );
+ USHORT nScriptType = GetScriptType( aPaM );
+ if ( nScriptType == i18n::ScriptType::COMPLEX )
+ {
+ uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
+ sal_Int32 nCount = 1;
+ lang::Locale aLocale = GetLocale( aPaM );
+ USHORT nRight = (USHORT)_xBI->nextCharacters( *pParaPortion->GetNode(), nChar, aLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount );
+ USHORT nLeft = (USHORT)_xBI->previousCharacters( *pParaPortion->GetNode(), nRight, aLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount );
+ if ( ( nLeft != nChar ) && ( nRight != nChar ) )
+ {
+ nChar = ( Abs( nRight - nChar ) < Abs( nLeft - nChar ) ) ? nRight : nLeft;
+ }
+ }
+ }
+ }
+ }
+
+ nCurIndex = nCurIndex + pPortion->GetLen();
+ }
+
+ if ( nChar == 0xFFFF )
+ {
+ nChar = ( nXPos <= pLine->GetStartPosX() ) ? pLine->GetStart() : pLine->GetEnd();
+ }
+
+ return nChar;
+}
+
+Range ImpEditEngine::GetLineXPosStartEnd( ParaPortion* pParaPortion, EditLine* pLine )
+{
+ Range aLineXPosStartEnd;
+
+ USHORT nPara = GetEditDoc().GetPos( pParaPortion->GetNode() );
+ if ( !IsRightToLeft( nPara ) )
+ {
+ aLineXPosStartEnd.Min() = pLine->GetStartPosX();
+ aLineXPosStartEnd.Max() = pLine->GetStartPosX() + pLine->GetTextWidth();
+ }
+ else
+ {
+ aLineXPosStartEnd.Min() = GetPaperSize().Width() - ( pLine->GetStartPosX() + pLine->GetTextWidth() );
+ aLineXPosStartEnd.Max() = GetPaperSize().Width() - pLine->GetStartPosX();
+ }
+
+
+ return aLineXPosStartEnd;
+}
+
+long ImpEditEngine::GetPortionXOffset( ParaPortion* pParaPortion, EditLine* pLine, USHORT nTextPortion )
+{
+ long nX = pLine->GetStartPosX();
+
+ for ( USHORT i = pLine->GetStartPortion(); i < nTextPortion; i++ )
+ {
+ TextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( i );
+ switch ( pPortion->GetKind() )
+ {
+ case PORTIONKIND_FIELD:
+ case PORTIONKIND_TEXT:
+ case PORTIONKIND_HYPHENATOR:
+ case PORTIONKIND_TAB:
+// case PORTIONKIND_EXTRASPACE:
+ {
+ nX += pPortion->GetSize().Width();
+ }
+ break;
+ }
+ }
+
+ USHORT nPara = GetEditDoc().GetPos( pParaPortion->GetNode() );
+ BOOL bR2LPara = IsRightToLeft( nPara );
+
+ TextPortion* pDestPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
+ if ( pDestPortion->GetKind() != PORTIONKIND_TAB )
+ {
+ if ( !bR2LPara && pDestPortion->GetRightToLeft() )
+ {
+ // Portions behind must be added, visual before this portion
+ sal_uInt16 nTmpPortion = nTextPortion+1;
+ while ( nTmpPortion <= pLine->GetEndPortion() )
+ {
+ TextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
+ if ( pNextTextPortion->GetRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) )
+ nX += pNextTextPortion->GetSize().Width();
+ else
+ break;
+ nTmpPortion++;
+ }
+ // Portions before must be removed, visual behind this portion
+ nTmpPortion = nTextPortion;
+ while ( nTmpPortion > pLine->GetStartPortion() )
+ {
+ --nTmpPortion;
+ TextPortion* pPrevTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
+ if ( pPrevTextPortion->GetRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) )
+ nX -= pPrevTextPortion->GetSize().Width();
+ else
+ break;
+ }
+ }
+ else if ( bR2LPara && !pDestPortion->IsRightToLeft() )
+ {
+ // Portions behind must be ermoved, visual behind this portion
+ sal_uInt16 nTmpPortion = nTextPortion+1;
+ while ( nTmpPortion <= pLine->GetEndPortion() )
+ {
+ TextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
+ if ( !pNextTextPortion->IsRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) )
+ nX += pNextTextPortion->GetSize().Width();
+ else
+ break;
+ nTmpPortion++;
+ }
+ // Portions before must be added, visual before this portion
+ nTmpPortion = nTextPortion;
+ while ( nTmpPortion > pLine->GetStartPortion() )
+ {
+ --nTmpPortion;
+ TextPortion* pPrevTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
+ if ( !pPrevTextPortion->IsRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) )
+ nX -= pPrevTextPortion->GetSize().Width();
+ else
+ break;
+ }
+ }
+ }
+ if ( bR2LPara )
+ {
+ // Switch X postions...
+ DBG_ASSERT( GetTextRanger() || GetPaperSize().Width(), "GetPortionXOffset - paper size?!" );
+ DBG_ASSERT( GetTextRanger() || (nX <= GetPaperSize().Width()), "GetPortionXOffset - position out of paper size!" );
+ nX = GetPaperSize().Width() - nX;
+ nX -= pDestPortion->GetSize().Width();
+ }
+
+ return nX;
+}
+
+long ImpEditEngine::GetXPos( ParaPortion* pParaPortion, EditLine* pLine, USHORT nIndex, BOOL bPreferPortionStart )
+{
+ DBG_ASSERT( pLine, "Keine Zeile erhalten: GetXPos" );
+ DBG_ASSERT( ( nIndex >= pLine->GetStart() ) && ( nIndex <= pLine->GetEnd() ) , "GetXPos muss richtig gerufen werden!" );
+
+ BOOL bDoPreferPortionStart = bPreferPortionStart;
+ // Assure that the portion belongs to this line:
+ if ( nIndex == pLine->GetStart() )
+ bDoPreferPortionStart = TRUE;
+ else if ( nIndex == pLine->GetEnd() )
+ bDoPreferPortionStart = FALSE;
+
+ USHORT nTextPortionStart = 0;
+ USHORT nTextPortion = pParaPortion->GetTextPortions().FindPortion( nIndex, nTextPortionStart, bDoPreferPortionStart );
+
+ DBG_ASSERT( ( nTextPortion >= pLine->GetStartPortion() ) && ( nTextPortion <= pLine->GetEndPortion() ), "GetXPos: Portion not in current line! " );
+
+ TextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
+
+ long nX = GetPortionXOffset( pParaPortion, pLine, nTextPortion );
+
+ // calc text width, portion size may include CJK/CTL spacing...
+ // But the array migh not be init yet, if using text ranger this method is called within CreateLines()...
+ long nPortionTextWidth = pPortion->GetSize().Width();
+ if ( ( pPortion->GetKind() == PORTIONKIND_TEXT ) && pPortion->GetLen() && !GetTextRanger() )
+ nPortionTextWidth = pLine->GetCharPosArray().GetObject( nTextPortionStart + pPortion->GetLen() - 1 - pLine->GetStart() );
+
+ if ( nTextPortionStart != nIndex )
+ {
+ // Search within portion...
+ if ( nIndex == ( nTextPortionStart + pPortion->GetLen() ) )
+ {
+ // End of Portion
+ if ( pPortion->GetKind() == PORTIONKIND_TAB )
+ {
+ if ( (nTextPortion+1) < pParaPortion->GetTextPortions().Count() )
+ {
+ TextPortion* pNextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion+1 );
+ if ( pNextPortion->GetKind() != PORTIONKIND_TAB )
+ {
+ // DBG_ASSERT( !bPreferPortionStart, "GetXPos - How can we this tab portion here???" );
+ // #109879# We loop if nIndex == pLine->GetEnd, because bPreferPortionStart will be reset
+ if ( !bPreferPortionStart )
+ nX = GetXPos( pParaPortion, pLine, nIndex, TRUE );
+ else if ( !IsRightToLeft( GetEditDoc().GetPos( pParaPortion->GetNode() ) ) )
+ nX += nPortionTextWidth;
+ }
+ }
+ else if ( !IsRightToLeft( GetEditDoc().GetPos( pParaPortion->GetNode() ) ) )
+ {
+ nX += nPortionTextWidth;
+ }
+ }
+ else if ( !pPortion->IsRightToLeft() )
+ {
+ nX += nPortionTextWidth;
+ }
+ }
+ else if ( pPortion->GetKind() == PORTIONKIND_TEXT )
+ {
+ DBG_ASSERT( nIndex != pLine->GetStart(), "Strange behavior in new GetXPos()" );
+ DBG_ASSERT( pLine && pLine->GetCharPosArray().Count(), "svx::ImpEditEngine::GetXPos(), portion in an empty line?" );
+
+ if( pLine->GetCharPosArray().Count() )
+ {
+ USHORT nPos = nIndex - 1 - pLine->GetStart();
+ if( nPos >= pLine->GetCharPosArray().Count() )
+ {
+ nPos = pLine->GetCharPosArray().Count()-1;
+ DBG_ERROR("svx::ImpEditEngine::GetXPos(), index out of range!");
+ }
+
+ long nPosInPortion = pLine->GetCharPosArray().GetObject( nPos );
+
+ if ( !pPortion->IsRightToLeft() )
+ {
+ nX += nPosInPortion;
+ }
+ else
+ {
+ nX += nPortionTextWidth - nPosInPortion;
+ }
+
+ if ( pPortion->GetExtraInfos() && pPortion->GetExtraInfos()->bCompressed )
+ {
+ nX += pPortion->GetExtraInfos()->nPortionOffsetX;
+ if ( pPortion->GetExtraInfos()->nAsianCompressionTypes & CHAR_PUNCTUATIONRIGHT )
+ {
+ BYTE nType = GetCharTypeForCompression( pParaPortion->GetNode()->GetChar( nIndex ) );
+ if ( nType == CHAR_PUNCTUATIONRIGHT )
+ {
+ USHORT n = nIndex - nTextPortionStart;
+ const sal_Int32* pDXArray = pLine->GetCharPosArray().GetData()+( nTextPortionStart-pLine->GetStart() );
+ sal_Int32 nCharWidth = ( ( (n+1) < pPortion->GetLen() ) ? pDXArray[n] : pPortion->GetSize().Width() )
+ - ( n ? pDXArray[n-1] : 0 );
+ if ( (n+1) < pPortion->GetLen() )
+ {
+ // smaller, when char behind is CHAR_PUNCTUATIONRIGHT also
+ nType = GetCharTypeForCompression( pParaPortion->GetNode()->GetChar( nIndex+1 ) );
+ if ( nType == CHAR_PUNCTUATIONRIGHT )
+ {
+ sal_Int32 nNextCharWidth = ( ( (n+2) < pPortion->GetLen() ) ? pDXArray[n+1] : pPortion->GetSize().Width() )
+ - pDXArray[n];
+ sal_Int32 nCompressed = nNextCharWidth/2;
+ nCompressed *= pPortion->GetExtraInfos()->nMaxCompression100thPercent;
+ nCompressed /= 10000;
+ nCharWidth += nCompressed;
+ }
+ }
+ else
+ {
+ nCharWidth *= 2; // last char pos to portion end is only compressed size
+ }
+ nX += nCharWidth/2; // 50% compression
+ }
+ }
+ }
+ }
+ }
+ }
+ else // if ( nIndex == pLine->GetStart() )
+ {
+ if ( pPortion->IsRightToLeft() )
+ {
+ nX += nPortionTextWidth;
+ }
+ }
+
+ return nX;
+}
+
+void ImpEditEngine::CalcHeight( ParaPortion* pPortion )
+{
+ pPortion->nHeight = 0;
+ pPortion->nFirstLineOffset = 0;
+
+ if ( pPortion->IsVisible() )
+ {
+ DBG_ASSERT( pPortion->GetLines().Count(), "Absatz ohne Zeilen in ParaPortion::CalcHeight" );
+ for ( USHORT nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
+ pPortion->nHeight += pPortion->GetLines().GetObject( nLine )->GetHeight();
+
+ if ( !aStatus.IsOutliner() )
+ {
+ const SvxULSpaceItem& rULItem = (const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
+ const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
+ USHORT nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX ) ? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
+
+ if ( nSBL )
+ {
+ if ( pPortion->GetLines().Count() > 1 )
+ pPortion->nHeight += ( pPortion->GetLines().Count() - 1 ) * nSBL;
+ if ( aStatus.ULSpaceSummation() )
+ pPortion->nHeight += nSBL;
+ }
+
+ USHORT nPortion = GetParaPortions().GetPos( pPortion );
+ if ( nPortion || aStatus.ULSpaceFirstParagraph() )
+ {
+ USHORT nUpper = GetYValue( rULItem.GetUpper() );
+ pPortion->nHeight += nUpper;
+ pPortion->nFirstLineOffset = nUpper;
+ }
+
+ if ( ( nPortion != (GetParaPortions().Count()-1) ) )
+ {
+ pPortion->nHeight += GetYValue( rULItem.GetLower() ); // nicht in letzter
+ }
+
+
+ if ( nPortion && !aStatus.ULSpaceSummation() )
+ {
+ ParaPortion* pPrev = GetParaPortions().SaveGetObject( nPortion-1 );
+ const SvxULSpaceItem& rPrevULItem = (const SvxULSpaceItem&)pPrev->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
+ const SvxLineSpacingItem& rPrevLSItem = (const SvxLineSpacingItem&)pPrev->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
+
+ // Verhalten WinWord6/Writer3:
+ // Bei einem proportionalen Zeilenabstand wird auch der Absatzabstand
+ // manipuliert.
+ // Nur Writer3: Nicht aufaddieren, sondern Mindestabstand.
+
+ // Pruefen, ob Abstand durch LineSpacing > Upper:
+ USHORT nExtraSpace = GetYValue( lcl_CalcExtraSpace( pPortion, rLSItem ) );
+ if ( nExtraSpace > pPortion->nFirstLineOffset )
+ {
+ // Absatz wird 'groesser':
+ pPortion->nHeight += ( nExtraSpace - pPortion->nFirstLineOffset );
+ pPortion->nFirstLineOffset = nExtraSpace;
+ }
+
+ // nFirstLineOffset jetzt f(pNode) => jetzt f(pNode, pPrev) ermitteln:
+ USHORT nPrevLower = GetYValue( rPrevULItem.GetLower() );
+
+ // Dieser PrevLower steckt noch in der Hoehe der PrevPortion...
+ if ( nPrevLower > pPortion->nFirstLineOffset )
+ {
+ // Absatz wird 'kleiner':
+ pPortion->nHeight -= pPortion->nFirstLineOffset;
+ pPortion->nFirstLineOffset = 0;
+ }
+ else if ( nPrevLower )
+ {
+ // Absatz wird 'etwas kleiner':
+ pPortion->nHeight -= nPrevLower;
+ pPortion->nFirstLineOffset =
+ pPortion->nFirstLineOffset - nPrevLower;
+ }
+
+ // Finde ich zwar nicht so gut, aber Writer3-Feature:
+ // Pruefen, ob Abstand durch LineSpacing > Lower:
+ // Dieser Wert steckt nicht in der Hoehe der PrevPortion.
+ if ( !pPrev->IsInvalid() )
+ {
+ nExtraSpace = GetYValue( lcl_CalcExtraSpace( pPrev, rPrevLSItem ) );
+ if ( nExtraSpace > nPrevLower )
+ {
+ USHORT nMoreLower = nExtraSpace - nPrevLower;
+ // Absatz wird 'groesser', 'waechst' nach unten:
+ if ( nMoreLower > pPortion->nFirstLineOffset )
+ {
+ pPortion->nHeight += ( nMoreLower - pPortion->nFirstLineOffset );
+ pPortion->nFirstLineOffset = nMoreLower;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+Rectangle ImpEditEngine::GetEditCursor( ParaPortion* pPortion, USHORT nIndex, USHORT nFlags )
+{
+ DBG_ASSERT( pPortion->IsVisible(), "Wozu GetEditCursor() bei einem unsichtbaren Absatz?" );
+ DBG_ASSERT( IsFormatted() || GetTextRanger(), "GetEditCursor: Nicht formatiert" );
+
+ /*
+ GETCRSR_ENDOFLINE: Wenn hinter dem letzten Zeichen einer umgebrochenen Zeile,
+ am Ende der Zeile bleiben, nicht am Anfang der naechsten.
+ Zweck: - END => wirklich hinter das letzte Zeichen
+ - Selektion....
+ */
+
+ long nY = pPortion->GetFirstLineOffset();
+
+ const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
+ USHORT nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
+ ? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
+
+ USHORT nCurIndex = 0;
+ DBG_ASSERT( pPortion->GetLines().Count(), "Leere ParaPortion in GetEditCursor!" );
+ EditLine* pLine = 0;
+ BOOL bEOL = ( nFlags & GETCRSR_ENDOFLINE ) ? TRUE : FALSE;
+ for ( USHORT nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
+ {
+ EditLine* pTmpLine = pPortion->GetLines().GetObject( nLine );
+ if ( ( pTmpLine->GetStart() == nIndex ) || ( pTmpLine->IsIn( nIndex, bEOL ) ) )
+ {
+ pLine = pTmpLine;
+ break;
+ }
+
+ nCurIndex = nCurIndex + pTmpLine->GetLen();
+ nY += pTmpLine->GetHeight();
+ if ( !aStatus.IsOutliner() )
+ nY += nSBL;
+ }
+ if ( !pLine )
+ {
+ // Cursor am Ende des Absatzes.
+ DBG_ASSERT( nIndex == nCurIndex, "Index voll daneben in GetEditCursor!" );
+
+ pLine = pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1 );
+ nY -= pLine->GetHeight();
+ if ( !aStatus.IsOutliner() )
+ nY -= nSBL;
+ nCurIndex = nCurIndex - pLine->GetLen();
+ }
+
+ Rectangle aEditCursor;
+
+ aEditCursor.Top() = nY;
+ nY += pLine->GetHeight();
+ aEditCursor.Bottom() = nY-1;
+
+ // innerhalb der Zeile suchen...
+ long nX;
+
+ if ( ( nIndex == pLine->GetStart() ) && ( nFlags & GETCRSR_STARTOFLINE ) )
+ {
+ Range aXRange = GetLineXPosStartEnd( pPortion, pLine );
+ nX = !IsRightToLeft( GetEditDoc().GetPos( pPortion->GetNode() ) ) ? aXRange.Min() : aXRange.Max();
+ }
+ else if ( ( nIndex == pLine->GetEnd() ) && ( nFlags & GETCRSR_ENDOFLINE ) )
+ {
+ Range aXRange = GetLineXPosStartEnd( pPortion, pLine );
+ nX = !IsRightToLeft( GetEditDoc().GetPos( pPortion->GetNode() ) ) ? aXRange.Max() : aXRange.Min();
+ }
+ else
+ {
+ nX = GetXPos( pPortion, pLine, nIndex, ( nFlags & GETCRSR_PREFERPORTIONSTART ) ? TRUE : FALSE );
+ }
+
+ aEditCursor.Left() = aEditCursor.Right() = nX;
+
+ if ( nFlags & GETCRSR_TXTONLY )
+ aEditCursor.Top() = aEditCursor.Bottom() - pLine->GetTxtHeight() + 1;
+ else
+ aEditCursor.Top() = aEditCursor.Bottom() - Min( pLine->GetTxtHeight(), pLine->GetHeight() ) + 1;
+
+ return aEditCursor;
+}
+
+void ImpEditEngine::SetValidPaperSize( const Size& rNewSz )
+{
+ aPaperSize = rNewSz;
+
+ long nMinWidth = aStatus.AutoPageWidth() ? aMinAutoPaperSize.Width() : 0;
+ long nMaxWidth = aStatus.AutoPageWidth() ? aMaxAutoPaperSize.Width() : 0x7FFFFFFF;
+ long nMinHeight = aStatus.AutoPageHeight() ? aMinAutoPaperSize.Height() : 0;
+ long nMaxHeight = aStatus.AutoPageHeight() ? aMaxAutoPaperSize.Height() : 0x7FFFFFFF;
+
+ // Minimale/Maximale Breite:
+ if ( aPaperSize.Width() < nMinWidth )
+ aPaperSize.Width() = nMinWidth;
+ else if ( aPaperSize.Width() > nMaxWidth )
+ aPaperSize.Width() = nMaxWidth;
+
+ // Minimale/Maximale Hoehe:
+ if ( aPaperSize.Height() < nMinHeight )
+ aPaperSize.Height() = nMinHeight;
+ else if ( aPaperSize.Height() > nMaxHeight )
+ aPaperSize.Height() = nMaxHeight;
+}
+
+void ImpEditEngine::IndentBlock( EditView* pEditView, BOOL bRight )
+{
+ ESelection aESel( CreateESel( pEditView->pImpEditView->GetEditSelection() ) );
+ aESel.Adjust();
+
+ // Nur wenn mehrere selektierte Absaetze...
+ if ( aESel.nEndPara > aESel.nStartPara )
+ {
+ ESelection aNewSel = aESel;
+ aNewSel.nStartPos = 0;
+ aNewSel.nEndPos = 0xFFFF;
+
+ if ( aESel.nEndPos == 0 )
+ {
+ aESel.nEndPara--; // dann diesen Absatz nicht...
+ aNewSel.nEndPos = 0;
+ }
+
+ pEditView->pImpEditView->DrawSelection();
+ pEditView->pImpEditView->SetEditSelection(
+ pEditView->pImpEditView->GetEditSelection().Max() );
+ UndoActionStart( bRight ? EDITUNDO_INDENTBLOCK : EDITUNDO_UNINDENTBLOCK );
+
+ for ( USHORT nPara = aESel.nStartPara; nPara <= aESel.nEndPara; nPara++ )
+ {
+ ContentNode* pNode = GetEditDoc().GetObject( nPara );
+ if ( bRight )
+ {
+ // Tabs hinzufuegen
+ EditPaM aPaM( pNode, 0 );
+ InsertTab( aPaM );
+ }
+ else
+ {
+ // Tabs entfernen
+ EditCharAttrib* pFeature = pNode->GetCharAttribs().FindFeature( 0 );
+ if ( pFeature && ( pFeature->GetStart() == 0 ) &&
+ ( pFeature->GetItem()->Which() == EE_FEATURE_TAB ) )
+ {
+ EditPaM aStartPaM( pNode, 0 );
+ EditPaM aEndPaM( pNode, 1 );
+ ImpDeleteSelection( EditSelection( aStartPaM, aEndPaM ) );
+ }
+ }
+ }
+
+ UndoActionEnd( bRight ? EDITUNDO_INDENTBLOCK : EDITUNDO_UNINDENTBLOCK );
+ UpdateSelections();
+ FormatAndUpdate( pEditView );
+
+ ContentNode* pLastNode = GetEditDoc().GetObject( aNewSel.nEndPara );
+ if ( pLastNode->Len() < aNewSel.nEndPos )
+ aNewSel.nEndPos = pLastNode->Len();
+ pEditView->pImpEditView->SetEditSelection( CreateSel( aNewSel ) );
+ pEditView->pImpEditView->DrawSelection();
+ pEditView->pImpEditView->ShowCursor( FALSE, TRUE );
+ }
+}
+
+vos::ORef<SvxForbiddenCharactersTable> ImpEditEngine::GetForbiddenCharsTable( BOOL bGetInternal ) const
+{
+ vos::ORef<SvxForbiddenCharactersTable> xF = xForbiddenCharsTable;
+ if ( !xF.isValid() && bGetInternal )
+ xF = EE_DLL()->GetGlobalData()->GetForbiddenCharsTable();
+ return xF;
+}
+
+void ImpEditEngine::SetForbiddenCharsTable( vos::ORef<SvxForbiddenCharactersTable> xForbiddenChars )
+{
+ EE_DLL()->GetGlobalData()->SetForbiddenCharsTable( xForbiddenChars );
+}
+
+svtools::ColorConfig& ImpEditEngine::GetColorConfig()
+{
+ if ( !pColorConfig )
+ pColorConfig = new svtools::ColorConfig;
+
+ return *pColorConfig;
+}
+
+BOOL ImpEditEngine::IsVisualCursorTravelingEnabled()
+{
+ BOOL bVisualCursorTravaling = FALSE;
+
+ if( !pCTLOptions )
+ pCTLOptions = new SvtCTLOptions;
+
+ if ( pCTLOptions->IsCTLFontEnabled() && ( pCTLOptions->GetCTLCursorMovement() == SvtCTLOptions::MOVEMENT_VISUAL ) )
+ {
+ bVisualCursorTravaling = TRUE;
+ }
+
+ return bVisualCursorTravaling;
+
+}
+
+BOOL ImpEditEngine::DoVisualCursorTraveling( const ContentNode* )
+{
+ // Don't check if it's necessary, because we also need it when leaving the paragraph
+ return IsVisualCursorTravelingEnabled();
+/*
+ BOOL bDoVisualCursorTraveling = FALSE;
+
+ if ( IsVisualCursorTravelingEnabled() && pNode->Len() )
+ {
+ // Only necessary when RTL text in LTR para or LTR text in RTL para
+ bDoVisualCursorTraveling = HasDifferentRTLLevels( pNode );
+ }
+
+ return bDoVisualCursorTraveling;
+*/
+}
+
+
+void ImpEditEngine::CallNotify( EENotify& rNotify )
+{
+ if ( !nBlockNotifications )
+ {
+ GetNotifyHdl().Call( &rNotify );
+ }
+ else
+ {
+ EENotify* pNewNotify = new EENotify( rNotify );
+ aNotifyCache.Insert( pNewNotify, aNotifyCache.Count() );
+ }
+}
+
+void ImpEditEngine::EnterBlockNotifications()
+{
+ if( !nBlockNotifications )
+ {
+ // #109864# Send out START notification immediately, to allow
+ // external, non-queued events to be captured as well from
+ // client side
+ EENotify aNotify( EE_NOTIFY_BLOCKNOTIFICATION_START );
+ aNotify.pEditEngine = GetEditEnginePtr();
+ GetNotifyHdl().Call( &aNotify );
+ }
+
+ nBlockNotifications++;
+}
+
+void ImpEditEngine::LeaveBlockNotifications()
+{
+ DBG_ASSERT( nBlockNotifications, "LeaveBlockNotifications - Why?" );
+
+ nBlockNotifications--;
+ if ( !nBlockNotifications )
+ {
+ // Call blocked notify events...
+ while ( aNotifyCache.Count() )
+ {
+ EENotify* pNotify = aNotifyCache[0];
+ // Remove from list before calling, maybe we enter LeaveBlockNotifications while calling the handler...
+ aNotifyCache.Remove( 0 );
+ GetNotifyHdl().Call( pNotify );
+ delete pNotify;
+ }
+
+ EENotify aNotify( EE_NOTIFY_BLOCKNOTIFICATION_END );
+ aNotify.pEditEngine = GetEditEnginePtr();
+ GetNotifyHdl().Call( &aNotify );
+ }
+}
+
+IMPL_LINK( ImpEditEngine, DocModified, void*, EMPTYARG )
+{
+ aModifyHdl.Call( NULL /*GetEditEnginePtr()*/ ); // NULL, because also used for Outliner
+ return 0;
+}