xref: /trunk/main/editeng/source/editeng/impedit2.cxx (revision cf6516809c57e1bb0a940545cca99cdad54d4ce2)
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