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