xref: /AOO41X/main/svtools/source/edit/texteng.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_svtools.hxx"
26 
27 
28 #include <tools/stream.hxx>
29 
30 #include <svtools/texteng.hxx>
31 #include <svtools/textview.hxx>
32 #include <textdoc.hxx>
33 #include <textdat2.hxx>
34 #include <textundo.hxx>
35 #include <textund2.hxx>
36 #include <svl/ctloptions.hxx>
37 #include <vcl/window.hxx>
38 
39 #include <vcl/edit.hxx>
40 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
41 #include <com/sun/star/beans/PropertyValues.hpp>
42 
43 #ifndef _COM_SUN_STAR_TEXT_XBREAKITERATOR_HPP_
44 #include <com/sun/star/i18n/XBreakIterator.hpp>
45 #endif
46 
47 #ifndef _COM_SUN_STAR_TEXT_CHARACTERITERATORMODE_HPP_
48 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
49 #endif
50 
51 #ifndef _COM_SUN_STAR_TEXT_WORDTYPE_HPP_
52 #include <com/sun/star/i18n/WordType.hpp>
53 #endif
54 
55 #ifndef _COM_SUN_STAR_I18N_XEXTENDEDINPUTSEQUENCECHECKER_HDL_
56 #include <com/sun/star/i18n/XExtendedInputSequenceChecker.hpp>
57 #endif
58 #include <com/sun/star/i18n/InputSequenceCheckMode.hpp>
59 #include <com/sun/star/i18n/ScriptType.hpp>
60 
61 #include <comphelper/processfactory.hxx>
62 
63 #include <unotools/localedatawrapper.hxx>
64 #include <vcl/unohelp.hxx>
65 
66 #include <vcl/svapp.hxx>
67 #include <vcl/unohelp.hxx>
68 #include <vcl/metric.hxx>
69 
70 #include <unicode/ubidi.h>
71 
72 using namespace ::com::sun::star;
73 using namespace ::com::sun::star::uno;
74 using namespace ::rtl;
75 
76 typedef TextView* TextViewPtr;
77 SV_DECL_PTRARR( TextViews, TextViewPtr, 0, 1 )
78 // SV_IMPL_PTRARR( TextViews, TextViewPtr );
79 
80 SV_DECL_VARARR_SORT( TESortedPositions, sal_uLong, 16, 8 )
SV_IMPL_VARARR_SORT(TESortedPositions,sal_uLong)81 SV_IMPL_VARARR_SORT( TESortedPositions, sal_uLong )
82 
83 #define RESDIFF     10
84 #define SCRLRANGE   20      // 1/20 der Breite/Hoehe scrollen, wenn im QueryDrop
85 
86 
87 // -------------------------------------------------------------------------
88 // (-) class TextEngine
89 // -------------------------------------------------------------------------
90 TextEngine::TextEngine()
91 {
92     mpDoc = 0;
93     mpTEParaPortions = 0;
94 
95     mpViews = new TextViews;
96     mpActiveView = NULL;
97 
98     mbIsFormatting      = sal_False;
99     mbFormatted         = sal_False;
100     mbUpdate            = sal_True;
101     mbModified          = sal_False;
102     mbUndoEnabled       = sal_False;
103     mbIsInUndo          = sal_False;
104     mbDowning           = sal_False;
105     mbRightToLeft       = sal_False;
106     mbHasMultiLineParas = sal_False;
107 
108     meAlign         = TXTALIGN_LEFT;
109 
110     mnMaxTextWidth  = 0;
111     mnMaxTextLen    = 0;
112     mnCurTextWidth  = 0xFFFFFFFF;
113     mnCurTextHeight = 0;
114 
115     mpUndoManager   = NULL;
116     mpIMEInfos      = NULL;
117     mpLocaleDataWrapper = NULL;
118 
119     mpIdleFormatter = new IdleFormatter;
120     mpIdleFormatter->SetTimeoutHdl( LINK( this, TextEngine, IdleFormatHdl ) );
121 
122     mpRefDev = new VirtualDevice;
123 
124     ImpInitLayoutMode( mpRefDev );
125 
126     ImpInitDoc();
127 
128     maTextColor = COL_BLACK;
129     Font aFont;
130     aFont.SetTransparent( sal_False );
131     Color aFillColor( aFont.GetFillColor() );
132     aFillColor.SetTransparency( 0 );
133     aFont.SetFillColor( aFillColor );
134     SetFont( aFont );
135 }
136 
~TextEngine()137 TextEngine::~TextEngine()
138 {
139     mbDowning = sal_True;
140 
141     delete mpIdleFormatter;
142     delete mpDoc;
143     delete mpTEParaPortions;
144     delete mpViews; // nur die Liste, nicht die Vies
145     delete mpRefDev;
146     delete mpUndoManager;
147     delete mpIMEInfos;
148     delete mpLocaleDataWrapper;
149 }
150 
InsertView(TextView * pTextView)151 void TextEngine::InsertView( TextView* pTextView )
152 {
153     mpViews->Insert( pTextView, mpViews->Count() );
154     pTextView->SetSelection( TextSelection() );
155 
156     if ( !GetActiveView() )
157         SetActiveView( pTextView );
158 }
159 
RemoveView(TextView * pTextView)160 void TextEngine::RemoveView( TextView* pTextView )
161 {
162     sal_uInt16 nPos = mpViews->GetPos( pTextView );
163     if( nPos != USHRT_MAX )
164     {
165         pTextView->HideCursor();
166         mpViews->Remove( nPos, 1 );
167         if ( pTextView == GetActiveView() )
168             SetActiveView( 0 );
169     }
170 }
171 
GetViewCount() const172 sal_uInt16 TextEngine::GetViewCount() const
173 {
174     return mpViews->Count();
175 }
176 
GetView(sal_uInt16 nView) const177 TextView* TextEngine::GetView( sal_uInt16 nView ) const
178 {
179     return mpViews->GetObject( nView );
180 }
181 
GetActiveView() const182 TextView* TextEngine::GetActiveView() const
183 {
184     return mpActiveView;
185 }
186 
SetActiveView(TextView * pTextView)187 void TextEngine::SetActiveView( TextView* pTextView )
188 {
189     if ( pTextView != mpActiveView )
190     {
191         if ( mpActiveView )
192             mpActiveView->HideSelection();
193 
194         mpActiveView = pTextView;
195 
196         if ( mpActiveView )
197             mpActiveView->ShowSelection();
198     }
199 }
200 
SetFont(const Font & rFont)201 void TextEngine::SetFont( const Font& rFont )
202 {
203     if ( rFont != maFont )
204     {
205         maFont = rFont;
206         // #i40221# As the font's color now defaults to transparent (since i35764)
207         //  we have to choose a useful textcolor in this case.
208         // Otherwise maTextColor and maFont.GetColor() are both transparent....
209         if( rFont.GetColor() == COL_TRANSPARENT )
210             maTextColor = COL_BLACK;
211         else
212             maTextColor = rFont.GetColor();
213 
214         // Wegen Selektion keinen Transparenten Font zulassen...
215         // (Sonst spaeter in ImplPaint den Hintergrund anders loeschen...)
216         maFont.SetTransparent( sal_False );
217         // Tell VCL not to use the font color, use text color from OutputDevice
218         maFont.SetColor( COL_TRANSPARENT );
219         Color aFillColor( maFont.GetFillColor() );
220         aFillColor.SetTransparency( 0 );
221         maFont.SetFillColor( aFillColor );
222 
223         maFont.SetAlign( ALIGN_TOP );
224         mpRefDev->SetFont( maFont);
225         Size aTextSize;
226         aTextSize.Width() = mpRefDev->GetTextWidth( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "    " ) ) );
227         aTextSize.Height() = mpRefDev->GetTextHeight();
228         if ( !aTextSize.Width() )
229             aTextSize.Width() = mpRefDev->GetTextWidth( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "XXXX" ) ) );
230 
231         mnDefTab = (sal_uInt16)aTextSize.Width();
232         if ( !mnDefTab )
233             mnDefTab = 1;
234         mnCharHeight = (sal_uInt16)aTextSize.Height();
235 /*
236         // #93746# Doesn't work with CJK HalfWidth/FullWidth
237         FontMetric aRealFont( mpRefDev->GetFontMetric() );
238         if ( aRealFont.GetPitch() == PITCH_FIXED )
239         {
240             String aX100;
241             aX100.Fill( 100, 'X' );
242             mnFixCharWidth100 = (sal_uInt16)mpRefDev->GetTextWidth( aX100 );
243         }
244         else
245 */
246         mnFixCharWidth100 = 0;
247 
248         FormatFullDoc();
249         UpdateViews();
250 
251         for ( sal_uInt16 nView = mpViews->Count(); nView; )
252         {
253             TextView* pView = mpViews->GetObject( --nView );
254             pView->GetWindow()->SetInputContext( InputContext( GetFont(), !pView->IsReadOnly() ? INPUTCONTEXT_TEXT|INPUTCONTEXT_EXTTEXTINPUT : 0 ) );
255         }
256     }
257 }
258 
SetDefTab(sal_uInt16 nDefTab)259 void TextEngine::SetDefTab( sal_uInt16 nDefTab )
260 {
261     mnDefTab = nDefTab;
262     // evtl neu setzen?
263 }
264 
SetMaxTextLen(sal_uLong nLen)265 void TextEngine::SetMaxTextLen( sal_uLong nLen )
266 {
267     mnMaxTextLen = nLen;
268 }
269 
SetMaxTextWidth(sal_uLong nMaxWidth)270 void TextEngine::SetMaxTextWidth( sal_uLong nMaxWidth )
271 {
272     if ( nMaxWidth != mnMaxTextWidth )
273     {
274         mnMaxTextWidth = Min( nMaxWidth, (sal_uLong)0x7FFFFFFF );
275         FormatFullDoc();
276         UpdateViews();
277     }
278 }
279 
280 static sal_Unicode static_aLFText[] = { '\n', 0 };
281 static sal_Unicode static_aCRText[] = { '\r', 0 };
282 static sal_Unicode static_aCRLFText[] = { '\r', '\n', 0 };
283 
static_getLineEndText(LineEnd aLineEnd)284 static inline const sal_Unicode* static_getLineEndText( LineEnd aLineEnd )
285 {
286     const sal_Unicode* pRet = NULL;
287 
288     switch( aLineEnd )
289     {
290     case LINEEND_LF: pRet = static_aLFText;break;
291     case LINEEND_CR: pRet = static_aCRText;break;
292     case LINEEND_CRLF: pRet = static_aCRLFText;break;
293     }
294     return pRet;
295 }
296 
ReplaceText(const TextSelection & rSel,const String & rText)297 void  TextEngine::ReplaceText(const TextSelection& rSel, const String& rText)
298 {
299     ImpInsertText( rSel, rText );
300 }
301 
GetText(LineEnd aSeparator) const302 String TextEngine::GetText( LineEnd aSeparator ) const
303 {
304     return mpDoc->GetText( static_getLineEndText( aSeparator ) );
305 }
306 
GetTextLines(LineEnd aSeparator) const307 String TextEngine::GetTextLines( LineEnd aSeparator ) const
308 {
309     String aText;
310     sal_uLong nParas = mpTEParaPortions->Count();
311     const sal_Unicode* pSep = static_getLineEndText( aSeparator );
312     for ( sal_uLong nP = 0; nP < nParas; nP++ )
313     {
314         TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nP );
315 
316         sal_uInt16 nLines = pTEParaPortion->GetLines().Count();
317         for ( sal_uInt16 nL = 0; nL < nLines; nL++ )
318         {
319             TextLine* pLine = pTEParaPortion->GetLines()[nL];
320             aText += pTEParaPortion->GetNode()->GetText().Copy( pLine->GetStart(), pLine->GetEnd() - pLine->GetStart() );
321             if ( pSep && ( ( (nP+1) < nParas ) || ( (nL+1) < nLines ) ) )
322                 aText += pSep;
323         }
324     }
325     return aText;
326 }
327 
GetText(sal_uLong nPara) const328 String TextEngine::GetText( sal_uLong nPara ) const
329 {
330     return mpDoc->GetText( nPara );
331 }
332 
GetTextLen(LineEnd aSeparator) const333 sal_uLong TextEngine::GetTextLen( LineEnd aSeparator ) const
334 {
335     return mpDoc->GetTextLen( static_getLineEndText( aSeparator ) );
336 }
337 
GetTextLen(const TextSelection & rSel,LineEnd aSeparator) const338 sal_uLong TextEngine::GetTextLen( const TextSelection& rSel, LineEnd aSeparator ) const
339 {
340     TextSelection aSel( rSel );
341     aSel.Justify();
342     ValidateSelection( aSel );
343     return mpDoc->GetTextLen( static_getLineEndText( aSeparator ), &aSel );
344 }
345 
GetTextLen(sal_uLong nPara) const346 sal_uInt16 TextEngine::GetTextLen( sal_uLong nPara ) const
347 {
348     return mpDoc->GetNodes().GetObject( nPara )->GetText().Len();
349 }
350 
SetUpdateMode(sal_Bool bUpdate)351 void TextEngine::SetUpdateMode( sal_Bool bUpdate )
352 {
353     if ( bUpdate != mbUpdate )
354     {
355         mbUpdate = bUpdate;
356         if ( mbUpdate )
357         {
358             FormatAndUpdate( GetActiveView() );
359             if ( GetActiveView() )
360                 GetActiveView()->ShowCursor();
361         }
362     }
363 }
364 
DoesKeyMoveCursor(const KeyEvent & rKeyEvent)365 sal_Bool TextEngine::DoesKeyMoveCursor( const KeyEvent& rKeyEvent )
366 {
367     sal_Bool bDoesMove = sal_False;
368 
369     switch ( rKeyEvent.GetKeyCode().GetCode() )
370     {
371         case KEY_UP:
372         case KEY_DOWN:
373         case KEY_LEFT:
374         case KEY_RIGHT:
375         case KEY_HOME:
376         case KEY_END:
377         case KEY_PAGEUP:
378         case KEY_PAGEDOWN:
379         {
380             if ( !rKeyEvent.GetKeyCode().IsMod2() )
381                 bDoesMove = sal_True;
382         }
383         break;
384     }
385     return bDoesMove;
386 }
387 
DoesKeyChangeText(const KeyEvent & rKeyEvent)388 sal_Bool TextEngine::DoesKeyChangeText( const KeyEvent& rKeyEvent )
389 {
390     sal_Bool bDoesChange = sal_False;
391 
392     KeyFuncType eFunc = rKeyEvent.GetKeyCode().GetFunction();
393     if ( eFunc != KEYFUNC_DONTKNOW )
394     {
395         switch ( eFunc )
396         {
397             case KEYFUNC_UNDO:
398             case KEYFUNC_REDO:
399             case KEYFUNC_CUT:
400             case KEYFUNC_PASTE: bDoesChange = sal_True;
401             break;
402             default:    // wird dann evtl. unten bearbeitet.
403                         eFunc = KEYFUNC_DONTKNOW;
404         }
405     }
406     if ( eFunc == KEYFUNC_DONTKNOW )
407     {
408         switch ( rKeyEvent.GetKeyCode().GetCode() )
409         {
410             case KEY_DELETE:
411             case KEY_BACKSPACE:
412             {
413                 if ( !rKeyEvent.GetKeyCode().IsMod2() )
414                     bDoesChange = sal_True;
415             }
416             break;
417             case KEY_RETURN:
418             case KEY_TAB:
419             {
420                 if ( !rKeyEvent.GetKeyCode().IsMod1() && !rKeyEvent.GetKeyCode().IsMod2() )
421                     bDoesChange = sal_True;
422             }
423             break;
424             default:
425             {
426                 bDoesChange = TextEngine::IsSimpleCharInput( rKeyEvent );
427             }
428         }
429     }
430     return bDoesChange;
431 }
432 
IsSimpleCharInput(const KeyEvent & rKeyEvent)433 sal_Bool TextEngine::IsSimpleCharInput( const KeyEvent& rKeyEvent )
434 {
435     if( rKeyEvent.GetCharCode() >= 32 && rKeyEvent.GetCharCode() != 127 &&
436         KEY_MOD1 != (rKeyEvent.GetKeyCode().GetModifier() & ~KEY_SHIFT) && // (ssa) #i45714#:
437         KEY_MOD2 != (rKeyEvent.GetKeyCode().GetModifier() & ~KEY_SHIFT) )  // check for Ctrl and Alt separately
438     {
439         return sal_True;
440     }
441     return sal_False;
442 }
443 
ImpInitDoc()444 void TextEngine::ImpInitDoc()
445 {
446     if ( mpDoc )
447         mpDoc->Clear();
448     else
449         mpDoc = new TextDoc;
450 
451     delete mpTEParaPortions;
452     mpTEParaPortions = new TEParaPortions;
453 
454     TextNode* pNode = new TextNode( String() );
455     mpDoc->GetNodes().Insert( pNode, 0 );
456 
457     TEParaPortion* pIniPortion = new TEParaPortion( pNode );
458     mpTEParaPortions->Insert( pIniPortion, (sal_uLong)0 );
459 
460     mbFormatted = sal_False;
461 
462     ImpParagraphRemoved( TEXT_PARA_ALL );
463     ImpParagraphInserted( 0 );
464 }
465 
GetText(const TextSelection & rSel,LineEnd aSeparator) const466 String TextEngine::GetText( const TextSelection& rSel, LineEnd aSeparator ) const
467 {
468     String aText;
469 
470     if ( !rSel.HasRange() )
471         return aText;
472 
473     TextSelection aSel( rSel );
474     aSel.Justify();
475 
476     sal_uLong nStartPara = aSel.GetStart().GetPara();
477     sal_uLong nEndPara = aSel.GetEnd().GetPara();
478     const sal_Unicode* pSep = static_getLineEndText( aSeparator );
479     for ( sal_uLong nNode = aSel.GetStart().GetPara(); nNode <= nEndPara; nNode++ )
480     {
481         TextNode* pNode = mpDoc->GetNodes().GetObject( nNode );
482 
483         sal_uInt16 nStartPos = 0;
484         sal_uInt16 nEndPos = pNode->GetText().Len();
485         if ( nNode == nStartPara )
486             nStartPos = aSel.GetStart().GetIndex();
487         if ( nNode == nEndPara ) // kann auch == nStart sein!
488             nEndPos = aSel.GetEnd().GetIndex();
489 
490         aText += pNode->GetText().Copy( nStartPos, nEndPos-nStartPos );
491         if ( nNode < nEndPara )
492             aText += pSep;
493     }
494     return aText;
495 }
496 
ImpRemoveText()497 void TextEngine::ImpRemoveText()
498 {
499     ImpInitDoc();
500 
501     TextPaM aStartPaM( 0, 0 );
502     TextSelection aEmptySel( aStartPaM, aStartPaM );
503     for ( sal_uInt16 nView = 0; nView < mpViews->Count(); nView++ )
504     {
505         TextView* pView = mpViews->GetObject( nView );
506         pView->ImpSetSelection( aEmptySel );
507     }
508     ResetUndo();
509 }
510 
SetText(const XubString & rText)511 void TextEngine::SetText( const XubString& rText )
512 {
513     ImpRemoveText();
514 
515     sal_Bool bUndoCurrentlyEnabled = IsUndoEnabled();
516     // Der von Hand reingesteckte Text kann nicht vom Anwender rueckgaengig gemacht werden.
517     EnableUndo( sal_False );
518 
519     TextPaM aStartPaM( 0, 0 );
520     TextSelection aEmptySel( aStartPaM, aStartPaM );
521 
522     TextPaM aPaM = aStartPaM;
523     if ( rText.Len() )
524         aPaM = ImpInsertText( aEmptySel, rText );
525 
526     for ( sal_uInt16 nView = 0; nView < mpViews->Count(); nView++ )
527     {
528         TextView* pView = mpViews->GetObject( nView );
529         pView->ImpSetSelection( aEmptySel );
530 
531         // Wenn kein Text, dann auch Kein Format&Update
532         // => Der Text bleibt stehen.
533         if ( !rText.Len() && GetUpdateMode() )
534             pView->Invalidate();
535     }
536 
537     if( !rText.Len() )  // sonst muss spaeter noch invalidiert werden, !bFormatted reicht.
538         mnCurTextHeight = 0;
539 
540     FormatAndUpdate();
541 
542     EnableUndo( bUndoCurrentlyEnabled );
543     DBG_ASSERT( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "Undo nach SetText?" );
544 }
545 
546 
CursorMoved(sal_uLong nNode)547 void TextEngine::CursorMoved( sal_uLong nNode )
548 {
549     // Leere Attribute loeschen, aber nur, wenn Absatz nicht leer!
550     TextNode* pNode = mpDoc->GetNodes().GetObject( nNode );
551     if ( pNode && pNode->GetCharAttribs().HasEmptyAttribs() && pNode->GetText().Len() )
552         pNode->GetCharAttribs().DeleteEmptyAttribs();
553 }
554 
ImpRemoveChars(const TextPaM & rPaM,sal_uInt16 nChars,SfxUndoAction *)555 void TextEngine::ImpRemoveChars( const TextPaM& rPaM, sal_uInt16 nChars, SfxUndoAction* )
556 {
557     DBG_ASSERT( nChars, "ImpRemoveChars - 0 Chars?!" );
558     if ( IsUndoEnabled() && !IsInUndo() )
559     {
560         // Attribute muessen hier vorm RemoveChars fuer UNDO gesichert werden!
561         TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
562         XubString aStr( pNode->GetText().Copy( rPaM.GetIndex(), nChars ) );
563 
564         // Pruefen, ob Attribute geloescht oder geaendert werden:
565         sal_uInt16 nStart = rPaM.GetIndex();
566         sal_uInt16 nEnd = nStart + nChars;
567         for ( sal_uInt16 nAttr = pNode->GetCharAttribs().Count(); nAttr; )
568         {
569             TextCharAttrib* pAttr = pNode->GetCharAttribs().GetAttrib( --nAttr );
570             if ( ( pAttr->GetEnd() >= nStart ) && ( pAttr->GetStart() < nEnd ) )
571             {
572 //              TextSelection aSel( rPaM );
573 //              aSel.GetEnd().GetIndex() += nChars;
574 //              TextUndoSetAttribs* pAttrUndo = CreateAttribUndo( aSel );
575 //              InsertUndo( pAttrUndo );
576                 break;  // for
577             }
578         }
579 //      if ( pCurUndo && ( CreateTextPaM( pCurUndo->GetEPaM() ) == rPaM ) )
580 //          pCurUndo->GetStr() += aStr;
581 //      else
582             InsertUndo( new TextUndoRemoveChars( this, rPaM, aStr ) );
583     }
584 
585     mpDoc->RemoveChars( rPaM, nChars );
586     ImpCharsRemoved( rPaM.GetPara(), rPaM.GetIndex(), nChars );
587 }
588 
ImpConnectParagraphs(sal_uLong nLeft,sal_uLong nRight)589 TextPaM TextEngine::ImpConnectParagraphs( sal_uLong nLeft, sal_uLong nRight )
590 {
591     DBG_ASSERT( nLeft != nRight, "Den gleichen Absatz zusammenfuegen ?" );
592 
593     TextNode* pLeft = mpDoc->GetNodes().GetObject( nLeft );
594     TextNode* pRight = mpDoc->GetNodes().GetObject( nRight );
595 
596     if ( IsUndoEnabled() && !IsInUndo() )
597         InsertUndo( new TextUndoConnectParas( this, nLeft, pLeft->GetText().Len() ) );
598 
599     // Erstmal Portions suchen, da pRight nach ConnectParagraphs weg.
600     TEParaPortion* pLeftPortion = mpTEParaPortions->GetObject( nLeft );
601     TEParaPortion* pRightPortion = mpTEParaPortions->GetObject( nRight );
602     DBG_ASSERT( pLeft && pLeftPortion, "Blinde Portion in ImpConnectParagraphs(1)" );
603     DBG_ASSERT( pRight && pRightPortion, "Blinde Portion in ImpConnectParagraphs(2)" );
604 
605     TextPaM aPaM = mpDoc->ConnectParagraphs( pLeft, pRight );
606     ImpParagraphRemoved( nRight );
607 
608     pLeftPortion->MarkSelectionInvalid( aPaM.GetIndex(), pLeft->GetText().Len() );
609 
610     mpTEParaPortions->Remove( nRight );
611     delete pRightPortion;
612     // der rechte Node wird von EditDoc::ConnectParagraphs() geloescht.
613 
614     return aPaM;
615 }
616 
ImpDeleteText(const TextSelection & rSel)617 TextPaM TextEngine::ImpDeleteText( const TextSelection& rSel )
618 {
619     if ( !rSel.HasRange() )
620         return rSel.GetStart();
621 
622     TextSelection aSel( rSel );
623     aSel.Justify();
624     TextPaM aStartPaM( aSel.GetStart() );
625     TextPaM aEndPaM( aSel.GetEnd() );
626 
627     CursorMoved( aStartPaM.GetPara() ); // nur damit neu eingestellte Attribute verschwinden...
628     CursorMoved( aEndPaM.GetPara() );   // nur damit neu eingestellte Attribute verschwinden...
629 
630     DBG_ASSERT( mpDoc->IsValidPaM( aStartPaM ), "Index im Wald in ImpDeleteText" );
631     DBG_ASSERT( mpDoc->IsValidPaM( aEndPaM ), "Index im Wald in ImpDeleteText" );
632 
633     sal_uLong nStartNode = aStartPaM.GetPara();
634     sal_uLong nEndNode = aEndPaM.GetPara();
635 
636     // Alle Nodes dazwischen entfernen....
637     for ( sal_uLong z = nStartNode+1; z < nEndNode; z++ )
638     {
639         // Immer nStartNode+1, wegen Remove()!
640         ImpRemoveParagraph( nStartNode+1 );
641     }
642 
643     if ( nStartNode != nEndNode )
644     {
645         // Den Rest des StartNodes...
646         TextNode* pLeft = mpDoc->GetNodes().GetObject( nStartNode );
647         sal_uInt16 nChars = pLeft->GetText().Len() - aStartPaM.GetIndex();
648         if ( nChars )
649         {
650             ImpRemoveChars( aStartPaM, nChars );
651             TEParaPortion* pPortion = mpTEParaPortions->GetObject( nStartNode );
652             DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteText(3)" );
653             pPortion->MarkSelectionInvalid( aStartPaM.GetIndex(), pLeft->GetText().Len() );
654         }
655 
656         // Den Anfang des EndNodes....
657         nEndNode = nStartNode+1;    // Die anderen Absaetze wurden geloescht
658         nChars = aEndPaM.GetIndex();
659         if ( nChars )
660         {
661             aEndPaM.GetPara() = nEndNode;
662             aEndPaM.GetIndex() = 0;
663             ImpRemoveChars( aEndPaM, nChars );
664             TEParaPortion* pPortion = mpTEParaPortions->GetObject( nEndNode );
665             DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteText(4)" );
666             pPortion->MarkSelectionInvalid( 0, pPortion->GetNode()->GetText().Len() );
667         }
668 
669         // Zusammenfuegen....
670         aStartPaM = ImpConnectParagraphs( nStartNode, nEndNode );
671     }
672     else
673     {
674         sal_uInt16 nChars;
675         nChars = aEndPaM.GetIndex() - aStartPaM.GetIndex();
676         ImpRemoveChars( aStartPaM, nChars );
677         TEParaPortion* pPortion = mpTEParaPortions->GetObject( nStartNode );
678         DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteText(5)" );
679         pPortion->MarkInvalid( aEndPaM.GetIndex(), aStartPaM.GetIndex() - aEndPaM.GetIndex() );
680     }
681 
682 //  UpdateSelections();
683     TextModified();
684     return aStartPaM;
685 }
686 
ImpRemoveParagraph(sal_uLong nPara)687 void TextEngine::ImpRemoveParagraph( sal_uLong nPara )
688 {
689     TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
690     TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
691 
692     // Der Node wird vom Undo verwaltet und ggf. zerstoert!
693     /* delete */ mpDoc->GetNodes().Remove( nPara );
694     if ( IsUndoEnabled() && !IsInUndo() )
695         InsertUndo( new TextUndoDelPara( this, pNode, nPara ) );
696     else
697         delete pNode;
698 
699     mpTEParaPortions->Remove( nPara );
700     delete pPortion;
701 
702     ImpParagraphRemoved( nPara );
703 }
704 
GetInputSequenceChecker() const705 uno::Reference < i18n::XExtendedInputSequenceChecker > TextEngine::GetInputSequenceChecker() const
706 {
707     uno::Reference < i18n::XExtendedInputSequenceChecker > xISC;
708 //    if ( !xISC.is() )
709     {
710         uno::Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
711         uno::Reference< uno::XInterface > xI = xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.InputSequenceChecker" ) );
712         if ( xI.is() )
713         {
714             Any x = xI->queryInterface( ::getCppuType((const uno::Reference< i18n::XExtendedInputSequenceChecker >*)0) );
715             x >>= xISC;
716         }
717     }
718     return xISC;
719 }
720 
IsInputSequenceCheckingRequired(sal_Unicode c,const TextSelection & rCurSel) const721 sal_Bool TextEngine::IsInputSequenceCheckingRequired( sal_Unicode c, const TextSelection& rCurSel ) const
722 {
723     uno::Reference< i18n::XBreakIterator > xBI = ((TextEngine *) this)->GetBreakIterator();
724     SvtCTLOptions aCTLOptions;
725 
726     // get the index that really is first
727     sal_uInt16 nFirstPos = rCurSel.GetStart().GetIndex();
728     sal_uInt16 nMaxPos   = rCurSel.GetEnd().GetIndex();
729     if (nMaxPos < nFirstPos)
730         nFirstPos = nMaxPos;
731 
732     sal_Bool bIsSequenceChecking =
733         aCTLOptions.IsCTLFontEnabled() &&
734         aCTLOptions.IsCTLSequenceChecking() &&
735         nFirstPos != 0 && /* first char needs not to be checked */
736         xBI.is() && i18n::ScriptType::COMPLEX == xBI->getScriptType( rtl::OUString( c ), 0 );
737 
738     return bIsSequenceChecking;
739 }
740 
ImpInsertText(const TextSelection & rCurSel,sal_Unicode c,sal_Bool bOverwrite)741 TextPaM TextEngine::ImpInsertText( const TextSelection& rCurSel, sal_Unicode c, sal_Bool bOverwrite )
742 {
743     return ImpInsertText( c, rCurSel, bOverwrite, sal_False );
744 }
745 
ImpInsertText(sal_Unicode c,const TextSelection & rCurSel,sal_Bool bOverwrite,sal_Bool bIsUserInput)746 TextPaM TextEngine::ImpInsertText( sal_Unicode c, const TextSelection& rCurSel, sal_Bool bOverwrite, sal_Bool bIsUserInput )
747 {
748     DBG_ASSERT( c != '\n', "Zeilenumbruch bei InsertText ?" );
749     DBG_ASSERT( c != '\r', "Zeilenumbruch bei InsertText ?" );
750 
751     TextPaM aPaM( rCurSel.GetStart() );
752     TextNode* pNode = mpDoc->GetNodes().GetObject( aPaM.GetPara() );
753 
754     if ( pNode->GetText().Len() < STRING_MAXLEN )
755     {
756         sal_Bool bDoOverwrite = ( bOverwrite &&
757                 ( aPaM.GetIndex() < pNode->GetText().Len() ) ) ? sal_True : sal_False;
758 
759         sal_Bool bUndoAction = ( rCurSel.HasRange() || bDoOverwrite );
760 
761         if ( bUndoAction )
762             UndoActionStart();
763 
764         if ( rCurSel.HasRange() )
765         {
766             aPaM = ImpDeleteText( rCurSel );
767         }
768         else if ( bDoOverwrite )
769         {
770             // Wenn Selektion, dann kein Zeichen ueberschreiben
771             TextSelection aTmpSel( aPaM );
772             aTmpSel.GetEnd().GetIndex()++;
773             ImpDeleteText( aTmpSel );
774         }
775 
776         if (bIsUserInput && IsInputSequenceCheckingRequired( c, rCurSel ))
777         {
778             uno::Reference < i18n::XExtendedInputSequenceChecker > xISC = GetInputSequenceChecker();
779             SvtCTLOptions aCTLOptions;
780 
781             if (xISC.is())
782             {
783                 xub_StrLen nTmpPos = aPaM.GetIndex();
784                 sal_Int16 nCheckMode = aCTLOptions.IsCTLSequenceCheckingRestricted() ?
785                         i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC;
786 
787                 // the text that needs to be checked is only the one
788                 // before the current cursor position
789                 rtl::OUString aOldText( mpDoc->GetText( aPaM.GetPara() ).Copy(0, nTmpPos) );
790                 rtl::OUString aNewText( aOldText );
791                 if (aCTLOptions.IsCTLSequenceCheckingTypeAndReplace())
792                 {
793                     xISC->correctInputSequence( aNewText, nTmpPos - 1, c, nCheckMode );
794 
795                     // find position of first character that has changed
796                     sal_Int32 nOldLen = aOldText.getLength();
797                     sal_Int32 nNewLen = aNewText.getLength();
798                     const sal_Unicode *pOldTxt = aOldText.getStr();
799                     const sal_Unicode *pNewTxt = aNewText.getStr();
800                     sal_Int32 nChgPos = 0;
801                     while ( nChgPos < nOldLen && nChgPos < nNewLen &&
802                             pOldTxt[nChgPos] == pNewTxt[nChgPos] )
803                         ++nChgPos;
804 
805                     xub_StrLen nChgLen = static_cast< xub_StrLen >(nNewLen - nChgPos);
806                     String aChgText( aNewText.copy( nChgPos ).getStr(), nChgLen );
807 
808                     // select text from first pos to be changed to current pos
809                     TextSelection aSel( TextPaM( aPaM.GetPara(), (sal_uInt16) nChgPos ), aPaM );
810 
811                     if (aChgText.Len())
812                         // ImpInsertText implicitly handles undo...
813                         return ImpInsertText( aSel, aChgText );
814                     else
815                         return aPaM;
816                 }
817                 else
818                 {
819                     // should the character be ignored (i.e. not get inserted) ?
820                     if (!xISC->checkInputSequence( aOldText, nTmpPos - 1, c, nCheckMode ))
821                         return aPaM;    // nothing to be done -> no need for undo
822                 }
823             }
824 
825             // at this point now we will insert the character 'normally' some lines below...
826         }
827 
828 
829         if ( IsUndoEnabled() && !IsInUndo() )
830         {
831             TextUndoInsertChars* pNewUndo = new TextUndoInsertChars( this, aPaM, c );
832             sal_Bool bTryMerge = ( !bDoOverwrite && ( c != ' ' ) ) ? sal_True : sal_False;
833             InsertUndo( pNewUndo, bTryMerge );
834         }
835 
836         TEParaPortion* pPortion = mpTEParaPortions->GetObject( aPaM.GetPara() );
837         pPortion->MarkInvalid( aPaM.GetIndex(), 1 );
838         if ( c == '\t' )
839             pPortion->SetNotSimpleInvalid();
840         aPaM = mpDoc->InsertText( aPaM, c );
841         ImpCharsInserted( aPaM.GetPara(), aPaM.GetIndex()-1, 1 );
842 
843         TextModified();
844 
845         if ( bUndoAction )
846             UndoActionEnd();
847     }
848 
849     return aPaM;
850 }
851 
852 
ImpInsertText(const TextSelection & rCurSel,const XubString & rStr)853 TextPaM TextEngine::ImpInsertText( const TextSelection& rCurSel, const XubString& rStr )
854 {
855     UndoActionStart();
856 
857     TextPaM aPaM;
858 
859     if ( rCurSel.HasRange() )
860         aPaM = ImpDeleteText( rCurSel );
861     else
862         aPaM = rCurSel.GetEnd();
863 
864     XubString aText( rStr );
865     aText.ConvertLineEnd( LINEEND_LF );
866 
867     sal_uInt16 nStart = 0;
868     while ( nStart < aText.Len() )
869     {
870         sal_uInt16 nEnd = aText.Search( LINE_SEP, nStart );
871         if ( nEnd == STRING_NOTFOUND )
872             nEnd = aText.Len(); // nicht dereferenzieren!
873 
874         // Start == End => Leerzeile
875         if ( nEnd > nStart )
876         {
877             sal_uLong nL = aPaM.GetIndex();
878             nL += ( nEnd-nStart );
879             if ( nL > STRING_MAXLEN )
880             {
881                 sal_uInt16 nDiff = (sal_uInt16) (nL-STRING_MAXLEN);
882                 nEnd = nEnd - nDiff;
883             }
884 
885             XubString aLine( aText, nStart, nEnd-nStart );
886             if ( IsUndoEnabled() && !IsInUndo() )
887                 InsertUndo( new TextUndoInsertChars( this, aPaM, aLine ) );
888 
889             TEParaPortion* pPortion = mpTEParaPortions->GetObject( aPaM.GetPara() );
890             pPortion->MarkInvalid( aPaM.GetIndex(), aLine.Len() );
891             if ( aLine.Search( '\t' ) != STRING_NOTFOUND )
892                 pPortion->SetNotSimpleInvalid();
893 
894             aPaM = mpDoc->InsertText( aPaM, aLine );
895             ImpCharsInserted( aPaM.GetPara(), aPaM.GetIndex()-aLine.Len(), aLine.Len() );
896 
897         }
898         if ( nEnd < aText.Len() )
899             aPaM = ImpInsertParaBreak( aPaM );
900 
901         nStart = nEnd+1;
902 
903         if ( nStart < nEnd )    // #108611# overflow
904             break;
905     }
906 
907     UndoActionEnd();
908 
909     TextModified();
910     return aPaM;
911 }
912 
ImpInsertParaBreak(const TextSelection & rCurSel,sal_Bool bKeepEndingAttribs)913 TextPaM TextEngine::ImpInsertParaBreak( const TextSelection& rCurSel, sal_Bool bKeepEndingAttribs )
914 {
915     TextPaM aPaM;
916     if ( rCurSel.HasRange() )
917         aPaM = ImpDeleteText( rCurSel );
918     else
919         aPaM = rCurSel.GetEnd();
920 
921     return ImpInsertParaBreak( aPaM, bKeepEndingAttribs );
922 }
923 
ImpInsertParaBreak(const TextPaM & rPaM,sal_Bool bKeepEndingAttribs)924 TextPaM TextEngine::ImpInsertParaBreak( const TextPaM& rPaM, sal_Bool bKeepEndingAttribs )
925 {
926     if ( IsUndoEnabled() && !IsInUndo() )
927         InsertUndo( new TextUndoSplitPara( this, rPaM.GetPara(), rPaM.GetIndex() ) );
928 
929     TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
930     sal_Bool bFirstParaContentChanged = rPaM.GetIndex() < pNode->GetText().Len();
931 
932     TextPaM aPaM( mpDoc->InsertParaBreak( rPaM, bKeepEndingAttribs ) );
933 
934     TEParaPortion* pPortion = mpTEParaPortions->GetObject( rPaM.GetPara() );
935     DBG_ASSERT( pPortion, "Blinde Portion in ImpInsertParaBreak" );
936     pPortion->MarkInvalid( rPaM.GetIndex(), 0 );
937 
938     TextNode* pNewNode = mpDoc->GetNodes().GetObject( aPaM.GetPara() );
939     TEParaPortion* pNewPortion = new TEParaPortion( pNewNode );
940     mpTEParaPortions->Insert( pNewPortion, aPaM.GetPara() );
941     ImpParagraphInserted( aPaM.GetPara() );
942 
943     CursorMoved( rPaM.GetPara() );  // falls leeres Attribut entstanden.
944     TextModified();
945 
946     if ( bFirstParaContentChanged )
947         Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED, rPaM.GetPara() ) );
948 
949     return aPaM;
950 }
951 
PaMtoEditCursor(const TextPaM & rPaM,sal_Bool bSpecial)952 Rectangle TextEngine::PaMtoEditCursor( const TextPaM& rPaM, sal_Bool bSpecial )
953 {
954     DBG_ASSERT( GetUpdateMode(), "Darf bei Update=sal_False nicht erreicht werden: PaMtoEditCursor" );
955 
956     Rectangle aEditCursor;
957     long nY = 0;
958 
959     if ( !mbHasMultiLineParas )
960     {
961         nY = rPaM.GetPara() * mnCharHeight;
962     }
963     else
964     {
965         for ( sal_uLong nPortion = 0; nPortion < rPaM.GetPara(); nPortion++ )
966         {
967             TEParaPortion* pPortion = mpTEParaPortions->GetObject(nPortion);
968             nY += pPortion->GetLines().Count() * mnCharHeight;
969         }
970     }
971 
972     aEditCursor = GetEditCursor( rPaM, bSpecial );
973     aEditCursor.Top() += nY;
974     aEditCursor.Bottom() += nY;
975     return aEditCursor;
976 }
977 
GetEditCursor(const TextPaM & rPaM,sal_Bool bSpecial,sal_Bool bPreferPortionStart)978 Rectangle TextEngine::GetEditCursor( const TextPaM& rPaM, sal_Bool bSpecial, sal_Bool bPreferPortionStart )
979 {
980     if ( !IsFormatted() && !IsFormatting() )
981         FormatAndUpdate();
982 
983     TEParaPortion* pPortion = mpTEParaPortions->GetObject( rPaM.GetPara() );
984     //TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
985 
986     /*
987      bSpecial:  Wenn hinter dem letzten Zeichen einer umgebrochenen Zeile,
988      am Ende der Zeile bleiben, nicht am Anfang der naechsten.
989      Zweck:     - END => wirklich hinter das letzte Zeichen
990                 - Selektion....
991       bSpecial: If behind the last character of a made up line, stay at the
992                 end of the line, not at the start of the next line.
993       Purpose:  - really END = > behind the last character
994                 - to selection...
995 
996     */
997 
998     long nY = 0;
999     sal_uInt16 nCurIndex = 0;
1000     TextLine* pLine = 0;
1001     for ( sal_uInt16 nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
1002     {
1003         TextLine* pTmpLine = pPortion->GetLines().GetObject( nLine );
1004         if ( ( pTmpLine->GetStart() == rPaM.GetIndex() ) || ( pTmpLine->IsIn( rPaM.GetIndex(), bSpecial ) ) )
1005         {
1006             pLine = pTmpLine;
1007             break;
1008         }
1009 
1010         nCurIndex = nCurIndex + pTmpLine->GetLen();
1011         nY += mnCharHeight;
1012     }
1013     if ( !pLine )
1014     {
1015         // Cursor am Ende des Absatzes.
1016         DBG_ASSERT( rPaM.GetIndex() == nCurIndex, "Index voll daneben in GetEditCursor!" );
1017 
1018         pLine = pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1 );
1019         nY -= mnCharHeight;
1020         nCurIndex = nCurIndex - pLine->GetLen();
1021     }
1022 
1023     Rectangle aEditCursor;
1024 
1025     aEditCursor.Top() = nY;
1026     nY += mnCharHeight;
1027     aEditCursor.Bottom() = nY-1;
1028 
1029     // innerhalb der Zeile suchen....
1030     long nX = ImpGetXPos( rPaM.GetPara(), pLine, rPaM.GetIndex(), bPreferPortionStart );
1031     aEditCursor.Left() = aEditCursor.Right() = nX;
1032     return aEditCursor;
1033 }
1034 
ImpGetXPos(sal_uLong nPara,TextLine * pLine,sal_uInt16 nIndex,sal_Bool bPreferPortionStart)1035 long TextEngine::ImpGetXPos( sal_uLong nPara, TextLine* pLine, sal_uInt16 nIndex, sal_Bool bPreferPortionStart )
1036 {
1037     DBG_ASSERT( ( nIndex >= pLine->GetStart() ) && ( nIndex <= pLine->GetEnd() ) , "ImpGetXPos muss richtig gerufen werden!" );
1038 
1039     sal_Bool bDoPreferPortionStart = bPreferPortionStart;
1040     // Assure that the portion belongs to this line:
1041     if ( nIndex == pLine->GetStart() )
1042         bDoPreferPortionStart = sal_True;
1043     else if ( nIndex == pLine->GetEnd() )
1044         bDoPreferPortionStart = sal_False;
1045 
1046     TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
1047 
1048     sal_uInt16 nTextPortionStart = 0;
1049     sal_uInt16 nTextPortion = pParaPortion->GetTextPortions().FindPortion( nIndex, nTextPortionStart, bDoPreferPortionStart );
1050 
1051     DBG_ASSERT( ( nTextPortion >= pLine->GetStartPortion() ) && ( nTextPortion <= pLine->GetEndPortion() ), "GetXPos: Portion not in current line! " );
1052 
1053     TETextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
1054 
1055     long nX = ImpGetPortionXOffset( nPara, pLine, nTextPortion );
1056 
1057     long nPortionTextWidth = pPortion->GetWidth();
1058 
1059     if ( nTextPortionStart != nIndex )
1060     {
1061         // Search within portion...
1062         if ( nIndex == ( nTextPortionStart + pPortion->GetLen() ) )
1063         {
1064             // End of Portion
1065             if ( ( pPortion->GetKind() == PORTIONKIND_TAB ) ||
1066                  ( !IsRightToLeft() && !pPortion->IsRightToLeft() ) ||
1067                  ( IsRightToLeft() && pPortion->IsRightToLeft() ) )
1068             {
1069                 nX += nPortionTextWidth;
1070                 if ( ( pPortion->GetKind() == PORTIONKIND_TAB ) && ( (nTextPortion+1) < pParaPortion->GetTextPortions().Count() ) )
1071                 {
1072                     TETextPortion* pNextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion+1 );
1073                     if ( ( pNextPortion->GetKind() != PORTIONKIND_TAB ) && (
1074                               ( !IsRightToLeft() && pNextPortion->IsRightToLeft() ) ||
1075                               ( IsRightToLeft() && !pNextPortion->IsRightToLeft() ) ) )
1076                     {
1077 //                        nX += pNextPortion->GetWidth();
1078                         // End of the tab portion, use start of next for cursor pos
1079                         DBG_ASSERT( !bPreferPortionStart, "ImpGetXPos - How can we this tab portion here???" );
1080                         nX = ImpGetXPos( nPara, pLine, nIndex, sal_True );
1081                     }
1082 
1083                 }
1084             }
1085         }
1086         else if ( pPortion->GetKind() == PORTIONKIND_TEXT )
1087         {
1088             DBG_ASSERT( nIndex != pLine->GetStart(), "Strange behavior in new ImpGetXPos()" );
1089 
1090             long nPosInPortion = (long)CalcTextWidth( nPara, nTextPortionStart, nIndex-nTextPortionStart );
1091 
1092             if ( ( !IsRightToLeft() && !pPortion->IsRightToLeft() ) ||
1093                  ( IsRightToLeft() && pPortion->IsRightToLeft() ) )
1094             {
1095                 nX += nPosInPortion;
1096             }
1097             else
1098             {
1099                 nX += nPortionTextWidth - nPosInPortion;
1100             }
1101         }
1102     }
1103     else // if ( nIndex == pLine->GetStart() )
1104     {
1105         if ( ( pPortion->GetKind() != PORTIONKIND_TAB ) &&
1106                 ( ( !IsRightToLeft() && pPortion->IsRightToLeft() ) ||
1107                 ( IsRightToLeft() && !pPortion->IsRightToLeft() ) ) )
1108         {
1109             nX += nPortionTextWidth;
1110         }
1111     }
1112 
1113     return nX;
1114 }
1115 
FindAttrib(const TextPaM & rPaM,sal_uInt16 nWhich) const1116 const TextAttrib* TextEngine::FindAttrib( const TextPaM& rPaM, sal_uInt16 nWhich ) const
1117 {
1118     const TextAttrib* pAttr = NULL;
1119     const TextCharAttrib* pCharAttr = FindCharAttrib( rPaM, nWhich );
1120     if ( pCharAttr )
1121         pAttr = &pCharAttr->GetAttr();
1122     return pAttr;
1123 }
1124 
FindCharAttrib(const TextPaM & rPaM,sal_uInt16 nWhich) const1125 const TextCharAttrib* TextEngine::FindCharAttrib( const TextPaM& rPaM, sal_uInt16 nWhich ) const
1126 {
1127     const TextCharAttrib* pAttr = NULL;
1128     TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
1129     if ( pNode && ( rPaM.GetIndex() < pNode->GetText().Len() ) )
1130         pAttr = pNode->GetCharAttribs().FindAttrib( nWhich, rPaM.GetIndex() );
1131     return pAttr;
1132 }
1133 
HasAttrib(sal_uInt16 nWhich) const1134 sal_Bool TextEngine::HasAttrib( sal_uInt16 nWhich ) const
1135 {
1136     sal_Bool bAttr = sal_False;
1137     for ( sal_uLong n = mpDoc->GetNodes().Count(); --n && !bAttr; )
1138     {
1139         TextNode* pNode = mpDoc->GetNodes().GetObject( n );
1140         bAttr = pNode->GetCharAttribs().HasAttrib( nWhich );
1141     }
1142     return bAttr;
1143 }
1144 
GetPaM(const Point & rDocPos,sal_Bool bSmart)1145 TextPaM TextEngine::GetPaM( const Point& rDocPos, sal_Bool bSmart )
1146 {
1147     DBG_ASSERT( GetUpdateMode(), "Darf bei Update=sal_False nicht erreicht werden: GetPaM" );
1148 
1149     long nY = 0;
1150     for ( sal_uLong nPortion = 0; nPortion < mpTEParaPortions->Count(); nPortion++ )
1151     {
1152         TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion );
1153         long nTmpHeight = pPortion->GetLines().Count() * mnCharHeight;
1154         nY += nTmpHeight;
1155         if ( nY > rDocPos.Y() )
1156         {
1157             nY -= nTmpHeight;
1158             Point aPosInPara( rDocPos );
1159             aPosInPara.Y() -= nY;
1160 
1161             TextPaM aPaM( nPortion, 0 );
1162             aPaM.GetIndex() = ImpFindIndex( nPortion, aPosInPara, bSmart );
1163             return aPaM;
1164         }
1165     }
1166 
1167     // Nicht gefunden - Dann den letzten sichtbare...
1168     sal_uLong nLastNode = mpDoc->GetNodes().Count() - 1;
1169     TextNode* pLast = mpDoc->GetNodes().GetObject( nLastNode );
1170     return TextPaM( nLastNode, pLast->GetText().Len() );
1171 }
1172 
ImpFindIndex(sal_uLong nPortion,const Point & rPosInPara,sal_Bool bSmart)1173 sal_uInt16 TextEngine::ImpFindIndex( sal_uLong nPortion, const Point& rPosInPara, sal_Bool bSmart )
1174 {
1175     DBG_ASSERT( IsFormatted(), "GetPaM: Nicht formatiert" );
1176     TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion );
1177 
1178     sal_uInt16 nCurIndex = 0;
1179 
1180     long nY = 0;
1181     TextLine* pLine = 0;
1182     sal_uInt16 nLine;
1183     for ( nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ )
1184     {
1185         TextLine* pTmpLine = pPortion->GetLines().GetObject( nLine );
1186         nY += mnCharHeight;
1187         if ( nY > rPosInPara.Y() )  // das war 'se
1188         {
1189             pLine = pTmpLine;
1190             break;                  // richtige Y-Position intressiert nicht
1191         }
1192     }
1193     DBG_ASSERT( pLine, "ImpFindIndex: pLine ?" );
1194 
1195     nCurIndex = GetCharPos( nPortion, nLine, rPosInPara.X(), bSmart );
1196 
1197     if ( nCurIndex && ( nCurIndex == pLine->GetEnd() ) &&
1198          ( pLine != pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1) ) )
1199     {
1200         uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator();
1201         sal_Int32 nCount = 1;
1202         nCurIndex = (sal_uInt16)xBI->previousCharacters( pPortion->GetNode()->GetText(), nCurIndex, GetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount );
1203     }
1204     return nCurIndex;
1205 }
1206 
GetCharPos(sal_uLong nPortion,sal_uInt16 nLine,long nXPos,sal_Bool)1207 sal_uInt16 TextEngine::GetCharPos( sal_uLong nPortion, sal_uInt16 nLine, long nXPos, sal_Bool )
1208 {
1209 
1210     TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion );
1211     TextLine* pLine = pPortion->GetLines().GetObject( nLine );
1212 
1213     sal_uInt16 nCurIndex = pLine->GetStart();
1214 
1215     long nTmpX = pLine->GetStartX();
1216     if ( nXPos <= nTmpX )
1217         return nCurIndex;
1218 
1219     for ( sal_uInt16 i = pLine->GetStartPortion(); i <= pLine->GetEndPortion(); i++ )
1220     {
1221         TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( i );
1222         nTmpX += pTextPortion->GetWidth();
1223 
1224         if ( nTmpX > nXPos )
1225         {
1226             if( pTextPortion->GetLen() > 1 )
1227             {
1228                 nTmpX -= pTextPortion->GetWidth();  // vor die Portion stellen
1229                 // Optimieren: Kein GetTextBreak, wenn feste Fontbreite...
1230                 Font aFont;
1231                 SeekCursor( nPortion, nCurIndex+1, aFont, NULL );
1232                 mpRefDev->SetFont( aFont);
1233                 long nPosInPortion = nXPos-nTmpX;
1234                 if ( IsRightToLeft() != pTextPortion->IsRightToLeft() )
1235                     nPosInPortion = pTextPortion->GetWidth() - nPosInPortion;
1236                 nCurIndex = mpRefDev->GetTextBreak( pPortion->GetNode()->GetText(), nPosInPortion, nCurIndex );
1237                 // MT: GetTextBreak should assure that we are not withing a CTL cell...
1238             }
1239             return nCurIndex;
1240         }
1241         nCurIndex = nCurIndex + pTextPortion->GetLen();
1242     }
1243     return nCurIndex;
1244 }
1245 
1246 
GetTextHeight() const1247 sal_uLong TextEngine::GetTextHeight() const
1248 {
1249     DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=sal_False nicht verwendet werden: GetTextHeight" );
1250 
1251     if ( !IsFormatted() && !IsFormatting() )
1252         ((TextEngine*)this)->FormatAndUpdate();
1253 
1254     return mnCurTextHeight;
1255 }
1256 
GetTextHeight(sal_uLong nParagraph) const1257 sal_uLong TextEngine::GetTextHeight( sal_uLong nParagraph ) const
1258 {
1259     DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=sal_False nicht verwendet werden: GetTextHeight" );
1260 
1261     if ( !IsFormatted() && !IsFormatting() )
1262         ((TextEngine*)this)->FormatAndUpdate();
1263 
1264     return CalcParaHeight( nParagraph );
1265 }
1266 
CalcTextWidth(sal_uLong nPara)1267 sal_uLong TextEngine::CalcTextWidth( sal_uLong nPara )
1268 {
1269     sal_uLong nParaWidth = 0;
1270     TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
1271     for ( sal_uInt16 nLine = pPortion->GetLines().Count(); nLine; )
1272     {
1273         sal_uLong nLineWidth = 0;
1274         TextLine* pLine = pPortion->GetLines().GetObject( --nLine );
1275         for ( sal_uInt16 nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ )
1276         {
1277             TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( nTP );
1278             nLineWidth += pTextPortion->GetWidth();
1279         }
1280         if ( nLineWidth > nParaWidth )
1281             nParaWidth = nLineWidth;
1282     }
1283     return nParaWidth;
1284 }
1285 
CalcTextWidth()1286 sal_uLong TextEngine::CalcTextWidth()
1287 {
1288     if ( !IsFormatted() && !IsFormatting() )
1289         FormatAndUpdate();
1290 
1291     if ( mnCurTextWidth == 0xFFFFFFFF )
1292     {
1293         mnCurTextWidth = 0;
1294         for ( sal_uLong nPara = mpTEParaPortions->Count(); nPara; )
1295         {
1296             sal_uLong nParaWidth = CalcTextWidth( --nPara );
1297             if ( nParaWidth > mnCurTextWidth )
1298                 mnCurTextWidth = nParaWidth;
1299         }
1300     }
1301     return mnCurTextWidth+1;// Ein breiter, da in CreateLines bei >= umgebrochen wird.
1302 }
1303 
CalcTextHeight()1304 sal_uLong TextEngine::CalcTextHeight()
1305 {
1306     DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=sal_False nicht verwendet werden: CalcTextHeight" );
1307 
1308     sal_uLong nY = 0;
1309     for ( sal_uLong nPortion = mpTEParaPortions->Count(); nPortion; )
1310         nY += CalcParaHeight( --nPortion );
1311     return nY;
1312 }
1313 
CalcTextWidth(sal_uLong nPara,sal_uInt16 nPortionStart,sal_uInt16 nLen,const Font * pFont)1314 sal_uLong TextEngine::CalcTextWidth( sal_uLong nPara, sal_uInt16 nPortionStart, sal_uInt16 nLen, const Font* pFont )
1315 {
1316     // Innerhalb des Textes darf es keinen Portionwechsel (Attribut/Tab) geben!
1317     DBG_ASSERT( mpDoc->GetNodes().GetObject( nPara )->GetText().Search( '\t', nPortionStart ) >= (nPortionStart+nLen), "CalcTextWidth: Tab!" );
1318 
1319     sal_uLong nWidth;
1320     if ( mnFixCharWidth100 )
1321     {
1322         nWidth = (sal_uLong)nLen*mnFixCharWidth100/100;
1323     }
1324     else
1325     {
1326         if ( pFont )
1327         {
1328             if ( !mpRefDev->GetFont().IsSameInstance( *pFont ) )
1329                 mpRefDev->SetFont( *pFont );
1330         }
1331         else
1332         {
1333             Font aFont;
1334             SeekCursor( nPara, nPortionStart+1, aFont, NULL );
1335             mpRefDev->SetFont( aFont );
1336         }
1337         TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
1338         nWidth = (sal_uLong)mpRefDev->GetTextWidth( pNode->GetText(), nPortionStart, nLen );
1339 
1340     }
1341     return nWidth;
1342 }
1343 
1344 
GetLineCount(sal_uLong nParagraph) const1345 sal_uInt16 TextEngine::GetLineCount( sal_uLong nParagraph ) const
1346 {
1347     DBG_ASSERT( nParagraph < mpTEParaPortions->Count(), "GetLineCount: Out of range" );
1348 
1349     TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph );
1350     if ( pPPortion )
1351         return pPPortion->GetLines().Count();
1352 
1353     return 0xFFFF;
1354 }
1355 
GetLineLen(sal_uLong nParagraph,sal_uInt16 nLine) const1356 sal_uInt16 TextEngine::GetLineLen( sal_uLong nParagraph, sal_uInt16 nLine ) const
1357 {
1358     DBG_ASSERT( nParagraph < mpTEParaPortions->Count(), "GetLineCount: Out of range" );
1359 
1360     TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph );
1361     if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) )
1362     {
1363         TextLine* pLine = pPPortion->GetLines().GetObject( nLine );
1364         return pLine->GetLen();
1365     }
1366 
1367     return 0xFFFF;
1368 }
1369 
CalcParaHeight(sal_uLong nParagraph) const1370 sal_uLong TextEngine::CalcParaHeight( sal_uLong nParagraph ) const
1371 {
1372     sal_uLong nHeight = 0;
1373 
1374     TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph );
1375     DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetParaHeight" );
1376     if ( pPPortion )
1377         nHeight = pPPortion->GetLines().Count() * mnCharHeight;
1378 
1379     return nHeight;
1380 }
1381 
UpdateSelections()1382 void TextEngine::UpdateSelections()
1383 {
1384 }
1385 
GetInvalidYOffsets(sal_uLong nPortion)1386 Range TextEngine::GetInvalidYOffsets( sal_uLong nPortion )
1387 {
1388     TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPortion );
1389     sal_uInt16 nLines = pTEParaPortion->GetLines().Count();
1390     sal_uInt16 nLastInvalid, nFirstInvalid = 0;
1391     sal_uInt16 nLine;
1392     for ( nLine = 0; nLine < nLines; nLine++ )
1393     {
1394         TextLine* pL = pTEParaPortion->GetLines().GetObject( nLine );
1395         if ( pL->IsInvalid() )
1396         {
1397             nFirstInvalid = nLine;
1398             break;
1399         }
1400     }
1401 
1402     for ( nLastInvalid = nFirstInvalid; nLastInvalid < nLines; nLastInvalid++ )
1403     {
1404         TextLine* pL = pTEParaPortion->GetLines().GetObject( nLine );
1405         if ( pL->IsValid() )
1406             break;
1407     }
1408 
1409     if ( nLastInvalid >= nLines )
1410         nLastInvalid = nLines-1;
1411 
1412     return Range( nFirstInvalid*mnCharHeight, ((nLastInvalid+1)*mnCharHeight)-1 );
1413 }
1414 
GetParagraphCount() const1415 sal_uLong TextEngine::GetParagraphCount() const
1416 {
1417     return mpDoc->GetNodes().Count();
1418 }
1419 
EnableUndo(sal_Bool bEnable)1420 void TextEngine::EnableUndo( sal_Bool bEnable )
1421 {
1422     // Beim Umschalten des Modus Liste loeschen:
1423     if ( bEnable != IsUndoEnabled() )
1424         ResetUndo();
1425 
1426     mbUndoEnabled = bEnable;
1427 }
1428 
GetUndoManager()1429 ::svl::IUndoManager& TextEngine::GetUndoManager()
1430 {
1431     if ( !mpUndoManager )
1432         mpUndoManager = new TextUndoManager( this );
1433     return *mpUndoManager;
1434 }
1435 
UndoActionStart(sal_uInt16 nId)1436 void TextEngine::UndoActionStart( sal_uInt16 nId )
1437 {
1438     if ( IsUndoEnabled() && !IsInUndo() )
1439     {
1440         String aComment;
1441         // ...
1442         GetUndoManager().EnterListAction( aComment, XubString(), nId );
1443     }
1444 }
1445 
UndoActionEnd()1446 void TextEngine::UndoActionEnd()
1447 {
1448     if ( IsUndoEnabled() && !IsInUndo() )
1449         GetUndoManager().LeaveListAction();
1450 }
1451 
InsertUndo(TextUndo * pUndo,sal_Bool bTryMerge)1452 void TextEngine::InsertUndo( TextUndo* pUndo, sal_Bool bTryMerge )
1453 {
1454     DBG_ASSERT( !IsInUndo(), "InsertUndo im Undomodus!" );
1455     GetUndoManager().AddUndoAction( pUndo, bTryMerge );
1456 }
1457 
ResetUndo()1458 void TextEngine::ResetUndo()
1459 {
1460     if ( mpUndoManager )
1461         mpUndoManager->Clear();
1462 }
1463 
InsertContent(TextNode * pNode,sal_uLong nPara)1464 void TextEngine::InsertContent( TextNode* pNode, sal_uLong nPara )
1465 {
1466     DBG_ASSERT( pNode, "NULL-Pointer in InsertContent! " );
1467     DBG_ASSERT( IsInUndo(), "InsertContent nur fuer Undo()!" );
1468     TEParaPortion* pNew = new TEParaPortion( pNode );
1469     mpTEParaPortions->Insert( pNew, nPara );
1470     mpDoc->GetNodes().Insert( pNode, nPara );
1471     ImpParagraphInserted( nPara );
1472 }
1473 
SplitContent(sal_uLong nNode,sal_uInt16 nSepPos)1474 TextPaM TextEngine::SplitContent( sal_uLong nNode, sal_uInt16 nSepPos )
1475 {
1476     #ifdef DBG_UTIL
1477     TextNode* pNode = mpDoc->GetNodes().GetObject( nNode );
1478     DBG_ASSERT( pNode, "Ungueltiger Node in SplitContent" );
1479     DBG_ASSERT( IsInUndo(), "SplitContent nur fuer Undo()!" );
1480     DBG_ASSERT( nSepPos <= pNode->GetText().Len(), "Index im Wald: SplitContent" );
1481     #endif
1482     TextPaM aPaM( nNode, nSepPos );
1483     return ImpInsertParaBreak( aPaM );
1484 }
1485 
ConnectContents(sal_uLong nLeftNode)1486 TextPaM TextEngine::ConnectContents( sal_uLong nLeftNode )
1487 {
1488     DBG_ASSERT( IsInUndo(), "ConnectContent nur fuer Undo()!" );
1489     return ImpConnectParagraphs( nLeftNode, nLeftNode+1 );
1490 }
1491 
SeekCursor(sal_uLong nPara,sal_uInt16 nPos,Font & rFont,OutputDevice * pOutDev)1492 void TextEngine::SeekCursor( sal_uLong nPara, sal_uInt16 nPos, Font& rFont, OutputDevice* pOutDev )
1493 {
1494     rFont = maFont;
1495     if ( pOutDev )
1496         pOutDev->SetTextColor( maTextColor );
1497 
1498     TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
1499     sal_uInt16 nAttribs = pNode->GetCharAttribs().Count();
1500     for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
1501     {
1502         TextCharAttrib* pAttrib = pNode->GetCharAttribs().GetAttrib( nAttr );
1503         if ( pAttrib->GetStart() > nPos )
1504             break;
1505 
1506         // Beim Seeken nicht die Attr beruecksichtigen, die dort beginnen!
1507         // Leere Attribute werden beruecksichtigt( verwendet), da diese
1508         // gerade eingestellt wurden.
1509         // 12.4.95: Doch keine Leeren Attribute verwenden:
1510         // - Wenn gerade eingestellt und leer => keine Auswirkung auf Font
1511         // In einem leeren Absatz eingestellte Zeichen werden sofort wirksam.
1512         if ( ( ( pAttrib->GetStart() < nPos ) && ( pAttrib->GetEnd() >= nPos ) )
1513                     || !pNode->GetText().Len() )
1514         {
1515             if ( pAttrib->Which() != TEXTATTR_FONTCOLOR )
1516             {
1517                 pAttrib->GetAttr().SetFont( rFont );
1518             }
1519             else
1520             {
1521                 if ( pOutDev )
1522                     pOutDev->SetTextColor( ((TextAttribFontColor&)pAttrib->GetAttr()).GetColor() );
1523             }
1524         }
1525     }
1526 
1527     if ( mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetPara() == nPara ) &&
1528         ( nPos > mpIMEInfos->aPos.GetIndex() ) && ( nPos <= ( mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen ) ) )
1529     {
1530         sal_uInt16 nAttr = mpIMEInfos->pAttribs[ nPos - mpIMEInfos->aPos.GetIndex() - 1 ];
1531         if ( nAttr & EXTTEXTINPUT_ATTR_UNDERLINE )
1532             rFont.SetUnderline( UNDERLINE_SINGLE );
1533         else if ( nAttr & EXTTEXTINPUT_ATTR_BOLDUNDERLINE )
1534             rFont.SetUnderline( UNDERLINE_BOLD );
1535         else if ( nAttr & EXTTEXTINPUT_ATTR_DOTTEDUNDERLINE )
1536             rFont.SetUnderline( UNDERLINE_DOTTED );
1537         else if ( nAttr & EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE )
1538             rFont.SetUnderline( UNDERLINE_DOTTED );
1539         if ( nAttr & EXTTEXTINPUT_ATTR_REDTEXT )
1540             rFont.SetColor( Color( COL_RED ) );
1541         else if ( nAttr & EXTTEXTINPUT_ATTR_HALFTONETEXT )
1542             rFont.SetColor( Color( COL_LIGHTGRAY ) );
1543         if ( nAttr & EXTTEXTINPUT_ATTR_HIGHLIGHT )
1544         {
1545             const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1546             rFont.SetColor( rStyleSettings.GetHighlightTextColor() );
1547             rFont.SetFillColor( rStyleSettings.GetHighlightColor() );
1548             rFont.SetTransparent( sal_False );
1549         }
1550         else if ( nAttr & EXTTEXTINPUT_ATTR_GRAYWAVELINE )
1551         {
1552             rFont.SetUnderline( UNDERLINE_WAVE );
1553 //          if( pOut )
1554 //              pOut->SetTextLineColor( Color( COL_LIGHTGRAY ) );
1555         }
1556     }
1557 }
1558 
SetUpdateMode(sal_Bool bUp,TextView * pCurView,sal_Bool bForceUpdate)1559 void TextEngine::SetUpdateMode( sal_Bool bUp, TextView* pCurView, sal_Bool bForceUpdate )
1560 {
1561     sal_Bool bChanged = ( GetUpdateMode() != bUp );
1562 
1563     mbUpdate = bUp;
1564     if ( mbUpdate && ( bChanged || bForceUpdate ) )
1565         FormatAndUpdate( pCurView );
1566 }
1567 
FormatAndUpdate(TextView * pCurView)1568 void TextEngine::FormatAndUpdate( TextView* pCurView )
1569 {
1570     if ( mbDowning )
1571         return ;
1572 
1573     if ( IsInUndo() )
1574         IdleFormatAndUpdate( pCurView );
1575     else
1576     {
1577         FormatDoc();
1578         UpdateViews( pCurView );
1579     }
1580 }
1581 
1582 
IdleFormatAndUpdate(TextView * pCurView,sal_uInt16 nMaxTimerRestarts)1583 void TextEngine::IdleFormatAndUpdate( TextView* pCurView, sal_uInt16 nMaxTimerRestarts )
1584 {
1585     mpIdleFormatter->DoIdleFormat( pCurView, nMaxTimerRestarts );
1586 }
1587 
TextModified()1588 void TextEngine::TextModified()
1589 {
1590     mbFormatted = sal_False;
1591     mbModified = sal_True;
1592 }
1593 
UpdateViews(TextView * pCurView)1594 void TextEngine::UpdateViews( TextView* pCurView )
1595 {
1596     if ( !GetUpdateMode() || IsFormatting() || maInvalidRec.IsEmpty() )
1597         return;
1598 
1599     DBG_ASSERT( IsFormatted(), "UpdateViews: Doc nicht formatiert!" );
1600 
1601     for ( sal_uInt16 nView = 0; nView < mpViews->Count(); nView++ )
1602     {
1603         TextView* pView = mpViews->GetObject( nView );
1604         pView->HideCursor();
1605 
1606         Rectangle aClipRec( maInvalidRec );
1607         Size aOutSz = pView->GetWindow()->GetOutputSizePixel();
1608         Rectangle aVisArea( pView->GetStartDocPos(), aOutSz );
1609         aClipRec.Intersection( aVisArea );
1610         if ( !aClipRec.IsEmpty() )
1611         {
1612             // in Fensterkoordinaten umwandeln....
1613             Point aNewPos = pView->GetWindowPos( aClipRec.TopLeft() );
1614             if ( IsRightToLeft() )
1615                 aNewPos.X() -= aOutSz.Width() - 1;
1616             aClipRec.SetPos( aNewPos );
1617 
1618             if ( pView == pCurView )
1619                 pView->ImpPaint( aClipRec, !pView->GetWindow()->IsPaintTransparent() );
1620             else
1621                 pView->GetWindow()->Invalidate( aClipRec );
1622         }
1623     }
1624 
1625     if ( pCurView )
1626     {
1627         pCurView->ShowCursor( pCurView->IsAutoScroll() );
1628     }
1629 
1630     maInvalidRec = Rectangle();
1631 }
1632 
IMPL_LINK(TextEngine,IdleFormatHdl,Timer *,EMPTYARG)1633 IMPL_LINK( TextEngine, IdleFormatHdl, Timer *, EMPTYARG )
1634 {
1635     FormatAndUpdate( mpIdleFormatter->GetView() );
1636     return 0;
1637 }
1638 
CheckIdleFormatter()1639 void TextEngine::CheckIdleFormatter()
1640 {
1641     mpIdleFormatter->ForceTimeout();
1642 }
1643 
FormatFullDoc()1644 void TextEngine::FormatFullDoc()
1645 {
1646     for ( sal_uLong nPortion = 0; nPortion < mpTEParaPortions->Count(); nPortion++ )
1647     {
1648         TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPortion );        sal_uInt16 nLen = pTEParaPortion->GetNode()->GetText().Len();
1649         pTEParaPortion->MarkSelectionInvalid( 0, nLen );
1650     }
1651     mbFormatted = sal_False;
1652     FormatDoc();
1653 }
1654 
FormatDoc()1655 void TextEngine::FormatDoc()
1656 {
1657     if ( IsFormatted() || !GetUpdateMode() || IsFormatting() )
1658         return;
1659 
1660     mbIsFormatting = sal_True;
1661     mbHasMultiLineParas = sal_False;
1662 
1663     long nY = 0;
1664     sal_Bool bGrow = sal_False;
1665 
1666     maInvalidRec = Rectangle(); // leermachen
1667     for ( sal_uLong nPara = 0; nPara < mpTEParaPortions->Count(); nPara++ )
1668     {
1669         TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1670         if ( pTEParaPortion->IsInvalid() )
1671         {
1672             sal_uLong nOldParaWidth = 0xFFFFFFFF;
1673             if ( mnCurTextWidth != 0xFFFFFFFF )
1674                 nOldParaWidth = CalcTextWidth( nPara );
1675 
1676             ImpFormattingParagraph( nPara );
1677 
1678             if ( CreateLines( nPara ) )
1679                 bGrow = sal_True;
1680 
1681             // InvalidRec nur einmal setzen...
1682             if ( maInvalidRec.IsEmpty() )
1683             {
1684                 // Bei Paperwidth 0 (AutoPageSize) bleibt es sonst Empty()...
1685                 long nWidth = (long)mnMaxTextWidth;
1686                 if ( !nWidth )
1687                     nWidth = 0x7FFFFFFF;
1688                 Range aInvRange( GetInvalidYOffsets( nPara ) );
1689                 maInvalidRec = Rectangle( Point( 0, nY+aInvRange.Min() ),
1690                     Size( nWidth, aInvRange.Len() ) );
1691             }
1692             else
1693             {
1694                 maInvalidRec.Bottom() = nY + CalcParaHeight( nPara );
1695             }
1696 
1697             if ( mnCurTextWidth != 0xFFFFFFFF )
1698             {
1699                 sal_uLong nNewParaWidth = CalcTextWidth( nPara );
1700                 if ( nNewParaWidth >= mnCurTextWidth )
1701                     mnCurTextWidth = nNewParaWidth;
1702                 else if ( ( nOldParaWidth != 0xFFFFFFFF ) && ( nOldParaWidth >= mnCurTextWidth ) )
1703                     mnCurTextWidth = 0xFFFFFFFF;
1704             }
1705         }
1706         else if ( bGrow )
1707         {
1708             maInvalidRec.Bottom() = nY + CalcParaHeight( nPara );
1709         }
1710         nY += CalcParaHeight( nPara );
1711         if ( !mbHasMultiLineParas && pTEParaPortion->GetLines().Count() > 1 )
1712             mbHasMultiLineParas = sal_True;
1713     }
1714 
1715     if ( !maInvalidRec.IsEmpty() )
1716     {
1717         sal_uLong nNewHeight = CalcTextHeight();
1718         long nDiff = nNewHeight - mnCurTextHeight;
1719         if ( nNewHeight < mnCurTextHeight )
1720         {
1721             maInvalidRec.Bottom() = (long)Max( nNewHeight, mnCurTextHeight );
1722             if ( maInvalidRec.IsEmpty() )
1723             {
1724                 maInvalidRec.Top() = 0;
1725                 // Left und Right werden nicht ausgewertet, aber wegen IsEmpty gesetzt.
1726                 maInvalidRec.Left() = 0;
1727                 maInvalidRec.Right() = mnMaxTextWidth;
1728             }
1729         }
1730 
1731         mnCurTextHeight = nNewHeight;
1732         if ( nDiff )
1733         {
1734             mbFormatted = sal_True;
1735             ImpTextHeightChanged();
1736         }
1737     }
1738 
1739     mbIsFormatting = sal_False;
1740     mbFormatted = sal_True;
1741 
1742     ImpTextFormatted();
1743 }
1744 
CreateAndInsertEmptyLine(sal_uLong nPara)1745 void TextEngine::CreateAndInsertEmptyLine( sal_uLong nPara )
1746 {
1747     TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
1748     TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1749 
1750     TextLine* pTmpLine = new TextLine;
1751     pTmpLine->SetStart( pNode->GetText().Len() );
1752     pTmpLine->SetEnd( pTmpLine->GetStart() );
1753     pTEParaPortion->GetLines().Insert( pTmpLine, pTEParaPortion->GetLines().Count() );
1754 
1755     if ( ImpGetAlign() == TXTALIGN_CENTER )
1756         pTmpLine->SetStartX( (short)(mnMaxTextWidth / 2) );
1757     else if ( ImpGetAlign() == TXTALIGN_RIGHT )
1758         pTmpLine->SetStartX( (short)mnMaxTextWidth );
1759     else
1760         pTmpLine->SetStartX( mpDoc->GetLeftMargin() );
1761 
1762     sal_Bool bLineBreak = pNode->GetText().Len() ? sal_True : sal_False;
1763 
1764     TETextPortion* pDummyPortion = new TETextPortion( 0 );
1765     pDummyPortion->GetWidth() = 0;
1766     pTEParaPortion->GetTextPortions().Insert( pDummyPortion, pTEParaPortion->GetTextPortions().Count() );
1767 
1768     if ( bLineBreak == sal_True )
1769     {
1770         // -2: Die neue ist bereits eingefuegt.
1771         #ifdef DBG_UTIL
1772         TextLine* pLastLine = pTEParaPortion->GetLines().GetObject( pTEParaPortion->GetLines().Count()-2 );
1773         DBG_ASSERT( pLastLine, "Weicher Umbruch, keine Zeile ?!" );
1774         #endif
1775         sal_uInt16 nPos = (sal_uInt16) pTEParaPortion->GetTextPortions().Count() - 1 ;
1776         pTmpLine->SetStartPortion( nPos );
1777         pTmpLine->SetEndPortion( nPos );
1778     }
1779 }
1780 
ImpBreakLine(sal_uLong nPara,TextLine * pLine,TETextPortion *,sal_uInt16 nPortionStart,long nRemainingWidth)1781 void TextEngine::ImpBreakLine( sal_uLong nPara, TextLine* pLine, TETextPortion*, sal_uInt16 nPortionStart, long nRemainingWidth )
1782 {
1783     TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
1784 
1785     // Font sollte noch eingestellt sein.
1786     sal_uInt16 nMaxBreakPos = mpRefDev->GetTextBreak( pNode->GetText(), nRemainingWidth, nPortionStart );
1787 
1788     DBG_ASSERT( nMaxBreakPos < pNode->GetText().Len(), "Break?!" );
1789 
1790     if ( nMaxBreakPos == STRING_LEN )   // GetTextBreak() ist anderer Auffassung als GetTextSize()
1791         nMaxBreakPos = pNode->GetText().Len() - 1;
1792 
1793     uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator();
1794     i18n::LineBreakHyphenationOptions aHyphOptions( NULL, uno::Sequence< beans::PropertyValue >(), 1 );
1795 
1796     i18n::LineBreakUserOptions aUserOptions;
1797     aUserOptions.forbiddenBeginCharacters = ImpGetLocaleDataWrapper()->getForbiddenCharacters().beginLine;
1798     aUserOptions.forbiddenEndCharacters = ImpGetLocaleDataWrapper()->getForbiddenCharacters().endLine;
1799     aUserOptions.applyForbiddenRules = sal_True;
1800     aUserOptions.allowPunctuationOutsideMargin = sal_False;
1801     aUserOptions.allowHyphenateEnglish = sal_False;
1802 
1803     static const com::sun::star::lang::Locale aDefLocale;
1804     i18n::LineBreakResults aLBR = xBI->getLineBreak( pNode->GetText(), nMaxBreakPos, aDefLocale, pLine->GetStart(), aHyphOptions, aUserOptions );
1805     sal_uInt16 nBreakPos = (sal_uInt16)aLBR.breakIndex;
1806     if ( nBreakPos <= pLine->GetStart() )
1807     {
1808         nBreakPos = nMaxBreakPos;
1809         if ( nBreakPos <= pLine->GetStart() )
1810             nBreakPos = pLine->GetStart() + 1;  // Sonst Endlosschleife!
1811     }
1812 
1813 
1814     // die angeknackste Portion ist die End-Portion
1815     pLine->SetEnd( nBreakPos );
1816     sal_uInt16 nEndPortion = SplitTextPortion( nPara, nBreakPos );
1817 
1818     sal_Bool bBlankSeparator = ( ( nBreakPos >= pLine->GetStart() ) &&
1819                                  ( pNode->GetText().GetChar( nBreakPos ) == ' ' ) ) ? sal_True : sal_False;
1820     if ( bBlankSeparator )
1821     {
1822         // Blanks am Zeilenende generell unterdruecken...
1823         TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1824         TETextPortion* pTP = pTEParaPortion->GetTextPortions().GetObject( nEndPortion );
1825         DBG_ASSERT( nBreakPos > pLine->GetStart(), "SplitTextPortion am Anfang der Zeile?" );
1826         pTP->GetWidth() = (long)CalcTextWidth( nPara, nBreakPos-pTP->GetLen(), pTP->GetLen()-1 );
1827     }
1828     pLine->SetEndPortion( nEndPortion );
1829 }
1830 
SplitTextPortion(sal_uLong nPara,sal_uInt16 nPos)1831 sal_uInt16 TextEngine::SplitTextPortion( sal_uLong nPara, sal_uInt16 nPos )
1832 {
1833 
1834     // Die Portion bei nPos wird geplittet, wenn bei nPos nicht
1835     // sowieso ein Wechsel ist
1836     if ( nPos == 0 )
1837         return 0;
1838 
1839     sal_uInt16 nSplitPortion;
1840     sal_uInt16 nTmpPos = 0;
1841     TETextPortion* pTextPortion = 0;
1842     TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1843     sal_uInt16 nPortions = pTEParaPortion->GetTextPortions().Count();
1844     for ( nSplitPortion = 0; nSplitPortion < nPortions; nSplitPortion++ )
1845     {
1846         TETextPortion* pTP = pTEParaPortion->GetTextPortions().GetObject(nSplitPortion);
1847         nTmpPos = nTmpPos + pTP->GetLen();
1848         if ( nTmpPos >= nPos )
1849         {
1850             if ( nTmpPos == nPos )  // dann braucht nichts geteilt werden
1851                 return nSplitPortion;
1852             pTextPortion = pTP;
1853             break;
1854         }
1855     }
1856 
1857     DBG_ASSERT( pTextPortion, "Position ausserhalb des Bereichs!" );
1858 
1859     sal_uInt16 nOverlapp = nTmpPos - nPos;
1860     pTextPortion->GetLen() = pTextPortion->GetLen() - nOverlapp;
1861     TETextPortion* pNewPortion = new TETextPortion( nOverlapp );
1862     pTEParaPortion->GetTextPortions().Insert( pNewPortion, nSplitPortion+1 );
1863     pTextPortion->GetWidth() = (long)CalcTextWidth( nPara, nPos-pTextPortion->GetLen(), pTextPortion->GetLen() );
1864 
1865     return nSplitPortion;
1866 }
1867 
CreateTextPortions(sal_uLong nPara,sal_uInt16 nStartPos)1868 void TextEngine::CreateTextPortions( sal_uLong nPara, sal_uInt16 nStartPos )
1869 {
1870     TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1871     TextNode* pNode = pTEParaPortion->GetNode();
1872     DBG_ASSERT( pNode->GetText().Len(), "CreateTextPortions sollte nicht fuer leere Absaetze verwendet werden!" );
1873 
1874     TESortedPositions aPositions;
1875     sal_uLong nZero = 0;
1876     aPositions.Insert( nZero );
1877 
1878     sal_uInt16 nAttribs = pNode->GetCharAttribs().Count();
1879     for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
1880     {
1881         TextCharAttrib* pAttrib = pNode->GetCharAttribs().GetAttrib( nAttr );
1882 
1883         // Start und Ende in das Array eintragen...
1884         // Die InsertMethode laesst keine doppelten Werte zu....
1885         aPositions.Insert( pAttrib->GetStart() );
1886         aPositions.Insert( pAttrib->GetEnd() );
1887     }
1888     aPositions.Insert( pNode->GetText().Len() );
1889 
1890     const TEWritingDirectionInfos& rWritingDirections = pTEParaPortion->GetWritingDirectionInfos();
1891     for ( sal_uInt16 nD = 0; nD < rWritingDirections.Count(); nD++ )
1892         aPositions.Insert( rWritingDirections[nD].nStartPos );
1893 
1894     if ( mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetPara() == nPara ) )
1895     {
1896         sal_uInt16 nLastAttr = 0xFFFF;
1897         for( sal_uInt16 n = 0; n < mpIMEInfos->nLen; n++ )
1898         {
1899             if ( mpIMEInfos->pAttribs[n] != nLastAttr )
1900             {
1901                 aPositions.Insert( mpIMEInfos->aPos.GetIndex() + n );
1902                 nLastAttr = mpIMEInfos->pAttribs[n];
1903             }
1904         }
1905     }
1906 
1907     sal_uInt16 nTabPos = pNode->GetText().Search( '\t', 0 );
1908     while ( nTabPos != STRING_NOTFOUND )
1909     {
1910         aPositions.Insert( nTabPos );
1911         aPositions.Insert( nTabPos + 1 );
1912         nTabPos = pNode->GetText().Search( '\t', nTabPos+1 );
1913     }
1914 
1915     // Ab ... loeschen:
1916     // Leider muss die Anzahl der TextPortions mit aPositions.Count()
1917     // nicht uebereinstimmen, da evtl. Zeilenumbrueche...
1918     sal_uInt16 nPortionStart = 0;
1919     sal_uInt16 nInvPortion = 0;
1920     sal_uInt16 nP;
1921     for ( nP = 0; nP < pTEParaPortion->GetTextPortions().Count(); nP++ )
1922     {
1923         TETextPortion* pTmpPortion = pTEParaPortion->GetTextPortions().GetObject(nP);
1924         nPortionStart = nPortionStart + pTmpPortion->GetLen();
1925         if ( nPortionStart >= nStartPos )
1926         {
1927             nPortionStart = nPortionStart - pTmpPortion->GetLen();
1928             nInvPortion = nP;
1929             break;
1930         }
1931     }
1932     DBG_ASSERT( nP < pTEParaPortion->GetTextPortions().Count() || !pTEParaPortion->GetTextPortions().Count(), "Nichts zum loeschen: CreateTextPortions" );
1933     if ( nInvPortion && ( nPortionStart+pTEParaPortion->GetTextPortions().GetObject(nInvPortion)->GetLen() > nStartPos ) )
1934     {
1935         // lieber eine davor...
1936         // Aber nur wenn es mitten in der Portion war, sonst ist es evtl.
1937         // die einzige in der Zeile davor !
1938         nInvPortion--;
1939         nPortionStart = nPortionStart - pTEParaPortion->GetTextPortions().GetObject(nInvPortion)->GetLen();
1940     }
1941     pTEParaPortion->GetTextPortions().DeleteFromPortion( nInvPortion );
1942 
1943     // Eine Portion kann auch durch einen Zeilenumbruch entstanden sein:
1944     aPositions.Insert( nPortionStart );
1945 
1946     sal_uInt16 nInvPos;
1947     #ifdef DBG_UTIL
1948     sal_Bool bFound =
1949     #endif
1950         aPositions.Seek_Entry( nPortionStart, &nInvPos );
1951     DBG_ASSERT( bFound && ( nInvPos < (aPositions.Count()-1) ), "InvPos ?!" );
1952     for ( sal_uInt16 i = nInvPos+1; i < aPositions.Count(); i++ )
1953     {
1954         TETextPortion* pNew = new TETextPortion( (sal_uInt16)aPositions[i] - (sal_uInt16)aPositions[i-1] );
1955         pTEParaPortion->GetTextPortions().Insert( pNew, pTEParaPortion->GetTextPortions().Count());
1956     }
1957 
1958     DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "Keine Portions?!" );
1959 #ifdef EDITDEBUG
1960     DBG_ASSERT( pTEParaPortion->DbgCheckTextPortions(), "Portions kaputt?" );
1961 #endif
1962 }
1963 
RecalcTextPortion(sal_uLong nPara,sal_uInt16 nStartPos,short nNewChars)1964 void TextEngine::RecalcTextPortion( sal_uLong nPara, sal_uInt16 nStartPos, short nNewChars )
1965 {
1966     TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1967     DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "Keine Portions!" );
1968     DBG_ASSERT( nNewChars, "RecalcTextPortion mit Diff == 0" );
1969 
1970     TextNode* const pNode = pTEParaPortion->GetNode();
1971     if ( nNewChars > 0 )
1972     {
1973         // Wenn an nStartPos ein Attribut beginnt/endet, oder vor nStartPos
1974         // ein Tab steht, faengt eine neue Portion an,
1975         // ansonsten wird die Portion an nStartPos erweitert.
1976         // Oder wenn ganz vorne ( StartPos 0 ) und dann ein Tab
1977 
1978         if ( ( pNode->GetCharAttribs().HasBoundingAttrib( nStartPos ) ) ||
1979              ( nStartPos && ( pNode->GetText().GetChar( nStartPos - 1 ) == '\t' ) ) ||
1980              ( ( !nStartPos && ( nNewChars < pNode->GetText().Len() ) && pNode->GetText().GetChar( nNewChars ) == '\t' ) ) )
1981         {
1982             sal_uInt16 nNewPortionPos = 0;
1983             if ( nStartPos )
1984                 nNewPortionPos = SplitTextPortion( nPara, nStartPos ) + 1;
1985 //          else if ( ( pTEParaPortion->GetTextPortions().Count() == 1 ) &&
1986 //                      !pTEParaPortion->GetTextPortions()[0]->GetLen() )
1987 //              pTEParaPortion->GetTextPortions().Reset();  // DummyPortion
1988 
1989             // Eine leere Portion kann hier stehen, wenn der Absatz leer war,
1990             // oder eine Zeile durch einen harten Zeilenumbruch entstanden ist.
1991             if ( ( nNewPortionPos < pTEParaPortion->GetTextPortions().Count() ) &&
1992                     !pTEParaPortion->GetTextPortions()[nNewPortionPos]->GetLen() )
1993             {
1994                 // Dann die leere Portion verwenden.
1995                 sal_uInt16 & r =
1996                     pTEParaPortion->GetTextPortions()[nNewPortionPos]->GetLen();
1997                 r = r + nNewChars;
1998             }
1999             else
2000             {
2001                 TETextPortion* pNewPortion = new TETextPortion( nNewChars );
2002                 pTEParaPortion->GetTextPortions().Insert( pNewPortion, nNewPortionPos );
2003             }
2004         }
2005         else
2006         {
2007             sal_uInt16 nPortionStart;
2008             const sal_uInt16 nTP = pTEParaPortion->GetTextPortions().
2009                 FindPortion( nStartPos, nPortionStart );
2010             TETextPortion* const pTP = pTEParaPortion->GetTextPortions()[ nTP ];
2011             DBG_ASSERT( pTP, "RecalcTextPortion: Portion nicht gefunden"  );
2012             pTP->GetLen() = pTP->GetLen() + nNewChars;
2013             pTP->GetWidth() = (-1);
2014         }
2015     }
2016     else
2017     {
2018         // Portion schrumpfen oder ggf. entfernen.
2019         // Vor Aufruf dieser Methode muss sichergestellt sein, dass
2020         // keine Portions in dem geloeschten Bereich lagen!
2021 
2022         // Es darf keine reinragende oder im Bereich startende Portion geben,
2023         // also muss nStartPos <= nPos <= nStartPos - nNewChars(neg.) sein
2024         sal_uInt16 nPortion = 0;
2025         sal_uInt16 nPos = 0;
2026         sal_uInt16 nEnd = nStartPos-nNewChars;
2027         sal_uInt16 nPortions = pTEParaPortion->GetTextPortions().Count();
2028         TETextPortion* pTP = 0;
2029         for ( nPortion = 0; nPortion < nPortions; nPortion++ )
2030         {
2031             pTP = pTEParaPortion->GetTextPortions()[ nPortion ];
2032             if ( ( nPos+pTP->GetLen() ) > nStartPos )
2033             {
2034                 DBG_ASSERT( nPos <= nStartPos, "Start falsch!" );
2035                 DBG_ASSERT( nPos+pTP->GetLen() >= nEnd, "End falsch!" );
2036                 break;
2037             }
2038             nPos = nPos + pTP->GetLen();
2039         }
2040         DBG_ASSERT( pTP, "RecalcTextPortion: Portion nicht gefunden" );
2041         if ( ( nPos == nStartPos ) && ( (nPos+pTP->GetLen()) == nEnd ) )
2042         {
2043             // Portion entfernen;
2044             pTEParaPortion->GetTextPortions().Remove( nPortion );
2045             delete pTP;
2046         }
2047         else
2048         {
2049             DBG_ASSERT( pTP->GetLen() > (-nNewChars), "Portion zu klein zum schrumpfen!" );
2050             pTP->GetLen() = pTP->GetLen() + nNewChars;
2051         }
2052         DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "RecalcTextPortions: Keine mehr da!" );
2053     }
2054 
2055 #ifdef EDITDEBUG
2056     DBG_ASSERT( pTEParaPortion->DbgCheckTextPortions(), "Portions kaputt?" );
2057 #endif
2058 }
2059 
ImpPaint(OutputDevice * pOutDev,const Point & rStartPos,Rectangle const * pPaintArea,TextSelection const * pPaintRange,TextSelection const * pSelection)2060 void TextEngine::ImpPaint( OutputDevice* pOutDev, const Point& rStartPos, Rectangle const* pPaintArea, TextSelection const* pPaintRange, TextSelection const* pSelection )
2061 {
2062     if ( !GetUpdateMode() )
2063         return;
2064 
2065     if ( !IsFormatted() )
2066         FormatDoc();
2067 
2068     bool bTransparent = false;
2069     Window* pOutWin = dynamic_cast<Window*>(pOutDev);
2070     bTransparent = (pOutWin && pOutWin->IsPaintTransparent());
2071 
2072     long nY = rStartPos.Y();
2073 
2074     TextPaM const* pSelStart = 0;
2075     TextPaM const* pSelEnd = 0;
2076     if ( pSelection && pSelection->HasRange() )
2077     {
2078         sal_Bool bInvers = pSelection->GetEnd() < pSelection->GetStart();
2079         pSelStart = !bInvers ? &pSelection->GetStart() : &pSelection->GetEnd();
2080         pSelEnd = bInvers ? &pSelection->GetStart() : &pSelection->GetEnd();
2081     }
2082     DBG_ASSERT( !pPaintRange || ( pPaintRange->GetStart() < pPaintRange->GetEnd() ), "ImpPaint: Paint-Range?!" );
2083 
2084     const StyleSettings& rStyleSettings = pOutDev->GetSettings().GetStyleSettings();
2085 
2086     // --------------------------------------------------
2087     // Ueber alle Absaetze...
2088     // --------------------------------------------------
2089     for ( sal_uLong nPara = 0; nPara < mpTEParaPortions->Count(); nPara++ )
2090     {
2091         TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
2092         // falls beim Tippen Idle-Formatierung, asynchrones Paint.
2093         if ( pPortion->IsInvalid() )
2094             return;
2095 
2096         sal_uLong nParaHeight = CalcParaHeight( nPara );
2097         sal_uInt16 nIndex = 0;
2098         if ( ( !pPaintArea || ( ( nY + (long)nParaHeight ) > pPaintArea->Top() ) )
2099                 && ( !pPaintRange || ( ( nPara >= pPaintRange->GetStart().GetPara() ) && ( nPara <= pPaintRange->GetEnd().GetPara() ) ) ) )
2100         {
2101             // --------------------------------------------------
2102             // Ueber die Zeilen des Absatzes...
2103             // --------------------------------------------------
2104             sal_uInt16 nLines = pPortion->GetLines().Count();
2105             for ( sal_uInt16 nLine = 0; nLine < nLines; nLine++ )
2106             {
2107                 TextLine* pLine = pPortion->GetLines().GetObject(nLine);
2108                 Point aTmpPos( rStartPos.X() + pLine->GetStartX(), nY );
2109 
2110                 if ( ( !pPaintArea || ( ( nY + mnCharHeight ) > pPaintArea->Top() ) )
2111                     && ( !pPaintRange || (
2112                         ( TextPaM( nPara, pLine->GetStart() ) < pPaintRange->GetEnd() ) &&
2113                         ( TextPaM( nPara, pLine->GetEnd() ) > pPaintRange->GetStart() ) ) ) )
2114                 {
2115                     // --------------------------------------------------
2116                     // Ueber die Portions der Zeile...
2117                     // --------------------------------------------------
2118                     nIndex = pLine->GetStart();
2119                     for ( sal_uInt16 y = pLine->GetStartPortion(); y <= pLine->GetEndPortion(); y++ )
2120                     {
2121                         DBG_ASSERT( pPortion->GetTextPortions().Count(), "Zeile ohne Textportion im Paint!" );
2122                         TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( y );
2123                         DBG_ASSERT( pTextPortion, "NULL-Pointer im Portioniterator in UpdateViews" );
2124 
2125                         ImpInitLayoutMode( pOutDev /*, pTextPortion->IsRightToLeft() */);
2126 
2127                         long nTxtWidth = pTextPortion->GetWidth();
2128                         aTmpPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nIndex, nIndex );
2129 
2130                         // nur ausgeben, was im sichtbaren Bereich beginnt:
2131                         if ( ( ( aTmpPos.X() + nTxtWidth ) >= 0 )
2132                             && ( !pPaintRange || (
2133                                 ( TextPaM( nPara, nIndex ) < pPaintRange->GetEnd() ) &&
2134                                     ( TextPaM( nPara, nIndex + pTextPortion->GetLen() ) > pPaintRange->GetStart() ) ) ) )
2135                         {
2136                             switch ( pTextPortion->GetKind() )
2137                             {
2138                                 case PORTIONKIND_TEXT:
2139                                 {
2140                                     {
2141                                         Font aFont;
2142                                         SeekCursor( nPara, nIndex+1, aFont, pOutDev );
2143                                         if( bTransparent )
2144                                             aFont.SetTransparent( sal_True );
2145                                         else if ( pSelection )
2146                                             aFont.SetTransparent( sal_False );
2147                                         pOutDev->SetFont( aFont );
2148 
2149                                         sal_uInt16 nTmpIndex = nIndex;
2150                                         sal_uInt16 nEnd = nTmpIndex + pTextPortion->GetLen();
2151                                         Point aPos = aTmpPos;
2152                                         if ( pPaintRange )
2153                                         {
2154                                             // evtl soll nicht alles ausgegeben werden...
2155                                             if ( ( pPaintRange->GetStart().GetPara() == nPara )
2156                                                     && ( nTmpIndex < pPaintRange->GetStart().GetIndex() ) )
2157                                             {
2158                                                 nTmpIndex = pPaintRange->GetStart().GetIndex();
2159                                             }
2160                                             if ( ( pPaintRange->GetEnd().GetPara() == nPara )
2161                                                     && ( nEnd > pPaintRange->GetEnd().GetIndex() ) )
2162                                             {
2163                                                 nEnd = pPaintRange->GetEnd().GetIndex();
2164                                             }
2165                                         }
2166 
2167                                         sal_Bool bDone = sal_False;
2168                                         if ( pSelStart )
2169                                         {
2170                                             // liegt ein Teil in der Selektion???
2171                                             TextPaM aTextStart( nPara, nTmpIndex );
2172                                             TextPaM aTextEnd( nPara, nEnd );
2173                                             if ( ( aTextStart < *pSelEnd ) && ( aTextEnd > *pSelStart ) )
2174                                             {
2175                                                 sal_uInt16 nL;
2176 
2177                                                 // 1) Bereich vor Selektion
2178                                                 if ( aTextStart < *pSelStart )
2179                                                 {
2180                                                     nL = pSelStart->GetIndex() - nTmpIndex;
2181                                                     pOutDev->SetFont( aFont);
2182                                                     aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL );
2183                                                     pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nL );
2184                                                     nTmpIndex = nTmpIndex + nL;
2185 
2186                                                 }
2187                                                 // 2) Bereich mit Selektion
2188                                                 nL = nEnd-nTmpIndex;
2189                                                 if ( aTextEnd > *pSelEnd )
2190                                                     nL = pSelEnd->GetIndex() - nTmpIndex;
2191                                                 if ( nL )
2192                                                 {
2193                                                     Color aOldTextColor = pOutDev->GetTextColor();
2194                                                     pOutDev->SetTextColor( rStyleSettings.GetHighlightTextColor() );
2195                                                     pOutDev->SetTextFillColor( rStyleSettings.GetHighlightColor() );
2196                                                     aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL );
2197                                                     pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nL );
2198                                                     pOutDev->SetTextColor( aOldTextColor );
2199                                                     pOutDev->SetTextFillColor();
2200                                                     nTmpIndex = nTmpIndex + nL;
2201                                                 }
2202 
2203                                                 // 3) Bereich nach Selektion
2204                                                 if ( nTmpIndex < nEnd )
2205                                                 {
2206                                                     nL = nEnd-nTmpIndex;
2207                                                     aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL );
2208                                                     pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nEnd-nTmpIndex );
2209                                                 }
2210                                                 bDone = sal_True;
2211                                             }
2212                                         }
2213                                         if ( !bDone )
2214                                         {
2215                                             aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nEnd );
2216                                             pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nEnd-nTmpIndex );
2217                                         }
2218                                     }
2219 
2220                                 }
2221                                 break;
2222                                 case PORTIONKIND_TAB:
2223                                 {
2224                                     // Bei HideSelection() nur Range, pSelection = 0.
2225                                     if ( pSelStart || pPaintRange )
2226                                     {
2227                                         Rectangle aTabArea( aTmpPos, Point( aTmpPos.X()+nTxtWidth, aTmpPos.Y()+mnCharHeight-1 ) );
2228                                         sal_Bool bDone = sal_False;
2229                                         if ( pSelStart )
2230                                         {
2231                                             // liegt der Tab in der Selektion???
2232                                             TextPaM aTextStart( nPara, nIndex );
2233                                             TextPaM aTextEnd( nPara, nIndex+1 );
2234                                             if ( ( aTextStart < *pSelEnd ) && ( aTextEnd > *pSelStart ) )
2235                                             {
2236                                                 Color aOldColor = pOutDev->GetFillColor();
2237                                                 pOutDev->SetFillColor( rStyleSettings.GetHighlightColor() );
2238                                                 pOutDev->DrawRect( aTabArea );
2239                                                 pOutDev->SetFillColor( aOldColor );
2240                                                 bDone = sal_True;
2241                                             }
2242                                         }
2243                                         if ( !bDone )
2244                                         {
2245                                             pOutDev->Erase( aTabArea );
2246                                         }
2247                                     }
2248 #ifdef EDITDEBUG
2249                                     Rectangle aTabArea( aTmpPos, Point( aTmpPos.X()+nTxtWidth, aTmpPos.Y()+mnCharHeight-1 ) );
2250                                     Color aOldColor = pOutDev->GetFillColor();
2251                                     pOutDev->SetFillColor( (y%2) ? COL_RED : COL_GREEN );
2252                                     pOutDev->DrawRect( aTabArea );
2253                                     pOutDev->SetFillColor( aOldColor );
2254 #endif
2255                                 }
2256                                 break;
2257                                 default:    DBG_ERROR( "ImpPaint: Unknown Portion-Type !" );
2258                             }
2259                         }
2260 
2261                         nIndex = nIndex + pTextPortion->GetLen();
2262                     }
2263                 }
2264 
2265                 nY += mnCharHeight;
2266 
2267                 if ( pPaintArea && ( nY >= pPaintArea->Bottom() ) )
2268                     break;  // keine sichtbaren Aktionen mehr...
2269             }
2270         }
2271         else
2272         {
2273             nY += nParaHeight;
2274         }
2275 
2276         if ( pPaintArea && ( nY > pPaintArea->Bottom() ) )
2277             break;  // keine sichtbaren Aktionen mehr...
2278     }
2279 }
2280 
CreateLines(sal_uLong nPara)2281 sal_Bool TextEngine::CreateLines( sal_uLong nPara )
2282 {
2283     // sal_Bool: Aenderung der Hoehe des Absatzes Ja/Nein - sal_True/sal_False
2284 
2285     TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2286     TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2287     DBG_ASSERT( pTEParaPortion->IsInvalid(), "CreateLines: Portion nicht invalid!" );
2288 
2289     sal_uInt16 nOldLineCount = pTEParaPortion->GetLines().Count();
2290 
2291     // ---------------------------------------------------------------
2292     // Schnelle Sonderbehandlung fuer leere Absaetze...
2293     // ---------------------------------------------------------------
2294     if ( pTEParaPortion->GetNode()->GetText().Len() == 0 )
2295     {
2296         // schnelle Sonderbehandlung...
2297         if ( pTEParaPortion->GetTextPortions().Count() )
2298             pTEParaPortion->GetTextPortions().Reset();
2299         if ( pTEParaPortion->GetLines().Count() )
2300             pTEParaPortion->GetLines().DeleteAndDestroy( 0, pTEParaPortion->GetLines().Count() );
2301         CreateAndInsertEmptyLine( nPara );
2302         pTEParaPortion->SetValid();
2303         return nOldLineCount != pTEParaPortion->GetLines().Count();
2304     }
2305 
2306     // ---------------------------------------------------------------
2307     // Initialisierung......
2308     // ---------------------------------------------------------------
2309 
2310     if ( pTEParaPortion->GetLines().Count() == 0 )
2311     {
2312         TextLine* pL = new TextLine;
2313         pTEParaPortion->GetLines().Insert( pL, 0 );
2314     }
2315 
2316     const short nInvalidDiff = pTEParaPortion->GetInvalidDiff();
2317     const sal_uInt16 nInvalidStart = pTEParaPortion->GetInvalidPosStart();
2318     const sal_uInt16 nInvalidEnd =  nInvalidStart + Abs( nInvalidDiff );
2319     sal_Bool bQuickFormat = sal_False;
2320 
2321     if ( !pTEParaPortion->GetWritingDirectionInfos().Count() )
2322         ImpInitWritingDirections( nPara );
2323 
2324     if ( pTEParaPortion->GetWritingDirectionInfos().Count() == 1 )
2325     {
2326         if ( pTEParaPortion->IsSimpleInvalid() && ( nInvalidDiff > 0 ) )
2327         {
2328             bQuickFormat = sal_True;
2329         }
2330         else if ( ( pTEParaPortion->IsSimpleInvalid() ) && ( nInvalidDiff < 0 ) )
2331         {
2332             // pruefen, ob loeschen ueber Portiongrenzen erfolgte...
2333             sal_uInt16 nStart = nInvalidStart;  // DOPPELT !!!!!!!!!!!!!!!
2334             sal_uInt16 nEnd = nStart - nInvalidDiff;  // neg.
2335             bQuickFormat = sal_True;
2336             sal_uInt16 nPos = 0;
2337             sal_uInt16 nPortions = pTEParaPortion->GetTextPortions().Count();
2338             for ( sal_uInt16 nTP = 0; nTP < nPortions; nTP++ )
2339             {
2340                 // Es darf kein Start/Ende im geloeschten Bereich liegen.
2341                 TETextPortion* const pTP = pTEParaPortion->GetTextPortions().GetObject( nTP );
2342                 nPos = nPos + pTP->GetLen();
2343                 if ( ( nPos > nStart ) && ( nPos < nEnd ) )
2344                 {
2345                     bQuickFormat = sal_False;
2346                     break;
2347                 }
2348             }
2349         }
2350     }
2351 
2352     if ( bQuickFormat )
2353         RecalcTextPortion( nPara, nInvalidStart, nInvalidDiff );
2354     else
2355         CreateTextPortions( nPara, nInvalidStart );
2356 
2357     // ---------------------------------------------------------------
2358     // Zeile mit InvalidPos suchen, eine Zeile davor beginnen...
2359     // Zeilen flaggen => nicht removen !
2360     // ---------------------------------------------------------------
2361 
2362     sal_uInt16 nLine = pTEParaPortion->GetLines().Count()-1;
2363     for ( sal_uInt16 nL = 0; nL <= nLine; nL++ )
2364     {
2365         TextLine* pLine = pTEParaPortion->GetLines().GetObject( nL );
2366         if ( pLine->GetEnd() > nInvalidStart )
2367         {
2368             nLine = nL;
2369             break;
2370         }
2371         pLine->SetValid();
2372     }
2373     // Eine Zeile davor beginnen...
2374     // Wenn ganz hinten getippt wird, kann sich die Zeile davor nicht aendern.
2375     if ( nLine && ( !pTEParaPortion->IsSimpleInvalid() || ( nInvalidEnd < pNode->GetText().Len() ) || ( nInvalidDiff <= 0 ) ) )
2376         nLine--;
2377 
2378     TextLine* pLine = pTEParaPortion->GetLines().GetObject( nLine );
2379 
2380     // ---------------------------------------------------------------
2381     // Ab hier alle Zeilen durchformatieren...
2382     // ---------------------------------------------------------------
2383     sal_uInt16 nDelFromLine = 0xFFFF;
2384     sal_Bool bLineBreak = sal_False;
2385 
2386     sal_uInt16 nIndex = pLine->GetStart();
2387     TextLine aSaveLine( *pLine );
2388 
2389     Font aFont;
2390 
2391     sal_Bool bCalcPortion = sal_True;
2392 
2393     while ( nIndex < pNode->GetText().Len() )
2394     {
2395         sal_Bool bEOL = sal_False;
2396         sal_Bool bEOC = sal_False;
2397         sal_uInt16 nPortionStart = 0;
2398         sal_uInt16 nPortionEnd = 0;
2399 
2400         sal_uInt16 nTmpPos = nIndex;
2401         sal_uInt16 nTmpPortion = pLine->GetStartPortion();
2402         long nTmpWidth = mpDoc->GetLeftMargin();
2403 //      long nXWidth = mnMaxTextWidth ? ( mnMaxTextWidth - mpDoc->GetLeftMargin() ) : 0x7FFFFFFF;
2404         // Margin nicht abziehen, ist schon in TmpWidth enthalten.
2405         long nXWidth = mnMaxTextWidth ? mnMaxTextWidth : 0x7FFFFFFF;
2406         if ( nXWidth < nTmpWidth )
2407             nXWidth = nTmpWidth;
2408 
2409         // Portion suchen, die nicht mehr in Zeile passt....
2410         TETextPortion* pPortion = 0;
2411         sal_Bool bBrokenLine = sal_False;
2412         bLineBreak = sal_False;
2413 
2414         while ( ( nTmpWidth <= nXWidth ) && !bEOL && ( nTmpPortion < pTEParaPortion->GetTextPortions().Count() ) )
2415         {
2416             nPortionStart = nTmpPos;
2417             pPortion = pTEParaPortion->GetTextPortions().GetObject( nTmpPortion );
2418             DBG_ASSERT( pPortion->GetLen(), "Leere Portion in CreateLines ?!" );
2419             if ( pNode->GetText().GetChar( nTmpPos ) == '\t' )
2420             {
2421                 long nCurPos = nTmpWidth-mpDoc->GetLeftMargin();
2422                 nTmpWidth = ((nCurPos/mnDefTab)+1)*mnDefTab+mpDoc->GetLeftMargin();
2423                 pPortion->GetWidth() = nTmpWidth - nCurPos - mpDoc->GetLeftMargin();
2424                 // Wenn dies das erste Token in der Zeile ist, und
2425                 // nTmpWidth > aPaperSize.Width, habe ich eine Endlos-Schleife!
2426                 if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) )
2427                 {
2428                     // Aber was jetzt ? Tab passend machen!
2429                     pPortion->GetWidth() = nXWidth-1;
2430                     nTmpWidth = pPortion->GetWidth();
2431                     bEOL = sal_True;
2432                     bBrokenLine = sal_True;
2433                 }
2434                 pPortion->GetKind() = PORTIONKIND_TAB;
2435             }
2436             else
2437             {
2438 
2439                 if ( bCalcPortion || !pPortion->HasValidSize() )
2440                     pPortion->GetWidth() = (long)CalcTextWidth( nPara, nTmpPos, pPortion->GetLen() );
2441                 nTmpWidth += pPortion->GetWidth();
2442 
2443                 pPortion->GetRightToLeft() = ImpGetRightToLeft( nPara, nTmpPos+1 );
2444                 pPortion->GetKind() = PORTIONKIND_TEXT;
2445             }
2446 
2447             nTmpPos = nTmpPos + pPortion->GetLen();
2448             nPortionEnd = nTmpPos;
2449             nTmpPortion++;
2450         }
2451 
2452         // das war evtl. eine Portion zu weit:
2453         sal_Bool bFixedEnd = sal_False;
2454         if ( nTmpWidth > nXWidth )
2455         {
2456             nPortionEnd = nTmpPos;
2457             nTmpPos = nTmpPos - pPortion->GetLen();
2458             nPortionStart = nTmpPos;
2459             nTmpPortion--;
2460             bEOL = sal_False;
2461             bEOC = sal_False;
2462 
2463             nTmpWidth -= pPortion->GetWidth();
2464             if ( pPortion->GetKind() == PORTIONKIND_TAB )
2465             {
2466                 bEOL = sal_True;
2467                 bFixedEnd = sal_True;
2468             }
2469         }
2470         else
2471         {
2472             bEOL = sal_True;
2473             bEOC = sal_True;
2474             pLine->SetEnd( nPortionEnd );
2475             DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "Keine TextPortions?" );
2476             pLine->SetEndPortion( (sal_uInt16)pTEParaPortion->GetTextPortions().Count() - 1 );
2477         }
2478 
2479         if ( bFixedEnd )
2480         {
2481             pLine->SetEnd( nPortionStart );
2482             pLine->SetEndPortion( nTmpPortion-1 );
2483         }
2484         else if ( bLineBreak || bBrokenLine )
2485         {
2486             pLine->SetEnd( nPortionStart+1 );
2487             pLine->SetEndPortion( nTmpPortion-1 );
2488             bEOC = sal_False; // wurde oben gesetzt, vielleich mal die if's umstellen?
2489         }
2490         else if ( !bEOL )
2491         {
2492             DBG_ASSERT( (nPortionEnd-nPortionStart) == pPortion->GetLen(), "Doch eine andere Portion?!" );
2493             long nRemainingWidth = mnMaxTextWidth - nTmpWidth;
2494             ImpBreakLine( nPara, pLine, pPortion, nPortionStart, nRemainingWidth );
2495         }
2496 
2497         if ( ( ImpGetAlign() == TXTALIGN_CENTER ) || ( ImpGetAlign() == TXTALIGN_RIGHT ) )
2498         {
2499             // Ausrichten...
2500             long nTextWidth = 0;
2501             for ( sal_uInt16 nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ )
2502             {
2503                 TETextPortion* pTextPortion = pTEParaPortion->GetTextPortions().GetObject( nTP );
2504                 nTextWidth += pTextPortion->GetWidth();
2505             }
2506             long nSpace = mnMaxTextWidth - nTextWidth;
2507             if ( nSpace > 0 )
2508             {
2509                 if ( ImpGetAlign() == TXTALIGN_CENTER )
2510                     pLine->SetStartX( (sal_uInt16)(nSpace / 2) );
2511                 else    // TXTALIGN_RIGHT
2512                     pLine->SetStartX( (sal_uInt16)nSpace );
2513             }
2514         }
2515         else
2516         {
2517             pLine->SetStartX( mpDoc->GetLeftMargin() );
2518         }
2519 
2520         // -----------------------------------------------------------------
2521         // pruefen, ob die Zeile neu ausgegeben werden muss...
2522         // -----------------------------------------------------------------
2523         pLine->SetInvalid();
2524 
2525         if ( pTEParaPortion->IsSimpleInvalid() )
2526         {
2527             // Aenderung durch einfache Textaenderung...
2528             // Formatierung nicht abbrechen, da Portions evtl. wieder
2529             // gesplittet werden muessen!
2530             // Wenn irgendwann mal abbrechbar, dann fogende Zeilen Validieren!
2531             // Aber ggf. als Valid markieren, damit weniger Ausgabe...
2532             if ( pLine->GetEnd() < nInvalidStart )
2533             {
2534                 if ( *pLine == aSaveLine )
2535                 {
2536                     pLine->SetValid();
2537                 }
2538             }
2539             else
2540             {
2541                 sal_uInt16 nStart = pLine->GetStart();
2542                 sal_uInt16 nEnd = pLine->GetEnd();
2543 
2544                 if ( nStart > nInvalidEnd )
2545                 {
2546                     if ( ( ( nStart-nInvalidDiff ) == aSaveLine.GetStart() ) &&
2547                             ( ( nEnd-nInvalidDiff ) == aSaveLine.GetEnd() ) )
2548                     {
2549                         pLine->SetValid();
2550                         if ( bCalcPortion && bQuickFormat )
2551                         {
2552                             bCalcPortion = sal_False;
2553                             pTEParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
2554                             break;
2555                         }
2556                     }
2557                 }
2558                 else if ( bQuickFormat && ( nEnd > nInvalidEnd) )
2559                 {
2560                     // Wenn die ungueltige Zeile so endet, dass die naechste an
2561                     // der 'gleichen' Textstelle wie vorher beginnt, also nicht
2562                     // anders umgebrochen wird, brauche ich dort auch nicht die
2563                     // textbreiten neu bestimmen:
2564                     if ( nEnd == ( aSaveLine.GetEnd() + nInvalidDiff ) )
2565                     {
2566                         bCalcPortion = sal_False;
2567                         pTEParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
2568                         break;
2569                     }
2570                 }
2571             }
2572         }
2573 
2574         nIndex = pLine->GetEnd();   // naechste Zeile Start = letzte Zeile Ende
2575                                     // weil nEnd hinter das letzte Zeichen zeigt!
2576 
2577         sal_uInt16 nEndPortion = pLine->GetEndPortion();
2578 
2579         // Naechste Zeile oder ggf. neue Zeile....
2580         pLine = 0;
2581         if ( nLine < pTEParaPortion->GetLines().Count()-1 )
2582             pLine = pTEParaPortion->GetLines().GetObject( ++nLine );
2583         if ( pLine && ( nIndex >= pNode->GetText().Len() ) )
2584         {
2585             nDelFromLine = nLine;
2586             break;
2587         }
2588         if ( !pLine && ( nIndex < pNode->GetText().Len() )  )
2589         {
2590             pLine = new TextLine;
2591             pTEParaPortion->GetLines().Insert( pLine, ++nLine );
2592         }
2593         if ( pLine )
2594         {
2595             aSaveLine = *pLine;
2596             pLine->SetStart( nIndex );
2597             pLine->SetEnd( nIndex );
2598             pLine->SetStartPortion( nEndPortion+1 );
2599             pLine->SetEndPortion( nEndPortion+1 );
2600         }
2601     }   // while ( Index < Len )
2602 
2603     if ( nDelFromLine != 0xFFFF )
2604         pTEParaPortion->GetLines().DeleteAndDestroy( nDelFromLine, pTEParaPortion->GetLines().Count() - nDelFromLine );
2605 
2606     DBG_ASSERT( pTEParaPortion->GetLines().Count(), "Keine Zeile nach CreateLines!" );
2607 
2608     if ( bLineBreak == sal_True )
2609         CreateAndInsertEmptyLine( nPara );
2610 
2611     pTEParaPortion->SetValid();
2612 
2613     return nOldLineCount != pTEParaPortion->GetLines().Count();
2614 }
2615 
GetWord(const TextPaM & rCursorPos,TextPaM * pStartOfWord)2616 String TextEngine::GetWord( const TextPaM& rCursorPos, TextPaM* pStartOfWord )
2617 {
2618     String aWord;
2619     if ( rCursorPos.GetPara() < mpDoc->GetNodes().Count() )
2620     {
2621         TextSelection aSel( rCursorPos );
2622         TextNode* pNode = mpDoc->GetNodes().GetObject(  rCursorPos.GetPara() );
2623         uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator();
2624         i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), rCursorPos.GetIndex(), GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True );
2625         aSel.GetStart().GetIndex() = (sal_uInt16)aBoundary.startPos;
2626         aSel.GetEnd().GetIndex() = (sal_uInt16)aBoundary.endPos;
2627         aWord = pNode->GetText().Copy( aSel.GetStart().GetIndex(), aSel.GetEnd().GetIndex() - aSel.GetStart().GetIndex() );
2628         if ( pStartOfWord )
2629             *pStartOfWord = aSel.GetStart();
2630     }
2631     return aWord;
2632 }
2633 
Read(SvStream & rInput,const TextSelection * pSel)2634 sal_Bool TextEngine::Read( SvStream& rInput, const TextSelection* pSel )
2635 {
2636     sal_Bool bUpdate = GetUpdateMode();
2637     SetUpdateMode( sal_False );
2638 
2639     UndoActionStart();
2640     TextSelection aSel;
2641     if ( pSel )
2642         aSel = *pSel;
2643     else
2644     {
2645         sal_uLong nParas = mpDoc->GetNodes().Count();
2646         TextNode* pNode = mpDoc->GetNodes().GetObject( nParas - 1 );
2647         aSel = TextPaM( nParas-1 , pNode->GetText().Len() );
2648     }
2649 
2650     if ( aSel.HasRange() )
2651         aSel = ImpDeleteText( aSel );
2652 
2653     ByteString aLine;
2654     sal_Bool bDone = rInput.ReadLine( aLine );
2655     String aTmpStr( aLine, rInput.GetStreamCharSet() ), aStr;
2656     while ( bDone )
2657     {
2658         aSel = ImpInsertText( aSel, aTmpStr );
2659         bDone = rInput.ReadLine( aLine );
2660         aTmpStr = String( aLine, rInput.GetStreamCharSet() );
2661         if ( bDone )
2662             aSel = ImpInsertParaBreak( aSel.GetEnd() );
2663     }
2664 
2665     UndoActionEnd();
2666 
2667     TextSelection aNewSel( aSel.GetEnd(), aSel.GetEnd() );
2668 
2669     // Damit bei FormatAndUpdate nicht auf die ungueltige Selektion zugegriffen wird.
2670     if ( GetActiveView() )
2671         GetActiveView()->ImpSetSelection( aNewSel );
2672 
2673     SetUpdateMode( bUpdate );
2674     FormatAndUpdate( GetActiveView() );
2675 
2676     return rInput.GetError() ? sal_False : sal_True;
2677 }
2678 
Write(SvStream & rOutput,const TextSelection * pSel,sal_Bool bHTML)2679 sal_Bool TextEngine::Write( SvStream& rOutput, const TextSelection* pSel, sal_Bool bHTML )
2680 {
2681     TextSelection aSel;
2682     if ( pSel )
2683         aSel = *pSel;
2684     else
2685     {
2686         sal_uLong nParas = mpDoc->GetNodes().Count();
2687         TextNode* pNode = mpDoc->GetNodes().GetObject( nParas - 1 );
2688         aSel.GetStart() = TextPaM( 0, 0 );
2689         aSel.GetEnd() = TextPaM( nParas-1, pNode->GetText().Len() );
2690     }
2691 
2692     if ( bHTML )
2693     {
2694         rOutput.WriteLine( "<HTML>" );
2695         rOutput.WriteLine( "<BODY>" );
2696     }
2697 
2698     for ( sal_uLong nPara = aSel.GetStart().GetPara(); nPara <= aSel.GetEnd().GetPara(); nPara++  )
2699     {
2700         TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2701 
2702         sal_uInt16 nStartPos = 0;
2703         sal_uInt16 nEndPos = pNode->GetText().Len();
2704         if ( nPara == aSel.GetStart().GetPara() )
2705             nStartPos = aSel.GetStart().GetIndex();
2706         if ( nPara == aSel.GetEnd().GetPara() )
2707             nEndPos = aSel.GetEnd().GetIndex();
2708 
2709         String aText;
2710         if ( !bHTML )
2711         {
2712             aText = pNode->GetText().Copy( nStartPos, nEndPos-nStartPos );
2713         }
2714         else
2715         {
2716             aText.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "<P STYLE=\"margin-bottom: 0cm\">" ) );
2717 
2718             if ( nStartPos == nEndPos )
2719             {
2720                 // Leerzeilen werden von Writer wegoptimiert
2721                 aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "<BR>" ) );
2722             }
2723             else
2724             {
2725                 sal_uInt16 nTmpStart = nStartPos;
2726                 sal_uInt16 nTmpEnd = nEndPos;
2727                 do
2728                 {
2729                     TextCharAttrib* pAttr = pNode->GetCharAttribs().FindNextAttrib( TEXTATTR_HYPERLINK, nTmpStart, nEndPos );
2730                     nTmpEnd = pAttr ? pAttr->GetStart() : nEndPos;
2731 
2732                     // Text vor dem Attribut
2733                     aText += pNode->GetText().Copy( nTmpStart, nTmpEnd-nTmpStart );
2734 
2735                     if ( pAttr )
2736                     {
2737                         nTmpEnd = Min( pAttr->GetEnd(), nEndPos );
2738 
2739                         // z.B. <A HREF="http://www.mopo.de/">Morgenpost</A>
2740                         aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "<A HREF=\"" ) );
2741                         aText += ((const TextAttribHyperLink&) pAttr->GetAttr() ).GetURL();
2742                         aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "\">" ) );
2743                         nTmpStart = pAttr->GetStart();
2744                         aText += pNode->GetText().Copy( nTmpStart, nTmpEnd-nTmpStart );
2745                         aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "</A>" ) );
2746 
2747                         nTmpStart = pAttr->GetEnd();
2748                     }
2749                 } while ( nTmpEnd < nEndPos );
2750             }
2751 
2752             aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "</P>" ) );
2753         }
2754         rOutput.WriteLine( ByteString( aText, rOutput.GetStreamCharSet() ) );
2755     }
2756 
2757     if ( bHTML )
2758     {
2759         rOutput.WriteLine( "</BODY>" );
2760         rOutput.WriteLine( "</HTML>" );
2761     }
2762 
2763     return rOutput.GetError() ? sal_False : sal_True;
2764 }
2765 
RemoveAttribs(sal_uLong nPara,sal_Bool bIdleFormatAndUpdate)2766 void TextEngine::RemoveAttribs( sal_uLong nPara, sal_Bool bIdleFormatAndUpdate )
2767 {
2768     if ( nPara < mpDoc->GetNodes().Count() )
2769     {
2770         TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2771         if ( pNode->GetCharAttribs().Count() )
2772         {
2773             pNode->GetCharAttribs().Clear( sal_True );
2774 
2775             TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2776             pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() );
2777 
2778             mbFormatted = sal_False;
2779 
2780             if ( bIdleFormatAndUpdate )
2781                 IdleFormatAndUpdate( NULL, 0xFFFF );
2782             else
2783                 FormatAndUpdate( NULL );
2784         }
2785     }
2786 }
RemoveAttribs(sal_uLong nPara,sal_uInt16 nWhich,sal_Bool bIdleFormatAndUpdate)2787 void TextEngine::RemoveAttribs( sal_uLong nPara, sal_uInt16 nWhich, sal_Bool bIdleFormatAndUpdate )
2788 {
2789     if ( nPara < mpDoc->GetNodes().Count() )
2790     {
2791         TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2792         if ( pNode->GetCharAttribs().Count() )
2793         {
2794             TextCharAttribList& rAttribs = pNode->GetCharAttribs();
2795             sal_uInt16 nAttrCount = rAttribs.Count();
2796             for(sal_uInt16 nAttr = nAttrCount; nAttr; --nAttr)
2797             {
2798                 if(rAttribs.GetAttrib( nAttr - 1 )->Which() == nWhich)
2799                     rAttribs.RemoveAttrib( nAttr -1 );
2800             }
2801             TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2802             pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() );
2803             mbFormatted = sal_False;
2804             if(bIdleFormatAndUpdate)
2805                 IdleFormatAndUpdate( NULL, 0xFFFF );
2806             else
2807                 FormatAndUpdate( NULL );
2808         }
2809     }
2810 }
RemoveAttrib(sal_uLong nPara,const TextCharAttrib & rAttrib)2811 void TextEngine::RemoveAttrib( sal_uLong nPara, const TextCharAttrib& rAttrib )
2812 {
2813     if ( nPara < mpDoc->GetNodes().Count() )
2814     {
2815         TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2816         if ( pNode->GetCharAttribs().Count() )
2817         {
2818             TextCharAttribList& rAttribs = pNode->GetCharAttribs();
2819             sal_uInt16 nAttrCount = rAttribs.Count();
2820             for(sal_uInt16 nAttr = nAttrCount; nAttr; --nAttr)
2821             {
2822                 if(rAttribs.GetAttrib( nAttr - 1 ) == &rAttrib)
2823                 {
2824                     rAttribs.RemoveAttrib( nAttr -1 );
2825                     break;
2826                 }
2827             }
2828             TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2829             pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() );
2830             mbFormatted = sal_False;
2831             FormatAndUpdate( NULL );
2832         }
2833     }
2834 }
2835 
SetAttrib(const TextAttrib & rAttr,sal_uLong nPara,sal_uInt16 nStart,sal_uInt16 nEnd,sal_Bool bIdleFormatAndUpdate)2836 void TextEngine::SetAttrib( const TextAttrib& rAttr, sal_uLong nPara, sal_uInt16 nStart, sal_uInt16 nEnd, sal_Bool bIdleFormatAndUpdate )
2837 {
2838     // Es wird hier erstmal nicht geprueft, ob sich Attribute ueberlappen!
2839     // Diese Methode ist erstmal nur fuer einen Editor, der fuer eine Zeile
2840     // _schnell_ das Syntax-Highlight einstellen will.
2841 
2842     // Da die TextEngine z.Zt fuer Editoren gedacht ist gibt es auch kein
2843     // Undo fuer Attribute!
2844 
2845     if ( nPara < mpDoc->GetNodes().Count() )
2846     {
2847         TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2848         TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2849 
2850         sal_uInt16 nMax = pNode->GetText().Len();
2851         if ( nStart > nMax )
2852             nStart = nMax;
2853         if ( nEnd > nMax )
2854             nEnd = nMax;
2855 
2856         pNode->GetCharAttribs().InsertAttrib( new TextCharAttrib( rAttr, nStart, nEnd ) );
2857         pTEParaPortion->MarkSelectionInvalid( nStart, nEnd );
2858 
2859         mbFormatted = sal_False;
2860         if ( bIdleFormatAndUpdate )
2861             IdleFormatAndUpdate( NULL, 0xFFFF );
2862         else
2863             FormatAndUpdate( NULL );
2864     }
2865 }
2866 
SetTextAlign(TxtAlign eAlign)2867 void TextEngine::SetTextAlign( TxtAlign eAlign )
2868 {
2869     if ( eAlign != meAlign )
2870     {
2871         meAlign = eAlign;
2872         FormatFullDoc();
2873         UpdateViews();
2874     }
2875 }
2876 
2877 
ValidateSelection(TextSelection & rSel) const2878 void TextEngine::ValidateSelection( TextSelection& rSel ) const
2879 {
2880     ValidatePaM( rSel.GetStart() );
2881     ValidatePaM( rSel.GetEnd() );
2882 }
2883 
ValidatePaM(TextPaM & rPaM) const2884 void TextEngine::ValidatePaM( TextPaM& rPaM ) const
2885 {
2886     sal_uLong nMaxPara = mpDoc->GetNodes().Count() - 1;
2887     if ( rPaM.GetPara() > nMaxPara )
2888     {
2889         rPaM.GetPara() = nMaxPara;
2890         rPaM.GetIndex() = 0xFFFF;
2891     }
2892 
2893     sal_uInt16 nMaxIndex = GetTextLen( rPaM.GetPara() );
2894     if ( rPaM.GetIndex() > nMaxIndex )
2895         rPaM.GetIndex() = nMaxIndex;
2896 }
2897 
2898 
2899 // Status & Selektionsanpassung
2900 
ImpParagraphInserted(sal_uLong nPara)2901 void TextEngine::ImpParagraphInserted( sal_uLong nPara )
2902 {
2903     // Die aktive View braucht nicht angepasst werden, aber bei allen
2904     // passiven muss die Selektion angepasst werden:
2905     if ( mpViews->Count() > 1 )
2906     {
2907         for ( sal_uInt16 nView = mpViews->Count(); nView; )
2908         {
2909             TextView* pView = mpViews->GetObject( --nView );
2910             if ( pView != GetActiveView() )
2911             {
2912 //              sal_Bool bInvers = pView->maSelection.GetEnd() < pView->maSelection.GetStart();
2913 //              TextPaM& rMin = !bInvers ? pView->maSelection.GetStart(): pView->maSelection.GetEnd();
2914 //              TextPaM& rMax = bInvers ? pView->maSelection.GetStart() : pView->maSelection.GetEnd();
2915 //
2916 //              if ( rMin.GetPara() >= nPara )
2917 //                  rMin.GetPara()++;
2918 //              if ( rMax.GetPara() >= nPara )
2919 //                  rMax.GetPara()++;
2920                 for ( int n = 0; n <= 1; n++ )
2921                 {
2922                     TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd();
2923                     if ( rPaM.GetPara() >= nPara )
2924                         rPaM.GetPara()++;
2925                 }
2926             }
2927         }
2928     }
2929     Broadcast( TextHint( TEXT_HINT_PARAINSERTED, nPara ) );
2930 }
2931 
ImpParagraphRemoved(sal_uLong nPara)2932 void TextEngine::ImpParagraphRemoved( sal_uLong nPara )
2933 {
2934     if ( mpViews->Count() > 1 )
2935     {
2936         for ( sal_uInt16 nView = mpViews->Count(); nView; )
2937         {
2938             TextView* pView = mpViews->GetObject( --nView );
2939             if ( pView != GetActiveView() )
2940             {
2941                 sal_uLong nParas = mpDoc->GetNodes().Count();
2942                 for ( int n = 0; n <= 1; n++ )
2943                 {
2944                     TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd();
2945                     if ( rPaM.GetPara() > nPara )
2946                         rPaM.GetPara()--;
2947                     else if ( rPaM.GetPara() == nPara )
2948                     {
2949                         rPaM.GetIndex() = 0;
2950                         if ( rPaM.GetPara() >= nParas )
2951                             rPaM.GetPara()--;
2952                     }
2953                 }
2954             }
2955         }
2956     }
2957     Broadcast( TextHint( TEXT_HINT_PARAREMOVED, nPara ) );
2958 }
2959 
ImpCharsRemoved(sal_uLong nPara,sal_uInt16 nPos,sal_uInt16 nChars)2960 void TextEngine::ImpCharsRemoved( sal_uLong nPara, sal_uInt16 nPos, sal_uInt16 nChars )
2961 {
2962     if ( mpViews->Count() > 1 )
2963     {
2964         for ( sal_uInt16 nView = mpViews->Count(); nView; )
2965         {
2966             TextView* pView = mpViews->GetObject( --nView );
2967             if ( pView != GetActiveView() )
2968             {
2969                 sal_uInt16 nEnd = nPos+nChars;
2970                 for ( int n = 0; n <= 1; n++ )
2971                 {
2972                     TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd();
2973                     if ( rPaM.GetPara() == nPara )
2974                     {
2975                         if ( rPaM.GetIndex() > nEnd )
2976                             rPaM.GetIndex() = rPaM.GetIndex() - nChars;
2977                         else if ( rPaM.GetIndex() > nPos )
2978                             rPaM.GetIndex() = nPos;
2979                     }
2980                 }
2981             }
2982         }
2983     }
2984     Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED, nPara ) );
2985 }
2986 
ImpCharsInserted(sal_uLong nPara,sal_uInt16 nPos,sal_uInt16 nChars)2987 void TextEngine::ImpCharsInserted( sal_uLong nPara, sal_uInt16 nPos, sal_uInt16 nChars )
2988 {
2989     if ( mpViews->Count() > 1 )
2990     {
2991         for ( sal_uInt16 nView = mpViews->Count(); nView; )
2992         {
2993             TextView* pView = mpViews->GetObject( --nView );
2994             if ( pView != GetActiveView() )
2995             {
2996                 for ( int n = 0; n <= 1; n++ )
2997                 {
2998                     TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd();
2999                     if ( rPaM.GetPara() == nPara )
3000                     {
3001                         if ( rPaM.GetIndex() >= nPos )
3002                             rPaM.GetIndex() = rPaM.GetIndex() + nChars;
3003                     }
3004                 }
3005             }
3006         }
3007     }
3008     Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED, nPara ) );
3009 }
3010 
ImpFormattingParagraph(sal_uLong nPara)3011 void TextEngine::ImpFormattingParagraph( sal_uLong nPara )
3012 {
3013     Broadcast( TextHint( TEXT_HINT_FORMATPARA, nPara ) );
3014 }
3015 
ImpTextHeightChanged()3016 void TextEngine::ImpTextHeightChanged()
3017 {
3018     Broadcast( TextHint( TEXT_HINT_TEXTHEIGHTCHANGED ) );
3019 }
3020 
ImpTextFormatted()3021 void TextEngine::ImpTextFormatted()
3022 {
3023     Broadcast( TextHint( TEXT_HINT_TEXTFORMATTED ) );
3024 }
3025 
Draw(OutputDevice * pDev,const Point & rPos)3026 void TextEngine::Draw( OutputDevice* pDev, const Point& rPos )
3027 {
3028     ImpPaint( pDev, rPos, NULL );
3029 }
3030 
SetLeftMargin(sal_uInt16 n)3031 void TextEngine::SetLeftMargin( sal_uInt16 n )
3032 {
3033     mpDoc->SetLeftMargin( n );
3034 }
3035 
GetLeftMargin() const3036 sal_uInt16 TextEngine::GetLeftMargin() const
3037 {
3038     return mpDoc->GetLeftMargin();
3039 }
3040 
GetBreakIterator()3041 uno::Reference< i18n::XBreakIterator > TextEngine::GetBreakIterator()
3042 {
3043     if ( !mxBreakIterator.is() )
3044         mxBreakIterator = vcl::unohelper::CreateBreakIterator();
3045     DBG_ASSERT( mxBreakIterator.is(), "Could not create BreakIterator" );
3046     return mxBreakIterator;
3047 }
3048 
SetLocale(const::com::sun::star::lang::Locale & rLocale)3049 void TextEngine::SetLocale( const ::com::sun::star::lang::Locale& rLocale )
3050 {
3051     maLocale = rLocale;
3052     delete mpLocaleDataWrapper;
3053     mpLocaleDataWrapper = NULL;
3054 }
3055 
GetLocale()3056 ::com::sun::star::lang::Locale TextEngine::GetLocale()
3057 {
3058     if ( !maLocale.Language.getLength() )
3059     {
3060         maLocale = Application::GetSettings().GetUILocale();
3061     }
3062     return maLocale;
3063 }
3064 
ImpGetLocaleDataWrapper()3065 LocaleDataWrapper* TextEngine::ImpGetLocaleDataWrapper()
3066 {
3067     if ( !mpLocaleDataWrapper )
3068         mpLocaleDataWrapper = new LocaleDataWrapper( vcl::unohelper::GetMultiServiceFactory(), GetLocale() );
3069 
3070     return mpLocaleDataWrapper;
3071 }
3072 
SetRightToLeft(sal_Bool bR2L)3073 void TextEngine::SetRightToLeft( sal_Bool bR2L )
3074 {
3075     if ( mbRightToLeft != bR2L )
3076     {
3077         mbRightToLeft = bR2L;
3078         meAlign = bR2L ? TXTALIGN_RIGHT : TXTALIGN_LEFT;
3079         FormatFullDoc();
3080         UpdateViews();
3081     }
3082 }
3083 
ImpInitWritingDirections(sal_uLong nPara)3084 void TextEngine::ImpInitWritingDirections( sal_uLong nPara )
3085 {
3086     TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
3087     TEWritingDirectionInfos& rInfos = pParaPortion->GetWritingDirectionInfos();
3088     rInfos.Remove( 0, rInfos.Count() );
3089 
3090     if ( pParaPortion->GetNode()->GetText().Len() )
3091     {
3092         const UBiDiLevel nBidiLevel = IsRightToLeft() ? 1 /*RTL*/ : 0 /*LTR*/;
3093         String aText( pParaPortion->GetNode()->GetText() );
3094 
3095         //
3096         // Bidi functions from icu 2.0
3097         //
3098         UErrorCode nError = U_ZERO_ERROR;
3099         UBiDi* pBidi = ubidi_openSized( aText.Len(), 0, &nError );
3100         nError = U_ZERO_ERROR;
3101 
3102         ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aText.GetBuffer()), aText.Len(), nBidiLevel, NULL, &nError ); // UChar != sal_Unicode in MinGW
3103         nError = U_ZERO_ERROR;
3104 
3105         long nCount = ubidi_countRuns( pBidi, &nError );
3106 
3107         int32_t nStart = 0;
3108         int32_t nEnd;
3109         UBiDiLevel nCurrDir;
3110 
3111         for ( sal_uInt16 nIdx = 0; nIdx < nCount; ++nIdx )
3112         {
3113             ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir );
3114             rInfos.Insert( TEWritingDirectionInfo( nCurrDir, (sal_uInt16)nStart, (sal_uInt16)nEnd ), rInfos.Count() );
3115             nStart = nEnd;
3116         }
3117 
3118         ubidi_close( pBidi );
3119     }
3120 
3121     // No infos mean no CTL and default dir is L2R...
3122     if ( !rInfos.Count() )
3123         rInfos.Insert( TEWritingDirectionInfo( 0, 0, (sal_uInt16)pParaPortion->GetNode()->GetText().Len() ), rInfos.Count() );
3124 
3125 }
3126 
ImpGetRightToLeft(sal_uLong nPara,sal_uInt16 nPos,sal_uInt16 * pStart,sal_uInt16 * pEnd)3127 sal_uInt8 TextEngine::ImpGetRightToLeft( sal_uLong nPara, sal_uInt16 nPos, sal_uInt16* pStart, sal_uInt16* pEnd )
3128 {
3129     sal_uInt8 nRightToLeft = 0;
3130 
3131     TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
3132     if ( pNode && pNode->GetText().Len() )
3133     {
3134         TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
3135         if ( !pParaPortion->GetWritingDirectionInfos().Count() )
3136             ImpInitWritingDirections( nPara );
3137 
3138         TEWritingDirectionInfos& rDirInfos = pParaPortion->GetWritingDirectionInfos();
3139         for ( sal_uInt16 n = 0; n < rDirInfos.Count(); n++ )
3140         {
3141             if ( ( rDirInfos[n].nStartPos <= nPos ) && ( rDirInfos[n].nEndPos >= nPos ) )
3142             {
3143                 nRightToLeft = rDirInfos[n].nType;
3144                 if ( pStart )
3145                     *pStart = rDirInfos[n].nStartPos;
3146                 if ( pEnd )
3147                     *pEnd = rDirInfos[n].nEndPos;
3148                 break;
3149             }
3150         }
3151     }
3152     return nRightToLeft;
3153 }
3154 
ImpGetPortionXOffset(sal_uLong nPara,TextLine * pLine,sal_uInt16 nTextPortion)3155 long TextEngine::ImpGetPortionXOffset( sal_uLong nPara, TextLine* pLine, sal_uInt16 nTextPortion )
3156 {
3157     long nX = pLine->GetStartX();
3158 
3159     TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
3160 
3161     for ( sal_uInt16 i = pLine->GetStartPortion(); i < nTextPortion; i++ )
3162     {
3163         TETextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( i );
3164         nX += pPortion->GetWidth();
3165     }
3166 
3167     TETextPortion* pDestPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion );
3168     if ( pDestPortion->GetKind() != PORTIONKIND_TAB )
3169     {
3170         if ( !IsRightToLeft() && pDestPortion->GetRightToLeft() )
3171         {
3172             // Portions behind must be added, visual before this portion
3173             sal_uInt16 nTmpPortion = nTextPortion+1;
3174             while ( nTmpPortion <= pLine->GetEndPortion() )
3175             {
3176                 TETextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
3177                 if ( pNextTextPortion->GetRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) )
3178                     nX += pNextTextPortion->GetWidth();
3179                 else
3180                     break;
3181                 nTmpPortion++;
3182             }
3183             // Portions before must be removed, visual behind this portion
3184             nTmpPortion = nTextPortion;
3185             while ( nTmpPortion > pLine->GetStartPortion() )
3186             {
3187                 --nTmpPortion;
3188                 TETextPortion* pPrevTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
3189                 if ( pPrevTextPortion->GetRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) )
3190                     nX -= pPrevTextPortion->GetWidth();
3191                 else
3192                     break;
3193             }
3194         }
3195         else if ( IsRightToLeft() && !pDestPortion->IsRightToLeft() )
3196         {
3197             // Portions behind must be removed, visual behind this portion
3198             sal_uInt16 nTmpPortion = nTextPortion+1;
3199             while ( nTmpPortion <= pLine->GetEndPortion() )
3200             {
3201                 TETextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
3202                 if ( !pNextTextPortion->IsRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) )
3203                     nX += pNextTextPortion->GetWidth();
3204                 else
3205                     break;
3206                 nTmpPortion++;
3207             }
3208             // Portions before must be added, visual before this portion
3209             nTmpPortion = nTextPortion;
3210             while ( nTmpPortion > pLine->GetStartPortion() )
3211             {
3212                 --nTmpPortion;
3213                 TETextPortion* pPrevTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
3214                 if ( !pPrevTextPortion->IsRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) )
3215                     nX -= pPrevTextPortion->GetWidth();
3216                 else
3217                     break;
3218             }
3219         }
3220     }
3221 /*
3222     if ( IsRightToLeft() )
3223     {
3224         // Switch X postions...
3225         DBG_ASSERT( GetMaxTextWidth(), "GetPortionXOffset - max text width?!" );
3226         DBG_ASSERT( nX <= (long)GetMaxTextWidth(), "GetPortionXOffset - position out of paper size!" );
3227         nX = GetMaxTextWidth() - nX;
3228         nX -= pDestPortion->GetWidth();
3229     }
3230 */
3231 
3232     return nX;
3233 }
3234 
ImpInitLayoutMode(OutputDevice * pOutDev,sal_Bool bDrawingR2LPortion)3235 void TextEngine::ImpInitLayoutMode( OutputDevice* pOutDev, sal_Bool bDrawingR2LPortion )
3236 {
3237     sal_uLong nLayoutMode = pOutDev->GetLayoutMode();
3238 
3239     nLayoutMode &= ~(TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_COMPLEX_DISABLED | TEXT_LAYOUT_BIDI_STRONG );
3240     if ( bDrawingR2LPortion )
3241         nLayoutMode |= TEXT_LAYOUT_BIDI_RTL;
3242 
3243     pOutDev->SetLayoutMode( nLayoutMode );
3244 }
3245 
ImpGetAlign() const3246 TxtAlign TextEngine::ImpGetAlign() const
3247 {
3248     TxtAlign eAlign = meAlign;
3249     if ( IsRightToLeft() )
3250     {
3251         if ( eAlign == TXTALIGN_LEFT )
3252             eAlign = TXTALIGN_RIGHT;
3253         else if ( eAlign == TXTALIGN_RIGHT )
3254             eAlign = TXTALIGN_LEFT;
3255     }
3256     return eAlign;
3257 }
3258 
ImpGetOutputOffset(sal_uLong nPara,TextLine * pLine,sal_uInt16 nIndex,sal_uInt16 nIndex2)3259 long TextEngine::ImpGetOutputOffset( sal_uLong nPara, TextLine* pLine, sal_uInt16 nIndex, sal_uInt16 nIndex2 )
3260 {
3261     TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
3262 
3263     sal_uInt16 nPortionStart;
3264     sal_uInt16 nPortion = pPortion->GetTextPortions().FindPortion( nIndex, nPortionStart, sal_True );
3265 
3266     TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( nPortion );
3267 
3268     long nX;
3269 
3270     if ( ( nIndex == nPortionStart ) && ( nIndex == nIndex2 )  )
3271     {
3272         // Output of full portion, so we need portion x offset.
3273         // Use ImpGetPortionXOffset, because GetXPos may deliver left or right position from portioon, depending on R2L, L2R
3274         nX = ImpGetPortionXOffset( nPara, pLine, nPortion );
3275         if ( IsRightToLeft() )
3276         {
3277             nX = -nX -pTextPortion->GetWidth();
3278         }
3279     }
3280     else
3281     {
3282         nX = ImpGetXPos( nPara, pLine, nIndex, nIndex == nPortionStart );
3283         if ( nIndex2 != nIndex )
3284         {
3285             long nX2 = ImpGetXPos( nPara, pLine, nIndex2, sal_False );
3286             if ( ( !IsRightToLeft() && ( nX2 < nX ) ) ||
3287                  ( IsRightToLeft() && ( nX2 > nX ) ) )
3288             {
3289                 nX = nX2;
3290             }
3291         }
3292         if ( IsRightToLeft() )
3293         {
3294             nX = -nX;
3295         }
3296     }
3297 
3298     return nX;
3299 }
3300