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