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