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