merge the formfield patch from ooo-build
[ooovba.git] / svx / source / editeng / impedit2.cxx
blob9f543cb8b6adb5198d208b10a0dc43164a7b373d
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: impedit2.cxx,v $
10 * $Revision: 1.124.40.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_svx.hxx"
34 #include <eeng_pch.hxx>
36 #include <svx/lspcitem.hxx>
37 #include <svx/flditem.hxx>
38 #include <impedit.hxx>
39 #include <svx/editeng.hxx>
40 #include <svx/editview.hxx>
41 #include <editdbg.hxx>
42 #include <eerdll2.hxx>
43 #include <eerdll.hxx>
44 #include <edtspell.hxx>
45 #include <eeobj.hxx>
46 #include <txtrange.hxx>
47 #include <svtools/urlbmk.hxx>
48 #include <svtools/colorcfg.hxx>
49 #include <svtools/ctloptions.hxx>
50 #include <acorrcfg.hxx>
52 #include <svx/fhgtitem.hxx>
53 #include <svx/lrspitem.hxx>
54 #include <svx/ulspitem.hxx>
55 #include <svx/wghtitem.hxx>
56 #include <svx/postitem.hxx>
57 #include <svx/udlnitem.hxx>
58 #include <svx/adjitem.hxx>
59 #include <svx/scripttypeitem.hxx>
60 #include <svx/frmdiritem.hxx>
61 #include <fontitem.hxx>
62 #include <sfx2/viewfrm.hxx>
63 #include <sfx2/fcontnr.hxx>
64 #include <sfx2/dispatch.hxx>
65 #include <vcl/cmdevt.h>
67 #ifndef SVX_LIGHT
68 #ifndef _SFXFRAME_HXX //autogen
69 #include <sfx2/frame.hxx>
70 #endif
71 #endif
72 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
73 #include <com/sun/star/i18n/WordType.hpp>
74 #include <com/sun/star/i18n/ScriptType.hpp>
75 #include <com/sun/star/lang/Locale.hpp>
76 #include <com/sun/star/text/CharacterCompressionType.hpp>
77 #include <com/sun/star/i18n/InputSequenceCheckMode.hpp>
79 #include <comphelper/processfactory.hxx>
81 #include <sot/formats.hxx>
83 #include <unicode/ubidi.h>
85 using namespace ::com::sun::star;
87 USHORT lcl_CalcExtraSpace( ParaPortion*, const SvxLineSpacingItem& rLSItem )
89 USHORT nExtra = 0;
90 /* if ( ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP )
91 && ( rLSItem.GetPropLineSpace() != 100 ) )
93 // ULONG nH = pPortion->GetNode()->GetCharAttribs().GetDefFont().GetSize().Height();
94 ULONG nH = pPortion->GetLines().GetObject( 0 )->GetHeight();
95 long n = nH * rLSItem.GetPropLineSpace();
96 n /= 100;
97 n -= nH; // nur den Abstand
98 if ( n > 0 )
99 nExtra = (USHORT)n;
101 else */
102 if ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
104 nExtra = rLSItem.GetInterLineSpace();
107 return nExtra;
110 \f// ----------------------------------------------------------------------
111 // class ImpEditEngine
112 // ----------------------------------------------------------------------
114 ImpEditEngine::ImpEditEngine( EditEngine* pEE, SfxItemPool* pItemPool ) :
115 aPaperSize( 0x7FFFFFFF, 0x7FFFFFFF ),
116 aMinAutoPaperSize( 0x0, 0x0 ),
117 aMaxAutoPaperSize( 0x7FFFFFFF, 0x7FFFFFFF ),
118 aEditDoc( pItemPool ),
119 aWordDelimiters( RTL_CONSTASCII_USTRINGPARAM( " .,;:-'`'?!_=\"{}()[]\0xFF" ) ),
120 aGroupChars( RTL_CONSTASCII_USTRINGPARAM( "{}()[]" ) )
122 pEditEngine = pEE;
123 pRefDev = NULL;
124 pVirtDev = NULL;
125 pEmptyItemSet = NULL;
126 pActiveView = NULL;
127 pSpellInfo = NULL;
128 pConvInfo = NULL;
129 pTextObjectPool = NULL;
130 mpIMEInfos = NULL;
131 pStylePool = NULL;
132 pUndoManager = NULL;
133 pUndoMarkSelection = NULL;
134 pTextRanger = NULL;
135 pColorConfig = NULL;
136 pCTLOptions = NULL;
138 nCurTextHeight = 0;
139 nBlockNotifications = 0;
140 nBigTextObjectStart = 20;
142 nStretchX = 100;
143 nStretchY = 100;
145 bInSelection = FALSE;
146 bOwnerOfRefDev = FALSE;
147 bDowning = FALSE;
148 bIsInUndo = FALSE;
149 bIsFormatting = FALSE;
150 bFormatted = FALSE;
151 bUpdate = TRUE;
152 bUseAutoColor = TRUE;
153 bForceAutoColor = FALSE;
154 bAddExtLeading = FALSE;
155 bUndoEnabled = TRUE;
156 bCallParaInsertedOrDeleted = FALSE;
157 bImpConvertFirstCall= FALSE;
158 bFirstWordCapitalization = TRUE;
160 eDefLanguage = LANGUAGE_DONTKNOW;
161 maBackgroundColor = COL_AUTO;
163 nAsianCompressionMode = text::CharacterCompressionType::NONE;
164 bKernAsianPunctuation = FALSE;
166 eDefaultHorizontalTextDirection = EE_HTEXTDIR_DEFAULT;
169 aStatus.GetControlWord() = EE_CNTRL_USECHARATTRIBS | EE_CNTRL_DOIDLEFORMAT |
170 EE_CNTRL_PASTESPECIAL | EE_CNTRL_UNDOATTRIBS |
171 EE_CNTRL_ALLOWBIGOBJS | EE_CNTRL_RTFSTYLESHEETS |
172 EE_CNTRL_FORMAT100;
174 aSelEngine.SetFunctionSet( &aSelFuncSet );
176 aStatusTimer.SetTimeout( 200 );
177 aStatusTimer.SetTimeoutHdl( LINK( this, ImpEditEngine, StatusTimerHdl ) );
179 aIdleFormatter.SetTimeout( 5 );
180 aIdleFormatter.SetTimeoutHdl( LINK( this, ImpEditEngine, IdleFormatHdl ) );
182 aOnlineSpellTimer.SetTimeout( 100 );
183 aOnlineSpellTimer.SetTimeoutHdl( LINK( this, ImpEditEngine, OnlineSpellHdl ) );
185 pRefDev = EE_DLL()->GetGlobalData()->GetStdRefDevice();
187 // Ab hier wird schon auf Daten zugegriffen!
188 SetRefDevice( pRefDev );
189 InitDoc( FALSE );
191 bCallParaInsertedOrDeleted = TRUE;
193 aEditDoc.SetModifyHdl( LINK( this, ImpEditEngine, DocModified ) );
195 mbLastTryMerge = FALSE;
198 ImpEditEngine::~ImpEditEngine()
200 aStatusTimer.Stop();
201 aOnlineSpellTimer.Stop();
202 aIdleFormatter.Stop();
204 // das Zerstoeren von Vorlagen kann sonst unnoetiges Formatieren ausloesen,
205 // wenn eine Parent-Vorlage zerstoert wird.
206 // Und das nach dem Zerstoeren der Daten!
207 bDowning = TRUE;
208 SetUpdateMode( FALSE );
210 delete pVirtDev;
211 delete pEmptyItemSet;
212 delete pUndoManager;
213 delete pTextRanger;
214 delete mpIMEInfos;
215 delete pColorConfig;
216 delete pCTLOptions;
217 if ( bOwnerOfRefDev )
218 delete pRefDev;
219 delete pSpellInfo;
222 void ImpEditEngine::SetRefDevice( OutputDevice* pRef )
224 if ( bOwnerOfRefDev )
225 delete pRefDev;
227 pRefDev = pRef;
228 bOwnerOfRefDev = FALSE;
230 if ( !pRef )
231 pRefDev = EE_DLL()->GetGlobalData()->GetStdRefDevice();
233 nOnePixelInRef = (USHORT)pRefDev->PixelToLogic( Size( 1, 0 ) ).Width();
235 if ( IsFormatted() )
237 FormatFullDoc();
238 UpdateViews( (EditView*) 0);
242 void ImpEditEngine::SetRefMapMode( const MapMode& rMapMode )
244 if ( GetRefDevice()->GetMapMode() == rMapMode )
245 return;
247 // Wenn RefDev == GlobalRefDev => eigenes anlegen!
248 if ( !bOwnerOfRefDev && ( pRefDev == EE_DLL()->GetGlobalData()->GetStdRefDevice() ) )
250 pRefDev = new VirtualDevice;
251 pRefDev->SetMapMode( MAP_TWIP );
252 SetRefDevice( pRefDev );
253 bOwnerOfRefDev = TRUE;
255 pRefDev->SetMapMode( rMapMode );
256 nOnePixelInRef = (USHORT)pRefDev->PixelToLogic( Size( 1, 0 ) ).Width();
257 if ( IsFormatted() )
259 FormatFullDoc();
260 UpdateViews( (EditView*) 0);
264 void ImpEditEngine::InitDoc( BOOL bKeepParaAttribs )
266 USHORT nParas = aEditDoc.Count();
267 for ( USHORT n = bKeepParaAttribs ? 1 : 0; n < nParas; n++ )
269 if ( aEditDoc[n]->GetStyleSheet() )
270 EndListening( *aEditDoc[n]->GetStyleSheet(), FALSE );
273 if ( bKeepParaAttribs )
274 aEditDoc.RemoveText();
275 else
276 aEditDoc.Clear();
278 GetParaPortions().Reset();
280 ParaPortion* pIniPortion = new ParaPortion( aEditDoc[0] );
281 GetParaPortions().Insert( pIniPortion, 0 );
283 bFormatted = FALSE;
285 if ( IsCallParaInsertedOrDeleted() )
287 GetEditEnginePtr()->ParagraphDeleted( EE_PARA_ALL );
288 GetEditEnginePtr()->ParagraphInserted( 0 );
291 #ifndef SVX_LIGHT
292 if ( GetStatus().DoOnlineSpelling() )
293 aEditDoc.GetObject( 0 )->CreateWrongList();
294 #endif // !SVX_LIGHT
297 EditPaM ImpEditEngine::DeleteSelected( EditSelection aSel )
299 EditPaM aPaM ( ImpDeleteSelection( aSel ) );
300 return aPaM;
303 XubString ImpEditEngine::GetSelected( const EditSelection& rSel, const LineEnd eEnd ) const
305 XubString aText;
306 if ( !rSel.HasRange() )
307 return aText;
309 String aSep = EditDoc::GetSepStr( eEnd );
311 EditSelection aSel( rSel );
312 aSel.Adjust( aEditDoc );
314 ContentNode* pStartNode = aSel.Min().GetNode();
315 ContentNode* pEndNode = aSel.Max().GetNode();
316 USHORT nStartNode = aEditDoc.GetPos( pStartNode );
317 USHORT nEndNode = aEditDoc.GetPos( pEndNode );
319 DBG_ASSERT( nStartNode <= nEndNode, "Selektion nicht sortiert ?" );
321 // ueber die Absaetze iterieren...
322 for ( USHORT nNode = nStartNode; nNode <= nEndNode; nNode++ )
324 DBG_ASSERT( aEditDoc.SaveGetObject( nNode ), "Node nicht gefunden: GetSelected" );
325 ContentNode* pNode = aEditDoc.GetObject( nNode );
327 xub_StrLen nStartPos = 0;
328 xub_StrLen nEndPos = pNode->Len();
329 if ( nNode == nStartNode )
330 nStartPos = aSel.Min().GetIndex();
331 if ( nNode == nEndNode ) // kann auch == nStart sein!
332 nEndPos = aSel.Max().GetIndex();
334 aText += aEditDoc.GetParaAsString( pNode, nStartPos, nEndPos );
335 if ( nNode < nEndNode )
336 aText += aSep;
338 return aText;
341 BOOL ImpEditEngine::MouseButtonDown( const MouseEvent& rMEvt, EditView* pView )
343 GetSelEngine().SetCurView( pView );
344 SetActiveView( pView );
346 if ( GetAutoCompleteText().Len() )
347 SetAutoCompleteText( String(), TRUE );
349 GetSelEngine().SelMouseButtonDown( rMEvt );
350 // Sonderbehandlungen
351 EditSelection aCurSel( pView->pImpEditView->GetEditSelection() );
352 if ( !rMEvt.IsShift() )
354 if ( rMEvt.GetClicks() == 2 )
356 // damit die SelectionEngine weiss, dass Anker.
357 aSelEngine.CursorPosChanging( TRUE, FALSE );
359 EditSelection aNewSelection( SelectWord( aCurSel ) );
360 pView->pImpEditView->DrawSelection();
361 pView->pImpEditView->SetEditSelection( aNewSelection );
362 pView->pImpEditView->DrawSelection();
363 pView->ShowCursor( TRUE, TRUE );
365 else if ( rMEvt.GetClicks() == 3 )
367 // damit die SelectionEngine weiss, dass Anker.
368 aSelEngine.CursorPosChanging( TRUE, FALSE );
370 EditSelection aNewSelection( aCurSel );
371 aNewSelection.Min().SetIndex( 0 );
372 aNewSelection.Max().SetIndex( aCurSel.Min().GetNode()->Len() );
373 pView->pImpEditView->DrawSelection();
374 pView->pImpEditView->SetEditSelection( aNewSelection );
375 pView->pImpEditView->DrawSelection();
376 pView->ShowCursor( TRUE, TRUE );
379 return TRUE;
382 void ImpEditEngine::Command( const CommandEvent& rCEvt, EditView* pView )
384 GetSelEngine().SetCurView( pView );
385 SetActiveView( pView );
386 if ( rCEvt.GetCommand() == COMMAND_VOICE )
388 const CommandVoiceData* pData = rCEvt.GetVoiceData();
389 if ( pData->GetType() == VOICECOMMANDTYPE_DICTATION )
391 // Funktionen auf KeyEvents umbiegen, wenn keine entsprechende
392 // Methode an EditView/EditEngine, damit Undo konsistent bleibt.
394 SfxPoolItem* pNewAttr = NULL;
396 switch ( pData->GetCommand() )
398 case DICTATIONCOMMAND_UNKNOWN:
400 pView->InsertText( pData->GetText() );
402 break;
403 case DICTATIONCOMMAND_NEWPARAGRAPH:
405 pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_RETURN, 0 ) ) );
407 break;
408 case DICTATIONCOMMAND_NEWLINE:
410 pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_RETURN, KEY_SHIFT ) ) );
412 break;
413 case DICTATIONCOMMAND_TAB:
415 pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_TAB, 0 ) ) );
417 break;
418 case DICTATIONCOMMAND_LEFT:
420 pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_LEFT, KEY_MOD1 ) ) );
422 break;
423 case DICTATIONCOMMAND_RIGHT:
425 pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_RIGHT, KEY_MOD1 ) ) );
427 break;
428 case DICTATIONCOMMAND_UP:
430 pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_UP, 0 ) ) );
432 break;
433 case DICTATIONCOMMAND_DOWN:
435 pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_UP, 0 ) ) );
437 break;
438 case DICTATIONCOMMAND_UNDO:
440 pView->Undo();
442 break;
443 case DICTATIONCOMMAND_DEL:
445 pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_LEFT, KEY_MOD1|KEY_SHIFT ) ) );
446 pView->DeleteSelected();
448 break;
449 case DICTATIONCOMMAND_BOLD_ON:
451 pNewAttr = new SvxWeightItem( WEIGHT_BOLD, EE_CHAR_WEIGHT );
453 break;
454 case DICTATIONCOMMAND_BOLD_OFF:
456 pNewAttr = new SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT );
458 break;
459 case DICTATIONCOMMAND_ITALIC_ON:
461 pNewAttr = new SvxPostureItem( ITALIC_NORMAL, EE_CHAR_ITALIC );
463 break;
464 case DICTATIONCOMMAND_ITALIC_OFF:
466 pNewAttr = new SvxPostureItem( ITALIC_NORMAL, EE_CHAR_ITALIC );
468 break;
469 case DICTATIONCOMMAND_UNDERLINE_ON:
471 pNewAttr = new SvxUnderlineItem( UNDERLINE_SINGLE, EE_CHAR_UNDERLINE );
473 break;
474 case DICTATIONCOMMAND_UNDERLINE_OFF:
476 pNewAttr = new SvxUnderlineItem( UNDERLINE_NONE, EE_CHAR_UNDERLINE );
478 break;
481 if ( pNewAttr )
483 SfxItemSet aSet( GetEmptyItemSet() );
484 aSet.Put( *pNewAttr );
485 pView->SetAttribs( aSet );
486 delete pNewAttr;
490 else if ( rCEvt.GetCommand() == COMMAND_STARTEXTTEXTINPUT )
492 pView->DeleteSelected();
493 delete mpIMEInfos;
494 EditPaM aPaM = pView->GetImpEditView()->GetEditSelection().Max();
495 String aOldTextAfterStartPos = aPaM.GetNode()->Copy( aPaM.GetIndex() );
496 USHORT nMax = aOldTextAfterStartPos.Search( CH_FEATURE );
497 if ( nMax != STRING_NOTFOUND ) // don't overwrite features!
498 aOldTextAfterStartPos.Erase( nMax );
499 mpIMEInfos = new ImplIMEInfos( aPaM, aOldTextAfterStartPos );
500 mpIMEInfos->bWasCursorOverwrite = !pView->IsInsertMode();
501 UndoActionStart( EDITUNDO_INSERT );
503 else if ( rCEvt.GetCommand() == COMMAND_ENDEXTTEXTINPUT )
505 DBG_ASSERT( mpIMEInfos, "COMMAND_ENDEXTTEXTINPUT => Kein Start ?" );
506 if( mpIMEInfos )
508 // #102812# convert quotes in IME text
509 // works on the last input character, this is escpecially in Korean text often done
510 // quotes that are inside of the string are not replaced!
511 // Borrowed from sw: edtwin.cxx
512 if ( mpIMEInfos->nLen )
514 EditSelection aSel( mpIMEInfos->aPos );
515 aSel.Min().GetIndex() += mpIMEInfos->nLen-1;
516 aSel.Max().GetIndex() =
517 aSel.Max().GetIndex() + mpIMEInfos->nLen;
518 // #102812# convert quotes in IME text
519 // works on the last input character, this is escpecially in Korean text often done
520 // quotes that are inside of the string are not replaced!
521 const sal_Unicode nCharCode = aSel.Min().GetNode()->GetChar( aSel.Min().GetIndex() );
522 if ( ( GetStatus().DoAutoCorrect() ) && ( ( nCharCode == '\"' ) || ( nCharCode == '\'' ) ) )
524 aSel = DeleteSelected( aSel );
525 aSel = AutoCorrect( aSel, nCharCode, mpIMEInfos->bWasCursorOverwrite );
526 pView->pImpEditView->SetEditSelection( aSel );
530 ParaPortion* pPortion = FindParaPortion( mpIMEInfos->aPos.GetNode() );
531 pPortion->MarkSelectionInvalid( mpIMEInfos->aPos.GetIndex(), 0 );
533 BOOL bWasCursorOverwrite = mpIMEInfos->bWasCursorOverwrite;
535 delete mpIMEInfos;
536 mpIMEInfos = NULL;
538 FormatAndUpdate( pView );
540 pView->SetInsertMode( !bWasCursorOverwrite );
542 UndoActionEnd( EDITUNDO_INSERT );
544 else if ( rCEvt.GetCommand() == COMMAND_EXTTEXTINPUT )
546 DBG_ASSERT( mpIMEInfos, "COMMAND_EXTTEXTINPUT => Kein Start ?" );
547 if( mpIMEInfos )
549 const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();
551 if ( !pData->IsOnlyCursorChanged() )
553 EditSelection aSel( mpIMEInfos->aPos );
554 aSel.Max().GetIndex() =
555 aSel.Max().GetIndex() + mpIMEInfos->nLen;
556 aSel = DeleteSelected( aSel );
557 aSel = ImpInsertText( aSel, pData->GetText() );
559 if ( mpIMEInfos->bWasCursorOverwrite )
561 USHORT nOldIMETextLen = mpIMEInfos->nLen;
562 USHORT nNewIMETextLen = pData->GetText().Len();
564 if ( ( nOldIMETextLen > nNewIMETextLen ) &&
565 ( nNewIMETextLen < mpIMEInfos->aOldTextAfterStartPos.Len() ) )
567 // restore old characters
568 USHORT nRestore = nOldIMETextLen - nNewIMETextLen;
569 EditPaM aPaM( mpIMEInfos->aPos );
570 aPaM.GetIndex() = aPaM.GetIndex() + nNewIMETextLen;
571 ImpInsertText( aPaM, mpIMEInfos->aOldTextAfterStartPos.Copy( nNewIMETextLen, nRestore ) );
573 else if ( ( nOldIMETextLen < nNewIMETextLen ) &&
574 ( nOldIMETextLen < mpIMEInfos->aOldTextAfterStartPos.Len() ) )
576 // overwrite
577 USHORT nOverwrite = nNewIMETextLen - nOldIMETextLen;
578 if ( ( nOldIMETextLen + nOverwrite ) > mpIMEInfos->aOldTextAfterStartPos.Len() )
579 nOverwrite = mpIMEInfos->aOldTextAfterStartPos.Len() - nOldIMETextLen;
580 DBG_ASSERT( nOverwrite && (nOverwrite < 0xFF00), "IME Overwrite?!" );
581 EditPaM aPaM( mpIMEInfos->aPos );
582 aPaM.GetIndex() = aPaM.GetIndex() + nNewIMETextLen;
583 EditSelection _aSel( aPaM );
584 _aSel.Max().GetIndex() =
585 _aSel.Max().GetIndex() + nOverwrite;
586 DeleteSelected( _aSel );
589 if ( pData->GetTextAttr() )
591 mpIMEInfos->CopyAttribs( pData->GetTextAttr(), pData->GetText().Len() );
592 mpIMEInfos->bCursor = pData->IsCursorVisible();
594 else
596 mpIMEInfos->DestroyAttribs();
597 mpIMEInfos->nLen = pData->GetText().Len();
600 ParaPortion* pPortion = FindParaPortion( mpIMEInfos->aPos.GetNode() );
601 pPortion->MarkSelectionInvalid( mpIMEInfos->aPos.GetIndex(), 0 );
602 FormatAndUpdate( pView );
605 EditSelection aNewSel = EditPaM( mpIMEInfos->aPos.GetNode(), mpIMEInfos->aPos.GetIndex()+pData->GetCursorPos() );
606 pView->SetSelection( CreateESel( aNewSel ) );
607 pView->SetInsertMode( !pData->IsCursorOverwrite() );
609 if ( pData->IsCursorVisible() )
610 pView->ShowCursor();
611 else
612 pView->HideCursor();
615 else if ( rCEvt.GetCommand() == COMMAND_INPUTCONTEXTCHANGE )
618 else if ( rCEvt.GetCommand() == COMMAND_CURSORPOS )
620 if ( mpIMEInfos && mpIMEInfos->nLen )
622 EditPaM aPaM( pView->pImpEditView->GetEditSelection().Max() );
623 Rectangle aR1 = PaMtoEditCursor( aPaM, 0 );
625 USHORT nInputEnd = mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen;
627 if ( !IsFormatted() )
628 FormatDoc();
630 ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( GetEditDoc().GetPos( aPaM.GetNode() ) );
631 USHORT nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), sal_True );
632 EditLine* pLine = pParaPortion->GetLines().GetObject( nLine );
633 if ( pLine && ( nInputEnd > pLine->GetEnd() ) )
634 nInputEnd = pLine->GetEnd();
635 Rectangle aR2 = PaMtoEditCursor( EditPaM( aPaM.GetNode(), nInputEnd ), GETCRSR_ENDOFLINE );
636 Rectangle aRect = pView->GetImpEditView()->GetWindowPos( aR1 );
637 pView->GetWindow()->SetCursorRect( &aRect, aR2.Left()-aR1.Right() );
639 else
641 pView->GetWindow()->SetCursorRect();
644 else if ( rCEvt.GetCommand() == COMMAND_SELECTIONCHANGE )
646 const CommandSelectionChangeData *pData = rCEvt.GetSelectionChangeData();
648 ESelection aSelection = pView->GetSelection();
649 aSelection.Adjust();
651 if( pView->HasSelection() )
653 aSelection.nEndPos = aSelection.nStartPos;
654 aSelection.nStartPos += pData->GetStart();
655 aSelection.nEndPos += pData->GetEnd();
657 else
659 aSelection.nStartPos = pData->GetStart();
660 aSelection.nEndPos = pData->GetEnd();
662 pView->SetSelection( aSelection );
664 else if ( rCEvt.GetCommand() == COMMAND_PREPARERECONVERSION )
666 if ( pView->HasSelection() )
668 ESelection aSelection = pView->GetSelection();
669 aSelection.Adjust();
671 if ( aSelection.nStartPara != aSelection.nEndPara )
673 xub_StrLen aParaLen = pEditEngine->GetTextLen( aSelection.nStartPara );
674 aSelection.nEndPara = aSelection.nStartPara;
675 aSelection.nEndPos = aParaLen;
676 pView->SetSelection( aSelection );
681 GetSelEngine().Command( rCEvt );
684 BOOL ImpEditEngine::MouseButtonUp( const MouseEvent& rMEvt, EditView* pView )
686 GetSelEngine().SetCurView( pView );
687 GetSelEngine().SelMouseButtonUp( rMEvt );
688 bInSelection = FALSE;
689 // Sonderbehandlungen
690 EditSelection aCurSel( pView->pImpEditView->GetEditSelection() );
691 if ( !aCurSel.HasRange() )
693 if ( ( rMEvt.GetClicks() == 1 ) && rMEvt.IsLeft() && !rMEvt.IsMod2() )
695 const SvxFieldItem* pFld = pView->GetFieldUnderMousePointer();
696 if ( pFld )
698 EditPaM aPaM( aCurSel.Max() );
699 USHORT nPara = GetEditDoc().GetPos( aPaM.GetNode() );
700 GetEditEnginePtr()->FieldClicked( *pFld, nPara, aPaM.GetIndex() );
704 return TRUE;
707 BOOL ImpEditEngine::MouseMove( const MouseEvent& rMEvt, EditView* pView )
709 // MouseMove wird sofort nach ShowQuickHelp() gerufen!
710 // if ( GetAutoCompleteText().Len() )
711 // SetAutoCompleteText( String(), TRUE );
712 GetSelEngine().SetCurView( pView );
713 GetSelEngine().SelMouseMove( rMEvt );
714 return TRUE;
717 EditPaM ImpEditEngine::InsertText( EditSelection aSel, const XubString& rStr )
719 EditPaM aPaM = ImpInsertText( aSel, rStr );
720 return aPaM;
723 EditPaM ImpEditEngine::Clear()
725 InitDoc( FALSE );
727 EditPaM aPaM = aEditDoc.GetStartPaM();
728 EditSelection aSel( aPaM );
730 nCurTextHeight = 0;
732 ResetUndoManager();
734 for ( USHORT nView = aEditViews.Count(); nView; )
736 EditView* pView = aEditViews[--nView];
737 DBG_CHKOBJ( pView, EditView, 0 );
738 pView->pImpEditView->SetEditSelection( aSel );
741 return aPaM;
744 EditPaM ImpEditEngine::RemoveText()
746 InitDoc( TRUE );
748 EditPaM aStartPaM = aEditDoc.GetStartPaM();
749 EditSelection aEmptySel( aStartPaM, aStartPaM );
750 for ( USHORT nView = 0; nView < aEditViews.Count(); nView++ )
752 EditView* pView = aEditViews.GetObject(nView);
753 DBG_CHKOBJ( pView, EditView, 0 );
754 pView->pImpEditView->SetEditSelection( aEmptySel );
756 ResetUndoManager();
757 return aEditDoc.GetStartPaM();
761 void ImpEditEngine::SetText( const XubString& rText )
763 // RemoveText loescht die Undo-Liste!
764 EditPaM aStartPaM = RemoveText();
765 BOOL bUndoCurrentlyEnabled = IsUndoEnabled();
766 // Der von Hand reingesteckte Text kann nicht vom Anwender rueckgaengig gemacht werden.
767 EnableUndo( FALSE );
769 EditSelection aEmptySel( aStartPaM, aStartPaM );
770 EditPaM aPaM = aStartPaM;
771 if ( rText.Len() )
772 aPaM = ImpInsertText( aEmptySel, rText );
774 for ( USHORT nView = 0; nView < aEditViews.Count(); nView++ )
776 EditView* pView = aEditViews[nView];
777 DBG_CHKOBJ( pView, EditView, 0 );
778 pView->pImpEditView->SetEditSelection( EditSelection( aPaM, aPaM ) );
779 // Wenn kein Text, dann auch Kein Format&Update
780 // => Der Text bleibt stehen.
781 if ( !rText.Len() && GetUpdateMode() )
783 Rectangle aTmpRec( pView->GetOutputArea().TopLeft(),
784 Size( aPaperSize.Width(), nCurTextHeight ) );
785 aTmpRec.Intersection( pView->GetOutputArea() );
786 pView->GetWindow()->Invalidate( aTmpRec );
789 if( !rText.Len() ) // sonst muss spaeter noch invalidiert werden, !bFormatted reicht.
790 nCurTextHeight = 0;
791 EnableUndo( bUndoCurrentlyEnabled );
792 #ifndef SVX_LIGHT
793 DBG_ASSERT( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "Undo nach SetText?" );
794 #endif
798 const SfxItemSet& ImpEditEngine::GetEmptyItemSet()
800 if ( !pEmptyItemSet )
802 pEmptyItemSet = new SfxItemSet( aEditDoc.GetItemPool(), EE_ITEMS_START, EE_ITEMS_END );
803 for ( USHORT nWhich = EE_ITEMS_START; nWhich <= EE_CHAR_END; nWhich++)
805 pEmptyItemSet->ClearItem( nWhich );
808 return *pEmptyItemSet;
811 // ----------------------------------------------------------------------
812 // MISC
813 // ----------------------------------------------------------------------
814 void ImpEditEngine::CursorMoved( ContentNode* pPrevNode )
816 // Leere Attribute loeschen, aber nur, wenn Absatz nicht leer!
817 if ( pPrevNode->GetCharAttribs().HasEmptyAttribs() && pPrevNode->Len() )
818 pPrevNode->GetCharAttribs().DeleteEmptyAttribs( aEditDoc.GetItemPool() );
821 void ImpEditEngine::TextModified()
823 bFormatted = FALSE;
825 if ( GetNotifyHdl().IsSet() )
827 EENotify aNotify( EE_NOTIFY_TEXTMODIFIED );
828 aNotify.pEditEngine = GetEditEnginePtr();
829 CallNotify( aNotify );
834 void ImpEditEngine::ParaAttribsChanged( ContentNode* pNode )
836 DBG_ASSERT( pNode, "ParaAttribsChanged: Welcher?" );
838 aEditDoc.SetModified( TRUE );
839 bFormatted = FALSE;
841 ParaPortion* pPortion = FindParaPortion( pNode );
842 DBG_ASSERT( pPortion, "ParaAttribsChanged: Portion?" );
843 pPortion->MarkSelectionInvalid( 0, pNode->Len() );
845 USHORT nPara = aEditDoc.GetPos( pNode );
846 pEditEngine->ParaAttribsChanged( nPara );
848 ParaPortion* pNextPortion = GetParaPortions().SaveGetObject( nPara+1 );
849 // => wird sowieso noch formatiert, wenn Invalid.
850 if ( pNextPortion && !pNextPortion->IsInvalid() )
851 CalcHeight( pNextPortion );
854 // ----------------------------------------------------------------------
855 // Cursorbewegungen
856 // ----------------------------------------------------------------------
858 EditSelection ImpEditEngine::MoveCursor( const KeyEvent& rKeyEvent, EditView* pEditView )
860 // Eigentlich nur bei Up/Down noetig, aber was solls.
861 CheckIdleFormatter();
863 EditPaM aPaM( pEditView->pImpEditView->GetEditSelection().Max() );
865 EditPaM aOldPaM( aPaM );
867 TextDirectionality eTextDirection = TextDirectionality_LeftToRight_TopToBottom;
868 if ( IsVertical() )
869 eTextDirection = TextDirectionality_TopToBottom_RightToLeft;
870 else if ( IsRightToLeft( GetEditDoc().GetPos( aPaM.GetNode() ) ) )
871 eTextDirection = TextDirectionality_RightToLeft_TopToBottom;
873 KeyEvent aTranslatedKeyEvent = rKeyEvent.LogicalTextDirectionality( eTextDirection );
875 BOOL bCtrl = aTranslatedKeyEvent.GetKeyCode().IsMod1() ? TRUE : FALSE;
876 USHORT nCode = aTranslatedKeyEvent.GetKeyCode().GetCode();
878 if ( DoVisualCursorTraveling( aPaM.GetNode() ) )
880 // Only for simple cursor movement...
881 if ( !bCtrl && ( ( nCode == KEY_LEFT ) || ( nCode == KEY_RIGHT ) ) )
883 aPaM = CursorVisualLeftRight( pEditView, aPaM, rKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL, rKeyEvent.GetKeyCode().GetCode() == KEY_LEFT );
884 nCode = 0; // skip switch statement
887 else if ( !bCtrl && ( ( nCode == KEY_HOME ) || ( nCode == KEY_END ) ) )
889 aPaM = CursorVisualStartEnd( pEditView, aPaM, nCode == KEY_HOME );
890 nCode = 0; // skip switch statement
895 bool bKeyModifySelection = aTranslatedKeyEvent.GetKeyCode().IsShift();
896 switch ( nCode )
898 case KEY_UP: aPaM = CursorUp( aPaM, pEditView );
899 break;
900 case KEY_DOWN: aPaM = CursorDown( aPaM, pEditView );
901 break;
902 case KEY_LEFT: aPaM = bCtrl ? WordLeft( aPaM ) : CursorLeft( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL );
903 break;
904 case KEY_RIGHT: aPaM = bCtrl ? WordRight( aPaM ) : CursorRight( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL );
905 break;
906 case KEY_HOME: aPaM = bCtrl ? CursorStartOfDoc() : CursorStartOfLine( aPaM );
907 break;
908 case KEY_END: aPaM = bCtrl ? CursorEndOfDoc() : CursorEndOfLine( aPaM );
909 break;
910 case KEY_PAGEUP: aPaM = bCtrl ? CursorStartOfDoc() : PageUp( aPaM, pEditView );
911 break;
912 case KEY_PAGEDOWN: aPaM = bCtrl ? CursorEndOfDoc() : PageDown( aPaM, pEditView );
913 break;
914 case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_LINE:
915 aPaM = CursorStartOfLine( aPaM );
916 bKeyModifySelection = false;
917 break;
918 case com::sun::star::awt::Key::MOVE_TO_END_OF_LINE:
919 aPaM = CursorEndOfLine( aPaM );
920 bKeyModifySelection = false;
921 break;
922 case com::sun::star::awt::Key::MOVE_WORD_BACKWARD:
923 aPaM = WordLeft( aPaM );
924 bKeyModifySelection = false;
925 break;
926 case com::sun::star::awt::Key::MOVE_WORD_FORWARD:
927 aPaM = WordRight( aPaM );
928 bKeyModifySelection = false;
929 break;
930 case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
931 aPaM = CursorStartOfParagraph( aPaM );
932 if( aPaM == aOldPaM )
934 aPaM = CursorLeft( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
935 aPaM = CursorStartOfParagraph( aPaM );
937 bKeyModifySelection = false;
938 break;
939 case com::sun::star::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
940 aPaM = CursorEndOfParagraph( aPaM );
941 if( aPaM == aOldPaM )
943 aPaM = CursorRight( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
944 aPaM = CursorEndOfParagraph( aPaM );
946 bKeyModifySelection = false;
947 break;
948 case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
949 aPaM = CursorStartOfDoc();
950 bKeyModifySelection = false;
951 break;
952 case com::sun::star::awt::Key::MOVE_TO_END_OF_DOCUMENT:
953 aPaM = CursorEndOfDoc();
954 bKeyModifySelection = false;
955 break;
956 case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_LINE:
957 aPaM = CursorStartOfLine( aPaM );
958 bKeyModifySelection = true;
959 break;
960 case com::sun::star::awt::Key::SELECT_TO_END_OF_LINE:
961 aPaM = CursorEndOfLine( aPaM );
962 bKeyModifySelection = true;
963 break;
964 case com::sun::star::awt::Key::SELECT_BACKWARD:
965 aPaM = CursorLeft( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
966 bKeyModifySelection = true;
967 break;
968 case com::sun::star::awt::Key::SELECT_FORWARD:
969 aPaM = CursorRight( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
970 bKeyModifySelection = true;
971 break;
972 case com::sun::star::awt::Key::SELECT_WORD_BACKWARD:
973 aPaM = WordLeft( aPaM );
974 bKeyModifySelection = true;
975 break;
976 case com::sun::star::awt::Key::SELECT_WORD_FORWARD:
977 aPaM = WordRight( aPaM );
978 bKeyModifySelection = true;
979 break;
980 case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
981 aPaM = CursorStartOfParagraph( aPaM );
982 if( aPaM == aOldPaM )
984 aPaM = CursorLeft( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
985 aPaM = CursorStartOfParagraph( aPaM );
987 bKeyModifySelection = true;
988 break;
989 case com::sun::star::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
990 aPaM = CursorEndOfParagraph( aPaM );
991 if( aPaM == aOldPaM )
993 aPaM = CursorRight( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
994 aPaM = CursorEndOfParagraph( aPaM );
996 bKeyModifySelection = true;
997 break;
998 case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
999 aPaM = CursorStartOfDoc();
1000 bKeyModifySelection = true;
1001 break;
1002 case com::sun::star::awt::Key::SELECT_TO_END_OF_DOCUMENT:
1003 aPaM = CursorEndOfDoc();
1004 bKeyModifySelection = true;
1005 break;
1008 if ( aOldPaM != aPaM )
1010 CursorMoved( aOldPaM.GetNode() );
1011 if ( aStatus.NotifyCursorMovements() && ( aOldPaM.GetNode() != aPaM.GetNode() ) )
1013 aStatus.GetStatusWord() = aStatus.GetStatusWord() | EE_STAT_CRSRLEFTPARA;
1014 aStatus.GetPrevParagraph() = aEditDoc.GetPos( aOldPaM.GetNode() );
1017 else
1018 aStatus.GetStatusWord() = aStatus.GetStatusWord() | EE_STAT_CRSRMOVEFAIL;
1020 // Bewirkt evtl. ein CreateAnchor oder Deselection all
1021 aSelEngine.SetCurView( pEditView );
1022 aSelEngine.CursorPosChanging( bKeyModifySelection, aTranslatedKeyEvent.GetKeyCode().IsMod1() );
1023 EditPaM aOldEnd( pEditView->pImpEditView->GetEditSelection().Max() );
1024 pEditView->pImpEditView->GetEditSelection().Max() = aPaM;
1025 if ( bKeyModifySelection )
1027 // Dann wird die Selektion erweitert...
1028 EditSelection aTmpNewSel( aOldEnd, aPaM );
1029 pEditView->pImpEditView->DrawSelection( aTmpNewSel );
1031 else
1032 pEditView->pImpEditView->GetEditSelection().Min() = aPaM;
1034 return pEditView->pImpEditView->GetEditSelection();
1037 EditPaM ImpEditEngine::CursorVisualStartEnd( EditView* pEditView, const EditPaM& rPaM, BOOL bStart )
1039 EditPaM aPaM( rPaM );
1041 USHORT nPara = GetEditDoc().GetPos( aPaM.GetNode() );
1042 ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
1044 USHORT nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), sal_False );
1045 EditLine* pLine = pParaPortion->GetLines().GetObject( nLine );
1046 BOOL bEmptyLine = pLine->GetStart() == pLine->GetEnd();
1048 pEditView->pImpEditView->nExtraCursorFlags = 0;
1050 if ( !bEmptyLine )
1052 String aLine( *aPaM.GetNode(), pLine->GetStart(), pLine->GetEnd() - pLine->GetStart() );
1053 // USHORT nPosInLine = aPaM.GetIndex() - pLine->GetStart();
1055 const sal_Unicode* pLineString = aLine.GetBuffer();
1057 UErrorCode nError = U_ZERO_ERROR;
1058 UBiDi* pBidi = ubidi_openSized( aLine.Len(), 0, &nError );
1060 const UBiDiLevel nBidiLevel = IsRightToLeft( nPara ) ? 1 /*RTL*/ : 0 /*LTR*/;
1061 ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(pLineString), aLine.Len(), nBidiLevel, NULL, &nError ); // UChar != sal_Unicode in MinGW
1063 USHORT nVisPos = bStart ? 0 : aLine.Len()-1;
1064 USHORT nLogPos = (USHORT)ubidi_getLogicalIndex( pBidi, nVisPos, &nError );
1066 ubidi_close( pBidi );
1068 aPaM.GetIndex() = nLogPos + pLine->GetStart();
1070 USHORT nTmp;
1071 USHORT nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nTmp, TRUE );
1072 TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
1073 USHORT nRTLLevel = pTextPortion->GetRightToLeft();
1074 // BOOL bParaRTL = IsRightToLeft( nPara );
1075 BOOL bPortionRTL = nRTLLevel%2 ? TRUE : FALSE;
1077 if ( bStart )
1079 pEditView->pImpEditView->SetCursorBidiLevel( bPortionRTL ? 0 : 1 );
1080 // Maybe we must be *behind* the character
1081 if ( bPortionRTL && pEditView->IsInsertMode() )
1082 aPaM.GetIndex()++;
1084 else
1086 pEditView->pImpEditView->SetCursorBidiLevel( bPortionRTL ? 1 : 0 );
1087 if ( !bPortionRTL && pEditView->IsInsertMode() )
1088 aPaM.GetIndex()++;
1092 return aPaM;
1095 EditPaM ImpEditEngine::CursorVisualLeftRight( EditView* pEditView, const EditPaM& rPaM, USHORT nCharacterIteratorMode, BOOL bVisualToLeft )
1097 EditPaM aPaM( rPaM );
1099 USHORT nPara = GetEditDoc().GetPos( aPaM.GetNode() );
1100 ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
1102 USHORT nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), sal_False );
1103 EditLine* pLine = pParaPortion->GetLines().GetObject( nLine );
1104 BOOL bEmptyLine = pLine->GetStart() == pLine->GetEnd();
1106 // USHORT nCurrentCursorFlags = pEditView->pImpEditView->nExtraCursorFlags;
1107 pEditView->pImpEditView->nExtraCursorFlags = 0;
1109 BOOL bParaRTL = IsRightToLeft( nPara );
1111 BOOL bDone = FALSE;
1113 if ( bEmptyLine )
1115 if ( bVisualToLeft )
1117 aPaM = CursorUp( aPaM, pEditView );
1118 if ( aPaM != rPaM )
1119 aPaM = CursorVisualStartEnd( pEditView, aPaM, FALSE );
1121 else
1123 aPaM = CursorDown( aPaM, pEditView );
1124 if ( aPaM != rPaM )
1125 aPaM = CursorVisualStartEnd( pEditView, aPaM, TRUE );
1128 bDone = TRUE;
1131 BOOL bLogicalBackward = bParaRTL ? !bVisualToLeft : bVisualToLeft;
1133 if ( !bDone && pEditView->IsInsertMode() )
1135 // Check if we are within a portion and don't have overwrite mode, then it's easy...
1136 USHORT nPortionStart;
1137 USHORT nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, FALSE );
1138 TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
1140 BOOL bPortionBoundary = ( aPaM.GetIndex() == nPortionStart ) || ( aPaM.GetIndex() == (nPortionStart+pTextPortion->GetLen()) );
1141 USHORT nRTLLevel = pTextPortion->GetRightToLeft();
1143 // Portion boundary doesn't matter if both have same RTL level
1144 USHORT nRTLLevelNextPortion = 0xFFFF;
1145 if ( bPortionBoundary && aPaM.GetIndex() && ( aPaM.GetIndex() < aPaM.GetNode()->Len() ) )
1147 USHORT nTmp;
1148 USHORT nNextTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex()+1, nTmp, bLogicalBackward ? FALSE : TRUE );
1149 TextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nNextTextPortion );
1150 nRTLLevelNextPortion = pNextTextPortion->GetRightToLeft();
1153 if ( !bPortionBoundary || ( nRTLLevel == nRTLLevelNextPortion ) )
1155 if ( ( bVisualToLeft && !(nRTLLevel%2) ) || ( !bVisualToLeft && (nRTLLevel%2) ) )
1157 aPaM = CursorLeft( aPaM, nCharacterIteratorMode );
1158 pEditView->pImpEditView->SetCursorBidiLevel( 1 );
1160 else
1162 aPaM = CursorRight( aPaM, nCharacterIteratorMode );
1163 pEditView->pImpEditView->SetCursorBidiLevel( 0 );
1165 bDone = TRUE;
1169 if ( !bDone )
1171 BOOL bGotoStartOfNextLine = FALSE;
1172 BOOL bGotoEndOfPrevLine = FALSE;
1174 String aLine( *aPaM.GetNode(), pLine->GetStart(), pLine->GetEnd() - pLine->GetStart() );
1175 USHORT nPosInLine = aPaM.GetIndex() - pLine->GetStart();
1177 const sal_Unicode* pLineString = aLine.GetBuffer();
1179 UErrorCode nError = U_ZERO_ERROR;
1180 UBiDi* pBidi = ubidi_openSized( aLine.Len(), 0, &nError );
1182 const UBiDiLevel nBidiLevel = IsRightToLeft( nPara ) ? 1 /*RTL*/ : 0 /*LTR*/;
1183 ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(pLineString), aLine.Len(), nBidiLevel, NULL, &nError ); // UChar != sal_Unicode in MinGW
1185 if ( !pEditView->IsInsertMode() )
1187 BOOL bEndOfLine = nPosInLine == aLine.Len();
1188 USHORT nVisPos = (USHORT)ubidi_getVisualIndex( pBidi, !bEndOfLine ? nPosInLine : nPosInLine-1, &nError );
1189 if ( bVisualToLeft )
1191 bGotoEndOfPrevLine = nVisPos == 0;
1192 if ( !bEndOfLine )
1193 nVisPos--;
1195 else
1197 bGotoStartOfNextLine = nVisPos == (aLine.Len() - 1);
1198 if ( !bEndOfLine )
1199 nVisPos++;
1202 if ( !bGotoEndOfPrevLine && !bGotoStartOfNextLine )
1204 USHORT nLogPos = (USHORT)ubidi_getLogicalIndex( pBidi, nVisPos, &nError );
1205 aPaM.GetIndex() = pLine->GetStart() + nLogPos;
1206 pEditView->pImpEditView->SetCursorBidiLevel( 0 );
1209 else
1211 BOOL bWasBehind = FALSE;
1212 BOOL bBeforePortion = !nPosInLine || pEditView->pImpEditView->GetCursorBidiLevel() == 1;
1213 if ( nPosInLine && ( !bBeforePortion ) ) // before the next portion
1214 bWasBehind = TRUE; // step one back, otherwise visual will be unusable when rtl portion follows.
1216 USHORT nPortionStart;
1217 USHORT nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, bBeforePortion );
1218 TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
1219 BOOL bRTLPortion = (pTextPortion->GetRightToLeft() % 2) != 0;
1221 // -1: We are 'behind' the character
1222 long nVisPos = (long)ubidi_getVisualIndex( pBidi, bWasBehind ? nPosInLine-1 : nPosInLine, &nError );
1223 if ( bVisualToLeft )
1225 if ( !bWasBehind || bRTLPortion )
1226 nVisPos--;
1228 else
1230 if ( bWasBehind || bRTLPortion || bBeforePortion )
1231 nVisPos++;
1232 // if ( bWasBehind && bRTLPortion )
1233 // nVisPos++;
1236 bGotoEndOfPrevLine = nVisPos < 0;
1237 bGotoStartOfNextLine = nVisPos >= aLine.Len();
1239 if ( !bGotoEndOfPrevLine && !bGotoStartOfNextLine )
1241 USHORT nLogPos = (USHORT)ubidi_getLogicalIndex( pBidi, nVisPos, &nError );
1244 if ( nLogPos == aPaM.GetIndex() )
1246 if ( bVisualToLeft )
1247 bGotoEndOfPrevLine = TRUE;
1248 else
1249 bGotoStartOfNextLine = TRUE;
1251 else
1254 aPaM.GetIndex() = pLine->GetStart() + nLogPos;
1256 // RTL portion, stay visually on the left side.
1257 USHORT _nPortionStart;
1258 // USHORT nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, !bRTLPortion );
1259 USHORT _nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), _nPortionStart, TRUE );
1260 TextPortion* _pTextPortion = pParaPortion->GetTextPortions().GetObject( _nTextPortion );
1261 if ( bVisualToLeft && !bRTLPortion && ( _pTextPortion->GetRightToLeft() % 2 ) )
1262 aPaM.GetIndex()++;
1263 else if ( !bVisualToLeft && bRTLPortion && ( bWasBehind || !(_pTextPortion->GetRightToLeft() % 2 )) )
1264 aPaM.GetIndex()++;
1266 pEditView->pImpEditView->SetCursorBidiLevel( _nPortionStart );
1271 ubidi_close( pBidi );
1273 if ( bGotoEndOfPrevLine )
1275 aPaM = CursorUp( aPaM, pEditView );
1276 if ( aPaM != rPaM )
1277 aPaM = CursorVisualStartEnd( pEditView, aPaM, FALSE );
1279 else if ( bGotoStartOfNextLine )
1281 aPaM = CursorDown( aPaM, pEditView );
1282 if ( aPaM != rPaM )
1283 aPaM = CursorVisualStartEnd( pEditView, aPaM, TRUE );
1286 return aPaM;
1290 EditPaM ImpEditEngine::CursorLeft( const EditPaM& rPaM, USHORT nCharacterIteratorMode )
1292 EditPaM aCurPaM( rPaM );
1293 EditPaM aNewPaM( aCurPaM );
1295 if ( aCurPaM.GetIndex() )
1297 sal_Int32 nCount = 1;
1298 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1299 aNewPaM.SetIndex( (USHORT)_xBI->previousCharacters( *aNewPaM.GetNode(), aNewPaM.GetIndex(), GetLocale( aNewPaM ), nCharacterIteratorMode, nCount, nCount ) );
1301 else
1303 ContentNode* pNode = aCurPaM.GetNode();
1304 pNode = GetPrevVisNode( pNode );
1305 if ( pNode )
1307 aNewPaM.SetNode( pNode );
1308 aNewPaM.SetIndex( pNode->Len() );
1312 return aNewPaM;
1315 EditPaM ImpEditEngine::CursorRight( const EditPaM& rPaM, USHORT nCharacterIteratorMode )
1317 EditPaM aCurPaM( rPaM );
1318 EditPaM aNewPaM( aCurPaM );
1320 if ( aCurPaM.GetIndex() < aCurPaM.GetNode()->Len() )
1322 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1323 sal_Int32 nCount = 1;
1324 aNewPaM.SetIndex( (USHORT)_xBI->nextCharacters( *aNewPaM.GetNode(), aNewPaM.GetIndex(), GetLocale( aNewPaM ), nCharacterIteratorMode, nCount, nCount ) );
1326 else
1328 ContentNode* pNode = aCurPaM.GetNode();
1329 pNode = GetNextVisNode( pNode );
1330 if ( pNode )
1332 aNewPaM.SetNode( pNode );
1333 aNewPaM.SetIndex( 0 );
1337 return aNewPaM;
1340 EditPaM ImpEditEngine::CursorUp( const EditPaM& rPaM, EditView* pView )
1342 DBG_ASSERT( pView, "Keine View - Keine Cursorbewegung!" );
1344 ParaPortion* pPPortion = FindParaPortion( rPaM.GetNode() );
1345 DBG_ASSERT( pPPortion, "Keine passende Portion gefunden: CursorUp" );
1346 USHORT nLine = pPPortion->GetLineNumber( rPaM.GetIndex() );
1347 EditLine* pLine = pPPortion->GetLines().GetObject( nLine );
1349 long nX;
1350 if ( pView->pImpEditView->nTravelXPos == TRAVEL_X_DONTKNOW )
1352 nX = GetXPos( pPPortion, pLine, rPaM.GetIndex() );
1353 pView->pImpEditView->nTravelXPos = nX+nOnePixelInRef;
1355 else
1356 nX = pView->pImpEditView->nTravelXPos;
1358 EditPaM aNewPaM( rPaM );
1359 if ( nLine ) // gleicher Absatz
1361 EditLine* pPrevLine = pPPortion->GetLines().GetObject(nLine-1);
1362 aNewPaM.SetIndex( GetChar( pPPortion, pPrevLine, nX ) );
1363 // Wenn davor eine autom.Umgebrochene Zeile, und ich muss genau an das
1364 // Ende dieser Zeile, landet der Cursor in der aktuellen Zeile am Anfang
1365 // Siehe Problem: Letztes Zeichen einer autom.umgebr. Zeile = Cursor
1366 if ( aNewPaM.GetIndex() && ( aNewPaM.GetIndex() == pLine->GetStart() ) )
1367 aNewPaM = CursorLeft( aNewPaM );
1369 else // vorheriger Absatz
1371 ParaPortion* pPrevPortion = GetPrevVisPortion( pPPortion );
1372 if ( pPrevPortion )
1374 pLine = pPrevPortion->GetLines().GetObject( pPrevPortion->GetLines().Count()-1 );
1375 DBG_ASSERT( pLine, "Zeile davor nicht gefunden: CursorUp" );
1376 aNewPaM.SetNode( pPrevPortion->GetNode() );
1377 aNewPaM.SetIndex( GetChar( pPrevPortion, pLine, nX+nOnePixelInRef ) );
1381 return aNewPaM;
1384 EditPaM ImpEditEngine::CursorDown( const EditPaM& rPaM, EditView* pView )
1386 DBG_ASSERT( pView, "Keine View - Keine Cursorbewegung!" );
1388 ParaPortion* pPPortion = FindParaPortion( rPaM.GetNode() );
1389 DBG_ASSERT( pPPortion, "Keine passende Portion gefunden: CursorDown" );
1390 USHORT nLine = pPPortion->GetLineNumber( rPaM.GetIndex() );
1392 long nX;
1393 if ( pView->pImpEditView->nTravelXPos == TRAVEL_X_DONTKNOW )
1395 EditLine* pLine = pPPortion->GetLines().GetObject(nLine);
1396 nX = GetXPos( pPPortion, pLine, rPaM.GetIndex() );
1397 pView->pImpEditView->nTravelXPos = nX+nOnePixelInRef;
1399 else
1400 nX = pView->pImpEditView->nTravelXPos;
1402 EditPaM aNewPaM( rPaM );
1403 if ( nLine < pPPortion->GetLines().Count()-1 )
1405 EditLine* pNextLine = pPPortion->GetLines().GetObject(nLine+1);
1406 aNewPaM.SetIndex( GetChar( pPPortion, pNextLine, nX ) );
1407 // Sonderbehandlung siehe CursorUp...
1408 if ( ( aNewPaM.GetIndex() == pNextLine->GetEnd() ) && ( aNewPaM.GetIndex() > pNextLine->GetStart() ) && ( aNewPaM.GetIndex() < pPPortion->GetNode()->Len() ) )
1409 aNewPaM = CursorLeft( aNewPaM );
1411 else // naechster Absatz
1413 ParaPortion* pNextPortion = GetNextVisPortion( pPPortion );
1414 if ( pNextPortion )
1416 EditLine* pLine = pNextPortion->GetLines().GetObject(0);
1417 DBG_ASSERT( pLine, "Zeile davor nicht gefunden: CursorUp" );
1418 aNewPaM.SetNode( pNextPortion->GetNode() );
1419 // Nie ganz ans Ende wenn mehrere Zeilen, da dann eine
1420 // Zeile darunter der Cursor angezeigt wird.
1421 aNewPaM.SetIndex( GetChar( pNextPortion, pLine, nX+nOnePixelInRef ) );
1422 if ( ( aNewPaM.GetIndex() == pLine->GetEnd() ) && ( aNewPaM.GetIndex() > pLine->GetStart() ) && ( pNextPortion->GetLines().Count() > 1 ) )
1423 aNewPaM = CursorLeft( aNewPaM );
1427 return aNewPaM;
1430 EditPaM ImpEditEngine::CursorStartOfLine( const EditPaM& rPaM )
1432 ParaPortion* pCurPortion = FindParaPortion( rPaM.GetNode() );
1433 DBG_ASSERT( pCurPortion, "Keine Portion fuer den PaM ?" );
1434 USHORT nLine = pCurPortion->GetLineNumber( rPaM.GetIndex() );
1435 EditLine* pLine = pCurPortion->GetLines().GetObject(nLine);
1436 DBG_ASSERT( pLine, "Aktuelle Zeile nicht gefunden ?!" );
1438 EditPaM aNewPaM( rPaM );
1439 aNewPaM.SetIndex( pLine->GetStart() );
1440 return aNewPaM;
1443 EditPaM ImpEditEngine::CursorEndOfLine( const EditPaM& rPaM )
1445 ParaPortion* pCurPortion = FindParaPortion( rPaM.GetNode() );
1446 DBG_ASSERT( pCurPortion, "Keine Portion fuer den PaM ?" );
1447 USHORT nLine = pCurPortion->GetLineNumber( rPaM.GetIndex() );
1448 EditLine* pLine = pCurPortion->GetLines().GetObject(nLine);
1449 DBG_ASSERT( pLine, "Aktuelle Zeile nicht gefunden ?!" );
1451 EditPaM aNewPaM( rPaM );
1452 aNewPaM.SetIndex( pLine->GetEnd() );
1453 if ( pLine->GetEnd() > pLine->GetStart() )
1455 // xub_Unicode cLastChar = aNewPaM.GetNode()->GetChar( aNewPaM.GetIndex()-1 );
1456 if ( aNewPaM.GetNode()->IsFeature( aNewPaM.GetIndex() - 1 ) )
1458 // Bei einem weichen Umbruch muss ich davor stehen!
1459 EditCharAttrib* pNextFeature = aNewPaM.GetNode()->GetCharAttribs().FindFeature( aNewPaM.GetIndex()-1 );
1460 if ( pNextFeature && ( pNextFeature->GetItem()->Which() == EE_FEATURE_LINEBR ) )
1461 aNewPaM = CursorLeft( aNewPaM );
1463 else if ( ( aNewPaM.GetNode()->GetChar( aNewPaM.GetIndex() - 1 ) == ' ' ) && ( aNewPaM.GetIndex() != aNewPaM.GetNode()->Len() ) )
1465 // Bei einem Blank in einer autom. umgebrochenen Zeile macht es Sinn,
1466 // davor zu stehen, da der Anwender hinter das Wort will.
1467 // Wenn diese geaendert wird, Sonderbehandlung fuer Pos1 nach End!
1468 aNewPaM = CursorLeft( aNewPaM );
1471 return aNewPaM;
1474 EditPaM ImpEditEngine::CursorStartOfParagraph( const EditPaM& rPaM )
1476 EditPaM aPaM( rPaM.GetNode(), 0 );
1477 return aPaM;
1480 EditPaM ImpEditEngine::CursorEndOfParagraph( const EditPaM& rPaM )
1482 EditPaM aPaM( rPaM.GetNode(), rPaM.GetNode()->Len() );
1483 return aPaM;
1486 EditPaM ImpEditEngine::CursorStartOfDoc()
1488 EditPaM aPaM( aEditDoc.SaveGetObject( 0 ), 0 );
1489 return aPaM;
1492 EditPaM ImpEditEngine::CursorEndOfDoc()
1494 ContentNode* pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count()-1 );
1495 ParaPortion* pLastPortion = GetParaPortions().SaveGetObject( aEditDoc.Count()-1 );
1496 DBG_ASSERT( pLastNode && pLastPortion, "CursorEndOfDoc: Node oder Portion nicht gefunden" );
1498 if ( !pLastPortion->IsVisible() )
1500 pLastNode = GetPrevVisNode( pLastPortion->GetNode() );
1501 DBG_ASSERT( pLastNode, "Kein sichtbarer Absatz?" );
1502 if ( !pLastNode )
1503 pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count()-1 );
1506 EditPaM aPaM( pLastNode, pLastNode->Len() );
1507 return aPaM;
1510 EditPaM ImpEditEngine::PageUp( const EditPaM& rPaM, EditView* pView )
1512 Rectangle aRec = PaMtoEditCursor( rPaM );
1513 Point aTopLeft = aRec.TopLeft();
1514 aTopLeft.Y() -= pView->GetVisArea().GetHeight() *9/10;
1515 aTopLeft.X() += nOnePixelInRef;
1516 if ( aTopLeft.Y() < 0 )
1518 aTopLeft.Y() = 0;
1520 return GetPaM( aTopLeft );
1523 EditPaM ImpEditEngine::PageDown( const EditPaM& rPaM, EditView* pView )
1525 Rectangle aRec = PaMtoEditCursor( rPaM );
1526 Point aBottomRight = aRec.BottomRight();
1527 aBottomRight.Y() += pView->GetVisArea().GetHeight() *9/10;
1528 aBottomRight.X() += nOnePixelInRef;
1529 long nHeight = GetTextHeight();
1530 if ( aBottomRight.Y() > nHeight )
1532 aBottomRight.Y() = nHeight-2;
1534 return GetPaM( aBottomRight );
1537 EditPaM ImpEditEngine::WordLeft( const EditPaM& rPaM, sal_Int16 nWordType )
1539 USHORT nCurrentPos = rPaM.GetIndex();
1540 EditPaM aNewPaM( rPaM );
1541 if ( nCurrentPos == 0 )
1543 // Vorheriger Absatz...
1544 USHORT nCurPara = aEditDoc.GetPos( aNewPaM.GetNode() );
1545 ContentNode* pPrevNode = aEditDoc.SaveGetObject( --nCurPara );
1546 if ( pPrevNode )
1548 aNewPaM.SetNode( pPrevNode );
1549 aNewPaM.SetIndex( pPrevNode->Len() );
1552 else
1554 // we need to increase the position by 1 when retrieving the locale
1555 // since the attribute for the char left to the cursor position is returned
1556 EditPaM aTmpPaM( aNewPaM );
1557 xub_StrLen nMax = rPaM.GetNode()->Len();
1558 if ( aTmpPaM.GetIndex() < nMax )
1559 aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
1560 lang::Locale aLocale( GetLocale( aTmpPaM ) );
1562 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1563 i18n::Boundary aBoundary = _xBI->getWordBoundary( *aNewPaM.GetNode(), nCurrentPos, aLocale, nWordType, sal_True );
1564 if ( aBoundary.startPos >= nCurrentPos )
1565 aBoundary = _xBI->previousWord( *aNewPaM.GetNode(), nCurrentPos, aLocale, nWordType );
1566 aNewPaM.SetIndex( ( aBoundary.startPos != (-1) ) ? (USHORT)aBoundary.startPos : 0 );
1569 return aNewPaM;
1572 EditPaM ImpEditEngine::WordRight( const EditPaM& rPaM, sal_Int16 nWordType )
1574 xub_StrLen nMax = rPaM.GetNode()->Len();
1575 EditPaM aNewPaM( rPaM );
1576 if ( aNewPaM.GetIndex() < nMax )
1578 // we need to increase the position by 1 when retrieving the locale
1579 // since the attribute for the char left to the cursor position is returned
1580 EditPaM aTmpPaM( aNewPaM );
1581 aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
1582 lang::Locale aLocale( GetLocale( aTmpPaM ) );
1584 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1585 i18n::Boundary aBoundary = _xBI->nextWord( *aNewPaM.GetNode(), aNewPaM.GetIndex(), aLocale, nWordType );
1586 aNewPaM.SetIndex( (USHORT)aBoundary.startPos );
1588 // not 'else', maybe the index reached nMax now...
1589 if ( aNewPaM.GetIndex() >= nMax )
1591 // Naechster Absatz...
1592 USHORT nCurPara = aEditDoc.GetPos( aNewPaM.GetNode() );
1593 ContentNode* pNextNode = aEditDoc.SaveGetObject( ++nCurPara );
1594 if ( pNextNode )
1596 aNewPaM.SetNode( pNextNode );
1597 aNewPaM.SetIndex( 0 );
1600 return aNewPaM;
1603 EditPaM ImpEditEngine::StartOfWord( const EditPaM& rPaM, sal_Int16 nWordType )
1605 EditPaM aNewPaM( rPaM );
1607 // we need to increase the position by 1 when retrieving the locale
1608 // since the attribute for the char left to the cursor position is returned
1609 EditPaM aTmpPaM( aNewPaM );
1610 xub_StrLen nMax = rPaM.GetNode()->Len();
1611 if ( aTmpPaM.GetIndex() < nMax )
1612 aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
1613 lang::Locale aLocale( GetLocale( aTmpPaM ) );
1615 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1616 i18n::Boundary aBoundary = _xBI->getWordBoundary( *rPaM.GetNode(), rPaM.GetIndex(), aLocale, nWordType, sal_True );
1617 aNewPaM.SetIndex( (USHORT)aBoundary.startPos );
1618 return aNewPaM;
1621 EditPaM ImpEditEngine::EndOfWord( const EditPaM& rPaM, sal_Int16 nWordType )
1623 EditPaM aNewPaM( rPaM );
1625 // we need to increase the position by 1 when retrieving the locale
1626 // since the attribute for the char left to the cursor position is returned
1627 EditPaM aTmpPaM( aNewPaM );
1628 xub_StrLen nMax = rPaM.GetNode()->Len();
1629 if ( aTmpPaM.GetIndex() < nMax )
1630 aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
1631 lang::Locale aLocale( GetLocale( aTmpPaM ) );
1633 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1634 i18n::Boundary aBoundary = _xBI->getWordBoundary( *rPaM.GetNode(), rPaM.GetIndex(), aLocale, nWordType, sal_True );
1635 aNewPaM.SetIndex( (USHORT)aBoundary.endPos );
1636 return aNewPaM;
1639 EditSelection ImpEditEngine::SelectWord( const EditSelection& rCurSel, sal_Int16 nWordType, BOOL bAcceptStartOfWord )
1641 EditSelection aNewSel( rCurSel );
1642 EditPaM aPaM( rCurSel.Max() );
1644 // we need to increase the position by 1 when retrieving the locale
1645 // since the attribute for the char left to the cursor position is returned
1646 EditPaM aTmpPaM( aPaM );
1647 xub_StrLen nMax = aPaM.GetNode()->Len();
1648 if ( aTmpPaM.GetIndex() < nMax )
1649 aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
1650 lang::Locale aLocale( GetLocale( aTmpPaM ) );
1652 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1653 sal_Int16 nType = _xBI->getWordType( *aPaM.GetNode(), aPaM.GetIndex(), aLocale );
1654 if ( nType == i18n::WordType::ANY_WORD )
1656 i18n::Boundary aBoundary = _xBI->getWordBoundary( *aPaM.GetNode(), aPaM.GetIndex(), aLocale, nWordType, sal_True );
1657 // don't select when curser at end of word
1658 if ( ( aBoundary.endPos > aPaM.GetIndex() ) &&
1659 ( ( aBoundary.startPos < aPaM.GetIndex() ) || ( bAcceptStartOfWord && ( aBoundary.startPos == aPaM.GetIndex() ) ) ) )
1661 aNewSel.Min().SetIndex( (USHORT)aBoundary.startPos );
1662 aNewSel.Max().SetIndex( (USHORT)aBoundary.endPos );
1666 return aNewSel;
1669 EditSelection ImpEditEngine::SelectSentence( const EditSelection& rCurSel )
1671 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1672 const EditPaM& rPaM = rCurSel.Min();
1673 const ContentNode* pNode = rPaM.GetNode();
1674 // #i50710# line breaks are marked with 0x01 - the break iterator prefers 0x0a for that
1675 String sParagraph(*pNode);
1676 sParagraph.SearchAndReplaceAll(0x01,0x0a);
1677 //return Null if search starts at the beginning of the string
1678 long nStart = rPaM.GetIndex() ? _xBI->beginOfSentence( sParagraph, rPaM.GetIndex(), GetLocale( rPaM ) ) : 0;
1680 long nEnd = _xBI->endOfSentence( *pNode, rPaM.GetIndex(), GetLocale( rPaM ) );
1681 EditSelection aNewSel( rCurSel );
1682 DBG_ASSERT(nStart < pNode->Len() && nEnd <= pNode->Len(), "sentence indices out of range");
1683 aNewSel.Min().SetIndex( (USHORT)nStart );
1684 aNewSel.Max().SetIndex( (USHORT)nEnd );
1685 return aNewSel;
1688 sal_Bool ImpEditEngine::IsInputSequenceCheckingRequired( sal_Unicode nChar, const EditSelection& rCurSel ) const
1690 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1691 if (!pCTLOptions)
1692 pCTLOptions = new SvtCTLOptions;
1694 // get the index that really is first
1695 USHORT nFirstPos = rCurSel.Min().GetIndex();
1696 USHORT nMaxPos = rCurSel.Max().GetIndex();
1697 if (nMaxPos < nFirstPos)
1698 nFirstPos = nMaxPos;
1700 sal_Bool bIsSequenceChecking =
1701 pCTLOptions->IsCTLFontEnabled() &&
1702 pCTLOptions->IsCTLSequenceChecking() &&
1703 nFirstPos != 0 && /* first char needs not to be checked */
1704 _xBI.is() && i18n::ScriptType::COMPLEX == _xBI->getScriptType( rtl::OUString( nChar ), 0 );
1706 return bIsSequenceChecking;
1709 /*************************************************************************
1710 * lcl_HasStrongLTR
1711 *************************************************************************/
1712 bool lcl_HasStrongLTR ( const String& rTxt, xub_StrLen nStart, xub_StrLen nEnd )
1714 for ( xub_StrLen nCharIdx = nStart; nCharIdx < nEnd; ++nCharIdx )
1716 const UCharDirection nCharDir = u_charDirection ( rTxt.GetChar ( nCharIdx ));
1717 if ( nCharDir == U_LEFT_TO_RIGHT ||
1718 nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
1719 nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
1720 return true;
1722 return false;
1727 void ImpEditEngine::InitScriptTypes( USHORT nPara )
1729 ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
1730 ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
1731 rTypes.Remove( 0, rTypes.Count() );
1733 // pParaPortion->aExtraCharInfos.Remove( 0, pParaPortion->aExtraCharInfos.Count() );
1735 ContentNode* pNode = pParaPortion->GetNode();
1736 if ( pNode->Len() )
1738 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1740 String aText( *pNode );
1742 // To handle fields put the character from the field in the string,
1743 // because endOfScript( ... ) will skip the CH_FEATURE, because this is WEAK
1744 EditCharAttrib* pField = pNode->GetCharAttribs().FindNextAttrib( EE_FEATURE_FIELD, 0 );
1745 while ( pField )
1747 ::rtl::OUString aFldText( ((EditCharAttribField*)pField)->GetFieldValue() );
1748 if ( aFldText.getLength() )
1750 aText.SetChar( pField->GetStart(), aFldText.getStr()[0] );
1751 short nFldScriptType = _xBI->getScriptType( aFldText, 0 );
1753 for ( USHORT nCharInField = 1; nCharInField < aFldText.getLength(); nCharInField++ )
1755 short nTmpType = _xBI->getScriptType( aFldText, nCharInField );
1757 // First char from field wins...
1758 if ( nFldScriptType == i18n::ScriptType::WEAK )
1760 nFldScriptType = nTmpType;
1761 aText.SetChar( pField->GetStart(), aFldText.getStr()[nCharInField] );
1764 // ... but if the first one is LATIN, and there are CJK or CTL chars too,
1765 // we prefer that ScripType because we need an other font.
1766 if ( ( nTmpType == i18n::ScriptType::ASIAN ) || ( nTmpType == i18n::ScriptType::COMPLEX ) )
1768 aText.SetChar( pField->GetStart(), aFldText.getStr()[nCharInField] );
1769 break;
1773 // #112831# Last Field might go from 0xffff to 0x0000
1774 pField = pField->GetEnd() ? pNode->GetCharAttribs().FindNextAttrib( EE_FEATURE_FIELD, pField->GetEnd() ) : NULL;
1777 ::rtl::OUString aOUText( aText );
1778 USHORT nTextLen = (USHORT)aOUText.getLength();
1780 sal_Int32 nPos = 0;
1781 short nScriptType = _xBI->getScriptType( aOUText, nPos );
1782 rTypes.Insert( ScriptTypePosInfo( nScriptType, (USHORT)nPos, nTextLen ), rTypes.Count() );
1783 nPos = _xBI->endOfScript( aOUText, nPos, nScriptType );
1784 while ( ( nPos != (-1) ) && ( nPos < nTextLen ) )
1786 rTypes[rTypes.Count()-1].nEndPos = (USHORT)nPos;
1788 nScriptType = _xBI->getScriptType( aOUText, nPos );
1789 long nEndPos = _xBI->endOfScript( aOUText, nPos, nScriptType );
1791 if ( ( nScriptType == i18n::ScriptType::WEAK ) || ( nScriptType == rTypes[rTypes.Count()-1].nScriptType ) )
1793 // Expand last ScriptTypePosInfo, don't create weak or unecessary portions
1794 rTypes[rTypes.Count()-1].nEndPos = (USHORT)nEndPos;
1796 else
1798 if ( _xBI->getScriptType( aOUText, nPos - 1 ) == i18n::ScriptType::WEAK )
1800 switch ( u_charType(aOUText.iterateCodePoints(&nPos, 0) ) ) {
1801 case U_NON_SPACING_MARK:
1802 case U_ENCLOSING_MARK:
1803 case U_COMBINING_SPACING_MARK:
1804 --nPos;
1805 rTypes[rTypes.Count()-1].nEndPos--;
1806 break;
1809 rTypes.Insert( ScriptTypePosInfo( nScriptType, (USHORT)nPos, nTextLen ), rTypes.Count() );
1812 nPos = nEndPos;
1815 if ( rTypes[0].nScriptType == i18n::ScriptType::WEAK )
1816 rTypes[0].nScriptType = ( rTypes.Count() > 1 ) ? rTypes[1].nScriptType : GetI18NScriptTypeOfLanguage( GetDefaultLanguage() );
1818 // create writing direction information:
1819 if ( !pParaPortion->aWritingDirectionInfos.Count() )
1820 InitWritingDirections( nPara );
1822 // i89825: Use CTL font for numbers embedded into an RTL run:
1823 WritingDirectionInfos& rDirInfos = pParaPortion->aWritingDirectionInfos;
1824 for ( USHORT n = 0; n < rDirInfos.Count(); ++n )
1826 const xub_StrLen nStart = rDirInfos[n].nStartPos;
1827 const xub_StrLen nEnd = rDirInfos[n].nEndPos;
1828 const BYTE nCurrDirType = rDirInfos[n].nType;
1830 if ( nCurrDirType % 2 == UBIDI_RTL || // text in RTL run
1831 ( nCurrDirType > UBIDI_LTR && !lcl_HasStrongLTR( aText, nStart, nEnd ) ) ) // non-strong text in embedded LTR run
1833 USHORT nIdx = 0;
1835 // Skip entries in ScriptArray which are not inside the RTL run:
1836 while ( nIdx < rTypes.Count() && rTypes[nIdx].nStartPos < nStart )
1837 ++nIdx;
1839 // Remove any entries *inside* the current run:
1840 while ( nIdx < rTypes.Count() && rTypes[nIdx].nEndPos <= nEnd )
1841 rTypes.Remove( nIdx );
1843 // special case:
1844 if(nIdx < rTypes.Count() && rTypes[nIdx].nStartPos < nStart && rTypes[nIdx].nEndPos > nEnd)
1846 rTypes.Insert( ScriptTypePosInfo( rTypes[nIdx].nScriptType, (USHORT)nEnd, rTypes[nIdx].nEndPos ), nIdx );
1847 rTypes[nIdx].nEndPos = nStart;
1850 if( nIdx )
1851 rTypes[nIdx - 1].nEndPos = nStart;
1853 rTypes.Insert( ScriptTypePosInfo( i18n::ScriptType::COMPLEX, (USHORT)nStart, (USHORT)nEnd), nIdx );
1854 ++nIdx;
1856 if( nIdx < rTypes.Count() )
1857 rTypes[nIdx].nStartPos = nEnd;
1861 #if OSL_DEBUG_LEVEL > 1
1862 USHORT nDebugStt = 0;
1863 USHORT nDebugEnd = 0;
1864 short nDebugType = 0;
1865 for ( USHORT n = 0; n < rTypes.Count(); ++n )
1867 nDebugStt = rTypes[n].nStartPos;
1868 nDebugEnd = rTypes[n].nEndPos;
1869 nDebugType = rTypes[n].nScriptType;
1871 #endif
1875 USHORT ImpEditEngine::GetScriptType( const EditPaM& rPaM, USHORT* pEndPos ) const
1877 USHORT nScriptType = 0;
1879 if ( pEndPos )
1880 *pEndPos = rPaM.GetNode()->Len();
1882 if ( rPaM.GetNode()->Len() )
1884 USHORT nPara = GetEditDoc().GetPos( rPaM.GetNode() );
1885 ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
1886 if ( !pParaPortion->aScriptInfos.Count() )
1887 ((ImpEditEngine*)this)->InitScriptTypes( nPara );
1889 ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
1890 USHORT nPos = rPaM.GetIndex();
1891 for ( USHORT n = 0; n < rTypes.Count(); n++ )
1893 if ( ( rTypes[n].nStartPos <= nPos ) && ( rTypes[n].nEndPos >= nPos ) )
1895 nScriptType = rTypes[n].nScriptType;
1896 if( pEndPos )
1897 *pEndPos = rTypes[n].nEndPos;
1898 break;
1902 return nScriptType ? nScriptType : GetI18NScriptTypeOfLanguage( GetDefaultLanguage() );
1905 USHORT ImpEditEngine::GetScriptType( const EditSelection& rSel ) const
1907 EditSelection aSel( rSel );
1908 aSel.Adjust( aEditDoc );
1910 short nScriptType = 0;
1912 USHORT nStartPara = GetEditDoc().GetPos( aSel.Min().GetNode() );
1913 USHORT nEndPara = GetEditDoc().GetPos( aSel.Max().GetNode() );
1915 for ( USHORT nPara = nStartPara; nPara <= nEndPara; nPara++ )
1917 ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
1918 if ( !pParaPortion->aScriptInfos.Count() )
1919 ((ImpEditEngine*)this)->InitScriptTypes( nPara );
1921 ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
1923 // find the first(!) script type position that holds the
1924 // complete selection. Thus it will work for selections as
1925 // well as with just moving the cursor from char to char.
1926 USHORT nS = ( nPara == nStartPara ) ? aSel.Min().GetIndex() : 0;
1927 USHORT nE = ( nPara == nEndPara ) ? aSel.Max().GetIndex() : pParaPortion->GetNode()->Len();
1928 for ( USHORT n = 0; n < rTypes.Count(); n++ )
1930 if (rTypes[n].nStartPos <= nS && nE <= rTypes[n].nEndPos)
1932 if ( rTypes[n].nScriptType != i18n::ScriptType::WEAK )
1934 nScriptType |= GetItemScriptType ( rTypes[n].nScriptType );
1936 else
1938 if ( !nScriptType && n )
1940 // #93548# When starting with WEAK, use prev ScriptType...
1941 nScriptType = rTypes[n-1].nScriptType;
1944 break;
1948 return nScriptType ? nScriptType : GetI18NScriptTypeOfLanguage( GetDefaultLanguage() );
1951 BOOL ImpEditEngine::IsScriptChange( const EditPaM& rPaM ) const
1953 BOOL bScriptChange = FALSE;
1955 if ( rPaM.GetNode()->Len() )
1957 USHORT nPara = GetEditDoc().GetPos( rPaM.GetNode() );
1958 ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
1959 if ( !pParaPortion->aScriptInfos.Count() )
1960 ((ImpEditEngine*)this)->InitScriptTypes( nPara );
1962 ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
1963 USHORT nPos = rPaM.GetIndex();
1964 for ( USHORT n = 0; n < rTypes.Count(); n++ )
1966 if ( rTypes[n].nStartPos == nPos )
1968 bScriptChange = TRUE;
1969 break;
1973 return bScriptChange;
1976 BOOL ImpEditEngine::HasScriptType( USHORT nPara, USHORT nType ) const
1978 BOOL bTypeFound = FALSE;
1980 ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
1981 if ( !pParaPortion->aScriptInfos.Count() )
1982 ((ImpEditEngine*)this)->InitScriptTypes( nPara );
1984 ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
1985 for ( USHORT n = rTypes.Count(); n && !bTypeFound; )
1987 if ( rTypes[--n].nScriptType == nType )
1988 bTypeFound = TRUE;
1990 return bTypeFound;
1993 void ImpEditEngine::InitWritingDirections( USHORT nPara )
1995 ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
1996 WritingDirectionInfos& rInfos = pParaPortion->aWritingDirectionInfos;
1997 rInfos.Remove( 0, rInfos.Count() );
1999 BOOL bCTL = FALSE;
2000 ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
2001 for ( USHORT n = 0; n < rTypes.Count(); n++ )
2003 if ( rTypes[n].nScriptType == i18n::ScriptType::COMPLEX )
2005 bCTL = TRUE;
2006 break;
2010 const UBiDiLevel nBidiLevel = IsRightToLeft( nPara ) ? 1 /*RTL*/ : 0 /*LTR*/;
2011 if ( ( bCTL || ( nBidiLevel == 1 /*RTL*/ ) ) && pParaPortion->GetNode()->Len() )
2014 String aText( *pParaPortion->GetNode() );
2017 // Bidi functions from icu 2.0
2019 UErrorCode nError = U_ZERO_ERROR;
2020 UBiDi* pBidi = ubidi_openSized( aText.Len(), 0, &nError );
2021 nError = U_ZERO_ERROR;
2023 ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aText.GetBuffer()), aText.Len(), nBidiLevel, NULL, &nError ); // UChar != sal_Unicode in MinGW
2024 nError = U_ZERO_ERROR;
2026 long nCount = ubidi_countRuns( pBidi, &nError );
2028 int32_t nStart = 0;
2029 int32_t nEnd;
2030 UBiDiLevel nCurrDir;
2032 for ( USHORT nIdx = 0; nIdx < nCount; ++nIdx )
2034 ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir );
2035 rInfos.Insert( WritingDirectionInfo( nCurrDir, (USHORT)nStart, (USHORT)nEnd ), rInfos.Count() );
2036 nStart = nEnd;
2039 ubidi_close( pBidi );
2042 // No infos mean no CTL and default dir is L2R...
2043 if ( !rInfos.Count() )
2044 rInfos.Insert( WritingDirectionInfo( 0, 0, (USHORT)pParaPortion->GetNode()->Len() ), rInfos.Count() );
2048 BOOL ImpEditEngine::IsRightToLeft( USHORT nPara ) const
2050 BOOL bR2L = FALSE;
2051 const SvxFrameDirectionItem* pFrameDirItem = NULL;
2053 if ( !IsVertical() )
2055 bR2L = GetDefaultHorizontalTextDirection() == EE_HTEXTDIR_R2L;
2056 pFrameDirItem = &(const SvxFrameDirectionItem&)GetParaAttrib( nPara, EE_PARA_WRITINGDIR );
2057 if ( pFrameDirItem->GetValue() == FRMDIR_ENVIRONMENT )
2059 // #103045# if DefaultHorizontalTextDirection is set, use that value, otherwise pool default.
2060 if ( GetDefaultHorizontalTextDirection() != EE_HTEXTDIR_DEFAULT )
2062 pFrameDirItem = NULL; // bR2L allready set to default horizontal text direction
2064 else
2066 // Use pool default
2067 pFrameDirItem = &(const SvxFrameDirectionItem&)((ImpEditEngine*)this)->GetEmptyItemSet().Get( EE_PARA_WRITINGDIR );
2072 if ( pFrameDirItem )
2073 bR2L = pFrameDirItem->GetValue() == FRMDIR_HORI_RIGHT_TOP;
2075 return bR2L;
2078 BOOL ImpEditEngine::HasDifferentRTLLevels( const ContentNode* pNode )
2080 USHORT nPara = GetEditDoc().GetPos( (ContentNode*)pNode );
2081 ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
2083 BOOL bHasDifferentRTLLevels = FALSE;
2085 USHORT nRTLLevel = IsRightToLeft( nPara ) ? 1 : 0;
2086 for ( USHORT n = 0; n < pParaPortion->GetTextPortions().Count(); n++ )
2088 TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( n );
2089 if ( pTextPortion->GetRightToLeft() != nRTLLevel )
2091 bHasDifferentRTLLevels = TRUE;
2092 break;
2095 return bHasDifferentRTLLevels;
2099 BYTE ImpEditEngine::GetRightToLeft( USHORT nPara, USHORT nPos, USHORT* pStart, USHORT* pEnd )
2101 // BYTE nRightToLeft = IsRightToLeft( nPara ) ? 1 : 0;
2102 BYTE nRightToLeft = 0;
2104 ContentNode* pNode = aEditDoc.SaveGetObject( nPara );
2105 if ( pNode && pNode->Len() )
2107 ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
2108 if ( !pParaPortion->aWritingDirectionInfos.Count() )
2109 InitWritingDirections( nPara );
2111 // BYTE nType = 0;
2112 WritingDirectionInfos& rDirInfos = pParaPortion->aWritingDirectionInfos;
2113 for ( USHORT n = 0; n < rDirInfos.Count(); n++ )
2115 if ( ( rDirInfos[n].nStartPos <= nPos ) && ( rDirInfos[n].nEndPos >= nPos ) )
2117 nRightToLeft = rDirInfos[n].nType;
2118 if ( pStart )
2119 *pStart = rDirInfos[n].nStartPos;
2120 if ( pEnd )
2121 *pEnd = rDirInfos[n].nEndPos;
2122 break;
2126 return nRightToLeft;
2129 SvxAdjust ImpEditEngine::GetJustification( USHORT nPara ) const
2131 SvxAdjust eJustification = SVX_ADJUST_LEFT;
2133 if ( !aStatus.IsOutliner() )
2135 eJustification = ((const SvxAdjustItem&) GetParaAttrib( nPara, EE_PARA_JUST )).GetAdjust();
2137 if ( IsRightToLeft( nPara ) )
2139 if ( eJustification == SVX_ADJUST_LEFT )
2140 eJustification = SVX_ADJUST_RIGHT;
2141 else if ( eJustification == SVX_ADJUST_RIGHT )
2142 eJustification = SVX_ADJUST_LEFT;
2145 return eJustification;
2149 // ----------------------------------------------------------------------
2150 // Textaenderung
2151 // ----------------------------------------------------------------------
2153 void ImpEditEngine::ImpRemoveChars( const EditPaM& rPaM, USHORT nChars, EditUndoRemoveChars* pCurUndo )
2155 if ( IsUndoEnabled() && !IsInUndo() )
2157 XubString aStr( rPaM.GetNode()->Copy( rPaM.GetIndex(), nChars ) );
2159 // Pruefen, ob Attribute geloescht oder geaendert werden:
2160 USHORT nStart = rPaM.GetIndex();
2161 USHORT nEnd = nStart + nChars;
2162 CharAttribArray& rAttribs = rPaM.GetNode()->GetCharAttribs().GetAttribs();
2163 // USHORT nAttrs = rAttribs.Count();
2164 for ( USHORT nAttr = 0; nAttr < rAttribs.Count(); nAttr++ )
2166 EditCharAttrib* pAttr = rAttribs[nAttr];
2167 if ( ( pAttr->GetEnd() >= nStart ) && ( pAttr->GetStart() < nEnd ) )
2169 #ifndef SVX_LIGHT
2170 EditSelection aSel( rPaM );
2171 aSel.Max().GetIndex() = aSel.Max().GetIndex() + nChars;
2172 EditUndoSetAttribs* pAttrUndo = CreateAttribUndo( aSel, GetEmptyItemSet() );
2173 InsertUndo( pAttrUndo );
2174 #endif
2175 break; // for
2178 if ( pCurUndo && ( CreateEditPaM( pCurUndo->GetEPaM() ) == rPaM ) )
2179 pCurUndo->GetStr() += aStr;
2180 #ifndef SVX_LIGHT
2181 else
2182 InsertUndo( new EditUndoRemoveChars( this, CreateEPaM( rPaM ), aStr ) );
2183 #endif
2186 aEditDoc.RemoveChars( rPaM, nChars );
2187 TextModified();
2190 EditSelection ImpEditEngine::ImpMoveParagraphs( Range aOldPositions, USHORT nNewPos )
2192 aOldPositions.Justify();
2193 BOOL bValidAction = ( (long)nNewPos < aOldPositions.Min() ) || ( (long)nNewPos > aOldPositions.Max() );
2194 DBG_ASSERT( bValidAction, "Move in sich selbst ?" );
2195 DBG_ASSERT( aOldPositions.Max() <= (long)GetParaPortions().Count(), "Voll drueber weg: MoveParagraphs" );
2197 EditSelection aSelection;
2199 if ( !bValidAction )
2201 aSelection = aEditDoc.GetStartPaM();
2202 return aSelection;
2205 ULONG nParaCount = GetParaPortions().Count();
2207 if ( nNewPos >= nParaCount )
2208 nNewPos = GetParaPortions().Count();
2210 // Height may change when moving first or last Paragraph
2211 ParaPortion* pRecalc1 = NULL;
2212 ParaPortion* pRecalc2 = NULL;
2213 ParaPortion* pRecalc3 = NULL;
2214 ParaPortion* pRecalc4 = NULL;
2216 if ( nNewPos == 0 ) // Move to Start
2218 pRecalc1 = GetParaPortions().GetObject( 0 );
2219 pRecalc2 = GetParaPortions().GetObject( (USHORT)aOldPositions.Min() );
2222 else if ( nNewPos == nParaCount )
2224 pRecalc1 = GetParaPortions().GetObject( (USHORT)(nParaCount-1) );
2225 pRecalc2 = GetParaPortions().GetObject( (USHORT)aOldPositions.Max() );
2228 if ( aOldPositions.Min() == 0 ) // Move from Start
2230 pRecalc3 = GetParaPortions().GetObject( 0 );
2231 pRecalc4 = GetParaPortions().GetObject(
2232 sal::static_int_cast< USHORT >( aOldPositions.Max()+1 ) );
2234 else if ( (USHORT)aOldPositions.Max() == (nParaCount-1) )
2236 pRecalc3 = GetParaPortions().GetObject( (USHORT)aOldPositions.Max() );
2237 pRecalc4 = GetParaPortions().GetObject( (USHORT)(aOldPositions.Min()-1) );
2240 MoveParagraphsInfo aMoveParagraphsInfo( sal::static_int_cast< USHORT >(aOldPositions.Min()), sal::static_int_cast< USHORT >(aOldPositions.Max()), nNewPos );
2241 aBeginMovingParagraphsHdl.Call( &aMoveParagraphsInfo );
2243 if ( IsUndoEnabled() && !IsInUndo())
2244 InsertUndo( new EditUndoMoveParagraphs( this, aOldPositions, nNewPos ) );
2246 // Position nicht aus dem Auge verlieren!
2247 ParaPortion* pDestPortion = GetParaPortions().SaveGetObject( nNewPos );
2249 ParaPortionList aTmpPortionList;
2250 USHORT i;
2251 for ( i = (USHORT)aOldPositions.Min(); i <= (USHORT)aOldPositions.Max(); i++ )
2253 // Immer aOldPositions.Min(), da Remove().
2254 ParaPortion* pTmpPortion = GetParaPortions().GetObject( (USHORT)aOldPositions.Min() );
2255 GetParaPortions().Remove( (USHORT)aOldPositions.Min() );
2256 aEditDoc.Remove( (USHORT)aOldPositions.Min() );
2257 aTmpPortionList.Insert( pTmpPortion, aTmpPortionList.Count() );
2260 USHORT nRealNewPos = pDestPortion ? GetParaPortions().GetPos( pDestPortion ) : GetParaPortions().Count();
2261 DBG_ASSERT( nRealNewPos != USHRT_MAX, "ImpMoveParagraphs: Ungueltige Position!" );
2263 for ( i = 0; i < (USHORT)aTmpPortionList.Count(); i++ )
2265 ParaPortion* pTmpPortion = aTmpPortionList.GetObject( i );
2266 if ( i == 0 )
2267 aSelection.Min().SetNode( pTmpPortion->GetNode() );
2269 aSelection.Max().SetNode( pTmpPortion->GetNode() );
2270 aSelection.Max().SetIndex( pTmpPortion->GetNode()->Len() );
2272 ContentNode* pN = pTmpPortion->GetNode();
2273 aEditDoc.Insert( pN, nRealNewPos+i );
2275 GetParaPortions().Insert( pTmpPortion, nRealNewPos+i );
2278 aEndMovingParagraphsHdl.Call( &aMoveParagraphsInfo );
2280 if ( GetNotifyHdl().IsSet() )
2282 EENotify aNotify( EE_NOTIFY_PARAGRAPHSMOVED );
2283 aNotify.pEditEngine = GetEditEnginePtr();
2284 aNotify.nParagraph = nNewPos;
2285 aNotify.nParam1 = sal::static_int_cast< USHORT >(aOldPositions.Min());
2286 aNotify.nParam2 = sal::static_int_cast< USHORT >(aOldPositions.Max());
2287 CallNotify( aNotify );
2290 aEditDoc.SetModified( TRUE );
2292 if ( pRecalc1 )
2293 CalcHeight( pRecalc1 );
2294 if ( pRecalc2 )
2295 CalcHeight( pRecalc2 );
2296 if ( pRecalc3 )
2297 CalcHeight( pRecalc3 );
2298 if ( pRecalc4 )
2299 CalcHeight( pRecalc4 );
2301 aTmpPortionList.Remove( 0, aTmpPortionList.Count() ); // wichtig !
2303 #ifdef EDITDEBUG
2304 GetParaPortions().DbgCheck(aEditDoc);
2305 #endif
2306 return aSelection;
2310 EditPaM ImpEditEngine::ImpConnectParagraphs( ContentNode* pLeft, ContentNode* pRight, BOOL bBackward )
2312 DBG_ASSERT( pLeft != pRight, "Den gleichen Absatz zusammenfuegen ?" );
2313 DBG_ASSERT( aEditDoc.GetPos( pLeft ) != USHRT_MAX, "Einzufuegenden Node nicht gefunden(1)" );
2314 DBG_ASSERT( aEditDoc.GetPos( pRight ) != USHRT_MAX, "Einzufuegenden Node nicht gefunden(2)" );
2316 USHORT nParagraphTobeDeleted = aEditDoc.GetPos( pRight );
2317 DeletedNodeInfo* pInf = new DeletedNodeInfo( (ULONG)pRight, nParagraphTobeDeleted );
2318 aDeletedNodes.Insert( pInf, aDeletedNodes.Count() );
2320 GetEditEnginePtr()->ParagraphConnected( aEditDoc.GetPos( pLeft ), aEditDoc.GetPos( pRight ) );
2322 #ifndef SVX_LIGHT
2323 if ( IsUndoEnabled() && !IsInUndo() )
2325 InsertUndo( new EditUndoConnectParas( this,
2326 aEditDoc.GetPos( pLeft ), pLeft->Len(),
2327 pLeft->GetContentAttribs().GetItems(), pRight->GetContentAttribs().GetItems(),
2328 pLeft->GetStyleSheet(), pRight->GetStyleSheet(), bBackward ) );
2330 #endif
2332 if ( bBackward )
2334 pLeft->SetStyleSheet( pRight->GetStyleSheet(), TRUE );
2335 pLeft->GetContentAttribs().GetItems().Set( pRight->GetContentAttribs().GetItems() );
2336 pLeft->GetCharAttribs().GetDefFont() = pRight->GetCharAttribs().GetDefFont();
2339 ParaAttribsChanged( pLeft );
2341 // Erstmal Portions suchen, da pRight nach ConnectParagraphs weg.
2342 ParaPortion* pLeftPortion = FindParaPortion( pLeft );
2343 ParaPortion* pRightPortion = FindParaPortion( pRight );
2344 DBG_ASSERT( pLeftPortion, "Blinde Portion in ImpConnectParagraphs(1)" );
2345 DBG_ASSERT( pRightPortion, "Blinde Portion in ImpConnectParagraphs(2)" );
2346 DBG_ASSERT( nParagraphTobeDeleted == GetParaPortions().GetPos( pRightPortion ), "NodePos != PortionPos?" );
2348 #ifndef SVX_LIGHT
2349 if ( GetStatus().DoOnlineSpelling() )
2351 xub_StrLen nEnd = pLeft->Len();
2352 xub_StrLen nInv = nEnd ? nEnd-1 : nEnd;
2353 pLeft->GetWrongList()->ClearWrongs( nInv, 0xFFFF, pLeft ); // Evtl. einen wegnehmen
2354 pLeft->GetWrongList()->MarkInvalid( nInv, nEnd+1 );
2355 // Falschgeschriebene Woerter ruebernehmen:
2356 USHORT nRWrongs = pRight->GetWrongList()->Count();
2357 for ( USHORT nW = 0; nW < nRWrongs; nW++ )
2359 WrongRange aWrong = pRight->GetWrongList()->GetObject( nW );
2360 if ( aWrong.nStart != 0 ) // Nicht ein anschliessender
2362 aWrong.nStart = aWrong.nStart + nEnd;
2363 aWrong.nEnd = aWrong.nEnd + nEnd;
2364 pLeft->GetWrongList()->InsertWrong( aWrong, pLeft->GetWrongList()->Count() );
2368 #endif
2370 if ( IsCallParaInsertedOrDeleted() )
2371 GetEditEnginePtr()->ParagraphDeleted( nParagraphTobeDeleted );
2373 EditPaM aPaM = aEditDoc.ConnectParagraphs( pLeft, pRight );
2374 GetParaPortions().Remove( nParagraphTobeDeleted );
2375 delete pRightPortion;
2377 pLeftPortion->MarkSelectionInvalid( aPaM.GetIndex(), pLeft->Len() );
2379 // der rechte Node wird von EditDoc::ConnectParagraphs() geloescht.
2381 if ( GetTextRanger() )
2383 // Durch das zusammenfuegen wird der linke zwar neu formatiert, aber
2384 // wenn sich dessen Hoehe nicht aendert bekommt die Formatierung die
2385 // Aenderung der Gesaamthoehe des Textes zu spaet mit...
2386 for ( USHORT n = nParagraphTobeDeleted; n < GetParaPortions().Count(); n++ )
2388 ParaPortion* pPP = GetParaPortions().GetObject( n );
2389 pPP->MarkSelectionInvalid( 0, pPP->GetNode()->Len() );
2390 pPP->GetLines().Reset();
2394 TextModified();
2396 return aPaM;
2399 EditPaM ImpEditEngine::DeleteLeftOrRight( const EditSelection& rSel, BYTE nMode, BYTE nDelMode )
2401 DBG_ASSERT( !EditSelection( rSel ).DbgIsBuggy( aEditDoc ), "Index im Wald in DeleteLeftOrRight" );
2403 if ( rSel.HasRange() ) // dann nur Sel. loeschen
2404 return ImpDeleteSelection( rSel );
2406 const EditPaM aCurPos( rSel.Max() );
2407 EditPaM aDelStart( aCurPos );
2408 EditPaM aDelEnd( aCurPos );
2409 if ( nMode == DEL_LEFT )
2411 if ( nDelMode == DELMODE_SIMPLE )
2413 aDelStart = CursorLeft( aCurPos, i18n::CharacterIteratorMode::SKIPCHARACTER );
2415 else if ( nDelMode == DELMODE_RESTOFWORD )
2417 aDelStart = StartOfWord( aCurPos );
2418 if ( aDelStart.GetIndex() == aCurPos.GetIndex() )
2419 aDelStart = WordLeft( aCurPos );
2421 else // DELMODE_RESTOFCONTENT
2423 aDelStart.SetIndex( 0 );
2424 if ( aDelStart == aCurPos )
2426 // kompletter Absatz davor
2427 ContentNode* pPrev = GetPrevVisNode( aCurPos.GetNode() );
2428 if ( pPrev )
2429 aDelStart = EditPaM( pPrev, 0 );
2433 else
2435 if ( nDelMode == DELMODE_SIMPLE )
2437 aDelEnd = CursorRight( aCurPos );
2439 else if ( nDelMode == DELMODE_RESTOFWORD )
2441 aDelEnd = EndOfWord( aCurPos );
2442 if (aDelEnd.GetIndex() == aCurPos.GetIndex())
2444 xub_StrLen nLen = aCurPos.GetNode()->Len();
2445 // end of para?
2446 if (aDelEnd.GetIndex() == nLen)
2447 aDelEnd = WordLeft( aCurPos );
2448 else // there's still sth to delete on the right
2450 aDelEnd = EndOfWord( WordRight( aCurPos ) );
2451 // if there'n no next word...
2452 if (aDelEnd.GetIndex() == nLen )
2453 aDelEnd.SetIndex( nLen );
2457 else // DELMODE_RESTOFCONTENT
2459 aDelEnd.SetIndex( aCurPos.GetNode()->Len() );
2460 if ( aDelEnd == aCurPos )
2462 // kompletter Absatz dahinter
2463 ContentNode* pNext = GetNextVisNode( aCurPos.GetNode() );
2464 if ( pNext )
2465 aDelEnd = EditPaM( pNext, pNext->Len() );
2470 // Bei DELMODE_RESTOFCONTENT reicht bei verschiedenen Nodes
2471 // kein ConnectParagraphs.
2472 if ( ( nDelMode == DELMODE_RESTOFCONTENT ) || ( aDelStart.GetNode() == aDelEnd.GetNode() ) )
2473 return ImpDeleteSelection( EditSelection( aDelStart, aDelEnd ) );
2475 // Jetzt entscheiden, ob noch Selektion loeschen (RESTOFCONTENTS)
2476 BOOL bSpecialBackward = ( ( nMode == DEL_LEFT ) && ( nDelMode == DELMODE_SIMPLE ) )
2477 ? TRUE : FALSE;
2478 if ( aStatus.IsAnyOutliner() )
2479 bSpecialBackward = FALSE;
2481 return ImpConnectParagraphs( aDelStart.GetNode(), aDelEnd.GetNode(), bSpecialBackward );
2484 EditPaM ImpEditEngine::ImpDeleteSelection( EditSelection aSel )
2486 if ( !aSel.HasRange() )
2487 return aSel.Min();
2489 aSel.Adjust( aEditDoc );
2490 EditPaM aStartPaM( aSel.Min() );
2491 EditPaM aEndPaM( aSel.Max() );
2493 CursorMoved( aStartPaM.GetNode() ); // nur damit neu eingestellte Attribute verschwinden...
2494 CursorMoved( aEndPaM.GetNode() ); // nur damit neu eingestellte Attribute verschwinden...
2496 DBG_ASSERT( aStartPaM.GetIndex() <= aStartPaM.GetNode()->Len(), "Index im Wald in ImpDeleteSelection" );
2497 DBG_ASSERT( aEndPaM.GetIndex() <= aEndPaM.GetNode()->Len(), "Index im Wald in ImpDeleteSelection" );
2499 USHORT nStartNode = aEditDoc.GetPos( aStartPaM.GetNode() );
2500 USHORT nEndNode = aEditDoc.GetPos( aEndPaM.GetNode() );
2502 DBG_ASSERT( nEndNode != USHRT_MAX, "Start > End ?!" );
2503 DBG_ASSERT( nStartNode <= nEndNode, "Start > End ?!" );
2505 // Alle Nodes dazwischen entfernen....
2506 for ( ULONG z = nStartNode+1; z < nEndNode; z++ )
2508 // Immer nStartNode+1, wegen Remove()!
2509 ImpRemoveParagraph( nStartNode+1 );
2512 if ( aStartPaM.GetNode() != aEndPaM.GetNode() )
2514 // Den Rest des StartNodes...
2515 USHORT nChars;
2516 nChars = aStartPaM.GetNode()->Len() - aStartPaM.GetIndex();
2517 ImpRemoveChars( aStartPaM, nChars );
2518 ParaPortion* pPortion = FindParaPortion( aStartPaM.GetNode() );
2519 DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteSelection(3)" );
2520 pPortion->MarkSelectionInvalid( aStartPaM.GetIndex(), aStartPaM.GetNode()->Len() );
2522 // Den Anfang des EndNodes....
2523 nChars = aEndPaM.GetIndex();
2524 aEndPaM.SetIndex( 0 );
2525 ImpRemoveChars( aEndPaM, nChars );
2526 pPortion = FindParaPortion( aEndPaM.GetNode() );
2527 DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteSelection(4)" );
2528 pPortion->MarkSelectionInvalid( 0, aEndPaM.GetNode()->Len() );
2529 // Zusammenfuegen....
2530 aStartPaM = ImpConnectParagraphs( aStartPaM.GetNode(), aEndPaM.GetNode() );
2532 else
2534 USHORT nChars;
2535 nChars = aEndPaM.GetIndex() - aStartPaM.GetIndex();
2536 ImpRemoveChars( aStartPaM, nChars );
2537 ParaPortion* pPortion = FindParaPortion( aStartPaM.GetNode() );
2538 DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteSelection(5)" );
2539 pPortion->MarkInvalid( aEndPaM.GetIndex(), aStartPaM.GetIndex() - aEndPaM.GetIndex() );
2542 UpdateSelections();
2543 TextModified();
2544 return aStartPaM;
2547 void ImpEditEngine::ImpRemoveParagraph( USHORT nPara )
2549 ContentNode* pNode = aEditDoc.SaveGetObject( nPara );
2550 ContentNode* pNextNode = aEditDoc.SaveGetObject( nPara+1 );
2551 ParaPortion* pPortion = GetParaPortions().SaveGetObject( nPara );
2553 DBG_ASSERT( pNode, "Blinder Node in ImpRemoveParagraph" );
2554 DBG_ASSERT( pPortion, "Blinde Portion in ImpRemoveParagraph(2)" );
2556 DeletedNodeInfo* pInf = new DeletedNodeInfo( (ULONG)pNode, nPara );
2557 aDeletedNodes.Insert( pInf, aDeletedNodes.Count() );
2559 // Der Node wird vom Undo verwaltet und ggf. zerstoert!
2560 /* delete */ aEditDoc.Remove( nPara );
2561 GetParaPortions().Remove( nPara );
2562 delete pPortion;
2564 if ( IsCallParaInsertedOrDeleted() )
2566 GetEditEnginePtr()->ParagraphDeleted( nPara );
2569 // Im folgenden muss ggf. Extra-Space neu ermittelt werden.
2570 // Bei ParaAttribsChanged wird leider der Absatz neu formatiert,
2571 // aber diese Methode sollte nicht Zeitkritsch sein!
2572 if ( pNextNode )
2573 ParaAttribsChanged( pNextNode );
2575 #ifndef SVX_LIGHT
2576 if ( IsUndoEnabled() && !IsInUndo() )
2577 InsertUndo( new EditUndoDelContent( this, pNode, nPara ) );
2578 else
2579 #endif
2581 aEditDoc.RemoveItemsFromPool( pNode );
2582 if ( pNode->GetStyleSheet() )
2583 EndListening( *pNode->GetStyleSheet(), FALSE );
2584 delete pNode;
2588 EditPaM ImpEditEngine::AutoCorrect( const EditSelection& rCurSel, xub_Unicode c,
2589 bool bOverwrite, Window* pFrameWin )
2591 EditSelection aSel( rCurSel );
2592 #ifndef SVX_LIGHT
2593 SvxAutoCorrect* pAutoCorrect = SvxAutoCorrCfg::Get()->GetAutoCorrect();
2594 if ( pAutoCorrect )
2596 if ( aSel.HasRange() )
2597 aSel = ImpDeleteSelection( rCurSel );
2599 // #i78661 allow application to turn off capitalization of
2600 // start sentence explicitly.
2601 // (This is done by setting IsFirstWordCapitalization to FALSE.)
2602 BOOL bOldCptlSttSntnc = pAutoCorrect->IsAutoCorrFlag( CptlSttSntnc );
2603 if (!IsFirstWordCapitalization())
2605 ESelection aESel( CreateESel(aSel) );
2606 EditSelection aFirstWordSel;
2607 EditSelection aSecondWordSel;
2608 if (aESel.nEndPara == 0) // is this the first para?
2610 // select first word...
2611 // start by checking if para starts with word.
2612 aFirstWordSel = SelectWord( CreateSel(ESelection()) );
2613 if (aFirstWordSel.Min().GetIndex() == 0 && aFirstWordSel.Max().GetIndex() == 0)
2615 // para does not start with word -> select next/first word
2616 EditPaM aRightWord( WordRight( aFirstWordSel.Max(), 1 ) );
2617 aFirstWordSel = SelectWord( EditSelection( aRightWord ) );
2620 // select second word
2621 // (sometimes aSel mightnot point to the end of the first word
2622 // but to some following char like '.'. ':', ...
2623 // In those cases we need aSecondWordSel to see if aSel
2624 // will actually effect the first word.)
2625 EditPaM aRight2Word( WordRight( aFirstWordSel.Max(), 1 ) );
2626 aSecondWordSel = SelectWord( EditSelection( aRight2Word ) );
2628 BOOL bIsFirstWordInFirstPara = aESel.nEndPara == 0 &&
2629 aFirstWordSel.Max().GetIndex() <= aSel.Max().GetIndex() &&
2630 aSel.Max().GetIndex() <= aSecondWordSel.Min().GetIndex();
2632 if (bIsFirstWordInFirstPara)
2633 pAutoCorrect->SetAutoCorrFlag( CptlSttSntnc, IsFirstWordCapitalization() );
2636 ContentNode* pNode = aSel.Max().GetNode();
2637 USHORT nIndex = aSel.Max().GetIndex();
2638 EdtAutoCorrDoc aAuto( this, pNode, nIndex, c );
2639 pAutoCorrect->AutoCorrect( aAuto, *pNode, nIndex, c, !bOverwrite, pFrameWin );
2640 aSel.Max().SetIndex( aAuto.GetCursor() );
2642 // #i78661 since the SvxAutoCorrect object used here is
2643 // shared we need to reset the value to it's original state.
2644 pAutoCorrect->SetAutoCorrFlag( CptlSttSntnc, bOldCptlSttSntnc );
2646 #endif // !SVX_LIGHT
2647 return aSel.Max();
2651 EditPaM ImpEditEngine::InsertText( const EditSelection& rCurSel,
2652 xub_Unicode c, BOOL bOverwrite, sal_Bool bIsUserInput )
2654 DBG_ASSERT( c != '\t', "Tab bei InsertText ?" );
2655 DBG_ASSERT( c != '\n', "Zeilenumbruch bei InsertText ?" );
2657 EditPaM aPaM( rCurSel.Min() );
2659 BOOL bDoOverwrite = ( bOverwrite &&
2660 ( aPaM.GetIndex() < aPaM.GetNode()->Len() ) ) ? TRUE : FALSE;
2662 BOOL bUndoAction = ( rCurSel.HasRange() || bDoOverwrite );
2664 if ( bUndoAction )
2665 UndoActionStart( EDITUNDO_INSERT );
2667 if ( rCurSel.HasRange() )
2669 aPaM = ImpDeleteSelection( rCurSel );
2671 else if ( bDoOverwrite )
2673 // Wenn Selektion, dann nicht auch noch ein Zeichen ueberschreiben!
2674 EditSelection aTmpSel( aPaM );
2675 aTmpSel.Max().GetIndex()++;
2676 DBG_ASSERT( !aTmpSel.DbgIsBuggy( aEditDoc ), "Overwrite: Fehlerhafte Selektion!" );
2677 ImpDeleteSelection( aTmpSel );
2680 if ( aPaM.GetNode()->Len() < MAXCHARSINPARA )
2682 if (bIsUserInput && IsInputSequenceCheckingRequired( c, rCurSel ))
2684 uno::Reference < i18n::XExtendedInputSequenceChecker > _xISC( ImplGetInputSequenceChecker() );
2685 if (!pCTLOptions)
2686 pCTLOptions = new SvtCTLOptions;
2688 if (_xISC.is() || pCTLOptions)
2690 xub_StrLen nTmpPos = aPaM.GetIndex();
2691 sal_Int16 nCheckMode = pCTLOptions->IsCTLSequenceCheckingRestricted() ?
2692 i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC;
2694 // the text that needs to be checked is only the one
2695 // before the current cursor position
2696 rtl::OUString aOldText( aPaM.GetNode()->Copy(0, nTmpPos) );
2697 rtl::OUString aNewText( aOldText );
2698 if (pCTLOptions->IsCTLSequenceCheckingTypeAndReplace())
2700 /*const xub_StrLen nPrevPos = static_cast< xub_StrLen >*/( _xISC->correctInputSequence( aNewText, nTmpPos - 1, c, nCheckMode ) );
2702 // find position of first character that has changed
2703 sal_Int32 nOldLen = aOldText.getLength();
2704 sal_Int32 nNewLen = aNewText.getLength();
2705 const sal_Unicode *pOldTxt = aOldText.getStr();
2706 const sal_Unicode *pNewTxt = aNewText.getStr();
2707 sal_Int32 nChgPos = 0;
2708 while ( nChgPos < nOldLen && nChgPos < nNewLen &&
2709 pOldTxt[nChgPos] == pNewTxt[nChgPos] )
2710 ++nChgPos;
2712 xub_StrLen nChgLen = static_cast< xub_StrLen >( nNewLen - nChgPos );
2713 String aChgText( aNewText.copy( nChgPos ), nChgLen );
2715 // select text from first pos to be changed to current pos
2716 EditSelection aSel( EditPaM( aPaM.GetNode(), (USHORT) nChgPos ), aPaM );
2718 if (aChgText.Len())
2719 return InsertText( aSel, aChgText ); // implicitly handles undo
2720 else
2721 return aPaM;
2723 else
2725 // should the character be ignored (i.e. not get inserted) ?
2726 if (!_xISC->checkInputSequence( aOldText, nTmpPos - 1, c, nCheckMode ))
2727 return aPaM; // nothing to be done -> no need for undo
2731 // at this point now we will insert the character 'normally' some lines below...
2734 if ( IsUndoEnabled() && !IsInUndo() )
2736 EditUndoInsertChars* pNewUndo = new EditUndoInsertChars( this, CreateEPaM( aPaM ), c );
2737 BOOL bTryMerge = ( !bDoOverwrite && ( c != ' ' ) ) ? TRUE : FALSE;
2738 InsertUndo( pNewUndo, bTryMerge );
2741 aEditDoc.InsertText( (const EditPaM&)aPaM, c );
2742 ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
2743 DBG_ASSERT( pPortion, "Blinde Portion in InsertText" );
2744 pPortion->MarkInvalid( aPaM.GetIndex(), 1 );
2745 aPaM.GetIndex()++; // macht EditDoc-Methode nicht mehr
2748 TextModified();
2750 if ( bUndoAction )
2751 UndoActionEnd( EDITUNDO_INSERT );
2753 return aPaM;
2756 EditPaM ImpEditEngine::ImpInsertText( EditSelection aCurSel, const XubString& rStr )
2758 UndoActionStart( EDITUNDO_INSERT );
2760 EditPaM aPaM;
2761 if ( aCurSel.HasRange() )
2762 aPaM = ImpDeleteSelection( aCurSel );
2763 else
2764 aPaM = aCurSel.Max();
2766 EditPaM aCurPaM( aPaM ); // fuers Invalidieren
2768 XubString aText( rStr );
2769 aText.ConvertLineEnd( LINEEND_LF );
2770 SfxVoidItem aTabItem( EE_FEATURE_TAB );
2772 // Konvertiert nach LineSep = \n
2773 // Token mit LINE_SEP abfragen,
2774 // da der MAC-Compiler aus \n etwas anderes macht!
2776 USHORT nStart = 0;
2777 while ( nStart < aText.Len() )
2779 USHORT nEnd = aText.Search( LINE_SEP, nStart );
2780 if ( nEnd == STRING_NOTFOUND )
2781 nEnd = aText.Len(); // nicht dereferenzieren!
2783 // Start == End => Leerzeile
2784 if ( nEnd > nStart )
2786 XubString aLine( aText, nStart, nEnd-nStart );
2787 xub_StrLen nChars = aPaM.GetNode()->Len() + aLine.Len();
2788 if ( nChars > MAXCHARSINPARA )
2790 USHORT nMaxNewChars = MAXCHARSINPARA-aPaM.GetNode()->Len();
2791 nEnd -= ( aLine.Len() - nMaxNewChars ); // Dann landen die Zeichen im naechsten Absatz.
2792 aLine.Erase( nMaxNewChars ); // Del Rest...
2794 #ifndef SVX_LIGHT
2795 if ( IsUndoEnabled() && !IsInUndo() )
2796 InsertUndo( new EditUndoInsertChars( this, CreateEPaM( aPaM ), aLine ) );
2797 #endif
2798 // Tabs ?
2799 if ( aLine.Search( '\t' ) == STRING_NOTFOUND )
2800 aPaM = aEditDoc.InsertText( aPaM, aLine );
2801 else
2803 USHORT nStart2 = 0;
2804 while ( nStart2 < aLine.Len() )
2806 USHORT nEnd2 = aLine.Search( '\t', nStart2 );
2807 if ( nEnd2 == STRING_NOTFOUND )
2808 nEnd2 = aLine.Len(); // nicht dereferenzieren!
2810 if ( nEnd2 > nStart2 )
2811 aPaM = aEditDoc.InsertText( aPaM, XubString( aLine, nStart2, nEnd2-nStart2 ) );
2812 if ( nEnd2 < aLine.Len() )
2814 // aPaM = ImpInsertFeature( EditSelection( aPaM, aPaM ), );
2815 aPaM = aEditDoc.InsertFeature( aPaM, aTabItem );
2817 nStart2 = nEnd2+1;
2820 ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
2821 DBG_ASSERT( pPortion, "Blinde Portion in InsertText" );
2822 pPortion->MarkInvalid( aCurPaM.GetIndex(), aLine.Len() );
2824 if ( nEnd < aText.Len() )
2825 aPaM = ImpInsertParaBreak( aPaM );
2827 nStart = nEnd+1;
2830 UndoActionEnd( EDITUNDO_INSERT );
2832 TextModified();
2833 return aPaM;
2836 EditPaM ImpEditEngine::ImpFastInsertText( EditPaM aPaM, const XubString& rStr )
2838 DBG_ASSERT( rStr.Search( 0x0A ) == STRING_NOTFOUND, "FastInsertText: Zeilentrenner nicht erlaubt!" );
2839 DBG_ASSERT( rStr.Search( 0x0D ) == STRING_NOTFOUND, "FastInsertText: Zeilentrenner nicht erlaubt!" );
2840 DBG_ASSERT( rStr.Search( '\t' ) == STRING_NOTFOUND, "FastInsertText: Features nicht erlaubt!" );
2842 if ( ( aPaM.GetNode()->Len() + rStr.Len() ) < MAXCHARSINPARA )
2844 #ifndef SVX_LIGHT
2845 if ( IsUndoEnabled() && !IsInUndo() )
2846 InsertUndo( new EditUndoInsertChars( this, CreateEPaM( aPaM ), rStr ) );
2847 #endif
2849 aPaM = aEditDoc.InsertText( aPaM, rStr );
2850 TextModified();
2852 else
2854 aPaM = ImpInsertText( aPaM, rStr );
2857 return aPaM;
2860 EditPaM ImpEditEngine::ImpInsertFeature( EditSelection aCurSel, const SfxPoolItem& rItem )
2862 EditPaM aPaM;
2863 if ( aCurSel.HasRange() )
2864 aPaM = ImpDeleteSelection( aCurSel );
2865 else
2866 aPaM = aCurSel.Max();
2868 if ( aPaM.GetIndex() >= 0xfffe )
2869 return aPaM;
2871 #ifndef SVX_LIGHT
2872 if ( IsUndoEnabled() && !IsInUndo() )
2873 InsertUndo( new EditUndoInsertFeature( this, CreateEPaM( aPaM ), rItem ) );
2874 #endif
2875 aPaM = aEditDoc.InsertFeature( aPaM, rItem );
2877 ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
2878 DBG_ASSERT( pPortion, "Blinde Portion in InsertFeature" );
2879 pPortion->MarkInvalid( aPaM.GetIndex()-1, 1 );
2881 TextModified();
2883 return aPaM;
2886 EditPaM ImpEditEngine::ImpInsertParaBreak( const EditSelection& rCurSel, BOOL bKeepEndingAttribs )
2888 EditPaM aPaM;
2889 if ( rCurSel.HasRange() )
2890 aPaM = ImpDeleteSelection( rCurSel );
2891 else
2892 aPaM = rCurSel.Max();
2894 return ImpInsertParaBreak( aPaM, bKeepEndingAttribs );
2897 EditPaM ImpEditEngine::ImpInsertParaBreak( const EditPaM& rPaM, BOOL bKeepEndingAttribs )
2899 if ( aEditDoc.Count() >= 0xFFFE )
2901 DBG_ERROR( "Can't process more than 64K paragraphs!" );
2902 return rPaM;
2905 #ifndef SVX_LIGHT
2906 if ( IsUndoEnabled() && !IsInUndo() )
2907 InsertUndo( new EditUndoSplitPara( this, aEditDoc.GetPos( rPaM.GetNode() ), rPaM.GetIndex() ) );
2908 #endif
2910 EditPaM aPaM( aEditDoc.InsertParaBreak( rPaM, bKeepEndingAttribs ) );
2912 #ifndef SVX_LIGHT
2913 if ( GetStatus().DoOnlineSpelling() )
2915 xub_StrLen nEnd = rPaM.GetNode()->Len();
2916 aPaM.GetNode()->CreateWrongList();
2917 WrongList* pLWrongs = rPaM.GetNode()->GetWrongList();
2918 WrongList* pRWrongs = aPaM.GetNode()->GetWrongList();
2919 // Falschgeschriebene Woerter ruebernehmen:
2920 USHORT nLWrongs = pLWrongs->Count();
2921 for ( USHORT nW = 0; nW < nLWrongs; nW++ )
2923 WrongRange& rWrong = pLWrongs->GetObject( nW );
2924 // Nur wenn wirklich dahinter, ein ueberlappendes wird beim Spell korrigiert
2925 if ( rWrong.nStart > nEnd )
2927 pRWrongs->InsertWrong( rWrong, pRWrongs->Count() );
2928 WrongRange& rRWrong = pRWrongs->GetObject( pRWrongs->Count() - 1 );
2929 rRWrong.nStart = rRWrong.nStart - nEnd;
2930 rRWrong.nEnd = rRWrong.nEnd - nEnd;
2932 else if ( ( rWrong.nStart < nEnd ) && ( rWrong.nEnd > nEnd ) )
2933 rWrong.nEnd = nEnd;
2935 USHORT nInv = nEnd ? nEnd-1 : nEnd;
2936 if ( nEnd )
2937 pLWrongs->MarkInvalid( nInv, nEnd );
2938 else
2939 pLWrongs->SetValid();
2940 pRWrongs->SetValid(); // sonst 0 - 0xFFFF
2941 pRWrongs->MarkInvalid( 0, 1 ); // Nur das erste Wort testen
2943 #endif // !SVX_LIGHT
2946 ParaPortion* pPortion = FindParaPortion( rPaM.GetNode() );
2947 DBG_ASSERT( pPortion, "Blinde Portion in ImpInsertParaBreak" );
2948 pPortion->MarkInvalid( rPaM.GetIndex(), 0 );
2950 // Optimieren: Nicht unnoetig viele GetPos auf die Listen ansetzen!
2951 // Hier z.B. bei Undo, aber auch in allen anderen Methoden.
2952 USHORT nPos = GetParaPortions().GetPos( pPortion );
2953 ParaPortion* pNewPortion = new ParaPortion( aPaM.GetNode() );
2954 GetParaPortions().Insert( pNewPortion, nPos + 1 );
2955 ParaAttribsChanged( pNewPortion->GetNode() );
2956 if ( IsCallParaInsertedOrDeleted() )
2957 GetEditEnginePtr()->ParagraphInserted( nPos+1 );
2959 CursorMoved( rPaM.GetNode() ); // falls leeres Attribut entstanden.
2960 TextModified();
2961 return aPaM;
2964 EditPaM ImpEditEngine::ImpFastInsertParagraph( USHORT nPara )
2966 #ifndef SVX_LIGHT
2967 if ( IsUndoEnabled() && !IsInUndo() )
2969 if ( nPara )
2971 DBG_ASSERT( aEditDoc.SaveGetObject( nPara-1 ), "FastInsertParagraph: Prev existiert nicht" );
2972 InsertUndo( new EditUndoSplitPara( this, nPara-1, aEditDoc.GetObject( nPara-1 )->Len() ) );
2974 else
2975 InsertUndo( new EditUndoSplitPara( this, 0, 0 ) );
2977 #endif
2979 ContentNode* pNode = new ContentNode( aEditDoc.GetItemPool() );
2980 // Falls FlatMode, wird spaeter kein Font eingestellt:
2981 pNode->GetCharAttribs().GetDefFont() = aEditDoc.GetDefFont();
2983 #ifndef SVX_LIGHT
2984 if ( GetStatus().DoOnlineSpelling() )
2985 pNode->CreateWrongList();
2986 #endif // !SVX_LIGHT
2988 aEditDoc.Insert( pNode, nPara );
2990 ParaPortion* pNewPortion = new ParaPortion( pNode );
2991 GetParaPortions().Insert( pNewPortion, nPara );
2992 if ( IsCallParaInsertedOrDeleted() )
2993 GetEditEnginePtr()->ParagraphInserted( nPara );
2995 return EditPaM( pNode, 0 );
2998 EditPaM ImpEditEngine::InsertParaBreak( EditSelection aCurSel )
3000 EditPaM aPaM( ImpInsertParaBreak( aCurSel ) );
3001 if ( aStatus.DoAutoIndenting() )
3003 USHORT nPara = aEditDoc.GetPos( aPaM.GetNode() );
3004 DBG_ASSERT( nPara > 0, "AutoIndenting: Fehler!" );
3005 XubString aPrevParaText( GetEditDoc().GetParaAsString( nPara-1 ) );
3006 USHORT n = 0;
3007 while ( ( n < aPrevParaText.Len() ) &&
3008 ( ( aPrevParaText.GetChar(n) == ' ' ) || ( aPrevParaText.GetChar(n) == '\t' ) ) )
3010 if ( aPrevParaText.GetChar(n) == '\t' )
3011 aPaM = ImpInsertFeature( aPaM, SfxVoidItem( EE_FEATURE_TAB ) );
3012 else
3013 aPaM = ImpInsertText( aPaM, aPrevParaText.GetChar(n) );
3014 n++;
3018 return aPaM;
3021 EditPaM ImpEditEngine::InsertTab( EditSelection aCurSel )
3023 EditPaM aPaM( ImpInsertFeature( aCurSel, SfxVoidItem( EE_FEATURE_TAB ) ) );
3024 return aPaM;
3027 EditPaM ImpEditEngine::InsertField( EditSelection aCurSel, const SvxFieldItem& rFld )
3029 EditPaM aPaM( ImpInsertFeature( aCurSel, rFld ) );
3030 return aPaM;
3033 BOOL ImpEditEngine::UpdateFields()
3035 BOOL bChanges = FALSE;
3036 USHORT nParas = GetEditDoc().Count();
3037 for ( USHORT nPara = 0; nPara < nParas; nPara++ )
3039 BOOL bChangesInPara = FALSE;
3040 ContentNode* pNode = GetEditDoc().GetObject( nPara );
3041 DBG_ASSERT( pNode, "NULL-Pointer im Doc" );
3042 CharAttribArray& rAttribs = pNode->GetCharAttribs().GetAttribs();
3043 // USHORT nAttrs = rAttribs.Count();
3044 for ( USHORT nAttr = 0; nAttr < rAttribs.Count(); nAttr++ )
3046 EditCharAttrib* pAttr = rAttribs[nAttr];
3047 if ( pAttr->Which() == EE_FEATURE_FIELD )
3049 EditCharAttribField* pField = (EditCharAttribField*)pAttr;
3050 EditCharAttribField* pCurrent = new EditCharAttribField( *pField );
3051 pField->Reset();
3053 if ( aStatus.MarkFields() )
3054 pField->GetFldColor() = new Color( GetColorConfig().GetColorValue( svtools::WRITERFIELDSHADINGS ).nColor );
3056 XubString aFldValue = GetEditEnginePtr()->CalcFieldValue(
3057 (const SvxFieldItem&)*pField->GetItem(),
3058 nPara, pField->GetStart(),
3059 pField->GetTxtColor(), pField->GetFldColor() );
3060 pField->GetFieldValue() = aFldValue;
3061 if ( *pField != *pCurrent )
3063 bChanges = TRUE;
3064 bChangesInPara = TRUE;
3066 delete pCurrent;
3069 if ( bChangesInPara )
3071 // ggf. etwas genauer invalidieren.
3072 ParaPortion* pPortion = GetParaPortions().GetObject( nPara );
3073 DBG_ASSERT( pPortion, "NULL-Pointer im Doc" );
3074 pPortion->MarkSelectionInvalid( 0, pNode->Len() );
3077 return bChanges;
3080 EditPaM ImpEditEngine::InsertLineBreak( EditSelection aCurSel )
3082 EditPaM aPaM( ImpInsertFeature( aCurSel, SfxVoidItem( EE_FEATURE_LINEBR ) ) );
3083 return aPaM;
3086 // ----------------------------------------------------------------------
3087 // Hilfsfunktionen
3088 // ----------------------------------------------------------------------
3089 Rectangle ImpEditEngine::PaMtoEditCursor( EditPaM aPaM, USHORT nFlags )
3091 DBG_ASSERT( GetUpdateMode(), "Darf bei Update=FALSE nicht erreicht werden: PaMtoEditCursor" );
3093 Rectangle aEditCursor;
3094 long nY = 0;
3095 for ( USHORT nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
3097 ParaPortion* pPortion = GetParaPortions().GetObject(nPortion);
3098 ContentNode* pNode = pPortion->GetNode();
3099 DBG_ASSERT( pNode, "Ungueltiger Node in Portion!" );
3100 if ( pNode != aPaM.GetNode() )
3102 nY += pPortion->GetHeight();
3104 else
3106 aEditCursor = GetEditCursor( pPortion, aPaM.GetIndex(), nFlags );
3107 aEditCursor.Top() += nY;
3108 aEditCursor.Bottom() += nY;
3109 return aEditCursor;
3112 DBG_ERROR( "Portion nicht gefunden!" );
3113 return aEditCursor;
3116 EditPaM ImpEditEngine::GetPaM( Point aDocPos, BOOL bSmart )
3118 DBG_ASSERT( GetUpdateMode(), "Darf bei Update=FALSE nicht erreicht werden: GetPaM" );
3120 long nY = 0;
3121 long nTmpHeight;
3122 EditPaM aPaM;
3123 USHORT nPortion;
3124 for ( nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
3126 ParaPortion* pPortion = GetParaPortions().GetObject(nPortion);
3127 nTmpHeight = pPortion->GetHeight(); // sollte auch bei !bVisible richtig sein!
3128 nY += nTmpHeight;
3129 if ( nY > aDocPos.Y() )
3131 nY -= nTmpHeight;
3132 aDocPos.Y() -= nY;
3133 // unsichtbare Portions ueberspringen:
3134 while ( pPortion && !pPortion->IsVisible() )
3136 nPortion++;
3137 pPortion = GetParaPortions().SaveGetObject( nPortion );
3139 DBG_ASSERT( pPortion, "Keinen sichtbaren Absatz gefunden: GetPaM" );
3140 aPaM = GetPaM( pPortion, aDocPos, bSmart );
3141 return aPaM;
3145 // Dann den letzten sichtbaren Suchen:
3146 nPortion = GetParaPortions().Count()-1;
3147 while ( nPortion && !GetParaPortions()[nPortion]->IsVisible() )
3148 nPortion--;
3150 DBG_ASSERT( GetParaPortions()[nPortion]->IsVisible(), "Keinen sichtbaren Absatz gefunden: GetPaM" );
3151 aPaM.SetNode( GetParaPortions()[nPortion]->GetNode() );
3152 aPaM.SetIndex( GetParaPortions()[nPortion]->GetNode()->Len() );
3153 return aPaM;
3156 sal_uInt32 ImpEditEngine::GetTextHeight() const
3158 DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=FALSE nicht verwendet werden: GetTextHeight" );
3159 DBG_ASSERT( IsFormatted() || IsFormatting(), "GetTextHeight: Nicht formatiert" );
3160 return nCurTextHeight;
3163 sal_uInt32 ImpEditEngine::CalcTextWidth( BOOL bIgnoreExtraSpace )
3165 // Wenn noch nicht formatiert und nicht gerade dabei.
3166 // Wird in der Formatierung bei AutoPageSize gerufen.
3167 if ( !IsFormatted() && !IsFormatting() )
3168 FormatDoc();
3170 EditLine* pLine;
3172 long nMaxWidth = 0;
3173 long nCurWidth = 0;
3175 // --------------------------------------------------
3176 // Ueber alle Absaetze...
3177 // --------------------------------------------------
3178 USHORT nParas = GetParaPortions().Count();
3179 // USHORT nBiggestPara = 0;
3180 // USHORT nBiggestLine = 0;
3181 for ( USHORT nPara = 0; nPara < nParas; nPara++ )
3183 ParaPortion* pPortion = GetParaPortions().GetObject( nPara );
3184 if ( pPortion->IsVisible() )
3186 const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pPortion->GetNode() );
3187 sal_Int32 nSpaceBeforeAndMinLabelWidth = GetSpaceBeforeAndMinLabelWidth( pPortion->GetNode() );
3189 // --------------------------------------------------
3190 // Ueber die Zeilen des Absatzes...
3191 // --------------------------------------------------
3192 ULONG nLines = pPortion->GetLines().Count();
3193 for ( USHORT nLine = 0; nLine < nLines; nLine++ )
3195 pLine = pPortion->GetLines().GetObject( nLine );
3196 DBG_ASSERT( pLine, "NULL-Pointer im Zeileniterator in CalcWidth" );
3197 // nCurWidth = pLine->GetStartPosX();
3198 // Bei Center oder Right haengt die breite von der
3199 // Papierbreite ab, hier nicht erwuenscht.
3200 // Am besten generell nicht auf StartPosX verlassen,
3201 // es muss auch die rechte Einrueckung beruecksichtigt werden!
3202 nCurWidth = GetXValue( rLRItem.GetTxtLeft() + nSpaceBeforeAndMinLabelWidth );
3203 if ( nLine == 0 )
3205 long nFI = GetXValue( rLRItem.GetTxtFirstLineOfst() );
3206 nCurWidth -= nFI;
3207 if ( pPortion->GetBulletX() > nCurWidth )
3209 nCurWidth += nFI; // LI?
3210 if ( pPortion->GetBulletX() > nCurWidth )
3211 nCurWidth = pPortion->GetBulletX();
3214 nCurWidth += GetXValue( rLRItem.GetRight() );
3215 nCurWidth += CalcLineWidth( pPortion, pLine, bIgnoreExtraSpace );
3216 if ( nCurWidth > nMaxWidth )
3218 nMaxWidth = nCurWidth;
3223 if ( nMaxWidth < 0 )
3224 nMaxWidth = 0;
3226 nMaxWidth++; // Ein breiter, da in CreateLines bei >= umgebrochen wird.
3227 return (sal_uInt32)nMaxWidth;
3230 sal_uInt32 ImpEditEngine::CalcLineWidth( ParaPortion* pPortion, EditLine* pLine, BOOL bIgnoreExtraSpace )
3232 USHORT nPara = GetEditDoc().GetPos( pPortion->GetNode() );
3234 // #114278# Saving both layout mode and language (since I'm
3235 // potentially changing both)
3236 GetRefDevice()->Push( PUSH_TEXTLAYOUTMODE|PUSH_TEXTLANGUAGE );
3238 ImplInitLayoutMode( GetRefDevice(), nPara, 0xFFFF );
3240 SvxAdjust eJustification = GetJustification( nPara );
3242 // Berechnung der Breite ohne die Indents...
3243 sal_uInt32 nWidth = 0;
3244 USHORT nPos = pLine->GetStart();
3245 for ( USHORT nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ )
3247 TextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( nTP );
3248 switch ( pTextPortion->GetKind() )
3250 case PORTIONKIND_FIELD:
3251 case PORTIONKIND_HYPHENATOR:
3252 case PORTIONKIND_TAB:
3254 nWidth += pTextPortion->GetSize().Width();
3256 break;
3257 case PORTIONKIND_TEXT:
3259 if ( ( eJustification != SVX_ADJUST_BLOCK ) || ( !bIgnoreExtraSpace ) )
3261 nWidth += pTextPortion->GetSize().Width();
3263 else
3265 SvxFont aTmpFont( pPortion->GetNode()->GetCharAttribs().GetDefFont() );
3266 SeekCursor( pPortion->GetNode(), nPos+1, aTmpFont );
3267 aTmpFont.SetPhysFont( GetRefDevice() );
3268 ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
3269 nWidth += aTmpFont.QuickGetTextSize( GetRefDevice(), *pPortion->GetNode(), nPos, pTextPortion->GetLen(), NULL ).Width();
3272 break;
3274 nPos = nPos + pTextPortion->GetLen();
3277 GetRefDevice()->Pop();
3279 return nWidth;
3282 sal_uInt32 ImpEditEngine::CalcTextHeight()
3284 DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=FALSE nicht verwendet werden: CalcTextHeight" );
3285 sal_uInt32 nY = 0;
3286 for ( USHORT nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
3287 nY += GetParaPortions()[nPortion]->GetHeight();
3288 return nY;
3291 USHORT ImpEditEngine::GetLineCount( USHORT nParagraph ) const
3293 DBG_ASSERT( nParagraph < GetParaPortions().Count(), "GetLineCount: Out of range" );
3294 ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
3295 DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetLineCount" );
3296 if ( pPPortion )
3297 return pPPortion->GetLines().Count();
3299 return 0xFFFF;
3302 xub_StrLen ImpEditEngine::GetLineLen( USHORT nParagraph, USHORT nLine ) const
3304 DBG_ASSERT( nParagraph < GetParaPortions().Count(), "GetLineLen: Out of range" );
3305 ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
3306 DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetLineLen" );
3307 if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) )
3309 EditLine* pLine = pPPortion->GetLines().GetObject( nLine );
3310 DBG_ASSERT( pLine, "Zeile nicht gefunden: GetLineHeight" );
3311 return pLine->GetLen();
3314 return 0xFFFF;
3317 void ImpEditEngine::GetLineBoundaries( /*out*/USHORT &rStart, /*out*/USHORT &rEnd, USHORT nParagraph, USHORT nLine ) const
3319 DBG_ASSERT( nParagraph < GetParaPortions().Count(), "GetLineCount: Out of range" );
3320 ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
3321 DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetLineBoundaries" );
3322 rStart = rEnd = 0xFFFF; // default values in case of error
3323 if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) )
3325 EditLine* pLine = pPPortion->GetLines().GetObject( nLine );
3326 DBG_ASSERT( pLine, "Zeile nicht gefunden: GetLineBoundaries" );
3327 rStart = pLine->GetStart();
3328 rEnd = pLine->GetEnd();
3332 USHORT ImpEditEngine::GetLineNumberAtIndex( USHORT nPara, USHORT nIndex ) const
3334 USHORT nLineNo = 0xFFFF;
3335 ContentNode* pNode = GetEditDoc().SaveGetObject( nPara );
3336 DBG_ASSERT( pNode, "GetLineNumberAtIndex: invalid paragraph index" );
3337 if (pNode)
3339 // we explicitly allow for the index to point at the character right behind the text
3340 const bool bValidIndex = /*0 <= nIndex &&*/ nIndex <= pNode->Len();
3341 DBG_ASSERT( bValidIndex, "GetLineNumberAtIndex: invalid index" );
3342 const USHORT nLineCount = GetLineCount( nPara );
3343 if (nIndex == pNode->Len())
3344 nLineNo = nLineCount > 0 ? nLineCount - 1 : 0;
3345 else if (bValidIndex) // nIndex < pNode->Len()
3347 USHORT nStart = USHRT_MAX, nEnd = USHRT_MAX;
3348 for (USHORT i = 0; i < nLineCount && nLineNo == 0xFFFF; ++i)
3350 GetLineBoundaries( nStart, nEnd, nPara, i );
3351 if (nStart <= nIndex && nIndex < nEnd)
3352 nLineNo = i;
3356 return nLineNo;
3359 USHORT ImpEditEngine::GetLineHeight( USHORT nParagraph, USHORT nLine )
3361 DBG_ASSERT( nParagraph < GetParaPortions().Count(), "GetLineCount: Out of range" );
3362 ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
3363 DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetLineHeight" );
3364 if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) )
3366 EditLine* pLine = pPPortion->GetLines().GetObject( nLine );
3367 DBG_ASSERT( pLine, "Zeile nicht gefunden: GetLineHeight" );
3368 return pLine->GetHeight();
3371 return 0xFFFF;
3374 sal_uInt32 ImpEditEngine::GetParaHeight( USHORT nParagraph )
3376 sal_uInt32 nHeight = 0;
3378 ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
3379 DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetParaHeight" );
3381 if ( pPPortion )
3382 nHeight = pPPortion->GetHeight();
3384 return nHeight;
3387 void ImpEditEngine::UpdateSelections()
3389 USHORT nInvNodes = aDeletedNodes.Count();
3391 // Pruefen, ob eine der Selektionen auf einem geloeschten Node steht...
3392 // Wenn der Node gueltig ist, muss noch der Index geprueft werden!
3393 for ( USHORT nView = 0; nView < aEditViews.Count(); nView++ )
3395 EditView* pView = aEditViews.GetObject(nView);
3396 DBG_CHKOBJ( pView, EditView, 0 );
3397 EditSelection aCurSel( pView->pImpEditView->GetEditSelection() );
3398 BOOL bChanged = FALSE;
3399 for ( USHORT n = 0; n < nInvNodes; n++ )
3401 DeletedNodeInfo* pInf = aDeletedNodes.GetObject( n );
3402 if ( ( ( ULONG )(aCurSel.Min().GetNode()) == pInf->GetInvalidAdress() ) ||
3403 ( ( ULONG )(aCurSel.Max().GetNode()) == pInf->GetInvalidAdress() ) )
3405 // ParaPortions verwenden, da jetzt auch versteckte
3406 // Absaetze beruecksichtigt werden muessen!
3407 USHORT nPara = pInf->GetPosition();
3408 ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nPara );
3409 if ( !pPPortion ) // letzter Absatz
3411 nPara = GetParaPortions().Count()-1;
3412 pPPortion = GetParaPortions().GetObject( nPara );
3414 DBG_ASSERT( pPPortion, "Leeres Document in UpdateSelections ?" );
3415 // Nicht aus einem verstecktem Absatz landen:
3416 USHORT nCurPara = nPara;
3417 USHORT nLastPara = GetParaPortions().Count()-1;
3418 while ( nPara <= nLastPara && !GetParaPortions()[nPara]->IsVisible() )
3419 nPara++;
3420 if ( nPara > nLastPara ) // dann eben rueckwaerts...
3422 nPara = nCurPara;
3423 while ( nPara && !GetParaPortions()[nPara]->IsVisible() )
3424 nPara--;
3426 DBG_ASSERT( GetParaPortions()[nPara]->IsVisible(), "Keinen sichtbaren Absatz gefunden: UpdateSelections" );
3428 ParaPortion* pParaPortion = GetParaPortions()[nPara];
3429 EditSelection aTmpSelection( EditPaM( pParaPortion->GetNode(), 0 ) );
3430 pView->pImpEditView->SetEditSelection( aTmpSelection );
3431 bChanged=TRUE;
3432 break; // for-Schleife
3435 if ( !bChanged )
3437 // Index prueffen, falls Node geschrumpft.
3438 if ( aCurSel.Min().GetIndex() > aCurSel.Min().GetNode()->Len() )
3440 aCurSel.Min().GetIndex() = aCurSel.Min().GetNode()->Len();
3441 pView->pImpEditView->SetEditSelection( aCurSel );
3443 if ( aCurSel.Max().GetIndex() > aCurSel.Max().GetNode()->Len() )
3445 aCurSel.Max().GetIndex() = aCurSel.Max().GetNode()->Len();
3446 pView->pImpEditView->SetEditSelection( aCurSel );
3451 // Loeschen...
3452 for ( USHORT n = 0; n < nInvNodes; n++ )
3454 DeletedNodeInfo* pInf = aDeletedNodes.GetObject( n );
3455 delete pInf;
3457 aDeletedNodes.Remove( 0, aDeletedNodes.Count() );
3460 EditSelection ImpEditEngine::ConvertSelection( USHORT nStartPara, USHORT nStartPos,
3461 USHORT nEndPara, USHORT nEndPos ) const
3463 EditSelection aNewSelection;
3465 // Start...
3466 ContentNode* pNode = aEditDoc.SaveGetObject( nStartPara );
3467 USHORT nIndex = nStartPos;
3468 if ( !pNode )
3470 pNode = aEditDoc[ aEditDoc.Count()-1 ];
3471 nIndex = pNode->Len();
3473 else if ( nIndex > pNode->Len() )
3474 nIndex = pNode->Len();
3476 aNewSelection.Min().SetNode( pNode );
3477 aNewSelection.Min().SetIndex( nIndex );
3479 // End...
3480 pNode = aEditDoc.SaveGetObject( nEndPara );
3481 nIndex = nEndPos;
3482 if ( !pNode )
3484 pNode = aEditDoc[ aEditDoc.Count()-1 ];
3485 nIndex = pNode->Len();
3487 else if ( nIndex > pNode->Len() )
3488 nIndex = pNode->Len();
3490 aNewSelection.Max().SetNode( pNode );
3491 aNewSelection.Max().SetIndex( nIndex );
3493 return aNewSelection;
3496 EditSelection ImpEditEngine::MatchGroup( const EditSelection& rSel )
3498 EditSelection aMatchSel;
3499 EditSelection aTmpSel( rSel );
3500 aTmpSel.Adjust( GetEditDoc() );
3501 if ( ( aTmpSel.Min().GetNode() != aTmpSel.Max().GetNode() ) ||
3502 ( ( aTmpSel.Max().GetIndex() - aTmpSel.Min().GetIndex() ) > 1 ) )
3504 return aMatchSel;
3507 USHORT nPos = aTmpSel.Min().GetIndex();
3508 ContentNode* pNode = aTmpSel.Min().GetNode();
3509 if ( nPos >= pNode->Len() )
3510 return aMatchSel;
3512 USHORT nMatchChar = aGroupChars.Search( pNode->GetChar( nPos ) );
3513 if ( nMatchChar != STRING_NOTFOUND )
3515 USHORT nNode = aEditDoc.GetPos( pNode );
3516 if ( ( nMatchChar % 2 ) == 0 )
3518 // Vorwaerts suchen...
3519 xub_Unicode nSC = aGroupChars.GetChar( nMatchChar );
3520 DBG_ASSERT( aGroupChars.Len() > (nMatchChar+1), "Ungueltige Gruppe von MatchChars!" );
3521 xub_Unicode nEC = aGroupChars.GetChar( nMatchChar+1 );
3523 USHORT nCur = aTmpSel.Min().GetIndex()+1;
3524 USHORT nLevel = 1;
3525 while ( pNode && nLevel )
3527 XubString& rStr = *pNode;
3528 while ( nCur < rStr.Len() )
3530 if ( rStr.GetChar( nCur ) == nSC )
3531 nLevel++;
3532 else if ( rStr.GetChar( nCur ) == nEC )
3534 nLevel--;
3535 if ( !nLevel )
3536 break; // while nCur...
3538 nCur++;
3541 if ( nLevel )
3543 nNode++;
3544 pNode = nNode < aEditDoc.Count() ? aEditDoc.GetObject( nNode ) : 0;
3545 nCur = 0;
3548 if ( nLevel == 0 ) // gefunden
3550 aMatchSel.Min() = aTmpSel.Min();
3551 aMatchSel.Max() = EditPaM( pNode, nCur+1 );
3554 else
3556 // Rueckwaerts suchen...
3557 xub_Unicode nEC = aGroupChars.GetChar( nMatchChar );
3558 xub_Unicode nSC = aGroupChars.GetChar( nMatchChar-1 );
3560 USHORT nCur = aTmpSel.Min().GetIndex()-1;
3561 USHORT nLevel = 1;
3562 while ( pNode && nLevel )
3564 if ( pNode->Len() )
3566 XubString& rStr = *pNode;
3567 while ( nCur )
3569 if ( rStr.GetChar( nCur ) == nSC )
3571 nLevel--;
3572 if ( !nLevel )
3573 break; // while nCur...
3575 else if ( rStr.GetChar( nCur ) == nEC )
3576 nLevel++;
3578 nCur--;
3582 if ( nLevel )
3584 pNode = nNode ? aEditDoc.GetObject( --nNode ) : 0;
3585 if ( pNode )
3586 nCur = pNode->Len()-1; // egal ob negativ, weil if Len()
3590 if ( nLevel == 0 ) // gefunden
3592 aMatchSel.Min() = aTmpSel.Min();
3593 aMatchSel.Min().GetIndex()++; // hinter das Zeichen
3594 aMatchSel.Max() = EditPaM( pNode, nCur );
3598 return aMatchSel;
3601 void ImpEditEngine::StopSelectionMode()
3603 if ( ( IsInSelectionMode() || aSelEngine.IsInSelection() ) && pActiveView )
3605 pActiveView->pImpEditView->DrawSelection(); // Wegzeichnen...
3606 EditSelection aSel( pActiveView->pImpEditView->GetEditSelection() );
3607 aSel.Min() = aSel.Max();
3608 pActiveView->pImpEditView->SetEditSelection( aSel );
3609 pActiveView->ShowCursor();
3610 aSelEngine.Reset();
3611 bInSelection = FALSE;
3615 void ImpEditEngine::SetActiveView( EditView* pView )
3617 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3618 // Eigentlich waere jetzt ein bHasVisSel und HideSelection notwendig !!!
3620 if ( pView == pActiveView )
3621 return;
3623 if ( pActiveView && pActiveView->HasSelection() )
3624 pActiveView->pImpEditView->DrawSelection(); // Wegzeichnen...
3626 pActiveView = pView;
3628 if ( pActiveView && pActiveView->HasSelection() )
3629 pActiveView->pImpEditView->DrawSelection(); // Wegzeichnen...
3631 // NN: Quick fix for #78668#:
3632 // When editing of a cell in Calc is ended, the edit engine is not deleted,
3633 // only the edit views are removed. If mpIMEInfos is still set in that case,
3634 // mpIMEInfos->aPos points to an invalid selection.
3635 // -> reset mpIMEInfos now
3636 // (probably something like this is necessary whenever the content is modified
3637 // from the outside)
3639 if ( !pView && mpIMEInfos )
3641 delete mpIMEInfos;
3642 mpIMEInfos = NULL;
3646 uno::Reference< datatransfer::XTransferable > ImpEditEngine::CreateTransferable( const EditSelection& rSelection ) const
3648 #ifndef SVX_LIGHT
3649 EditSelection aSelection( rSelection );
3650 aSelection.Adjust( GetEditDoc() );
3652 EditDataObject* pDataObj = new EditDataObject;
3653 uno::Reference< datatransfer::XTransferable > xDataObj;
3654 xDataObj = pDataObj;
3656 XubString aText( GetSelected( aSelection ) );
3657 aText.ConvertLineEnd(); // Systemspezifisch
3658 pDataObj->GetString() = aText;
3660 SvxFontItem::EnableStoreUnicodeNames( TRUE );
3661 WriteBin( pDataObj->GetStream(), aSelection, TRUE );
3662 pDataObj->GetStream().Seek( 0 );
3663 SvxFontItem::EnableStoreUnicodeNames( FALSE );
3665 ((ImpEditEngine*)this)->WriteRTF( pDataObj->GetRTFStream(), aSelection );
3666 pDataObj->GetRTFStream().Seek( 0 );
3668 if ( ( aSelection.Min().GetNode() == aSelection.Max().GetNode() )
3669 && ( aSelection.Max().GetIndex() == (aSelection.Min().GetIndex()+1) ) )
3671 const EditCharAttrib* pAttr = aSelection.Min().GetNode()->GetCharAttribs().
3672 FindFeature( aSelection.Min().GetIndex() );
3673 if ( pAttr &&
3674 ( pAttr->GetStart() == aSelection.Min().GetIndex() ) &&
3675 ( pAttr->Which() == EE_FEATURE_FIELD ) )
3677 const SvxFieldItem* pField = (const SvxFieldItem*)pAttr->GetItem();
3678 const SvxFieldData* pFld = pField->GetField();
3679 if ( pFld && pFld->ISA( SvxURLField ) )
3681 // Office-Bookmark
3682 String aURL( ((const SvxURLField*)pFld)->GetURL() );
3683 String aTxt( ((const SvxURLField*)pFld)->GetRepresentation() );
3684 pDataObj->GetURL() = aURL;
3689 return xDataObj;
3690 #else
3691 return uno::Reference< datatransfer::XTransferable >();
3692 #endif
3695 EditSelection ImpEditEngine::InsertText( uno::Reference< datatransfer::XTransferable >& rxDataObj, const String& rBaseURL, const EditPaM& rPaM, BOOL bUseSpecial )
3697 EditSelection aNewSelection( rPaM );
3699 if ( rxDataObj.is() )
3701 datatransfer::DataFlavor aFlavor;
3702 BOOL bDone = FALSE;
3704 if ( bUseSpecial )
3706 // BIN
3707 SotExchange::GetFormatDataFlavor( SOT_FORMATSTR_ID_EDITENGINE, aFlavor );
3708 if ( rxDataObj->isDataFlavorSupported( aFlavor ) )
3712 uno::Any aData = rxDataObj->getTransferData( aFlavor );
3713 uno::Sequence< sal_Int8 > aSeq;
3714 aData >>= aSeq;
3716 SvMemoryStream aBinStream( aSeq.getArray(), aSeq.getLength(), STREAM_READ );
3717 aNewSelection = Read( aBinStream, rBaseURL, EE_FORMAT_BIN, rPaM );
3719 bDone = TRUE;
3721 catch( const ::com::sun::star::uno::Exception& )
3726 if ( !bDone )
3728 // Bookmark
3730 String aURL = ...;
3731 String aTxt = ...;
3732 // Feld nur einfuegen, wenn Factory vorhanden.
3733 if ( ITEMDATA() && ITEMDATA()->GetClassManager().Get( SVX_URLFIELD ) )
3735 SvxFieldItem aField( SvxURLField( aURL, aTxt, SVXURLFORMAT_URL ), EE_FEATURE_FIELD );
3736 aNewSelection = InsertField( aPaM, aField );
3737 UpdateFields();
3739 else
3740 aNewSelection = ImpInsertText( aPaM, aURL );
3744 if ( !bDone )
3746 // RTF
3747 SotExchange::GetFormatDataFlavor( SOT_FORMAT_RTF, aFlavor );
3748 if ( rxDataObj->isDataFlavorSupported( aFlavor ) )
3752 uno::Any aData = rxDataObj->getTransferData( aFlavor );
3753 uno::Sequence< sal_Int8 > aSeq;
3754 aData >>= aSeq;
3756 SvMemoryStream aRTFStream( aSeq.getArray(), aSeq.getLength(), STREAM_READ );
3757 aNewSelection = Read( aRTFStream, rBaseURL, EE_FORMAT_RTF, rPaM );
3759 bDone = TRUE;
3761 catch( const ::com::sun::star::uno::Exception& )
3766 if ( !bDone )
3768 // XML ?
3769 // Currently, there is nothing like "The" XML format, StarOffice doesn't offer plain XML in Clipboard...
3772 if ( !bDone )
3774 SotExchange::GetFormatDataFlavor( SOT_FORMAT_STRING, aFlavor );
3775 if ( rxDataObj->isDataFlavorSupported( aFlavor ) )
3779 uno::Any aData = rxDataObj->getTransferData( aFlavor );
3780 ::rtl::OUString aText;
3781 aData >>= aText;
3782 aNewSelection = ImpInsertText( rPaM, aText );
3783 bDone = TRUE;
3785 catch( ... )
3787 ; // #i9286# can happen, even if isDataFlavorSupported returns true...
3793 return aNewSelection;
3796 Range ImpEditEngine::GetInvalidYOffsets( ParaPortion* pPortion )
3798 Range aRange( 0, 0 );
3800 if ( pPortion->IsVisible() )
3802 const SvxULSpaceItem& rULSpace = (const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
3803 const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
3804 USHORT nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
3805 ? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
3807 // erst von vorne...
3808 USHORT nFirstInvalid = 0xFFFF;
3809 USHORT nLine;
3810 for ( nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
3812 EditLine* pL = pPortion->GetLines().GetObject( nLine );
3813 if ( pL->IsInvalid() )
3815 nFirstInvalid = nLine;
3816 break;
3818 if ( nLine && !aStatus.IsOutliner() ) // nicht die erste Zeile
3819 aRange.Min() += nSBL;
3820 aRange.Min() += pL->GetHeight();
3822 DBG_ASSERT( nFirstInvalid != 0xFFFF, "Keine ungueltige Zeile gefunden in GetInvalidYOffset(1)" );
3825 // Abgleichen und weiter...
3826 aRange.Max() = aRange.Min();
3827 aRange.Max() += pPortion->GetFirstLineOffset();
3828 if ( nFirstInvalid != 0 ) // Nur wenn nicht die erste Zeile ungueltig
3829 aRange.Min() = aRange.Max();
3831 USHORT nLastInvalid = pPortion->GetLines().Count()-1;
3832 for ( nLine = nFirstInvalid; nLine < pPortion->GetLines().Count(); nLine++ )
3834 EditLine* pL = pPortion->GetLines().GetObject( nLine );
3835 if ( pL->IsValid() )
3837 nLastInvalid = nLine;
3838 break;
3841 if ( nLine && !aStatus.IsOutliner() )
3842 aRange.Max() += nSBL;
3843 aRange.Max() += pL->GetHeight();
3846 // MT 07/00 SBL kann jetzt kleiner 100% sein => ggf. die Zeile davor neu ausgeben.
3847 if( ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP ) && rLSItem.GetPropLineSpace() &&
3848 ( rLSItem.GetPropLineSpace() < 100 ) )
3850 EditLine* pL = pPortion->GetLines().GetObject( nFirstInvalid );
3851 long n = pL->GetTxtHeight() * ( 100 - rLSItem.GetPropLineSpace() );
3852 n /= 100;
3853 aRange.Min() -= n;
3854 aRange.Max() += n;
3857 if ( ( nLastInvalid == pPortion->GetLines().Count()-1 ) && ( !aStatus.IsOutliner() ) )
3858 aRange.Max() += GetYValue( rULSpace.GetLower() );
3860 return aRange;
3863 EditPaM ImpEditEngine::GetPaM( ParaPortion* pPortion, Point aDocPos, BOOL bSmart )
3865 DBG_ASSERT( pPortion->IsVisible(), "Wozu GetPaM() bei einem unsichtbaren Absatz?" );
3866 DBG_ASSERT( IsFormatted(), "GetPaM: Nicht formatiert" );
3868 USHORT nCurIndex = 0;
3869 EditPaM aPaM;
3870 aPaM.SetNode( pPortion->GetNode() );
3872 const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
3873 USHORT nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
3874 ? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
3876 long nY = pPortion->GetFirstLineOffset();
3878 DBG_ASSERT( pPortion->GetLines().Count(), "Leere ParaPortion in GetPaM!" );
3880 EditLine* pLine = 0;
3881 for ( USHORT nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
3883 EditLine* pTmpLine = pPortion->GetLines().GetObject( nLine );
3884 nY += pTmpLine->GetHeight();
3885 if ( !aStatus.IsOutliner() )
3886 nY += nSBL;
3887 if ( nY > aDocPos.Y() ) // das war 'se
3889 pLine = pTmpLine;
3890 break; // richtige Y-Position intressiert nicht
3893 nCurIndex = nCurIndex + pTmpLine->GetLen();
3896 if ( !pLine ) // darf nur im Bereich von SA passieren!
3898 #ifdef DBG_UTIL
3899 const SvxULSpaceItem& rULSpace =(const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
3900 DBG_ASSERT( nY+GetYValue( rULSpace.GetLower() ) >= aDocPos.Y() , "Index in keiner Zeile, GetPaM ?" );
3901 #endif
3902 aPaM.SetIndex( pPortion->GetNode()->Len() );
3903 return aPaM;
3906 // Wenn Zeile gefunden, nur noch X-Position => Index
3907 nCurIndex = GetChar( pPortion, pLine, aDocPos.X(), bSmart );
3908 aPaM.SetIndex( nCurIndex );
3910 if ( nCurIndex && ( nCurIndex == pLine->GetEnd() ) &&
3911 ( pLine != pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1) ) )
3913 aPaM = CursorLeft( aPaM, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL );
3916 return aPaM;
3919 USHORT ImpEditEngine::GetChar( ParaPortion* pParaPortion, EditLine* pLine, long nXPos, BOOL bSmart )
3921 DBG_ASSERT( pLine, "Keine Zeile erhalten: GetChar" );
3923 USHORT nChar = 0xFFFF;
3924 USHORT nCurIndex = pLine->GetStart();
3927 // Search best matching portion with GetPortionXOffset()
3928 for ( USHORT i = pLine->GetStartPortion(); i <= pLine->GetEndPortion(); i++ )
3930 TextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( i );
3931 long nXLeft = GetPortionXOffset( pParaPortion, pLine, i );
3932 long nXRight = nXLeft + pPortion->GetSize().Width();
3933 if ( ( nXLeft <= nXPos ) && ( nXRight >= nXPos ) )
3935 nChar = nCurIndex;
3937 // Search within Portion...
3939 // Don't search within special portions...
3940 if ( pPortion->GetKind() != PORTIONKIND_TEXT )
3942 // ...but check on which side
3943 if ( bSmart )
3945 long nLeftDiff = nXPos-nXLeft;
3946 long nRightDiff = nXRight-nXPos;
3947 if ( nRightDiff < nLeftDiff )
3948 nChar++;
3951 else
3953 USHORT nMax = pPortion->GetLen();
3954 USHORT nOffset = 0xFFFF;
3955 USHORT nTmpCurIndex = nChar - pLine->GetStart();
3957 long nXInPortion = nXPos - nXLeft;
3958 if ( pPortion->IsRightToLeft() )
3959 nXInPortion = nXRight - nXPos;
3961 // Search in Array...
3962 for ( USHORT x = 0; x < nMax; x++ )
3964 long nTmpPosMax = pLine->GetCharPosArray().GetObject( nTmpCurIndex+x );
3965 if ( nTmpPosMax > nXInPortion )
3967 // pruefen, ob dieser oder der davor...
3968 long nTmpPosMin = x ? pLine->GetCharPosArray().GetObject( nTmpCurIndex+x-1 ) : 0;
3969 long nDiffLeft = nXInPortion - nTmpPosMin;
3970 long nDiffRight = nTmpPosMax - nXInPortion;
3971 DBG_ASSERT( nDiffLeft >= 0, "DiffLeft negativ" );
3972 DBG_ASSERT( nDiffRight >= 0, "DiffRight negativ" );
3973 nOffset = ( bSmart && ( nDiffRight < nDiffLeft ) ) ? x+1 : x;
3974 // I18N: If there are character position with the length of 0,
3975 // they belong to the same character, we can not use this position as an index.
3976 // Skip all 0-positions, cheaper than using XBreakIterator:
3977 if ( nOffset < nMax )
3979 const long nX = pLine->GetCharPosArray().GetObject(nOffset);
3980 while ( ( (nOffset+1) < nMax ) && ( pLine->GetCharPosArray().GetObject(nOffset+1) == nX ) )
3981 nOffset++;
3983 break;
3987 // Bei Verwendung des CharPosArray duerfte es keine Ungenauigkeiten geben!
3988 // Vielleicht bei Kerning ?
3989 // 0xFFF passiert z.B. bei Outline-Font, wenn ganz hinten.
3990 if ( nOffset == 0xFFFF )
3991 nOffset = nMax;
3993 DBG_ASSERT( nOffset <= nMax, "nOffset > nMax" );
3995 nChar = nChar + nOffset;
3997 // Check if index is within a cell:
3998 if ( nChar && ( nChar < pParaPortion->GetNode()->Len() ) )
4000 EditPaM aPaM( pParaPortion->GetNode(), nChar+1 );
4001 USHORT nScriptType = GetScriptType( aPaM );
4002 if ( nScriptType == i18n::ScriptType::COMPLEX )
4004 uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
4005 sal_Int32 nCount = 1;
4006 lang::Locale aLocale = GetLocale( aPaM );
4007 USHORT nRight = (USHORT)_xBI->nextCharacters( *pParaPortion->GetNode(), nChar, aLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount );
4008 USHORT nLeft = (USHORT)_xBI->previousCharacters( *pParaPortion->GetNode(), nRight, aLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount );
4009 if ( ( nLeft != nChar ) && ( nRight != nChar ) )
4011 nChar = ( Abs( nRight - nChar ) < Abs( nLeft - nChar ) ) ? nRight : nLeft;
4018 nCurIndex = nCurIndex + pPortion->GetLen();
4021 if ( nChar == 0xFFFF )
4023 nChar = ( nXPos <= pLine->GetStartPosX() ) ? pLine->GetStart() : pLine->GetEnd();
4026 return nChar;
4029 Range ImpEditEngine::GetLineXPosStartEnd( ParaPortion* pParaPortion, EditLine* pLine )
4031 Range aLineXPosStartEnd;
4033 USHORT nPara = GetEditDoc().GetPos( pParaPortion->GetNode() );
4034 if ( !IsRightToLeft( nPara ) )
4036 aLineXPosStartEnd.Min() = pLine->GetStartPosX();
4037 aLineXPosStartEnd.Max() = pLine->GetStartPosX() + pLine->GetTextWidth();
4039 else
4041 aLineXPosStartEnd.Min() = GetPaperSize().Width() - ( pLine->GetStartPosX() + pLine->GetTextWidth() );
4042 aLineXPosStartEnd.Max() = GetPaperSize().Width() - pLine->GetStartPosX();
4046 return aLineXPosStartEnd;
4049 long ImpEditEngine::GetPortionXOffset( ParaPortion* pParaPortion, EditLine* pLine, USHORT nTextPortion )
4051 long nX = pLine->GetStartPosX();
4053 for ( USHORT i = pLine->GetStartPortion(); i < nTextPortion; i++ )
4055 TextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( i );
4056 switch ( pPortion->GetKind() )
4058 case PORTIONKIND_FIELD:
4059 case PORTIONKIND_TEXT:
4060 case PORTIONKIND_HYPHENATOR:
4061 case PORTIONKIND_TAB:
4062 // case PORTIONKIND_EXTRASPACE:
4064 nX += pPortion->GetSize().Width();
4066 break;
4070 USHORT nPara = GetEditDoc().GetPos( pParaPortion->GetNode() );
4071 BOOL bR2LPara = IsRightToLeft( nPara );
4073 TextPortion* pDestPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
4074 if ( pDestPortion->GetKind() != PORTIONKIND_TAB )
4076 if ( !bR2LPara && pDestPortion->GetRightToLeft() )
4078 // Portions behind must be added, visual before this portion
4079 sal_uInt16 nTmpPortion = nTextPortion+1;
4080 while ( nTmpPortion <= pLine->GetEndPortion() )
4082 TextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
4083 if ( pNextTextPortion->GetRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) )
4084 nX += pNextTextPortion->GetSize().Width();
4085 else
4086 break;
4087 nTmpPortion++;
4089 // Portions before must be removed, visual behind this portion
4090 nTmpPortion = nTextPortion;
4091 while ( nTmpPortion > pLine->GetStartPortion() )
4093 --nTmpPortion;
4094 TextPortion* pPrevTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
4095 if ( pPrevTextPortion->GetRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) )
4096 nX -= pPrevTextPortion->GetSize().Width();
4097 else
4098 break;
4101 else if ( bR2LPara && !pDestPortion->IsRightToLeft() )
4103 // Portions behind must be ermoved, visual behind this portion
4104 sal_uInt16 nTmpPortion = nTextPortion+1;
4105 while ( nTmpPortion <= pLine->GetEndPortion() )
4107 TextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
4108 if ( !pNextTextPortion->IsRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) )
4109 nX += pNextTextPortion->GetSize().Width();
4110 else
4111 break;
4112 nTmpPortion++;
4114 // Portions before must be added, visual before this portion
4115 nTmpPortion = nTextPortion;
4116 while ( nTmpPortion > pLine->GetStartPortion() )
4118 --nTmpPortion;
4119 TextPortion* pPrevTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
4120 if ( !pPrevTextPortion->IsRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) )
4121 nX -= pPrevTextPortion->GetSize().Width();
4122 else
4123 break;
4127 if ( bR2LPara )
4129 // Switch X postions...
4130 DBG_ASSERT( GetTextRanger() || GetPaperSize().Width(), "GetPortionXOffset - paper size?!" );
4131 DBG_ASSERT( GetTextRanger() || (nX <= GetPaperSize().Width()), "GetPortionXOffset - position out of paper size!" );
4132 nX = GetPaperSize().Width() - nX;
4133 nX -= pDestPortion->GetSize().Width();
4136 return nX;
4139 long ImpEditEngine::GetXPos( ParaPortion* pParaPortion, EditLine* pLine, USHORT nIndex, BOOL bPreferPortionStart )
4141 DBG_ASSERT( pLine, "Keine Zeile erhalten: GetXPos" );
4142 DBG_ASSERT( ( nIndex >= pLine->GetStart() ) && ( nIndex <= pLine->GetEnd() ) , "GetXPos muss richtig gerufen werden!" );
4144 BOOL bDoPreferPortionStart = bPreferPortionStart;
4145 // Assure that the portion belongs to this line:
4146 if ( nIndex == pLine->GetStart() )
4147 bDoPreferPortionStart = TRUE;
4148 else if ( nIndex == pLine->GetEnd() )
4149 bDoPreferPortionStart = FALSE;
4151 USHORT nTextPortionStart = 0;
4152 USHORT nTextPortion = pParaPortion->GetTextPortions().FindPortion( nIndex, nTextPortionStart, bDoPreferPortionStart );
4154 DBG_ASSERT( ( nTextPortion >= pLine->GetStartPortion() ) && ( nTextPortion <= pLine->GetEndPortion() ), "GetXPos: Portion not in current line! " );
4156 TextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
4158 long nX = GetPortionXOffset( pParaPortion, pLine, nTextPortion );
4160 // calc text width, portion size may include CJK/CTL spacing...
4161 // But the array migh not be init yet, if using text ranger this method is called within CreateLines()...
4162 long nPortionTextWidth = pPortion->GetSize().Width();
4163 if ( ( pPortion->GetKind() == PORTIONKIND_TEXT ) && pPortion->GetLen() && !GetTextRanger() )
4164 nPortionTextWidth = pLine->GetCharPosArray().GetObject( nTextPortionStart + pPortion->GetLen() - 1 - pLine->GetStart() );
4166 if ( nTextPortionStart != nIndex )
4168 // Search within portion...
4169 if ( nIndex == ( nTextPortionStart + pPortion->GetLen() ) )
4171 // End of Portion
4172 if ( pPortion->GetKind() == PORTIONKIND_TAB )
4174 if ( (nTextPortion+1) < pParaPortion->GetTextPortions().Count() )
4176 TextPortion* pNextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion+1 );
4177 if ( pNextPortion->GetKind() != PORTIONKIND_TAB )
4179 // DBG_ASSERT( !bPreferPortionStart, "GetXPos - How can we this tab portion here???" );
4180 // #109879# We loop if nIndex == pLine->GetEnd, because bPreferPortionStart will be reset
4181 if ( !bPreferPortionStart )
4182 nX = GetXPos( pParaPortion, pLine, nIndex, TRUE );
4183 else if ( !IsRightToLeft( GetEditDoc().GetPos( pParaPortion->GetNode() ) ) )
4184 nX += nPortionTextWidth;
4187 else if ( !IsRightToLeft( GetEditDoc().GetPos( pParaPortion->GetNode() ) ) )
4189 nX += nPortionTextWidth;
4192 else if ( !pPortion->IsRightToLeft() )
4194 nX += nPortionTextWidth;
4197 else if ( pPortion->GetKind() == PORTIONKIND_TEXT )
4199 DBG_ASSERT( nIndex != pLine->GetStart(), "Strange behavior in new GetXPos()" );
4200 DBG_ASSERT( pLine && pLine->GetCharPosArray().Count(), "svx::ImpEditEngine::GetXPos(), portion in an empty line?" );
4202 if( pLine->GetCharPosArray().Count() )
4204 USHORT nPos = nIndex - 1 - pLine->GetStart();
4205 if( nPos >= pLine->GetCharPosArray().Count() )
4207 nPos = pLine->GetCharPosArray().Count()-1;
4208 DBG_ERROR("svx::ImpEditEngine::GetXPos(), index out of range!");
4211 long nPosInPortion = pLine->GetCharPosArray().GetObject( nPos );
4213 if ( !pPortion->IsRightToLeft() )
4215 nX += nPosInPortion;
4217 else
4219 nX += nPortionTextWidth - nPosInPortion;
4222 if ( pPortion->GetExtraInfos() && pPortion->GetExtraInfos()->bCompressed )
4224 nX += pPortion->GetExtraInfos()->nPortionOffsetX;
4225 if ( pPortion->GetExtraInfos()->nAsianCompressionTypes & CHAR_PUNCTUATIONRIGHT )
4227 BYTE nType = GetCharTypeForCompression( pParaPortion->GetNode()->GetChar( nIndex ) );
4228 if ( nType == CHAR_PUNCTUATIONRIGHT )
4230 USHORT n = nIndex - nTextPortionStart;
4231 const sal_Int32* pDXArray = pLine->GetCharPosArray().GetData()+( nTextPortionStart-pLine->GetStart() );
4232 sal_Int32 nCharWidth = ( ( (n+1) < pPortion->GetLen() ) ? pDXArray[n] : pPortion->GetSize().Width() )
4233 - ( n ? pDXArray[n-1] : 0 );
4234 if ( (n+1) < pPortion->GetLen() )
4236 // smaller, when char behind is CHAR_PUNCTUATIONRIGHT also
4237 nType = GetCharTypeForCompression( pParaPortion->GetNode()->GetChar( nIndex+1 ) );
4238 if ( nType == CHAR_PUNCTUATIONRIGHT )
4240 sal_Int32 nNextCharWidth = ( ( (n+2) < pPortion->GetLen() ) ? pDXArray[n+1] : pPortion->GetSize().Width() )
4241 - pDXArray[n];
4242 sal_Int32 nCompressed = nNextCharWidth/2;
4243 nCompressed *= pPortion->GetExtraInfos()->nMaxCompression100thPercent;
4244 nCompressed /= 10000;
4245 nCharWidth += nCompressed;
4248 else
4250 nCharWidth *= 2; // last char pos to portion end is only compressed size
4252 nX += nCharWidth/2; // 50% compression
4259 else // if ( nIndex == pLine->GetStart() )
4261 if ( pPortion->IsRightToLeft() )
4263 nX += nPortionTextWidth;
4267 return nX;
4270 void ImpEditEngine::CalcHeight( ParaPortion* pPortion )
4272 pPortion->nHeight = 0;
4273 pPortion->nFirstLineOffset = 0;
4275 if ( pPortion->IsVisible() )
4277 DBG_ASSERT( pPortion->GetLines().Count(), "Absatz ohne Zeilen in ParaPortion::CalcHeight" );
4278 for ( USHORT nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
4279 pPortion->nHeight += pPortion->GetLines().GetObject( nLine )->GetHeight();
4281 if ( !aStatus.IsOutliner() )
4283 const SvxULSpaceItem& rULItem = (const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
4284 const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
4285 USHORT nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX ) ? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
4287 if ( nSBL )
4289 if ( pPortion->GetLines().Count() > 1 )
4290 pPortion->nHeight += ( pPortion->GetLines().Count() - 1 ) * nSBL;
4291 if ( aStatus.ULSpaceSummation() )
4292 pPortion->nHeight += nSBL;
4295 USHORT nPortion = GetParaPortions().GetPos( pPortion );
4296 if ( nPortion || aStatus.ULSpaceFirstParagraph() )
4298 USHORT nUpper = GetYValue( rULItem.GetUpper() );
4299 pPortion->nHeight += nUpper;
4300 pPortion->nFirstLineOffset = nUpper;
4303 if ( ( nPortion != (GetParaPortions().Count()-1) ) )
4305 pPortion->nHeight += GetYValue( rULItem.GetLower() ); // nicht in letzter
4309 if ( nPortion && !aStatus.ULSpaceSummation() )
4311 ParaPortion* pPrev = GetParaPortions().SaveGetObject( nPortion-1 );
4312 const SvxULSpaceItem& rPrevULItem = (const SvxULSpaceItem&)pPrev->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
4313 const SvxLineSpacingItem& rPrevLSItem = (const SvxLineSpacingItem&)pPrev->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
4315 // Verhalten WinWord6/Writer3:
4316 // Bei einem proportionalen Zeilenabstand wird auch der Absatzabstand
4317 // manipuliert.
4318 // Nur Writer3: Nicht aufaddieren, sondern Mindestabstand.
4320 // Pruefen, ob Abstand durch LineSpacing > Upper:
4321 USHORT nExtraSpace = GetYValue( lcl_CalcExtraSpace( pPortion, rLSItem ) );
4322 if ( nExtraSpace > pPortion->nFirstLineOffset )
4324 // Absatz wird 'groesser':
4325 pPortion->nHeight += ( nExtraSpace - pPortion->nFirstLineOffset );
4326 pPortion->nFirstLineOffset = nExtraSpace;
4329 // nFirstLineOffset jetzt f(pNode) => jetzt f(pNode, pPrev) ermitteln:
4330 USHORT nPrevLower = GetYValue( rPrevULItem.GetLower() );
4332 // Dieser PrevLower steckt noch in der Hoehe der PrevPortion...
4333 if ( nPrevLower > pPortion->nFirstLineOffset )
4335 // Absatz wird 'kleiner':
4336 pPortion->nHeight -= pPortion->nFirstLineOffset;
4337 pPortion->nFirstLineOffset = 0;
4339 else if ( nPrevLower )
4341 // Absatz wird 'etwas kleiner':
4342 pPortion->nHeight -= nPrevLower;
4343 pPortion->nFirstLineOffset =
4344 pPortion->nFirstLineOffset - nPrevLower;
4347 // Finde ich zwar nicht so gut, aber Writer3-Feature:
4348 // Pruefen, ob Abstand durch LineSpacing > Lower:
4349 // Dieser Wert steckt nicht in der Hoehe der PrevPortion.
4350 if ( !pPrev->IsInvalid() )
4352 nExtraSpace = GetYValue( lcl_CalcExtraSpace( pPrev, rPrevLSItem ) );
4353 if ( nExtraSpace > nPrevLower )
4355 USHORT nMoreLower = nExtraSpace - nPrevLower;
4356 // Absatz wird 'groesser', 'waechst' nach unten:
4357 if ( nMoreLower > pPortion->nFirstLineOffset )
4359 pPortion->nHeight += ( nMoreLower - pPortion->nFirstLineOffset );
4360 pPortion->nFirstLineOffset = nMoreLower;
4369 Rectangle ImpEditEngine::GetEditCursor( ParaPortion* pPortion, USHORT nIndex, USHORT nFlags )
4371 DBG_ASSERT( pPortion->IsVisible(), "Wozu GetEditCursor() bei einem unsichtbaren Absatz?" );
4372 DBG_ASSERT( IsFormatted() || GetTextRanger(), "GetEditCursor: Nicht formatiert" );
4375 GETCRSR_ENDOFLINE: Wenn hinter dem letzten Zeichen einer umgebrochenen Zeile,
4376 am Ende der Zeile bleiben, nicht am Anfang der naechsten.
4377 Zweck: - END => wirklich hinter das letzte Zeichen
4378 - Selektion....
4381 long nY = pPortion->GetFirstLineOffset();
4383 const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
4384 USHORT nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
4385 ? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
4387 USHORT nCurIndex = 0;
4388 DBG_ASSERT( pPortion->GetLines().Count(), "Leere ParaPortion in GetEditCursor!" );
4389 EditLine* pLine = 0;
4390 BOOL bEOL = ( nFlags & GETCRSR_ENDOFLINE ) ? TRUE : FALSE;
4391 for ( USHORT nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
4393 EditLine* pTmpLine = pPortion->GetLines().GetObject( nLine );
4394 if ( ( pTmpLine->GetStart() == nIndex ) || ( pTmpLine->IsIn( nIndex, bEOL ) ) )
4396 pLine = pTmpLine;
4397 break;
4400 nCurIndex = nCurIndex + pTmpLine->GetLen();
4401 nY += pTmpLine->GetHeight();
4402 if ( !aStatus.IsOutliner() )
4403 nY += nSBL;
4405 if ( !pLine )
4407 // Cursor am Ende des Absatzes.
4408 DBG_ASSERT( nIndex == nCurIndex, "Index voll daneben in GetEditCursor!" );
4410 pLine = pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1 );
4411 nY -= pLine->GetHeight();
4412 if ( !aStatus.IsOutliner() )
4413 nY -= nSBL;
4414 nCurIndex = nCurIndex - pLine->GetLen();
4417 Rectangle aEditCursor;
4419 aEditCursor.Top() = nY;
4420 nY += pLine->GetHeight();
4421 aEditCursor.Bottom() = nY-1;
4423 // innerhalb der Zeile suchen...
4424 long nX;
4426 if ( ( nIndex == pLine->GetStart() ) && ( nFlags & GETCRSR_STARTOFLINE ) )
4428 Range aXRange = GetLineXPosStartEnd( pPortion, pLine );
4429 nX = !IsRightToLeft( GetEditDoc().GetPos( pPortion->GetNode() ) ) ? aXRange.Min() : aXRange.Max();
4431 else if ( ( nIndex == pLine->GetEnd() ) && ( nFlags & GETCRSR_ENDOFLINE ) )
4433 Range aXRange = GetLineXPosStartEnd( pPortion, pLine );
4434 nX = !IsRightToLeft( GetEditDoc().GetPos( pPortion->GetNode() ) ) ? aXRange.Max() : aXRange.Min();
4436 else
4438 nX = GetXPos( pPortion, pLine, nIndex, ( nFlags & GETCRSR_PREFERPORTIONSTART ) ? TRUE : FALSE );
4441 aEditCursor.Left() = aEditCursor.Right() = nX;
4443 if ( nFlags & GETCRSR_TXTONLY )
4444 aEditCursor.Top() = aEditCursor.Bottom() - pLine->GetTxtHeight() + 1;
4445 else
4446 aEditCursor.Top() = aEditCursor.Bottom() - Min( pLine->GetTxtHeight(), pLine->GetHeight() ) + 1;
4448 return aEditCursor;
4451 void ImpEditEngine::SetValidPaperSize( const Size& rNewSz )
4453 aPaperSize = rNewSz;
4455 long nMinWidth = aStatus.AutoPageWidth() ? aMinAutoPaperSize.Width() : 0;
4456 long nMaxWidth = aStatus.AutoPageWidth() ? aMaxAutoPaperSize.Width() : 0x7FFFFFFF;
4457 long nMinHeight = aStatus.AutoPageHeight() ? aMinAutoPaperSize.Height() : 0;
4458 long nMaxHeight = aStatus.AutoPageHeight() ? aMaxAutoPaperSize.Height() : 0x7FFFFFFF;
4460 // Minimale/Maximale Breite:
4461 if ( aPaperSize.Width() < nMinWidth )
4462 aPaperSize.Width() = nMinWidth;
4463 else if ( aPaperSize.Width() > nMaxWidth )
4464 aPaperSize.Width() = nMaxWidth;
4466 // Minimale/Maximale Hoehe:
4467 if ( aPaperSize.Height() < nMinHeight )
4468 aPaperSize.Height() = nMinHeight;
4469 else if ( aPaperSize.Height() > nMaxHeight )
4470 aPaperSize.Height() = nMaxHeight;
4473 void ImpEditEngine::IndentBlock( EditView* pEditView, BOOL bRight )
4475 ESelection aESel( CreateESel( pEditView->pImpEditView->GetEditSelection() ) );
4476 aESel.Adjust();
4478 // Nur wenn mehrere selektierte Absaetze...
4479 if ( aESel.nEndPara > aESel.nStartPara )
4481 ESelection aNewSel = aESel;
4482 aNewSel.nStartPos = 0;
4483 aNewSel.nEndPos = 0xFFFF;
4485 if ( aESel.nEndPos == 0 )
4487 aESel.nEndPara--; // dann diesen Absatz nicht...
4488 aNewSel.nEndPos = 0;
4491 pEditView->pImpEditView->DrawSelection();
4492 pEditView->pImpEditView->SetEditSelection(
4493 pEditView->pImpEditView->GetEditSelection().Max() );
4494 UndoActionStart( bRight ? EDITUNDO_INDENTBLOCK : EDITUNDO_UNINDENTBLOCK );
4496 for ( USHORT nPara = aESel.nStartPara; nPara <= aESel.nEndPara; nPara++ )
4498 ContentNode* pNode = GetEditDoc().GetObject( nPara );
4499 if ( bRight )
4501 // Tabs hinzufuegen
4502 EditPaM aPaM( pNode, 0 );
4503 InsertTab( aPaM );
4505 else
4507 // Tabs entfernen
4508 EditCharAttrib* pFeature = pNode->GetCharAttribs().FindFeature( 0 );
4509 if ( pFeature && ( pFeature->GetStart() == 0 ) &&
4510 ( pFeature->GetItem()->Which() == EE_FEATURE_TAB ) )
4512 EditPaM aStartPaM( pNode, 0 );
4513 EditPaM aEndPaM( pNode, 1 );
4514 ImpDeleteSelection( EditSelection( aStartPaM, aEndPaM ) );
4519 UndoActionEnd( bRight ? EDITUNDO_INDENTBLOCK : EDITUNDO_UNINDENTBLOCK );
4520 UpdateSelections();
4521 FormatAndUpdate( pEditView );
4523 ContentNode* pLastNode = GetEditDoc().GetObject( aNewSel.nEndPara );
4524 if ( pLastNode->Len() < aNewSel.nEndPos )
4525 aNewSel.nEndPos = pLastNode->Len();
4526 pEditView->pImpEditView->SetEditSelection( CreateSel( aNewSel ) );
4527 pEditView->pImpEditView->DrawSelection();
4528 pEditView->pImpEditView->ShowCursor( FALSE, TRUE );
4532 vos::ORef<SvxForbiddenCharactersTable> ImpEditEngine::GetForbiddenCharsTable( BOOL bGetInternal ) const
4534 vos::ORef<SvxForbiddenCharactersTable> xF = xForbiddenCharsTable;
4535 if ( !xF.isValid() && bGetInternal )
4536 xF = EE_DLL()->GetGlobalData()->GetForbiddenCharsTable();
4537 return xF;
4540 void ImpEditEngine::SetForbiddenCharsTable( vos::ORef<SvxForbiddenCharactersTable> xForbiddenChars )
4542 EE_DLL()->GetGlobalData()->SetForbiddenCharsTable( xForbiddenChars );
4545 svtools::ColorConfig& ImpEditEngine::GetColorConfig()
4547 if ( !pColorConfig )
4548 pColorConfig = new svtools::ColorConfig;
4550 return *pColorConfig;
4553 BOOL ImpEditEngine::IsVisualCursorTravelingEnabled()
4555 BOOL bVisualCursorTravaling = FALSE;
4557 if( !pCTLOptions )
4558 pCTLOptions = new SvtCTLOptions;
4560 if ( pCTLOptions->IsCTLFontEnabled() && ( pCTLOptions->GetCTLCursorMovement() == SvtCTLOptions::MOVEMENT_VISUAL ) )
4562 bVisualCursorTravaling = TRUE;
4565 return bVisualCursorTravaling;
4569 BOOL ImpEditEngine::DoVisualCursorTraveling( const ContentNode* )
4571 // Don't check if it's necessary, because we also need it when leaving the paragraph
4572 return IsVisualCursorTravelingEnabled();
4574 BOOL bDoVisualCursorTraveling = FALSE;
4576 if ( IsVisualCursorTravelingEnabled() && pNode->Len() )
4578 // Only necessary when RTL text in LTR para or LTR text in RTL para
4579 bDoVisualCursorTraveling = HasDifferentRTLLevels( pNode );
4582 return bDoVisualCursorTraveling;
4587 void ImpEditEngine::CallNotify( EENotify& rNotify )
4589 if ( !nBlockNotifications )
4591 GetNotifyHdl().Call( &rNotify );
4593 else
4595 EENotify* pNewNotify = new EENotify( rNotify );
4596 aNotifyCache.Insert( pNewNotify, aNotifyCache.Count() );
4600 void ImpEditEngine::EnterBlockNotifications()
4602 if( !nBlockNotifications )
4604 // #109864# Send out START notification immediately, to allow
4605 // external, non-queued events to be captured as well from
4606 // client side
4607 EENotify aNotify( EE_NOTIFY_BLOCKNOTIFICATION_START );
4608 aNotify.pEditEngine = GetEditEnginePtr();
4609 GetNotifyHdl().Call( &aNotify );
4612 nBlockNotifications++;
4615 void ImpEditEngine::LeaveBlockNotifications()
4617 DBG_ASSERT( nBlockNotifications, "LeaveBlockNotifications - Why?" );
4619 nBlockNotifications--;
4620 if ( !nBlockNotifications )
4622 // Call blocked notify events...
4623 while ( aNotifyCache.Count() )
4625 EENotify* pNotify = aNotifyCache[0];
4626 // Remove from list before calling, maybe we enter LeaveBlockNotifications while calling the handler...
4627 aNotifyCache.Remove( 0 );
4628 GetNotifyHdl().Call( pNotify );
4629 delete pNotify;
4632 EENotify aNotify( EE_NOTIFY_BLOCKNOTIFICATION_END );
4633 aNotify.pEditEngine = GetEditEnginePtr();
4634 GetNotifyHdl().Call( &aNotify );
4638 IMPL_LINK( ImpEditEngine, DocModified, void*, EMPTYARG )
4640 aModifyHdl.Call( NULL /*GetEditEnginePtr()*/ ); // NULL, because also used for Outliner
4641 return 0;