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