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