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