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