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