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