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