xref: /trunk/main/editeng/source/editeng/impedit2.cxx (revision 300d4866)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_editeng.hxx"
26 
27 #include <vcl/wrkwin.hxx>
28 #include <vcl/dialog.hxx>
29 #include <vcl/msgbox.hxx>
30 #include <vcl/svapp.hxx>
31 
32 #include <editeng/lspcitem.hxx>
33 #include <editeng/flditem.hxx>
34 #include <impedit.hxx>
35 #include <editeng/editeng.hxx>
36 #include <editeng/editview.hxx>
37 #include <editdbg.hxx>
38 #include <eerdll2.hxx>
39 #include <editeng/eerdll.hxx>
40 #include <edtspell.hxx>
41 #include <eeobj.hxx>
42 #include <editeng/txtrange.hxx>
43 #include <svl/urlbmk.hxx>
44 #include <svtools/colorcfg.hxx>
45 #include <svl/ctloptions.hxx>
46 #include <editeng/acorrcfg.hxx>
47 #include <editeng/fhgtitem.hxx>
48 #include <editeng/lrspitem.hxx>
49 #include <editeng/ulspitem.hxx>
50 #include <editeng/wghtitem.hxx>
51 #include <editeng/postitem.hxx>
52 #include <editeng/udlnitem.hxx>
53 #include <editeng/adjitem.hxx>
54 #include <editeng/scripttypeitem.hxx>
55 #include <editeng/frmdiritem.hxx>
56 #include <editeng/fontitem.hxx>
57 #include <vcl/cmdevt.h>
58 
59 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
60 #include <com/sun/star/i18n/WordType.hpp>
61 #include <com/sun/star/i18n/ScriptType.hpp>
62 #include <com/sun/star/lang/Locale.hpp>
63 #include <com/sun/star/text/CharacterCompressionType.hpp>
64 #include <com/sun/star/i18n/InputSequenceCheckMode.hpp>
65 
66 #include <comphelper/processfactory.hxx>
67 
68 #include <sot/formats.hxx>
69 
70 #include <unicode/ubidi.h>
71 
72 using namespace ::com::sun::star;
73 
lcl_CalcExtraSpace(ParaPortion *,const SvxLineSpacingItem & rLSItem)74 sal_uInt16 lcl_CalcExtraSpace( ParaPortion*, const SvxLineSpacingItem& rLSItem )
75 {
76 	sal_uInt16 nExtra = 0;
77 	/* if ( ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP )
78 			&& ( rLSItem.GetPropLineSpace() != 100 ) )
79 	{
80 		// sal_uLong nH = pPortion->GetNode()->GetCharAttribs().GetDefFont().GetSize().Height();
81 		sal_uLong nH = pPortion->GetLines().GetObject( 0 )->GetHeight();
82 		long n = nH * rLSItem.GetPropLineSpace();
83 		n /= 100;
84 		n -= nH;	// nur den Abstand
85 		if ( n > 0 )
86 			nExtra = (sal_uInt16)n;
87 	}
88 	else */
89 	if ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
90 	{
91 		nExtra = rLSItem.GetInterLineSpace();
92 	}
93 
94 	return nExtra;
95 }
96 
97 // ----------------------------------------------------------------------
98 //	class ImpEditEngine
99 //	----------------------------------------------------------------------
100 
ImpEditEngine(EditEngine * pEE,SfxItemPool * pItemPool)101 ImpEditEngine::ImpEditEngine( EditEngine* pEE, SfxItemPool* pItemPool ) :
102 	aPaperSize( 0x7FFFFFFF, 0x7FFFFFFF ),
103 	aMinAutoPaperSize( 0x0, 0x0 ),
104 	aMaxAutoPaperSize( 0x7FFFFFFF, 0x7FFFFFFF ),
105 	aEditDoc( pItemPool ),
106 	aWordDelimiters( RTL_CONSTASCII_USTRINGPARAM( "  .,;:-'`'?!_=\"{}()[]\0xFF" ) ),
107 	aGroupChars( RTL_CONSTASCII_USTRINGPARAM( "{}()[]" ) )
108 {
109 	pEditEngine 		= pEE;
110 	pRefDev 			= NULL;
111 	pVirtDev 			= NULL;
112 	pEmptyItemSet 		= NULL;
113 	pActiveView 		= NULL;
114 	pSpellInfo 			= NULL;
115     pConvInfo           = NULL;
116 	pTextObjectPool 	= NULL;
117 	mpIMEInfos			= NULL;
118 	pStylePool 			= NULL;
119 	pUndoManager 		= NULL;
120 	pUndoMarkSelection	= NULL;
121 	pTextRanger			= NULL;
122     pColorConfig        = NULL;
123     pCTLOptions         = NULL;
124 
125 	nCurTextHeight 		= 0;
126     nBlockNotifications = 0;
127 	nBigTextObjectStart	= 20;
128 
129 	nStretchX			= 100;
130 	nStretchY			= 100;
131 
132 	bInSelection 		= sal_False;
133 	bOwnerOfRefDev 		= sal_False;
134 	bDowning 			= sal_False;
135 	bIsInUndo 			= sal_False;
136 	bIsFormatting 		= sal_False;
137 	bFormatted			= sal_False;
138 	bUpdate 			= sal_True;
139 	bUpdateForAcc		= TRUE;
140     bUseAutoColor       = sal_True;
141     bForceAutoColor     = sal_False;
142     bAddExtLeading      = sal_False;
143 	bUndoEnabled 		= sal_True;
144 	bCallParaInsertedOrDeleted = sal_False;
145     bImpConvertFirstCall= sal_False;
146     bFirstWordCapitalization    = sal_True;
147 
148 	eDefLanguage		= LANGUAGE_DONTKNOW;
149 	maBackgroundColor	= COL_AUTO;
150 
151     nAsianCompressionMode = text::CharacterCompressionType::NONE;
152 	bKernAsianPunctuation = sal_False;
153 
154     eDefaultHorizontalTextDirection = EE_HTEXTDIR_DEFAULT;
155 
156 
157 	aStatus.GetControlWord() =	EE_CNTRL_USECHARATTRIBS | EE_CNTRL_DOIDLEFORMAT |
158 								EE_CNTRL_PASTESPECIAL | EE_CNTRL_UNDOATTRIBS |
159 								EE_CNTRL_ALLOWBIGOBJS | EE_CNTRL_RTFSTYLESHEETS |
160 								EE_CNTRL_FORMAT100;
161 
162 	aSelEngine.SetFunctionSet( &aSelFuncSet );
163 
164 	aStatusTimer.SetTimeout( 200 );
165 	aStatusTimer.SetTimeoutHdl( LINK( this, ImpEditEngine, StatusTimerHdl ) );
166 
167 	aIdleFormatter.SetTimeout( 5 );
168 	aIdleFormatter.SetTimeoutHdl( LINK( this, ImpEditEngine, IdleFormatHdl ) );
169 
170 	aOnlineSpellTimer.SetTimeout( 100 );
171 	aOnlineSpellTimer.SetTimeoutHdl( LINK( this, ImpEditEngine, OnlineSpellHdl ) );
172 
173 	pRefDev 			= EE_DLL()->GetGlobalData()->GetStdRefDevice();
174 
175 	// Ab hier wird schon auf Daten zugegriffen!
176 	SetRefDevice( pRefDev );
177 	InitDoc( sal_False );
178 
179 	bCallParaInsertedOrDeleted = sal_True;
180 
181     aEditDoc.SetModifyHdl( LINK( this, ImpEditEngine, DocModified ) );
182 
183 	mbLastTryMerge = sal_False;
184 }
185 
~ImpEditEngine()186 ImpEditEngine::~ImpEditEngine()
187 {
188 	aStatusTimer.Stop();
189 	aOnlineSpellTimer.Stop();
190 	aIdleFormatter.Stop();
191 
192 	// das Zerstoeren von Vorlagen kann sonst unnoetiges Formatieren ausloesen,
193 	// wenn eine Parent-Vorlage zerstoert wird.
194 	// Und das nach dem Zerstoeren der Daten!
195 	bDowning = sal_True;
196 	SetUpdateMode( sal_False );
197 
198 	delete pVirtDev;
199 	delete pEmptyItemSet;
200 	delete pUndoManager;
201 	delete pTextRanger;
202 	delete mpIMEInfos;
203     delete pColorConfig;
204     delete pCTLOptions;
205 	if ( bOwnerOfRefDev )
206 		delete pRefDev;
207     delete pSpellInfo;
208 }
209 
SetRefDevice(OutputDevice * pRef)210 void ImpEditEngine::SetRefDevice( OutputDevice* pRef )
211 {
212 	if ( bOwnerOfRefDev )
213 		delete pRefDev;
214 
215 	pRefDev = pRef;
216 	bOwnerOfRefDev = sal_False;
217 
218 	if ( !pRef )
219 		pRefDev = EE_DLL()->GetGlobalData()->GetStdRefDevice();
220 
221 	nOnePixelInRef = (sal_uInt16)pRefDev->PixelToLogic( Size( 1, 0 ) ).Width();
222 
223 	if ( IsFormatted() )
224 	{
225 		FormatFullDoc();
226 		UpdateViews( (EditView*) 0);
227 	}
228 }
229 
SetRefMapMode(const MapMode & rMapMode)230 void ImpEditEngine::SetRefMapMode( const MapMode& rMapMode )
231 {
232 	if ( GetRefDevice()->GetMapMode() == rMapMode )
233 		return;
234 
235 	// Wenn RefDev == GlobalRefDev => eigenes anlegen!
236 	if ( !bOwnerOfRefDev && ( pRefDev == EE_DLL()->GetGlobalData()->GetStdRefDevice() ) )
237 	{
238 		pRefDev = new VirtualDevice;
239 		pRefDev->SetMapMode( MAP_TWIP );
240 		SetRefDevice( pRefDev );
241 		bOwnerOfRefDev = sal_True;
242 	}
243 	pRefDev->SetMapMode( rMapMode );
244 	nOnePixelInRef = (sal_uInt16)pRefDev->PixelToLogic( Size( 1, 0 ) ).Width();
245 	if ( IsFormatted() )
246 	{
247 		FormatFullDoc();
248 		UpdateViews( (EditView*) 0);
249 	}
250 }
251 
InitDoc(sal_Bool bKeepParaAttribs)252 void ImpEditEngine::InitDoc( sal_Bool bKeepParaAttribs )
253 {
254 	sal_uInt32 nParas = aEditDoc.Count();
255 	for ( sal_uInt32 n = bKeepParaAttribs ? 1 : 0; n < nParas; n++ )
256 	{
257 		if ( aEditDoc[n]->GetStyleSheet() )
258 			EndListening( *aEditDoc[n]->GetStyleSheet(), sal_False );
259 	}
260 
261 	if ( bKeepParaAttribs )
262 		aEditDoc.RemoveText();
263 	else
264 		aEditDoc.Clear();
265 
266 	GetParaPortions().Reset();
267 
268 	ParaPortion* pIniPortion = new ParaPortion( aEditDoc[0] );
269 	GetParaPortions().Insert( pIniPortion, 0 );
270 
271 	bFormatted = sal_False;
272 
273 	if ( IsCallParaInsertedOrDeleted() )
274 	{
275 		GetEditEnginePtr()->ParagraphDeleted( EE_PARA_ALL );
276 		GetEditEnginePtr()->ParagraphInserted( 0 );
277 	}
278 
279 #ifndef SVX_LIGHT
280 	if ( GetStatus().DoOnlineSpelling() )
281 		aEditDoc.GetObject( 0 )->CreateWrongList();
282 #endif // !SVX_LIGHT
283 }
284 
DeleteSelected(EditSelection aSel)285 EditPaM ImpEditEngine::DeleteSelected( EditSelection aSel )
286 {
287 	EditPaM aPaM ( ImpDeleteSelection( aSel ) );
288 	return aPaM;
289 }
290 
GetSelected(const EditSelection & rSel,const LineEnd eEnd) const291 XubString ImpEditEngine::GetSelected( const EditSelection& rSel, const LineEnd eEnd  ) const
292 {
293 	XubString aText;
294 	if ( !rSel.HasRange() )
295 		return aText;
296 
297 	String aSep = EditDoc::GetSepStr( eEnd );
298 
299 	EditSelection aSel( rSel );
300 	aSel.Adjust( aEditDoc );
301 
302 	ContentNode* pStartNode = aSel.Min().GetNode();
303 	ContentNode* pEndNode = aSel.Max().GetNode();
304 	sal_uInt32 nStartNode = aEditDoc.GetPos( pStartNode );
305 	sal_uInt32 nEndNode = aEditDoc.GetPos( pEndNode );
306 
307 	DBG_ASSERT( nStartNode <= nEndNode, "Selektion nicht sortiert ?" );
308 
309 	// ueber die Absaetze iterieren...
310 	for ( sal_uInt32 nNode = nStartNode; nNode <= nEndNode; nNode++	)
311 	{
312 		DBG_ASSERT( aEditDoc.SaveGetObject( nNode ), "Node nicht gefunden: GetSelected" );
313 		ContentNode* pNode = aEditDoc.GetObject( nNode );
314 
315 		xub_StrLen nStartPos = 0;
316 		xub_StrLen nEndPos = pNode->Len();
317 		if ( nNode == nStartNode )
318 			nStartPos = aSel.Min().GetIndex();
319 		if ( nNode == nEndNode ) // kann auch == nStart sein!
320 			nEndPos = aSel.Max().GetIndex();
321 
322 		aText += aEditDoc.GetParaAsString( pNode, nStartPos, nEndPos );
323 		if ( nNode < nEndNode )
324 			aText += aSep;
325 	}
326 	return aText;
327 }
328 
MouseButtonDown(const MouseEvent & rMEvt,EditView * pView)329 sal_Bool ImpEditEngine::MouseButtonDown( const MouseEvent& rMEvt, EditView* pView )
330 {
331 	GetSelEngine().SetCurView( pView );
332 	SetActiveView( pView );
333 
334 	if ( GetAutoCompleteText().Len() )
335 		SetAutoCompleteText( String(), sal_True );
336 
337 	GetSelEngine().SelMouseButtonDown( rMEvt );
338 	// Sonderbehandlungen
339 	EditSelection aCurSel( pView->pImpEditView->GetEditSelection() );
340 	if ( !rMEvt.IsShift() )
341 	{
342 		if ( rMEvt.GetClicks() == 2 )
343 		{
344 			// damit die SelectionEngine weiss, dass Anker.
345 			aSelEngine.CursorPosChanging( sal_True, sal_False );
346 
347 			EditSelection aNewSelection( SelectWord( aCurSel ) );
348 			pView->pImpEditView->DrawSelection();
349 			pView->pImpEditView->SetEditSelection( aNewSelection );
350 			pView->pImpEditView->DrawSelection();
351 			pView->ShowCursor( sal_True, sal_True );
352 		}
353 		else if ( rMEvt.GetClicks() == 3 )
354 		{
355 			// damit die SelectionEngine weiss, dass Anker.
356 			aSelEngine.CursorPosChanging( sal_True, sal_False );
357 
358 			EditSelection aNewSelection( aCurSel );
359 			aNewSelection.Min().SetIndex( 0 );
360 			aNewSelection.Max().SetIndex( aCurSel.Min().GetNode()->Len() );
361 			pView->pImpEditView->DrawSelection();
362 			pView->pImpEditView->SetEditSelection( aNewSelection );
363 			pView->pImpEditView->DrawSelection();
364 			pView->ShowCursor( sal_True, sal_True );
365 		}
366 	}
367 	return sal_True;
368 }
369 
Command(const CommandEvent & rCEvt,EditView * pView)370 void ImpEditEngine::Command( const CommandEvent& rCEvt, EditView* pView )
371 {
372 	GetSelEngine().SetCurView( pView );
373 	SetActiveView( pView );
374     if ( rCEvt.GetCommand() == COMMAND_VOICE )
375 	{
376 		const CommandVoiceData* pData = rCEvt.GetVoiceData();
377 		if ( pData->GetType() == VOICECOMMANDTYPE_DICTATION )
378 		{
379 			// Funktionen auf KeyEvents umbiegen, wenn keine entsprechende
380 			// Methode an EditView/EditEngine, damit Undo konsistent bleibt.
381 
382 			SfxPoolItem* pNewAttr = NULL;
383 
384 			switch ( pData->GetCommand() )
385 			{
386 				case DICTATIONCOMMAND_UNKNOWN:
387 				{
388 					pView->InsertText( pData->GetText() );
389 				}
390 				break;
391 				case DICTATIONCOMMAND_NEWPARAGRAPH:
392 				{
393 					pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_RETURN, 0 ) ) );
394 				}
395 				break;
396 				case DICTATIONCOMMAND_NEWLINE:
397 				{
398 					pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_RETURN, KEY_SHIFT ) ) );
399 				}
400 				break;
401 				case DICTATIONCOMMAND_TAB:
402 				{
403 					pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_TAB, 0 ) ) );
404 				}
405 				break;
406 				case DICTATIONCOMMAND_LEFT:
407 				{
408 					pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_LEFT, KEY_MOD1  ) ) );
409 				}
410 				break;
411 				case DICTATIONCOMMAND_RIGHT:
412 				{
413 					pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_RIGHT, KEY_MOD1  ) ) );
414 				}
415 				break;
416 				case DICTATIONCOMMAND_UP:
417 				{
418 					pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_UP, 0 ) ) );
419 				}
420 				break;
421 				case DICTATIONCOMMAND_DOWN:
422 				{
423 					pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_UP, 0 ) ) );
424 				}
425 				break;
426 				case DICTATIONCOMMAND_UNDO:
427 				{
428 					pView->Undo();
429 				}
430 				break;
431 				case DICTATIONCOMMAND_DEL:
432 				{
433 					pView->PostKeyEvent( KeyEvent( 0, KeyCode( KEY_LEFT, KEY_MOD1|KEY_SHIFT  ) ) );
434 					pView->DeleteSelected();
435 				}
436 				break;
437 				case DICTATIONCOMMAND_BOLD_ON:
438 				{
439 					pNewAttr = new SvxWeightItem( WEIGHT_BOLD, EE_CHAR_WEIGHT );
440 				}
441 				break;
442 				case DICTATIONCOMMAND_BOLD_OFF:
443 				{
444 					pNewAttr = new SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT );
445 				}
446 				break;
447 				case DICTATIONCOMMAND_ITALIC_ON:
448 				{
449 					pNewAttr = new SvxPostureItem( ITALIC_NORMAL, EE_CHAR_ITALIC );
450 				}
451 				break;
452 				case DICTATIONCOMMAND_ITALIC_OFF:
453 				{
454 					pNewAttr = new SvxPostureItem( ITALIC_NORMAL, EE_CHAR_ITALIC );
455 				}
456 				break;
457 				case DICTATIONCOMMAND_UNDERLINE_ON:
458 				{
459 					pNewAttr = new SvxUnderlineItem( UNDERLINE_SINGLE, EE_CHAR_UNDERLINE );
460 				}
461 				break;
462 				case DICTATIONCOMMAND_UNDERLINE_OFF:
463 				{
464 					pNewAttr = new SvxUnderlineItem( UNDERLINE_NONE, EE_CHAR_UNDERLINE );
465 				}
466 				break;
467 			}
468 
469 			if ( pNewAttr )
470 			{
471 				SfxItemSet aSet( GetEmptyItemSet() );
472 				aSet.Put( *pNewAttr );
473 				pView->SetAttribs( aSet );
474 				delete pNewAttr;
475 			}
476 		}
477 	}
478 	else if ( rCEvt.GetCommand() == COMMAND_STARTEXTTEXTINPUT )
479 	{
480 		pView->DeleteSelected();
481 		delete mpIMEInfos;
482         EditPaM aPaM = pView->GetImpEditView()->GetEditSelection().Max();
483         String aOldTextAfterStartPos = aPaM.GetNode()->Copy( aPaM.GetIndex() );
484         sal_uInt16 nMax = aOldTextAfterStartPos.Search( CH_FEATURE );
485         if ( nMax != STRING_NOTFOUND )  // don't overwrite features!
486             aOldTextAfterStartPos.Erase( nMax );
487 	    mpIMEInfos = new ImplIMEInfos( aPaM, aOldTextAfterStartPos );
488 		mpIMEInfos->bWasCursorOverwrite = !pView->IsInsertMode();
489         UndoActionStart( EDITUNDO_INSERT );
490 	}
491 	else if ( rCEvt.GetCommand() == COMMAND_ENDEXTTEXTINPUT )
492 	{
493 		DBG_ASSERT( mpIMEInfos, "COMMAND_ENDEXTTEXTINPUT => Kein Start ?" );
494 		if( mpIMEInfos )
495 		{
496 			// #102812# convert quotes in IME text
497 			// works on the last input character, this is escpecially in Korean text often done
498 			// quotes that are inside of the string are not replaced!
499 			// Borrowed from sw: edtwin.cxx
500 			if ( mpIMEInfos->nLen )
501 			{
502 				EditSelection aSel( mpIMEInfos->aPos );
503 				aSel.Min().GetIndex() += mpIMEInfos->nLen-1;
504 				aSel.Max().GetIndex() =
505                     aSel.Max().GetIndex() + mpIMEInfos->nLen;
506 				// #102812# convert quotes in IME text
507 				// works on the last input character, this is escpecially in Korean text often done
508 				// quotes that are inside of the string are not replaced!
509 				const sal_Unicode nCharCode = aSel.Min().GetNode()->GetChar( aSel.Min().GetIndex() );
510 				if ( ( GetStatus().DoAutoCorrect() ) && ( ( nCharCode == '\"' ) || ( nCharCode == '\'' ) ) )
511 				{
512 					aSel = DeleteSelected( aSel );
513 					aSel = AutoCorrect( aSel, nCharCode, mpIMEInfos->bWasCursorOverwrite );
514 					pView->pImpEditView->SetEditSelection( aSel );
515 				}
516 			}
517 
518 			ParaPortion* pPortion = FindParaPortion( mpIMEInfos->aPos.GetNode() );
519 			pPortion->MarkSelectionInvalid( mpIMEInfos->aPos.GetIndex(), 0 );
520 
521 			sal_Bool bWasCursorOverwrite = mpIMEInfos->bWasCursorOverwrite;
522 
523 			delete mpIMEInfos;
524 			mpIMEInfos = NULL;
525 
526 			FormatAndUpdate( pView );
527 
528 			pView->SetInsertMode( !bWasCursorOverwrite );
529 		}
530         UndoActionEnd( EDITUNDO_INSERT );
531 	}
532 	else if ( rCEvt.GetCommand() == COMMAND_EXTTEXTINPUT )
533 	{
534 		DBG_ASSERT( mpIMEInfos, "COMMAND_EXTTEXTINPUT => Kein Start ?" );
535 		if( mpIMEInfos )
536 		{
537 			const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();
538 
539 			if ( !pData->IsOnlyCursorChanged() )
540 			{
541 				EditSelection aSel( mpIMEInfos->aPos );
542 				aSel.Max().GetIndex() =
543                     aSel.Max().GetIndex() + mpIMEInfos->nLen;
544 				aSel = DeleteSelected( aSel );
545 				aSel = ImpInsertText( aSel, pData->GetText() );
546 
547                 if ( mpIMEInfos->bWasCursorOverwrite )
548                 {
549                     sal_uInt16 nOldIMETextLen = mpIMEInfos->nLen;
550                     sal_uInt16 nNewIMETextLen = pData->GetText().Len();
551 
552                     if ( ( nOldIMETextLen > nNewIMETextLen ) &&
553                          ( nNewIMETextLen < mpIMEInfos->aOldTextAfterStartPos.Len() ) )
554                     {
555                         // restore old characters
556                         sal_uInt16 nRestore = nOldIMETextLen - nNewIMETextLen;
557                         EditPaM aPaM( mpIMEInfos->aPos );
558                         aPaM.GetIndex() = aPaM.GetIndex() + nNewIMETextLen;
559                         ImpInsertText( aPaM, mpIMEInfos->aOldTextAfterStartPos.Copy( nNewIMETextLen, nRestore ) );
560                     }
561                     else if ( ( nOldIMETextLen < nNewIMETextLen ) &&
562                               ( nOldIMETextLen < mpIMEInfos->aOldTextAfterStartPos.Len() ) )
563                     {
564                         // overwrite
565                         sal_uInt16 nOverwrite = nNewIMETextLen - nOldIMETextLen;
566                         if ( ( nOldIMETextLen + nOverwrite ) > mpIMEInfos->aOldTextAfterStartPos.Len() )
567                             nOverwrite = mpIMEInfos->aOldTextAfterStartPos.Len() - nOldIMETextLen;
568                         DBG_ASSERT( nOverwrite && (nOverwrite < 0xFF00), "IME Overwrite?!" );
569                         EditPaM aPaM( mpIMEInfos->aPos );
570                         aPaM.GetIndex() = aPaM.GetIndex() + nNewIMETextLen;
571                         EditSelection _aSel( aPaM );
572                         _aSel.Max().GetIndex() =
573                             _aSel.Max().GetIndex() + nOverwrite;
574                         DeleteSelected( _aSel );
575                     }
576                 }
577 				if ( pData->GetTextAttr() )
578 				{
579 					mpIMEInfos->CopyAttribs( pData->GetTextAttr(), pData->GetText().Len() );
580 					mpIMEInfos->bCursor = pData->IsCursorVisible();
581 				}
582 				else
583 				{
584 					mpIMEInfos->DestroyAttribs();
585 					mpIMEInfos->nLen = pData->GetText().Len();
586 				}
587 
588 				ParaPortion* pPortion = FindParaPortion( mpIMEInfos->aPos.GetNode() );
589 				pPortion->MarkSelectionInvalid( mpIMEInfos->aPos.GetIndex(), 0 );
590 				FormatAndUpdate( pView );
591 			}
592 
593 			EditSelection aNewSel = EditPaM( mpIMEInfos->aPos.GetNode(), mpIMEInfos->aPos.GetIndex()+pData->GetCursorPos() );
594 			pView->SetSelection( CreateESel( aNewSel ) );
595 			pView->SetInsertMode( !pData->IsCursorOverwrite() );
596 
597 			if ( pData->IsCursorVisible() )
598 				pView->ShowCursor();
599 			else
600 				pView->HideCursor();
601 		}
602 	}
603 	else if ( rCEvt.GetCommand() == COMMAND_INPUTCONTEXTCHANGE )
604 	{
605 	}
606 	else if ( rCEvt.GetCommand() == COMMAND_CURSORPOS )
607 	{
608 		if ( mpIMEInfos && mpIMEInfos->nLen )
609 		{
610 			EditPaM aPaM( pView->pImpEditView->GetEditSelection().Max() );
611 			Rectangle aR1 = PaMtoEditCursor( aPaM, 0 );
612 
613 			sal_uInt16 nInputEnd = mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen;
614 
615 			if ( !IsFormatted() )
616 				FormatDoc();
617 
618 			ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( GetEditDoc().GetPos( aPaM.GetNode() ) );
619 			sal_uInt16 nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), sal_True );
620 			EditLine* pLine = pParaPortion->GetLines().GetObject( nLine );
621 			if ( pLine && ( nInputEnd > pLine->GetEnd() ) )
622 				nInputEnd = pLine->GetEnd();
623 			Rectangle aR2 = PaMtoEditCursor( EditPaM( aPaM.GetNode(), nInputEnd ), GETCRSR_ENDOFLINE );
624 			Rectangle aRect = pView->GetImpEditView()->GetWindowPos( aR1 );
625 			long nWidth = aR2.Left()-aR1.Right();
626 			if ( nWidth == 0 ) {
627 				Rectangle aR3 = PaMtoEditCursor( mpIMEInfos->aPos );
628 				nWidth = -(aR1.Left() - aR3.Left());
629 			}
630 			pView->GetWindow()->SetCursorRect( &aRect, nWidth );
631 		}
632 		else
633 		{
634 			pView->GetWindow()->SetCursorRect();
635 		}
636 	}
637     else if ( rCEvt.GetCommand() == COMMAND_SELECTIONCHANGE )
638 	{
639         const CommandSelectionChangeData *pData = rCEvt.GetSelectionChangeData();
640 
641         ESelection aSelection = pView->GetSelection();
642         aSelection.Adjust();
643 
644 	    if( pView->HasSelection() )
645 	    {
646 		    aSelection.nEndPos = aSelection.nStartPos;
647 		    aSelection.nStartPos += pData->GetStart();
648 		    aSelection.nEndPos += pData->GetEnd();
649 	    }
650 	    else
651 	    {
652 		    aSelection.nStartPos = pData->GetStart();
653 		    aSelection.nEndPos = pData->GetEnd();
654 	    }
655         pView->SetSelection( aSelection );
656 	}
657 	else if ( rCEvt.GetCommand() == COMMAND_PREPARERECONVERSION )
658 	{
659 	    if ( pView->HasSelection() )
660 	    {
661 		    ESelection aSelection = pView->GetSelection();
662 		    aSelection.Adjust();
663 
664 		    if ( aSelection.nStartPara != aSelection.nEndPara )
665 		    {
666 		        xub_StrLen aParaLen = pEditEngine->GetTextLen( aSelection.nStartPara );
667 		        aSelection.nEndPara = aSelection.nStartPara;
668 		        aSelection.nEndPos = aParaLen;
669 		        pView->SetSelection( aSelection );
670 		    }
671 	    }
672 	}
673 
674 	GetSelEngine().Command( rCEvt );
675 }
676 
MouseButtonUp(const MouseEvent & rMEvt,EditView * pView)677 sal_Bool ImpEditEngine::MouseButtonUp( const MouseEvent& rMEvt, EditView* pView )
678 {
679 	GetSelEngine().SetCurView( pView );
680 	GetSelEngine().SelMouseButtonUp( rMEvt );
681 	bInSelection = sal_False;
682 	// Sonderbehandlungen
683 	EditSelection aCurSel( pView->pImpEditView->GetEditSelection() );
684 	if ( !aCurSel.HasRange() )
685 	{
686 		if ( ( rMEvt.GetClicks() == 1 ) && rMEvt.IsLeft() && !rMEvt.IsMod2() )
687 		{
688 			const SvxFieldItem* pFld = pView->GetFieldUnderMousePointer();
689 			if ( pFld )
690 			{
691 				EditPaM aPaM( aCurSel.Max() );
692 				sal_uInt32 nPara = GetEditDoc().GetPos( aPaM.GetNode() );
693 				GetEditEnginePtr()->FieldClicked( *pFld, nPara, aPaM.GetIndex() );
694 			}
695 		}
696 	}
697 	return sal_True;
698 }
699 
MouseMove(const MouseEvent & rMEvt,EditView * pView)700 sal_Bool ImpEditEngine::MouseMove( const MouseEvent& rMEvt, EditView* pView )
701 {
702 	// MouseMove wird sofort nach ShowQuickHelp() gerufen!
703 //	if ( GetAutoCompleteText().Len() )
704 //		SetAutoCompleteText( String(), sal_True );
705 	GetSelEngine().SetCurView( pView );
706 	GetSelEngine().SelMouseMove( rMEvt );
707 	return sal_True;
708 }
709 
InsertText(EditSelection aSel,const XubString & rStr)710 EditPaM ImpEditEngine::InsertText( EditSelection aSel, const XubString& rStr )
711 {
712 	EditPaM aPaM = ImpInsertText( aSel, rStr );
713 	return aPaM;
714 }
715 
Clear()716 EditPaM ImpEditEngine::Clear()
717 {
718 	InitDoc( sal_False );
719 
720 	EditPaM aPaM = aEditDoc.GetStartPaM();
721 	EditSelection aSel( aPaM );
722 
723 	nCurTextHeight = 0;
724 
725 	ResetUndoManager();
726 
727 	for ( sal_uInt16 nView = aEditViews.Count(); nView; )
728 	{
729 		EditView* pView = aEditViews[--nView];
730 		DBG_CHKOBJ( pView, EditView, 0 );
731 		pView->pImpEditView->SetEditSelection( aSel );
732 	}
733 
734 	return aPaM;
735 }
736 
RemoveText()737 EditPaM ImpEditEngine::RemoveText()
738 {
739 	InitDoc( sal_True );
740 
741 	EditPaM aStartPaM = aEditDoc.GetStartPaM();
742 	EditSelection aEmptySel( aStartPaM, aStartPaM );
743 	for ( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
744 	{
745 		EditView* pView = aEditViews.GetObject(nView);
746 		DBG_CHKOBJ( pView, EditView, 0 );
747 		pView->pImpEditView->SetEditSelection( aEmptySel );
748 	}
749 	ResetUndoManager();
750 	return aEditDoc.GetStartPaM();
751 }
752 
753 
SetText(const XubString & rText)754 void ImpEditEngine::SetText( const XubString& rText )
755 {
756 	// RemoveText loescht die Undo-Liste!
757 	EditPaM aStartPaM = RemoveText();
758 	sal_Bool bUndoCurrentlyEnabled = IsUndoEnabled();
759 	// Der von Hand reingesteckte Text kann nicht vom Anwender rueckgaengig gemacht werden.
760 	EnableUndo( sal_False );
761 
762 	EditSelection aEmptySel( aStartPaM, aStartPaM );
763 	EditPaM aPaM = aStartPaM;
764 	if ( rText.Len() )
765 		aPaM = ImpInsertText( aEmptySel, rText );
766 
767 	for ( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
768 	{
769 		EditView* pView = aEditViews[nView];
770 		DBG_CHKOBJ( pView, EditView, 0 );
771 		pView->pImpEditView->SetEditSelection( EditSelection( aPaM, aPaM ) );
772 		// Wenn kein Text, dann auch Kein Format&Update
773 		// => Der Text bleibt stehen.
774 		if ( !rText.Len() && GetUpdateMode() )
775 		{
776 			Rectangle aTmpRec( pView->GetOutputArea().TopLeft(),
777 								Size( aPaperSize.Width(), nCurTextHeight ) );
778 			aTmpRec.Intersection( pView->GetOutputArea() );
779 			pView->GetWindow()->Invalidate( aTmpRec );
780 		}
781 	}
782 	if( !rText.Len() )	// sonst muss spaeter noch invalidiert werden, !bFormatted reicht.
783 		nCurTextHeight = 0;
784 	EnableUndo( bUndoCurrentlyEnabled );
785 #ifndef SVX_LIGHT
786 	DBG_ASSERT( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "Undo nach SetText?" );
787 #endif
788 }
789 
790 
GetEmptyItemSet()791 const SfxItemSet& ImpEditEngine::GetEmptyItemSet()
792 {
793 	if ( !pEmptyItemSet )
794 	{
795 		pEmptyItemSet = new SfxItemSet( aEditDoc.GetItemPool(), EE_ITEMS_START, EE_ITEMS_END );
796 		for ( sal_uInt16 nWhich = EE_ITEMS_START; nWhich <= EE_CHAR_END; nWhich++)
797 		{
798 			pEmptyItemSet->ClearItem( nWhich );
799 		}
800 	}
801 	return *pEmptyItemSet;
802 }
803 
804 //	----------------------------------------------------------------------
805 //	MISC
806 //	----------------------------------------------------------------------
CursorMoved(ContentNode * pPrevNode)807 void ImpEditEngine::CursorMoved( ContentNode* pPrevNode )
808 {
809 	// Leere Attribute loeschen, aber nur, wenn Absatz nicht leer!
810 	if ( pPrevNode->GetCharAttribs().HasEmptyAttribs() && pPrevNode->Len() )
811 		pPrevNode->GetCharAttribs().DeleteEmptyAttribs( aEditDoc.GetItemPool() );
812 }
813 
TextModified()814 void ImpEditEngine::TextModified()
815 {
816 	bFormatted = sal_False;
817 
818     if ( GetNotifyHdl().IsSet() )
819     {
820         EENotify aNotify( EE_NOTIFY_TEXTMODIFIED );
821         aNotify.pEditEngine = GetEditEnginePtr();
822         CallNotify( aNotify );
823     }
824 }
825 
826 
ParaAttribsChanged(ContentNode * pNode)827 void ImpEditEngine::ParaAttribsChanged( ContentNode* pNode )
828 {
829 	DBG_ASSERT( pNode, "ParaAttribsChanged: Welcher?" );
830 
831 	aEditDoc.SetModified( sal_True );
832 	bFormatted = sal_False;
833 
834 	ParaPortion* pPortion = FindParaPortion( pNode );
835 	DBG_ASSERT( pPortion, "ParaAttribsChanged: Portion?" );
836 	pPortion->MarkSelectionInvalid( 0, pNode->Len() );
837 
838 	sal_uInt32 nPara = aEditDoc.GetPos( pNode );
839 	pEditEngine->ParaAttribsChanged( nPara );
840 
841 	ParaPortion* pNextPortion = GetParaPortions().SaveGetObject( nPara+1 );
842 	// => wird sowieso noch formatiert, wenn Invalid.
843 	if ( pNextPortion && !pNextPortion->IsInvalid() )
844 		CalcHeight( pNextPortion );
845 }
846 
847 //	----------------------------------------------------------------------
848 //	Cursorbewegungen
849 //	----------------------------------------------------------------------
850 
MoveCursor(const KeyEvent & rKeyEvent,EditView * pEditView)851 EditSelection ImpEditEngine::MoveCursor( const KeyEvent& rKeyEvent, EditView* pEditView )
852 {
853 	// Eigentlich nur bei Up/Down noetig, aber was solls.
854 	CheckIdleFormatter();
855 
856 	EditPaM aPaM( pEditView->pImpEditView->GetEditSelection().Max() );
857 
858 	EditPaM aOldPaM( aPaM );
859 
860     TextDirectionality eTextDirection = TextDirectionality_LeftToRight_TopToBottom;
861 	if ( IsVertical() )
862         eTextDirection = TextDirectionality_TopToBottom_RightToLeft;
863     else if ( IsRightToLeft( GetEditDoc().GetPos( aPaM.GetNode() ) ) )
864         eTextDirection = TextDirectionality_RightToLeft_TopToBottom;
865 
866     KeyEvent aTranslatedKeyEvent = rKeyEvent.LogicalTextDirectionality( eTextDirection );
867 
868     sal_Bool bCtrl = aTranslatedKeyEvent.GetKeyCode().IsMod1() ? sal_True : sal_False;
869 	sal_uInt16 nCode = aTranslatedKeyEvent.GetKeyCode().GetCode();
870 
871     if ( DoVisualCursorTraveling( aPaM.GetNode() ) )
872     {
873         // Only for simple cursor movement...
874         if ( !bCtrl && ( ( nCode == KEY_LEFT ) || ( nCode == KEY_RIGHT ) ) )
875         {
876             aPaM = CursorVisualLeftRight( pEditView, aPaM, rKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL, rKeyEvent.GetKeyCode().GetCode() == KEY_LEFT );
877 	        nCode = 0;  // skip switch statement
878         }
879         /*
880         else if ( !bCtrl && ( ( nCode == KEY_HOME ) || ( nCode == KEY_END ) ) )
881         {
882             aPaM = CursorVisualStartEnd( pEditView, aPaM, nCode == KEY_HOME );
883 	        nCode = 0;  // skip switch statement
884         }
885         */
886     }
887 
888     bool bKeyModifySelection = aTranslatedKeyEvent.GetKeyCode().IsShift();
889 	switch ( nCode )
890 	{
891 		case KEY_UP:		aPaM = CursorUp( aPaM, pEditView );
892 							break;
893 		case KEY_DOWN:		aPaM = CursorDown( aPaM, pEditView );
894 							break;
895         case KEY_LEFT:		aPaM = bCtrl ? WordLeft( aPaM ) : CursorLeft( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL );
896 							break;
897 		case KEY_RIGHT: 	aPaM = bCtrl ? WordRight( aPaM ) : CursorRight( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL );
898 							break;
899 		case KEY_HOME:		aPaM = bCtrl ? CursorStartOfDoc() : CursorStartOfLine( aPaM );
900 							break;
901 		case KEY_END:		aPaM = bCtrl ? CursorEndOfDoc() : CursorEndOfLine( aPaM );
902 							break;
903 		case KEY_PAGEUP:	aPaM = bCtrl ? CursorStartOfDoc() : PageUp( aPaM, pEditView );
904 							break;
905 		case KEY_PAGEDOWN:	aPaM = bCtrl ? CursorEndOfDoc() : PageDown( aPaM, pEditView );
906 							break;
907         case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_LINE:
908                             aPaM = CursorStartOfLine( aPaM );
909                             bKeyModifySelection = false;
910                             break;
911         case com::sun::star::awt::Key::MOVE_TO_END_OF_LINE:
912                             aPaM = CursorEndOfLine( aPaM );
913                             bKeyModifySelection = false;
914                             break;
915         case com::sun::star::awt::Key::MOVE_WORD_BACKWARD:
916                             aPaM = WordLeft( aPaM );
917                             bKeyModifySelection = false;
918                             break;
919         case com::sun::star::awt::Key::MOVE_WORD_FORWARD:
920                             aPaM = WordRight( aPaM );
921                             bKeyModifySelection = false;
922                             break;
923         case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
924                             aPaM = CursorStartOfParagraph( aPaM );
925                             if( aPaM == aOldPaM )
926                             {
927                                 aPaM = CursorLeft( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
928                                 aPaM = CursorStartOfParagraph( aPaM );
929                             }
930                             bKeyModifySelection = false;
931                             break;
932         case com::sun::star::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
933                             aPaM = CursorEndOfParagraph( aPaM );
934                             if( aPaM == aOldPaM )
935                             {
936                                 aPaM = CursorRight( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
937                                 aPaM = CursorEndOfParagraph( aPaM );
938                             }
939                             bKeyModifySelection = false;
940                             break;
941         case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
942                             aPaM = CursorStartOfDoc();
943                             bKeyModifySelection = false;
944                             break;
945         case com::sun::star::awt::Key::MOVE_TO_END_OF_DOCUMENT:
946                             aPaM = CursorEndOfDoc();
947                             bKeyModifySelection = false;
948                             break;
949         case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_LINE:
950                             aPaM = CursorStartOfLine( aPaM );
951                             bKeyModifySelection = true;
952                             break;
953         case com::sun::star::awt::Key::SELECT_TO_END_OF_LINE:
954                             aPaM = CursorEndOfLine( aPaM );
955                             bKeyModifySelection = true;
956                             break;
957         case com::sun::star::awt::Key::SELECT_BACKWARD:
958                             aPaM = CursorLeft( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
959                             bKeyModifySelection = true;
960                             break;
961         case com::sun::star::awt::Key::SELECT_FORWARD:
962                             aPaM = CursorRight( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
963                             bKeyModifySelection = true;
964                             break;
965         case com::sun::star::awt::Key::SELECT_WORD_BACKWARD:
966                             aPaM = WordLeft( aPaM );
967                             bKeyModifySelection = true;
968                             break;
969         case com::sun::star::awt::Key::SELECT_WORD_FORWARD:
970                             aPaM = WordRight( aPaM );
971                             bKeyModifySelection = true;
972                             break;
973         case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
974                             aPaM = CursorStartOfParagraph( aPaM );
975                             if( aPaM == aOldPaM )
976                             {
977                                 aPaM = CursorLeft( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
978                                 aPaM = CursorStartOfParagraph( aPaM );
979                             }
980                             bKeyModifySelection = true;
981                             break;
982         case com::sun::star::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
983                             aPaM = CursorEndOfParagraph( aPaM );
984                             if( aPaM == aOldPaM )
985                             {
986                                 aPaM = CursorRight( aPaM, i18n::CharacterIteratorMode::SKIPCELL );
987                                 aPaM = CursorEndOfParagraph( aPaM );
988                             }
989                             bKeyModifySelection = true;
990                             break;
991         case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
992                             aPaM = CursorStartOfDoc();
993                             bKeyModifySelection = true;
994                             break;
995         case com::sun::star::awt::Key::SELECT_TO_END_OF_DOCUMENT:
996                             aPaM = CursorEndOfDoc();
997                             bKeyModifySelection = true;
998                             break;
999 	}
1000 
1001 	if ( aOldPaM != aPaM )
1002 	{
1003 		CursorMoved( aOldPaM.GetNode() );
1004 		if ( aStatus.NotifyCursorMovements() && ( aOldPaM.GetNode() != aPaM.GetNode() ) )
1005 		{
1006 			aStatus.GetStatusWord() = aStatus.GetStatusWord() | EE_STAT_CRSRLEFTPARA;
1007 			aStatus.GetPrevParagraph() = aEditDoc.GetPos( aOldPaM.GetNode() );
1008 		}
1009 	}
1010 	else
1011 		aStatus.GetStatusWord() = aStatus.GetStatusWord() | EE_STAT_CRSRMOVEFAIL;
1012 
1013 	// Bewirkt evtl. ein CreateAnchor oder Deselection all
1014 	aSelEngine.SetCurView( pEditView );
1015 	aSelEngine.CursorPosChanging( bKeyModifySelection, aTranslatedKeyEvent.GetKeyCode().IsMod1() );
1016 	EditPaM aOldEnd( pEditView->pImpEditView->GetEditSelection().Max() );
1017 	pEditView->pImpEditView->GetEditSelection().Max() = aPaM;
1018 	if ( bKeyModifySelection )
1019 	{
1020 		// Dann wird die Selektion erweitert...
1021 		EditSelection aTmpNewSel( aOldEnd, aPaM );
1022 		pEditView->pImpEditView->DrawSelection( aTmpNewSel );
1023 	}
1024 	else
1025 		pEditView->pImpEditView->GetEditSelection().Min() = aPaM;
1026 
1027 	return pEditView->pImpEditView->GetEditSelection();
1028 }
1029 
CursorVisualStartEnd(EditView * pEditView,const EditPaM & rPaM,sal_Bool bStart)1030 EditPaM ImpEditEngine::CursorVisualStartEnd( EditView* pEditView, const EditPaM& rPaM, sal_Bool bStart )
1031 {
1032     EditPaM aPaM( rPaM );
1033 
1034     sal_uInt32 nPara = GetEditDoc().GetPos( aPaM.GetNode() );
1035     ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
1036 
1037     sal_uInt16 nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), sal_False );
1038     EditLine* pLine = pParaPortion->GetLines().GetObject( nLine );
1039     sal_Bool bEmptyLine = pLine->GetStart() == pLine->GetEnd();
1040 
1041     pEditView->pImpEditView->nExtraCursorFlags = 0;
1042 
1043     if ( !bEmptyLine )
1044     {
1045         String aLine( *aPaM.GetNode(), pLine->GetStart(), pLine->GetEnd() - pLine->GetStart() );
1046 //        sal_uInt16 nPosInLine = aPaM.GetIndex() - pLine->GetStart();
1047 
1048         const sal_Unicode* pLineString = aLine.GetBuffer();
1049 
1050         UErrorCode nError = U_ZERO_ERROR;
1051         UBiDi* pBidi = ubidi_openSized( aLine.Len(), 0, &nError );
1052 
1053         const UBiDiLevel  nBidiLevel = IsRightToLeft( nPara ) ? 1 /*RTL*/ : 0 /*LTR*/;
1054         ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(pLineString), aLine.Len(), nBidiLevel, NULL, &nError );	// UChar != sal_Unicode in MinGW
1055 
1056         sal_uInt16 nVisPos = bStart ? 0 : aLine.Len()-1;
1057         sal_uInt16 nLogPos = (sal_uInt16)ubidi_getLogicalIndex( pBidi, nVisPos, &nError );
1058 
1059         ubidi_close( pBidi );
1060 
1061         aPaM.GetIndex() = nLogPos + pLine->GetStart();
1062 
1063         sal_uInt16 nTmp;
1064         sal_uInt16 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nTmp, sal_True );
1065         TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
1066         sal_uInt16 nRTLLevel = pTextPortion->GetRightToLeft();
1067 //        sal_Bool bParaRTL = IsRightToLeft( nPara );
1068         sal_Bool bPortionRTL = nRTLLevel%2 ? sal_True : sal_False;
1069 
1070         if ( bStart )
1071         {
1072             pEditView->pImpEditView->SetCursorBidiLevel( bPortionRTL ? 0 : 1 );
1073             // Maybe we must be *behind* the character
1074             if ( bPortionRTL && pEditView->IsInsertMode() )
1075                 aPaM.GetIndex()++;
1076         }
1077         else
1078         {
1079             pEditView->pImpEditView->SetCursorBidiLevel( bPortionRTL ? 1 : 0 );
1080             if ( !bPortionRTL && pEditView->IsInsertMode() )
1081                 aPaM.GetIndex()++;
1082         }
1083     }
1084 
1085     return aPaM;
1086 }
1087 
CursorVisualLeftRight(EditView * pEditView,const EditPaM & rPaM,sal_uInt16 nCharacterIteratorMode,sal_Bool bVisualToLeft)1088 EditPaM ImpEditEngine::CursorVisualLeftRight( EditView* pEditView, const EditPaM& rPaM, sal_uInt16 nCharacterIteratorMode, sal_Bool bVisualToLeft )
1089 {
1090     EditPaM aPaM( rPaM );
1091 
1092     sal_uInt32 nPara = GetEditDoc().GetPos( aPaM.GetNode() );
1093     ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
1094 
1095     sal_uInt16 nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), sal_False );
1096     EditLine* pLine = pParaPortion->GetLines().GetObject( nLine );
1097     sal_Bool bEmptyLine = pLine->GetStart() == pLine->GetEnd();
1098 
1099 //    sal_uInt16 nCurrentCursorFlags = pEditView->pImpEditView->nExtraCursorFlags;
1100     pEditView->pImpEditView->nExtraCursorFlags = 0;
1101 
1102     sal_Bool bParaRTL = IsRightToLeft( nPara );
1103 
1104     sal_Bool bDone = sal_False;
1105 
1106     if ( bEmptyLine )
1107     {
1108         if ( bVisualToLeft )
1109         {
1110             aPaM = CursorUp( aPaM, pEditView );
1111             if ( aPaM != rPaM )
1112                 aPaM = CursorVisualStartEnd( pEditView, aPaM, sal_False );
1113         }
1114         else
1115         {
1116             aPaM = CursorDown( aPaM, pEditView );
1117             if ( aPaM != rPaM )
1118                 aPaM = CursorVisualStartEnd( pEditView, aPaM, sal_True );
1119         }
1120 
1121         bDone = sal_True;
1122     }
1123 
1124     sal_Bool bLogicalBackward = bParaRTL ? !bVisualToLeft : bVisualToLeft;
1125 
1126     if ( !bDone && pEditView->IsInsertMode() )
1127     {
1128         // Check if we are within a portion and don't have overwrite mode, then it's easy...
1129         sal_uInt16 nPortionStart;
1130         sal_uInt16 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, sal_False );
1131         TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
1132 
1133         sal_Bool bPortionBoundary = ( aPaM.GetIndex() == nPortionStart ) || ( aPaM.GetIndex() == (nPortionStart+pTextPortion->GetLen()) );
1134         sal_uInt16 nRTLLevel = pTextPortion->GetRightToLeft();
1135 
1136         // Portion boundary doesn't matter if both have same RTL level
1137         sal_uInt16 nRTLLevelNextPortion = 0xFFFF;
1138         if ( bPortionBoundary && aPaM.GetIndex() && ( aPaM.GetIndex() < aPaM.GetNode()->Len() ) )
1139         {
1140             sal_uInt16 nTmp;
1141             sal_uInt16 nNextTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex()+1, nTmp, bLogicalBackward ? sal_False : sal_True );
1142             TextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nNextTextPortion );
1143             nRTLLevelNextPortion = pNextTextPortion->GetRightToLeft();
1144         }
1145 
1146         if ( !bPortionBoundary || ( nRTLLevel == nRTLLevelNextPortion ) )
1147         {
1148             if ( ( bVisualToLeft && !(nRTLLevel%2) ) || ( !bVisualToLeft && (nRTLLevel%2) ) )
1149             {
1150                 aPaM = CursorLeft( aPaM, nCharacterIteratorMode );
1151                 pEditView->pImpEditView->SetCursorBidiLevel( 1 );
1152             }
1153             else
1154             {
1155                 aPaM = CursorRight( aPaM, nCharacterIteratorMode );
1156                 pEditView->pImpEditView->SetCursorBidiLevel( 0 );
1157             }
1158             bDone = sal_True;
1159         }
1160     }
1161 
1162     if ( !bDone )
1163     {
1164         sal_Bool bGotoStartOfNextLine = sal_False;
1165         sal_Bool bGotoEndOfPrevLine = sal_False;
1166 
1167         String aLine( *aPaM.GetNode(), pLine->GetStart(), pLine->GetEnd() - pLine->GetStart() );
1168         sal_uInt16 nPosInLine = aPaM.GetIndex() - pLine->GetStart();
1169 
1170         const sal_Unicode* pLineString = aLine.GetBuffer();
1171 
1172         UErrorCode nError = U_ZERO_ERROR;
1173         UBiDi* pBidi = ubidi_openSized( aLine.Len(), 0, &nError );
1174 
1175         const UBiDiLevel  nBidiLevel = IsRightToLeft( nPara ) ? 1 /*RTL*/ : 0 /*LTR*/;
1176         ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(pLineString), aLine.Len(), nBidiLevel, NULL, &nError );	// UChar != sal_Unicode in MinGW
1177 
1178         if ( !pEditView->IsInsertMode() )
1179         {
1180             sal_Bool bEndOfLine = nPosInLine == aLine.Len();
1181             sal_uInt16 nVisPos = (sal_uInt16)ubidi_getVisualIndex( pBidi, !bEndOfLine ? nPosInLine : nPosInLine-1, &nError );
1182             if ( bVisualToLeft )
1183             {
1184                 bGotoEndOfPrevLine = nVisPos == 0;
1185                 if ( !bEndOfLine )
1186                     nVisPos--;
1187             }
1188             else
1189             {
1190                 bGotoStartOfNextLine = nVisPos == (aLine.Len() - 1);
1191                 if ( !bEndOfLine )
1192                     nVisPos++;
1193             }
1194 
1195             if ( !bGotoEndOfPrevLine && !bGotoStartOfNextLine )
1196             {
1197                 sal_uInt16 nLogPos = (sal_uInt16)ubidi_getLogicalIndex( pBidi, nVisPos, &nError );
1198                 aPaM.GetIndex() = pLine->GetStart() + nLogPos;
1199                 pEditView->pImpEditView->SetCursorBidiLevel( 0 );
1200             }
1201         }
1202         else
1203         {
1204             sal_Bool bWasBehind = sal_False;
1205             sal_Bool bBeforePortion = !nPosInLine || pEditView->pImpEditView->GetCursorBidiLevel() == 1;
1206             if ( nPosInLine && ( !bBeforePortion ) ) // before the next portion
1207                 bWasBehind = sal_True;  // step one back, otherwise visual will be unusable when rtl portion follows.
1208 
1209             sal_uInt16 nPortionStart;
1210             sal_uInt16 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, bBeforePortion );
1211             TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
1212             sal_Bool bRTLPortion = (pTextPortion->GetRightToLeft() % 2) != 0;
1213 
1214             // -1: We are 'behind' the character
1215             long nVisPos = (long)ubidi_getVisualIndex( pBidi, bWasBehind ? nPosInLine-1 : nPosInLine, &nError );
1216             if ( bVisualToLeft )
1217             {
1218                 if ( !bWasBehind || bRTLPortion )
1219                     nVisPos--;
1220             }
1221             else
1222             {
1223                 if ( bWasBehind || bRTLPortion || bBeforePortion )
1224                     nVisPos++;
1225 //                if ( bWasBehind && bRTLPortion )
1226 //                    nVisPos++;
1227             }
1228 
1229             bGotoEndOfPrevLine = nVisPos < 0;
1230             bGotoStartOfNextLine = nVisPos >= aLine.Len();
1231 
1232             if ( !bGotoEndOfPrevLine && !bGotoStartOfNextLine )
1233             {
1234                 sal_uInt16 nLogPos = (sal_uInt16)ubidi_getLogicalIndex( pBidi, nVisPos, &nError );
1235 
1236 /*
1237                 if ( nLogPos == aPaM.GetIndex() )
1238                 {
1239                     if ( bVisualToLeft )
1240                         bGotoEndOfPrevLine = sal_True;
1241                     else
1242                         bGotoStartOfNextLine = sal_True;
1243                 }
1244                 else
1245 */
1246                 {
1247                     aPaM.GetIndex() = pLine->GetStart() + nLogPos;
1248 
1249                     // RTL portion, stay visually on the left side.
1250                     sal_uInt16 _nPortionStart;
1251                     // sal_uInt16 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, !bRTLPortion );
1252                     sal_uInt16 _nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), _nPortionStart, sal_True );
1253                     TextPortion* _pTextPortion = pParaPortion->GetTextPortions().GetObject( _nTextPortion );
1254                     if ( bVisualToLeft && !bRTLPortion && ( _pTextPortion->GetRightToLeft() % 2 ) )
1255                         aPaM.GetIndex()++;
1256                     else if ( !bVisualToLeft && bRTLPortion && ( bWasBehind || !(_pTextPortion->GetRightToLeft() % 2 )) )
1257                         aPaM.GetIndex()++;
1258 
1259                     pEditView->pImpEditView->SetCursorBidiLevel( _nPortionStart );
1260                 }
1261             }
1262         }
1263 
1264         ubidi_close( pBidi );
1265 
1266         if ( bGotoEndOfPrevLine )
1267         {
1268             aPaM = CursorUp( aPaM, pEditView );
1269             if ( aPaM != rPaM )
1270                 aPaM = CursorVisualStartEnd( pEditView, aPaM, sal_False );
1271         }
1272         else if ( bGotoStartOfNextLine )
1273         {
1274             aPaM = CursorDown( aPaM, pEditView );
1275             if ( aPaM != rPaM )
1276                 aPaM = CursorVisualStartEnd( pEditView, aPaM, sal_True );
1277         }
1278     }
1279     return aPaM;
1280 }
1281 
1282 
CursorLeft(const EditPaM & rPaM,sal_uInt16 nCharacterIteratorMode)1283 EditPaM ImpEditEngine::CursorLeft( const EditPaM& rPaM, sal_uInt16 nCharacterIteratorMode )
1284 {
1285     EditPaM aCurPaM( rPaM );
1286 	EditPaM aNewPaM( aCurPaM );
1287 
1288 	if ( aCurPaM.GetIndex() )
1289 	{
1290 		sal_Int32 nCount = 1;
1291 		uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1292  		aNewPaM.SetIndex( (sal_uInt16)_xBI->previousCharacters( *aNewPaM.GetNode(), aNewPaM.GetIndex(), GetLocale( aNewPaM ), nCharacterIteratorMode, nCount, nCount ) );
1293 	}
1294 	else
1295 	{
1296 		ContentNode* pNode = aCurPaM.GetNode();
1297 		pNode = GetPrevVisNode( pNode );
1298 		if ( pNode )
1299 		{
1300 			aNewPaM.SetNode( pNode );
1301 			aNewPaM.SetIndex( pNode->Len() );
1302 		}
1303 	}
1304 
1305 	return aNewPaM;
1306 }
1307 
CursorRight(const EditPaM & rPaM,sal_uInt16 nCharacterIteratorMode)1308 EditPaM ImpEditEngine::CursorRight( const EditPaM& rPaM, sal_uInt16 nCharacterIteratorMode )
1309 {
1310     EditPaM aCurPaM( rPaM );
1311 	EditPaM aNewPaM( aCurPaM );
1312 
1313     if ( aCurPaM.GetIndex() < aCurPaM.GetNode()->Len() )
1314 	{
1315 		uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1316 		sal_Int32 nCount = 1;
1317 		aNewPaM.SetIndex( (sal_uInt16)_xBI->nextCharacters( *aNewPaM.GetNode(), aNewPaM.GetIndex(), GetLocale( aNewPaM ), nCharacterIteratorMode, nCount, nCount ) );
1318 	}
1319 	else
1320 	{
1321 		ContentNode* pNode = aCurPaM.GetNode();
1322 		pNode = GetNextVisNode( pNode );
1323 		if ( pNode )
1324 		{
1325 			aNewPaM.SetNode( pNode );
1326 			aNewPaM.SetIndex( 0 );
1327 		}
1328 	}
1329 
1330 	return aNewPaM;
1331 }
1332 
CursorUp(const EditPaM & rPaM,EditView * pView)1333 EditPaM ImpEditEngine::CursorUp( const EditPaM& rPaM, EditView* pView )
1334 {
1335 	DBG_ASSERT( pView, "Keine View - Keine Cursorbewegung!" );
1336 
1337 	ParaPortion* pPPortion = FindParaPortion( rPaM.GetNode() );
1338 	DBG_ASSERT( pPPortion, "Keine passende Portion gefunden: CursorUp" );
1339 	sal_uInt16 nLine = pPPortion->GetLineNumber( rPaM.GetIndex() );
1340 	EditLine* pLine = pPPortion->GetLines().GetObject( nLine );
1341 
1342 	long nX;
1343 	if ( pView->pImpEditView->nTravelXPos == TRAVEL_X_DONTKNOW )
1344 	{
1345 		nX = GetXPos( pPPortion, pLine, rPaM.GetIndex() );
1346 		pView->pImpEditView->nTravelXPos = nX+nOnePixelInRef;
1347 	}
1348 	else
1349 		nX = pView->pImpEditView->nTravelXPos;
1350 
1351 	EditPaM aNewPaM( rPaM );
1352 	if ( nLine )	// gleicher Absatz
1353 	{
1354 		EditLine* pPrevLine = pPPortion->GetLines().GetObject(nLine-1);
1355 		aNewPaM.SetIndex( GetChar( pPPortion, pPrevLine, nX ) );
1356 		// Wenn davor eine autom.Umgebrochene Zeile, und ich muss genau an das
1357 		// Ende dieser Zeile, landet der Cursor in der aktuellen Zeile am Anfang
1358 		// Siehe Problem: Letztes Zeichen einer autom.umgebr. Zeile = Cursor
1359 		if ( aNewPaM.GetIndex() && ( aNewPaM.GetIndex() == pLine->GetStart() ) )
1360 			aNewPaM = CursorLeft( aNewPaM );
1361 	}
1362 	else	// vorheriger Absatz
1363 	{
1364 		ParaPortion* pPrevPortion = GetPrevVisPortion( pPPortion );
1365 		if ( pPrevPortion )
1366 		{
1367 			pLine = pPrevPortion->GetLines().GetObject( pPrevPortion->GetLines().Count()-1 );
1368 			DBG_ASSERT( pLine, "Zeile davor nicht gefunden: CursorUp" );
1369 			aNewPaM.SetNode( pPrevPortion->GetNode() );
1370 			aNewPaM.SetIndex( GetChar( pPrevPortion, pLine, nX+nOnePixelInRef ) );
1371 		}
1372 	}
1373 
1374 	return aNewPaM;
1375 }
1376 
CursorDown(const EditPaM & rPaM,EditView * pView)1377 EditPaM ImpEditEngine::CursorDown( const EditPaM& rPaM, EditView* pView )
1378 {
1379 	DBG_ASSERT( pView, "Keine View - Keine Cursorbewegung!" );
1380 
1381 	ParaPortion* pPPortion = FindParaPortion( rPaM.GetNode() );
1382 	DBG_ASSERT( pPPortion, "Keine passende Portion gefunden: CursorDown" );
1383 	sal_uInt16 nLine = pPPortion->GetLineNumber( rPaM.GetIndex() );
1384 
1385 	long nX;
1386 	if ( pView->pImpEditView->nTravelXPos == TRAVEL_X_DONTKNOW )
1387 	{
1388 		EditLine* pLine = pPPortion->GetLines().GetObject(nLine);
1389 		nX = GetXPos( pPPortion, pLine, rPaM.GetIndex() );
1390 		pView->pImpEditView->nTravelXPos = nX+nOnePixelInRef;
1391 	}
1392 	else
1393 		nX = pView->pImpEditView->nTravelXPos;
1394 
1395 	EditPaM aNewPaM( rPaM );
1396 	if ( nLine < pPPortion->GetLines().Count()-1 )
1397 	{
1398 		EditLine* pNextLine = pPPortion->GetLines().GetObject(nLine+1);
1399 		aNewPaM.SetIndex( GetChar( pPPortion, pNextLine, nX ) );
1400 		// Sonderbehandlung siehe CursorUp...
1401 		if ( ( aNewPaM.GetIndex() == pNextLine->GetEnd() ) && ( aNewPaM.GetIndex() > pNextLine->GetStart() ) && ( aNewPaM.GetIndex() < pPPortion->GetNode()->Len() ) )
1402 			aNewPaM = CursorLeft( aNewPaM );
1403 	}
1404 	else	// naechster Absatz
1405 	{
1406 		ParaPortion* pNextPortion = GetNextVisPortion( pPPortion );
1407 		if ( pNextPortion )
1408 		{
1409 			EditLine* pLine = pNextPortion->GetLines().GetObject(0);
1410 			DBG_ASSERT( pLine, "Zeile davor nicht gefunden: CursorUp" );
1411 			aNewPaM.SetNode( pNextPortion->GetNode() );
1412 			// Nie ganz ans Ende wenn mehrere Zeilen, da dann eine
1413 			// Zeile darunter der Cursor angezeigt wird.
1414 			aNewPaM.SetIndex( GetChar( pNextPortion, pLine, nX+nOnePixelInRef ) );
1415 			if ( ( aNewPaM.GetIndex() == pLine->GetEnd() ) && ( aNewPaM.GetIndex() > pLine->GetStart() ) && ( pNextPortion->GetLines().Count() > 1 ) )
1416 				aNewPaM = CursorLeft( aNewPaM );
1417 		}
1418 	}
1419 
1420 	return aNewPaM;
1421 }
1422 
CursorStartOfLine(const EditPaM & rPaM)1423 EditPaM ImpEditEngine::CursorStartOfLine( const EditPaM& rPaM )
1424 {
1425 	ParaPortion* pCurPortion = FindParaPortion( rPaM.GetNode() );
1426 	DBG_ASSERT( pCurPortion, "Keine Portion fuer den PaM ?" );
1427 	sal_uInt16 nLine = pCurPortion->GetLineNumber( rPaM.GetIndex() );
1428 	EditLine* pLine = pCurPortion->GetLines().GetObject(nLine);
1429 	DBG_ASSERT( pLine, "Aktuelle Zeile nicht gefunden ?!" );
1430 
1431 	EditPaM aNewPaM( rPaM );
1432 	aNewPaM.SetIndex( pLine->GetStart() );
1433 	return aNewPaM;
1434 }
1435 
CursorEndOfLine(const EditPaM & rPaM)1436 EditPaM ImpEditEngine::CursorEndOfLine( const EditPaM& rPaM )
1437 {
1438 	ParaPortion* pCurPortion = FindParaPortion( rPaM.GetNode() );
1439 	DBG_ASSERT( pCurPortion, "Keine Portion fuer den PaM ?" );
1440 	sal_uInt16 nLine = pCurPortion->GetLineNumber( rPaM.GetIndex() );
1441 	EditLine* pLine = pCurPortion->GetLines().GetObject(nLine);
1442 	DBG_ASSERT( pLine, "Aktuelle Zeile nicht gefunden ?!" );
1443 
1444 	EditPaM aNewPaM( rPaM );
1445 	aNewPaM.SetIndex( pLine->GetEnd() );
1446 	if ( pLine->GetEnd() > pLine->GetStart() )
1447 	{
1448 //		xub_Unicode cLastChar = aNewPaM.GetNode()->GetChar( aNewPaM.GetIndex()-1 );
1449 		if ( aNewPaM.GetNode()->IsFeature( aNewPaM.GetIndex() - 1 ) )
1450 		{
1451 			// Bei einem weichen Umbruch muss ich davor stehen!
1452 			EditCharAttrib* pNextFeature = aNewPaM.GetNode()->GetCharAttribs().FindFeature( aNewPaM.GetIndex()-1 );
1453 			if ( pNextFeature && ( pNextFeature->GetItem()->Which() == EE_FEATURE_LINEBR ) )
1454 				aNewPaM = CursorLeft( aNewPaM );
1455 		}
1456 		else if ( ( aNewPaM.GetNode()->GetChar( aNewPaM.GetIndex() - 1 ) == ' ' ) && ( aNewPaM.GetIndex() != aNewPaM.GetNode()->Len() ) )
1457 		{
1458 			// Bei einem Blank in einer autom. umgebrochenen Zeile macht es Sinn,
1459 			// davor zu stehen, da der Anwender hinter das Wort will.
1460 			// Wenn diese geaendert wird, Sonderbehandlung fuer Pos1 nach End!
1461 			aNewPaM = CursorLeft( aNewPaM );
1462 		}
1463 	}
1464 	return aNewPaM;
1465 }
1466 
CursorStartOfParagraph(const EditPaM & rPaM)1467 EditPaM ImpEditEngine::CursorStartOfParagraph( const EditPaM& rPaM )
1468 {
1469 	EditPaM aPaM( rPaM.GetNode(), 0 );
1470 	return aPaM;
1471 }
1472 
CursorEndOfParagraph(const EditPaM & rPaM)1473 EditPaM ImpEditEngine::CursorEndOfParagraph( const EditPaM& rPaM )
1474 {
1475 	EditPaM aPaM( rPaM.GetNode(), rPaM.GetNode()->Len() );
1476 	return aPaM;
1477 }
1478 
CursorStartOfDoc()1479 EditPaM ImpEditEngine::CursorStartOfDoc()
1480 {
1481 	EditPaM aPaM( aEditDoc.SaveGetObject( 0 ), 0 );
1482 	return aPaM;
1483 }
1484 
CursorEndOfDoc()1485 EditPaM ImpEditEngine::CursorEndOfDoc()
1486 {
1487 	ContentNode* pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count()-1 );
1488 	ParaPortion* pLastPortion = GetParaPortions().SaveGetObject( aEditDoc.Count()-1 );
1489 	DBG_ASSERT( pLastNode && pLastPortion, "CursorEndOfDoc: Node oder Portion nicht gefunden" );
1490 
1491 	if ( !pLastPortion->IsVisible() )
1492 	{
1493 		pLastNode = GetPrevVisNode( pLastPortion->GetNode() );
1494 		DBG_ASSERT( pLastNode, "Kein sichtbarer Absatz?" );
1495 		if ( !pLastNode )
1496 			pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count()-1 );
1497 	}
1498 
1499 	EditPaM aPaM( pLastNode, pLastNode->Len() );
1500 	return aPaM;
1501 }
1502 
PageUp(const EditPaM & rPaM,EditView * pView)1503 EditPaM ImpEditEngine::PageUp( const EditPaM& rPaM, EditView* pView )
1504 {
1505 	Rectangle aRec = PaMtoEditCursor( rPaM );
1506 	Point aTopLeft = aRec.TopLeft();
1507 	aTopLeft.Y() -= pView->GetVisArea().GetHeight() *9/10;
1508 	aTopLeft.X() += nOnePixelInRef;
1509 	if ( aTopLeft.Y() < 0 )
1510 	{
1511 		aTopLeft.Y() = 0;
1512 	}
1513 	return GetPaM( aTopLeft );
1514 }
1515 
PageDown(const EditPaM & rPaM,EditView * pView)1516 EditPaM ImpEditEngine::PageDown( const EditPaM& rPaM, EditView* pView )
1517 {
1518 	Rectangle aRec = PaMtoEditCursor( rPaM );
1519 	Point aBottomRight = aRec.BottomRight();
1520 	aBottomRight.Y() += pView->GetVisArea().GetHeight() *9/10;
1521 	aBottomRight.X() += nOnePixelInRef;
1522 	long nHeight = GetTextHeight();
1523 	if ( aBottomRight.Y() > nHeight )
1524 	{
1525 		aBottomRight.Y() = nHeight-2;
1526 	}
1527 	return GetPaM( aBottomRight );
1528 }
1529 
WordLeft(const EditPaM & rPaM,sal_Int16 nWordType)1530 EditPaM ImpEditEngine::WordLeft( const EditPaM& rPaM, sal_Int16 nWordType )
1531 {
1532 	sal_uInt16 nCurrentPos = rPaM.GetIndex();
1533 	EditPaM aNewPaM( rPaM );
1534 	if ( nCurrentPos == 0 )
1535 	{
1536 		// Vorheriger Absatz...
1537 		sal_uInt32 nCurPara = aEditDoc.GetPos( aNewPaM.GetNode() );
1538 		ContentNode* pPrevNode = aEditDoc.SaveGetObject( --nCurPara );
1539 		if ( pPrevNode )
1540 		{
1541 			aNewPaM.SetNode( pPrevNode );
1542 			aNewPaM.SetIndex( pPrevNode->Len() );
1543 		}
1544 	}
1545 	else
1546 	{
1547 		// we need to increase the position by 1 when retrieving the locale
1548 		// since the attribute for the char left to the cursor position is returned
1549 		EditPaM aTmpPaM( aNewPaM );
1550 		xub_StrLen nMax = rPaM.GetNode()->Len();
1551 		if ( aTmpPaM.GetIndex() < nMax )
1552 			aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
1553         lang::Locale aLocale( GetLocale( aTmpPaM ) );
1554 
1555 		uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1556 		i18n::Boundary aBoundary = _xBI->getWordBoundary( *aNewPaM.GetNode(), nCurrentPos, aLocale, nWordType, sal_True );
1557 		if ( aBoundary.startPos >= nCurrentPos )
1558             aBoundary = _xBI->previousWord( *aNewPaM.GetNode(), nCurrentPos, aLocale, nWordType );
1559         aNewPaM.SetIndex( ( aBoundary.startPos != (-1) ) ? (sal_uInt16)aBoundary.startPos : 0 );
1560 	}
1561 
1562 	return aNewPaM;
1563 }
1564 
WordRight(const EditPaM & rPaM,sal_Int16 nWordType)1565 EditPaM ImpEditEngine::WordRight( const EditPaM& rPaM, sal_Int16 nWordType )
1566 {
1567 	xub_StrLen nMax = rPaM.GetNode()->Len();
1568 	EditPaM aNewPaM( rPaM );
1569 	if ( aNewPaM.GetIndex() < nMax )
1570 	{
1571 		// we need to increase the position by 1 when retrieving the locale
1572 		// since the attribute for the char left to the cursor position is returned
1573 		EditPaM aTmpPaM( aNewPaM );
1574         aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
1575         lang::Locale aLocale( GetLocale( aTmpPaM ) );
1576 
1577 		uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1578         i18n::Boundary aBoundary = _xBI->nextWord( *aNewPaM.GetNode(), aNewPaM.GetIndex(), aLocale, nWordType );
1579 		aNewPaM.SetIndex( (sal_uInt16)aBoundary.startPos );
1580 	}
1581 	// not 'else', maybe the index reached nMax now...
1582 	if ( aNewPaM.GetIndex() >= nMax )
1583 	{
1584 		// Naechster Absatz...
1585 		sal_uInt32 nCurPara = aEditDoc.GetPos( aNewPaM.GetNode() );
1586 		ContentNode* pNextNode = aEditDoc.SaveGetObject( ++nCurPara );
1587 		if ( pNextNode )
1588 		{
1589 			aNewPaM.SetNode( pNextNode );
1590 			aNewPaM.SetIndex( 0 );
1591 		}
1592 	}
1593 	return aNewPaM;
1594 }
1595 
StartOfWord(const EditPaM & rPaM,sal_Int16 nWordType)1596 EditPaM ImpEditEngine::StartOfWord( const EditPaM& rPaM, sal_Int16 nWordType )
1597 {
1598 	EditPaM aNewPaM( rPaM );
1599 
1600 	// we need to increase the position by 1 when retrieving the locale
1601 	// since the attribute for the char left to the cursor position is returned
1602 	EditPaM aTmpPaM( aNewPaM );
1603 	xub_StrLen nMax = rPaM.GetNode()->Len();
1604 	if ( aTmpPaM.GetIndex() < nMax )
1605 		aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
1606     lang::Locale aLocale( GetLocale( aTmpPaM ) );
1607 
1608 	uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1609 	i18n::Boundary aBoundary = _xBI->getWordBoundary( *rPaM.GetNode(), rPaM.GetIndex(), aLocale, nWordType, sal_True );
1610 	aNewPaM.SetIndex( (sal_uInt16)aBoundary.startPos );
1611 	return aNewPaM;
1612 }
1613 
EndOfWord(const EditPaM & rPaM,sal_Int16 nWordType)1614 EditPaM ImpEditEngine::EndOfWord( const EditPaM& rPaM, sal_Int16 nWordType )
1615 {
1616 	EditPaM aNewPaM( rPaM );
1617 
1618 	// we need to increase the position by 1 when retrieving the locale
1619 	// since the attribute for the char left to the cursor position is returned
1620 	EditPaM aTmpPaM( aNewPaM );
1621 	xub_StrLen nMax = rPaM.GetNode()->Len();
1622 	if ( aTmpPaM.GetIndex() < nMax )
1623 		aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
1624     lang::Locale aLocale( GetLocale( aTmpPaM ) );
1625 
1626 	uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1627 	i18n::Boundary aBoundary = _xBI->getWordBoundary( *rPaM.GetNode(), rPaM.GetIndex(), aLocale, nWordType, sal_True );
1628 	aNewPaM.SetIndex( (sal_uInt16)aBoundary.endPos );
1629 	return aNewPaM;
1630 }
1631 
SelectWord(const EditSelection & rCurSel,sal_Int16 nWordType,sal_Bool bAcceptStartOfWord)1632 EditSelection ImpEditEngine::SelectWord( const EditSelection& rCurSel, sal_Int16 nWordType, sal_Bool bAcceptStartOfWord )
1633 {
1634     EditSelection aNewSel( rCurSel );
1635 	EditPaM aPaM( rCurSel.Max() );
1636 
1637 	// we need to increase the position by 1 when retrieving the locale
1638 	// since the attribute for the char left to the cursor position is returned
1639 	EditPaM aTmpPaM( aPaM );
1640 	xub_StrLen nMax = aPaM.GetNode()->Len();
1641 	if ( aTmpPaM.GetIndex() < nMax )
1642 		aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
1643     lang::Locale aLocale( GetLocale( aTmpPaM ) );
1644 
1645 	uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1646     sal_Int16 nType = _xBI->getWordType( *aPaM.GetNode(), aPaM.GetIndex(), aLocale );
1647 	if ( nType == i18n::WordType::ANY_WORD )
1648 	{
1649 		i18n::Boundary aBoundary = _xBI->getWordBoundary( *aPaM.GetNode(), aPaM.GetIndex(), aLocale, nWordType, sal_True );
1650 		// don't select when curser at end of word
1651 		if ( ( aBoundary.endPos > aPaM.GetIndex() ) &&
1652 			 ( ( aBoundary.startPos < aPaM.GetIndex() ) || ( bAcceptStartOfWord && ( aBoundary.startPos == aPaM.GetIndex() ) ) ) )
1653 		{
1654 			aNewSel.Min().SetIndex( (sal_uInt16)aBoundary.startPos );
1655 			aNewSel.Max().SetIndex( (sal_uInt16)aBoundary.endPos );
1656 		}
1657 	}
1658 
1659 	return aNewSel;
1660 }
1661 
SelectSentence(const EditSelection & rCurSel)1662 EditSelection ImpEditEngine::SelectSentence( const EditSelection& rCurSel )
1663 {
1664     uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1665     const EditPaM& rPaM = rCurSel.Min();
1666     const ContentNode* pNode = rPaM.GetNode();
1667     // #i50710# line breaks are marked with 0x01 - the break iterator prefers 0x0a for that
1668     String sParagraph(*pNode);
1669     sParagraph.SearchAndReplaceAll(0x01,0x0a);
1670     //return Null if search starts at the beginning of the string
1671     long nStart = rPaM.GetIndex() ? _xBI->beginOfSentence( sParagraph, rPaM.GetIndex(), GetLocale( rPaM ) ) : 0;
1672 
1673     long nEnd = _xBI->endOfSentence( *pNode, rPaM.GetIndex(), GetLocale( rPaM ) );
1674     EditSelection aNewSel( rCurSel );
1675     DBG_ASSERT(nStart < pNode->Len() && nEnd <= pNode->Len(), "sentence indices out of range");
1676     aNewSel.Min().SetIndex( (sal_uInt16)nStart );
1677     aNewSel.Max().SetIndex( (sal_uInt16)nEnd );
1678     return aNewSel;
1679 }
1680 
IsInputSequenceCheckingRequired(sal_Unicode nChar,const EditSelection & rCurSel) const1681 sal_Bool ImpEditEngine::IsInputSequenceCheckingRequired( sal_Unicode nChar, const EditSelection& rCurSel ) const
1682 {
1683     uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1684     if (!pCTLOptions)
1685         pCTLOptions = new SvtCTLOptions;
1686 
1687     // get the index that really is first
1688     sal_uInt16 nFirstPos = rCurSel.Min().GetIndex();
1689     sal_uInt16 nMaxPos   = rCurSel.Max().GetIndex();
1690     if (nMaxPos < nFirstPos)
1691         nFirstPos = nMaxPos;
1692 
1693     sal_Bool bIsSequenceChecking =
1694         pCTLOptions->IsCTLFontEnabled() &&
1695         pCTLOptions->IsCTLSequenceChecking() &&
1696         nFirstPos != 0 && /* first char needs not to be checked */
1697         _xBI.is() && i18n::ScriptType::COMPLEX == _xBI->getScriptType( rtl::OUString( nChar ), 0 );
1698 
1699     return bIsSequenceChecking;
1700 }
1701 
1702 /*************************************************************************
1703  *                 lcl_HasStrongLTR
1704  *************************************************************************/
lcl_HasStrongLTR(const String & rTxt,xub_StrLen nStart,xub_StrLen nEnd)1705  bool lcl_HasStrongLTR ( const String& rTxt, xub_StrLen nStart, xub_StrLen nEnd )
1706  {
1707      for ( xub_StrLen nCharIdx = nStart; nCharIdx < nEnd; ++nCharIdx )
1708      {
1709          const UCharDirection nCharDir = u_charDirection ( rTxt.GetChar ( nCharIdx ));
1710          if ( nCharDir == U_LEFT_TO_RIGHT ||
1711               nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
1712               nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
1713              return true;
1714      }
1715      return false;
1716  }
1717 
1718 
1719 
InitScriptTypes(sal_uInt32 nPara)1720 void ImpEditEngine::InitScriptTypes( sal_uInt32 nPara )
1721 {
1722 	ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
1723 	ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
1724 	rTypes.clear();
1725 
1726 	ContentNode* pNode = pParaPortion->GetNode();
1727 	if ( pNode->Len() )
1728 	{
1729 		uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1730 
1731 		String aText( *pNode );
1732 
1733 		// To handle fields put the character from the field in the string,
1734 		// because endOfScript( ... ) will skip the CH_FEATURE, because this is WEAK
1735 		EditCharAttrib* pField = pNode->GetCharAttribs().FindNextAttrib( EE_FEATURE_FIELD, 0 );
1736 		while ( pField )
1737 		{
1738 			::rtl::OUString aFldText( ((EditCharAttribField*)pField)->GetFieldValue() );
1739 			if ( aFldText.getLength() )
1740 			{
1741 				aText.SetChar( pField->GetStart(), aFldText.getStr()[0] );
1742 				short nFldScriptType = _xBI->getScriptType( aFldText, 0 );
1743 
1744 				for ( sal_uInt16 nCharInField = 1; nCharInField < aFldText.getLength(); nCharInField++ )
1745 				{
1746 					short nTmpType = _xBI->getScriptType( aFldText, nCharInField );
1747 
1748 					// First char from field wins...
1749 					if ( nFldScriptType == i18n::ScriptType::WEAK )
1750 					{
1751 						nFldScriptType = nTmpType;
1752 						aText.SetChar( pField->GetStart(), aFldText.getStr()[nCharInField] );
1753 					}
1754 
1755 					// ...  but if the first one is LATIN, and there are CJK or CTL chars too,
1756 					// we prefer that ScripType because we need an other font.
1757 					if ( ( nTmpType == i18n::ScriptType::ASIAN ) || ( nTmpType == i18n::ScriptType::COMPLEX ) )
1758 					{
1759 						aText.SetChar( pField->GetStart(), aFldText.getStr()[nCharInField] );
1760 						break;
1761 					}
1762 				}
1763 			}
1764 			// #112831# Last Field might go from 0xffff to 0x0000
1765 			pField = pField->GetEnd() ? pNode->GetCharAttribs().FindNextAttrib( EE_FEATURE_FIELD, pField->GetEnd() ) : NULL;
1766 		}
1767 
1768 		::rtl::OUString aOUText( aText );
1769 		sal_uInt16 nTextLen = (sal_uInt16)aOUText.getLength();
1770 
1771 		sal_Int32 nPos = 0;
1772 		short nScriptType = _xBI->getScriptType( aOUText, nPos );
1773 		rTypes.push_back( ScriptTypePosInfo( nScriptType, (sal_uInt16)nPos, nTextLen ) );
1774 		nPos = _xBI->endOfScript( aOUText, nPos, nScriptType );
1775 		while ( ( nPos != (-1) ) && ( nPos < nTextLen ) )
1776 		{
1777 			rTypes.back().nEndPos = (sal_uInt16)nPos;
1778 
1779             nScriptType = _xBI->getScriptType( aOUText, nPos );
1780 			long nEndPos = _xBI->endOfScript( aOUText, nPos, nScriptType );
1781 
1782             if ( ( nScriptType == i18n::ScriptType::WEAK ) || ( nScriptType == rTypes.back().nScriptType ) )
1783             {
1784                 // Expand last ScriptTypePosInfo, don't create weak or unnecessary portions
1785                 rTypes.back().nEndPos = (sal_uInt16)nEndPos;
1786             }
1787             else
1788             {
1789                 if ( _xBI->getScriptType( aOUText, nPos - 1 ) == i18n::ScriptType::WEAK )
1790                 {
1791                     switch ( u_charType(aOUText.iterateCodePoints(&nPos, 0) ) ) {
1792                     case U_NON_SPACING_MARK:
1793                     case U_ENCLOSING_MARK:
1794                     case U_COMBINING_SPACING_MARK:
1795                         --nPos;
1796                         rTypes.back().nEndPos--;
1797                         break;
1798                     }
1799                 }
1800 			    rTypes.push_back( ScriptTypePosInfo( nScriptType, (sal_uInt16)nPos, nTextLen ) );
1801             }
1802 
1803 			nPos = nEndPos;
1804 		}
1805 
1806 		if ( rTypes[0].nScriptType == i18n::ScriptType::WEAK )
1807 			rTypes[0].nScriptType = ( rTypes.size() > 1 ) ? rTypes[1].nScriptType : GetI18NScriptTypeOfLanguage( GetDefaultLanguage() );
1808 
1809 		// create writing direction information:
1810 		if ( pParaPortion->aWritingDirectionInfos.empty() )
1811 			InitWritingDirections( nPara );
1812 
1813 		// i89825: Use CTL font for numbers embedded into an RTL run:
1814         WritingDirectionInfos& rDirInfos = pParaPortion->aWritingDirectionInfos;
1815         for ( size_t n = 0; n < rDirInfos.size(); ++n )
1816         {
1817             const xub_StrLen nStart = rDirInfos[n].nStartPos;
1818             const xub_StrLen nEnd   = rDirInfos[n].nEndPos;
1819             const sal_uInt8 nCurrDirType = rDirInfos[n].nType;
1820 
1821             if ( nCurrDirType % 2 == UBIDI_RTL  || // text in RTL run
1822                 ( nCurrDirType > UBIDI_LTR && !lcl_HasStrongLTR( aText, nStart, nEnd ) ) ) // non-strong text in embedded LTR run
1823             {
1824                 size_t nIdx = 0;
1825 
1826                 // Skip entries in ScriptArray which are not inside the RTL run:
1827                 while ( nIdx < rTypes.size() && rTypes[nIdx].nStartPos < nStart )
1828                     ++nIdx;
1829 
1830                 // Remove any entries *inside* the current run:
1831                 while ( nIdx < rTypes.size() && rTypes[nIdx].nEndPos <= nEnd )
1832                     rTypes.erase( rTypes.begin()+nIdx );
1833 
1834                 // special case:
1835                 if(nIdx < rTypes.size() && rTypes[nIdx].nStartPos < nStart && rTypes[nIdx].nEndPos > nEnd)
1836                 {
1837                     rTypes.insert( rTypes.begin()+nIdx, ScriptTypePosInfo( rTypes[nIdx].nScriptType, (sal_uInt16)nEnd, rTypes[nIdx].nEndPos ) );
1838                     rTypes[nIdx].nEndPos = nStart;
1839                 }
1840 
1841                 if( nIdx )
1842                     rTypes[nIdx - 1].nEndPos = nStart;
1843 
1844                 rTypes.insert( rTypes.begin()+nIdx, ScriptTypePosInfo( i18n::ScriptType::COMPLEX, (sal_uInt16)nStart, (sal_uInt16)nEnd) );
1845                 ++nIdx;
1846 
1847                 if( nIdx < rTypes.size() )
1848                     rTypes[nIdx].nStartPos = nEnd;
1849             }
1850         }
1851 
1852 #if OSL_DEBUG_LEVEL > 1
1853         sal_uInt16 nDebugStt = 0;
1854         sal_uInt16 nDebugEnd = 0;
1855         short nDebugType = 0;
1856         for ( size_t n = 0; n < rTypes.size(); ++n )
1857         {
1858             nDebugStt  = rTypes[n].nStartPos;
1859             nDebugEnd  = rTypes[n].nEndPos;
1860             nDebugType = rTypes[n].nScriptType;
1861         }
1862 #endif
1863     }
1864 }
1865 
GetScriptType(const EditPaM & rPaM,sal_uInt16 * pEndPos) const1866 sal_uInt16 ImpEditEngine::GetScriptType( const EditPaM& rPaM, sal_uInt16* pEndPos ) const
1867 {
1868 	sal_uInt16 nScriptType = 0;
1869 
1870 	if ( pEndPos )
1871 		*pEndPos = rPaM.GetNode()->Len();
1872 
1873 	if ( rPaM.GetNode()->Len() )
1874 	{
1875  		sal_uInt32 nPara = GetEditDoc().GetPos( rPaM.GetNode() );
1876 		ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
1877 		if ( pParaPortion->aScriptInfos.empty() )
1878 			((ImpEditEngine*)this)->InitScriptTypes( nPara );
1879 
1880 		ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
1881 		sal_uInt16 nPos = rPaM.GetIndex();
1882 		for ( size_t n = 0; n < rTypes.size(); n++ )
1883 		{
1884 			if ( ( rTypes[n].nStartPos <= nPos ) && ( rTypes[n].nEndPos >= nPos ) )
1885 	   		{
1886 				nScriptType = rTypes[n].nScriptType;
1887 				if( pEndPos )
1888 					*pEndPos = rTypes[n].nEndPos;
1889 				break;
1890 			}
1891 		}
1892 	}
1893 	return nScriptType ? nScriptType : GetI18NScriptTypeOfLanguage( GetDefaultLanguage() );
1894 }
1895 
GetScriptType(const EditSelection & rSel) const1896 sal_uInt16 ImpEditEngine::GetScriptType( const EditSelection& rSel ) const
1897 {
1898 	EditSelection aSel( rSel );
1899 	aSel.Adjust( aEditDoc );
1900 
1901 	short nScriptType = 0;
1902 
1903  	sal_uInt32 nStartPara = GetEditDoc().GetPos( aSel.Min().GetNode() );
1904  	sal_uInt32 nEndPara = GetEditDoc().GetPos( aSel.Max().GetNode() );
1905 
1906 	for ( sal_uInt32 nPara = nStartPara; nPara <= nEndPara; nPara++ )
1907 	{
1908 		ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
1909 		if ( pParaPortion->aScriptInfos.empty() )
1910 			((ImpEditEngine*)this)->InitScriptTypes( nPara );
1911 
1912 		ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
1913 
1914 		// find the first(!) script type position that holds the
1915 		// complete selection. Thus it will work for selections as
1916 		// well as with just moving the cursor from char to char.
1917 		sal_uInt16 nS = ( nPara == nStartPara ) ? aSel.Min().GetIndex() : 0;
1918 		sal_uInt16 nE = ( nPara == nEndPara ) ? aSel.Max().GetIndex() : pParaPortion->GetNode()->Len();
1919 		for ( size_t n = 0; n < rTypes.size(); n++ )
1920 		{
1921             if (rTypes[n].nStartPos <= nS  &&  nE <= rTypes[n].nEndPos)
1922 		   	{
1923 				if ( rTypes[n].nScriptType != i18n::ScriptType::WEAK )
1924 				{
1925                     nScriptType |= GetItemScriptType ( rTypes[n].nScriptType );
1926                 }
1927                 else
1928 				{
1929                     if ( !nScriptType && n )
1930                     {
1931                         // #93548# When starting with WEAK, use prev ScriptType...
1932                         nScriptType = rTypes[n-1].nScriptType;
1933                     }
1934 				}
1935 				break;
1936 			}
1937 		}
1938 	}
1939 	return nScriptType ? nScriptType : GetI18NScriptTypeOfLanguage( GetDefaultLanguage() );
1940 }
1941 
IsScriptChange(const EditPaM & rPaM) const1942 sal_Bool ImpEditEngine::IsScriptChange( const EditPaM& rPaM ) const
1943 {
1944 	sal_Bool bScriptChange = sal_False;
1945 
1946 	if ( rPaM.GetNode()->Len() )
1947 	{
1948 		sal_uInt32 nPara = GetEditDoc().GetPos( rPaM.GetNode() );
1949 		ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
1950 		if ( pParaPortion->aScriptInfos.empty() )
1951 			((ImpEditEngine*)this)->InitScriptTypes( nPara );
1952 
1953 		ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
1954 		sal_uInt16 nPos = rPaM.GetIndex();
1955 		for ( size_t n = 0; n < rTypes.size(); n++ )
1956 		{
1957 			if ( rTypes[n].nStartPos == nPos )
1958 	   		{
1959 				bScriptChange = sal_True;
1960 				break;
1961 			}
1962 		}
1963 	}
1964 	return bScriptChange;
1965 }
1966 
HasScriptType(sal_uInt32 nPara,sal_uInt16 nType) const1967 sal_Bool ImpEditEngine::HasScriptType( sal_uInt32 nPara, sal_uInt16 nType ) const
1968 {
1969     sal_Bool bTypeFound = sal_False;
1970 
1971 	ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
1972 	if ( pParaPortion->aScriptInfos.empty() )
1973 		((ImpEditEngine*)this)->InitScriptTypes( nPara );
1974 
1975     ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
1976 	for ( size_t n = rTypes.size(); n && !bTypeFound; )
1977 	{
1978 	    if ( rTypes[--n].nScriptType == nType )
1979                 bTypeFound = sal_True;
1980     }
1981     return bTypeFound;
1982 }
1983 
InitWritingDirections(sal_uInt32 nPara)1984 void ImpEditEngine::InitWritingDirections( sal_uInt32 nPara )
1985 {
1986 	ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
1987 	WritingDirectionInfos& rInfos = pParaPortion->aWritingDirectionInfos;
1988 	rInfos.clear();
1989 
1990     sal_Bool bCTL = sal_False;
1991 	ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
1992 	for ( size_t n = 0; n < rTypes.size(); n++ )
1993 	{
1994 		if ( rTypes[n].nScriptType == i18n::ScriptType::COMPLEX )
1995 	   	{
1996 			bCTL = sal_True;
1997 			break;
1998 		}
1999 	}
2000 
2001     const UBiDiLevel nBidiLevel = IsRightToLeft( nPara ) ? 1 /*RTL*/ : 0 /*LTR*/;
2002     if ( ( bCTL || ( nBidiLevel == 1 /*RTL*/ ) ) && pParaPortion->GetNode()->Len() )
2003 	{
2004 
2005         String aText( *pParaPortion->GetNode() );
2006 
2007         //
2008         // Bidi functions from icu 2.0
2009         //
2010         UErrorCode nError = U_ZERO_ERROR;
2011         UBiDi* pBidi = ubidi_openSized( aText.Len(), 0, &nError );
2012         nError = U_ZERO_ERROR;
2013 
2014         ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aText.GetBuffer()), aText.Len(), nBidiLevel, NULL, &nError );	// UChar != sal_Unicode in MinGW
2015         nError = U_ZERO_ERROR;
2016 
2017         size_t nCount = ubidi_countRuns( pBidi, &nError );
2018 
2019         int32_t nStart = 0;
2020         int32_t nEnd;
2021         UBiDiLevel nCurrDir;
2022 
2023         for ( size_t nIdx = 0; nIdx < nCount; ++nIdx )
2024         {
2025             ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir );
2026             rInfos.push_back( WritingDirectionInfo( nCurrDir, (sal_uInt16)nStart, (sal_uInt16)nEnd ) );
2027             nStart = nEnd;
2028         }
2029 
2030         ubidi_close( pBidi );
2031 	}
2032 
2033     // No infos mean no CTL and default dir is L2R...
2034     if ( rInfos.empty() )
2035         rInfos.push_back( WritingDirectionInfo( 0, 0, (sal_uInt16)pParaPortion->GetNode()->Len() ) );
2036 
2037 }
2038 
IsRightToLeft(sal_uInt32 nPara) const2039 sal_Bool ImpEditEngine::IsRightToLeft( sal_uInt32 nPara ) const
2040 {
2041     sal_Bool bR2L = sal_False;
2042     const SvxFrameDirectionItem* pFrameDirItem = NULL;
2043 
2044     if ( !IsVertical() )
2045     {
2046         bR2L = GetDefaultHorizontalTextDirection() == EE_HTEXTDIR_R2L;
2047         pFrameDirItem = &(const SvxFrameDirectionItem&)GetParaAttrib( nPara, EE_PARA_WRITINGDIR );
2048         if ( pFrameDirItem->GetValue() == FRMDIR_ENVIRONMENT )
2049         {
2050             // #103045# if DefaultHorizontalTextDirection is set, use that value, otherwise pool default.
2051             if ( GetDefaultHorizontalTextDirection() != EE_HTEXTDIR_DEFAULT )
2052             {
2053                 pFrameDirItem = NULL; // bR2L already set to default horizontal text direction
2054             }
2055             else
2056             {
2057                 // Use pool default
2058                 pFrameDirItem = &(const SvxFrameDirectionItem&)((ImpEditEngine*)this)->GetEmptyItemSet().Get( EE_PARA_WRITINGDIR );
2059             }
2060         }
2061     }
2062 
2063     if ( pFrameDirItem )
2064         bR2L = pFrameDirItem->GetValue() == FRMDIR_HORI_RIGHT_TOP;
2065 
2066     return bR2L;
2067 }
2068 
HasDifferentRTLLevels(const ContentNode * pNode)2069 sal_Bool ImpEditEngine::HasDifferentRTLLevels( const ContentNode* pNode )
2070 {
2071     sal_uInt32 nPara = GetEditDoc().GetPos( (ContentNode*)pNode );
2072 	ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
2073 
2074     sal_Bool bHasDifferentRTLLevels = sal_False;
2075 
2076     sal_uInt16 nRTLLevel = IsRightToLeft( nPara ) ? 1 : 0;
2077 	for ( sal_uInt16 n = 0; n < pParaPortion->GetTextPortions().Count(); n++ )
2078 	{
2079 		TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( n );
2080         if ( pTextPortion->GetRightToLeft() != nRTLLevel )
2081         {
2082             bHasDifferentRTLLevels = sal_True;
2083             break;
2084         }
2085 	}
2086     return bHasDifferentRTLLevels;
2087 }
2088 
2089 
GetRightToLeft(sal_uInt32 nPara,sal_uInt16 nPos,sal_uInt16 * pStart,sal_uInt16 * pEnd)2090 sal_uInt8 ImpEditEngine::GetRightToLeft( sal_uInt32 nPara, sal_uInt16 nPos, sal_uInt16* pStart, sal_uInt16* pEnd )
2091 {
2092 //    sal_uInt8 nRightToLeft = IsRightToLeft( nPara ) ? 1 : 0;
2093     sal_uInt8 nRightToLeft = 0;
2094 
2095     ContentNode* pNode = aEditDoc.SaveGetObject( nPara );
2096     if ( pNode && pNode->Len() )
2097     {
2098 		ParaPortion* pParaPortion = GetParaPortions().SaveGetObject( nPara );
2099 		if ( pParaPortion->aWritingDirectionInfos.empty() )
2100 			InitWritingDirections( nPara );
2101 
2102 //        sal_uInt8 nType = 0;
2103 		WritingDirectionInfos& rDirInfos = pParaPortion->aWritingDirectionInfos;
2104 		for ( size_t n = 0; n < rDirInfos.size(); n++ )
2105 		{
2106 			if ( ( rDirInfos[n].nStartPos <= nPos ) && ( rDirInfos[n].nEndPos >= nPos ) )
2107 	   		{
2108 				nRightToLeft = rDirInfos[n].nType;
2109                 if ( pStart )
2110                     *pStart = rDirInfos[n].nStartPos;
2111                 if ( pEnd )
2112                     *pEnd = rDirInfos[n].nEndPos;
2113 				break;
2114 			}
2115 		}
2116     }
2117     return nRightToLeft;
2118 }
2119 
GetJustification(sal_uInt32 nPara) const2120 SvxAdjust ImpEditEngine::GetJustification( sal_uInt32 nPara ) const
2121 {
2122 	SvxAdjust eJustification = SVX_ADJUST_LEFT;
2123 
2124     if ( !aStatus.IsOutliner() )
2125     {
2126 		eJustification = ((const SvxAdjustItem&) GetParaAttrib( nPara, EE_PARA_JUST )).GetAdjust();
2127 
2128         if ( IsRightToLeft( nPara ) )
2129         {
2130             if ( eJustification == SVX_ADJUST_LEFT )
2131                 eJustification = SVX_ADJUST_RIGHT;
2132             else if ( eJustification == SVX_ADJUST_RIGHT )
2133                 eJustification = SVX_ADJUST_LEFT;
2134         }
2135     }
2136     return eJustification;
2137 }
2138 
2139 
2140 //	----------------------------------------------------------------------
2141 //	Textaenderung
2142 //	----------------------------------------------------------------------
2143 
ImpRemoveChars(const EditPaM & rPaM,sal_uInt16 nChars,EditUndoRemoveChars * pCurUndo)2144 void ImpEditEngine::ImpRemoveChars( const EditPaM& rPaM, sal_uInt16 nChars, EditUndoRemoveChars* pCurUndo )
2145 {
2146 	if ( IsUndoEnabled() && !IsInUndo() )
2147 	{
2148 		XubString aStr( rPaM.GetNode()->Copy( rPaM.GetIndex(), nChars ) );
2149 
2150 		// Pruefen, ob Attribute geloescht oder geaendert werden:
2151 		sal_uInt16 nStart = rPaM.GetIndex();
2152 		sal_uInt16 nEnd = nStart + nChars;
2153 		CharAttribArray& rAttribs = rPaM.GetNode()->GetCharAttribs().GetAttribs();
2154 //		sal_uInt16 nAttrs = rAttribs.Count();
2155 		for ( sal_uInt16 nAttr = 0; nAttr < rAttribs.Count(); nAttr++ )
2156 		{
2157 			EditCharAttrib* pAttr = rAttribs[nAttr];
2158 			if ( ( pAttr->GetEnd() >= nStart ) && ( pAttr->GetStart() < nEnd ) )
2159 			{
2160 #ifndef SVX_LIGHT
2161 				EditSelection aSel( rPaM );
2162 				aSel.Max().GetIndex() = aSel.Max().GetIndex() + nChars;
2163 				EditUndoSetAttribs* pAttrUndo = CreateAttribUndo( aSel, GetEmptyItemSet() );
2164 				InsertUndo( pAttrUndo );
2165 #endif
2166 				break;	// for
2167 			}
2168 		}
2169 		if ( pCurUndo && ( CreateEditPaM( pCurUndo->GetEPaM() ) == rPaM ) )
2170 			pCurUndo->GetStr() += aStr;
2171 #ifndef SVX_LIGHT
2172 		else
2173 			InsertUndo( new EditUndoRemoveChars( this, CreateEPaM( rPaM ), aStr ) );
2174 #endif
2175 	}
2176 
2177 	aEditDoc.RemoveChars( rPaM, nChars );
2178 	TextModified();
2179 }
2180 
ImpMoveParagraphs(Range aOldPositions,sal_uInt32 nNewPos)2181 EditSelection ImpEditEngine::ImpMoveParagraphs( Range aOldPositions, sal_uInt32 nNewPos )
2182 {
2183 	aOldPositions.Justify();
2184 	sal_Bool bValidAction = ( (long)nNewPos < aOldPositions.Min() ) || ( (long)nNewPos > aOldPositions.Max() );
2185 	DBG_ASSERT( bValidAction, "Move in sich selbst ?" );
2186 	DBG_ASSERT( aOldPositions.Max() <= (long)GetParaPortions().Count(), "Voll drueber weg: MoveParagraphs" );
2187 
2188 	EditSelection aSelection;
2189 
2190 	if ( !bValidAction )
2191 	{
2192 		aSelection = aEditDoc.GetStartPaM();
2193 		return aSelection;
2194 	}
2195 
2196 	sal_uInt32 nParaCount = GetParaPortions().Count();
2197 
2198 	if ( nNewPos >= nParaCount )
2199 		nNewPos = GetParaPortions().Count();
2200 
2201 	// Height may change when moving first or last Paragraph
2202 	ParaPortion* pRecalc1 = NULL;
2203 	ParaPortion* pRecalc2 = NULL;
2204 	ParaPortion* pRecalc3 = NULL;
2205 	ParaPortion* pRecalc4 = NULL;
2206 
2207 	if ( nNewPos == 0 )	// Move to Start
2208 	{
2209 		pRecalc1 = GetParaPortions().GetObject( 0 );
2210 		pRecalc2 = GetParaPortions().GetObject( (sal_uInt32)aOldPositions.Min() );
2211 	}
2212 	else if ( nNewPos == nParaCount )
2213 	{
2214 		pRecalc1 = GetParaPortions().GetObject( (sal_uInt32)(nParaCount-1) );
2215 		pRecalc2 = GetParaPortions().GetObject( (sal_uInt32)aOldPositions.Max() );
2216 	}
2217 
2218 	if ( aOldPositions.Min() == 0 )	// Move from Start
2219 	{
2220 		pRecalc3 = GetParaPortions().GetObject( 0 );
2221 		pRecalc4 = GetParaPortions().GetObject( (sal_uInt32)(aOldPositions.Max()+1) );
2222 	}
2223 	else if ( aOldPositions.Max() == (nParaCount-1) )
2224 	{
2225 		pRecalc3 = GetParaPortions().GetObject( (sal_uInt32)aOldPositions.Max() );
2226 		pRecalc4 = GetParaPortions().GetObject( (sal_uInt32)(aOldPositions.Min()-1) );
2227 	}
2228 
2229 	MoveParagraphsInfo aMoveParagraphsInfo( sal::static_int_cast< sal_uInt32 >(aOldPositions.Min()), sal::static_int_cast< sal_uInt32 >(aOldPositions.Max()), nNewPos );
2230 	aBeginMovingParagraphsHdl.Call( &aMoveParagraphsInfo );
2231 
2232 	if ( IsUndoEnabled() && !IsInUndo())
2233 		InsertUndo( new EditUndoMoveParagraphs( this, aOldPositions, nNewPos ) );
2234 
2235 	// Position nicht aus dem Auge verlieren!
2236 	ParaPortion* pDestPortion = GetParaPortions().SaveGetObject( nNewPos );
2237 
2238 	ParaPortionList aTmpPortionList;
2239 	sal_uInt32 i;
2240 	for ( i = (sal_uInt32)aOldPositions.Min(); i <= (sal_uInt32)aOldPositions.Max(); i++  )
2241 	{
2242 		// Immer aOldPositions.Min(), da Remove().
2243 		ParaPortion* pTmpPortion = GetParaPortions().GetObject( (sal_uInt32)aOldPositions.Min() );
2244 		GetParaPortions().Remove( (sal_uInt32)aOldPositions.Min() );
2245 		aEditDoc.Remove( (sal_uInt32)aOldPositions.Min() );
2246 		aTmpPortionList.Insert( pTmpPortion, aTmpPortionList.Count() );
2247 	}
2248 
2249 	sal_uInt32 nRealNewPos = pDestPortion ? GetParaPortions().GetPos( pDestPortion ) : GetParaPortions().Count();
2250 	DBG_ASSERT( nRealNewPos != USHRT_MAX, "ImpMoveParagraphs: Ungueltige Position!" );
2251 
2252 	for ( i = 0; i < aTmpPortionList.Count(); i++  )
2253 	{
2254 		ParaPortion* pTmpPortion = aTmpPortionList.GetObject( i );
2255 		if ( i == 0 )
2256 			aSelection.Min().SetNode( pTmpPortion->GetNode() );
2257 
2258 		aSelection.Max().SetNode( pTmpPortion->GetNode() );
2259 		aSelection.Max().SetIndex( pTmpPortion->GetNode()->Len() );
2260 
2261 		ContentNode* pN = pTmpPortion->GetNode();
2262 		aEditDoc.Insert( pN, nRealNewPos+i );
2263 
2264 		GetParaPortions().Insert( pTmpPortion, nRealNewPos+i );
2265 	}
2266 
2267     aEndMovingParagraphsHdl.Call( &aMoveParagraphsInfo );
2268 
2269     if ( GetNotifyHdl().IsSet() )
2270     {
2271         EENotify aNotify( EE_NOTIFY_PARAGRAPHSMOVED );
2272         aNotify.pEditEngine = GetEditEnginePtr();
2273         aNotify.nParagraph = nNewPos;
2274         aNotify.nParam1 = sal::static_int_cast< sal_uInt32 >(aOldPositions.Min());
2275         aNotify.nParam2 = sal::static_int_cast< sal_uInt32 >(aOldPositions.Max());
2276         CallNotify( aNotify );
2277     }
2278 
2279 	aEditDoc.SetModified( sal_True );
2280 
2281 	if ( pRecalc1 )
2282 		CalcHeight( pRecalc1 );
2283 	if ( pRecalc2 )
2284 		CalcHeight( pRecalc2 );
2285 	if ( pRecalc3 )
2286 		CalcHeight( pRecalc3 );
2287 	if ( pRecalc4 )
2288 		CalcHeight( pRecalc4 );
2289 
2290 	aTmpPortionList.Remove( 0, aTmpPortionList.Count() );	// wichtig !
2291 
2292 #ifdef EDITDEBUG
2293 	GetParaPortions().DbgCheck(aEditDoc);
2294 #endif
2295 	return aSelection;
2296 }
2297 
2298 
ImpConnectParagraphs(ContentNode * pLeft,ContentNode * pRight,sal_Bool bBackward)2299 EditPaM ImpEditEngine::ImpConnectParagraphs( ContentNode* pLeft, ContentNode* pRight, sal_Bool bBackward )
2300 {
2301 	DBG_ASSERT( pLeft != pRight, "Den gleichen Absatz zusammenfuegen ?" );
2302 	DBG_ASSERT( aEditDoc.GetPos( pLeft ) != USHRT_MAX, "Einzufuegenden Node nicht gefunden(1)" );
2303 	DBG_ASSERT( aEditDoc.GetPos( pRight ) != USHRT_MAX, "Einzufuegenden Node nicht gefunden(2)" );
2304 
2305     // #120020# it is possible that left and right are *not* in the desired order (left/right)
2306     // so correct it. This correction is needed, else an invalid SfxLinkUndoAction will be
2307     // created from ConnectParagraphs below. Assert this situation, it should be corrected by the
2308     // caller.
2309     if(aEditDoc.GetPos( pLeft ) > aEditDoc.GetPos( pRight ))
2310     {
2311         OSL_ENSURE(false, "ImpConnectParagraphs wit wrong order of pLeft/pRight nodes (!)");
2312         std::swap(pLeft, pRight);
2313     }
2314 
2315 	sal_uInt32 nParagraphTobeDeleted = aEditDoc.GetPos( pRight );
2316 	DeletedNodeInfo* pInf = new DeletedNodeInfo( (sal_uLong)pRight, nParagraphTobeDeleted );
2317 	aDeletedNodes.Insert( pInf, aDeletedNodes.Count() );
2318 
2319     GetEditEnginePtr()->ParagraphConnected( aEditDoc.GetPos( pLeft ), aEditDoc.GetPos( pRight ) );
2320 
2321 #ifndef SVX_LIGHT
2322 	if ( IsUndoEnabled() && !IsInUndo() )
2323 	{
2324 		InsertUndo( new EditUndoConnectParas( this,
2325 			aEditDoc.GetPos( pLeft ), pLeft->Len(),
2326 			pLeft->GetContentAttribs().GetItems(), pRight->GetContentAttribs().GetItems(),
2327 			pLeft->GetStyleSheet(), pRight->GetStyleSheet(), bBackward ) );
2328 	}
2329 #endif
2330 
2331 	if ( bBackward )
2332 	{
2333 		pLeft->SetStyleSheet( pRight->GetStyleSheet(), sal_True );
2334 		pLeft->GetContentAttribs().GetItems().Set( pRight->GetContentAttribs().GetItems() );
2335 		pLeft->GetCharAttribs().GetDefFont() = pRight->GetCharAttribs().GetDefFont();
2336 	}
2337 
2338 	ParaAttribsChanged( pLeft );
2339 
2340 	// Erstmal Portions suchen, da pRight nach ConnectParagraphs weg.
2341 	ParaPortion* pLeftPortion = FindParaPortion( pLeft );
2342 	ParaPortion* pRightPortion = FindParaPortion( pRight );
2343 	DBG_ASSERT( pLeftPortion, "Blinde Portion in ImpConnectParagraphs(1)" );
2344 	DBG_ASSERT( pRightPortion, "Blinde Portion in ImpConnectParagraphs(2)" );
2345 	DBG_ASSERT( nParagraphTobeDeleted == GetParaPortions().GetPos( pRightPortion ), "NodePos != PortionPos?" );
2346 
2347 #ifndef SVX_LIGHT
2348 	if ( GetStatus().DoOnlineSpelling() )
2349 	{
2350 		xub_StrLen nEnd = pLeft->Len();
2351 		xub_StrLen nInv = nEnd ? nEnd-1 : nEnd;
2352 		pLeft->GetWrongList()->ClearWrongs( nInv, 0xFFFF, pLeft );	// Evtl. einen wegnehmen
2353 		pLeft->GetWrongList()->MarkInvalid( nInv, nEnd+1 );
2354 		// Falschgeschriebene Woerter ruebernehmen:
2355 		sal_uInt16 nRWrongs = pRight->GetWrongList()->Count();
2356 		for ( sal_uInt16 nW = 0; nW < nRWrongs; nW++ )
2357 		{
2358 			WrongRange aWrong = pRight->GetWrongList()->GetObject( nW );
2359 			if ( aWrong.nStart != 0 ) 	// Nicht ein anschliessender
2360 			{
2361 				aWrong.nStart = aWrong.nStart + nEnd;
2362 				aWrong.nEnd = aWrong.nEnd + nEnd;
2363 				pLeft->GetWrongList()->InsertWrong( aWrong, pLeft->GetWrongList()->Count() );
2364 			}
2365 		}
2366 	}
2367 #endif
2368 
2369 	if ( IsCallParaInsertedOrDeleted() )
2370 		GetEditEnginePtr()->ParagraphDeleted( nParagraphTobeDeleted );
2371 
2372 	EditPaM aPaM = aEditDoc.ConnectParagraphs( pLeft, pRight );
2373 	GetParaPortions().Remove( nParagraphTobeDeleted );
2374 	delete pRightPortion;
2375 
2376 	pLeftPortion->MarkSelectionInvalid( aPaM.GetIndex(), pLeft->Len() );
2377 
2378 	// der rechte Node wird von EditDoc::ConnectParagraphs() geloescht.
2379 
2380 	if ( GetTextRanger() )
2381 	{
2382 		// Durch das zusammenfuegen wird der linke zwar neu formatiert, aber
2383 		// wenn sich dessen Hoehe nicht aendert bekommt die Formatierung die
2384 		// Aenderung der Gesaamthoehe des Textes zu spaet mit...
2385 		for ( sal_uInt32 n = nParagraphTobeDeleted; n < GetParaPortions().Count(); n++ )
2386 		{
2387 			ParaPortion* pPP = GetParaPortions().GetObject( n );
2388 			pPP->MarkSelectionInvalid( 0, pPP->GetNode()->Len() );
2389 			pPP->GetLines().Reset();
2390 		}
2391 	}
2392 
2393 	TextModified();
2394 
2395 	return aPaM;
2396 }
2397 
DeleteLeftOrRight(const EditSelection & rSel,sal_uInt8 nMode,sal_uInt8 nDelMode)2398 EditPaM ImpEditEngine::DeleteLeftOrRight( const EditSelection& rSel, sal_uInt8 nMode, sal_uInt8 nDelMode )
2399 {
2400 	DBG_ASSERT( !EditSelection( rSel ).DbgIsBuggy( aEditDoc ), "Index im Wald in DeleteLeftOrRight" );
2401 
2402 	if ( rSel.HasRange() )	// dann nur Sel. loeschen
2403 		return ImpDeleteSelection( rSel );
2404 
2405 	const EditPaM aCurPos( rSel.Max() );
2406 	EditPaM aDelStart( aCurPos );
2407 	EditPaM aDelEnd( aCurPos );
2408 	if ( nMode == DEL_LEFT )
2409 	{
2410 		if ( nDelMode == DELMODE_SIMPLE )
2411 		{
2412 			aDelStart = CursorLeft( aCurPos, i18n::CharacterIteratorMode::SKIPCHARACTER );
2413 		}
2414 		else if ( nDelMode == DELMODE_RESTOFWORD )
2415 		{
2416 			aDelStart = StartOfWord( aCurPos );
2417 			if ( aDelStart.GetIndex() == aCurPos.GetIndex() )
2418 				aDelStart = WordLeft( aCurPos );
2419 		}
2420 		else	// DELMODE_RESTOFCONTENT
2421 		{
2422 			aDelStart.SetIndex( 0 );
2423 			if ( aDelStart == aCurPos )
2424 			{
2425 				// kompletter Absatz davor
2426 				ContentNode* pPrev = GetPrevVisNode( aCurPos.GetNode() );
2427 				if ( pPrev )
2428 					aDelStart = EditPaM( pPrev, 0 );
2429 			}
2430 		}
2431 	}
2432 	else
2433 	{
2434 		if ( nDelMode == DELMODE_SIMPLE )
2435 		{
2436 			aDelEnd = CursorRight( aCurPos );
2437 		}
2438 		else if ( nDelMode == DELMODE_RESTOFWORD )
2439 		{
2440             aDelEnd = EndOfWord( aCurPos );
2441 
2442             if (aDelEnd.GetIndex() == aCurPos.GetIndex())
2443             {
2444                 const xub_StrLen nLen(aCurPos.GetNode()->Len());
2445 
2446                 // #120020# when 0 == nLen, aDelStart needs to be adapted, not
2447                 // aDelEnd. This would (and did) lead to a wrong order in the
2448                 // ImpConnectParagraphs call later.
2449                 if(nLen)
2450                 {
2451                     // end of para?
2452                     if (aDelEnd.GetIndex() == nLen)
2453                     {
2454                         aDelEnd = WordLeft( aCurPos );
2455                     }
2456                     else // there's still sth to delete on the right
2457                     {
2458                         aDelEnd = EndOfWord( WordRight( aCurPos ) );
2459                         // if there'n no next word...
2460                         if (aDelEnd.GetIndex() == nLen )
2461                         {
2462                             aDelEnd.SetIndex( nLen );
2463                         }
2464                     }
2465                 }
2466                 else
2467                 {
2468                     aDelStart = WordLeft(aCurPos);
2469                 }
2470             }
2471         }
2472 		else	// DELMODE_RESTOFCONTENT
2473 		{
2474 			aDelEnd.SetIndex( aCurPos.GetNode()->Len() );
2475 			if ( aDelEnd == aCurPos )
2476 			{
2477 				// kompletter Absatz dahinter
2478 				ContentNode* pNext = GetNextVisNode( aCurPos.GetNode() );
2479 				if ( pNext )
2480 					aDelEnd = EditPaM( pNext, pNext->Len() );
2481 			}
2482 		}
2483 	}
2484 
2485 	// Bei DELMODE_RESTOFCONTENT reicht bei verschiedenen Nodes
2486 	// kein ConnectParagraphs.
2487 	if ( ( nDelMode == DELMODE_RESTOFCONTENT ) || ( aDelStart.GetNode() == aDelEnd.GetNode() ) )
2488 		return ImpDeleteSelection( EditSelection( aDelStart, aDelEnd ) );
2489 
2490 	// Jetzt entscheiden, ob noch Selektion loeschen (RESTOFCONTENTS)
2491 	sal_Bool bSpecialBackward = ( ( nMode == DEL_LEFT ) && ( nDelMode == DELMODE_SIMPLE ) )
2492 								? sal_True : sal_False;
2493 	if ( aStatus.IsAnyOutliner() )
2494 		bSpecialBackward = sal_False;
2495 
2496 	return ImpConnectParagraphs( aDelStart.GetNode(), aDelEnd.GetNode(), bSpecialBackward );
2497 }
2498 
ImpDeleteSelection(EditSelection aSel)2499 EditPaM ImpEditEngine::ImpDeleteSelection( EditSelection aSel )
2500 {
2501 	if ( !aSel.HasRange() )
2502 		return aSel.Min();
2503 
2504 	aSel.Adjust( aEditDoc );
2505 	EditPaM aStartPaM( aSel.Min() );
2506 	EditPaM aEndPaM( aSel.Max() );
2507 
2508 	CursorMoved( aStartPaM.GetNode() ); // nur damit neu eingestellte Attribute verschwinden...
2509 	CursorMoved( aEndPaM.GetNode() );	// nur damit neu eingestellte Attribute verschwinden...
2510 
2511 	DBG_ASSERT( aStartPaM.GetIndex() <= aStartPaM.GetNode()->Len(), "Index im Wald in ImpDeleteSelection" );
2512 	DBG_ASSERT( aEndPaM.GetIndex() <= aEndPaM.GetNode()->Len(), "Index im Wald in ImpDeleteSelection" );
2513 
2514 	sal_uInt32 nStartNode = aEditDoc.GetPos( aStartPaM.GetNode() );
2515 	sal_uInt32 nEndNode = aEditDoc.GetPos( aEndPaM.GetNode() );
2516 
2517 	DBG_ASSERT( nEndNode != USHRT_MAX, "Start > End ?!" );
2518 	DBG_ASSERT( nStartNode <= nEndNode, "Start > End ?!" );
2519 
2520 	// Alle Nodes dazwischen entfernen....
2521 	for ( sal_uLong z = nStartNode+1; z < nEndNode; z++ )
2522 	{
2523 		// Immer nStartNode+1, wegen Remove()!
2524 		ImpRemoveParagraph( nStartNode+1 );
2525 	}
2526 
2527 	if ( aStartPaM.GetNode() != aEndPaM.GetNode() )
2528 	{
2529 		// Den Rest des StartNodes...
2530 		sal_uInt16 nChars;
2531 		nChars = aStartPaM.GetNode()->Len() - aStartPaM.GetIndex();
2532 		ImpRemoveChars( aStartPaM, nChars );
2533 		ParaPortion* pPortion = FindParaPortion( aStartPaM.GetNode() );
2534 		DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteSelection(3)" );
2535 		pPortion->MarkSelectionInvalid( aStartPaM.GetIndex(), aStartPaM.GetNode()->Len() );
2536 
2537 		// Den Anfang des EndNodes....
2538 		nChars = aEndPaM.GetIndex();
2539 		aEndPaM.SetIndex( 0 );
2540 		ImpRemoveChars( aEndPaM, nChars );
2541 		pPortion = FindParaPortion( aEndPaM.GetNode() );
2542 		DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteSelection(4)" );
2543 		pPortion->MarkSelectionInvalid( 0, aEndPaM.GetNode()->Len() );
2544 		// Zusammenfuegen....
2545 		aStartPaM = ImpConnectParagraphs( aStartPaM.GetNode(), aEndPaM.GetNode() );
2546 	}
2547 	else
2548 	{
2549 		sal_uInt16 nChars;
2550 		nChars = aEndPaM.GetIndex() - aStartPaM.GetIndex();
2551 		ImpRemoveChars( aStartPaM, nChars );
2552 		ParaPortion* pPortion = FindParaPortion( aStartPaM.GetNode() );
2553 		DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteSelection(5)" );
2554 		pPortion->MarkInvalid( aEndPaM.GetIndex(), aStartPaM.GetIndex() - aEndPaM.GetIndex() );
2555 	}
2556 
2557 	UpdateSelections();
2558 	TextModified();
2559 	return aStartPaM;
2560 }
2561 
ImpRemoveParagraph(sal_uInt32 nPara)2562 void ImpEditEngine::ImpRemoveParagraph( sal_uInt32 nPara )
2563 {
2564 	ContentNode* pNode = aEditDoc.SaveGetObject( nPara );
2565 	ContentNode* pNextNode = aEditDoc.SaveGetObject( nPara+1 );
2566 	ParaPortion* pPortion = GetParaPortions().SaveGetObject( nPara );
2567 
2568 	DBG_ASSERT( pNode, "Blinder Node in ImpRemoveParagraph" );
2569 	DBG_ASSERT( pPortion, "Blinde Portion in ImpRemoveParagraph(2)" );
2570 
2571 	DeletedNodeInfo* pInf = new DeletedNodeInfo( (sal_uLong)pNode, nPara );
2572 	aDeletedNodes.Insert( pInf, aDeletedNodes.Count() );
2573 
2574 	// Der Node wird vom Undo verwaltet und ggf. zerstoert!
2575 	/* delete */ aEditDoc.Remove( nPara );
2576 	GetParaPortions().Remove( nPara );
2577 	delete pPortion;
2578 
2579 	if ( IsCallParaInsertedOrDeleted() )
2580 	{
2581 		GetEditEnginePtr()->ParagraphDeleted( nPara );
2582 	}
2583 
2584 	// Im folgenden muss ggf. Extra-Space neu ermittelt werden.
2585 	// Bei ParaAttribsChanged wird leider der Absatz neu formatiert,
2586 	// aber diese Methode sollte nicht Zeitkritsch sein!
2587 	if ( pNextNode )
2588 		ParaAttribsChanged( pNextNode );
2589 
2590 #ifndef SVX_LIGHT
2591 	if ( IsUndoEnabled() && !IsInUndo() )
2592 		InsertUndo( new EditUndoDelContent( this, pNode, nPara ) );
2593 	else
2594 #endif
2595 	{
2596 		aEditDoc.RemoveItemsFromPool( pNode );
2597 		if ( pNode->GetStyleSheet() )
2598 			EndListening( *pNode->GetStyleSheet(), sal_False );
2599 		delete pNode;
2600 	}
2601 }
2602 
AutoCorrect(const EditSelection & rCurSel,xub_Unicode c,sal_Bool bOverwrite)2603 EditPaM ImpEditEngine::AutoCorrect( const EditSelection& rCurSel, xub_Unicode c, sal_Bool bOverwrite )
2604 {
2605 	EditSelection aSel( rCurSel );
2606 #ifndef SVX_LIGHT
2607 	SvxAutoCorrect*	pAutoCorrect = SvxAutoCorrCfg::Get()->GetAutoCorrect();
2608 	if ( pAutoCorrect )
2609 	{
2610 		if ( aSel.HasRange() )
2611 			aSel = ImpDeleteSelection( rCurSel );
2612 
2613 		// #i78661 allow application to turn off capitalization of
2614 		// start sentence explicitly.
2615 		// (This is done by setting IsFirstWordCapitalization to sal_False.)
2616 		sal_Bool bOldCptlSttSntnc = pAutoCorrect->IsAutoCorrFlag( CptlSttSntnc );
2617 		if (!IsFirstWordCapitalization())
2618 		{
2619 			ESelection aESel( CreateESel(aSel) );
2620 			EditSelection aFirstWordSel;
2621 			EditSelection aSecondWordSel;
2622 			if (aESel.nEndPara == 0)	// is this the first para?
2623 			{
2624 				// select first word...
2625 				// start by checking if para starts with word.
2626 				aFirstWordSel = SelectWord( CreateSel(ESelection()) );
2627 				if (aFirstWordSel.Min().GetIndex() == 0 && aFirstWordSel.Max().GetIndex() == 0)
2628 				{
2629 					// para does not start with word -> select next/first word
2630 					EditPaM aRightWord( WordRight( aFirstWordSel.Max(), 1 ) );
2631 					aFirstWordSel = SelectWord( EditSelection( aRightWord ) );
2632 				}
2633 
2634 				// select second word
2635 				// (sometimes aSel mightnot point to the end of the first word
2636 				// but to some following char like '.'. ':', ...
2637 				// In those cases we need aSecondWordSel to see if aSel
2638 				// will actually effect the first word.)
2639 				EditPaM aRight2Word( WordRight( aFirstWordSel.Max(), 1 ) );
2640 				aSecondWordSel = SelectWord( EditSelection( aRight2Word ) );
2641 			}
2642 			sal_Bool bIsFirstWordInFirstPara = aESel.nEndPara == 0 &&
2643 					aFirstWordSel.Max().GetIndex() <= aSel.Max().GetIndex() &&
2644 					aSel.Max().GetIndex() <= aSecondWordSel.Min().GetIndex();
2645 
2646 			if (bIsFirstWordInFirstPara)
2647 				pAutoCorrect->SetAutoCorrFlag( CptlSttSntnc, IsFirstWordCapitalization() );
2648 		}
2649 
2650 		ContentNode* pNode = aSel.Max().GetNode();
2651 		sal_uInt16 nIndex = aSel.Max().GetIndex();
2652 		EdtAutoCorrDoc aAuto( this, pNode, nIndex, c );
2653 		pAutoCorrect->AutoCorrect( aAuto, *pNode, nIndex, c, !bOverwrite );
2654 		aSel.Max().SetIndex( aAuto.GetCursor() );
2655 
2656 		// #i78661 since the SvxAutoCorrect object used here is
2657 		// shared we need to reset the value to it's original state.
2658 		pAutoCorrect->SetAutoCorrFlag( CptlSttSntnc, bOldCptlSttSntnc );
2659 	}
2660 #endif // !SVX_LIGHT
2661 	return aSel.Max();
2662 }
2663 
2664 
InsertText(const EditSelection & rCurSel,xub_Unicode c,sal_Bool bOverwrite,sal_Bool bIsUserInput)2665 EditPaM ImpEditEngine::InsertText( const EditSelection& rCurSel,
2666         xub_Unicode c, sal_Bool bOverwrite, sal_Bool bIsUserInput )
2667 {
2668 	DBG_ASSERT( c != '\t', "Tab bei InsertText ?" );
2669 	DBG_ASSERT( c != '\n', "Zeilenumbruch bei InsertText ?" );
2670 
2671 	EditPaM aPaM( rCurSel.Min() );
2672 
2673 	sal_Bool bDoOverwrite = ( bOverwrite &&
2674 			( aPaM.GetIndex() < aPaM.GetNode()->Len() ) ) ? sal_True : sal_False;
2675 
2676 	sal_Bool bUndoAction = ( rCurSel.HasRange() || bDoOverwrite );
2677 
2678 	if ( bUndoAction )
2679 		UndoActionStart( EDITUNDO_INSERT );
2680 
2681 	if ( rCurSel.HasRange() )
2682 	{
2683 		aPaM = ImpDeleteSelection( rCurSel );
2684 	}
2685 	else if ( bDoOverwrite )
2686 	{
2687 		// Wenn Selektion, dann nicht auch noch ein Zeichen ueberschreiben!
2688 		EditSelection aTmpSel( aPaM );
2689 		aTmpSel.Max().GetIndex()++;
2690 		DBG_ASSERT( !aTmpSel.DbgIsBuggy( aEditDoc ), "Overwrite: Fehlerhafte Selektion!" );
2691 		ImpDeleteSelection( aTmpSel );
2692 	}
2693 
2694 	if ( aPaM.GetNode()->Len() < MAXCHARSINPARA )
2695 	{
2696         if (bIsUserInput && IsInputSequenceCheckingRequired( c, rCurSel ))
2697         {
2698             uno::Reference < i18n::XExtendedInputSequenceChecker > _xISC( ImplGetInputSequenceChecker() );
2699             if (!pCTLOptions)
2700                 pCTLOptions = new SvtCTLOptions;
2701 
2702             if (_xISC.is() || pCTLOptions)
2703             {
2704                 xub_StrLen nTmpPos = aPaM.GetIndex();
2705                 sal_Int16 nCheckMode = pCTLOptions->IsCTLSequenceCheckingRestricted() ?
2706                         i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC;
2707 
2708 				// the text that needs to be checked is only the one
2709 				// before the current cursor position
2710                 rtl::OUString aOldText( aPaM.GetNode()->Copy(0, nTmpPos) );
2711                 rtl::OUString aNewText( aOldText );
2712                 if (pCTLOptions->IsCTLSequenceCheckingTypeAndReplace())
2713                 {
2714                     /*const xub_StrLen nPrevPos = static_cast< xub_StrLen >*/( _xISC->correctInputSequence( aNewText, nTmpPos - 1, c, nCheckMode ) );
2715 
2716                     // find position of first character that has changed
2717                     sal_Int32 nOldLen = aOldText.getLength();
2718                     sal_Int32 nNewLen = aNewText.getLength();
2719                     const sal_Unicode *pOldTxt = aOldText.getStr();
2720                     const sal_Unicode *pNewTxt = aNewText.getStr();
2721                     sal_Int32 nChgPos = 0;
2722                     while ( nChgPos < nOldLen && nChgPos < nNewLen &&
2723                             pOldTxt[nChgPos] == pNewTxt[nChgPos] )
2724                         ++nChgPos;
2725 
2726                     xub_StrLen nChgLen = static_cast< xub_StrLen >( nNewLen - nChgPos );
2727 					String aChgText( aNewText.copy( nChgPos ).getStr(), nChgLen );
2728 
2729                     // select text from first pos to be changed to current pos
2730                     EditSelection aSel( EditPaM( aPaM.GetNode(), (sal_uInt16) nChgPos ), aPaM );
2731 
2732 					if (aChgText.Len())
2733 						return InsertText( aSel, aChgText ); // implicitly handles undo
2734 					else
2735 						return aPaM;
2736                 }
2737                 else
2738                 {
2739                     // should the character be ignored (i.e. not get inserted) ?
2740                     if (!_xISC->checkInputSequence( aOldText, nTmpPos - 1, c, nCheckMode ))
2741                         return aPaM;    // nothing to be done -> no need for undo
2742                 }
2743             }
2744 
2745             // at this point now we will insert the character 'normally' some lines below...
2746         }
2747 
2748 		if ( IsUndoEnabled() && !IsInUndo() )
2749 		{
2750 			EditUndoInsertChars* pNewUndo = new EditUndoInsertChars( this, CreateEPaM( aPaM ), c );
2751 			sal_Bool bTryMerge = ( !bDoOverwrite && ( c != ' ' ) ) ? sal_True : sal_False;
2752 			InsertUndo( pNewUndo, bTryMerge );
2753 		}
2754 
2755         aEditDoc.InsertText( (const EditPaM&)aPaM, c );
2756 		ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
2757 		DBG_ASSERT( pPortion, "Blinde Portion in InsertText" );
2758         pPortion->MarkInvalid( aPaM.GetIndex(), 1 );
2759         aPaM.GetIndex()++;   // macht EditDoc-Methode nicht mehr
2760 	}
2761 
2762 	TextModified();
2763 
2764 	if ( bUndoAction )
2765 		UndoActionEnd( EDITUNDO_INSERT );
2766 
2767 	return aPaM;
2768 }
2769 
ImpInsertText(EditSelection aCurSel,const XubString & rStr)2770 EditPaM ImpEditEngine::ImpInsertText( EditSelection aCurSel, const XubString& rStr )
2771 {
2772 	UndoActionStart( EDITUNDO_INSERT );
2773 
2774 	EditPaM aPaM;
2775 	if ( aCurSel.HasRange() )
2776 		aPaM = ImpDeleteSelection( aCurSel );
2777 	else
2778 		aPaM = aCurSel.Max();
2779 
2780 	EditPaM aCurPaM( aPaM );	// fuers Invalidieren
2781 
2782     // get word boundaries in order to clear possible WrongList entries
2783     // and invalidate all the necessary text (everything after and including the
2784     // start of the word)
2785     // #i107201# do the expensive SelectWord call only if online spelling is active
2786     EditSelection aCurWord;
2787     if ( GetStatus().DoOnlineSpelling() )
2788         aCurWord = SelectWord( aCurPaM, i18n::WordType::DICTIONARY_WORD );
2789 
2790 	XubString aText( rStr );
2791 	aText.ConvertLineEnd( LINEEND_LF );
2792 	SfxVoidItem aTabItem( EE_FEATURE_TAB );
2793 
2794 	// Konvertiert nach LineSep = \n
2795 	// Token mit LINE_SEP abfragen,
2796 	// da der MAC-Compiler aus \n etwas anderes macht!
2797 
2798 	// #117400
2799 	// The loop run variable must be capable to hold STRLEN_MAX+1,
2800 	// that with STRING32 would be SAL_MAX_INT32+1 but with 16-bit is 0xFFFF+1
2801 	sal_uInt32 nStart = 0;
2802 	while ( nStart < aText.Len() )
2803 	{
2804 		sal_uInt32 nEnd = aText.Search( LINE_SEP, static_cast<xub_StrLen>(nStart) );
2805 		if ( nEnd == STRING_NOTFOUND )
2806 			nEnd = aText.Len();	// nicht dereferenzieren!
2807 
2808 		// Start == End => Leerzeile
2809 		if ( nEnd > nStart )
2810 		{
2811 			XubString aLine( aText, nStart, static_cast<xub_StrLen>(nEnd-nStart) );
2812 			xub_StrLen nChars = aPaM.GetNode()->Len() + aLine.Len();
2813 			if ( nChars > MAXCHARSINPARA )
2814 			{
2815 				xub_StrLen nMaxNewChars = MAXCHARSINPARA-aPaM.GetNode()->Len();
2816 				nEnd -= ( aLine.Len() - nMaxNewChars );	// Dann landen die Zeichen im naechsten Absatz.
2817 				aLine.Erase( nMaxNewChars );            // Del Rest...
2818 			}
2819 #ifndef SVX_LIGHT
2820 			if ( IsUndoEnabled() && !IsInUndo() )
2821 				InsertUndo( new EditUndoInsertChars( this, CreateEPaM( aPaM ), aLine ) );
2822 #endif
2823 			// Tabs ?
2824 			if ( aLine.Search( '\t' ) == STRING_NOTFOUND )
2825 				aPaM = aEditDoc.InsertText( aPaM, aLine );
2826 			else
2827 			{
2828 				sal_uInt32 nStart2 = 0;
2829 				while ( nStart2 < aLine.Len() )
2830 				{
2831 					sal_uInt32 nEnd2 = aLine.Search( '\t', static_cast<xub_StrLen>(nStart2) );
2832 					if ( nEnd2 == STRING_NOTFOUND )
2833 						nEnd2 = aLine.Len();	// nicht dereferenzieren!
2834 
2835 					if ( nEnd2 > nStart2 )
2836 						aPaM = aEditDoc.InsertText( aPaM, XubString( aLine,
2837 							static_cast<xub_StrLen>(nStart2),
2838 							static_cast<xub_StrLen>(nEnd2-nStart2 ) ) );
2839 					if ( nEnd2 < aLine.Len() )
2840 					{
2841 						// aPaM = ImpInsertFeature( EditSelection( aPaM, aPaM ),  );
2842 						aPaM = aEditDoc.InsertFeature( aPaM, aTabItem );
2843 					}
2844 					nStart2 = nEnd2+1;
2845 				}
2846 			}
2847 			ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
2848 			DBG_ASSERT( pPortion, "Blinde Portion in InsertText" );
2849 
2850             if ( GetStatus().DoOnlineSpelling() )
2851             {
2852                 // now remove the Wrongs (red spell check marks) from both words...
2853                 WrongList *pWrongs = aCurPaM.GetNode()->GetWrongList();
2854                 if (pWrongs && pWrongs->HasWrongs())
2855                     pWrongs->ClearWrongs( aCurWord.Min().GetIndex(), aPaM.GetIndex(), aPaM.GetNode() );
2856                 // ... and mark both words as 'to be checked again'
2857                 pPortion->MarkInvalid( aCurWord.Min().GetIndex(), aLine.Len() );
2858             }
2859             else
2860                 pPortion->MarkInvalid( aCurPaM.GetIndex(), aLine.Len() );
2861 		}
2862 		if ( nEnd < aText.Len() )
2863 			aPaM = ImpInsertParaBreak( aPaM );
2864 
2865 		nStart = nEnd+1;
2866 	}
2867 
2868 	UndoActionEnd( EDITUNDO_INSERT );
2869 
2870 	TextModified();
2871 	return aPaM;
2872 }
2873 
ImpFastInsertText(EditPaM aPaM,const XubString & rStr)2874 EditPaM ImpEditEngine::ImpFastInsertText( EditPaM aPaM, const XubString& rStr )
2875 {
2876 	DBG_ASSERT( rStr.Search( 0x0A ) == STRING_NOTFOUND, "FastInsertText: Zeilentrenner nicht erlaubt!" );
2877 	DBG_ASSERT( rStr.Search( 0x0D ) == STRING_NOTFOUND, "FastInsertText: Zeilentrenner nicht erlaubt!" );
2878 	DBG_ASSERT( rStr.Search( '\t' ) == STRING_NOTFOUND, "FastInsertText: Features nicht erlaubt!" );
2879 
2880 	if ( ( aPaM.GetNode()->Len() + rStr.Len() ) < MAXCHARSINPARA )
2881 	{
2882 #ifndef SVX_LIGHT
2883 		if ( IsUndoEnabled() && !IsInUndo() )
2884 			InsertUndo( new EditUndoInsertChars( this, CreateEPaM( aPaM ), rStr ) );
2885 #endif
2886 
2887 		aPaM = aEditDoc.InsertText( aPaM, rStr );
2888 		TextModified();
2889 	}
2890 	else
2891 	{
2892 		aPaM = ImpInsertText( aPaM, rStr );
2893 	}
2894 
2895 	return aPaM;
2896 }
2897 
ImpInsertFeature(EditSelection aCurSel,const SfxPoolItem & rItem)2898 EditPaM ImpEditEngine::ImpInsertFeature( EditSelection aCurSel, const SfxPoolItem& rItem )
2899 {
2900 	EditPaM aPaM;
2901 	if ( aCurSel.HasRange() )
2902 		aPaM = ImpDeleteSelection( aCurSel );
2903 	else
2904 		aPaM = aCurSel.Max();
2905 
2906 	if ( aPaM.GetIndex() >= 0xfffe )
2907 		return aPaM;
2908 
2909 #ifndef SVX_LIGHT
2910 	if ( IsUndoEnabled() && !IsInUndo() )
2911 		InsertUndo( new EditUndoInsertFeature( this, CreateEPaM( aPaM ), rItem ) );
2912 #endif
2913 	aPaM = aEditDoc.InsertFeature( aPaM, rItem );
2914 
2915 	ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
2916 	DBG_ASSERT( pPortion, "Blinde Portion in InsertFeature" );
2917 	pPortion->MarkInvalid( aPaM.GetIndex()-1, 1 );
2918 
2919 	TextModified();
2920 
2921 	return aPaM;
2922 }
2923 
ImpInsertParaBreak(const EditSelection & rCurSel,sal_Bool bKeepEndingAttribs)2924 EditPaM ImpEditEngine::ImpInsertParaBreak( const EditSelection& rCurSel, sal_Bool bKeepEndingAttribs )
2925 {
2926 	EditPaM aPaM;
2927 	if ( rCurSel.HasRange() )
2928 		aPaM = ImpDeleteSelection( rCurSel );
2929 	else
2930 		aPaM = rCurSel.Max();
2931 
2932 	return ImpInsertParaBreak( aPaM, bKeepEndingAttribs );
2933 }
2934 
ImpInsertParaBreak(const EditPaM & rPaM,sal_Bool bKeepEndingAttribs)2935 EditPaM ImpEditEngine::ImpInsertParaBreak( const EditPaM& rPaM, sal_Bool bKeepEndingAttribs )
2936 {
2937 	if ( aEditDoc.Count() >= (EE_PARA_MAX - 1) )
2938 	{
2939 		DBG_ERROR( "Can't process more than (2^32 - 1) paragraphs!" );
2940 		return rPaM;
2941 	}
2942 
2943 #ifndef SVX_LIGHT
2944 	if ( IsUndoEnabled() && !IsInUndo() )
2945 		InsertUndo( new EditUndoSplitPara( this, aEditDoc.GetPos( rPaM.GetNode() ), rPaM.GetIndex() ) );
2946 #endif
2947 
2948 	EditPaM aPaM( aEditDoc.InsertParaBreak( rPaM, bKeepEndingAttribs ) );
2949 
2950 #ifndef SVX_LIGHT
2951 	if ( GetStatus().DoOnlineSpelling() )
2952 	{
2953 		xub_StrLen nEnd = rPaM.GetNode()->Len();
2954 		aPaM.GetNode()->CreateWrongList();
2955 		WrongList* pLWrongs = rPaM.GetNode()->GetWrongList();
2956 		WrongList* pRWrongs = aPaM.GetNode()->GetWrongList();
2957 		// Falschgeschriebene Woerter ruebernehmen:
2958 		sal_uInt16 nLWrongs = pLWrongs->Count();
2959 		for ( sal_uInt16 nW = 0; nW < nLWrongs; nW++ )
2960 		{
2961 			WrongRange& rWrong = pLWrongs->GetObject( nW );
2962 			// Nur wenn wirklich dahinter, ein ueberlappendes wird beim Spell korrigiert
2963 			if ( rWrong.nStart > nEnd )
2964 			{
2965 				pRWrongs->InsertWrong( rWrong, pRWrongs->Count() );
2966 				WrongRange& rRWrong = pRWrongs->GetObject( pRWrongs->Count() - 1 );
2967 				rRWrong.nStart = rRWrong.nStart - nEnd;
2968 				rRWrong.nEnd = rRWrong.nEnd - nEnd;
2969 			}
2970 			else if ( ( rWrong.nStart < nEnd ) && ( rWrong.nEnd > nEnd ) )
2971 				rWrong.nEnd = nEnd;
2972 		}
2973 		sal_uInt16 nInv = nEnd ? nEnd-1 : nEnd;
2974 		if ( nEnd )
2975 			pLWrongs->MarkInvalid( nInv, nEnd );
2976 		else
2977 			pLWrongs->SetValid();
2978 		pRWrongs->SetValid(); // sonst 0 - 0xFFFF
2979 		pRWrongs->MarkInvalid( 0, 1 );	// Nur das erste Wort testen
2980 	}
2981 #endif // !SVX_LIGHT
2982 
2983 
2984 	ParaPortion* pPortion = FindParaPortion( rPaM.GetNode() );
2985 	DBG_ASSERT( pPortion, "Blinde Portion in ImpInsertParaBreak" );
2986 	pPortion->MarkInvalid( rPaM.GetIndex(), 0 );
2987 
2988 	// Optimieren: Nicht unnoetig viele GetPos auf die Listen ansetzen!
2989 	// Hier z.B. bei Undo, aber auch in allen anderen Methoden.
2990 	sal_uInt32 nPos = GetParaPortions().GetPos( pPortion );
2991 	ParaPortion* pNewPortion = new ParaPortion( aPaM.GetNode() );
2992 	GetParaPortions().Insert( pNewPortion, nPos + 1 );
2993 	ParaAttribsChanged( pNewPortion->GetNode() );
2994 	if ( IsCallParaInsertedOrDeleted() )
2995 		GetEditEnginePtr()->ParagraphInserted( nPos+1 );
2996 
2997 	CursorMoved( rPaM.GetNode() );	// falls leeres Attribut entstanden.
2998 	TextModified();
2999 	return aPaM;
3000 }
3001 
ImpFastInsertParagraph(sal_uInt32 nPara)3002 EditPaM ImpEditEngine::ImpFastInsertParagraph( sal_uInt32 nPara )
3003 {
3004 #ifndef SVX_LIGHT
3005 	if ( IsUndoEnabled() && !IsInUndo() )
3006 	{
3007 		if ( nPara )
3008 		{
3009 			DBG_ASSERT( aEditDoc.SaveGetObject( nPara-1 ), "FastInsertParagraph: Prev existiert nicht" );
3010 			InsertUndo( new EditUndoSplitPara( this, nPara-1, aEditDoc.GetObject( nPara-1 )->Len() ) );
3011 		}
3012 		else
3013 			InsertUndo( new EditUndoSplitPara( this, 0, 0 ) );
3014 	}
3015 #endif
3016 
3017 	ContentNode* pNode = new ContentNode( aEditDoc.GetItemPool() );
3018 	// Falls FlatMode, wird spaeter kein Font eingestellt:
3019 	pNode->GetCharAttribs().GetDefFont() = aEditDoc.GetDefFont();
3020 
3021 #ifndef SVX_LIGHT
3022 	if ( GetStatus().DoOnlineSpelling() )
3023 		pNode->CreateWrongList();
3024 #endif // !SVX_LIGHT
3025 
3026 	aEditDoc.Insert( pNode, nPara );
3027 
3028 	ParaPortion* pNewPortion = new ParaPortion( pNode );
3029 	GetParaPortions().Insert( pNewPortion, nPara );
3030 	if ( IsCallParaInsertedOrDeleted() )
3031 		GetEditEnginePtr()->ParagraphInserted( nPara );
3032 
3033 	return EditPaM( pNode, 0 );
3034 }
3035 
InsertParaBreak(EditSelection aCurSel)3036 EditPaM ImpEditEngine::InsertParaBreak( EditSelection aCurSel )
3037 {
3038 	EditPaM aPaM( ImpInsertParaBreak( aCurSel ) );
3039 	if ( aStatus.DoAutoIndenting() )
3040 	{
3041 		sal_uInt32 nPara = aEditDoc.GetPos( aPaM.GetNode() );
3042 		DBG_ASSERT( nPara > 0, "AutoIndenting: Fehler!" );
3043 		XubString aPrevParaText( GetEditDoc().GetParaAsString( nPara-1 ) );
3044 		sal_uInt16 n = 0;
3045 		while ( ( n < aPrevParaText.Len() ) &&
3046 				( ( aPrevParaText.GetChar(n) == ' ' ) || ( aPrevParaText.GetChar(n) == '\t' ) ) )
3047 		{
3048 			if ( aPrevParaText.GetChar(n) == '\t' )
3049 				aPaM = ImpInsertFeature( aPaM, SfxVoidItem( EE_FEATURE_TAB ) );
3050 			else
3051 				aPaM = ImpInsertText( aPaM, aPrevParaText.GetChar(n) );
3052 			n++;
3053 		}
3054 
3055 	}
3056 	return aPaM;
3057 }
3058 
InsertTab(EditSelection aCurSel)3059 EditPaM ImpEditEngine::InsertTab( EditSelection aCurSel )
3060 {
3061 	EditPaM aPaM( ImpInsertFeature( aCurSel, SfxVoidItem( EE_FEATURE_TAB ) ) );
3062 	return aPaM;
3063 }
3064 
InsertField(EditSelection aCurSel,const SvxFieldItem & rFld)3065 EditPaM ImpEditEngine::InsertField( EditSelection aCurSel, const SvxFieldItem& rFld )
3066 {
3067 	EditPaM aPaM( ImpInsertFeature( aCurSel, rFld ) );
3068 	return aPaM;
3069 }
3070 
UpdateFields()3071 sal_Bool ImpEditEngine::UpdateFields()
3072 {
3073 	sal_Bool bChanges = sal_False;
3074 	sal_uInt32 nParas = GetEditDoc().Count();
3075 	for ( sal_uInt32 nPara = 0; nPara < nParas; nPara++ )
3076 	{
3077 		sal_Bool bChangesInPara = sal_False;
3078 		ContentNode* pNode = GetEditDoc().GetObject( nPara );
3079 		DBG_ASSERT( pNode, "NULL-Pointer im Doc" );
3080 		CharAttribArray& rAttribs = pNode->GetCharAttribs().GetAttribs();
3081 //		sal_uInt16 nAttrs = rAttribs.Count();
3082 		for ( sal_uInt16 nAttr = 0; nAttr < rAttribs.Count(); nAttr++ )
3083 		{
3084 			EditCharAttrib* pAttr = rAttribs[nAttr];
3085 			if ( pAttr->Which() == EE_FEATURE_FIELD )
3086 			{
3087 				EditCharAttribField* pField = (EditCharAttribField*)pAttr;
3088 				EditCharAttribField* pCurrent = new EditCharAttribField( *pField );
3089 				pField->Reset();
3090 
3091                 if ( aStatus.MarkFields() )
3092                     pField->GetFldColor() = new Color( GetColorConfig().GetColorValue( svtools::WRITERFIELDSHADINGS ).nColor );
3093 
3094 				XubString aFldValue = GetEditEnginePtr()->CalcFieldValue(
3095 										(const SvxFieldItem&)*pField->GetItem(),
3096 										nPara, pField->GetStart(),
3097 										pField->GetTxtColor(), pField->GetFldColor() );
3098 				pField->GetFieldValue() = aFldValue;
3099 				if ( *pField != *pCurrent )
3100 				{
3101 					bChanges = sal_True;
3102 					bChangesInPara = sal_True;
3103 				}
3104 				delete pCurrent;
3105 			}
3106 		}
3107 		if ( bChangesInPara )
3108 		{
3109 			// ggf. etwas genauer invalidieren.
3110 			ParaPortion* pPortion = GetParaPortions().GetObject( nPara );
3111 			DBG_ASSERT( pPortion, "NULL-Pointer im Doc" );
3112 			pPortion->MarkSelectionInvalid( 0, pNode->Len() );
3113 		}
3114 	}
3115 	return bChanges;
3116 }
3117 
InsertLineBreak(EditSelection aCurSel)3118 EditPaM ImpEditEngine::InsertLineBreak( EditSelection aCurSel )
3119 {
3120 	EditPaM aPaM( ImpInsertFeature( aCurSel, SfxVoidItem( EE_FEATURE_LINEBR ) ) );
3121 	return aPaM;
3122 }
3123 
3124 //	----------------------------------------------------------------------
3125 //	Hilfsfunktionen
3126 //	----------------------------------------------------------------------
PaMtoEditCursor(EditPaM aPaM,sal_uInt16 nFlags)3127 Rectangle ImpEditEngine::PaMtoEditCursor( EditPaM aPaM, sal_uInt16 nFlags )
3128 {
3129 	DBG_ASSERT( GetUpdateMode(), "Darf bei Update=sal_False nicht erreicht werden: PaMtoEditCursor" );
3130 
3131 	Rectangle aEditCursor;
3132 	long nY = 0;
3133 	for ( sal_uInt32 nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
3134 	{
3135 		ParaPortion* pPortion = GetParaPortions().GetObject(nPortion);
3136 		ContentNode* pNode = pPortion->GetNode();
3137 		DBG_ASSERT( pNode, "Ungueltiger Node in Portion!" );
3138 		if ( pNode != aPaM.GetNode() )
3139 		{
3140 			nY += pPortion->GetHeight();
3141 		}
3142 		else
3143 		{
3144 			aEditCursor = GetEditCursor( pPortion, aPaM.GetIndex(), nFlags );
3145 			aEditCursor.Top() += nY;
3146 			aEditCursor.Bottom() += nY;
3147 			return aEditCursor;
3148 		}
3149 	}
3150 	DBG_ERROR( "Portion nicht gefunden!" );
3151 	return aEditCursor;
3152 }
3153 
GetPaM(Point aDocPos,sal_Bool bSmart)3154 EditPaM ImpEditEngine::GetPaM( Point aDocPos, sal_Bool bSmart )
3155 {
3156 	DBG_ASSERT( GetUpdateMode(), "Darf bei Update=sal_False nicht erreicht werden: GetPaM" );
3157 
3158 	long nY = 0;
3159 	long nTmpHeight;
3160 	EditPaM aPaM;
3161 	sal_uInt32 nPortion;
3162 	for ( nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
3163 	{
3164 		ParaPortion* pPortion = GetParaPortions().GetObject(nPortion);
3165 		nTmpHeight = pPortion->GetHeight(); 	// sollte auch bei !bVisible richtig sein!
3166 		nY += nTmpHeight;
3167 		if ( nY > aDocPos.Y() )
3168 		{
3169 			nY -= nTmpHeight;
3170 			aDocPos.Y() -= nY;
3171 			// unsichtbare Portions ueberspringen:
3172 			while ( pPortion && !pPortion->IsVisible() )
3173 			{
3174 				nPortion++;
3175 				pPortion = GetParaPortions().SaveGetObject( nPortion );
3176 			}
3177 			DBG_ASSERT( pPortion, "Keinen sichtbaren Absatz gefunden: GetPaM" );
3178 			aPaM = GetPaM( pPortion, aDocPos, bSmart );
3179 			return aPaM;
3180 
3181 		}
3182 	}
3183 	// Dann den letzten sichtbaren Suchen:
3184 	nPortion = GetParaPortions().Count()-1;
3185 	while ( nPortion && !GetParaPortions()[nPortion]->IsVisible() )
3186 		nPortion--;
3187 
3188 	DBG_ASSERT( GetParaPortions()[nPortion]->IsVisible(), "Keinen sichtbaren Absatz gefunden: GetPaM" );
3189 	aPaM.SetNode( GetParaPortions()[nPortion]->GetNode() );
3190 	aPaM.SetIndex( GetParaPortions()[nPortion]->GetNode()->Len() );
3191 	return aPaM;
3192 }
3193 
GetTextHeight() const3194 sal_uInt32 ImpEditEngine::GetTextHeight() const
3195 {
3196 	DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=sal_False nicht verwendet werden: GetTextHeight" );
3197 	DBG_ASSERT( IsFormatted() || IsFormatting(), "GetTextHeight: Nicht formatiert" );
3198 	return nCurTextHeight;
3199 }
3200 
CalcTextWidth(sal_Bool bIgnoreExtraSpace)3201 sal_uInt32 ImpEditEngine::CalcTextWidth( sal_Bool bIgnoreExtraSpace )
3202 {
3203 	// Wenn noch nicht formatiert und nicht gerade dabei.
3204 	// Wird in der Formatierung bei AutoPageSize gerufen.
3205 	if ( !IsFormatted() && !IsFormatting() )
3206 		FormatDoc();
3207 
3208 	EditLine* pLine;
3209 
3210 	long nMaxWidth = 0;
3211 	long nCurWidth = 0;
3212 
3213 	// --------------------------------------------------
3214 	// Ueber alle Absaetze...
3215 	// --------------------------------------------------
3216 	sal_uInt32 nParas = GetParaPortions().Count();
3217 //	sal_uInt16 nBiggestPara = 0;
3218 //	sal_uInt16 nBiggestLine = 0;
3219 	for ( sal_uInt32 nPara = 0; nPara < nParas; nPara++ )
3220 	{
3221 		ParaPortion* pPortion = GetParaPortions().GetObject( nPara );
3222 		if ( pPortion->IsVisible() )
3223 		{
3224             const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pPortion->GetNode() );
3225             sal_Int32 nSpaceBeforeAndMinLabelWidth = GetSpaceBeforeAndMinLabelWidth( pPortion->GetNode() );
3226 
3227 			// --------------------------------------------------
3228 			// Ueber die Zeilen des Absatzes...
3229 			// --------------------------------------------------
3230 			sal_uLong nLines = pPortion->GetLines().Count();
3231 			for ( sal_uInt16 nLine = 0; nLine < nLines; nLine++ )
3232 			{
3233 				pLine = pPortion->GetLines().GetObject( nLine );
3234 				DBG_ASSERT( pLine, "NULL-Pointer im Zeileniterator in CalcWidth" );
3235 				// nCurWidth = pLine->GetStartPosX();
3236 				// Bei Center oder Right haengt die breite von der
3237 				// Papierbreite ab, hier nicht erwuenscht.
3238 				// Am besten generell nicht auf StartPosX verlassen,
3239 				// es muss auch die rechte Einrueckung beruecksichtigt werden!
3240                 nCurWidth = GetXValue( rLRItem.GetTxtLeft() + nSpaceBeforeAndMinLabelWidth );
3241 				if ( nLine == 0 )
3242 				{
3243 					long nFI = GetXValue( rLRItem.GetTxtFirstLineOfst() );
3244 					nCurWidth -= nFI;
3245 					if ( pPortion->GetBulletX() > nCurWidth )
3246 					{
3247 						nCurWidth += nFI;	// LI?
3248 						if ( pPortion->GetBulletX() > nCurWidth )
3249 							nCurWidth = pPortion->GetBulletX();
3250 					}
3251 				}
3252 				nCurWidth += GetXValue( rLRItem.GetRight() );
3253 				nCurWidth += CalcLineWidth( pPortion, pLine, bIgnoreExtraSpace );
3254 				if ( nCurWidth > nMaxWidth )
3255 				{
3256 					nMaxWidth = nCurWidth;
3257 				}
3258 			}
3259 		}
3260 	}
3261 	if ( nMaxWidth < 0 )
3262 		nMaxWidth = 0;
3263 
3264 	nMaxWidth++; // Ein breiter, da in CreateLines bei >= umgebrochen wird.
3265 	return (sal_uInt32)nMaxWidth;
3266 }
3267 
CalcLineWidth(ParaPortion * pPortion,EditLine * pLine,sal_Bool bIgnoreExtraSpace)3268 sal_uInt32 ImpEditEngine::CalcLineWidth( ParaPortion* pPortion, EditLine* pLine, sal_Bool bIgnoreExtraSpace )
3269 {
3270 	sal_uInt32 nPara = GetEditDoc().GetPos( pPortion->GetNode() );
3271 
3272     // #114278# Saving both layout mode and language (since I'm
3273     // potentially changing both)
3274     GetRefDevice()->Push( PUSH_TEXTLAYOUTMODE|PUSH_TEXTLANGUAGE );
3275 
3276     ImplInitLayoutMode( GetRefDevice(), nPara, 0xFFFF );
3277 
3278     SvxAdjust eJustification = GetJustification( nPara );
3279 
3280     // Berechnung der Breite ohne die Indents...
3281 	sal_uInt32 nWidth = 0;
3282     sal_uInt16 nPos = pLine->GetStart();
3283 	for ( sal_uInt16 nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ )
3284 	{
3285 		TextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( nTP );
3286 		switch ( pTextPortion->GetKind() )
3287 		{
3288 			case PORTIONKIND_FIELD:
3289 			case PORTIONKIND_HYPHENATOR:
3290 			case PORTIONKIND_TAB:
3291 			{
3292 				nWidth += pTextPortion->GetSize().Width();
3293 			}
3294 			break;
3295 			case PORTIONKIND_TEXT:
3296 			{
3297                 if ( ( eJustification != SVX_ADJUST_BLOCK ) || ( !bIgnoreExtraSpace ) )
3298                 {
3299 				    nWidth += pTextPortion->GetSize().Width();
3300                 }
3301                 else
3302                 {
3303 	                SvxFont aTmpFont( pPortion->GetNode()->GetCharAttribs().GetDefFont() );
3304 				    SeekCursor( pPortion->GetNode(), nPos+1, aTmpFont );
3305 				    aTmpFont.SetPhysFont( GetRefDevice() );
3306                     ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
3307                     nWidth += aTmpFont.QuickGetTextSize( GetRefDevice(), *pPortion->GetNode(), nPos, pTextPortion->GetLen(), NULL ).Width();
3308                 }
3309 			}
3310 			break;
3311 		}
3312         nPos = nPos + pTextPortion->GetLen();
3313 	}
3314 
3315     GetRefDevice()->Pop();
3316 
3317     return nWidth;
3318 }
3319 
CalcTextHeight()3320 sal_uInt32 ImpEditEngine::CalcTextHeight()
3321 {
3322 	DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=sal_False nicht verwendet werden: CalcTextHeight" );
3323 	sal_uInt32 nY = 0;
3324 	for ( sal_uInt32 nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
3325 		nY += GetParaPortions()[nPortion]->GetHeight();
3326 	return nY;
3327 }
3328 
GetLineCount(sal_uInt32 nParagraph) const3329 sal_uInt16 ImpEditEngine::GetLineCount( sal_uInt32 nParagraph ) const
3330 {
3331 	DBG_ASSERT( nParagraph < GetParaPortions().Count(), "GetLineCount: Out of range" );
3332 	ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
3333 	DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetLineCount" );
3334 	if ( pPPortion )
3335 		return pPPortion->GetLines().Count();
3336 
3337 	return 0xFFFF;
3338 }
3339 
GetLineLen(sal_uInt32 nParagraph,sal_uInt16 nLine) const3340 xub_StrLen ImpEditEngine::GetLineLen( sal_uInt32 nParagraph, sal_uInt16 nLine ) const
3341 {
3342     DBG_ASSERT( nParagraph < GetParaPortions().Count(), "GetLineLen: Out of range" );
3343 	ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
3344     DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetLineLen" );
3345 	if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) )
3346 	{
3347 		EditLine* pLine = pPPortion->GetLines().GetObject( nLine );
3348 		DBG_ASSERT( pLine, "Zeile nicht gefunden: GetLineHeight" );
3349 		return pLine->GetLen();
3350 	}
3351 
3352 	return 0xFFFF;
3353 }
3354 
GetLineBoundaries(sal_uInt16 & rStart,sal_uInt16 & rEnd,sal_uInt32 nParagraph,sal_uInt16 nLine) const3355 void ImpEditEngine::GetLineBoundaries( /*out*/sal_uInt16 &rStart, /*out*/sal_uInt16 &rEnd, sal_uInt32 nParagraph, sal_uInt16 nLine ) const
3356 {
3357     DBG_ASSERT( nParagraph < GetParaPortions().Count(), "GetLineCount: Out of range" );
3358     ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
3359     DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetLineBoundaries" );
3360     rStart = rEnd = 0xFFFF;     // default values in case of error
3361     if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) )
3362     {
3363         EditLine* pLine = pPPortion->GetLines().GetObject( nLine );
3364         DBG_ASSERT( pLine, "Zeile nicht gefunden: GetLineBoundaries" );
3365         rStart = pLine->GetStart();
3366         rEnd   = pLine->GetEnd();
3367     }
3368 }
3369 
GetLineNumberAtIndex(sal_uInt32 nPara,sal_uInt16 nIndex) const3370 sal_uInt16 ImpEditEngine::GetLineNumberAtIndex( sal_uInt32 nPara, sal_uInt16 nIndex ) const
3371 {
3372     sal_uInt16 nLineNo = 0xFFFF;
3373     ContentNode* pNode = GetEditDoc().SaveGetObject( nPara );
3374     DBG_ASSERT( pNode, "GetLineNumberAtIndex: invalid paragraph index" );
3375     if (pNode)
3376     {
3377         // we explicitly allow for the index to point at the character right behind the text
3378         const bool bValidIndex = /*0 <= nIndex &&*/ nIndex <= pNode->Len();
3379         DBG_ASSERT( bValidIndex, "GetLineNumberAtIndex: invalid index" );
3380         const sal_uInt16 nLineCount = GetLineCount( nPara );
3381         if (nIndex == pNode->Len())
3382             nLineNo = nLineCount > 0 ? nLineCount - 1 : 0;
3383         else if (bValidIndex)   // nIndex < pNode->Len()
3384         {
3385             sal_uInt16 nStart = USHRT_MAX, nEnd = USHRT_MAX;
3386             for (sal_uInt16 i = 0;  i < nLineCount && nLineNo == 0xFFFF;  ++i)
3387             {
3388                 GetLineBoundaries( nStart, nEnd, nPara, i );
3389                 if (nStart <= nIndex && nIndex < nEnd)
3390                     nLineNo = i;
3391             }
3392         }
3393     }
3394     return nLineNo;
3395 }
3396 
GetLineHeight(sal_uInt32 nParagraph,sal_uInt16 nLine)3397 sal_uInt16 ImpEditEngine::GetLineHeight( sal_uInt32 nParagraph, sal_uInt16 nLine )
3398 {
3399 	DBG_ASSERT( nParagraph < GetParaPortions().Count(), "GetLineCount: Out of range" );
3400 	ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
3401 	DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetLineHeight" );
3402 	if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) )
3403 	{
3404 		EditLine* pLine = pPPortion->GetLines().GetObject( nLine );
3405 		DBG_ASSERT( pLine, "Zeile nicht gefunden: GetLineHeight" );
3406 		return pLine->GetHeight();
3407 	}
3408 
3409 	return 0xFFFF;
3410 }
3411 
GetParaHeight(sal_uInt32 nParagraph)3412 sal_uInt32 ImpEditEngine::GetParaHeight( sal_uInt32 nParagraph )
3413 {
3414 	sal_uInt32 nHeight = 0;
3415 
3416 	ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
3417 	DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetParaHeight" );
3418 
3419 	if ( pPPortion )
3420 		nHeight = pPPortion->GetHeight();
3421 
3422 	return nHeight;
3423 }
3424 
UpdateSelections()3425 void ImpEditEngine::UpdateSelections()
3426 {
3427 	sal_uInt16 nInvNodes = aDeletedNodes.Count();
3428 
3429 	// Pruefen, ob eine der Selektionen auf einem geloeschten Node steht...
3430 	// Wenn der Node gueltig ist, muss noch der Index geprueft werden!
3431 	for ( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
3432 	{
3433 		EditView* pView = aEditViews.GetObject(nView);
3434 		DBG_CHKOBJ( pView, EditView, 0 );
3435 		EditSelection aCurSel( pView->pImpEditView->GetEditSelection() );
3436 		sal_Bool bChanged = sal_False;
3437 		for ( sal_uInt16 n = 0; n < nInvNodes; n++ )
3438 		{
3439 			DeletedNodeInfo* pInf = aDeletedNodes.GetObject( n );
3440 			if ( ( ( sal_uLong )(aCurSel.Min().GetNode()) == pInf->GetInvalidAdress() ) ||
3441 				 ( ( sal_uLong )(aCurSel.Max().GetNode()) == pInf->GetInvalidAdress() ) )
3442 			{
3443 				// ParaPortions verwenden, da jetzt auch versteckte
3444 				// Absaetze beruecksichtigt werden muessen!
3445 				sal_uInt32 nPara = pInf->GetPosition();
3446 				ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nPara );
3447 				if ( !pPPortion ) // letzter Absatz
3448 				{
3449 					nPara = GetParaPortions().Count()-1;
3450 					pPPortion = GetParaPortions().GetObject( nPara );
3451 				}
3452 				DBG_ASSERT( pPPortion, "Leeres Document in UpdateSelections ?" );
3453 				// Nicht aus einem verstecktem Absatz landen:
3454 				sal_uInt32 nCurPara = nPara;
3455 				sal_uInt32 nLastPara = GetParaPortions().Count()-1;
3456 				while ( nPara <= nLastPara && !GetParaPortions()[nPara]->IsVisible() )
3457 					nPara++;
3458 				if ( nPara > nLastPara ) // dann eben rueckwaerts...
3459 				{
3460 					nPara = nCurPara;
3461 					while ( nPara && !GetParaPortions()[nPara]->IsVisible() )
3462 						nPara--;
3463 				}
3464 				DBG_ASSERT( GetParaPortions()[nPara]->IsVisible(), "Keinen sichtbaren Absatz gefunden: UpdateSelections" );
3465 
3466 				ParaPortion* pParaPortion = GetParaPortions()[nPara];
3467 				EditSelection aTmpSelection( EditPaM( pParaPortion->GetNode(), 0 ) );
3468 				pView->pImpEditView->SetEditSelection( aTmpSelection );
3469 				bChanged=sal_True;
3470 				break;	// for-Schleife
3471 			}
3472 		}
3473 		if ( !bChanged )
3474 		{
3475 			// Index prueffen, falls Node geschrumpft.
3476 			if ( aCurSel.Min().GetIndex() > aCurSel.Min().GetNode()->Len() )
3477 			{
3478 				aCurSel.Min().GetIndex() = aCurSel.Min().GetNode()->Len();
3479 				pView->pImpEditView->SetEditSelection( aCurSel );
3480 			}
3481 			if ( aCurSel.Max().GetIndex() > aCurSel.Max().GetNode()->Len() )
3482 			{
3483 				aCurSel.Max().GetIndex() = aCurSel.Max().GetNode()->Len();
3484 				pView->pImpEditView->SetEditSelection( aCurSel );
3485 			}
3486 		}
3487 	}
3488 
3489 	// Loeschen...
3490 	for ( sal_uInt16 n = 0; n < nInvNodes; n++ )
3491 	{
3492 		DeletedNodeInfo* pInf = aDeletedNodes.GetObject( n );
3493 		delete pInf;
3494 	}
3495 	aDeletedNodes.Remove( 0, aDeletedNodes.Count() );
3496 }
3497 
ConvertSelection(sal_uInt32 nStartPara,sal_uInt16 nStartPos,sal_uInt32 nEndPara,sal_uInt16 nEndPos) const3498 EditSelection ImpEditEngine::ConvertSelection( sal_uInt32 nStartPara, sal_uInt16 nStartPos,
3499 							 sal_uInt32 nEndPara, sal_uInt16 nEndPos ) const
3500 {
3501 	EditSelection aNewSelection;
3502 
3503 	// Start...
3504 	ContentNode* pNode = aEditDoc.SaveGetObject( nStartPara );
3505 	sal_uInt16 nIndex = nStartPos;
3506 	if ( !pNode )
3507 	{
3508 		pNode = aEditDoc[ aEditDoc.Count()-1 ];
3509 		nIndex = pNode->Len();
3510 	}
3511 	else if ( nIndex > pNode->Len() )
3512 		nIndex = pNode->Len();
3513 
3514 	aNewSelection.Min().SetNode( pNode );
3515 	aNewSelection.Min().SetIndex( nIndex );
3516 
3517 	// End...
3518 	pNode = aEditDoc.SaveGetObject( nEndPara );
3519 	nIndex = nEndPos;
3520 	if ( !pNode )
3521 	{
3522 		pNode = aEditDoc[ aEditDoc.Count()-1 ];
3523 		nIndex = pNode->Len();
3524 	}
3525 	else if ( nIndex > pNode->Len() )
3526 		nIndex = pNode->Len();
3527 
3528 	aNewSelection.Max().SetNode( pNode );
3529 	aNewSelection.Max().SetIndex( nIndex );
3530 
3531 	return aNewSelection;
3532 }
3533 
MatchGroup(const EditSelection & rSel)3534 EditSelection ImpEditEngine::MatchGroup( const EditSelection& rSel )
3535 {
3536 	EditSelection aMatchSel;
3537 	EditSelection aTmpSel( rSel );
3538 	aTmpSel.Adjust( GetEditDoc() );
3539 	if ( (	aTmpSel.Min().GetNode() != aTmpSel.Max().GetNode() ) ||
3540 		 ( ( aTmpSel.Max().GetIndex() - aTmpSel.Min().GetIndex() ) > 1 ) )
3541 	{
3542 		return aMatchSel;
3543 	}
3544 
3545 	sal_uInt16 nPos = aTmpSel.Min().GetIndex();
3546 	ContentNode* pNode = aTmpSel.Min().GetNode();
3547 	if ( nPos >= pNode->Len() )
3548 		return aMatchSel;
3549 
3550 	sal_uInt16 nMatchChar = aGroupChars.Search( pNode->GetChar( nPos ) );
3551 	if ( nMatchChar != STRING_NOTFOUND )
3552 	{
3553 		sal_uInt32 nNode = aEditDoc.GetPos( pNode );
3554 		if ( ( nMatchChar % 2 ) == 0 )
3555 		{
3556 			// Vorwaerts suchen...
3557 			xub_Unicode nSC = aGroupChars.GetChar( nMatchChar );
3558 			DBG_ASSERT( aGroupChars.Len() > (nMatchChar+1), "Ungueltige Gruppe von MatchChars!" );
3559 			xub_Unicode nEC = aGroupChars.GetChar( nMatchChar+1 );
3560 
3561 			sal_uInt16 nCur = aTmpSel.Min().GetIndex()+1;
3562 			sal_uInt16 nLevel = 1;
3563 			while ( pNode && nLevel )
3564 			{
3565 				XubString& rStr = *pNode;
3566 				while ( nCur < rStr.Len() )
3567 				{
3568 					if ( rStr.GetChar( nCur ) == nSC )
3569 						nLevel++;
3570 					else if ( rStr.GetChar( nCur ) == nEC )
3571 					{
3572 						nLevel--;
3573 						if ( !nLevel )
3574 							break;	// while nCur...
3575 					}
3576 					nCur++;
3577 				}
3578 
3579 				if ( nLevel )
3580 				{
3581 					nNode++;
3582 					pNode = nNode < aEditDoc.Count() ? aEditDoc.GetObject( nNode ) : 0;
3583 					nCur = 0;
3584 				}
3585 			}
3586 			if ( nLevel == 0 )	// gefunden
3587 			{
3588 				aMatchSel.Min() = aTmpSel.Min();
3589 				aMatchSel.Max() = EditPaM( pNode, nCur+1 );
3590 			}
3591 		}
3592 		else
3593 		{
3594 			// Rueckwaerts suchen...
3595 			xub_Unicode nEC = aGroupChars.GetChar( nMatchChar );
3596 			xub_Unicode nSC = aGroupChars.GetChar( nMatchChar-1 );
3597 
3598 			sal_uInt16 nCur = aTmpSel.Min().GetIndex()-1;
3599 			sal_uInt16 nLevel = 1;
3600 			while ( pNode && nLevel )
3601 			{
3602 				if ( pNode->Len() )
3603 				{
3604 					XubString& rStr = *pNode;
3605 					while ( nCur )
3606 					{
3607 						if ( rStr.GetChar( nCur ) == nSC )
3608 						{
3609 							nLevel--;
3610 							if ( !nLevel )
3611 								break;	// while nCur...
3612 						}
3613 						else if ( rStr.GetChar( nCur ) == nEC )
3614 							nLevel++;
3615 
3616 						nCur--;
3617 					}
3618 				}
3619 
3620 				if ( nLevel )
3621 				{
3622 					pNode = nNode ? aEditDoc.GetObject( --nNode ) : 0;
3623 					if ( pNode )
3624 						nCur = pNode->Len()-1;	// egal ob negativ, weil if Len()
3625 				}
3626 			}
3627 
3628 			if ( nLevel == 0 )	// gefunden
3629 			{
3630 				aMatchSel.Min() = aTmpSel.Min();
3631 				aMatchSel.Min().GetIndex()++;	// hinter das Zeichen
3632 				aMatchSel.Max() = EditPaM( pNode, nCur );
3633 			}
3634 		}
3635 	}
3636 	return aMatchSel;
3637 }
3638 
StopSelectionMode()3639 void ImpEditEngine::StopSelectionMode()
3640 {
3641 	if ( ( IsInSelectionMode() || aSelEngine.IsInSelection() ) && pActiveView )
3642 	{
3643 		pActiveView->pImpEditView->DrawSelection();	// Wegzeichnen...
3644 		EditSelection aSel( pActiveView->pImpEditView->GetEditSelection() );
3645 		aSel.Min() = aSel.Max();
3646 		pActiveView->pImpEditView->SetEditSelection( aSel );
3647 		pActiveView->ShowCursor();
3648 		aSelEngine.Reset();
3649 		bInSelection = sal_False;
3650 	}
3651 }
3652 
SetActiveView(EditView * pView)3653 void ImpEditEngine::SetActiveView( EditView* pView )
3654 {
3655 	// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3656 	// Eigentlich waere jetzt ein bHasVisSel und HideSelection notwendig !!!
3657 
3658 	if ( pView == pActiveView )
3659 		return;
3660 
3661 	if ( pActiveView && pActiveView->HasSelection() )
3662 		pActiveView->pImpEditView->DrawSelection();	// Wegzeichnen...
3663 
3664 	pActiveView = pView;
3665 
3666 	if ( pActiveView && pActiveView->HasSelection() )
3667 		pActiveView->pImpEditView->DrawSelection();	// Wegzeichnen...
3668 
3669 	//	NN: Quick fix for #78668#:
3670 	//	When editing of a cell in Calc is ended, the edit engine is not deleted,
3671 	//	only the edit views are removed. If mpIMEInfos is still set in that case,
3672 	//	mpIMEInfos->aPos points to an invalid selection.
3673 	//	-> reset mpIMEInfos now
3674 	//	(probably something like this is necessary whenever the content is modified
3675 	//	from the outside)
3676 
3677 	if ( !pView && mpIMEInfos )
3678 	{
3679 		delete mpIMEInfos;
3680 		mpIMEInfos = NULL;
3681 	}
3682 }
3683 
CreateTransferable(const EditSelection & rSelection) const3684 uno::Reference< datatransfer::XTransferable > ImpEditEngine::CreateTransferable( const EditSelection& rSelection ) const
3685 {
3686 #ifndef SVX_LIGHT
3687     EditSelection aSelection( rSelection );
3688 	aSelection.Adjust( GetEditDoc() );
3689 
3690 	EditDataObject* pDataObj = new EditDataObject;
3691 	uno::Reference< datatransfer::XTransferable > xDataObj;
3692 	xDataObj = pDataObj;
3693 
3694 	XubString aText( GetSelected( aSelection ) );
3695 	aText.ConvertLineEnd();	// Systemspezifisch
3696 	pDataObj->GetString() = aText;
3697 
3698     SvxFontItem::EnableStoreUnicodeNames( sal_True );
3699 	WriteBin( pDataObj->GetStream(), aSelection, sal_True );
3700 	pDataObj->GetStream().Seek( 0 );
3701     SvxFontItem::EnableStoreUnicodeNames( sal_False );
3702 
3703     ((ImpEditEngine*)this)->WriteRTF( pDataObj->GetRTFStream(), aSelection );
3704 	pDataObj->GetRTFStream().Seek( 0 );
3705 
3706 	if ( ( aSelection.Min().GetNode() == aSelection.Max().GetNode() )
3707 			&& ( aSelection.Max().GetIndex() == (aSelection.Min().GetIndex()+1) ) )
3708 	{
3709 		const EditCharAttrib* pAttr = aSelection.Min().GetNode()->GetCharAttribs().
3710 			FindFeature( aSelection.Min().GetIndex() );
3711 		if ( pAttr &&
3712 			( pAttr->GetStart() == aSelection.Min().GetIndex() ) &&
3713 			( pAttr->Which() == EE_FEATURE_FIELD ) )
3714 		{
3715 			const SvxFieldItem*	pField = (const SvxFieldItem*)pAttr->GetItem();
3716 			const SvxFieldData* pFld = pField->GetField();
3717 			if ( pFld && pFld->ISA( SvxURLField ) )
3718 			{
3719 				// Office-Bookmark
3720 				String aURL( ((const SvxURLField*)pFld)->GetURL() );
3721 				String aTxt( ((const SvxURLField*)pFld)->GetRepresentation() );
3722 				pDataObj->GetURL() = aURL;
3723 			}
3724 		}
3725 	}
3726 
3727     return xDataObj;
3728 #else
3729 	return uno::Reference< datatransfer::XTransferable >();
3730 #endif
3731 }
3732 
InsertText(uno::Reference<datatransfer::XTransferable> & rxDataObj,const String & rBaseURL,const EditPaM & rPaM,sal_Bool bUseSpecial)3733 EditSelection ImpEditEngine::InsertText( uno::Reference< datatransfer::XTransferable >& rxDataObj, const String& rBaseURL, const EditPaM& rPaM, sal_Bool bUseSpecial )
3734 {
3735 	EditSelection aNewSelection( rPaM );
3736 
3737 	if ( rxDataObj.is() )
3738 	{
3739 		datatransfer::DataFlavor aFlavor;
3740 		sal_Bool bDone = sal_False;
3741 
3742 		if ( bUseSpecial )
3743 		{
3744 			// BIN
3745 			SotExchange::GetFormatDataFlavor( SOT_FORMATSTR_ID_EDITENGINE, aFlavor );
3746 			if ( rxDataObj->isDataFlavorSupported( aFlavor ) )
3747 			{
3748 				try
3749 				{
3750 					uno::Any aData = rxDataObj->getTransferData( aFlavor );
3751 					uno::Sequence< sal_Int8 > aSeq;
3752 					aData >>= aSeq;
3753 					{
3754 						SvMemoryStream aBinStream( aSeq.getArray(), aSeq.getLength(), STREAM_READ );
3755 						aNewSelection = Read( aBinStream, rBaseURL, EE_FORMAT_BIN, rPaM );
3756 					}
3757 					bDone = sal_True;
3758 				}
3759 				catch( const ::com::sun::star::uno::Exception& )
3760 				{
3761 				}
3762 			}
3763 
3764 			if ( !bDone )
3765 			{
3766 				// Bookmark
3767 				/*
3768 				String aURL = ...;
3769 				String aTxt = ...;
3770 				// Feld nur einfuegen, wenn Factory vorhanden.
3771 				if ( ITEMDATA() && ITEMDATA()->GetClassManager().Get( SVX_URLFIELD ) )
3772 				{
3773 					SvxFieldItem aField( SvxURLField( aURL, aTxt, SVXURLFORMAT_URL ), EE_FEATURE_FIELD  );
3774 					aNewSelection = InsertField( aPaM, aField );
3775 					UpdateFields();
3776 				}
3777 				else
3778 					aNewSelection = ImpInsertText( aPaM, aURL );
3779 				}
3780 				*/
3781 			}
3782 			if ( !bDone )
3783 			{
3784 				// RTF
3785 				SotExchange::GetFormatDataFlavor( SOT_FORMAT_RTF, aFlavor );
3786 				if ( rxDataObj->isDataFlavorSupported( aFlavor ) )
3787 				{
3788 					try
3789 					{
3790 						uno::Any aData = rxDataObj->getTransferData( aFlavor );
3791 						uno::Sequence< sal_Int8 > aSeq;
3792 						aData >>= aSeq;
3793 						{
3794 							SvMemoryStream aRTFStream( aSeq.getArray(), aSeq.getLength(), STREAM_READ );
3795 							aNewSelection = Read( aRTFStream, rBaseURL, EE_FORMAT_RTF, rPaM );
3796 						}
3797 						bDone = sal_True;
3798 					}
3799 					catch( const ::com::sun::star::uno::Exception& )
3800 					{
3801 					}
3802 				}
3803 			}
3804 			if ( !bDone )
3805 			{
3806 				// XML ?
3807                 // Currently, there is nothing like "The" XML format, StarOffice doesn't offer plain XML in Clipboard...
3808 			}
3809 		}
3810 		if ( !bDone )
3811 		{
3812 			SotExchange::GetFormatDataFlavor( SOT_FORMAT_STRING, aFlavor );
3813 			if ( rxDataObj->isDataFlavorSupported( aFlavor ) )
3814 			{
3815 			    try
3816         	    {
3817     				uno::Any aData = rxDataObj->getTransferData( aFlavor );
3818     				::rtl::OUString aText;
3819     				aData >>= aText;
3820     				aNewSelection = ImpInsertText( rPaM, aText );
3821    					bDone = sal_True;
3822 				}
3823 				catch( ... )
3824 				{
3825                     ; // #i9286# can happen, even if isDataFlavorSupported returns true...
3826 				}
3827 			}
3828 		}
3829 	}
3830 
3831 	return aNewSelection;
3832 }
3833 
GetInvalidYOffsets(ParaPortion * pPortion)3834 Range ImpEditEngine::GetInvalidYOffsets( ParaPortion* pPortion )
3835 {
3836 	Range aRange( 0, 0 );
3837 
3838 	if ( pPortion->IsVisible() )
3839 	{
3840 		const SvxULSpaceItem& rULSpace = (const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
3841 		const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
3842 		sal_uInt16 nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
3843 							? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
3844 
3845 		// erst von vorne...
3846 		sal_uInt16 nFirstInvalid = 0xFFFF;
3847 		sal_uInt16 nLine;
3848 		for ( nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
3849 		{
3850 			EditLine* pL = pPortion->GetLines().GetObject( nLine );
3851 			if ( pL->IsInvalid() )
3852 			{
3853 				nFirstInvalid = nLine;
3854 				break;
3855 			}
3856 			if ( nLine && !aStatus.IsOutliner() ) 	// nicht die erste Zeile
3857 				aRange.Min() += nSBL;
3858 			aRange.Min() += pL->GetHeight();
3859 		}
3860 		DBG_ASSERT( nFirstInvalid != 0xFFFF, "Keine ungueltige Zeile gefunden in GetInvalidYOffset(1)" );
3861 
3862 
3863 		// Abgleichen und weiter...
3864 		aRange.Max() = aRange.Min();
3865 		aRange.Max() += pPortion->GetFirstLineOffset();
3866 		if ( nFirstInvalid != 0 )	// Nur wenn nicht die erste Zeile ungueltig
3867 			aRange.Min() = aRange.Max();
3868 
3869 		sal_uInt16 nLastInvalid = pPortion->GetLines().Count()-1;
3870 		for ( nLine = nFirstInvalid; nLine < pPortion->GetLines().Count(); nLine++ )
3871 		{
3872 			EditLine* pL = pPortion->GetLines().GetObject( nLine );
3873 			if ( pL->IsValid() )
3874 			{
3875 				nLastInvalid = nLine;
3876 				break;
3877 			}
3878 
3879 			if ( nLine && !aStatus.IsOutliner() )
3880 				aRange.Max() += nSBL;
3881 			aRange.Max() += pL->GetHeight();
3882 		}
3883 
3884 		// MT 07/00 SBL kann jetzt kleiner 100% sein => ggf. die Zeile davor neu ausgeben.
3885 		if( ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP ) && rLSItem.GetPropLineSpace() &&
3886 			( rLSItem.GetPropLineSpace() < 100 ) )
3887 		{
3888 			EditLine* pL = pPortion->GetLines().GetObject( nFirstInvalid );
3889 			long n = pL->GetTxtHeight() * ( 100 - rLSItem.GetPropLineSpace() );
3890 			n /= 100;
3891 			aRange.Min() -= n;
3892 			aRange.Max() += n;
3893 		}
3894 
3895 		if ( ( nLastInvalid == pPortion->GetLines().Count()-1 ) && ( !aStatus.IsOutliner() ) )
3896 			aRange.Max() += GetYValue( rULSpace.GetLower() );
3897 	}
3898 	return aRange;
3899 }
3900 
GetPaM(ParaPortion * pPortion,Point aDocPos,sal_Bool bSmart)3901 EditPaM ImpEditEngine::GetPaM( ParaPortion* pPortion, Point aDocPos, sal_Bool bSmart )
3902 {
3903 	DBG_ASSERT( pPortion->IsVisible(), "Wozu GetPaM() bei einem unsichtbaren Absatz?" );
3904 	DBG_ASSERT( IsFormatted(), "GetPaM: Nicht formatiert" );
3905 
3906 	sal_uInt16 nCurIndex = 0;
3907 	EditPaM aPaM;
3908 	aPaM.SetNode( pPortion->GetNode() );
3909 
3910 	const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
3911 	sal_uInt16 nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
3912 						? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
3913 
3914 	long nY = pPortion->GetFirstLineOffset();
3915 
3916 	DBG_ASSERT( pPortion->GetLines().Count(), "Leere ParaPortion in GetPaM!" );
3917 
3918 	EditLine* pLine = 0;
3919 	for ( sal_uInt16 nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
3920 	{
3921 		EditLine* pTmpLine = pPortion->GetLines().GetObject( nLine );
3922 		nY += pTmpLine->GetHeight();
3923 		if ( !aStatus.IsOutliner() )
3924 			nY += nSBL;
3925 		if ( nY > aDocPos.Y() ) 	// das war 'se
3926 		{
3927 			pLine = pTmpLine;
3928 			break;					// richtige Y-Position intressiert nicht
3929 		}
3930 
3931 		nCurIndex = nCurIndex + pTmpLine->GetLen();
3932 	}
3933 
3934 	if ( !pLine ) // darf nur im Bereich von SA passieren!
3935 	{
3936 		#ifdef DBG_UTIL
3937 		 const SvxULSpaceItem& rULSpace =(const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
3938 		 DBG_ASSERT( nY+GetYValue( rULSpace.GetLower() ) >= aDocPos.Y() , "Index in keiner Zeile, GetPaM ?" );
3939 		#endif
3940 		aPaM.SetIndex( pPortion->GetNode()->Len() );
3941 		return aPaM;
3942 	}
3943 
3944 	// Wenn Zeile gefunden, nur noch X-Position => Index
3945 	nCurIndex = GetChar( pPortion, pLine, aDocPos.X(), bSmart );
3946 	aPaM.SetIndex( nCurIndex );
3947 
3948 	if ( nCurIndex && ( nCurIndex == pLine->GetEnd() ) &&
3949 		 ( pLine != pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1) ) )
3950     {
3951         aPaM = CursorLeft( aPaM, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL );
3952     }
3953 
3954 	return aPaM;
3955 }
3956 
GetChar(ParaPortion * pParaPortion,EditLine * pLine,long nXPos,sal_Bool bSmart)3957 sal_uInt16 ImpEditEngine::GetChar( ParaPortion* pParaPortion, EditLine* pLine, long nXPos, sal_Bool bSmart )
3958 {
3959 	DBG_ASSERT( pLine, "Keine Zeile erhalten: GetChar" );
3960 
3961     sal_uInt16 nChar = 0xFFFF;
3962     sal_uInt16 nCurIndex = pLine->GetStart();
3963 
3964 
3965     // Search best matching portion with GetPortionXOffset()
3966     for ( sal_uInt16 i = pLine->GetStartPortion(); i <= pLine->GetEndPortion(); i++ )
3967 	{
3968 		TextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( i );
3969         long nXLeft = GetPortionXOffset( pParaPortion, pLine, i );
3970         long nXRight = nXLeft + pPortion->GetSize().Width();
3971         if ( ( nXLeft <= nXPos ) && ( nXRight >= nXPos ) )
3972         {
3973              nChar = nCurIndex;
3974 
3975             // Search within Portion...
3976 
3977             // Don't search within special portions...
3978 			if ( pPortion->GetKind() != PORTIONKIND_TEXT )
3979 			{
3980                 // ...but check on which side
3981                 if ( bSmart )
3982                 {
3983 				    long nLeftDiff = nXPos-nXLeft;
3984 				    long nRightDiff = nXRight-nXPos;
3985 				    if ( nRightDiff < nLeftDiff )
3986 					    nChar++;
3987                 }
3988 			}
3989             else
3990             {
3991 			    sal_uInt16 nMax = pPortion->GetLen();
3992 			    sal_uInt16 nOffset = 0xFFFF;
3993 			    sal_uInt16 nTmpCurIndex = nChar - pLine->GetStart();
3994 
3995                 long nXInPortion = nXPos - nXLeft;
3996                 if ( pPortion->IsRightToLeft() )
3997                     nXInPortion = nXRight - nXPos;
3998 
3999                 // Search in Array...
4000 			    for ( sal_uInt16 x = 0; x < nMax; x++ )
4001 			    {
4002 				    long nTmpPosMax = pLine->GetCharPosArray().GetObject( nTmpCurIndex+x );
4003 				    if ( nTmpPosMax > nXInPortion )
4004 				    {
4005 					    // pruefen, ob dieser oder der davor...
4006                         long nTmpPosMin = x ? pLine->GetCharPosArray().GetObject( nTmpCurIndex+x-1 ) : 0;
4007 					    long nDiffLeft = nXInPortion - nTmpPosMin;
4008 					    long nDiffRight = nTmpPosMax - nXInPortion;
4009 					    DBG_ASSERT( nDiffLeft >= 0, "DiffLeft negativ" );
4010 					    DBG_ASSERT( nDiffRight >= 0, "DiffRight negativ" );
4011 					    nOffset = ( bSmart && ( nDiffRight < nDiffLeft ) ) ? x+1 : x;
4012 					    // I18N: If there are character position with the length of 0,
4013                         // they belong to the same character, we can not use this position as an index.
4014 					    // Skip all 0-positions, cheaper than using XBreakIterator:
4015 					    if ( nOffset < nMax )
4016 					    {
4017 						    const long nX = pLine->GetCharPosArray().GetObject(nOffset);
4018 						    while ( ( (nOffset+1) < nMax ) && ( pLine->GetCharPosArray().GetObject(nOffset+1) == nX ) )
4019 							    nOffset++;
4020 					    }
4021 					    break;
4022 				    }
4023 			    }
4024 
4025 			    // Bei Verwendung des CharPosArray duerfte es keine Ungenauigkeiten geben!
4026 			    // Vielleicht bei Kerning ?
4027 			    // 0xFFF passiert z.B. bei Outline-Font, wenn ganz hinten.
4028 			    if ( nOffset == 0xFFFF )
4029 				    nOffset = nMax;
4030 
4031 			    DBG_ASSERT( nOffset <= nMax, "nOffset > nMax" );
4032 
4033                 nChar = nChar + nOffset;
4034 
4035                 // Check if index is within a cell:
4036                 if ( nChar && ( nChar < pParaPortion->GetNode()->Len() ) )
4037                 {
4038                     EditPaM aPaM( pParaPortion->GetNode(), nChar+1 );
4039                     sal_uInt16 nScriptType = GetScriptType( aPaM );
4040                     if ( nScriptType == i18n::ScriptType::COMPLEX )
4041                     {
4042 		                uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
4043 		                sal_Int32 nCount = 1;
4044                         lang::Locale aLocale = GetLocale( aPaM );
4045                         sal_uInt16 nRight = (sal_uInt16)_xBI->nextCharacters( *pParaPortion->GetNode(), nChar, aLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount );
4046 						sal_uInt16 nLeft = (sal_uInt16)_xBI->previousCharacters( *pParaPortion->GetNode(), nRight, aLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount );
4047                         if ( ( nLeft != nChar ) && ( nRight != nChar ) )
4048                         {
4049                             nChar = ( Abs( nRight - nChar ) < Abs( nLeft - nChar ) ) ? nRight : nLeft;
4050                         }
4051                     }
4052                 }
4053             }
4054         }
4055 
4056         nCurIndex = nCurIndex + pPortion->GetLen();
4057     }
4058 
4059     if ( nChar == 0xFFFF )
4060     {
4061         nChar = ( nXPos <= pLine->GetStartPosX() ) ? pLine->GetStart() : pLine->GetEnd();
4062     }
4063 
4064     return nChar;
4065 }
4066 
GetLineXPosStartEnd(ParaPortion * pParaPortion,EditLine * pLine)4067 Range ImpEditEngine::GetLineXPosStartEnd( ParaPortion* pParaPortion, EditLine* pLine )
4068 {
4069     Range aLineXPosStartEnd;
4070 
4071     sal_uInt32 nPara = GetEditDoc().GetPos( pParaPortion->GetNode() );
4072     if ( !IsRightToLeft( nPara ) )
4073     {
4074         aLineXPosStartEnd.Min() = pLine->GetStartPosX();
4075         aLineXPosStartEnd.Max() = pLine->GetStartPosX() + pLine->GetTextWidth();
4076     }
4077     else
4078     {
4079         aLineXPosStartEnd.Min() = GetPaperSize().Width() - ( pLine->GetStartPosX() + pLine->GetTextWidth() );
4080         aLineXPosStartEnd.Max() = GetPaperSize().Width() - pLine->GetStartPosX();
4081     }
4082 
4083 
4084     return aLineXPosStartEnd;
4085 }
4086 
GetPortionXOffset(ParaPortion * pParaPortion,EditLine * pLine,sal_uInt16 nTextPortion)4087 long ImpEditEngine::GetPortionXOffset( ParaPortion* pParaPortion, EditLine* pLine, sal_uInt16 nTextPortion )
4088 {
4089 	long nX = pLine->GetStartPosX();
4090 
4091     for ( sal_uInt16 i = pLine->GetStartPortion(); i < nTextPortion; i++ )
4092 	{
4093 		TextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( i );
4094 		switch ( pPortion->GetKind() )
4095 		{
4096 			case PORTIONKIND_FIELD:
4097 			case PORTIONKIND_TEXT:
4098 			case PORTIONKIND_HYPHENATOR:
4099 			case PORTIONKIND_TAB:
4100 //	        case PORTIONKIND_EXTRASPACE:
4101 			{
4102 				nX += pPortion->GetSize().Width();
4103 			}
4104 			break;
4105 		}
4106     }
4107 
4108     sal_uInt32 nPara = GetEditDoc().GetPos( pParaPortion->GetNode() );
4109     sal_Bool bR2LPara = IsRightToLeft( nPara );
4110 
4111     TextPortion* pDestPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
4112     if ( pDestPortion->GetKind() != PORTIONKIND_TAB )
4113     {
4114         if ( !bR2LPara && pDestPortion->GetRightToLeft() )
4115         {
4116             // Portions behind must be added, visual before this portion
4117             sal_uInt16 nTmpPortion = nTextPortion+1;
4118             while ( nTmpPortion <= pLine->GetEndPortion() )
4119             {
4120 		        TextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
4121                 if ( pNextTextPortion->GetRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) )
4122                     nX += pNextTextPortion->GetSize().Width();
4123                 else
4124                     break;
4125                 nTmpPortion++;
4126             }
4127             // Portions before must be removed, visual behind this portion
4128             nTmpPortion = nTextPortion;
4129             while ( nTmpPortion > pLine->GetStartPortion() )
4130             {
4131                 --nTmpPortion;
4132 		        TextPortion* pPrevTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
4133                 if ( pPrevTextPortion->GetRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) )
4134                     nX -= pPrevTextPortion->GetSize().Width();
4135                 else
4136                     break;
4137             }
4138         }
4139         else if ( bR2LPara && !pDestPortion->IsRightToLeft() )
4140         {
4141             // Portions behind must be ermoved, visual behind this portion
4142             sal_uInt16 nTmpPortion = nTextPortion+1;
4143             while ( nTmpPortion <= pLine->GetEndPortion() )
4144             {
4145 		        TextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
4146                 if ( !pNextTextPortion->IsRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) )
4147                     nX += pNextTextPortion->GetSize().Width();
4148                 else
4149                     break;
4150                 nTmpPortion++;
4151             }
4152             // Portions before must be added, visual before this portion
4153             nTmpPortion = nTextPortion;
4154             while ( nTmpPortion > pLine->GetStartPortion() )
4155             {
4156                 --nTmpPortion;
4157 		        TextPortion* pPrevTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
4158                 if ( !pPrevTextPortion->IsRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) )
4159                     nX -= pPrevTextPortion->GetSize().Width();
4160                 else
4161                     break;
4162             }
4163         }
4164     }
4165     if ( bR2LPara )
4166     {
4167         // Switch X positions...
4168         DBG_ASSERT( GetTextRanger() || GetPaperSize().Width(), "GetPortionXOffset - paper size?!" );
4169         DBG_ASSERT( GetTextRanger() || (nX <= GetPaperSize().Width()), "GetPortionXOffset - position out of paper size!" );
4170         nX = GetPaperSize().Width() - nX;
4171         nX -= pDestPortion->GetSize().Width();
4172     }
4173 
4174     return nX;
4175 }
4176 
GetXPos(ParaPortion * pParaPortion,EditLine * pLine,sal_uInt16 nIndex,sal_Bool bPreferPortionStart)4177 long ImpEditEngine::GetXPos( ParaPortion* pParaPortion, EditLine* pLine, sal_uInt16 nIndex, sal_Bool bPreferPortionStart )
4178 {
4179 	DBG_ASSERT( pLine, "Keine Zeile erhalten: GetXPos" );
4180 	DBG_ASSERT( ( nIndex >= pLine->GetStart() ) && ( nIndex <= pLine->GetEnd() ) , "GetXPos muss richtig gerufen werden!" );
4181 
4182     sal_Bool bDoPreferPortionStart = bPreferPortionStart;
4183     // Assure that the portion belongs to this line:
4184     if ( nIndex == pLine->GetStart() )
4185         bDoPreferPortionStart = sal_True;
4186     else if ( nIndex == pLine->GetEnd() )
4187         bDoPreferPortionStart = sal_False;
4188 
4189     sal_uInt16 nTextPortionStart = 0;
4190     sal_uInt16 nTextPortion = pParaPortion->GetTextPortions().FindPortion( nIndex, nTextPortionStart, bDoPreferPortionStart );
4191 
4192     DBG_ASSERT( ( nTextPortion >= pLine->GetStartPortion() ) && ( nTextPortion <= pLine->GetEndPortion() ), "GetXPos: Portion not in current line! " );
4193 
4194     TextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
4195 
4196     long nX = GetPortionXOffset( pParaPortion, pLine, nTextPortion );
4197 
4198     // calc text width, portion size may include CJK/CTL spacing...
4199     // But the array migh not be init yet, if using text ranger this method is called within CreateLines()...
4200     long nPortionTextWidth = pPortion->GetSize().Width();
4201     if ( ( pPortion->GetKind() == PORTIONKIND_TEXT ) && pPortion->GetLen() && !GetTextRanger() )
4202         nPortionTextWidth = pLine->GetCharPosArray().GetObject( nTextPortionStart + pPortion->GetLen() - 1 - pLine->GetStart() );
4203 
4204     if ( nTextPortionStart != nIndex )
4205     {
4206         // Search within portion...
4207         if ( nIndex == ( nTextPortionStart + pPortion->GetLen() ) )
4208         {
4209             // End of Portion
4210             if ( pPortion->GetKind() == PORTIONKIND_TAB )
4211             {
4212                 if ( (nTextPortion+1) < pParaPortion->GetTextPortions().Count() )
4213                 {
4214                     TextPortion* pNextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion+1 );
4215                     if ( pNextPortion->GetKind() != PORTIONKIND_TAB )
4216                     {
4217                         // DBG_ASSERT( !bPreferPortionStart, "GetXPos - How can we this tab portion here???" );
4218 						// #109879# We loop if nIndex == pLine->GetEnd, because bPreferPortionStart will be reset
4219 						if ( !bPreferPortionStart )
4220 							nX = GetXPos( pParaPortion, pLine, nIndex, sal_True );
4221 						else if ( !IsRightToLeft( GetEditDoc().GetPos( pParaPortion->GetNode() ) ) )
4222 							nX += nPortionTextWidth;
4223                     }
4224                 }
4225                 else if ( !IsRightToLeft( GetEditDoc().GetPos( pParaPortion->GetNode() ) ) )
4226                 {
4227 				    nX += nPortionTextWidth;
4228                 }
4229             }
4230             else if ( !pPortion->IsRightToLeft() )
4231             {
4232 				nX += nPortionTextWidth;
4233             }
4234         }
4235         else if ( pPortion->GetKind() == PORTIONKIND_TEXT )
4236         {
4237 			DBG_ASSERT( nIndex != pLine->GetStart(), "Strange behavior in new GetXPos()" );
4238 			DBG_ASSERT( pLine && pLine->GetCharPosArray().Count(), "svx::ImpEditEngine::GetXPos(), portion in an empty line?" );
4239 
4240 			if( pLine->GetCharPosArray().Count() )
4241 			{
4242 				sal_uInt16 nPos = nIndex - 1 - pLine->GetStart();
4243 				if( nPos >= pLine->GetCharPosArray().Count() )
4244 				{
4245 					nPos = pLine->GetCharPosArray().Count()-1;
4246 					DBG_ERROR("svx::ImpEditEngine::GetXPos(), index out of range!");
4247 				}
4248 
4249                 // old code restored see #i112788 (which leaves #i74188 unfixed again)
4250                 long nPosInPortion = pLine->GetCharPosArray().GetObject( nPos );
4251 
4252 				if ( !pPortion->IsRightToLeft() )
4253 				{
4254 					nX += nPosInPortion;
4255 				}
4256 				else
4257 				{
4258 					nX += nPortionTextWidth - nPosInPortion;
4259 				}
4260 
4261 				if ( pPortion->GetExtraInfos() && pPortion->GetExtraInfos()->bCompressed )
4262 				{
4263 					nX += pPortion->GetExtraInfos()->nPortionOffsetX;
4264 					if ( pPortion->GetExtraInfos()->nAsianCompressionTypes & CHAR_PUNCTUATIONRIGHT )
4265 					{
4266 						sal_uInt8 nType = GetCharTypeForCompression( pParaPortion->GetNode()->GetChar( nIndex ) );
4267 						if ( nType == CHAR_PUNCTUATIONRIGHT )
4268 						{
4269 							sal_uInt16 n = nIndex - nTextPortionStart;
4270 							const sal_Int32* pDXArray = pLine->GetCharPosArray().GetData()+( nTextPortionStart-pLine->GetStart() );
4271 							sal_Int32 nCharWidth = ( ( (n+1) < pPortion->GetLen() ) ? pDXArray[n] : pPortion->GetSize().Width() )
4272 															- ( n ? pDXArray[n-1] : 0 );
4273 							if ( (n+1) < pPortion->GetLen() )
4274 							{
4275 								// smaller, when char behind is CHAR_PUNCTUATIONRIGHT also
4276 								nType = GetCharTypeForCompression( pParaPortion->GetNode()->GetChar( nIndex+1 ) );
4277 								if ( nType == CHAR_PUNCTUATIONRIGHT )
4278 								{
4279 									sal_Int32 nNextCharWidth = ( ( (n+2) < pPortion->GetLen() ) ? pDXArray[n+1] : pPortion->GetSize().Width() )
4280 																	- pDXArray[n];
4281 									sal_Int32 nCompressed = nNextCharWidth/2;
4282 									nCompressed *= pPortion->GetExtraInfos()->nMaxCompression100thPercent;
4283 									nCompressed /= 10000;
4284 									nCharWidth += nCompressed;
4285 								}
4286 							}
4287 							else
4288 							{
4289 								nCharWidth *= 2;    // last char pos to portion end is only compressed size
4290 							}
4291 							nX += nCharWidth/2; // 50% compression
4292 						}
4293 					}
4294 				}
4295 			}
4296 		}
4297     }
4298     else // if ( nIndex == pLine->GetStart() )
4299     {
4300         if ( pPortion->IsRightToLeft() )
4301         {
4302 		    nX += nPortionTextWidth;
4303         }
4304     }
4305 
4306 	return nX;
4307 }
4308 
CalcHeight(ParaPortion * pPortion)4309 void ImpEditEngine::CalcHeight( ParaPortion* pPortion )
4310 {
4311 	pPortion->nHeight = 0;
4312 	pPortion->nFirstLineOffset = 0;
4313 
4314 	if ( pPortion->IsVisible() )
4315 	{
4316 		DBG_ASSERT( pPortion->GetLines().Count(), "Absatz ohne Zeilen in ParaPortion::CalcHeight" );
4317 		for ( sal_uInt16 nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
4318 			pPortion->nHeight += pPortion->GetLines().GetObject( nLine )->GetHeight();
4319 
4320 		if ( !aStatus.IsOutliner() )
4321 		{
4322 			const SvxULSpaceItem& rULItem = (const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
4323 			const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
4324 			sal_uInt16 nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX ) ? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
4325 
4326 			if ( nSBL )
4327 			{
4328 				if ( pPortion->GetLines().Count() > 1 )
4329 					pPortion->nHeight += ( pPortion->GetLines().Count() - 1 ) * nSBL;
4330 				if ( aStatus.ULSpaceSummation() )
4331 					pPortion->nHeight += nSBL;
4332 			}
4333 
4334 			sal_uInt32 nPortion = GetParaPortions().GetPos( pPortion );
4335 			if ( nPortion || aStatus.ULSpaceFirstParagraph() )
4336 			{
4337 				sal_uInt16 nUpper = GetYValue( rULItem.GetUpper() );
4338 				pPortion->nHeight += nUpper;
4339 				pPortion->nFirstLineOffset = nUpper;
4340 			}
4341 
4342 			if ( ( nPortion != (GetParaPortions().Count()-1) ) )
4343 			{
4344 				pPortion->nHeight += GetYValue( rULItem.GetLower() );	// nicht in letzter
4345 			}
4346 
4347 
4348 			if ( nPortion && !aStatus.ULSpaceSummation() )
4349 			{
4350 				ParaPortion* pPrev = GetParaPortions().SaveGetObject( nPortion-1 );
4351 				const SvxULSpaceItem& rPrevULItem = (const SvxULSpaceItem&)pPrev->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
4352 				const SvxLineSpacingItem& rPrevLSItem = (const SvxLineSpacingItem&)pPrev->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
4353 
4354 				// Verhalten WinWord6/Writer3:
4355 				// Bei einem proportionalen Zeilenabstand wird auch der Absatzabstand
4356 				// manipuliert.
4357 				// Nur Writer3: Nicht aufaddieren, sondern Mindestabstand.
4358 
4359 				// Pruefen, ob Abstand durch LineSpacing > Upper:
4360 				sal_uInt16 nExtraSpace = GetYValue( lcl_CalcExtraSpace( pPortion, rLSItem ) );
4361 				if ( nExtraSpace > pPortion->nFirstLineOffset )
4362 				{
4363 					// Absatz wird 'groesser':
4364 					pPortion->nHeight += ( nExtraSpace - pPortion->nFirstLineOffset );
4365 					pPortion->nFirstLineOffset = nExtraSpace;
4366 				}
4367 
4368 				// nFirstLineOffset jetzt f(pNode) => jetzt f(pNode, pPrev) ermitteln:
4369 				sal_uInt16 nPrevLower = GetYValue( rPrevULItem.GetLower() );
4370 
4371 				// Dieser PrevLower steckt noch in der Hoehe der PrevPortion...
4372 				if ( nPrevLower > pPortion->nFirstLineOffset )
4373 				{
4374 					// Absatz wird 'kleiner':
4375 					pPortion->nHeight -= pPortion->nFirstLineOffset;
4376 					pPortion->nFirstLineOffset = 0;
4377 				}
4378 				else if ( nPrevLower )
4379 				{
4380 					// Absatz wird 'etwas kleiner':
4381 					pPortion->nHeight -= nPrevLower;
4382 					pPortion->nFirstLineOffset =
4383                         pPortion->nFirstLineOffset - nPrevLower;
4384 				}
4385 
4386 				// Finde ich zwar nicht so gut, aber Writer3-Feature:
4387 				// Pruefen, ob Abstand durch LineSpacing > Lower:
4388 				// Dieser Wert steckt nicht in der Hoehe der PrevPortion.
4389 				if ( !pPrev->IsInvalid() )
4390 				{
4391 					nExtraSpace = GetYValue( lcl_CalcExtraSpace( pPrev, rPrevLSItem ) );
4392 					if ( nExtraSpace > nPrevLower )
4393 					{
4394 						sal_uInt16 nMoreLower = nExtraSpace - nPrevLower;
4395 						// Absatz wird 'groesser', 'waechst' nach unten:
4396 						if ( nMoreLower > pPortion->nFirstLineOffset )
4397 						{
4398 							pPortion->nHeight += ( nMoreLower - pPortion->nFirstLineOffset );
4399 							pPortion->nFirstLineOffset = nMoreLower;
4400 						}
4401 					}
4402 				}
4403 			}
4404 		}
4405 	}
4406 }
4407 
GetEditCursor(ParaPortion * pPortion,sal_uInt16 nIndex,sal_uInt16 nFlags)4408 Rectangle ImpEditEngine::GetEditCursor( ParaPortion* pPortion, sal_uInt16 nIndex, sal_uInt16 nFlags )
4409 {
4410 	DBG_ASSERT( pPortion->IsVisible(), "Wozu GetEditCursor() bei einem unsichtbaren Absatz?" );
4411 	DBG_ASSERT( IsFormatted() || GetTextRanger(), "GetEditCursor: Nicht formatiert" );
4412 
4413 	/*
4414 	 GETCRSR_ENDOFLINE: Wenn hinter dem letzten Zeichen einer umgebrochenen Zeile,
4415 	 am Ende der Zeile bleiben, nicht am Anfang der naechsten.
4416 	 Zweck: 	- END => wirklich hinter das letzte Zeichen
4417 				- Selektion....
4418 	*/
4419 
4420 	long nY = pPortion->GetFirstLineOffset();
4421 
4422 	const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
4423 	sal_uInt16 nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
4424 						? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
4425 
4426 	sal_uInt16 nCurIndex = 0;
4427 	DBG_ASSERT( pPortion->GetLines().Count(), "Leere ParaPortion in GetEditCursor!" );
4428 	EditLine* pLine = 0;
4429 	sal_Bool bEOL = ( nFlags & GETCRSR_ENDOFLINE ) ? sal_True : sal_False;
4430 	for ( sal_uInt16 nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
4431 	{
4432 		EditLine* pTmpLine = pPortion->GetLines().GetObject( nLine );
4433 		if ( ( pTmpLine->GetStart() == nIndex ) || ( pTmpLine->IsIn( nIndex, bEOL ) ) )
4434 		{
4435 			pLine = pTmpLine;
4436 			break;
4437 		}
4438 
4439 		nCurIndex = nCurIndex + pTmpLine->GetLen();
4440 		nY += pTmpLine->GetHeight();
4441 		if ( !aStatus.IsOutliner() )
4442 			nY += nSBL;
4443 	}
4444 	if ( !pLine )
4445 	{
4446 		// Cursor am Ende des Absatzes.
4447 		DBG_ASSERT( nIndex == nCurIndex, "Index voll daneben in GetEditCursor!" );
4448 
4449 		pLine = pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1 );
4450 		nY -= pLine->GetHeight();
4451 		if ( !aStatus.IsOutliner() )
4452 			nY -= nSBL;
4453 		nCurIndex = nCurIndex -  pLine->GetLen();
4454 	}
4455 
4456 	Rectangle aEditCursor;
4457 
4458 	aEditCursor.Top() = nY;
4459 	nY += pLine->GetHeight();
4460 	aEditCursor.Bottom() = nY-1;
4461 
4462 	// innerhalb der Zeile suchen...
4463     long nX;
4464 
4465     if ( ( nIndex == pLine->GetStart() ) && ( nFlags & GETCRSR_STARTOFLINE ) )
4466     {
4467         Range aXRange = GetLineXPosStartEnd( pPortion, pLine );
4468         nX = !IsRightToLeft( GetEditDoc().GetPos( pPortion->GetNode() ) ) ? aXRange.Min() : aXRange.Max();
4469     }
4470     else if ( ( nIndex == pLine->GetEnd() ) && ( nFlags & GETCRSR_ENDOFLINE ) )
4471     {
4472         Range aXRange = GetLineXPosStartEnd( pPortion, pLine );
4473         nX = !IsRightToLeft( GetEditDoc().GetPos( pPortion->GetNode() ) ) ? aXRange.Max() : aXRange.Min();
4474     }
4475     else
4476     {
4477         nX = GetXPos( pPortion, pLine, nIndex, ( nFlags & GETCRSR_PREFERPORTIONSTART ) ? sal_True : sal_False );
4478     }
4479 
4480     aEditCursor.Left() = aEditCursor.Right() = nX;
4481 
4482 	if ( nFlags & GETCRSR_TXTONLY )
4483 		aEditCursor.Top() = aEditCursor.Bottom() - pLine->GetTxtHeight() + 1;
4484 	else
4485 		aEditCursor.Top() = aEditCursor.Bottom() - Min( pLine->GetTxtHeight(), pLine->GetHeight() ) + 1;
4486 
4487 	return aEditCursor;
4488 }
4489 
SetValidPaperSize(const Size & rNewSz)4490 void ImpEditEngine::SetValidPaperSize( const Size& rNewSz )
4491 {
4492 	aPaperSize = rNewSz;
4493 
4494 	long nMinWidth = aStatus.AutoPageWidth() ? aMinAutoPaperSize.Width() : 0;
4495 	long nMaxWidth = aStatus.AutoPageWidth() ? aMaxAutoPaperSize.Width() : 0x7FFFFFFF;
4496 	long nMinHeight = aStatus.AutoPageHeight() ? aMinAutoPaperSize.Height() : 0;
4497 	long nMaxHeight = aStatus.AutoPageHeight() ? aMaxAutoPaperSize.Height() : 0x7FFFFFFF;
4498 
4499 	// Minimale/Maximale Breite:
4500 	if ( aPaperSize.Width() < nMinWidth )
4501 		aPaperSize.Width() = nMinWidth;
4502 	else if ( aPaperSize.Width() > nMaxWidth )
4503 		aPaperSize.Width() = nMaxWidth;
4504 
4505 	// Minimale/Maximale Hoehe:
4506 	if ( aPaperSize.Height() < nMinHeight )
4507 		aPaperSize.Height() = nMinHeight;
4508 	else if ( aPaperSize.Height() > nMaxHeight )
4509 		aPaperSize.Height() = nMaxHeight;
4510 }
4511 
IndentBlock(EditView * pEditView,sal_Bool bRight)4512 void ImpEditEngine::IndentBlock( EditView* pEditView, sal_Bool bRight )
4513 {
4514 	ESelection aESel( CreateESel( pEditView->pImpEditView->GetEditSelection() ) );
4515 	aESel.Adjust();
4516 
4517 	// Nur wenn mehrere selektierte Absaetze...
4518 	if ( aESel.nEndPara > aESel.nStartPara )
4519 	{
4520 		ESelection aNewSel = aESel;
4521 		aNewSel.nStartPos = 0;
4522 		aNewSel.nEndPos = EE_INDEX_MAX;
4523 
4524 		if ( aESel.nEndPos == 0 )
4525 		{
4526 			aESel.nEndPara--;		// dann diesen Absatz nicht...
4527 			aNewSel.nEndPos = 0;
4528 		}
4529 
4530 		pEditView->pImpEditView->DrawSelection();
4531 		pEditView->pImpEditView->SetEditSelection(
4532 						pEditView->pImpEditView->GetEditSelection().Max() );
4533 		UndoActionStart( bRight ? EDITUNDO_INDENTBLOCK : EDITUNDO_UNINDENTBLOCK );
4534 
4535 		for ( sal_uInt32 nPara = aESel.nStartPara; nPara <= aESel.nEndPara; nPara++ )
4536 		{
4537 			ContentNode* pNode = GetEditDoc().GetObject( nPara );
4538 			if ( bRight )
4539 			{
4540 				// Tabs hinzufuegen
4541 				EditPaM aPaM( pNode, 0 );
4542 				InsertTab( aPaM );
4543 			}
4544 			else
4545 			{
4546 				// Tabs entfernen
4547 				EditCharAttrib* pFeature = pNode->GetCharAttribs().FindFeature( 0 );
4548 				if ( pFeature && ( pFeature->GetStart() == 0 ) &&
4549 				   ( pFeature->GetItem()->Which() == EE_FEATURE_TAB ) )
4550 				{
4551 					EditPaM aStartPaM( pNode, 0 );
4552 					EditPaM aEndPaM( pNode, 1 );
4553 					ImpDeleteSelection( EditSelection( aStartPaM, aEndPaM ) );
4554 				}
4555 			}
4556 		}
4557 
4558 		UndoActionEnd( bRight ? EDITUNDO_INDENTBLOCK : EDITUNDO_UNINDENTBLOCK );
4559 		UpdateSelections();
4560 		FormatAndUpdate( pEditView );
4561 
4562 		ContentNode* pLastNode = GetEditDoc().GetObject( aNewSel.nEndPara );
4563 		if ( pLastNode->Len() < aNewSel.nEndPos )
4564 			aNewSel.nEndPos = pLastNode->Len();
4565 		pEditView->pImpEditView->SetEditSelection( CreateSel( aNewSel ) );
4566 		pEditView->pImpEditView->DrawSelection();
4567 		pEditView->pImpEditView->ShowCursor( sal_False, sal_True );
4568 	}
4569 }
4570 
GetForbiddenCharsTable(sal_Bool bGetInternal) const4571 vos::ORef<SvxForbiddenCharactersTable> ImpEditEngine::GetForbiddenCharsTable( sal_Bool bGetInternal ) const
4572 {
4573 	vos::ORef<SvxForbiddenCharactersTable> xF = xForbiddenCharsTable;
4574 	if ( !xF.isValid() && bGetInternal )
4575 		xF = EE_DLL()->GetGlobalData()->GetForbiddenCharsTable();
4576 	return xF;
4577 }
4578 
SetForbiddenCharsTable(vos::ORef<SvxForbiddenCharactersTable> xForbiddenChars)4579 void ImpEditEngine::SetForbiddenCharsTable( vos::ORef<SvxForbiddenCharactersTable> xForbiddenChars )
4580 {
4581 	EE_DLL()->GetGlobalData()->SetForbiddenCharsTable( xForbiddenChars );
4582 }
4583 
GetColorConfig()4584 svtools::ColorConfig& ImpEditEngine::GetColorConfig()
4585 {
4586     if ( !pColorConfig )
4587         pColorConfig = new svtools::ColorConfig;
4588 
4589     return *pColorConfig;
4590 }
4591 
IsVisualCursorTravelingEnabled()4592 sal_Bool ImpEditEngine::IsVisualCursorTravelingEnabled()
4593 {
4594     sal_Bool bVisualCursorTravaling = sal_False;
4595 
4596     if( !pCTLOptions )
4597         pCTLOptions = new SvtCTLOptions;
4598 
4599     if ( pCTLOptions->IsCTLFontEnabled() && ( pCTLOptions->GetCTLCursorMovement() == SvtCTLOptions::MOVEMENT_VISUAL ) )
4600     {
4601         bVisualCursorTravaling = sal_True;
4602     }
4603 
4604     return bVisualCursorTravaling;
4605 
4606 }
4607 
DoVisualCursorTraveling(const ContentNode *)4608 sal_Bool ImpEditEngine::DoVisualCursorTraveling( const ContentNode* )
4609 {
4610     // Don't check if it's necessary, because we also need it when leaving the paragraph
4611     return IsVisualCursorTravelingEnabled();
4612 /*
4613     sal_Bool bDoVisualCursorTraveling = sal_False;
4614 
4615     if ( IsVisualCursorTravelingEnabled() && pNode->Len() )
4616     {
4617         // Only necessary when RTL text in LTR para or LTR text in RTL para
4618         bDoVisualCursorTraveling = HasDifferentRTLLevels( pNode );
4619     }
4620 
4621     return bDoVisualCursorTraveling;
4622 */
4623 }
4624 
4625 
CallNotify(EENotify & rNotify)4626 void ImpEditEngine::CallNotify( EENotify& rNotify )
4627 {
4628     if ( !nBlockNotifications )
4629     {
4630         GetNotifyHdl().Call( &rNotify );
4631     }
4632     else
4633     {
4634         EENotify* pNewNotify = new EENotify( rNotify );
4635         aNotifyCache.Insert( pNewNotify, aNotifyCache.Count() );
4636     }
4637 }
4638 
EnterBlockNotifications()4639 void ImpEditEngine::EnterBlockNotifications()
4640 {
4641     if( !nBlockNotifications )
4642     {
4643         // #109864# Send out START notification immediately, to allow
4644         // external, non-queued events to be captured as well from
4645         // client side
4646         EENotify aNotify( EE_NOTIFY_BLOCKNOTIFICATION_START );
4647         aNotify.pEditEngine = GetEditEnginePtr();
4648         GetNotifyHdl().Call( &aNotify );
4649     }
4650 
4651     nBlockNotifications++;
4652 }
4653 
LeaveBlockNotifications()4654 void ImpEditEngine::LeaveBlockNotifications()
4655 {
4656     DBG_ASSERT( nBlockNotifications, "LeaveBlockNotifications - Why?" );
4657 
4658     nBlockNotifications--;
4659     if ( !nBlockNotifications )
4660     {
4661         // Call blocked notify events...
4662         while ( aNotifyCache.Count() )
4663         {
4664             EENotify* pNotify = aNotifyCache[0];
4665             // Remove from list before calling, maybe we enter LeaveBlockNotifications while calling the handler...
4666             aNotifyCache.Remove( 0 );
4667             GetNotifyHdl().Call( pNotify );
4668             delete pNotify;
4669         }
4670 
4671         EENotify aNotify( EE_NOTIFY_BLOCKNOTIFICATION_END );
4672         aNotify.pEditEngine = GetEditEnginePtr();
4673         GetNotifyHdl().Call( &aNotify );
4674     }
4675 }
4676 
IMPL_LINK(ImpEditEngine,DocModified,void *,EMPTYARG)4677 IMPL_LINK( ImpEditEngine, DocModified, void*, EMPTYARG )
4678 {
4679     aModifyHdl.Call( NULL /*GetEditEnginePtr()*/ ); // NULL, because also used for Outliner
4680     return 0;
4681 }
4682