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