| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_editeng.hxx" |
| |
| #include <vcl/wrkwin.hxx> |
| #include <vcl/dialog.hxx> |
| #include <vcl/msgbox.hxx> |
| #include <vcl/svapp.hxx> |
| |
| #include <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; |
| |
| sal_uInt16 lcl_CalcExtraSpace( ParaPortion*, const SvxLineSpacingItem& rLSItem ) |
| { |
| sal_uInt16 nExtra = 0; |
| /* if ( ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP ) |
| && ( rLSItem.GetPropLineSpace() != 100 ) ) |
| { |
| // sal_uLong nH = pPortion->GetNode()->GetCharAttribs().GetDefFont().GetSize().Height(); |
| sal_uLong nH = pPortion->GetLines().GetObject( 0 )->GetHeight(); |
| long n = nH * rLSItem.GetPropLineSpace(); |
| n /= 100; |
| n -= nH; // nur den Abstand |
| if ( n > 0 ) |
| nExtra = (sal_uInt16)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 = sal_False; |
| bOwnerOfRefDev = sal_False; |
| bDowning = sal_False; |
| bIsInUndo = sal_False; |
| bIsFormatting = sal_False; |
| bFormatted = sal_False; |
| bUpdate = sal_True; |
| bUpdateForAcc = TRUE; |
| bUseAutoColor = sal_True; |
| bForceAutoColor = sal_False; |
| bAddExtLeading = sal_False; |
| bUndoEnabled = sal_True; |
| bCallParaInsertedOrDeleted = sal_False; |
| bImpConvertFirstCall= sal_False; |
| bFirstWordCapitalization = sal_True; |
| |
| eDefLanguage = LANGUAGE_DONTKNOW; |
| maBackgroundColor = COL_AUTO; |
| |
| nAsianCompressionMode = text::CharacterCompressionType::NONE; |
| bKernAsianPunctuation = sal_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( sal_False ); |
| |
| bCallParaInsertedOrDeleted = sal_True; |
| |
| aEditDoc.SetModifyHdl( LINK( this, ImpEditEngine, DocModified ) ); |
| |
| mbLastTryMerge = sal_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 = sal_True; |
| SetUpdateMode( sal_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 = sal_False; |
| |
| if ( !pRef ) |
| pRefDev = EE_DLL()->GetGlobalData()->GetStdRefDevice(); |
| |
| nOnePixelInRef = (sal_uInt16)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 = sal_True; |
| } |
| pRefDev->SetMapMode( rMapMode ); |
| nOnePixelInRef = (sal_uInt16)pRefDev->PixelToLogic( Size( 1, 0 ) ).Width(); |
| if ( IsFormatted() ) |
| { |
| FormatFullDoc(); |
| UpdateViews( (EditView*) 0); |
| } |
| } |
| |
| void ImpEditEngine::InitDoc( sal_Bool bKeepParaAttribs ) |
| { |
| sal_uInt16 nParas = aEditDoc.Count(); |
| for ( sal_uInt16 n = bKeepParaAttribs ? 1 : 0; n < nParas; n++ ) |
| { |
| if ( aEditDoc[n]->GetStyleSheet() ) |
| EndListening( *aEditDoc[n]->GetStyleSheet(), sal_False ); |
| } |
| |
| if ( bKeepParaAttribs ) |
| aEditDoc.RemoveText(); |
| else |
| aEditDoc.Clear(); |
| |
| GetParaPortions().Reset(); |
| |
| ParaPortion* pIniPortion = new ParaPortion( aEditDoc[0] ); |
| GetParaPortions().Insert( pIniPortion, 0 ); |
| |
| bFormatted = sal_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(); |
| sal_uInt16 nStartNode = aEditDoc.GetPos( pStartNode ); |
| sal_uInt16 nEndNode = aEditDoc.GetPos( pEndNode ); |
| |
| DBG_ASSERT( nStartNode <= nEndNode, "Selektion nicht sortiert ?" ); |
| |
| // ueber die Absaetze iterieren... |
| for ( sal_uInt16 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; |
| } |
| |
| sal_Bool ImpEditEngine::MouseButtonDown( const MouseEvent& rMEvt, EditView* pView ) |
| { |
| GetSelEngine().SetCurView( pView ); |
| SetActiveView( pView ); |
| |
| if ( GetAutoCompleteText().Len() ) |
| SetAutoCompleteText( String(), sal_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( sal_True, sal_False ); |
| |
| EditSelection aNewSelection( SelectWord( aCurSel ) ); |
| pView->pImpEditView->DrawSelection(); |
| pView->pImpEditView->SetEditSelection( aNewSelection ); |
| pView->pImpEditView->DrawSelection(); |
| pView->ShowCursor( sal_True, sal_True ); |
| } |
| else if ( rMEvt.GetClicks() == 3 ) |
| { |
| // damit die SelectionEngine weiss, dass Anker. |
| aSelEngine.CursorPosChanging( sal_True, sal_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( sal_True, sal_True ); |
| } |
| } |
| return sal_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() ); |
| sal_uInt16 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 ); |
| |
| sal_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 ) |
| { |
| sal_uInt16 nOldIMETextLen = mpIMEInfos->nLen; |
| sal_uInt16 nNewIMETextLen = pData->GetText().Len(); |
| |
| if ( ( nOldIMETextLen > nNewIMETextLen ) && |
| ( nNewIMETextLen < mpIMEInfos->aOldTextAfterStartPos.Len() ) ) |
| { |
| // restore old characters |
| sal_uInt16 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 |
| sal_uInt16 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 ); |
| |
| sal_uInt16 nInputEnd = mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen; |
| |
| if ( !IsFormatted() ) |
| FormatDoc(); |
| |
| ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( GetEditDoc().GetPos( aPaM.GetNode() ) ); |
| sal_uInt16 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 ); |
| } |
| |
| sal_Bool ImpEditEngine::MouseButtonUp( const MouseEvent& rMEvt, EditView* pView ) |
| { |
| GetSelEngine().SetCurView( pView ); |
| GetSelEngine().SelMouseButtonUp( rMEvt ); |
| bInSelection = sal_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() ); |
| sal_uInt16 nPara = GetEditDoc().GetPos( aPaM.GetNode() ); |
| GetEditEnginePtr()->FieldClicked( *pFld, nPara, aPaM.GetIndex() ); |
| } |
| } |
| } |
| return sal_True; |
| } |
| |
| sal_Bool ImpEditEngine::MouseMove( const MouseEvent& rMEvt, EditView* pView ) |
| { |
| // MouseMove wird sofort nach ShowQuickHelp() gerufen! |
| // if ( GetAutoCompleteText().Len() ) |
| // SetAutoCompleteText( String(), sal_True ); |
| GetSelEngine().SetCurView( pView ); |
| GetSelEngine().SelMouseMove( rMEvt ); |
| return sal_True; |
| } |
| |
| EditPaM ImpEditEngine::InsertText( EditSelection aSel, const XubString& rStr ) |
| { |
| EditPaM aPaM = ImpInsertText( aSel, rStr ); |
| return aPaM; |
| } |
| |
| EditPaM ImpEditEngine::Clear() |
| { |
| InitDoc( sal_False ); |
| |
| EditPaM aPaM = aEditDoc.GetStartPaM(); |
| EditSelection aSel( aPaM ); |
| |
| nCurTextHeight = 0; |
| |
| ResetUndoManager(); |
| |
| for ( sal_uInt16 nView = aEditViews.Count(); nView; ) |
| { |
| EditView* pView = aEditViews[--nView]; |
| DBG_CHKOBJ( pView, EditView, 0 ); |
| pView->pImpEditView->SetEditSelection( aSel ); |
| } |
| |
| return aPaM; |
| } |
| |
| EditPaM ImpEditEngine::RemoveText() |
| { |
| InitDoc( sal_True ); |
| |
| EditPaM aStartPaM = aEditDoc.GetStartPaM(); |
| EditSelection aEmptySel( aStartPaM, aStartPaM ); |
| for ( sal_uInt16 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(); |
| sal_Bool bUndoCurrentlyEnabled = IsUndoEnabled(); |
| // Der von Hand reingesteckte Text kann nicht vom Anwender rueckgaengig gemacht werden. |
| EnableUndo( sal_False ); |
| |
| EditSelection aEmptySel( aStartPaM, aStartPaM ); |
| EditPaM aPaM = aStartPaM; |
| if ( rText.Len() ) |
| aPaM = ImpInsertText( aEmptySel, rText ); |
| |
| for ( sal_uInt16 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 ( sal_uInt16 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 = sal_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( sal_True ); |
| bFormatted = sal_False; |
| |
| ParaPortion* pPortion = FindParaPortion( pNode ); |
| DBG_ASSERT( pPortion, "ParaAttribsChanged: Portion?" ); |
| pPortion->MarkSelectionInvalid( 0, pNode->Len() ); |
| |
| sal_uInt16 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 ); |
| |
| sal_Bool bCtrl = aTranslatedKeyEvent.GetKeyCode().IsMod1() ? sal_True : sal_False; |
| sal_uInt16 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, sal_Bool bStart ) |
| { |
| EditPaM aPaM( rPaM ); |
| |
| sal_uInt16 nPara = GetEditDoc().GetPos( aPaM.GetNode() ); |
| ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara ); |
| |
| sal_uInt16 nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), sal_False ); |
| EditLine* pLine = pParaPortion->GetLines().GetObject( nLine ); |
| sal_Bool bEmptyLine = pLine->GetStart() == pLine->GetEnd(); |
| |
| pEditView->pImpEditView->nExtraCursorFlags = 0; |
| |
| if ( !bEmptyLine ) |
| { |
| String aLine( *aPaM.GetNode(), pLine->GetStart(), pLine->GetEnd() - pLine->GetStart() ); |
| // sal_uInt16 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 |
| |
| sal_uInt16 nVisPos = bStart ? 0 : aLine.Len()-1; |
| sal_uInt16 nLogPos = (sal_uInt16)ubidi_getLogicalIndex( pBidi, nVisPos, &nError ); |
| |
| ubidi_close( pBidi ); |
| |
| aPaM.GetIndex() = nLogPos + pLine->GetStart(); |
| |
| sal_uInt16 nTmp; |
| sal_uInt16 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nTmp, sal_True ); |
| TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion ); |
| sal_uInt16 nRTLLevel = pTextPortion->GetRightToLeft(); |
| // sal_Bool bParaRTL = IsRightToLeft( nPara ); |
| sal_Bool bPortionRTL = nRTLLevel%2 ? sal_True : sal_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, sal_uInt16 nCharacterIteratorMode, sal_Bool bVisualToLeft ) |
| { |
| EditPaM aPaM( rPaM ); |
| |
| sal_uInt16 nPara = GetEditDoc().GetPos( aPaM.GetNode() ); |
| ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara ); |
| |
| sal_uInt16 nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), sal_False ); |
| EditLine* pLine = pParaPortion->GetLines().GetObject( nLine ); |
| sal_Bool bEmptyLine = pLine->GetStart() == pLine->GetEnd(); |
| |
| // sal_uInt16 nCurrentCursorFlags = pEditView->pImpEditView->nExtraCursorFlags; |
| pEditView->pImpEditView->nExtraCursorFlags = 0; |
| |
| sal_Bool bParaRTL = IsRightToLeft( nPara ); |
| |
| sal_Bool bDone = sal_False; |
| |
| if ( bEmptyLine ) |
| { |
| if ( bVisualToLeft ) |
| { |
| aPaM = CursorUp( aPaM, pEditView ); |
| if ( aPaM != rPaM ) |
| aPaM = CursorVisualStartEnd( pEditView, aPaM, sal_False ); |
| } |
| else |
| { |
| aPaM = CursorDown( aPaM, pEditView ); |
| if ( aPaM != rPaM ) |
| aPaM = CursorVisualStartEnd( pEditView, aPaM, sal_True ); |
| } |
| |
| bDone = sal_True; |
| } |
| |
| sal_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... |
| sal_uInt16 nPortionStart; |
| sal_uInt16 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, sal_False ); |
| TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion ); |
| |
| sal_Bool bPortionBoundary = ( aPaM.GetIndex() == nPortionStart ) || ( aPaM.GetIndex() == (nPortionStart+pTextPortion->GetLen()) ); |
| sal_uInt16 nRTLLevel = pTextPortion->GetRightToLeft(); |
| |
| // Portion boundary doesn't matter if both have same RTL level |
| sal_uInt16 nRTLLevelNextPortion = 0xFFFF; |
| if ( bPortionBoundary && aPaM.GetIndex() && ( aPaM.GetIndex() < aPaM.GetNode()->Len() ) ) |
| { |
| sal_uInt16 nTmp; |
| sal_uInt16 nNextTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex()+1, nTmp, bLogicalBackward ? sal_False : sal_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 = sal_True; |
| } |
| } |
| |
| if ( !bDone ) |
| { |
| sal_Bool bGotoStartOfNextLine = sal_False; |
| sal_Bool bGotoEndOfPrevLine = sal_False; |
| |
| String aLine( *aPaM.GetNode(), pLine->GetStart(), pLine->GetEnd() - pLine->GetStart() ); |
| sal_uInt16 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() ) |
| { |
| sal_Bool bEndOfLine = nPosInLine == aLine.Len(); |
| sal_uInt16 nVisPos = (sal_uInt16)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 ) |
| { |
| sal_uInt16 nLogPos = (sal_uInt16)ubidi_getLogicalIndex( pBidi, nVisPos, &nError ); |
| aPaM.GetIndex() = pLine->GetStart() + nLogPos; |
| pEditView->pImpEditView->SetCursorBidiLevel( 0 ); |
| } |
| } |
| else |
| { |
| sal_Bool bWasBehind = sal_False; |
| sal_Bool bBeforePortion = !nPosInLine || pEditView->pImpEditView->GetCursorBidiLevel() == 1; |
| if ( nPosInLine && ( !bBeforePortion ) ) // before the next portion |
| bWasBehind = sal_True; // step one back, otherwise visual will be unusable when rtl portion follows. |
| |
| sal_uInt16 nPortionStart; |
| sal_uInt16 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, bBeforePortion ); |
| TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion ); |
| sal_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 ) |
| { |
| sal_uInt16 nLogPos = (sal_uInt16)ubidi_getLogicalIndex( pBidi, nVisPos, &nError ); |
| |
| /* |
| if ( nLogPos == aPaM.GetIndex() ) |
| { |
| if ( bVisualToLeft ) |
| bGotoEndOfPrevLine = sal_True; |
| else |
| bGotoStartOfNextLine = sal_True; |
| } |
| else |
| */ |
| { |
| aPaM.GetIndex() = pLine->GetStart() + nLogPos; |
| |
| // RTL portion, stay visually on the left side. |
| sal_uInt16 _nPortionStart; |
| // sal_uInt16 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, !bRTLPortion ); |
| sal_uInt16 _nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), _nPortionStart, sal_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, sal_False ); |
| } |
| else if ( bGotoStartOfNextLine ) |
| { |
| aPaM = CursorDown( aPaM, pEditView ); |
| if ( aPaM != rPaM ) |
| aPaM = CursorVisualStartEnd( pEditView, aPaM, sal_True ); |
| } |
| } |
| return aPaM; |
| } |
| |
| |
| EditPaM ImpEditEngine::CursorLeft( const EditPaM& rPaM, sal_uInt16 nCharacterIteratorMode ) |
| { |
| EditPaM aCurPaM( rPaM ); |
| EditPaM aNewPaM( aCurPaM ); |
| |
| if ( aCurPaM.GetIndex() ) |
| { |
| sal_Int32 nCount = 1; |
| uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() ); |
| aNewPaM.SetIndex( (sal_uInt16)_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, sal_uInt16 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( (sal_uInt16)_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" ); |
| sal_uInt16 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" ); |
| sal_uInt16 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 ?" ); |
| sal_uInt16 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 ?" ); |
| sal_uInt16 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 ) |
| { |
| sal_uInt16 nCurrentPos = rPaM.GetIndex(); |
| EditPaM aNewPaM( rPaM ); |
| if ( nCurrentPos == 0 ) |
| { |
| // Vorheriger Absatz... |
| sal_uInt16 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) ) ? (sal_uInt16)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( (sal_uInt16)aBoundary.startPos ); |
| } |
| // not 'else', maybe the index reached nMax now... |
| if ( aNewPaM.GetIndex() >= nMax ) |
| { |
| // Naechster Absatz... |
| sal_uInt16 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( (sal_uInt16)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( (sal_uInt16)aBoundary.endPos ); |
| return aNewPaM; |
| } |
| |
| EditSelection ImpEditEngine::SelectWord( const EditSelection& rCurSel, sal_Int16 nWordType, sal_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( (sal_uInt16)aBoundary.startPos ); |
| aNewSel.Max().SetIndex( (sal_uInt16)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( (sal_uInt16)nStart ); |
| aNewSel.Max().SetIndex( (sal_uInt16)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 |
| sal_uInt16 nFirstPos = rCurSel.Min().GetIndex(); |
| sal_uInt16 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( sal_uInt16 nPara ) |
| { |
| ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara ); |
| ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos; |
| rTypes.clear(); |
| |
| 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 ( sal_uInt16 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 ); |
| sal_uInt16 nTextLen = (sal_uInt16)aOUText.getLength(); |
| |
| sal_Int32 nPos = 0; |
| short nScriptType = _xBI->getScriptType( aOUText, nPos ); |
| rTypes.push_back( ScriptTypePosInfo( nScriptType, (sal_uInt16)nPos, nTextLen ) ); |
| nPos = _xBI->endOfScript( aOUText, nPos, nScriptType ); |
| while ( ( nPos != (-1) ) && ( nPos < nTextLen ) ) |
| { |
| rTypes.back().nEndPos = (sal_uInt16)nPos; |
| |
| nScriptType = _xBI->getScriptType( aOUText, nPos ); |
| long nEndPos = _xBI->endOfScript( aOUText, nPos, nScriptType ); |
| |
| if ( ( nScriptType == i18n::ScriptType::WEAK ) || ( nScriptType == rTypes.back().nScriptType ) ) |
| { |
| // Expand last ScriptTypePosInfo, don't create weak or unnecessary portions |
| rTypes.back().nEndPos = (sal_uInt16)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.back().nEndPos--; |
| break; |
| } |
| } |
| rTypes.push_back( ScriptTypePosInfo( nScriptType, (sal_uInt16)nPos, nTextLen ) ); |
| } |
| |
| nPos = nEndPos; |
| } |
| |
| if ( rTypes[0].nScriptType == i18n::ScriptType::WEAK ) |
| rTypes[0].nScriptType = ( rTypes.size() > 1 ) ? rTypes[1].nScriptType : GetI18NScriptTypeOfLanguage( GetDefaultLanguage() ); |
| |
| // create writing direction information: |
| if ( pParaPortion->aWritingDirectionInfos.empty() ) |
| InitWritingDirections( nPara ); |
| |
| // i89825: Use CTL font for numbers embedded into an RTL run: |
| WritingDirectionInfos& rDirInfos = pParaPortion->aWritingDirectionInfos; |
| for ( size_t n = 0; n < rDirInfos.size(); ++n ) |
| { |
| const xub_StrLen nStart = rDirInfos[n].nStartPos; |
| const xub_StrLen nEnd = rDirInfos[n].nEndPos; |
| const sal_uInt8 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 |
| { |
| size_t nIdx = 0; |
| |
| // Skip entries in ScriptArray which are not inside the RTL run: |
| while ( nIdx < rTypes.size() && rTypes[nIdx].nStartPos < nStart ) |
| ++nIdx; |
| |
| // Remove any entries *inside* the current run: |
| while ( nIdx < rTypes.size() && rTypes[nIdx].nEndPos <= nEnd ) |
| rTypes.erase( rTypes.begin()+nIdx ); |
| |
| // special case: |
| if(nIdx < rTypes.size() && rTypes[nIdx].nStartPos < nStart && rTypes[nIdx].nEndPos > nEnd) |
| { |
| rTypes.insert( rTypes.begin()+nIdx, ScriptTypePosInfo( rTypes[nIdx].nScriptType, (sal_uInt16)nEnd, rTypes[nIdx].nEndPos ) ); |
| rTypes[nIdx].nEndPos = nStart; |
| } |
| |
| if( nIdx ) |
| rTypes[nIdx - 1].nEndPos = nStart; |
| |
| rTypes.insert( rTypes.begin()+nIdx, ScriptTypePosInfo( i18n::ScriptType::COMPLEX, (sal_uInt16)nStart, (sal_uInt16)nEnd) ); |
| ++nIdx; |
| |
| if( nIdx < rTypes.size() ) |
| rTypes[nIdx].nStartPos = nEnd; |
| } |
| } |
| |
| #if OSL_DEBUG_LEVEL > 1 |
| sal_uInt16 nDebugStt = 0; |
| sal_uInt16 nDebugEnd = 0; |
| short nDebugType = 0; |
| for ( size_t n = 0; n < rTypes.size(); ++n ) |
| { |
| nDebugStt = rTypes[n].nStartPos; |
| nDebugEnd = rTypes[n].nEndPos; |
| nDebugType = rTypes[n].nScriptType; |
| } |
| #endif |
| } |
| } |
| |
| sal_uInt16 ImpEditEngine::GetScriptType( const EditPaM& rPaM, sal_uInt16* pEndPos ) const |
| { |
| sal_uInt16 nScriptType = 0; |
| |
| if ( pEndPos ) |
| *pEndPos = rPaM.GetNode()->Len(); |
| |
| if ( rPaM.GetNode()->Len() ) |
| { |
| sal_uInt16 nPara = GetEditDoc().GetPos( rPaM.GetNode() ); |
| ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara ); |
| if ( pParaPortion->aScriptInfos.empty() ) |
| ((ImpEditEngine*)this)->InitScriptTypes( nPara ); |
| |
| ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos; |
| sal_uInt16 nPos = rPaM.GetIndex(); |
| for ( size_t n = 0; n < rTypes.size(); 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() ); |
| } |
| |
| sal_uInt16 ImpEditEngine::GetScriptType( const EditSelection& rSel ) const |
| { |
| EditSelection aSel( rSel ); |
| aSel.Adjust( aEditDoc ); |
| |
| short nScriptType = 0; |
| |
| sal_uInt16 nStartPara = GetEditDoc().GetPos( aSel.Min().GetNode() ); |
| sal_uInt16 nEndPara = GetEditDoc().GetPos( aSel.Max().GetNode() ); |
| |
| for ( sal_uInt16 nPara = nStartPara; nPara <= nEndPara; nPara++ ) |
| { |
| ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara ); |
| if ( pParaPortion->aScriptInfos.empty() ) |
| ((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. |
| sal_uInt16 nS = ( nPara == nStartPara ) ? aSel.Min().GetIndex() : 0; |
| sal_uInt16 nE = ( nPara == nEndPara ) ? aSel.Max().GetIndex() : pParaPortion->GetNode()->Len(); |
| for ( size_t n = 0; n < rTypes.size(); 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() ); |
| } |
| |
| sal_Bool ImpEditEngine::IsScriptChange( const EditPaM& rPaM ) const |
| { |
| sal_Bool bScriptChange = sal_False; |
| |
| if ( rPaM.GetNode()->Len() ) |
| { |
| sal_uInt16 nPara = GetEditDoc().GetPos( rPaM.GetNode() ); |
| ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara ); |
| if ( pParaPortion->aScriptInfos.empty() ) |
| ((ImpEditEngine*)this)->InitScriptTypes( nPara ); |
| |
| ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos; |
| sal_uInt16 nPos = rPaM.GetIndex(); |
| for ( size_t n = 0; n < rTypes.size(); n++ ) |
| { |
| if ( rTypes[n].nStartPos == nPos ) |
| { |
| bScriptChange = sal_True; |
| break; |
| } |
| } |
| } |
| return bScriptChange; |
| } |
| |
| sal_Bool ImpEditEngine::HasScriptType( sal_uInt16 nPara, sal_uInt16 nType ) const |
| { |
| sal_Bool bTypeFound = sal_False; |
| |
| ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara ); |
| if ( pParaPortion->aScriptInfos.empty() ) |
| ((ImpEditEngine*)this)->InitScriptTypes( nPara ); |
| |
| ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos; |
| for ( size_t n = rTypes.size(); n && !bTypeFound; ) |
| { |
| if ( rTypes[--n].nScriptType == nType ) |
| bTypeFound = sal_True; |
| } |
| return bTypeFound; |
| } |
| |
| void ImpEditEngine::InitWritingDirections( sal_uInt16 nPara ) |
| { |
| ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara ); |
| WritingDirectionInfos& rInfos = pParaPortion->aWritingDirectionInfos; |
| rInfos.clear(); |
| |
| sal_Bool bCTL = sal_False; |
| ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos; |
| for ( size_t n = 0; n < rTypes.size(); n++ ) |
| { |
| if ( rTypes[n].nScriptType == i18n::ScriptType::COMPLEX ) |
| { |
| bCTL = sal_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; |
| |
| size_t nCount = ubidi_countRuns( pBidi, &nError ); |
| |
| int32_t nStart = 0; |
| int32_t nEnd; |
| UBiDiLevel nCurrDir; |
| |
| for ( size_t nIdx = 0; nIdx < nCount; ++nIdx ) |
| { |
| ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir ); |
| rInfos.push_back( WritingDirectionInfo( nCurrDir, (sal_uInt16)nStart, (sal_uInt16)nEnd ) ); |
| nStart = nEnd; |
| } |
| |
| ubidi_close( pBidi ); |
| } |
| |
| // No infos mean no CTL and default dir is L2R... |
| if ( rInfos.empty() ) |
| rInfos.push_back( WritingDirectionInfo( 0, 0, (sal_uInt16)pParaPortion->GetNode()->Len() ) ); |
| |
| } |
| |
| sal_Bool ImpEditEngine::IsRightToLeft( sal_uInt16 nPara ) const |
| { |
| sal_Bool bR2L = sal_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 already 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; |
| } |
| |
| sal_Bool ImpEditEngine::HasDifferentRTLLevels( const ContentNode* pNode ) |
| { |
| sal_uInt16 nPara = GetEditDoc().GetPos( (ContentNode*)pNode ); |
| ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara ); |
| |
| sal_Bool bHasDifferentRTLLevels = sal_False; |
| |
| sal_uInt16 nRTLLevel = IsRightToLeft( nPara ) ? 1 : 0; |
| for ( sal_uInt16 n = 0; n < pParaPortion->GetTextPortions().Count(); n++ ) |
| { |
| TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( n ); |
| if ( pTextPortion->GetRightToLeft() != nRTLLevel ) |
| { |
| bHasDifferentRTLLevels = sal_True; |
| break; |
| } |
| } |
| return bHasDifferentRTLLevels; |
| } |
| |
| |
| sal_uInt8 ImpEditEngine::GetRightToLeft( sal_uInt16 nPara, sal_uInt16 nPos, sal_uInt16* pStart, sal_uInt16* pEnd ) |
| { |
| // sal_uInt8 nRightToLeft = IsRightToLeft( nPara ) ? 1 : 0; |
| sal_uInt8 nRightToLeft = 0; |
| |
| ContentNode* pNode = aEditDoc.SaveGetObject( nPara ); |
| if ( pNode && pNode->Len() ) |
| { |
| ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara ); |
| if ( pParaPortion->aWritingDirectionInfos.empty() ) |
| InitWritingDirections( nPara ); |
| |
| // sal_uInt8 nType = 0; |
| WritingDirectionInfos& rDirInfos = pParaPortion->aWritingDirectionInfos; |
| for ( size_t n = 0; n < rDirInfos.size(); 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( sal_uInt16 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, sal_uInt16 nChars, EditUndoRemoveChars* pCurUndo ) |
| { |
| if ( IsUndoEnabled() && !IsInUndo() ) |
| { |
| XubString aStr( rPaM.GetNode()->Copy( rPaM.GetIndex(), nChars ) ); |
| |
| // Pruefen, ob Attribute geloescht oder geaendert werden: |
| sal_uInt16 nStart = rPaM.GetIndex(); |
| sal_uInt16 nEnd = nStart + nChars; |
| CharAttribArray& rAttribs = rPaM.GetNode()->GetCharAttribs().GetAttribs(); |
| // sal_uInt16 nAttrs = rAttribs.Count(); |
| for ( sal_uInt16 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, sal_uInt16 nNewPos ) |
| { |
| aOldPositions.Justify(); |
| sal_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; |
| } |
| |
| sal_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( (sal_uInt16)aOldPositions.Min() ); |
| |
| } |
| else if ( nNewPos == nParaCount ) |
| { |
| pRecalc1 = GetParaPortions().GetObject( (sal_uInt16)(nParaCount-1) ); |
| pRecalc2 = GetParaPortions().GetObject( (sal_uInt16)aOldPositions.Max() ); |
| } |
| |
| if ( aOldPositions.Min() == 0 ) // Move from Start |
| { |
| pRecalc3 = GetParaPortions().GetObject( 0 ); |
| pRecalc4 = GetParaPortions().GetObject( |
| sal::static_int_cast< sal_uInt16 >( aOldPositions.Max()+1 ) ); |
| } |
| else if ( (sal_uInt16)aOldPositions.Max() == (nParaCount-1) ) |
| { |
| pRecalc3 = GetParaPortions().GetObject( (sal_uInt16)aOldPositions.Max() ); |
| pRecalc4 = GetParaPortions().GetObject( (sal_uInt16)(aOldPositions.Min()-1) ); |
| } |
| |
| MoveParagraphsInfo aMoveParagraphsInfo( sal::static_int_cast< sal_uInt16 >(aOldPositions.Min()), sal::static_int_cast< sal_uInt16 >(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; |
| sal_uInt16 i; |
| for ( i = (sal_uInt16)aOldPositions.Min(); i <= (sal_uInt16)aOldPositions.Max(); i++ ) |
| { |
| // Immer aOldPositions.Min(), da Remove(). |
| ParaPortion* pTmpPortion = GetParaPortions().GetObject( (sal_uInt16)aOldPositions.Min() ); |
| GetParaPortions().Remove( (sal_uInt16)aOldPositions.Min() ); |
| aEditDoc.Remove( (sal_uInt16)aOldPositions.Min() ); |
| aTmpPortionList.Insert( pTmpPortion, aTmpPortionList.Count() ); |
| } |
| |
| sal_uInt16 nRealNewPos = pDestPortion ? GetParaPortions().GetPos( pDestPortion ) : GetParaPortions().Count(); |
| DBG_ASSERT( nRealNewPos != USHRT_MAX, "ImpMoveParagraphs: Ungueltige Position!" ); |
| |
| for ( i = 0; i < (sal_uInt16)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< sal_uInt16 >(aOldPositions.Min()); |
| aNotify.nParam2 = sal::static_int_cast< sal_uInt16 >(aOldPositions.Max()); |
| CallNotify( aNotify ); |
| } |
| |
| aEditDoc.SetModified( sal_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, sal_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)" ); |
| |
| // #120020# it is possible that left and right are *not* in the desired order (left/right) |
| // so correct it. This correction is needed, else an invalid SfxLinkUndoAction will be |
| // created from ConnectParagraphs below. Assert this situation, it should be corrected by the |
| // caller. |
| if(aEditDoc.GetPos( pLeft ) > aEditDoc.GetPos( pRight )) |
| { |
| OSL_ENSURE(false, "ImpConnectParagraphs wit wrong order of pLeft/pRight nodes (!)"); |
| std::swap(pLeft, pRight); |
| } |
| |
| sal_uInt16 nParagraphTobeDeleted = aEditDoc.GetPos( pRight ); |
| DeletedNodeInfo* pInf = new DeletedNodeInfo( (sal_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(), sal_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: |
| sal_uInt16 nRWrongs = pRight->GetWrongList()->Count(); |
| for ( sal_uInt16 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 ( sal_uInt16 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, sal_uInt8 nMode, sal_uInt8 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()) |
| { |
| const xub_StrLen nLen(aCurPos.GetNode()->Len()); |
| |
| // #120020# when 0 == nLen, aDelStart needs to be adapted, not |
| // aDelEnd. This would (and did) lead to a wrong order in the |
| // ImpConnectParagraphs call later. |
| if(nLen) |
| { |
| // 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 |
| { |
| aDelStart = WordLeft(aCurPos); |
| } |
| } |
| } |
| 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) |
| sal_Bool bSpecialBackward = ( ( nMode == DEL_LEFT ) && ( nDelMode == DELMODE_SIMPLE ) ) |
| ? sal_True : sal_False; |
| if ( aStatus.IsAnyOutliner() ) |
| bSpecialBackward = sal_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" ); |
| |
| sal_uInt16 nStartNode = aEditDoc.GetPos( aStartPaM.GetNode() ); |
| sal_uInt16 nEndNode = aEditDoc.GetPos( aEndPaM.GetNode() ); |
| |
| DBG_ASSERT( nEndNode != USHRT_MAX, "Start > End ?!" ); |
| DBG_ASSERT( nStartNode <= nEndNode, "Start > End ?!" ); |
| |
| // Alle Nodes dazwischen entfernen.... |
| for ( sal_uLong z = nStartNode+1; z < nEndNode; z++ ) |
| { |
| // Immer nStartNode+1, wegen Remove()! |
| ImpRemoveParagraph( nStartNode+1 ); |
| } |
| |
| if ( aStartPaM.GetNode() != aEndPaM.GetNode() ) |
| { |
| // Den Rest des StartNodes... |
| sal_uInt16 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 |
| { |
| sal_uInt16 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( sal_uInt16 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( (sal_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(), sal_False ); |
| delete pNode; |
| } |
| } |
| |
| EditPaM ImpEditEngine::AutoCorrect( const EditSelection& rCurSel, xub_Unicode c, sal_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 sal_False.) |
| sal_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 ) ); |
| } |
| sal_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(); |
| sal_uInt16 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, sal_Bool bOverwrite, sal_Bool bIsUserInput ) |
| { |
| DBG_ASSERT( c != '\t', "Tab bei InsertText ?" ); |
| DBG_ASSERT( c != '\n', "Zeilenumbruch bei InsertText ?" ); |
| |
| EditPaM aPaM( rCurSel.Min() ); |
| |
| sal_Bool bDoOverwrite = ( bOverwrite && |
| ( aPaM.GetIndex() < aPaM.GetNode()->Len() ) ) ? sal_True : sal_False; |
| |
| sal_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 ).getStr(), nChgLen ); |
| |
| // select text from first pos to be changed to current pos |
| EditSelection aSel( EditPaM( aPaM.GetNode(), (sal_uInt16) 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 ); |
| sal_Bool bTryMerge = ( !bDoOverwrite && ( c != ' ' ) ) ? sal_True : sal_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 |
| |
| // get word boundaries in order to clear possible WrongList entries |
| // and invalidate all the necessary text (everything after and including the |
| // start of the word) |
| // #i107201# do the expensive SelectWord call only if online spelling is active |
| EditSelection aCurWord; |
| if ( GetStatus().DoOnlineSpelling() ) |
| aCurWord = SelectWord( aCurPaM, i18n::WordType::DICTIONARY_WORD ); |
| |
| 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! |
| |
| // #117400 |
| // The loop run variable must be capable to hold STRLEN_MAX+1, |
| // that with STRING32 would be SAL_MAX_INT32+1 but with 16-bit is 0xFFFF+1 |
| sal_uInt32 nStart = 0; |
| while ( nStart < aText.Len() ) |
| { |
| sal_uInt32 nEnd = aText.Search( LINE_SEP, static_cast<xub_StrLen>(nStart) ); |
| if ( nEnd == STRING_NOTFOUND ) |
| nEnd = aText.Len(); // nicht dereferenzieren! |
| |
| // Start == End => Leerzeile |
| if ( nEnd > nStart ) |
| { |
| XubString aLine( aText, nStart, static_cast<xub_StrLen>(nEnd-nStart) ); |
| xub_StrLen nChars = aPaM.GetNode()->Len() + aLine.Len(); |
| if ( nChars > MAXCHARSINPARA ) |
| { |
| xub_StrLen 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 |
| { |
| sal_uInt32 nStart2 = 0; |
| while ( nStart2 < aLine.Len() ) |
| { |
| sal_uInt32 nEnd2 = aLine.Search( '\t', static_cast<xub_StrLen>(nStart2) ); |
| if ( nEnd2 == STRING_NOTFOUND ) |
| nEnd2 = aLine.Len(); // nicht dereferenzieren! |
| |
| if ( nEnd2 > nStart2 ) |
| aPaM = aEditDoc.InsertText( aPaM, XubString( aLine, |
| static_cast<xub_StrLen>(nStart2), |
| static_cast<xub_StrLen>(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" ); |
| |
| if ( GetStatus().DoOnlineSpelling() ) |
| { |
| // now remove the Wrongs (red spell check marks) from both words... |
| WrongList *pWrongs = aCurPaM.GetNode()->GetWrongList(); |
| if (pWrongs && pWrongs->HasWrongs()) |
| pWrongs->ClearWrongs( aCurWord.Min().GetIndex(), aPaM.GetIndex(), aPaM.GetNode() ); |
| // ... and mark both words as 'to be checked again' |
| pPortion->MarkInvalid( aCurWord.Min().GetIndex(), aLine.Len() ); |
| } |
| else |
| 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, sal_Bool bKeepEndingAttribs ) |
| { |
| EditPaM aPaM; |
| if ( rCurSel.HasRange() ) |
| aPaM = ImpDeleteSelection( rCurSel ); |
| else |
| aPaM = rCurSel.Max(); |
| |
| return ImpInsertParaBreak( aPaM, bKeepEndingAttribs ); |
| } |
| |
| EditPaM ImpEditEngine::ImpInsertParaBreak( const EditPaM& rPaM, sal_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: |
| sal_uInt16 nLWrongs = pLWrongs->Count(); |
| for ( sal_uInt16 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; |
| } |
| sal_uInt16 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. |
| sal_uInt16 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( sal_uInt16 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() ) |
| { |
| sal_uInt16 nPara = aEditDoc.GetPos( aPaM.GetNode() ); |
| DBG_ASSERT( nPara > 0, "AutoIndenting: Fehler!" ); |
| XubString aPrevParaText( GetEditDoc().GetParaAsString( nPara-1 ) ); |
| sal_uInt16 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; |
| } |
| |
| sal_Bool ImpEditEngine::UpdateFields() |
| { |
| sal_Bool bChanges = sal_False; |
| sal_uInt16 nParas = GetEditDoc().Count(); |
| for ( sal_uInt16 nPara = 0; nPara < nParas; nPara++ ) |
| { |
| sal_Bool bChangesInPara = sal_False; |
| ContentNode* pNode = GetEditDoc().GetObject( nPara ); |
| DBG_ASSERT( pNode, "NULL-Pointer im Doc" ); |
| CharAttribArray& rAttribs = pNode->GetCharAttribs().GetAttribs(); |
| // sal_uInt16 nAttrs = rAttribs.Count(); |
| for ( sal_uInt16 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 = sal_True; |
| bChangesInPara = sal_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, sal_uInt16 nFlags ) |
| { |
| DBG_ASSERT( GetUpdateMode(), "Darf bei Update=sal_False nicht erreicht werden: PaMtoEditCursor" ); |
| |
| Rectangle aEditCursor; |
| long nY = 0; |
| for ( sal_uInt16 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, sal_Bool bSmart ) |
| { |
| DBG_ASSERT( GetUpdateMode(), "Darf bei Update=sal_False nicht erreicht werden: GetPaM" ); |
| |
| long nY = 0; |
| long nTmpHeight; |
| EditPaM aPaM; |
| sal_uInt16 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=sal_False nicht verwendet werden: GetTextHeight" ); |
| DBG_ASSERT( IsFormatted() || IsFormatting(), "GetTextHeight: Nicht formatiert" ); |
| return nCurTextHeight; |
| } |
| |
| sal_uInt32 ImpEditEngine::CalcTextWidth( sal_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... |
| // -------------------------------------------------- |
| sal_uInt16 nParas = GetParaPortions().Count(); |
| // sal_uInt16 nBiggestPara = 0; |
| // sal_uInt16 nBiggestLine = 0; |
| for ( sal_uInt16 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... |
| // -------------------------------------------------- |
| sal_uLong nLines = pPortion->GetLines().Count(); |
| for ( sal_uInt16 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, sal_Bool bIgnoreExtraSpace ) |
| { |
| sal_uInt16 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; |
| sal_uInt16 nPos = pLine->GetStart(); |
| for ( sal_uInt16 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=sal_False nicht verwendet werden: CalcTextHeight" ); |
| sal_uInt32 nY = 0; |
| for ( sal_uInt16 nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ ) |
| nY += GetParaPortions()[nPortion]->GetHeight(); |
| return nY; |
| } |
| |
| sal_uInt16 ImpEditEngine::GetLineCount( sal_uInt16 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( sal_uInt16 nParagraph, sal_uInt16 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*/sal_uInt16 &rStart, /*out*/sal_uInt16 &rEnd, sal_uInt16 nParagraph, sal_uInt16 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(); |
| } |
| } |
| |
| sal_uInt16 ImpEditEngine::GetLineNumberAtIndex( sal_uInt16 nPara, sal_uInt16 nIndex ) const |
| { |
| sal_uInt16 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 sal_uInt16 nLineCount = GetLineCount( nPara ); |
| if (nIndex == pNode->Len()) |
| nLineNo = nLineCount > 0 ? nLineCount - 1 : 0; |
| else if (bValidIndex) // nIndex < pNode->Len() |
| { |
| sal_uInt16 nStart = USHRT_MAX, nEnd = USHRT_MAX; |
| for (sal_uInt16 i = 0; i < nLineCount && nLineNo == 0xFFFF; ++i) |
| { |
| GetLineBoundaries( nStart, nEnd, nPara, i ); |
| if (nStart <= nIndex && nIndex < nEnd) |
| nLineNo = i; |
| } |
| } |
| } |
| return nLineNo; |
| } |
| |
| sal_uInt16 ImpEditEngine::GetLineHeight( sal_uInt16 nParagraph, sal_uInt16 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( sal_uInt16 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() |
| { |
| sal_uInt16 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 ( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ ) |
| { |
| EditView* pView = aEditViews.GetObject(nView); |
| DBG_CHKOBJ( pView, EditView, 0 ); |
| EditSelection aCurSel( pView->pImpEditView->GetEditSelection() ); |
| sal_Bool bChanged = sal_False; |
| for ( sal_uInt16 n = 0; n < nInvNodes; n++ ) |
| { |
| DeletedNodeInfo* pInf = aDeletedNodes.GetObject( n ); |
| if ( ( ( sal_uLong )(aCurSel.Min().GetNode()) == pInf->GetInvalidAdress() ) || |
| ( ( sal_uLong )(aCurSel.Max().GetNode()) == pInf->GetInvalidAdress() ) ) |
| { |
| // ParaPortions verwenden, da jetzt auch versteckte |
| // Absaetze beruecksichtigt werden muessen! |
| sal_uInt16 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: |
| sal_uInt16 nCurPara = nPara; |
| sal_uInt16 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=sal_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 ( sal_uInt16 n = 0; n < nInvNodes; n++ ) |
| { |
| DeletedNodeInfo* pInf = aDeletedNodes.GetObject( n ); |
| delete pInf; |
| } |
| aDeletedNodes.Remove( 0, aDeletedNodes.Count() ); |
| } |
| |
| EditSelection ImpEditEngine::ConvertSelection( sal_uInt16 nStartPara, sal_uInt16 nStartPos, |
| sal_uInt16 nEndPara, sal_uInt16 nEndPos ) const |
| { |
| EditSelection aNewSelection; |
| |
| // Start... |
| ContentNode* pNode = aEditDoc.SaveGetObject( nStartPara ); |
| sal_uInt16 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; |
| } |
| |
| sal_uInt16 nPos = aTmpSel.Min().GetIndex(); |
| ContentNode* pNode = aTmpSel.Min().GetNode(); |
| if ( nPos >= pNode->Len() ) |
| return aMatchSel; |
| |
| sal_uInt16 nMatchChar = aGroupChars.Search( pNode->GetChar( nPos ) ); |
| if ( nMatchChar != STRING_NOTFOUND ) |
| { |
| sal_uInt16 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 ); |
| |
| sal_uInt16 nCur = aTmpSel.Min().GetIndex()+1; |
| sal_uInt16 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 ); |
| |
| sal_uInt16 nCur = aTmpSel.Min().GetIndex()-1; |
| sal_uInt16 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 = sal_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( sal_True ); |
| WriteBin( pDataObj->GetStream(), aSelection, sal_True ); |
| pDataObj->GetStream().Seek( 0 ); |
| SvxFontItem::EnableStoreUnicodeNames( sal_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, sal_Bool bUseSpecial ) |
| { |
| EditSelection aNewSelection( rPaM ); |
| |
| if ( rxDataObj.is() ) |
| { |
| datatransfer::DataFlavor aFlavor; |
| sal_Bool bDone = sal_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 = sal_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 = sal_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 = sal_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 ); |
| sal_uInt16 nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX ) |
| ? GetYValue( rLSItem.GetInterLineSpace() ) : 0; |
| |
| // erst von vorne... |
| sal_uInt16 nFirstInvalid = 0xFFFF; |
| sal_uInt16 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(); |
| |
| sal_uInt16 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, sal_Bool bSmart ) |
| { |
| DBG_ASSERT( pPortion->IsVisible(), "Wozu GetPaM() bei einem unsichtbaren Absatz?" ); |
| DBG_ASSERT( IsFormatted(), "GetPaM: Nicht formatiert" ); |
| |
| sal_uInt16 nCurIndex = 0; |
| EditPaM aPaM; |
| aPaM.SetNode( pPortion->GetNode() ); |
| |
| const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL ); |
| sal_uInt16 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 ( sal_uInt16 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; |
| } |
| |
| sal_uInt16 ImpEditEngine::GetChar( ParaPortion* pParaPortion, EditLine* pLine, long nXPos, sal_Bool bSmart ) |
| { |
| DBG_ASSERT( pLine, "Keine Zeile erhalten: GetChar" ); |
| |
| sal_uInt16 nChar = 0xFFFF; |
| sal_uInt16 nCurIndex = pLine->GetStart(); |
| |
| |
| // Search best matching portion with GetPortionXOffset() |
| for ( sal_uInt16 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 |
| { |
| sal_uInt16 nMax = pPortion->GetLen(); |
| sal_uInt16 nOffset = 0xFFFF; |
| sal_uInt16 nTmpCurIndex = nChar - pLine->GetStart(); |
| |
| long nXInPortion = nXPos - nXLeft; |
| if ( pPortion->IsRightToLeft() ) |
| nXInPortion = nXRight - nXPos; |
| |
| // Search in Array... |
| for ( sal_uInt16 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 ); |
| sal_uInt16 nScriptType = GetScriptType( aPaM ); |
| if ( nScriptType == i18n::ScriptType::COMPLEX ) |
| { |
| uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() ); |
| sal_Int32 nCount = 1; |
| lang::Locale aLocale = GetLocale( aPaM ); |
| sal_uInt16 nRight = (sal_uInt16)_xBI->nextCharacters( *pParaPortion->GetNode(), nChar, aLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount ); |
| sal_uInt16 nLeft = (sal_uInt16)_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; |
| |
| sal_uInt16 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, sal_uInt16 nTextPortion ) |
| { |
| long nX = pLine->GetStartPosX(); |
| |
| for ( sal_uInt16 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; |
| } |
| } |
| |
| sal_uInt16 nPara = GetEditDoc().GetPos( pParaPortion->GetNode() ); |
| sal_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, sal_uInt16 nIndex, sal_Bool bPreferPortionStart ) |
| { |
| DBG_ASSERT( pLine, "Keine Zeile erhalten: GetXPos" ); |
| DBG_ASSERT( ( nIndex >= pLine->GetStart() ) && ( nIndex <= pLine->GetEnd() ) , "GetXPos muss richtig gerufen werden!" ); |
| |
| sal_Bool bDoPreferPortionStart = bPreferPortionStart; |
| // Assure that the portion belongs to this line: |
| if ( nIndex == pLine->GetStart() ) |
| bDoPreferPortionStart = sal_True; |
| else if ( nIndex == pLine->GetEnd() ) |
| bDoPreferPortionStart = sal_False; |
| |
| sal_uInt16 nTextPortionStart = 0; |
| sal_uInt16 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, sal_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() ) |
| { |
| sal_uInt16 nPos = nIndex - 1 - pLine->GetStart(); |
| if( nPos >= pLine->GetCharPosArray().Count() ) |
| { |
| nPos = pLine->GetCharPosArray().Count()-1; |
| DBG_ERROR("svx::ImpEditEngine::GetXPos(), index out of range!"); |
| } |
| |
| // old code restored see #i112788 (which leaves #i74188 unfixed again) |
| 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 ) |
| { |
| sal_uInt8 nType = GetCharTypeForCompression( pParaPortion->GetNode()->GetChar( nIndex ) ); |
| if ( nType == CHAR_PUNCTUATIONRIGHT ) |
| { |
| sal_uInt16 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 ( sal_uInt16 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 ); |
| sal_uInt16 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; |
| } |
| |
| sal_uInt16 nPortion = GetParaPortions().GetPos( pPortion ); |
| if ( nPortion || aStatus.ULSpaceFirstParagraph() ) |
| { |
| sal_uInt16 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: |
| sal_uInt16 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: |
| sal_uInt16 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 ) |
| { |
| sal_uInt16 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, sal_uInt16 nIndex, sal_uInt16 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 ); |
| sal_uInt16 nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX ) |
| ? GetYValue( rLSItem.GetInterLineSpace() ) : 0; |
| |
| sal_uInt16 nCurIndex = 0; |
| DBG_ASSERT( pPortion->GetLines().Count(), "Leere ParaPortion in GetEditCursor!" ); |
| EditLine* pLine = 0; |
| sal_Bool bEOL = ( nFlags & GETCRSR_ENDOFLINE ) ? sal_True : sal_False; |
| for ( sal_uInt16 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 ) ? sal_True : sal_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, sal_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 ( sal_uInt16 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( sal_False, sal_True ); |
| } |
| } |
| |
| vos::ORef<SvxForbiddenCharactersTable> ImpEditEngine::GetForbiddenCharsTable( sal_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; |
| } |
| |
| sal_Bool ImpEditEngine::IsVisualCursorTravelingEnabled() |
| { |
| sal_Bool bVisualCursorTravaling = sal_False; |
| |
| if( !pCTLOptions ) |
| pCTLOptions = new SvtCTLOptions; |
| |
| if ( pCTLOptions->IsCTLFontEnabled() && ( pCTLOptions->GetCTLCursorMovement() == SvtCTLOptions::MOVEMENT_VISUAL ) ) |
| { |
| bVisualCursorTravaling = sal_True; |
| } |
| |
| return bVisualCursorTravaling; |
| |
| } |
| |
| sal_Bool ImpEditEngine::DoVisualCursorTraveling( const ContentNode* ) |
| { |
| // Don't check if it's necessary, because we also need it when leaving the paragraph |
| return IsVisualCursorTravelingEnabled(); |
| /* |
| sal_Bool bDoVisualCursorTraveling = sal_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; |
| } |