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