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